diff --git a/.github/workflows/test-push.yml b/.github/workflows/test-push.yml index e34a19f1..f694bfc9 100644 --- a/.github/workflows/test-push.yml +++ b/.github/workflows/test-push.yml @@ -56,7 +56,7 @@ jobs: steps: - uses: actions/checkout@v3 - - run: gradle test --tests ${{ matrix.test }} --stacktrace --warning-mode fail + - run: gradle test --tests ${{ matrix.test }} --stacktrace --info --warning-mode fail env: TEST_WARNING_MODE: fail id: test @@ -90,7 +90,7 @@ jobs: with: java-version: ${{ matrix.java }} distribution: 'temurin' - - run: ./gradlew test --tests ${{ matrix.test }} --stacktrace --warning-mode fail + - run: ./gradlew test --tests ${{ matrix.test }} --stacktrace --info --warning-mode fail env: TEST_WARNING_MODE: fail id: test diff --git a/README.md b/README.md index fe7368d1..c2eecfb1 100644 --- a/README.md +++ b/README.md @@ -1,175 +1,54 @@ # Unimined -unified minecraft modding environment. - -## TODO for initial release - -* ~~remap fg2+ era at's back to notch (fixing mc 1.7+)~~ -* ~~remap user at's to notch~~ -* ~~auto disable combined on <=1.2.5~~ -* ~~figure out, why modloader not launching in dev due to classpath path instead of jar path~~ +unified minecraft modding environment with support for legacy environments. + +## Supported Loaders +* Fabric +* Quilt +* Forge +* Neoforge +* Modloader +* [JarModAgent](https://github.com/unimined/JarModAgent) +* just plain jarmodding + +## Planned Loaders +* Bukkit Derrivitives (at least paper) +* LiteLoader +* Sponge +* NilLoader + +## Custom Loaders +yes, this is possible, see [PrcraftExampleMod](https://github.com/prcraft-minecraft/PrcraftExampleMod) and it's buildsrc dir. ## TODO - -* ~~Refactor, refactor, refactor~~ -* ~~FG3+ support (>1.12.2)~~ -* ~~test user AT support~~ -* ~~fix fg3 versions of 1.12.2~~ -* ~~fabric aw support~~ -* ~~combined jar support : forge 1.13+ does this, do with the rest~~ -* fix split jars on fg3 -* ~~figure out how to get forge to recognise resources as part of the dev mod~~ -* split fg2+ out of the mc jar -* figure out how to do automated testing - * figure out how to determine the correctness of remap output - * aka automate the verification that versions work - * list of versions to verify - * 1.17.1 - * 1.16.5 - * 1.13.2 - * 1.12.2 - * 1.8.9 - * 1.7.10 - * 1.6.4 - * 1.5.2 - * 1.3.2 - * 1.2.5 - * 1.7.3 - * b1.3_01 - * a1.1.2_01 - * maybe by hash check? -* figure out what versions need `-Djava.util.Arrays.useLegacyMergeSort=true` to not randomly crash, this should really - be part of the legacy mc version.json, or at least betacraft's, but it's not -* ~~make myself a maven to host this on~~: https://maven.wagyourtail.xyz -* ~~fix forge mappings on 1.17+~~ -* ~~mixin support~~ -* ~~add parchment mappings support~~ - -## Example Usage - -```groovy -plugins { - id 'java' - id 'xyz.wagyourtail.unimined' // I'm using it from buildSrc, so I don't need a version, you probably do -} - -group 'com.example' -version '1.0-SNAPSHOT' - -repositories { - mavenCentral() -} - -// debug, puts some things in build/unimined instead of ~/.gradle/caches/unimined -unimined.useGlobalCache = false - -// when targetting main the first arg is optional -// unimined.minecraft { -unimined.minecraft(sourceSets.main) { - // defaults to combined on 1.3+ so you don't need to set this one - side "combined" - - version "1.14.4" - - mappings { - /* helper declarations - intermediary/searge are auto added by fabric/forge */ - - // intermediary() - // searge() - mojmap() - // retroMCP() - // yarn(1) - // parchment("1.19.3", "2022.12.18") - // mcp("stable", "39+1.12") - - // these will auto-resolve with the first available from your declared mappings - devNamespace "mojmap" - devFallbackNamespace "intermediary" - } - - // specify modloader - /* - fabric { - accessWidener "src/main/resources/whatever.aw" - loader "0.14.18" - - // set these ones to target fabric versions without intermediaries - customIntermediaries = true - prodNamespace "official" - devMappings = null - } - */ - /* - forge { - forge "28.2.26" - - accessTransformer "src/main/resources/META-INF/accesstransformer.cfg" - mixinConfig "modid.mixins.json" - } - */ - // other options available, see PatchProviders - - - mods { - // auto-genned, default to `configuration.${sourceSet.name}ModImplementation` (or just modImplementation if main), - // these are additive, so always has at least this configuration - remap(configurations.modImplementation) { - // optional, these are auto-set - namespace "intermediary" - fallbackNamespace "intermediary" - remapAtToLegacy = true // auto set to value in forge provider - mixinRemap("unimined") // default value is none, this value does full mixin remapping for dev... may be necessary for run configs in some envs - remapper { - // tiny remapper settings - } - } - // if you have multiple, and they have the same config, - // use a list of configurations so they can remap together for speed reasons - // the configurations can be different, just the options in remap the same - /* - remap([configurations.a, configurations.b]) { - // stuff - } - */ - } - - minecraftRemapper.config { - // tiny remapper settings - } - - - // this is default value when sourceSet main... - // would bind a remapJar task after jar - remap(jar) - - // this one is custom - remap(jar, "customRemap") { - // these can be config'd here, but don't need to be. they can be configured below - } - - runs { - // off = true // disable runs - config("client") { - jvmArgs += ["-Dexample.arg=true"] - } - } -} - -dependencies { - // these get prepended with the sourceSet name for the mc config - modImplementation "mod:identifier:stuff" - include "mod:identifier:stuff" - - modImplementation "other:mod:stuff" -} - -remapJar { - // basically don't need to change anything here, but you can do things like set the classifier -} - -customRemap { - // extra remap after jar, can be used for second other-mapped output for - // more complicated stuff - prodNamespace "official" -} -``` +* stop using artifactural +* rework mcpconfig runner to be more kotlin and less old version of arch-loom code +* Fabric injected interfaces +* Forge JarJar +* Support for launch configs in other dev envs + * vscode + * eclipse +* support to login to minecraft in dev +* support to launch with the prod jar +* add datagen support + * forge datagen + * fabric datagen + * quilt datagen +* fix yarn on neoforge (these will probably be agents, possibly in separate projects and pulled like JarModAgent) + * inject remapper into ASMAPI + * reflection remapper (also for forge potentially, or in general). +* genSources should apply forge source patches (at least on fg3+) + +## Recommended Setup +1. take one of the versions from [testing](./testing) +1. remove `includeBuild('../../')` from `settings.gradle` +1. put a proper version number for the plugin in `build.grade` + +## Other Setups + +### Arch-Loom Style +* direct porting of arch-loom projects without changing the directory structure is possible. +* instructions pending... +### third party template(s) +* arch style: https://github.com/firstdarkdev/fdd-xplat +* //todo: add more diff --git a/build.gradle.kts b/build.gradle.kts index 7ce8280a..9e7e029f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -169,12 +169,12 @@ dependencies { implementation("net.fabricmc:access-widener:2.1.0") // at - implementation("net.minecraftforge:accesstransformers:8.0.7") { + implementation("net.neoforged:accesstransformers:9.0.3") { exclude(group = "org.apache.logging.log4j") exclude(group = "org.ow2.asm") } - implementation("org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.4.2") { + compileOnly("org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.4.2") { isTransitive = false } } diff --git a/gradle.properties b/gradle.properties index 16ede68c..3b57568c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,4 +5,4 @@ org.gradle.parallel=true maven_group=xyz.wagyourtail.unimined archives_base_name=unimined -version=1.0.6 \ No newline at end of file +version=1.1.0 \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 20cd5a14..29d27311 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists \ No newline at end of file diff --git a/src/api/kotlin/xyz/wagyourtail/unimined/api/UniminedExtension.kt b/src/api/kotlin/xyz/wagyourtail/unimined/api/UniminedExtension.kt index 087e5c9b..cab7afa1 100644 --- a/src/api/kotlin/xyz/wagyourtail/unimined/api/UniminedExtension.kt +++ b/src/api/kotlin/xyz/wagyourtail/unimined/api/UniminedExtension.kt @@ -18,6 +18,9 @@ import kotlin.io.path.createDirectories val Project.unimined get() = extensions.getByType(UniminedExtension::class.java) +val Project.uniminedMaybe + get() = extensions.findByType(UniminedExtension::class.java) + /** * the main entrypoint. * @@ -54,6 +57,9 @@ abstract class UniminedExtension(val project: Project) { @get:ApiStatus.Internal abstract val minecrafts: DefaultMap + @get:ApiStatus.Internal + abstract val minecraftConfiguration: Map Unit> + /** * @since 1.0.0 */ @@ -135,12 +141,15 @@ abstract class UniminedExtension(val project: Project) { abstract fun minecraftForgeMaven() abstract fun fabricMaven() abstract fun legacyFabricMaven() + abstract fun ornitheMaven() abstract fun wagYourMaven(name: String) abstract fun mcphackersIvy() abstract fun quiltMaven() + @Deprecated("Use glassLauncherMaven(\"babric\") instead", ReplaceWith("glassLauncherMaven(\"babric\")")) abstract fun babricMaven() + abstract fun glassLauncherMaven(name: String) abstract fun parchmentMaven() abstract fun neoForgedMaven() abstract fun sonatypeStaging() -} \ No newline at end of file +} diff --git a/src/api/kotlin/xyz/wagyourtail/unimined/api/mapping/MappingNamespaceTree.kt b/src/api/kotlin/xyz/wagyourtail/unimined/api/mapping/MappingNamespaceTree.kt index 375cd13b..f924aaa5 100644 --- a/src/api/kotlin/xyz/wagyourtail/unimined/api/mapping/MappingNamespaceTree.kt +++ b/src/api/kotlin/xyz/wagyourtail/unimined/api/mapping/MappingNamespaceTree.kt @@ -24,6 +24,14 @@ open class MappingNamespaceTree { return _targets } + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is Namespace) return false + + if (name != other.name || named != other.named) return false + return true + } + override fun toString(): String { return name.lowercase() } diff --git a/src/api/kotlin/xyz/wagyourtail/unimined/api/mapping/MappingsConfig.kt b/src/api/kotlin/xyz/wagyourtail/unimined/api/mapping/MappingsConfig.kt index f905b8eb..e5f1132c 100644 --- a/src/api/kotlin/xyz/wagyourtail/unimined/api/mapping/MappingsConfig.kt +++ b/src/api/kotlin/xyz/wagyourtail/unimined/api/mapping/MappingsConfig.kt @@ -61,6 +61,22 @@ abstract class MappingsConfig(val project: Project, val minecraft: MinecraftConf } } + @JvmOverloads + abstract fun calamus(key: String = "intermediary", action: MappingDepConfig.() -> Unit = {}) + + @JvmOverloads + fun calamus( + key: String = "intermediary", + @DelegatesTo(value = MappingDepConfig::class, strategy = Closure.DELEGATE_FIRST) + action: Closure<*> + ) { + calamus(key) { + action.delegate = this + action.resolveStrategy = Closure.DELEGATE_FIRST + action.call() + } + } + @JvmOverloads abstract fun legacyIntermediary(revision: Int = 1, key: String = "intermediary", action: MappingDepConfig.() -> Unit = {}) @@ -243,6 +259,38 @@ abstract class MappingsConfig(val project: Project, val minecraft: MinecraftConf yarn(build.toInt(), key, action) } + @JvmOverloads + abstract fun feather(build: Int, key: String = "yarn", action: MappingDepConfig.() -> Unit = {}) + + @JvmOverloads + fun feather(build: String, key: String = "yarn", action: MappingDepConfig.() -> Unit = {}) { + feather(build.toInt(), key, action) + } + + @JvmOverloads + fun feather( + build: Int, + key: String = "yarn", + @DelegatesTo(value = MappingDepConfig::class, strategy = Closure.DELEGATE_FIRST) + action: Closure<*> + ) { + feather(build, key) { + action.delegate = this + action.resolveStrategy = Closure.DELEGATE_FIRST + action.call() + } + } + + @JvmOverloads + fun feather( + build: String, + key: String = "yarn", + @DelegatesTo(value = MappingDepConfig::class, strategy = Closure.DELEGATE_FIRST) + action: Closure<*> + ) { + feather(build.toInt(), key, action) + } + @JvmOverloads abstract fun legacyYarn(build: Int, revision: Int = 1, key: String = "yarn", action: MappingDepConfig.() -> Unit = {}) @@ -341,6 +389,23 @@ abstract class MappingsConfig(val project: Project, val minecraft: MinecraftConf barn(build.toInt(), key, action) } + @JvmOverloads + abstract fun biny(commitName: String, key: String = "yarn", action: MappingDepConfig.() -> Unit = {}) + + @JvmOverloads + fun biny( + commitName: String, + key: String = "yarn", + @DelegatesTo(value = MappingDepConfig::class, strategy = Closure.DELEGATE_FIRST) + action: Closure<*> + ) { + biny(commitName, key) { + action.delegate = this + action.resolveStrategy = Closure.DELEGATE_FIRST + action.call() + } + } + @JvmOverloads abstract fun quilt(build: Int, classifier: String = "intermediary-v2", key: String = "quilt", action: MappingDepConfig.() -> Unit = {}) diff --git a/src/api/kotlin/xyz/wagyourtail/unimined/api/mapping/mixin/MixinRemapOptions.kt b/src/api/kotlin/xyz/wagyourtail/unimined/api/mapping/mixin/MixinRemapOptions.kt new file mode 100644 index 00000000..d3e8c9eb --- /dev/null +++ b/src/api/kotlin/xyz/wagyourtail/unimined/api/mapping/mixin/MixinRemapOptions.kt @@ -0,0 +1,31 @@ +package xyz.wagyourtail.unimined.api.mapping.mixin + +import org.jetbrains.annotations.ApiStatus + +/** + * @since 1.1.0 + */ +interface MixinRemapOptions { + + fun enableMixinExtra() + + fun enableBaseMixin() + + fun enableJarModAgent() + + @ApiStatus.Experimental + fun reset() + + @ApiStatus.Experimental + fun resetMetadataReader() + + @ApiStatus.Experimental + fun resetHardRemapper() + + @ApiStatus.Experimental + fun resetRefmapBuilder() + fun off() + + fun disableRefmap() + fun disableRefmap(keys: List = listOf("BaseMixin", "JarModAgent")) +} \ No newline at end of file diff --git a/src/api/kotlin/xyz/wagyourtail/unimined/api/minecraft/MinecraftConfig.kt b/src/api/kotlin/xyz/wagyourtail/unimined/api/minecraft/MinecraftConfig.kt index f2075c84..0648ba37 100644 --- a/src/api/kotlin/xyz/wagyourtail/unimined/api/minecraft/MinecraftConfig.kt +++ b/src/api/kotlin/xyz/wagyourtail/unimined/api/minecraft/MinecraftConfig.kt @@ -22,6 +22,7 @@ import xyz.wagyourtail.unimined.api.unimined import xyz.wagyourtail.unimined.util.FinalizeOnRead import xyz.wagyourtail.unimined.util.LazyMutable import xyz.wagyourtail.unimined.util.MustSet +import xyz.wagyourtail.unimined.util.sourceSets import java.io.File import java.nio.file.Path @@ -92,12 +93,11 @@ abstract class MinecraftConfig(val project: Project, val sourceSet: SourceSet) : var defaultRemapJar: Boolean by FinalizeOnRead(true) /** - * the minecraft version to use + * if the jar task for defaultRemapJar doesn't exist, should unimined create it? + * if so, it will be created with `from(this.sourceSet.output, sourceSets.main.output)` + * and it's archiveClassifier will be this.sourceSet.name */ - fun version(version: String) { - project.logger.info("setting minecraft version to $version") - this.version = version - } + var createJarTask: Boolean by FinalizeOnRead(true) @set:ApiStatus.Internal abstract var mcPatcher: MinecraftPatcher @@ -108,6 +108,73 @@ abstract class MinecraftConfig(val project: Project, val sourceSet: SourceSet) : abstract val minecraftData: MinecraftData abstract val minecraftRemapper: MinecraftRemapConfig + + + /** + * @since 1.1.0 + * copy the config closure from the config for another sourceSet + */ + fun from(sourceSet: SourceSet) { + from(project, sourceSet) + } + + /** + * @since 1.1.0 + * copy the config closure from the config for another sourceSet + */ + abstract fun from(project: Project, sourceSet: SourceSet) + + /** + * @since 1.1.0 + * copy the config closure from the config for another sourceSet + */ + fun from(path: String) { + if (!path.contains(":")) { + from(project.sourceSets.getByName(path)) + return + } + val project = path.substringBeforeLast(":") + val name = path.substringAfterLast(":") + from(this.project.project(path).sourceSets.getByName(name)) + } + + /** + * @since 1.1.0 + */ + fun combineWith(sourceSet: SourceSet) { + combineWith(project, sourceSet) + } + + /** + * @since 1.1.0 + * calls `from(project, sourceSet)` and then also adds the target to the compileClasspath and runtimeClasspath, as well as adding it to the `from` in the jar task + * this also fixes some other things, like resources being split between the two sourceSets + * + * if the target isn't a unimined sourceSet, this will skip the `from` call + */ + abstract fun combineWith(project: Project, sourceSet: SourceSet) + + /** + * @since 1.1.0 + */ + fun combineWith(path: String) { + if (!path.contains(":")) { + combineWith(project, project.sourceSets.getByName(path)) + return + } + val project = path.substringBeforeLast(":") + val name = path.substringAfterLast(":") + combineWith(this.project.project(path), this.project.project(path).sourceSets.getByName(name)) + }; + + /** + * the minecraft version to use + */ + fun version(version: String) { + project.logger.info("setting minecraft version to $version") + this.version = version + } + abstract fun mappings(action: MappingsConfig.() -> Unit) fun mappings( @@ -217,15 +284,21 @@ abstract class MinecraftConfig(val project: Project, val sourceSet: SourceSet) : @get:ApiStatus.Internal abstract val mergedOfficialMinecraftFile: File? + @get:ApiStatus.Internal + abstract val minecraft: Configuration + @get:ApiStatus.Internal abstract val minecraftLibraries: Configuration @ApiStatus.Internal abstract fun isMinecraftJar(path: Path): Boolean + @get:ApiStatus.Internal abstract val minecraftDependency: ModuleDependency @get:ApiStatus.Internal val localCache by lazy { project.unimined.getLocalCache(sourceSet) } + @get:ApiStatus.Internal + abstract val combinedWithList: MutableList> } \ No newline at end of file diff --git a/src/api/kotlin/xyz/wagyourtail/unimined/api/minecraft/PatchProviders.kt b/src/api/kotlin/xyz/wagyourtail/unimined/api/minecraft/PatchProviders.kt index 81a9932f..061bbb23 100644 --- a/src/api/kotlin/xyz/wagyourtail/unimined/api/minecraft/PatchProviders.kt +++ b/src/api/kotlin/xyz/wagyourtail/unimined/api/minecraft/PatchProviders.kt @@ -161,7 +161,7 @@ interface PatchProviders { expression = "minecraftForge(action)" ) ) - fun forge(action: ForgeLikePatcher.() -> Unit) + fun forge(action: ForgeLikePatcher<*>.() -> Unit) /** * enables the forge patcher. @@ -208,7 +208,7 @@ interface PatchProviders { * enables the minecraft forge patcher. * @since 1.0.0 */ - fun minecraftForge(action: MinecraftForgePatcher.() -> Unit) + fun minecraftForge(action: MinecraftForgePatcher<*>.() -> Unit) /** * enables the minecraft forge patcher. @@ -237,13 +237,13 @@ interface PatchProviders { /** * enables the NeoForged patcher. - * @since 1.0.0 + * @since 1.1.0 */ - fun neoForged(action: NeoForgedPatcher.() -> Unit) + fun neoForged(action: NeoForgedPatcher<*>.() -> Unit) /** * enables the NeoForged patcher. - * @since 1.0.0 + * @since 1.1.0 */ fun neoForged( @@ -261,7 +261,7 @@ interface PatchProviders { /** * enables the NeoForged patcher. - * @since 1.0.0 + * @since 1.1.0 */ fun neoForged() { neoForged {} diff --git a/src/api/kotlin/xyz/wagyourtail/unimined/api/minecraft/patch/ForgeLikePatcher.kt b/src/api/kotlin/xyz/wagyourtail/unimined/api/minecraft/patch/ForgeLikePatcher.kt index f0cb13e1..1a8ebb56 100644 --- a/src/api/kotlin/xyz/wagyourtail/unimined/api/minecraft/patch/ForgeLikePatcher.kt +++ b/src/api/kotlin/xyz/wagyourtail/unimined/api/minecraft/patch/ForgeLikePatcher.kt @@ -11,7 +11,11 @@ import java.nio.file.Path * The class responsible for patching minecraft for forge. * @since 0.2.3 */ -interface ForgeLikePatcher: JarModPatcher, AccessTransformablePatcher { +interface ForgeLikePatcher: JarModPatcher, AccessTransformablePatcher { + + + @get:ApiStatus.Internal + var forgeTransformer: T /** * location of access transformer file to apply to the minecraft jar. diff --git a/src/api/kotlin/xyz/wagyourtail/unimined/api/minecraft/patch/JarModAgentPatcher.kt b/src/api/kotlin/xyz/wagyourtail/unimined/api/minecraft/patch/JarModAgentPatcher.kt index 83fdbba1..71ebab02 100644 --- a/src/api/kotlin/xyz/wagyourtail/unimined/api/minecraft/patch/JarModAgentPatcher.kt +++ b/src/api/kotlin/xyz/wagyourtail/unimined/api/minecraft/patch/JarModAgentPatcher.kt @@ -34,4 +34,5 @@ interface JarModAgentPatcher : JarModPatcher { */ fun transforms(transforms: List) + fun agentVersion(vers: String) } \ No newline at end of file diff --git a/src/api/kotlin/xyz/wagyourtail/unimined/api/minecraft/patch/MinecraftForgePatcher.kt b/src/api/kotlin/xyz/wagyourtail/unimined/api/minecraft/patch/MinecraftForgePatcher.kt index 8897a8b1..a03372ee 100644 --- a/src/api/kotlin/xyz/wagyourtail/unimined/api/minecraft/patch/MinecraftForgePatcher.kt +++ b/src/api/kotlin/xyz/wagyourtail/unimined/api/minecraft/patch/MinecraftForgePatcher.kt @@ -1,4 +1,4 @@ package xyz.wagyourtail.unimined.api.minecraft.patch -interface MinecraftForgePatcher : ForgeLikePatcher { +interface MinecraftForgePatcher : ForgeLikePatcher { } \ No newline at end of file diff --git a/src/api/kotlin/xyz/wagyourtail/unimined/api/minecraft/patch/MinecraftPatcher.kt b/src/api/kotlin/xyz/wagyourtail/unimined/api/minecraft/patch/MinecraftPatcher.kt index 5e89558a..dd115bde 100644 --- a/src/api/kotlin/xyz/wagyourtail/unimined/api/minecraft/patch/MinecraftPatcher.kt +++ b/src/api/kotlin/xyz/wagyourtail/unimined/api/minecraft/patch/MinecraftPatcher.kt @@ -54,4 +54,7 @@ interface MinecraftPatcher { @get:ApiStatus.Internal @set:ApiStatus.Experimental var unprotectRuntime: Boolean + + @ApiStatus.Internal + fun configureRemapJar(task: RemapJarTask) } \ No newline at end of file diff --git a/src/api/kotlin/xyz/wagyourtail/unimined/api/minecraft/patch/NeoForgedPatcher.kt b/src/api/kotlin/xyz/wagyourtail/unimined/api/minecraft/patch/NeoForgedPatcher.kt index ba445d31..a5a1db9e 100644 --- a/src/api/kotlin/xyz/wagyourtail/unimined/api/minecraft/patch/NeoForgedPatcher.kt +++ b/src/api/kotlin/xyz/wagyourtail/unimined/api/minecraft/patch/NeoForgedPatcher.kt @@ -1,4 +1,4 @@ package xyz.wagyourtail.unimined.api.minecraft.patch -interface NeoForgedPatcher : ForgeLikePatcher { +interface NeoForgedPatcher : ForgeLikePatcher { } \ No newline at end of file diff --git a/src/api/kotlin/xyz/wagyourtail/unimined/api/mod/ModRemapConfig.kt b/src/api/kotlin/xyz/wagyourtail/unimined/api/mod/ModRemapConfig.kt index 2b821efc..af719fdd 100644 --- a/src/api/kotlin/xyz/wagyourtail/unimined/api/mod/ModRemapConfig.kt +++ b/src/api/kotlin/xyz/wagyourtail/unimined/api/mod/ModRemapConfig.kt @@ -1,9 +1,12 @@ package xyz.wagyourtail.unimined.api.mod +import groovy.lang.Closure +import groovy.lang.DelegatesTo import net.fabricmc.tinyremapper.TinyRemapper import org.gradle.api.artifacts.Configuration import org.jetbrains.annotations.ApiStatus import xyz.wagyourtail.unimined.api.mapping.MappingNamespaceTree +import xyz.wagyourtail.unimined.api.mapping.mixin.MixinRemapOptions import xyz.wagyourtail.unimined.util.FinalizeOnRead import xyz.wagyourtail.unimined.util.LazyMutable @@ -24,20 +27,26 @@ abstract class ModRemapConfig(val configurations: Set) { @set:ApiStatus.Experimental abstract var remapAtToLegacy: Boolean - @set:ApiStatus.Internal - var mixinRemap: MixinRemap by FinalizeOnRead(LazyMutable { MixinRemap.NONE }) - - fun mixinRemap(remap: String) { - mixinRemap = MixinRemap.valueOf(remap.uppercase()) + /** + * @since 1.1.0 + */ + abstract fun mixinRemap(action: MixinRemapOptions.() -> Unit) + + /** + * @since 1.1.0 + */ + fun mixinRemap( + @DelegatesTo(MixinRemapOptions::class, strategy = Closure.DELEGATE_FIRST) + action: Closure<*> + ) { + mixinRemap { + action.delegate = this + action.resolveStrategy = Closure.DELEGATE_FIRST + action.call() + } } + @ApiStatus.Experimental abstract fun remapper(remapperBuilder: TinyRemapper.Builder.() -> Unit) - - enum class MixinRemap { - NONE, - TINY_HARD, - TINY_HARDSOFT, - UNIMINED - } } \ No newline at end of file diff --git a/src/api/kotlin/xyz/wagyourtail/unimined/api/runs/RunConfig.kt b/src/api/kotlin/xyz/wagyourtail/unimined/api/runs/RunConfig.kt index 11ba5425..d6538270 100644 --- a/src/api/kotlin/xyz/wagyourtail/unimined/api/runs/RunConfig.kt +++ b/src/api/kotlin/xyz/wagyourtail/unimined/api/runs/RunConfig.kt @@ -20,7 +20,7 @@ import kotlin.io.path.relativeTo */ data class RunConfig( val project: Project, - val javaVersion: JavaVersion, + var javaVersion: JavaVersion?, // set to null to use whatever gradle/idea is using val name: String, val taskName: String, var description: String, @@ -34,77 +34,63 @@ data class RunConfig( var disabled : Boolean = false, ) { - fun copy(): RunConfig { - return RunConfig( - project, - javaVersion, - name, - taskName, - description, - launchClasspath, - mainClass, - args.toMutableList(), - jvmArgs.toMutableList(), - workingDir, - env.toMutableMap(), - runFirst.toMutableList(), - disabled - ) - } - fun createIdeaRunConfig() { val file = project.rootDir.resolve(".idea") .resolve("runConfigurations") .resolve("${if (project.path != ":") project.path.replace(":", "_") + "_" else ""}+${taskName.withSourceSet(launchClasspath)}.xml") - - val toolchain = project.extensions.getByType(JavaToolchainService::class.java) - val launcher = toolchain.launcherFor { spec -> - spec.languageVersion.set(JavaLanguageVersion.of(javaVersion.majorVersion)) - } - val configuration = XMLBuilder("configuration").addStringOption("default", "false") .addStringOption("name", "${project.path}+${launchClasspath.name} $description") .addStringOption("type", "Application") .addStringOption("factoryName", "Application") - .append( + + javaVersion?.let { jv -> + val toolchain = project.extensions.getByType(JavaToolchainService::class.java) + val launcher = toolchain.launcherFor { spec -> + spec.languageVersion.set(JavaLanguageVersion.of(jv.majorVersion)) + } + configuration.append( XMLBuilder("option").addStringOption("name", "ALTERNATIVE_JRE_PATH") .addStringOption("value", launcher.get().metadata.installationPath.asFile.absolutePath), XMLBuilder("option").addStringOption("name", "ALTERNATIVE_JRE_PATH_ENABLED") - .addStringOption("value", "true"), - XMLBuilder("envs").append( - *env.map { (key, value) -> - XMLBuilder("env").addStringOption("name", key).addStringOption("value", value) - }.toTypedArray() - ), - XMLBuilder("option").addStringOption("name", "MAIN_CLASS_NAME") - .addStringOption("value", mainClass), - XMLBuilder("module").addStringOption( - "name", - "${ - if (project != project.rootProject) "${project.rootProject.name}${ - project.path.replace( - ":", - "." - ) - }" else project.name - }.${launchClasspath.name}" - ), - XMLBuilder("option").addStringOption("name", "PROGRAM_PARAMETERS") - .addStringOption("value", args.joinToString(" ")), - XMLBuilder("option").addStringOption("name", "VM_PARAMETERS") - .addStringOption( - "value", - jvmArgs.joinToString(" ") { if (it.contains(" ")) ""$it"" else it }), - XMLBuilder("option").addStringOption("name", "WORKING_DIRECTORY") - .addStringOption( - "value", - "\$PROJECT_DIR\$/${ - workingDir.toPath() - .relativeTo(project.rootProject.projectDir.toPath()) - }" - ), + .addStringOption("value", "true") ) + } + + configuration.append( + XMLBuilder("envs").append( + *env.map { (key, value) -> + XMLBuilder("env").addStringOption("name", key).addStringOption("value", value) + }.toTypedArray() + ), + XMLBuilder("option").addStringOption("name", "MAIN_CLASS_NAME") + .addStringOption("value", mainClass), + XMLBuilder("module").addStringOption( + "name", + "${ + if (project != project.rootProject) "${project.rootProject.name}${ + project.path.replace( + ":", + "." + ) + }" else project.name + }.${launchClasspath.name}" + ), + XMLBuilder("option").addStringOption("name", "PROGRAM_PARAMETERS") + .addStringOption("value", args.joinToString(" ")), + XMLBuilder("option").addStringOption("name", "VM_PARAMETERS") + .addStringOption( + "value", + jvmArgs.joinToString(" ") { if (it.contains(" ")) ""$it"" else it }), + XMLBuilder("option").addStringOption("name", "WORKING_DIRECTORY") + .addStringOption( + "value", + "\$PROJECT_DIR\$/${ + workingDir.toPath() + .relativeTo(project.rootProject.projectDir.toPath()) + }" + ), + ) val mv2 = XMLBuilder("method") .addStringOption("v", "2") @@ -146,11 +132,13 @@ data class RunConfig( fun createGradleTask(tasks: TaskContainer, group: String): Task { return tasks.create(taskName.withSourceSet(launchClasspath), JavaExec::class.java) { - val toolchain = project.extensions.getByType(JavaToolchainService::class.java) - val launcher = toolchain.launcherFor { spec -> - spec.languageVersion.set(JavaLanguageVersion.of(javaVersion.majorVersion)) + javaVersion?.let { jv -> + val toolchain = project.extensions.getByType(JavaToolchainService::class.java) + val launcher = toolchain.launcherFor { spec -> + spec.languageVersion.set(JavaLanguageVersion.of(jv.majorVersion)) + } + it.javaLauncher.set(launcher) } - it.javaLauncher.set(launcher) it.environment.putAll(env) @@ -172,36 +160,4 @@ data class RunConfig( } } } - -// fun copy( -// project: Project = this.project, -// name: String = this.name, -// taskName: String = this.taskName, -// description: String = this.description, -// commonClasspath: SourceSet = this.commonClasspath, -// launchClasspath: SourceSet = this.launchClasspath, -// mainClass: String = this.mainClass, -// args: MutableList = this.args.toMutableList(), -// jvmArgs: MutableList = this.jvmArgs.toMutableList(), -// workingDir: File = this.workingDir, -// env: MutableMap = this.env.toMutableMap(), -// assetsDir: Path = this.assetsDir, -// runFirst: MutableList = this.runFirst.toMutableList(), -// ): LaunchConfig { -// return LaunchConfig( -// project, -// name, -// taskName, -// description, -// commonClasspath, -// launchClasspath, -// mainClass, -// args, -// jvmArgs, -// workingDir, -// env, -// assetsDir, -// runFirst, -// ) -// } } \ No newline at end of file diff --git a/src/api/kotlin/xyz/wagyourtail/unimined/api/task/RemapJarTask.kt b/src/api/kotlin/xyz/wagyourtail/unimined/api/task/RemapJarTask.kt index d1875a8b..e892ece6 100644 --- a/src/api/kotlin/xyz/wagyourtail/unimined/api/task/RemapJarTask.kt +++ b/src/api/kotlin/xyz/wagyourtail/unimined/api/task/RemapJarTask.kt @@ -1,5 +1,9 @@ package xyz.wagyourtail.unimined.api.task +import groovy.lang.Closure +import groovy.lang.DelegatesTo +import groovy.transform.stc.ClosureParams +import groovy.transform.stc.SimpleType import org.gradle.api.file.RegularFileProperty import org.gradle.api.provider.Property import org.gradle.api.tasks.Input @@ -9,6 +13,7 @@ import org.gradle.api.tasks.Optional import org.gradle.jvm.tasks.Jar import org.jetbrains.annotations.ApiStatus import xyz.wagyourtail.unimined.api.mapping.MappingNamespaceTree +import xyz.wagyourtail.unimined.api.mapping.mixin.MixinRemapOptions import xyz.wagyourtail.unimined.util.FinalizeOnRead /** @@ -51,8 +56,21 @@ abstract class RemapJarTask : Jar() { abstract fun prodNamespace(namespace: String) + abstract fun mixinRemap(action: MixinRemapOptions.() -> Unit) + + fun mixinRemap( + @DelegatesTo(value = MixinRemapOptions::class, strategy = Closure.DELEGATE_FIRST) + action: Closure<*> + ) { + mixinRemap { + action.delegate = this + action.resolveStrategy = Closure.DELEGATE_FIRST + action.call() + } + } + init { - remapATToLegacy.convention(null as Boolean?) + remapATToLegacy.convention(null as Boolean?).finalizeValueOnRead() } } \ No newline at end of file diff --git a/src/api/kotlin/xyz/wagyourtail/unimined/util/DefaultMap.kt b/src/api/kotlin/xyz/wagyourtail/unimined/util/DefaultMap.kt index d1ceab3e..d57029a4 100644 --- a/src/api/kotlin/xyz/wagyourtail/unimined/util/DefaultMap.kt +++ b/src/api/kotlin/xyz/wagyourtail/unimined/util/DefaultMap.kt @@ -16,4 +16,6 @@ class DefaultMap(val initializer: (T) -> U, val map: MutableMap = mu } -fun defaultedMapOf(initializer: (T) -> U): DefaultMap = DefaultMap(initializer) \ No newline at end of file +fun defaultedMapOf(initializer: (T) -> U): DefaultMap = DefaultMap(initializer) + +fun defaultedMapOf(map: MutableMap, initializer: (T) -> U): DefaultMap = DefaultMap(initializer, map) \ No newline at end of file diff --git a/src/api/kotlin/xyz/wagyourtail/unimined/util/FinalizeOnRead.kt b/src/api/kotlin/xyz/wagyourtail/unimined/util/FinalizeOnRead.kt index 6e20d0be..69ac9967 100644 --- a/src/api/kotlin/xyz/wagyourtail/unimined/util/FinalizeOnRead.kt +++ b/src/api/kotlin/xyz/wagyourtail/unimined/util/FinalizeOnRead.kt @@ -24,14 +24,11 @@ class FinalizeOnRead(value: T) : ReadWriteProperty { if (finalized) { throw IllegalStateException("Cannot set finalized property") } - setValueIntl(value) - } - - fun setValueIntl(value: T) { - if (finalized) { - throw IllegalStateException("Cannot set finalized property") + if (value is ReadWriteProperty<*, *>) { + (value as ReadWriteProperty).setValue(thisRef, property, value) + } else { + this.value = value } - this.value = value } fun setValueIntl(value: ReadWriteProperty) { diff --git a/src/api/kotlin/xyz/wagyourtail/unimined/util/Utils.kt b/src/api/kotlin/xyz/wagyourtail/unimined/util/Utils.kt index f9788dfb..5b4b3319 100644 --- a/src/api/kotlin/xyz/wagyourtail/unimined/util/Utils.kt +++ b/src/api/kotlin/xyz/wagyourtail/unimined/util/Utils.kt @@ -2,6 +2,7 @@ package xyz.wagyourtail.unimined.util +import org.apache.commons.compress.archivers.zip.ZipFile import org.gradle.api.Project import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.Dependency @@ -18,6 +19,7 @@ import java.security.MessageDigest import java.util.* import java.util.zip.ZipInputStream import java.util.zip.ZipOutputStream +import kotlin.collections.HashMap import kotlin.io.path.* import kotlin.reflect.KClass import kotlin.reflect.KProperty1 @@ -118,6 +120,16 @@ fun Path.getShortSha1(): String = getSha1().substring(0, 7) fun File.getShortSha1() = toPath().getShortSha1() +fun HashMap.getSha1(): String { + val digestSha1 = MessageDigest.getInstance("SHA-1") + digestSha1.update(toString().toByteArray()) + val hashBytes = digestSha1.digest() + return hashBytes.joinToString("") { String.format("%02x", it)} +} + +fun HashMap.getShortSha1(): String = getSha1().substring(0, 7) + + //fun runJarInSubprocess( // jar: Path, @@ -228,7 +240,7 @@ fun Path.forEachFile(action: (Path) -> Unit) { fun ByteArray.toHex() = joinToString(separator = "") { byte -> "%02x".format(byte) } -fun Optional.orElse(invoke: () -> Optional): Optional { +fun Optional.orElseOptional(invoke: () -> Optional): Optional { return if (isPresent) { this } else { @@ -251,6 +263,8 @@ fun String.withSourceSet(sourceSet: SourceSet) = fun String.decapitalized(): String = if (this.isEmpty()) this else this[0].lowercase() + this.substring(1) +fun String.capitalized(): String = if (this.isEmpty()) this else this[0].uppercase() + this.substring(1) + @Suppress("UNCHECKED_CAST") fun Map.nonNullValues(): Map = filterValues { it != null } as Map @@ -294,23 +308,18 @@ fun Path.forEachInZip(action: (String, InputStream) -> Unit) { } fun Path.readZipInputStreamFor(path: String, throwIfMissing: Boolean = true, action: (InputStream) -> T): T { - ZipInputStream(inputStream()).use { stream -> - var entry = stream.nextEntry - while (entry != null) { - if (entry.isDirectory) { - entry = stream.nextEntry - continue - } - if (entry.name == path) { - return action(stream) + Files.newByteChannel(this).use { + ZipFile(it).use { zip -> + val entry = zip.getEntry(path.replace("\\", "/")) + if (entry != null) { + return zip.getInputStream(entry).use(action) + } else { + if (throwIfMissing) { + throw IllegalArgumentException("Missing file $path in $this") + } } - entry = stream.nextEntry } } - if (throwIfMissing) { - throw IllegalArgumentException("Missing file $path in $this") - } - @Suppress("UNCHECKED_CAST") return null as T } diff --git a/src/main/kotlin/xyz/wagyourtail/unimined/UniminedExtensionImpl.kt b/src/main/kotlin/xyz/wagyourtail/unimined/UniminedExtensionImpl.kt index 1bc8fcd6..11736617 100644 --- a/src/main/kotlin/xyz/wagyourtail/unimined/UniminedExtensionImpl.kt +++ b/src/main/kotlin/xyz/wagyourtail/unimined/UniminedExtensionImpl.kt @@ -25,13 +25,23 @@ open class UniminedExtensionImpl(project: Project) : UniminedExtension(project) override val minecrafts = defaultedMapOf { MinecraftProvider(project, it) } - + override val minecraftConfiguration = mutableMapOf Unit>() override fun minecraft(sourceSet: SourceSet, lateApply: Boolean, action: MinecraftConfig.() -> Unit) { if (minecrafts.containsKey(sourceSet) && (minecrafts[sourceSet] as MinecraftProvider).applied) { throw IllegalStateException("minecraft config for ${sourceSet.name} already applied, cannot configure further!") } else if (!minecrafts.containsKey(sourceSet)) { project.logger.info("[Unimined] registering minecraft config for ${sourceSet.name}") } + minecraftConfiguration.compute(sourceSet) { _, old -> + if (old != null) { + { + old() + action() + } + } else { + action + } + } minecrafts[sourceSet].action() if (!lateApply) (minecrafts[sourceSet] as MinecraftProvider).apply() } @@ -97,7 +107,7 @@ open class UniminedExtensionImpl(project: Project) : UniminedExtension(project) if (info.extension == "pom") { return Artifact.none() } - project.logger.info("[Unimined/ArtifactProvider] providing ${info.classifier ?: "combined"} jar") + project.logger.info("[Unimined/ArtifactProvider] providing ${info.classifier ?: "combined"} jar: ${info}") return StreamableArtifact.ofFile( info, ArtifactType.BINARY, @@ -146,7 +156,7 @@ open class UniminedExtensionImpl(project: Project) : UniminedExtension(project) } override fun minecraftForgeMaven() { - project.logger.info("[Unimined] adding forge maven: $minecraftForgeMaven") + project.logger.info("[Unimined] adding Minecraft Forge maven: $minecraftForgeMaven") } val neoForgedMaven by lazy { @@ -161,7 +171,7 @@ open class UniminedExtensionImpl(project: Project) : UniminedExtension(project) } override fun neoForgedMaven() { - project.logger.info("[Unimined] adding neoForged maven: $neoForgedMaven") + project.logger.info("[Unimined] adding Neo-Forged maven: $neoForgedMaven") } val fabricMaven by lazy { @@ -172,7 +182,17 @@ open class UniminedExtensionImpl(project: Project) : UniminedExtension(project) } override fun fabricMaven() { - project.logger.info("[Unimined] adding fabric maven: $fabricMaven") + project.logger.info("[Unimined] adding Fabric maven: $fabricMaven") + } + + val ornitheMaven by lazy { + project.repositories.maven { + it.name = "ornithe" + it.url = URI.create("https://maven.ornithemc.net/releases") + } + } + override fun ornitheMaven() { + project.logger.info("[Unimined] adding Ornithe maven: $ornitheMaven") } val legacyFabricMaven by lazy { @@ -182,7 +202,7 @@ open class UniminedExtensionImpl(project: Project) : UniminedExtension(project) } } override fun legacyFabricMaven() { - project.logger.info("[Unimined] adding legacy fabric maven: $legacyFabricMaven") + project.logger.info("[Unimined] adding Legacy Fabric maven: $legacyFabricMaven") } val quiltMaven by lazy { @@ -193,18 +213,23 @@ open class UniminedExtensionImpl(project: Project) : UniminedExtension(project) } override fun quiltMaven() { - project.logger.info("[Unimined] adding quilt maven: $quiltMaven") + project.logger.info("[Unimined] adding Quilt maven: $quiltMaven") + } + + @Deprecated("Use glassLauncherMaven(\"babric\") instead", ReplaceWith("glassLauncherMaven(\"babric\")")) + override fun babricMaven() { + glassLauncherMaven("babric") } - val babricMaven by lazy { + val glassLauncherMaven = defaultedMapOf { name -> project.repositories.maven { - it.name = "babric" - it.url = URI.create("https://maven.glass-launcher.net/babric/") + it.name = "Glass (${name.capitalized()})" + it.url = URI.create("https://maven.glass-launcher.net/$name/") } } - override fun babricMaven() { - project.logger.info("[Unimined] adding babric maven: $babricMaven") + override fun glassLauncherMaven(name: String) { + project.logger.info("[Unimined] adding Glass Launcher maven: ${glassLauncherMaven[name]}") } val wagYourMaven = defaultedMapOf { name -> @@ -215,7 +240,7 @@ open class UniminedExtensionImpl(project: Project) : UniminedExtension(project) } override fun wagYourMaven(name: String) { - project.logger.info("[Unimined] adding wagyourtail maven: ${wagYourMaven[name]}") + project.logger.info("[Unimined] adding WagYourTail maven: ${wagYourMaven[name]}") } val mcphackersIvy by lazy { @@ -301,4 +326,4 @@ open class UniminedExtensionImpl(project: Project) : UniminedExtension(project) } } } -} \ No newline at end of file +} diff --git a/src/mapping/java/net/fabricmc/loom/util/kotlin/KotlinRemapperClassloader.java b/src/mapping/java/net/fabricmc/loom/util/kotlin/KotlinRemapperClassloader.java index 6c31d69b..d413d884 100644 --- a/src/mapping/java/net/fabricmc/loom/util/kotlin/KotlinRemapperClassloader.java +++ b/src/mapping/java/net/fabricmc/loom/util/kotlin/KotlinRemapperClassloader.java @@ -40,7 +40,7 @@ public class KotlinRemapperClassloader extends URLClassLoader { // Packages that should be loaded from the gradle plugin classloader. private static final List PARENT_PACKAGES = Arrays.asList( "net.fabricmc.tinyremapper", - "net.fabricmc.loom.util.kotlin", + "net.fabricmc.loom.util.kotlin.KotlinMetadataTinyRemapperExtension", "org.objectweb.asm", "org.slf4j" ); diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/MappingsProvider.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/MappingsProvider.kt index 60a5bb97..ef26a45a 100644 --- a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/MappingsProvider.kt +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/MappingsProvider.kt @@ -65,6 +65,19 @@ class MappingsProvider(project: Project, minecraft: MinecraftConfig): MappingsCo } } + override fun calamus(key: String, action: MappingDepConfig.() -> Unit) { + project.unimined.ornitheMaven() + val environment = when (side) { + EnvType.CLIENT -> "-client" + EnvType.SERVER -> "-server" + else -> "" + } + mapping("net.ornithemc:calamus-intermediary:${minecraft.version}${environment}:v2", key) { + outputs("intermediary", false) { listOf("official") } + action() + } + } + override fun legacyIntermediary(revision: Int, key: String, action: MappingDepConfig.() -> Unit) { project.unimined.legacyFabricMaven() if (legacyFabricMappingsVersionFinalize.value != revision) { @@ -87,7 +100,7 @@ class MappingsProvider(project: Project, minecraft: MinecraftConfig): MappingsCo } override fun babricIntermediary(key: String, action: MappingDepConfig.() -> Unit) { - project.unimined.babricMaven() + project.unimined.glassLauncherMaven("babric") if (side != EnvType.COMBINED) { mapping("babric:intermediary:${minecraft.version}:v2", key) { mapNamespace(side.classifier!!, "official") @@ -243,6 +256,22 @@ class MappingsProvider(project: Project, minecraft: MinecraftConfig): MappingsCo } } + override fun feather(build: Int, key: String, action: MappingDepConfig.() -> Unit) { + project.unimined.ornitheMaven() + val environment = when (side) { + EnvType.CLIENT -> "-client" + EnvType.SERVER -> "-server" + else -> "" + } + mapping("net.ornithemc:feather:${minecraft.version}${environment}+build.${build}:v2", key) { + outputs("yarn", true) { listOf("intermediary") } + mapNamespace("named", "yarn") + sourceNamespace("intermediary") + renest() + action() + } + } + override fun legacyYarn(build: Int, revision: Int, key: String, action: MappingDepConfig.() -> Unit) { project.unimined.legacyFabricMaven() if (legacyFabricMappingsVersionFinalize.value != revision) { @@ -267,7 +296,7 @@ class MappingsProvider(project: Project, minecraft: MinecraftConfig): MappingsCo } override fun barn(build: Int, key: String, action: MappingDepConfig.() -> Unit) { - project.unimined.babricMaven() + project.unimined.glassLauncherMaven("babric") mapping("babric:barn:${minecraft.version}+build.${build}:v2", "yarn") { outputs("barn", true) { listOf("intermediary") } mapNamespace("named", "barn") @@ -276,6 +305,16 @@ class MappingsProvider(project: Project, minecraft: MinecraftConfig): MappingsCo } } + override fun biny(commitName: String, key: String, action: MappingDepConfig.() -> Unit) { + project.unimined.glassLauncherMaven("releases") + mapping("net.glasslauncher:biny:${minecraft.version}+${commitName}:v2", "yarn") { + outputs("biny", true) { listOf("intermediary") } + mapNamespace("named", "biny") + sourceNamespace("intermediary") + action() + } + } + override fun quilt(build: Int, classifier: String, key: String, action: MappingDepConfig.() -> Unit) { project.unimined.quiltMaven() mapping("org.quiltmc:quilt-mappings:${minecraft.version}+build.${build}:${classifier}", "quilt") { @@ -641,4 +680,4 @@ class MappingsProvider(project: Project, minecraft: MinecraftConfig): MappingsCo } } } -} \ No newline at end of file +} diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/at/AccessTransformerMinecraftTransformer.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/at/AccessTransformerMinecraftTransformer.kt index 2d438b8c..600551ba 100644 --- a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/at/AccessTransformerMinecraftTransformer.kt +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/at/AccessTransformerMinecraftTransformer.kt @@ -5,11 +5,15 @@ import net.fabricmc.accesswidener.AccessWidenerWriter import net.fabricmc.mappingio.tree.MappingTreeView import net.fabricmc.tinyremapper.OutputConsumerPath import net.fabricmc.tinyremapper.TinyRemapper -import net.minecraftforge.accesstransformer.* -import net.minecraftforge.accesstransformer.AccessTransformer.Modifier -import net.minecraftforge.accesstransformer.Target -import net.minecraftforge.accesstransformer.parser.AccessTransformerList +import net.neoforged.accesstransformer.* +import net.neoforged.accesstransformer.AccessTransformer.Modifier +import net.neoforged.accesstransformer.Target +import net.neoforged.accesstransformer.parser.AccessTransformerList +import org.apache.commons.io.output.NullOutputStream +import org.gradle.api.Project +import org.gradle.api.logging.LogLevel import org.gradle.api.logging.Logger +import org.gradle.api.logging.configuration.ShowStacktrace import java.io.* import java.nio.charset.StandardCharsets import java.nio.file.Files @@ -303,18 +307,57 @@ object AccessTransformerMinecraftTransformer { } } - fun transform(accessTransformers: List, baseMinecraft: Path, output: Path) { + + fun shouldShowVerboseStdout(project: Project): Boolean { + // if running with INFO or DEBUG logging + return project.gradle.startParameter.logLevel < LogLevel.LIFECYCLE + } + + fun shouldShowVerboseStderr(project: Project): Boolean { + // if stdout is shown or stacktraces are visible so that errors printed to stderr show up + return shouldShowVerboseStdout(project) || project.gradle.startParameter.showStacktrace != ShowStacktrace.INTERNAL_EXCEPTIONS + } + + fun transform(project: Project, accessTransformers: List, baseMinecraft: Path, output: Path) { if (accessTransformers.isEmpty()) return if (output.exists()) output.deleteIfExists() output.parent.createDirectories() - val processJar = TransformerProcessor::class.java.getDeclaredMethod( - "processJar", - Path::class.java, - Path::class.java, - List::class.java - ) - processJar.isAccessible = true - processJar(null, baseMinecraft, output, accessTransformers) + val temp = output.resolveSibling(baseMinecraft.nameWithoutExtension + "-mergedATs.cfg") + temp.parent.createDirectories() + temp.deleteIfExists() + // merge at's + temp.bufferedWriter( + StandardCharsets.UTF_8, + 1024, + StandardOpenOption.CREATE, + StandardOpenOption.TRUNCATE_EXISTING + ).use { + for (at in accessTransformers) { + at.bufferedReader().use { reader -> + reader.copyTo(it) + it.write("\n") + } + } + } + val result = project.javaexec { spec -> + spec.classpath = project.configurations.detachedConfiguration(project.dependencies.create("net.neoforged:accesstransformers:9.0.3")) + spec.mainClass.set("net.neoforged.accesstransformer.TransformerProcessor") + spec.args = listOf("--inJar", baseMinecraft.absolutePathString(), "--outJar", output.absolutePathString(), "--atFile", temp.absolutePathString()) + if (shouldShowVerboseStdout(project)) { + spec.standardOutput = System.out + } else { + spec.standardOutput = NullOutputStream.NULL_OUTPUT_STREAM + } + if (shouldShowVerboseStderr(project)) { + spec.errorOutput = System.err + } else { + spec.errorOutput = NullOutputStream.NULL_OUTPUT_STREAM + } + } + if (result.exitValue != 0) { + output.deleteIfExists() + throw IllegalStateException("Failed to run AccessTransformerProcessor") + } } fun aw2at(aw: Path, output: Path, legacy: Boolean = false): Path { diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/ArrayVisitorWrapper.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/ArrayVisitorWrapper.kt new file mode 100644 index 00000000..21e362fc --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/ArrayVisitorWrapper.kt @@ -0,0 +1,13 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension + +import org.objectweb.asm.AnnotationVisitor + +class ArrayVisitorWrapper( + val api: Int, + delegate: AnnotationVisitor?, + val delegateCreator: (AnnotationVisitor?) -> AnnotationVisitor +): AnnotationVisitor(api, delegate) { + override fun visitAnnotation(name: String?, descriptor: String): AnnotationVisitor { + return delegateCreator(super.visitAnnotation(name, descriptor)) + } +} \ No newline at end of file diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/Common.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/Common.kt new file mode 100644 index 00000000..097ae876 --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/Common.kt @@ -0,0 +1,13 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension + +fun splitMethodNameAndDescriptor(nameAndDescriptor: String): Pair { + val mName = nameAndDescriptor.substringBefore("(").substringAfter(";") + val desc = if (nameAndDescriptor.contains("(")) "(${nameAndDescriptor.substringAfter("(")}" else null + return Pair(mName, desc) +} + +fun splitFieldNameAndDescriptor(nameAndDescriptor: String): Pair { + val fName = nameAndDescriptor.substringBefore(":").substringAfter(";") + val desc = if (nameAndDescriptor.contains(":")) nameAndDescriptor.substringAfter(":") else null + return Pair(fName, desc) +} \ No newline at end of file diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/MixinRemapExtension.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/MixinRemapExtension.kt new file mode 100644 index 00000000..dab4ef6c --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/MixinRemapExtension.kt @@ -0,0 +1,303 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension + +import com.google.gson.JsonObject +import net.fabricmc.tinyremapper.InputTag +import net.fabricmc.tinyremapper.TinyRemapper +import net.fabricmc.tinyremapper.api.TrClass +import net.fabricmc.tinyremapper.api.TrEnvironment +import net.fabricmc.tinyremapper.extension.mixin.common.Logger +import net.fabricmc.tinyremapper.extension.mixin.common.data.Annotation +import net.fabricmc.tinyremapper.extension.mixin.common.data.CommonData +import net.fabricmc.tinyremapper.extension.mixin.common.data.Constant +import org.gradle.api.logging.LogLevel +import org.jetbrains.annotations.ApiStatus +import org.objectweb.asm.AnnotationVisitor +import org.objectweb.asm.ClassVisitor +import xyz.wagyourtail.unimined.api.mapping.mixin.MixinRemapOptions +import xyz.wagyourtail.unimined.internal.mapping.extension.jma.hard.JMAHard +import xyz.wagyourtail.unimined.internal.mapping.extension.jma.refmap.JMARefmap +import xyz.wagyourtail.unimined.internal.mapping.extension.jma.JarModAgentMetaData +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.OfficialMixinMetaData +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.hard.BaseMixinHard +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.hard.HardTargetRemappingClassVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.BaseMixinRefmap +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.RefmapBuilderClassVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixinextra.MixinExtra +import xyz.wagyourtail.unimined.util.DefaultMap +import xyz.wagyourtail.unimined.util.FinalizeOnRead +import xyz.wagyourtail.unimined.util.defaultedMapOf +import java.nio.file.FileSystem +import java.nio.file.Path +import java.util.* +import java.util.concurrent.CompletableFuture +import java.util.concurrent.ConcurrentLinkedDeque + +class MixinRemapExtension( + loggerLevel: LogLevel = LogLevel.WARN, + allowImplicitWildcards: Boolean = false, +) : PerInputTagExtension(), MixinRemapOptions { + + val allowImplicitWildcards by FinalizeOnRead(allowImplicitWildcards) + + override fun register(tag: InputTag): MixinTarget { + return MixinTarget(tag, this).apply { + metadataReader.forEach { + this.addMetadata(it(this@MixinRemapExtension)) + } + } + } + + private var metadataReader = mutableListOf<(MixinRemapExtension) -> MixinMetadata>() + private var modifyHardRemapper: (HardTargetRemappingClassVisitor) -> Unit = {} + private var modifyRefmapBuilder: (RefmapBuilderClassVisitor) -> Unit = {} + + + val logger: Logger = Logger( + translateLogLevel( + loggerLevel + ) + ) + + var off by FinalizeOnRead(false) + var noRefmap: Set by FinalizeOnRead(setOf()) + + @ApiStatus.Internal + fun modifyMetadataReader(modifier: (MixinRemapExtension) -> MixinMetadata) { + metadataReader.add(modifier) + } + + @ApiStatus.Internal + fun modifyHardRemapper(modifier: (HardTargetRemappingClassVisitor) -> Unit) { + val old = modifyHardRemapper + modifyHardRemapper = { old(it); modifier(it) } + } + + @ApiStatus.Internal + fun modifyRefmapBuilder(modifier: (RefmapBuilderClassVisitor) -> Unit) { + val old = modifyRefmapBuilder + modifyRefmapBuilder = { old(it); modifier(it) } + } + + override fun off() { + reset() + off = true + } + + override fun disableRefmap() { + disableRefmap(listOf("BaseMixin", "JarModAgent")) + } + + override fun disableRefmap(keys: List) { + noRefmap = keys.toSet() + } + + override fun enableMixinExtra() { + modifyRefmapBuilder(MixinExtra::refmapBuilder) + } + + override fun enableBaseMixin() { + modifyMetadataReader(::OfficialMixinMetaData) + modifyHardRemapper(BaseMixinHard::hardRemapper) + modifyRefmapBuilder(BaseMixinRefmap::refmapBuilder) + } + + override fun enableJarModAgent() { + modifyMetadataReader(::JarModAgentMetaData) + modifyHardRemapper(JMAHard::hardRemapper) + modifyRefmapBuilder(JMARefmap::refmapBuilder) + } + + override fun resetMetadataReader() { + metadataReader = mutableListOf() + } + + override fun resetHardRemapper() { + modifyHardRemapper = {} + } + + override fun resetRefmapBuilder() { + modifyRefmapBuilder = {} + } + + + override fun reset() { + off = false + noRefmap = setOf() + resetMetadataReader() + resetHardRemapper() + resetRefmapBuilder() + } + + + open class MixinTarget(override val inputTag: InputTag, val extension: MixinRemapExtension) : InputTagExtension { + protected val metadata: MergedMetadata = MergedMetadata(extension) + protected val tasks: DefaultMap Unit>> = defaultedMapOf { ConcurrentLinkedDeque() } + + fun addMetadata(metadata: MixinMetadata) { + this.metadata.addMetadata(metadata) + } + + override fun readInput(remapper: TinyRemapper, vararg input: Path): CompletableFuture<*> { + return metadata.readInput(*input).thenComposeAsync { super.readInput(remapper, *input) } + } + + override fun analyzeVisitor(mrjVersion: Int, className: String, next: ClassVisitor): ClassVisitor { + try { + return if (metadata.contains(dot(className))) { + val refmap = metadata.getExistingRefmapFor(dot(className)) + val existing = refmap?.get("mappings")?.asJsonObject?.get(className)?.asJsonObject ?: JsonObject() + val mappings = mutableMapOf() + existing.entrySet().forEach { + mappings[it.key] = it.value.asString + } + extension.logger.info("[HardTarget] found mixin class $className") + if (mappings.isNotEmpty()) { + extension.logger.info("[HardTarget] existing mappings $mappings") + } + val visitor = HardTargetRemappingClassVisitor( + next, + className, + mappings, + extension.logger + ) + extension.modifyHardRemapper(visitor) + synchronized(tasks) { + tasks[mrjVersion].add(visitor::runRemap) + } + visitor + } else { + return next + } + } catch (e: Exception) { + throw IllegalStateException("Error while processing class $className: ${e.javaClass.simpleName}: ${e.message}", e) + } + } + + override fun stateProcessor(environment: TrEnvironment) { + extension.logger.info("[HardTarget] processing state for ${environment.mrjVersion}") + val data = CommonData(environment, extension.logger) + try { + for (task in tasks[environment.mrjVersion]) { + task(data) + } + } catch (e: Exception) { + throw IllegalStateException("Error while processing state for ${environment.mrjVersion}: ${e.javaClass.simpleName}: ${e.message}", e) + } + } + + override fun preApplyVisitor(cls: TrClass, next: ClassVisitor): ClassVisitor { + try { + return if (metadata.contains(dot(cls.name))) { + val refmap = metadata.getExistingRefmapFor(dot(cls.name)) + val existing = refmap?.get("mappings")?.asJsonObject?.get(cls.name)?.asJsonObject ?: JsonObject() + val mappings = mutableMapOf() + existing.entrySet().forEach { + mappings[it.key] = it.value.asString + } + extension.logger.info("[RefmapBuilder] found mixin class ${cls.name}") + if (mappings.isNotEmpty()) { + extension.logger.info("[RefmapBuilder] existing mappings $mappings") + } + val target = JsonObject() + val visitor = RefmapBuilderClassVisitor( + CommonData(cls.environment, extension.logger), + cls.name, + target, + next, + mappings, + extension, + onEnd = { + if (target.size() > 0) { + extension.logger.info("[RefmapBuilder] adding ${target.size()} mappings for ${cls.name}") + val refmapJson = metadata.getRefmapFor(dot(cls.name)) + if (!refmapJson.has("mappings")) { + refmapJson.add("mappings", JsonObject()) + } + val refmapMappings = refmapJson.get("mappings").asJsonObject + refmapMappings.add(cls.name, target) + } + }, + allowImplicitWildcards = extension.allowImplicitWildcards + ) + extension.modifyRefmapBuilder(visitor) + visitor + } else { + if (!extension.off) { + object : ClassVisitor(Constant.ASM_VERSION, next) { + override fun visitAnnotation(descriptor: String?, visible: Boolean): AnnotationVisitor? { + if (descriptor == Annotation.MIXIN) { + extension.logger.warn("[RefmapBuilder] found mixin class ${cls.name} without entry!") + } + return super.visitAnnotation(descriptor, visible) + } + } + } else { + next + } + } + } catch (e: Exception) { + throw IllegalStateException("Error while processing class ${cls.name}: ${e.javaClass.simpleName}: ${e.message}", e) + } + } + + override fun insertExtra(fs: FileSystem) { + metadata.writeExtra(fs) + } + + } + + abstract class MixinMetadata(val parent: MixinRemapExtension) { + + abstract fun readInput(vararg input: Path): CompletableFuture<*> + + abstract fun contains(className: String): Boolean + abstract fun getRefmapFor(className: String): JsonObject + + abstract fun writeExtra(fs: FileSystem) + abstract fun getExistingRefmapFor(className: String): JsonObject? + } + + class MergedMetadata(parent: MixinRemapExtension) : MixinMetadata(parent) { + val metadata: MutableSet = mutableSetOf() + + fun addMetadata(metadata: MixinMetadata) { + this.metadata.add(metadata) + } + + override fun readInput(vararg input: Path): CompletableFuture<*> { + return CompletableFuture.allOf(*metadata.map { it.readInput(*input) }.toTypedArray()) + } + + override fun contains(className: String): Boolean { + return metadata.any { it.contains(className) } + } + + override fun getRefmapFor(className: String): JsonObject { + return metadata.first { it.contains(className) }.getRefmapFor(className) + } + + override fun writeExtra(fs: FileSystem) { + metadata.forEach { it.writeExtra(fs) } + } + + override fun getExistingRefmapFor(className: String): JsonObject? { + return metadata.firstOrNull { it.contains(className) }?.getExistingRefmapFor(className) + } + + } + + companion object { + fun dot(name: String): String = name.replace('/', '.') + + + fun translateLogLevel(loggerLevel: LogLevel) = when (loggerLevel) { + LogLevel.DEBUG -> Logger.Level.INFO + LogLevel.INFO -> Logger.Level.INFO + LogLevel.WARN -> Logger.Level.WARN + LogLevel.ERROR -> Logger.Level.ERROR + LogLevel.QUIET -> Logger.Level.ERROR + else -> Logger.Level.WARN + } + } + +} \ No newline at end of file diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/PerInputTagExtension.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/PerInputTagExtension.kt new file mode 100644 index 00000000..acc1b82a --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/PerInputTagExtension.kt @@ -0,0 +1,106 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension + +import net.fabricmc.tinyremapper.ClassInstance +import net.fabricmc.tinyremapper.InputTag +import net.fabricmc.tinyremapper.TinyRemapper +import net.fabricmc.tinyremapper.api.TrClass +import net.fabricmc.tinyremapper.api.TrEnvironment +import org.objectweb.asm.ClassVisitor +import xyz.wagyourtail.unimined.util.defaultedMapOf +import java.nio.file.FileSystem +import java.nio.file.Path +import java.util.concurrent.CompletableFuture + +abstract class PerInputTagExtension : TinyRemapper.Extension { + object SKIP + + companion object { + fun getInputTag(cls: TrClass): List? { + if (cls !is ClassInstance) return null + // InputTag[] getInputTags() + ClassInstance::class.java.getDeclaredMethod("getInputTags").apply { + isAccessible = true + val arr = (invoke(cls) as Array?) ?: return null + return arr.toList() + } + } + } + + private val inputTagExtensions: MutableMap = defaultedMapOf { + if (it == SKIP) object : InputTagExtension { + override val inputTag: InputTag? = null + } + else register(it as InputTag) + } + + protected abstract fun register(tag: InputTag): T + + override fun attach(builder: TinyRemapper.Builder) { + builder.extraAnalyzeVisitor(::analyzeVisitor) + builder.extraStateProcessor(::stateProcessor) + builder.extraPreApplyVisitor(::preApplyVisitor) + builder.extraPostApplyVisitor(::postApplyVisitor) + } + + fun readInput(remapper: TinyRemapper, tag: InputTag?, vararg input: Path): CompletableFuture<*> { + return inputTagExtensions[tag]!!.readInput(remapper, *input) + } + + fun readClassPath(remapper: TinyRemapper, vararg classpath: Path): CompletableFuture<*> { + return remapper.readClassPathAsync(*classpath) +// return CompletableFuture.completedFuture(null) + } + + fun Iterable.reduce(identity: U, reducer: T.(U) -> U): U { + var acc = identity + val iter = iterator() + while (iter.hasNext()) { + acc = iter.next().reducer(acc) + } + return acc + } + + private fun analyzeVisitor(mrjVersion: Int, className: String, next: ClassVisitor): ClassVisitor { + return inputTagExtensions.values.toList().reduce(next) { ni -> analyzeVisitor(mrjVersion, className, ni) } + } + + private fun stateProcessor(environment: TrEnvironment) { + for (extension in inputTagExtensions.values) { + extension.stateProcessor(environment) + } + } + + private fun preApplyVisitor(cls: TrClass, next: ClassVisitor): ClassVisitor { + val tags = getInputTag(cls) + return tags?.reduce(next) { ni -> inputTagExtensions[this]!!.preApplyVisitor(cls, ni) } + ?: inputTagExtensions[SKIP]!!.preApplyVisitor(cls, next) + } + + private fun postApplyVisitor(cls: TrClass, next: ClassVisitor): ClassVisitor { + val tags = getInputTag(cls) + return tags?.reduce(next) { ni -> inputTagExtensions[this]!!.postApplyVisitor(cls, ni) } + ?: inputTagExtensions[SKIP]!!.postApplyVisitor(cls, next) + } + + fun insertExtra(tag: InputTag, fs: FileSystem) { + inputTagExtensions[tag]!!.insertExtra(fs) + } + + interface InputTagExtension { + + val inputTag: InputTag? + + fun readInput(remapper: TinyRemapper, vararg input: Path): CompletableFuture<*> { + return remapper.readInputsAsync(inputTag, *input) + } + + fun analyzeVisitor(mrjVersion: Int, className: String, next: ClassVisitor): ClassVisitor { return next; } + fun stateProcessor(environment: TrEnvironment) {} + fun preApplyVisitor(cls: TrClass, next: ClassVisitor): ClassVisitor { return next; } + fun postApplyVisitor(cls: TrClass, next: ClassVisitor): ClassVisitor { return next; } + + fun insertExtra(fs: FileSystem) {} + + } + +} diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/DontRemapAnnotationVisitor.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/DontRemapAnnotationVisitor.kt new file mode 100644 index 00000000..16890a18 --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/DontRemapAnnotationVisitor.kt @@ -0,0 +1,299 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension.jma + +import net.fabricmc.tinyremapper.extension.mixin.common.data.Constant +import org.objectweb.asm.* +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.hard.HardTargetRemappingClassVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.RefmapBuilderClassVisitor + + +class DontRemapAnnotationVisitor(api: Int, parent: AnnotationVisitor?, val onEnd: (DontRemapAnnotationVisitor) -> Unit) : + AnnotationVisitor(api, parent) { + + companion object { + fun shouldVisitHardClass( + descriptor: String, + visible: Boolean, + hardTargetRemapper: HardTargetRemappingClassVisitor + ): Boolean { + return descriptor == JarModAgent.Annotation.DONTREMAP + } + + fun shouldVisitHardMethod( + descriptor: String, + visible: Boolean, + methodAccess: Int, + methodName: String, + methodDescriptor: String, + methodSignature: String?, + methodExceptions: Array?, + hardTargetRemapper: HardTargetRemappingClassVisitor + ): Boolean { + return descriptor == JarModAgent.Annotation.DONTREMAP + } + + fun shouldVisitHardField( + descriptor: String, + visible: Boolean, + access: Int, + fieldName: String, + fieldDescriptor: String, + fieldSignature: String?, + fieldValue: Any?, + hardTargetRemapper: HardTargetRemappingClassVisitor + ): Boolean { + return descriptor == JarModAgent.Annotation.DONTREMAP + } + + fun shouldVisitSoftClass( + descriptor: String, + visible: Boolean, + hardTargetRemapper: RefmapBuilderClassVisitor + ): Boolean { + return descriptor == JarModAgent.Annotation.DONTREMAP + } + + fun shouldVisitSoftMethod( + descriptor: String, + visible: Boolean, + methodAccess: Int, + methodName: String, + methodDescriptor: String, + methodSignature: String?, + methodExceptions: Array?, + hardTargetRemapper: RefmapBuilderClassVisitor + ): Boolean { + return descriptor == JarModAgent.Annotation.DONTREMAP + } + + fun shouldVisitSoftField( + descriptor: String, + visible: Boolean, + access: Int, + fieldName: String, + fieldDescriptor: String, + fieldSignature: String?, + fieldValue: Any?, + hardTargetRemapper: RefmapBuilderClassVisitor + ): Boolean { + return descriptor == JarModAgent.Annotation.DONTREMAP + } + + fun visitHardClass( + descriptor: String, + visible: Boolean, + parent: AnnotationVisitor?, + hardTargetRemapper: HardTargetRemappingClassVisitor + ): AnnotationVisitor { + return DontRemapAnnotationVisitor(Constant.ASM_VERSION, parent) { + hardTargetRemapper.dontRemap = it + } + } + + fun visitHardMethod( + descriptor: String, + visible: Boolean, + parent: AnnotationVisitor?, + methodAccess: Int, + methodName: String, + methodDescriptor: String, + methodSignature: String?, + methodExceptions: Array?, + hardTargetRemapper: HardTargetRemappingClassVisitor, + ): AnnotationVisitor { + return DontRemapAnnotationVisitor(Constant.ASM_VERSION, parent) { + hardTargetRemapper.dontRemap = it + } + } + + fun visitHardField( + descriptor: String, + visible: Boolean, + parent: AnnotationVisitor?, + fieldAccess: Int, + fieldName: String, + fieldDescriptor: String, + fieldSignature: String?, + fieldValue: Any?, + hardTargetRemapper: HardTargetRemappingClassVisitor + ): AnnotationVisitor { + return DontRemapAnnotationVisitor(Constant.ASM_VERSION, parent) { + hardTargetRemapper.dontRemap = it + } + } + + fun visitSoftClass( + descriptor: String, + visible: Boolean, + parent: AnnotationVisitor?, + hardTargetRemapper: RefmapBuilderClassVisitor + ): AnnotationVisitor { + return DontRemapAnnotationVisitor(Constant.ASM_VERSION, parent) { + hardTargetRemapper.dontRemap = it + } + } + + fun visitSoftMethod( + descriptor: String, + visible: Boolean, + parent: AnnotationVisitor, + methodAccess: Int, + methodName: String, + methodDescriptor: String, + methodSignature: String?, + methodExceptions: Array?, + hardTargetRemapper: RefmapBuilderClassVisitor + ): AnnotationVisitor { + return DontRemapAnnotationVisitor(Constant.ASM_VERSION, parent) { + hardTargetRemapper.dontRemap = it + } + } + + fun visitSoftField( + descriptor: String, + visible: Boolean, + parent: AnnotationVisitor, + fieldAccess: Int, + fieldName: String, + fieldDescriptor: String, + fieldSignature: String?, + fieldValue: Any?, + hardTargetRemapper: RefmapBuilderClassVisitor + ): AnnotationVisitor { + return DontRemapAnnotationVisitor(Constant.ASM_VERSION, parent) { + hardTargetRemapper.dontRemap = it + } + } + + + } + + + var dontRemap: MutableList = ArrayList() + var skip = false + + override fun visit(name: String, value: Any) { + if (name == "skip") { + skip = value as Boolean + } + super.visit(name, value) + } + + override fun visitArray(name: String?): AnnotationVisitor? { + return if (name == "value" || name == null) { + object : AnnotationVisitor(api, super.visitArray(name)) { + override fun visit(name: String?, value: Any) { + dontRemap.add(value as Type) + super.visit(name, value) + } + } + } else super.visitArray(name) + } + + override fun visitEnd() { + onEnd(this) + super.visitEnd() + } + + class DontRemapClassVisitor(api: Int, parent: ClassVisitor, val extraData: MutableMap) : ClassVisitor(api, parent) { + + override fun visitAnnotation(descriptor: String, visible: Boolean): AnnotationVisitor? { + if (descriptor == JarModAgent.Annotation.DONTREMAP) return super.visitAnnotation(descriptor, visible) + return object : AnnotationVisitor(api, super.visitAnnotation(descriptor, visible)) { + + override fun visitEnd() { + if ((extraData["dontRemap"] as DontRemapAnnotationVisitor?)?.skip != true) { + extraData.remove("dontRemap") + } + super.visitEnd() + } + } + } + + override fun visitMethod( + access: Int, + name: String?, + descriptor: String?, + signature: String?, + exceptions: Array? + ): MethodVisitor { + return object : MethodVisitor(api, super.visitMethod(access, name, descriptor, signature, exceptions)) { + override fun visitAnnotation(descriptor: String?, visible: Boolean): AnnotationVisitor? { + if (descriptor == JarModAgent.Annotation.DONTREMAP) return super.visitAnnotation(descriptor, visible) + return object : AnnotationVisitor(api, super.visitAnnotation(descriptor, visible)) { + + override fun visitEnd() { + if ((extraData["dontRemap"] as DontRemapAnnotationVisitor?)?.skip != true) { + extraData.remove("dontRemap") + } + super.visitEnd() + } + } + } + + override fun visitEnd() { + if ((extraData["dontRemap"] as DontRemapAnnotationVisitor?)?.skip != true) { + extraData.remove("dontRemap") + } + super.visitEnd() + } + } + } + + override fun visitField( + access: Int, + name: String?, + descriptor: String?, + signature: String?, + value: Any? + ): FieldVisitor { + + return object : FieldVisitor(api, super.visitField(access, name, descriptor, signature, value)) { + override fun visitAnnotation(descriptor: String?, visible: Boolean): AnnotationVisitor? { + if (descriptor == JarModAgent.Annotation.DONTREMAP) return super.visitAnnotation(descriptor, visible) + return object : AnnotationVisitor(api, super.visitAnnotation(descriptor, visible)) { + + override fun visitEnd() { + if ((extraData["dontRemap"] as DontRemapAnnotationVisitor?)?.skip != true) { + extraData.remove("dontRemap") + } + super.visitEnd() + } + } + } + + override fun visitEnd() { + if ((extraData["dontRemap"] as DontRemapAnnotationVisitor?)?.skip != true) { + extraData.remove("dontRemap") + } + super.visitEnd() + } + } + + } + + } +} + +var HardTargetRemappingClassVisitor.dontRemap: DontRemapAnnotationVisitor? + get() = this.extraData["dontRemap"] as DontRemapAnnotationVisitor? + set(value) { + if (value == null) throw NullPointerException() + else this.extraData["dontRemap"] = value + } + +var RefmapBuilderClassVisitor.dontRemap: DontRemapAnnotationVisitor? + get() = this.extraData["dontRemap"] as DontRemapAnnotationVisitor? + set(value) { + if (value == null) throw NullPointerException() + else this.extraData["dontRemap"] = value + } + +fun HardTargetRemappingClassVisitor.dontRemap(annDesc: String): Boolean { + if (dontRemap == null) return false + return dontRemap!!.skip || dontRemap!!.dontRemap.contains(Type.getType(annDesc)) +} + +fun RefmapBuilderClassVisitor.dontRemap(annDesc: String): Boolean { + if (dontRemap == null) return false + return dontRemap!!.skip || dontRemap!!.dontRemap.contains(Type.getType(annDesc)) +} \ No newline at end of file diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/JarModAgent.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/JarModAgent.kt new file mode 100644 index 00000000..e6bf3114 --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/JarModAgent.kt @@ -0,0 +1,26 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension.jma + +object JarModAgent { + + object AnnotationElement { + + const val NAME = "name" + + } + + object Annotation { + + const val CTRANSFORMER = "Lnet/lenni0451/classtransform/annotations/CTransformer;" + const val CSHADOW = "Lnet/lenni0451/classtransform/annotations/CShadow;" + const val CTARGET = "Lnet/lenni0451/classtransform/annotations/CTarget;" + + const val CINJECT = "Lnet/lenni0451/classtransform/annotations/injection/CInject;" + const val CMODIFYCONSTANT = "Lnet/lenni0451/classtransform/annotations/injection/CModifyConstant;" + const val COVERRIDE = "Lnet/lenni0451/classtransform/annotations/injection/COverride;" + const val CREDIRECT = "Lnet/lenni0451/classtransform/annotations/injection/CRedirect;" + const val CWRAPCATCH = "Lnet/lenni0451/classtransform/annotations/injection/CWrapCatch;" + + const val DONTREMAP = "Lxyz/wagyourtail/unimined/jarmodagent/transformer/annotation/DontRemap;" + } + +} \ No newline at end of file diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/JarModAgentMetaData.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/JarModAgentMetaData.kt new file mode 100644 index 00000000..6e13bd6e --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/JarModAgentMetaData.kt @@ -0,0 +1,119 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension.jma + +import com.google.gson.GsonBuilder +import com.google.gson.JsonObject +import com.google.gson.JsonParser +import xyz.wagyourtail.unimined.internal.mapping.extension.MixinRemapExtension +import xyz.wagyourtail.unimined.util.readZipInputStreamFor +import java.nio.file.FileSystem +import java.nio.file.Path +import java.nio.file.StandardOpenOption +import java.util.concurrent.CompletableFuture +import java.util.jar.Manifest +import kotlin.io.path.inputStream +import kotlin.io.path.outputStream +import kotlin.io.path.writeText + +class JarModAgentMetaData(parent: MixinRemapExtension) : MixinRemapExtension.MixinMetadata(parent) { + private val GSON = GsonBuilder().setPrettyPrinting().create() + private val classesToRefmap = mutableMapOf() + private val refmaps = mutableMapOf() + private val existingRefmaps = mutableMapOf() + + override fun readInput(vararg input: Path): CompletableFuture<*> { + val futures = mutableListOf>() + for (i in input) { + futures.add(readSingleInput(i)) + } + return CompletableFuture.allOf(*futures.toTypedArray()) + } + + private fun readSingleInput(input: Path): CompletableFuture<*> { + return CompletableFuture.runAsync { + val transforms = mutableListOf() + val refmaps = mutableListOf() + input.readZipInputStreamFor("META-INF/MANIFEST.MF", false) { + val manifest = Manifest(it) + val jarmodTransforms = manifest.mainAttributes.getValue("JarModAgent-Transforms") + val jarmodRefmaps = manifest.mainAttributes.getValue("JarModAgent-Refmaps") + if (jarmodTransforms != null) { + transforms.addAll(jarmodTransforms.split(" ")) + } + if (jarmodRefmaps != null) { + refmaps.addAll(jarmodRefmaps.split(" ")) + } + } + parent.logger.info("[PreRead] Found JarModAgent transforms: $transforms") + parent.logger.info("[PreRead] Found JarModAgent refmaps: $refmaps") + for (transform in transforms) { + val refmapName = transform.substringBeforeLast(".") + "-refmap.json" + this.refmaps.computeIfAbsent(refmapName) { JsonObject() } + input.readZipInputStreamFor(transform, false) { + it.bufferedReader().use { reader -> + for (line in reader.lines()) { + if (classesToRefmap.containsKey(line) && classesToRefmap[line] != refmapName) { + parent.logger.warn("[PreRead] $line already has a refmap entry!") + parent.logger.warn("[PreRead] ${classesToRefmap[line]} != $refmapName") + parent.logger.warn("[PreRead] Will only read/write to ${classesToRefmap[line]} for $line") + continue + } + classesToRefmap[line] = refmapName + parent.logger.info("[PreRead] Added $line to $refmapName") + } + } + } + } + for (refmap in refmaps) { + input.readZipInputStreamFor(refmap, false) { + try { + val json = JsonParser.parseReader(it.reader()).asJsonObject + this.existingRefmaps[refmap] = json + parent.logger.info("[PreRead] Found refmap $refmap") + } catch (e: Exception) { + parent.logger.error("[PreRead] Failed to parse refmap $refmap: ${e.message}") + } + } + } + + } + } + + override fun contains(className: String): Boolean { + return classesToRefmap.containsKey(className) + } + + override fun getRefmapFor(className: String): JsonObject { + return refmaps[classesToRefmap[className]]!! + } + + override fun getExistingRefmapFor(className: String): JsonObject? { + return existingRefmaps[classesToRefmap[className]] + } + + override fun writeExtra(fs: FileSystem) { + if (refmaps.isNotEmpty()) { + val manifest = Manifest(fs.getPath("META-INF/MANIFEST.MF").inputStream()) + + if (!parent.noRefmap.contains("JarModAgent")) { + manifest.mainAttributes.putValue("JarModAgent-Refmaps", refmaps.keys.joinToString(" ")) + } else { + manifest.mainAttributes.remove("JarModAgent-Refmaps") + } + + fs.getPath("META-INF/MANIFEST.MF") + .outputStream(StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE).use { + manifest.write(it) + } + + for ((name, json) in refmaps) { + fs.getPath(name).writeText( + GSON.toJson(json), + Charsets.UTF_8, + StandardOpenOption.TRUNCATE_EXISTING, + StandardOpenOption.CREATE + ) + } + } + } + +} diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/hard/JMAHard.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/hard/JMAHard.kt new file mode 100644 index 00000000..f2135ac6 --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/hard/JMAHard.kt @@ -0,0 +1,38 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension.jma.hard + +import CShadowFieldAnnotationVisitor +import net.fabricmc.tinyremapper.extension.mixin.common.data.Constant +import xyz.wagyourtail.unimined.internal.mapping.extension.jma.DontRemapAnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.jma.hard.annotations.clazz.CTransformerAnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.jma.hard.annotations.method.COverrideAnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.jma.hard.annotations.method.CShadowMethodAnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.hard.HardTargetRemappingClassVisitor + +object JMAHard { + + + fun hardRemapper(hardRemapper: HardTargetRemappingClassVisitor) { + + hardRemapper.insertVisitor { + DontRemapAnnotationVisitor.DontRemapClassVisitor(Constant.ASM_VERSION, it, hardRemapper.extraData) + } + + hardRemapper.classAnnotationVisitors.addAll(listOf( + DontRemapAnnotationVisitor.Companion::shouldVisitHardClass to DontRemapAnnotationVisitor.Companion::visitHardClass, + CTransformerAnnotationVisitor.Companion::shouldVisit to ::CTransformerAnnotationVisitor + )) + + hardRemapper.methodAnnotationVisitors.addAll(listOf( + DontRemapAnnotationVisitor.Companion::shouldVisitHardMethod to DontRemapAnnotationVisitor.Companion::visitHardMethod, + CShadowMethodAnnotationVisitor.Companion::shouldVisit to ::CShadowMethodAnnotationVisitor, + COverrideAnnotationVisitor.Companion::shouldVisit to ::COverrideAnnotationVisitor + )) + + hardRemapper.fieldAnnotationVisitors.addAll(listOf( + DontRemapAnnotationVisitor.Companion::shouldVisitHardField to DontRemapAnnotationVisitor.Companion::visitHardField, + CShadowFieldAnnotationVisitor.Companion::shouldVisit to ::CShadowFieldAnnotationVisitor + )) + } + + +} \ No newline at end of file diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/hard/annotations/clazz/CTransformerAnnotationVisitor.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/hard/annotations/clazz/CTransformerAnnotationVisitor.kt new file mode 100644 index 00000000..435e934e --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/hard/annotations/clazz/CTransformerAnnotationVisitor.kt @@ -0,0 +1,90 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension.jma.hard.annotations.clazz + +import net.fabricmc.tinyremapper.extension.mixin.common.data.AnnotationElement +import net.fabricmc.tinyremapper.extension.mixin.common.data.Constant +import org.objectweb.asm.AnnotationVisitor +import org.objectweb.asm.Type +import xyz.wagyourtail.unimined.internal.mapping.extension.jma.JarModAgent +import xyz.wagyourtail.unimined.internal.mapping.extension.jma.dontRemap +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.hard.HardTargetRemappingClassVisitor +import xyz.wagyourtail.unimined.util.orElseOptional +import java.util.* + +class CTransformerAnnotationVisitor( + val descriptor: String, + visible: Boolean, + parent: AnnotationVisitor?, + private val hardTargetRemapper: HardTargetRemappingClassVisitor +) : AnnotationVisitor(Constant.ASM_VERSION, parent) { + + + companion object { + + fun shouldVisit(descriptor: String, visible: Boolean, hardTargetRemapper: HardTargetRemappingClassVisitor): Boolean { + return descriptor == JarModAgent.Annotation.CTRANSFORMER + } + + } + + private val remap = !hardTargetRemapper.dontRemap(descriptor) + private val logger = hardTargetRemapper.logger + private val existingMappings = hardTargetRemapper.existingMappings + private val targetClasses = hardTargetRemapper.targetClasses + private val mixinName = hardTargetRemapper.mixinName + + private val classTargets = mutableListOf() + private val classValues = mutableListOf() + + override fun visit(name: String, value: Any) { + super.visit(name, value) + logger.info("Found annotation value $name: $value") + } + + override fun visitArray(name: String?): AnnotationVisitor { + return when (name) { + JarModAgent.AnnotationElement.NAME -> { + return object: AnnotationVisitor(Constant.ASM_VERSION, super.visitArray(name)) { + override fun visit(name: String?, value: Any) { + classTargets.add(value as String) + super.visit(name, value) + } + } + } + + AnnotationElement.VALUE, null -> { + return object: AnnotationVisitor(Constant.ASM_VERSION, super.visitArray(name)) { + override fun visit(name: String?, value: Any) { + classValues.add((value as Type).internalName) + super.visit(name, value) + } + } + } + + else -> { + super.visitArray(name) + } + } + } + + override fun visitEnd() { + super.visitEnd() + targetClasses.addAll((classValues + classTargets.map { it.replace('.', '/') }).toSet()) + hardTargetRemapper.addRemapTaskFirst { + if (remap) { + for (target in targetClasses) { + val clz = resolver.resolveClass(target.replace('.', '/')).orElseOptional { + existingMappings[target]?.let { + targetClasses.remove(target) + targetClasses.add(it) + resolver.resolveClass(it) + } ?: Optional.empty() + } + if (!clz.isPresent) { + logger.warn("Failed to resolve class $target in mixin ${mixinName.replace('/', '.')}") + } + } + } + } + } + +} \ No newline at end of file diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/hard/annotations/field/CShadowFieldAnnotationVisitor.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/hard/annotations/field/CShadowFieldAnnotationVisitor.kt new file mode 100644 index 00000000..be89e569 --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/hard/annotations/field/CShadowFieldAnnotationVisitor.kt @@ -0,0 +1,79 @@ +import net.fabricmc.tinyremapper.extension.mixin.common.ResolveUtility +import net.fabricmc.tinyremapper.extension.mixin.common.data.Annotation +import net.fabricmc.tinyremapper.extension.mixin.common.data.AnnotationElement +import net.fabricmc.tinyremapper.extension.mixin.common.data.Constant +import org.objectweb.asm.AnnotationVisitor +import org.objectweb.asm.FieldVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.jma.JarModAgent +import xyz.wagyourtail.unimined.internal.mapping.extension.jma.dontRemap +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.hard.HardTargetRemappingClassVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.hard.annotations.method.AbstractMethodAnnotationVisitor +import java.util.concurrent.atomic.AtomicBoolean + +class CShadowFieldAnnotationVisitor( + descriptor: String, + visible: Boolean, + parent: AnnotationVisitor?, + fieldAccess: Int, + protected val fieldName: String, + protected val fieldDescriptor: String, + fieldSignature: String?, + fieldValue: Any?, + protected val hardTargetRemapper: HardTargetRemappingClassVisitor, +) : AnnotationVisitor( + Constant.ASM_VERSION, + parent +) { + + protected val remap = !hardTargetRemapper.dontRemap(descriptor) + protected val logger = hardTargetRemapper.logger + protected val existingMappings = hardTargetRemapper.existingMappings + protected val targetClasses = hardTargetRemapper.targetClasses + protected val mixinName = hardTargetRemapper.mixinName + + companion object { + + fun shouldVisit( + descriptor: String, + visible: Boolean, + access: Int, + fieldName: String, + fieldDescriptor: String, + fieldSignature: String?, + fieldValue: Any?, + hardTargetRemapper: HardTargetRemappingClassVisitor + ): Boolean { + return descriptor == JarModAgent.Annotation.CSHADOW + } + + } + + var name: String? = null + + override fun visit(name: String?, value: Any?) { + if (name == AnnotationElement.VALUE) { + this.name = value as String + } + super.visit(name, value) + } + + override fun visitEnd() { + super.visitEnd() + hardTargetRemapper.addRemapTask { + if (remap && name == null) { + for (target in targetClasses) { + val resolved = resolver.resolveField(target, fieldName, fieldDescriptor, ResolveUtility.FLAG_UNIQUE or ResolveUtility.FLAG_RECURSIVE) + resolved.ifPresent { + val mappedName = mapper.mapName(resolved.get()) + propagate(hardTargetRemapper.mxClass.getField(fieldName, fieldDescriptor).asTrMember(resolver), mappedName) + } + if (resolved.isPresent) { + return@addRemapTask + } + } + logger.warn("Could not find target field for @Shadow $fieldName:$fieldDescriptor in $mixinName") + } + } + } + +} \ No newline at end of file diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/hard/annotations/method/COverrideAnnotationVisitor.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/hard/annotations/method/COverrideAnnotationVisitor.kt new file mode 100644 index 00000000..c6cf78b1 --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/hard/annotations/method/COverrideAnnotationVisitor.kt @@ -0,0 +1,91 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension.jma.hard.annotations.method + +import net.fabricmc.tinyremapper.extension.mixin.common.ResolveUtility +import net.fabricmc.tinyremapper.extension.mixin.common.data.AnnotationElement +import net.fabricmc.tinyremapper.extension.mixin.common.data.Constant +import org.objectweb.asm.AnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.jma.JarModAgent +import xyz.wagyourtail.unimined.internal.mapping.extension.jma.dontRemap +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.hard.HardTargetRemappingClassVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.hard.annotations.method.AbstractMethodAnnotationVisitor +import java.util.concurrent.atomic.AtomicBoolean + +class COverrideAnnotationVisitor( + descriptor: String, + visible: Boolean, + parent: AnnotationVisitor?, + methodAccess: Int, + methodName: String, + methodDescriptor: String, + methodSignature: String?, + methodExceptions: Array?, + refmapBuilder: HardTargetRemappingClassVisitor, +) : AbstractMethodAnnotationVisitor( + descriptor, + visible, + parent, + methodAccess, + methodName, + methodDescriptor, + methodSignature, + methodExceptions, + refmapBuilder +) { + + companion object { + + fun shouldVisit( + descriptor: String, + visible: Boolean, + methodAccess: Int, + methodName: String, + methodDescriptor: String, + methodSignature: String?, + methodExceptions: Array?, + hardTargetRemapper: HardTargetRemappingClassVisitor + ): Boolean { + return descriptor == JarModAgent.Annotation.COVERRIDE + } + + } + + val names: MutableList = mutableListOf() + override val remap = AtomicBoolean(!hardTargetRemapper.dontRemap(descriptor)) + + override fun visitArray(name: String): AnnotationVisitor { + val delegate = super.visitArray(name) + return when (name) { + AnnotationElement.VALUE -> { + object: AnnotationVisitor(Constant.ASM_VERSION, delegate) { + override fun visit(name: String?, value: Any) { + super.visit(name, value) + names.add(value as String) + } + } + } + + else -> { + delegate + } + } + } + override fun visitEnd() { + hardTargetRemapper.addRemapTask { + if (remap.get() && names.isEmpty()) { + for (target in targetClasses) { + val resolved = resolver.resolveMethod(target, methodName, methodDescriptor, ResolveUtility.FLAG_UNIQUE or ResolveUtility.FLAG_RECURSIVE) + resolved.ifPresent { + val mappedName = mapper.mapName(resolved.get()) + propagate(hardTargetRemapper.mxClass.getMethod(methodName, methodDescriptor).asTrMember(resolver), mappedName) + } + if (resolved.isPresent) { + return@addRemapTask + } + } + logger.warn("Could not find target method for @COverride $methodName$methodDescriptor in $mixinName") + } + } + super.visitEnd() + } + +} \ No newline at end of file diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/hard/annotations/method/CShadowMethodAnnotationVisitor.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/hard/annotations/method/CShadowMethodAnnotationVisitor.kt new file mode 100644 index 00000000..4b77181f --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/hard/annotations/method/CShadowMethodAnnotationVisitor.kt @@ -0,0 +1,80 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension.jma.hard.annotations.method + +import net.fabricmc.tinyremapper.extension.mixin.common.ResolveUtility +import net.fabricmc.tinyremapper.extension.mixin.common.data.AnnotationElement +import org.objectweb.asm.AnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.jma.JarModAgent +import xyz.wagyourtail.unimined.internal.mapping.extension.jma.dontRemap +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.hard.HardTargetRemappingClassVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.hard.annotations.method.AbstractMethodAnnotationVisitor +import java.util.concurrent.atomic.AtomicBoolean + +class CShadowMethodAnnotationVisitor( + descriptor: String, + visible: Boolean, + parent: AnnotationVisitor?, + methodAccess: Int, + methodName: String, + methodDescriptor: String, + methodSignature: String?, + methodExceptions: Array?, + refmapBuilder: HardTargetRemappingClassVisitor, +) : AbstractMethodAnnotationVisitor( + descriptor, + visible, + parent, + methodAccess, + methodName, + methodDescriptor, + methodSignature, + methodExceptions, + refmapBuilder +) { + + companion object { + + fun shouldVisit( + descriptor: String, + visible: Boolean, + methodAccess: Int, + methodName: String, + methodDescriptor: String, + methodSignature: String?, + methodExceptions: Array?, + hardTargetRemapper: HardTargetRemappingClassVisitor + ): Boolean { + return descriptor == JarModAgent.Annotation.CSHADOW + } + + } + + var name: String? = null + override val remap = AtomicBoolean(!hardTargetRemapper.dontRemap(descriptor)) + + override fun visit(name: String?, value: Any?) { + if (name == AnnotationElement.VALUE) { + this.name = value as String + } + super.visit(name, value) + } + + override fun visitEnd() { + hardTargetRemapper.addRemapTask { + if (remap.get() && name == null) { + for (target in targetClasses) { + val resolved = resolver.resolveMethod(target, methodName, methodDescriptor, ResolveUtility.FLAG_UNIQUE or ResolveUtility.FLAG_RECURSIVE) + resolved.ifPresent { + val mappedName = mapper.mapName(resolved.get()) + propagate(hardTargetRemapper.mxClass.getMethod(methodName, methodDescriptor).asTrMember(resolver), mappedName) + } + if (resolved.isPresent) { + return@addRemapTask + } + } + logger.warn("Could not find target method for @Shadow $methodName$methodDescriptor in $mixinName") + } + } + super.visitEnd() + } + +} \ No newline at end of file diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/refmap/JMARefmap.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/refmap/JMARefmap.kt new file mode 100644 index 00000000..63b6b9d9 --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/refmap/JMARefmap.kt @@ -0,0 +1,40 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension.jma.refmap + +import net.fabricmc.tinyremapper.extension.mixin.common.data.Constant +import xyz.wagyourtail.unimined.internal.mapping.extension.jma.DontRemapAnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.jma.refmap.annotations.clazz.CTransformerAnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.jma.refmap.annotations.field.CShadowFieldAnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.jma.refmap.annotations.method.* +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.RefmapBuilderClassVisitor + +object JMARefmap { + + fun refmapBuilder(refmapBuilder: RefmapBuilderClassVisitor) { + + refmapBuilder.insertVisitor { + DontRemapAnnotationVisitor.DontRemapClassVisitor(Constant.ASM_VERSION, it, refmapBuilder.extraData) + } + + refmapBuilder.classAnnotationVisitors.addAll(listOf( + DontRemapAnnotationVisitor.Companion::shouldVisitSoftClass to DontRemapAnnotationVisitor.Companion::visitSoftClass, + CTransformerAnnotationVisitor.Companion::shouldVisit to ::CTransformerAnnotationVisitor + )) + + refmapBuilder.methodAnnotationVisitors.addAll(listOf( + DontRemapAnnotationVisitor.Companion::shouldVisitSoftMethod to DontRemapAnnotationVisitor.Companion::visitSoftMethod, + CInjectAnnotationVisitor.Companion::shouldVisit to ::CInjectAnnotationVisitor, + CModifyConstantAnnotationVisitor.Companion::shouldVisit to ::CModifyConstantAnnotationVisitor, + COverrideAnnotationVisitor.Companion::shouldVisit to ::COverrideAnnotationVisitor, + CRedirectAnnotationVisitor.Companion::shouldVisit to ::CRedirectAnnotationVisitor, + CShadowMethodAnnotationVisitor.Companion::shouldVisit to ::CShadowMethodAnnotationVisitor, + CWrapCatchAnnotationVisitor.Companion::shouldVisit to ::CWrapCatchAnnotationVisitor, + )) + + refmapBuilder.fieldAnnotationVisitors.addAll(listOf( + DontRemapAnnotationVisitor.Companion::shouldVisitSoftField to DontRemapAnnotationVisitor.Companion::visitSoftField, + CShadowFieldAnnotationVisitor.Companion::shouldVisit to ::CShadowFieldAnnotationVisitor + )) + + } + +} \ No newline at end of file diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/refmap/annotations/CSliceAnnotationVisitor.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/refmap/annotations/CSliceAnnotationVisitor.kt new file mode 100644 index 00000000..f2e238c7 --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/refmap/annotations/CSliceAnnotationVisitor.kt @@ -0,0 +1,19 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension.jma.refmap.annotations + +import net.fabricmc.tinyremapper.extension.mixin.common.data.AnnotationElement +import net.fabricmc.tinyremapper.extension.mixin.common.data.Constant +import org.objectweb.asm.AnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.RefmapBuilderClassVisitor +import java.util.concurrent.atomic.AtomicBoolean + +class CSliceAnnotationVisitor(parent: AnnotationVisitor?, val remap: AtomicBoolean, private val refmapBuilder: RefmapBuilderClassVisitor) : AnnotationVisitor(Constant.ASM_VERSION, parent) { + + override fun visitAnnotation(name: String, descriptor: String): AnnotationVisitor { + return if (name == AnnotationElement.FROM || name == AnnotationElement.TO) { + CTargetAnnotationVisitor(super.visitAnnotation(name, descriptor), remap, refmapBuilder) + } else { + super.visitAnnotation(name, descriptor) + } + } + +} diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/refmap/annotations/CTargetAnnotationVisitor.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/refmap/annotations/CTargetAnnotationVisitor.kt new file mode 100644 index 00000000..e5ca3ff3 --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/refmap/annotations/CTargetAnnotationVisitor.kt @@ -0,0 +1,257 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension.jma.refmap.annotations + +import net.fabricmc.tinyremapper.extension.mixin.common.ResolveUtility +import net.fabricmc.tinyremapper.extension.mixin.common.StringUtility +import net.fabricmc.tinyremapper.extension.mixin.common.data.AnnotationElement +import net.fabricmc.tinyremapper.extension.mixin.common.data.Constant +import org.objectweb.asm.AnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.jma.JarModAgent +import xyz.wagyourtail.unimined.internal.mapping.extension.jma.dontRemap +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.RefmapBuilderClassVisitor +import xyz.wagyourtail.unimined.util.orElseOptional +import java.util.* +import java.util.concurrent.atomic.AtomicBoolean + +class CTargetAnnotationVisitor(parent: AnnotationVisitor?, remap: AtomicBoolean, private val refmapBuilder: RefmapBuilderClassVisitor) : AnnotationVisitor(Constant.ASM_VERSION, parent) { + private val remapAt = !refmapBuilder.dontRemap(JarModAgent.Annotation.CTARGET) + private var targetName: String? = null + + private val resolver = refmapBuilder.resolver + private val logger = refmapBuilder.logger + private val existingMappings = refmapBuilder.existingMappings + private val mapper = refmapBuilder.mapper + private val refmap = refmapBuilder.refmap + private val mixinName = refmapBuilder.mixinName + private val noRefmap = refmapBuilder.mixinRemapExtension.noRefmap.contains("JarModAgent") + + override fun visit(name: String, value: Any) { + super.visit(name, value) + if (name == AnnotationElement.TARGET) { + targetName = (value as String).replace(" ", "") + if (!noRefmap) super.visit(name, value) + } else { + super.visit(name, value) + } + } + + override fun visitAnnotation(name: String, descriptor: String): AnnotationVisitor { + logger.warn("Found annotation in target descriptor: $name $descriptor") + return super.visitAnnotation(name, descriptor) + } + + private val targetField = Regex("^(L[^;]+;|[^.]+?\\.)([^:]+):(.+)$") + private val targetMethod = Regex("^(L[^;]+;|[^.]+?\\.)([^(]+)\\s*([^>]+)$") + + + private fun matchToParts(match: MatchResult): Triple { + val targetOwner = match.groupValues[1].let { + if (it.startsWith("L") && it.endsWith(";")) it.substring( + 1, + it.length - 1 + ) else it.substring(0, it.length - 1) + } + return Triple(targetOwner, match.groupValues[2], match.groupValues[3]) + } + + override fun visitEnd() { + if (remapAt && targetName != null) { + val matchFd = targetField.matchEntire(targetName!!) + if (matchFd != null) { + var (targetOwner, targetName, targetDesc) = matchToParts(matchFd) + val target = resolver.resolveField( + targetOwner, + targetName, + targetDesc, + ResolveUtility.FLAG_UNIQUE or ResolveUtility.FLAG_RECURSIVE + ).orElseOptional { + existingMappings[this.targetName]?.let { existing -> + logger.info("remapping $existing from existing refmap") + val matchEFd = targetField.matchEntire(existing) + if (matchEFd != null) { + val matchResult = matchToParts(matchEFd) + targetOwner = matchResult.first + val fName = matchResult.second + val fDesc = matchResult.third + resolver.resolveField( + targetOwner, + fName, + fDesc, + ResolveUtility.FLAG_UNIQUE or ResolveUtility.FLAG_RECURSIVE + ) + } else { + Optional.empty() + } + } ?: Optional.empty() + } + val targetClass = resolver.resolveClass(targetOwner) + targetClass.ifPresent { clz -> + target.ifPresent { + val mappedOwner = mapper.mapName(clz) + val mappedName = mapper.mapName(it) + val mappedDesc = mapper.mapDesc(it) + refmap.addProperty(this.targetName, "L$mappedOwner;$mappedName:$mappedDesc") + if (noRefmap) { + super.visit(AnnotationElement.TARGET, "L$mappedOwner;$mappedName:$mappedDesc") + } + } + } + if (!target.isPresent || !targetClass.isPresent) { + logger.warn( + "Failed to resolve At target L$targetOwner;$targetName:$targetDesc in mixin ${ + mixinName.replace( + '/', + '.' + ) + }" + ) + if (noRefmap) { + super.visit(AnnotationElement.TARGET, this.targetName) + } + } + super.visitEnd() + return + } + val matchMd = targetMethod.matchEntire(targetName!!) + if (matchMd != null) { + var (targetOwner, targetName, targetDesc) = matchToParts(matchMd) + val target = resolver.resolveMethod( + targetOwner, + targetName, + targetDesc, + ResolveUtility.FLAG_UNIQUE or ResolveUtility.FLAG_RECURSIVE + ).orElseOptional { + existingMappings[this.targetName]?.let { existing -> + logger.info("remapping $existing from existing refmap") + val matchEMd = targetMethod.matchEntire(existing) + if (matchEMd != null) { + val matchResult = matchToParts(matchEMd) + targetOwner = matchResult.first + val mName = matchResult.second + val mDesc = matchResult.third + resolver.resolveMethod( + targetOwner, + mName, + mDesc, + ResolveUtility.FLAG_UNIQUE or ResolveUtility.FLAG_RECURSIVE + ) + } else { + Optional.empty() + } + } ?: Optional.empty() + } + val targetClass = resolver.resolveClass(targetOwner) + targetClass.ifPresent { clz -> + target.ifPresent { + val mappedOwner = mapper.mapName(clz) + val mappedName = mapper.mapName(it) + val mappedDesc = mapper.mapDesc(it) + refmap.addProperty(this.targetName, "L$mappedOwner;$mappedName$mappedDesc") + if (noRefmap) { + super.visit(AnnotationElement.TARGET, "L$mappedOwner;$mappedName$mappedDesc") + } + } + } + if (!target.isPresent || !targetClass.isPresent) { + logger.warn( + "Failed to resolve At target L$targetOwner;$targetName$targetDesc in mixin ${ + mixinName.replace( + '/', + '.' + ) + }" + ) + if (noRefmap) { + super.visit(AnnotationElement.TARGET, this.targetName) + } + } + super.visitEnd() + return + } + if (targetName!!.startsWith("(")) { + val existing = existingMappings[this.targetName] + if (existing != null) { + logger.info("remapping $existing from existing refmap") + val mapped = mapper.asTrRemapper().mapDesc(existing) + if (mapped == existing) { + logger.warn("Failed to remap $existing") + if (noRefmap) { + super.visit(AnnotationElement.TARGET, this.targetName) + } + super.visitEnd() + return + } + refmap.addProperty(this.targetName, mapped) + super.visitEnd() + return + } else { + val mapped = mapper.asTrRemapper().mapDesc(targetName) + if (mapped == targetName) { + logger.warn("Failed to remap $targetName") + if (noRefmap) { + super.visit(AnnotationElement.TARGET, this.targetName) + } + return + } else { + refmap.addProperty(this.targetName, mapped) + if (noRefmap) { + super.visit(AnnotationElement.TARGET, mapped) + } + super.visitEnd() + return + } + } + } + + // else is probably a class + // we will count (NEW, ) as that too + // carefully, by modifying method so it doesn't capture it + val fixedTarget = if (targetName!!.startsWith("L")) { + targetName!!.substring(1).substringBefore("").substringBefore(";") + } else { + targetName!!.substringBefore(".") + }.replace('.', '/') + if (StringUtility.isClassName(fixedTarget)) { + val target = resolver.resolveClass(fixedTarget).orElseOptional { + existingMappings[this.targetName]?.let { + logger.info("remapping $it from existing refmap") + resolver.resolveClass( + if (it.startsWith("L") && it.endsWith(";")) { + it.substring(1, it.length - 1) + } else { + it + }.replace('.', '/') + ) + } ?: Optional.empty() + } + target.ifPresent { + val mapped = mapper.mapName(it) + refmap.addProperty(this.targetName, "L$mapped;") + if (noRefmap) { + super.visit(AnnotationElement.TARGET, "L$mapped;") + } + } + if (target.isPresent) { + super.visitEnd() + return + } + } + + logger.warn( + "Failed to parse target descriptor: $targetName ($fixedTarget) in mixin ${ + mixinName.replace( + '/', + '.' + ) + }" + ) + if (noRefmap) { + super.visit(AnnotationElement.TARGET, this.targetName) + } + } else if (targetName != null) { + if (noRefmap) { + super.visit(AnnotationElement.TARGET, this.targetName) + } + } + super.visitEnd() + } +} \ No newline at end of file diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/refmap/annotations/clazz/CTransformerAnnotationVisitor.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/refmap/annotations/clazz/CTransformerAnnotationVisitor.kt new file mode 100644 index 00000000..a70d09a3 --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/refmap/annotations/clazz/CTransformerAnnotationVisitor.kt @@ -0,0 +1,110 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension.jma.refmap.annotations.clazz + +import net.fabricmc.tinyremapper.extension.mixin.common.data.Annotation +import net.fabricmc.tinyremapper.extension.mixin.common.data.AnnotationElement +import net.fabricmc.tinyremapper.extension.mixin.common.data.Constant +import org.objectweb.asm.AnnotationVisitor +import org.objectweb.asm.Type +import xyz.wagyourtail.unimined.internal.mapping.extension.jma.JarModAgent +import xyz.wagyourtail.unimined.internal.mapping.extension.jma.dontRemap +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.RefmapBuilderClassVisitor +import xyz.wagyourtail.unimined.util.orElseOptional +import java.util.* + +@Suppress("UNUSED_PARAMETER") +class CTransformerAnnotationVisitor( + descriptor: String, + visible: Boolean, + parent: AnnotationVisitor, + refmapBuilder: RefmapBuilderClassVisitor +) : AnnotationVisitor(Constant.ASM_VERSION, parent) { + + companion object { + + fun shouldVisit(descriptor: String, visible: Boolean, refmapBuilder: RefmapBuilderClassVisitor): Boolean { + return descriptor == JarModAgent.Annotation.CTRANSFORMER + } + + } + + private val remap = !refmapBuilder.dontRemap(descriptor) + private val resolver = refmapBuilder.resolver + private val logger = refmapBuilder.logger + private val existingMappings = refmapBuilder.existingMappings + private val mapper = refmapBuilder.mapper + private val refmap = refmapBuilder.refmap + private val mixinName = refmapBuilder.mixinName + private val targetClasses = refmapBuilder.targetClasses + private val noRefmap = refmapBuilder.mixinRemapExtension.noRefmap.contains("JarModAgent") + + private val classTargets = mutableListOf() + private val classValues = mutableListOf() + + override fun visit(name: String, value: Any) { + super.visit(name, value) + logger.info("Found annotation value $name: $value") + } + + override fun visitArray(name: String?): AnnotationVisitor { + return when (name) { + JarModAgent.AnnotationElement.NAME -> { + return object: AnnotationVisitor(Constant.ASM_VERSION, if (noRefmap) null else super.visitArray(name)) { + override fun visit(name: String?, value: Any) { + classTargets.add(value as String) + super.visit(name, value) + } + } + } + + AnnotationElement.VALUE, null -> { + return object: AnnotationVisitor(Constant.ASM_VERSION, super.visitArray(name)) { + override fun visit(name: String?, value: Any) { + classValues.add((value as Type).internalName) + super.visit(name, value) + } + } + } + + else -> { + super.visitArray(name) + } + } + } + + override fun visitEnd() { + val targets = if (noRefmap && classTargets.isNotEmpty()) { + super.visitArray(AnnotationElement.TARGETS) + } else { + null + } + if (remap) { + logger.info("existing mappings: $existingMappings") + for (target in classTargets.toSet()) { + val clz = resolver.resolveClass(target.replace('.', '/')) + .orElseOptional { + existingMappings[target]?.let { + logger.info("remapping $it from existing refmap") + classTargets.remove(target) + classTargets.add(it) + resolver.resolveClass(it) + } ?: Optional.empty() + } + clz.ifPresent { + refmap.addProperty(target, mapper.mapName(it)) + targets?.visit(null, target) + } + if (!clz.isPresent) { + targets?.visit(null, target) + logger.warn("Failed to resolve class $target in mixin ${mixinName.replace('/', '.')}") + } + } + } else { + for (target in classTargets) { + targets?.visit(null, target) + } + } + targets?.visitEnd() + targetClasses.addAll((classValues + classTargets.map { it.replace('.', '/') }).toSet()) + super.visitEnd() + } +} diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/refmap/annotations/field/CAbstractFieldAnnotationVisitor.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/refmap/annotations/field/CAbstractFieldAnnotationVisitor.kt new file mode 100644 index 00000000..83b22cf1 --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/refmap/annotations/field/CAbstractFieldAnnotationVisitor.kt @@ -0,0 +1,117 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension.jma.refmap.annotations.field + +import net.fabricmc.tinyremapper.extension.mixin.common.ResolveUtility +import net.fabricmc.tinyremapper.extension.mixin.common.data.AnnotationElement +import net.fabricmc.tinyremapper.extension.mixin.common.data.Constant +import org.objectweb.asm.AnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.jma.dontRemap +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.RefmapBuilderClassVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.splitFieldNameAndDescriptor +import xyz.wagyourtail.unimined.internal.mapping.extension.splitMethodNameAndDescriptor +import xyz.wagyourtail.unimined.util.orElseOptional +import java.util.* +import java.util.concurrent.atomic.AtomicBoolean + +abstract class CAbstractFieldAnnotationVisitor( + descriptor: String, + visible: Boolean, + parent: AnnotationVisitor, + fieldAccess: Int, + protected val fieldName: String, + protected val fieldDescriptor: String, + protected val fieldSignature: String?, + fieldValue: Any?, + protected val refmapBuilder: RefmapBuilderClassVisitor +) : AnnotationVisitor(Constant.ASM_VERSION, parent) { + + abstract val annotationName: String + + protected val remap = AtomicBoolean(!refmapBuilder.dontRemap(descriptor)) + protected var targetNames = mutableListOf() + + protected val resolver = refmapBuilder.resolver + protected val logger = refmapBuilder.logger + protected val existingMappings = refmapBuilder.existingMappings + protected val mapper = refmapBuilder.mapper + protected val refmap = refmapBuilder.refmap + protected val mixinName = refmapBuilder.mixinName + protected val targetClasses = refmapBuilder.targetClasses + protected val allowImplicitWildcards = refmapBuilder.allowImplicitWildcards + protected val noRefmap = refmapBuilder.mixinRemapExtension.noRefmap.contains("JarModAgent") + + override fun visit(name: String?, value: Any) { + super.visit(name, value) + if (name == AnnotationElement.REMAP) remap.set(value as Boolean) + } + + open fun getTargetNameAndDescs(targetField: String): Pair> { + val targetDescs = setOf(if (targetField.contains(":")) { + targetField.substringAfter(":") + } else { + null + }) + val targetName = targetField.substringBefore(":") + return targetName to targetDescs + } + + open fun remapTargetNames(noRefmapAcceptor: (String) -> Unit) { + if (remap.get()) { + outer@for (targetField in targetNames) { + val (targetName, targetDescs) = getTargetNameAndDescs(targetField) + for (targetDesc in targetDescs) { + var implicitWildcard = targetDesc == null && allowImplicitWildcards + for (targetClass in targetClasses) { + val target = resolver.resolveField( + targetClass, + targetName, + targetDesc, + (if (implicitWildcard) ResolveUtility.FLAG_FIRST else ResolveUtility.FLAG_UNIQUE) or ResolveUtility.FLAG_RECURSIVE + ) + .orElseOptional { + existingMappings[targetField]?.let { existing -> + logger.info("Remapping using existing mapping for $targetField: $existing") + val (fName, fDesc) = splitFieldNameAndDescriptor(existing) + if (fDesc == null && allowImplicitWildcards) { + implicitWildcard = true + } + resolver.resolveField( + targetClass, + fName, + fDesc, + (if (implicitWildcard) ResolveUtility.FLAG_FIRST else ResolveUtility.FLAG_UNIQUE) or ResolveUtility.FLAG_RECURSIVE + ) + } ?: Optional.empty() + } + target.ifPresent { targetVal -> + val mappedClass = resolver.resolveClass(targetClass) + .map { mapper.mapName(it) } + .orElse(targetClass) + val mappedName = mapper.mapName(targetVal) + val mappedDesc = mapper.mapDesc(targetVal) + if (targetClasses.size > 1) { + refmap.addProperty(targetField, "$mappedName$mappedDesc") + noRefmapAcceptor("$mappedName$mappedDesc") + } else { + refmap.addProperty(targetField, "L$mappedClass;$mappedName:$mappedDesc") + noRefmapAcceptor("L$mappedClass;$mappedName:$mappedDesc") + } + } + + if (target.isPresent) { + continue@outer + } + } + } + logger.warn( + "Failed to resolve $annotationName $targetField ($targetDescs) on ($fieldName:$fieldDescriptor) $fieldSignature in $mixinName" + ) + noRefmapAcceptor(targetField) + } + } else { + for (targetField in targetNames) { + noRefmapAcceptor(targetField) + } + } + } + +} \ No newline at end of file diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/refmap/annotations/field/CShadowFieldAnnotationVisitor.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/refmap/annotations/field/CShadowFieldAnnotationVisitor.kt new file mode 100644 index 00000000..aa9f6ae9 --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/refmap/annotations/field/CShadowFieldAnnotationVisitor.kt @@ -0,0 +1,77 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension.jma.refmap.annotations.field + +import net.fabricmc.tinyremapper.extension.mixin.common.data.AnnotationElement +import org.objectweb.asm.AnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.jma.JarModAgent +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.RefmapBuilderClassVisitor + +class CShadowFieldAnnotationVisitor( + descriptor: String, + visible: Boolean, + parent: AnnotationVisitor, + fieldAccess: Int, + fieldName: String, + fieldDescriptor: String, + fieldSignature: String?, + fieldValue: Any?, + refmapBuilder: RefmapBuilderClassVisitor +) : CAbstractFieldAnnotationVisitor( + descriptor, + visible, + parent, + fieldAccess, + fieldName, + fieldDescriptor, + fieldSignature, + fieldValue, + refmapBuilder +) { + + override val annotationName: String = "@CShadow" + + companion object{ + fun shouldVisit( + descriptor: String, + visible: Boolean, + fieldAccess: Int, + fieldName: String, + fieldDescriptor: String, + fieldSignature: String?, + fieldValue: Any?, + refmapBuilder: RefmapBuilderClassVisitor + ): Boolean{ + return descriptor == JarModAgent.Annotation.CSHADOW + } + } + + override fun visit(name: String?, value: Any) { + if (name == AnnotationElement.VALUE) { + targetNames.add(value as String) + if (!noRefmap) { + super.visit(name, value) + } + } else { + super.visit(name, value) + } + } + + override fun visitEnd() { + remapTargetNames { + if (noRefmap) { + super.visit(AnnotationElement.VALUE, it) + } + } + super.visitEnd() + } + + override fun getTargetNameAndDescs(targetField: String): Pair> { + return if (targetField.contains(":")) { + targetField.substringBefore(":") to setOf(targetField.substringAfter(":")) + } else { + targetField to setOf(fieldDescriptor) + } + } + + + +} \ No newline at end of file diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/refmap/annotations/method/CAbstractMethodAnnotationVisitor.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/refmap/annotations/method/CAbstractMethodAnnotationVisitor.kt new file mode 100644 index 00000000..5143baef --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/refmap/annotations/method/CAbstractMethodAnnotationVisitor.kt @@ -0,0 +1,142 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension.jma.refmap.annotations.method + +import net.fabricmc.tinyremapper.extension.mixin.common.ResolveUtility +import net.fabricmc.tinyremapper.extension.mixin.common.data.AnnotationElement +import net.fabricmc.tinyremapper.extension.mixin.common.data.Constant +import org.objectweb.asm.AnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.jma.dontRemap +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.RefmapBuilderClassVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.splitMethodNameAndDescriptor +import xyz.wagyourtail.unimined.util.orElseOptional +import java.util.* +import java.util.concurrent.atomic.AtomicBoolean + +abstract class CAbstractMethodAnnotationVisitor( + descriptor: String, + visible: Boolean, + parent: AnnotationVisitor, + methodAccess: Int, + protected val methodName: String, + protected val methodDescriptor: String, + protected val methodSignature: String?, + methodExceptions: Array?, + protected val refmapBuilder: RefmapBuilderClassVisitor +) : AnnotationVisitor(Constant.ASM_VERSION, parent) { + + abstract val annotationName: String + + protected val remap = AtomicBoolean(!refmapBuilder.dontRemap(descriptor)) + protected var targetNames = mutableListOf() + + protected val resolver = refmapBuilder.resolver + protected val logger = refmapBuilder.logger + protected val existingMappings = refmapBuilder.existingMappings + protected val mapper = refmapBuilder.mapper + protected val refmap = refmapBuilder.refmap + protected val mixinName = refmapBuilder.mixinName + protected val targetClasses = refmapBuilder.targetClasses + protected val allowImplicitWildcards = refmapBuilder.allowImplicitWildcards + protected val noRefmap = refmapBuilder.mixinRemapExtension.noRefmap.contains("JarModAgent") + + override fun visit(name: String?, value: Any) { + super.visit(name, value) + if (name == AnnotationElement.REMAP) remap.set(value as Boolean) + } + + open fun getTargetNameAndDescs(targetMethod: String, wildcard: Boolean): Pair> { + val targetDescs = setOf(if (targetMethod.contains("(")) { + "(" + targetMethod.substringAfter("(") + } else { + null + }) + val targetName = if (wildcard) { + targetMethod.substringBefore("*") + } else { + targetMethod.substringBefore("(") + } + return targetName to targetDescs + } + + open fun remapTargetNames(noRefmapAcceptor: (String) -> Unit) { + if (remap.get()) { + outer@for (targetMethod in targetNames) { + if (targetMethod == "" || targetMethod == "" || + targetMethod == "*" + ) { + noRefmapAcceptor(targetMethod) + continue + } + val wildcard = targetMethod.endsWith("*") + val (targetName, targetDescs) = getTargetNameAndDescs(targetMethod, wildcard) + for (targetDesc in targetDescs) { + var implicitWildcard = targetDesc == null && allowImplicitWildcards + for (targetClass in targetClasses) { + val target = if (targetDesc != null && targetDesc.endsWith(")")) { + resolver.resolveClass(targetClass).map { + it.resolveMethods(targetName, targetDesc, true, null, null) + }.flatMap { + if (it.size > 1 && !(wildcard || implicitWildcard)) throw IllegalStateException("Found multiple methods for $targetMethod ($targetDesc) in $targetClass: $it") + Optional.ofNullable(it.firstOrNull()) + } + } else { + resolver.resolveMethod( + targetClass, + targetName, + targetDesc, + (if (wildcard || implicitWildcard) ResolveUtility.FLAG_FIRST else ResolveUtility.FLAG_UNIQUE) or ResolveUtility.FLAG_RECURSIVE + ) + } + .orElseOptional { + existingMappings[targetMethod]?.let { existing -> + logger.info("Remapping using existing mapping for $targetMethod: $existing") + if (wildcard) { + val mname = existing.substringAfter(";").let { it.substring(0, it.length - 1 ) } + resolver.resolveMethod( + targetClass, + mname, + null, + ResolveUtility.FLAG_FIRST or ResolveUtility.FLAG_RECURSIVE + ) + } else { + val (mName, mDesc) = splitMethodNameAndDescriptor(existing) + if (mDesc == null && allowImplicitWildcards) { + implicitWildcard = true + } + resolver.resolveMethod( + targetClass, + mName, + mDesc, + (if (implicitWildcard) ResolveUtility.FLAG_FIRST else ResolveUtility.FLAG_UNIQUE) or ResolveUtility.FLAG_RECURSIVE + ) + } + } ?: Optional.empty() + } + target.ifPresent { targetVal -> + val mappedClass = resolver.resolveClass(targetClass) + .map { mapper.mapName(it) } + .orElse(targetClass) + val mappedName = mapper.mapName(targetVal) + val mappedDesc = /* if (implicitWildcard) "" else */ if (wildcard && mappedName != "") "*" else mapper.mapDesc(targetVal) + if (targetClasses.size > 1) { + refmap.addProperty(targetMethod, "$mappedName$mappedDesc") + noRefmapAcceptor("$mappedName$mappedDesc") + } else { + refmap.addProperty(targetMethod, "L$mappedClass;$mappedName$mappedDesc") + noRefmapAcceptor("L$mappedClass;$mappedName$mappedDesc") + } + } + + if (target.isPresent) { + continue@outer + } + } + } + logger.warn( + "Failed to resolve $annotationName $targetMethod ($targetDescs) on ($methodName$methodDescriptor) $methodSignature in $mixinName" + ) + noRefmapAcceptor(targetMethod) + } + } + } + +} \ No newline at end of file diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/refmap/annotations/method/CInjectAnnotationVisitor.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/refmap/annotations/method/CInjectAnnotationVisitor.kt new file mode 100644 index 00000000..c3c3320d --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/refmap/annotations/method/CInjectAnnotationVisitor.kt @@ -0,0 +1,109 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension.jma.refmap.annotations.method + +import net.fabricmc.tinyremapper.extension.mixin.common.data.AnnotationElement +import net.fabricmc.tinyremapper.extension.mixin.common.data.Constant +import org.objectweb.asm.AnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.ArrayVisitorWrapper +import xyz.wagyourtail.unimined.internal.mapping.extension.jma.JarModAgent +import xyz.wagyourtail.unimined.internal.mapping.extension.jma.refmap.annotations.CSliceAnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.jma.refmap.annotations.CTargetAnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.RefmapBuilderClassVisitor + +@Suppress("UNUSED_PARAMETER") +class CInjectAnnotationVisitor( + descriptor: String, + visible: Boolean, + parent: AnnotationVisitor, + methodAccess: Int, + methodName: String, + methodDescriptor: String, + methodSignature: String?, + methodExceptions: Array?, + refmapBuilder: RefmapBuilderClassVisitor, +) : CAbstractMethodAnnotationVisitor( + descriptor, + visible, + parent, + methodAccess, + methodName, + methodDescriptor, + methodSignature, + methodExceptions, + refmapBuilder +) { + + override val annotationName: String = "@CInject" + + companion object{ + fun shouldVisit( + descriptor:String, + visible:Boolean, + methodAccess:Int, + methodName:String, + methodDescriptor:String, + methodSignature:String?, + methodExceptions:Array?, + refmapBuilder:RefmapBuilderClassVisitor + ): Boolean{ + return descriptor == JarModAgent.Annotation.CINJECT + } + } + + override fun visitArray(name: String): AnnotationVisitor { + return when (name) { + AnnotationElement.TARGET -> { + ArrayVisitorWrapper(Constant.ASM_VERSION, super.visitArray(name)) { CTargetAnnotationVisitor(it, remap, refmapBuilder) } + } + + AnnotationElement.SLICE -> { + ArrayVisitorWrapper(Constant.ASM_VERSION, super.visitArray(name)) { CSliceAnnotationVisitor(it, remap, refmapBuilder) } + } + AnnotationElement.METHOD -> { + object: AnnotationVisitor(Constant.ASM_VERSION, if (noRefmap) null else super.visitArray(name)) { + override fun visit(name: String?, value: Any) { + super.visit(name, value) + targetNames.add(value as String) + } + } + } + + else -> { + super.visitArray(name) + } + } + } + + override fun visitEnd() { + val method = if (noRefmap) { + super.visitArray(AnnotationElement.METHOD) + } else { + null + } + remapTargetNames { + method?.visit(null, it) + } + method?.visitEnd() + super.visitEnd() + } + + private val callbackInfo = "Lnet/lenni0451/classtransform/InjectionCallback" + + private fun stripCallbackInfoFromDesc(): Set { + val desc = methodDescriptor.substringBeforeLast(callbackInfo) + ")" + return setOf(desc) + } + + override fun getTargetNameAndDescs(targetMethod: String, wildcard: Boolean): Pair> { + return if (targetMethod.contains("(")) { + val n = targetMethod.split("(") + (n[0] to setOf("(${n[1]}")) + } else { + if (wildcard) { + (targetMethod.substring(0, targetMethod.length - 1) to setOf(null)) + } else { + (targetMethod to stripCallbackInfoFromDesc() + setOf(null)) + } + } + } + +} diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/refmap/annotations/method/CModifyConstantAnnotationVisitor.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/refmap/annotations/method/CModifyConstantAnnotationVisitor.kt new file mode 100644 index 00000000..d9c65cce --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/refmap/annotations/method/CModifyConstantAnnotationVisitor.kt @@ -0,0 +1,96 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension.jma.refmap.annotations.method + +import net.fabricmc.tinyremapper.extension.mixin.common.data.AnnotationElement +import net.fabricmc.tinyremapper.extension.mixin.common.data.Constant +import org.objectweb.asm.AnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.ArrayVisitorWrapper +import xyz.wagyourtail.unimined.internal.mapping.extension.jma.JarModAgent +import xyz.wagyourtail.unimined.internal.mapping.extension.jma.refmap.annotations.CSliceAnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.RefmapBuilderClassVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.annotations.DescAnnotationVisitor + +@Suppress("UNUSED_PARAMETER") +open class CModifyConstantAnnotationVisitor( + descriptor: String, + visible: Boolean, + parent: AnnotationVisitor, + methodAccess: Int, + methodName: String, + methodDescriptor: String, + methodSignature: String?, + methodExceptions: Array?, + refmapBuilder: RefmapBuilderClassVisitor, +) : CAbstractMethodAnnotationVisitor( + descriptor, + visible, + parent, + methodAccess, + methodName, + methodDescriptor, + methodSignature, + methodExceptions, + refmapBuilder +) { + + override val annotationName: String = "@CModifyConstant" + + companion object { + fun shouldVisit( + descriptor: String, + visible: Boolean, + methodAccess: Int, + methodName: String, + methodDescriptor: String, + methodSignature: String?, + methodExceptions: Array?, + refmapBuilder: RefmapBuilderClassVisitor + ): Boolean { + return descriptor == JarModAgent.Annotation.CMODIFYCONSTANT + } + } + + + override fun visitAnnotation(name: String, descriptor: String): AnnotationVisitor { + return when (name) { + + AnnotationElement.SLICE -> { + CSliceAnnotationVisitor(super.visitAnnotation(name, descriptor), remap, refmapBuilder) + } + + else -> { + super.visitAnnotation(name, descriptor) + } + } + } + + override fun visitArray(name: String): AnnotationVisitor { + return when (name) { + AnnotationElement.METHOD -> { + object: AnnotationVisitor(Constant.ASM_VERSION, if (noRefmap) null else super.visitArray(name)) { + override fun visit(name: String?, value: Any) { + super.visit(name, value) + targetNames.add(value as String) + } + } + } + + else -> { + super.visitArray(name) + } + } + } + + override fun visitEnd() { + val method = if (noRefmap) { + super.visitArray(AnnotationElement.METHOD) + } else { + null + } + remapTargetNames { + method?.visit(null, it) + } + method?.visitEnd() + super.visitEnd() + } + +} diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/refmap/annotations/method/COverrideAnnotationVisitor.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/refmap/annotations/method/COverrideAnnotationVisitor.kt new file mode 100644 index 00000000..b83d9483 --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/refmap/annotations/method/COverrideAnnotationVisitor.kt @@ -0,0 +1,91 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension.jma.refmap.annotations.method + +import net.fabricmc.tinyremapper.extension.mixin.common.data.AnnotationElement +import net.fabricmc.tinyremapper.extension.mixin.common.data.Constant +import org.objectweb.asm.AnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.ArrayVisitorWrapper +import xyz.wagyourtail.unimined.internal.mapping.extension.jma.JarModAgent +import xyz.wagyourtail.unimined.internal.mapping.extension.jma.refmap.annotations.CSliceAnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.jma.refmap.annotations.CTargetAnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.RefmapBuilderClassVisitor + +@Suppress("UNUSED_PARAMETER") +class COverrideAnnotationVisitor( + descriptor: String, + visible: Boolean, + parent: AnnotationVisitor, + methodAccess: Int, + methodName: String, + methodDescriptor: String, + methodSignature: String?, + methodExceptions: Array?, + refmapBuilder: RefmapBuilderClassVisitor, +) : CAbstractMethodAnnotationVisitor( + descriptor, + visible, + parent, + methodAccess, + methodName, + methodDescriptor, + methodSignature, + methodExceptions, + refmapBuilder +) { + + override val annotationName: String = "@COverride" + + companion object{ + fun shouldVisit( + descriptor:String, + visible:Boolean, + methodAccess:Int, + methodName:String, + methodDescriptor:String, + methodSignature:String?, + methodExceptions:Array?, + refmapBuilder:RefmapBuilderClassVisitor + ): Boolean{ + return descriptor == JarModAgent.Annotation.CINJECT + } + } + + override fun visitArray(name: String): AnnotationVisitor { + return when (name) { + AnnotationElement.VALUE -> { + object: AnnotationVisitor(Constant.ASM_VERSION, if (noRefmap) null else super.visitArray(name)) { + override fun visit(name: String?, value: Any) { + super.visit(name, value) + targetNames.add(value as String) + } + } + } + + else -> { + super.visitArray(name) + } + } + } + + override fun visitEnd() { + val method = if (noRefmap) { + super.visitArray(AnnotationElement.VALUE) + } else { + null + } + remapTargetNames { + method?.visit(null, it) + } + method?.visitEnd() + super.visitEnd() + } + + override fun getTargetNameAndDescs(targetMethod: String, wildcard: Boolean): Pair> { + return if (targetMethod.contains("(")) { + val n = targetMethod.split("(") + n[0] to setOf("(${n[1]}") + } else { + targetMethod to setOf(methodDescriptor) + } + } + +} diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/refmap/annotations/method/CRedirectAnnotationVisitor.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/refmap/annotations/method/CRedirectAnnotationVisitor.kt new file mode 100644 index 00000000..bf66d459 --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/refmap/annotations/method/CRedirectAnnotationVisitor.kt @@ -0,0 +1,101 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension.jma.refmap.annotations.method + +import net.fabricmc.tinyremapper.extension.mixin.common.data.AnnotationElement +import net.fabricmc.tinyremapper.extension.mixin.common.data.Constant +import org.objectweb.asm.AnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.ArrayVisitorWrapper +import xyz.wagyourtail.unimined.internal.mapping.extension.jma.JarModAgent +import xyz.wagyourtail.unimined.internal.mapping.extension.jma.refmap.annotations.CSliceAnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.jma.refmap.annotations.CTargetAnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.RefmapBuilderClassVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.annotations.DescAnnotationVisitor + +@Suppress("UNUSED_PARAMETER") +open class CRedirectAnnotationVisitor( + descriptor: String, + visible: Boolean, + parent: AnnotationVisitor, + methodAccess: Int, + methodName: String, + methodDescriptor: String, + methodSignature: String?, + methodExceptions: Array?, + refmapBuilder: RefmapBuilderClassVisitor, +) : CAbstractMethodAnnotationVisitor( + descriptor, + visible, + parent, + methodAccess, + methodName, + methodDescriptor, + methodSignature, + methodExceptions, + refmapBuilder +) { + + override val annotationName: String = "@CRedirect" + + companion object { + fun shouldVisit( + descriptor: String, + visible: Boolean, + methodAccess: Int, + methodName: String, + methodDescriptor: String, + methodSignature: String?, + methodExceptions: Array?, + refmapBuilder: RefmapBuilderClassVisitor + ): Boolean { + return descriptor == JarModAgent.Annotation.CREDIRECT + } + } + + + override fun visitAnnotation(name: String, descriptor: String): AnnotationVisitor { + return when (name) { + + AnnotationElement.TARGET -> { + CTargetAnnotationVisitor(super.visitAnnotation(name, descriptor), remap, refmapBuilder) + } + + AnnotationElement.SLICE -> { + CSliceAnnotationVisitor(super.visitAnnotation(name, descriptor), remap, refmapBuilder) + } + + else -> { + super.visitAnnotation(name, descriptor) + } + } + } + + override fun visitArray(name: String): AnnotationVisitor { + return when (name) { + AnnotationElement.METHOD -> { + object: AnnotationVisitor(Constant.ASM_VERSION, if (noRefmap) null else super.visitArray(name)) { + override fun visit(name: String?, value: Any) { + super.visit(name, value) + targetNames.add(value as String) + } + } + } + + else -> { + super.visitArray(name) + } + } + } + + override fun visitEnd() { + val method = if (noRefmap) { + super.visitArray(AnnotationElement.METHOD) + } else { + null + } + remapTargetNames { + method?.visit(null, it) + } + method?.visitEnd() + super.visitEnd() + } + +} diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/refmap/annotations/method/CShadowMethodAnnotationVisitor.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/refmap/annotations/method/CShadowMethodAnnotationVisitor.kt new file mode 100644 index 00000000..80d1692e --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/refmap/annotations/method/CShadowMethodAnnotationVisitor.kt @@ -0,0 +1,74 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension.jma.refmap.annotations.method + +import net.fabricmc.tinyremapper.extension.mixin.common.data.AnnotationElement +import org.objectweb.asm.AnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.jma.JarModAgent +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.RefmapBuilderClassVisitor + +@Suppress("UNUSED_PARAMETER") +class CShadowMethodAnnotationVisitor( + descriptor: String, + visible: Boolean, + parent: AnnotationVisitor, + methodAccess: Int, + methodName: String, + methodDescriptor: String, + methodSignature: String?, + methodExceptions: Array?, + refmapBuilder: RefmapBuilderClassVisitor, +) : CAbstractMethodAnnotationVisitor( + descriptor, + visible, + parent, + methodAccess, + methodName, + methodDescriptor, + methodSignature, + methodExceptions, + refmapBuilder +) { + + override val annotationName: String = "@CShadow" + + companion object{ + fun shouldVisit( + descriptor:String, + visible:Boolean, + methodAccess:Int, + methodName:String, + methodDescriptor:String, + methodSignature:String?, + methodExceptions:Array?, + refmapBuilder:RefmapBuilderClassVisitor + ): Boolean{ + return descriptor == JarModAgent.Annotation.CINJECT + } + } + + override fun visit(name: String?, value: Any) { + if (name == AnnotationElement.VALUE) { + targetNames.add(value as String) + if (!noRefmap) super.visit(name, value) + } else { + super.visit(name, value) + } + } + + override fun getTargetNameAndDescs(targetMethod: String, wildcard: Boolean): Pair> { + return if (targetMethod.contains("(")) { + targetMethod.substringBefore("(") to setOf("(" + targetMethod.substringAfter("(")) + } else { + targetMethod to setOf(methodDescriptor) + } + } + + override fun visitEnd() { + remapTargetNames { + if (noRefmap) { + super.visit(AnnotationElement.VALUE, it) + } + } + super.visitEnd() + } + +} diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/refmap/annotations/method/CWrapCatchAnnotationVisitor.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/refmap/annotations/method/CWrapCatchAnnotationVisitor.kt new file mode 100644 index 00000000..4fdb173b --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/jma/refmap/annotations/method/CWrapCatchAnnotationVisitor.kt @@ -0,0 +1,188 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension.jma.refmap.annotations.method + +import net.fabricmc.tinyremapper.extension.mixin.common.ResolveUtility +import net.fabricmc.tinyremapper.extension.mixin.common.data.AnnotationElement +import net.fabricmc.tinyremapper.extension.mixin.common.data.Constant +import org.objectweb.asm.AnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.ArrayVisitorWrapper +import xyz.wagyourtail.unimined.internal.mapping.extension.jma.JarModAgent +import xyz.wagyourtail.unimined.internal.mapping.extension.jma.refmap.annotations.CSliceAnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.RefmapBuilderClassVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.annotations.DescAnnotationVisitor +import xyz.wagyourtail.unimined.util.orElseOptional +import java.util.* + +@Suppress("UNUSED_PARAMETER") +open class CWrapCatchAnnotationVisitor( + descriptor: String, + visible: Boolean, + parent: AnnotationVisitor, + methodAccess: Int, + methodName: String, + methodDescriptor: String, + methodSignature: String?, + methodExceptions: Array?, + refmapBuilder: RefmapBuilderClassVisitor, +) : CAbstractMethodAnnotationVisitor( + descriptor, + visible, + parent, + methodAccess, + methodName, + methodDescriptor, + methodSignature, + methodExceptions, + refmapBuilder +) { + + override val annotationName: String = "@CWrapCatch" + + companion object { + fun shouldVisit( + descriptor: String, + visible: Boolean, + methodAccess: Int, + methodName: String, + methodDescriptor: String, + methodSignature: String?, + methodExceptions: Array?, + refmapBuilder: RefmapBuilderClassVisitor + ): Boolean { + return descriptor == JarModAgent.Annotation.CWRAPCATCH + } + } + + var targetName: String? = null + + override fun visit(name: String?, value: Any) { + if (name == AnnotationElement.TARGET) { + targetName = value as String + if (!noRefmap) super.visit(name, value) + } else { + super.visit(name, value) + } + } + + override fun visitAnnotation(name: String, descriptor: String): AnnotationVisitor { + return when (name) { + + AnnotationElement.SLICE -> { + CSliceAnnotationVisitor(super.visitAnnotation(name, descriptor), remap, refmapBuilder) + } + + else -> { + super.visitAnnotation(name, descriptor) + } + } + } + + override fun visitArray(name: String): AnnotationVisitor { + return when (name) { + AnnotationElement.VALUE -> { + object: AnnotationVisitor(Constant.ASM_VERSION, if (noRefmap) null else super.visitArray(name)) { + override fun visit(name: String?, value: Any) { + super.visit(name, value) + targetNames.add(value as String) + } + } + } + + else -> { + super.visitArray(name) + } + } + } + + private val targetMethod = Regex("^(L[^;]+;|[^.]+?\\.)([^(]+)\\s*([^>]+)$") + + private fun matchToParts(match: MatchResult): Triple { + val targetOwner = match.groupValues[1].let { + if (it.startsWith("L") && it.endsWith(";")) it.substring( + 1, + it.length - 1 + ) else it.substring(0, it.length - 1) + } + return Triple(targetOwner, match.groupValues[2], match.groupValues[3]) + } + + override fun visitEnd() { + val method = if (noRefmap) { + super.visitArray(AnnotationElement.VALUE) + } else { + null + } + remapTargetNames { + method?.visit(null, it) + } + method?.visitEnd() + if (remap.get() && targetName != null) { + val matchMd = targetMethod.matchEntire(targetName!!) + if (matchMd != null) { + var (targetOwner, targetName, targetDesc) = matchToParts(matchMd) + val target = resolver.resolveMethod( + targetOwner, + targetName, + targetDesc, + ResolveUtility.FLAG_UNIQUE or ResolveUtility.FLAG_RECURSIVE + ).orElseOptional { + existingMappings[this.targetName]?.let { existing -> + logger.info("remapping $existing from existing refmap") + val matchEMd = targetMethod.matchEntire(existing) + if (matchEMd != null) { + val matchResult = matchToParts(matchEMd) + targetOwner = matchResult.first + val mName = matchResult.second + val mDesc = matchResult.third + resolver.resolveMethod( + targetOwner, + mName, + mDesc, + ResolveUtility.FLAG_UNIQUE or ResolveUtility.FLAG_RECURSIVE + ) + } else { + Optional.empty() + } + } ?: Optional.empty() + } + val targetClass = resolver.resolveClass(targetOwner) + targetClass.ifPresent { clz -> + target.ifPresent { + val mappedOwner = mapper.mapName(clz) + val mappedName = mapper.mapName(it) + val mappedDesc = mapper.mapDesc(it) + refmap.addProperty(this.targetName, "L$mappedOwner;$mappedName$mappedDesc") + if (noRefmap) { + super.visit(AnnotationElement.TARGET, "L$mappedOwner;$mappedName$mappedDesc") + } + } + } + if (!target.isPresent || !targetClass.isPresent) { + logger.warn( + "Failed to resolve CWrapCatch target $targetName in mixin ${ + mixinName.replace( + '/', + '.' + ) + }" + ) + if (noRefmap) { + super.visit(AnnotationElement.TARGET, targetName) + } + } + super.visitEnd() + return + } else { + logger.warn("Failed to parse CWrapCatch target $targetName in mixin ${mixinName.replace('/', '.')}") + if (noRefmap && targetName != null) { + super.visit(AnnotationElement.TARGET, targetName!!) + } + } + } else { + if (noRefmap && targetName != null) { + super.visit(AnnotationElement.TARGET, targetName!!) + } + } + super.visitEnd() + } + +} diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/OfficialMixinMetaData.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/OfficialMixinMetaData.kt new file mode 100644 index 00000000..9562e0b6 --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/OfficialMixinMetaData.kt @@ -0,0 +1,121 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension.mixin + +import com.google.gson.GsonBuilder +import com.google.gson.JsonObject +import com.google.gson.JsonParser +import xyz.wagyourtail.unimined.internal.mapping.extension.MixinRemapExtension +import xyz.wagyourtail.unimined.util.forEachInZip +import java.nio.file.FileSystem +import java.nio.file.Path +import java.nio.file.StandardOpenOption +import java.util.concurrent.CompletableFuture +import kotlin.io.path.writeText + +class OfficialMixinMetaData(parent: MixinRemapExtension) : MixinRemapExtension.MixinMetadata(parent) { + private val GSON = GsonBuilder().setPrettyPrinting().create() + private val classesToRefmap = mutableMapOf() + private val refmaps = mutableMapOf() + private val existingRefmaps = mutableMapOf() + private val mixinJsons = mutableMapOf() + + private fun mixinCheck(relativePath: String): Boolean = + relativePath.contains("mixins") && relativePath.endsWith(".json") + + private fun refmapCheck(relativePath: String): Boolean = + relativePath.contains("refmap") && relativePath.endsWith(".json") + + + private fun refmapNameCalculator(relativePath: String): String { + val split = relativePath.split("/") + val name = split[split.size - 1].substringBefore(".json") + return name + "-refmap.json" + } + + + override fun readInput(vararg input: Path): CompletableFuture<*> { + val futures = mutableListOf>() + for (i in input) { + futures.add(readSingleInput(i)) + } + return CompletableFuture.allOf(*futures.toTypedArray()) + } + + private fun readSingleInput(input: Path): CompletableFuture<*> { + return CompletableFuture.runAsync { + input.forEachInZip { file, stream -> + if (refmapCheck(file)) { + try { + parent.logger.info("[PreRead] Found refmap: $file") + val json = JsonParser.parseReader(stream.reader()).asJsonObject + existingRefmaps[file] = json + } catch (e: Exception) { + parent.logger.error("[PreRead] Failed to parse refmap $file: ${e.message}") + } + } else if (mixinCheck(file)) { + try { + parent.logger.info("[PreRead] Found mixin config: $file") + val json = JsonParser.parseReader(stream.reader()).asJsonObject + val refmap = json["refmap"]?.asString ?: refmapNameCalculator(file) + val pkg = json.get("package")?.asString + refmaps.computeIfAbsent(refmap) { JsonObject() } + val mixins = (json["mixins"]?.asJsonArray ?: listOf()) + + (json["client"]?.asJsonArray ?: listOf()) + + (json["server"]?.asJsonArray ?: listOf()) + + parent.logger.info("[PreRead] Found mixins: ${mixins.size}") + for (mixin in mixins) { + val mixinName = mixin.asString + if (classesToRefmap.containsKey("$pkg.$mixinName") && classesToRefmap["$pkg.$mixinName"] != refmap) { + parent.logger.warn("[PreRead] $pkg.$mixinName already has a refmap entry!") + parent.logger.warn("[PreRead] ${classesToRefmap["$pkg.$mixinName"]} != $refmap") + parent.logger.warn("[PreRead] Will only read/write to ${classesToRefmap["$pkg.$mixinName"]} for $pkg.$mixinName") + continue + } + classesToRefmap["$pkg.$mixinName"] = refmap + parent.logger.info("[PreRead] Added $pkg.$mixinName to $refmap") + } + if (parent.noRefmap.contains("BaseMixin")) { + json.remove("refmap") + } else { + json.addProperty("refmap", refmap) + } + mixinJsons[file] = json + } catch (e: Exception) { + parent.logger.error("[PreRead] Failed to parse mixin config $file: ${e.message}") + } + } + } + } + } + + override fun contains(className: String): Boolean { + return classesToRefmap.containsKey(className) + } + + override fun getRefmapFor(className: String): JsonObject { + return refmaps[classesToRefmap[className]]!! + } + + override fun getExistingRefmapFor(className: String): JsonObject? { + return existingRefmaps[classesToRefmap[className]] + } + + override fun writeExtra(fs: FileSystem) { + if (!parent.noRefmap.contains("BaseMixin")) { + for ((name, json) in refmaps) { + parent.logger.info("[Unimined/MixinMetaData] Writing refmap $name") + fs.getPath(name).writeText( + GSON.toJson(json), + Charsets.UTF_8, + StandardOpenOption.CREATE, + StandardOpenOption.TRUNCATE_EXISTING + ) + } + } + for ((name, json) in mixinJsons) { + parent.logger.info("[Unimined/MixinMetaData] Writing mixin config $name") + fs.getPath(name).writeText(GSON.toJson(json), Charsets.UTF_8, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING) + } + } + +} \ No newline at end of file diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/hard/BaseMixinHard.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/hard/BaseMixinHard.kt new file mode 100644 index 00000000..6585a051 --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/hard/BaseMixinHard.kt @@ -0,0 +1,31 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension.mixin.hard + +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.hard.annotations.field.ShadowFieldAnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.hard.annotations.clazz.ImplementsAnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.hard.annotations.clazz.MixinAnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.hard.annotations.method.OverwriteAnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.hard.annotations.method.ShadowMethodAnnotationVisitor + +object BaseMixinHard { + + + fun hardRemapper(hardRemapper: HardTargetRemappingClassVisitor) { + + hardRemapper.classAnnotationVisitors.addAll(listOf( + MixinAnnotationVisitor.Companion::shouldVisit to ::MixinAnnotationVisitor, + ImplementsAnnotationVisitor.Companion::shouldVisit to ::ImplementsAnnotationVisitor + )) + + hardRemapper.methodAnnotationVisitors.addAll(listOf( + OverwriteAnnotationVisitor.Companion::shouldVisit to ::OverwriteAnnotationVisitor, + ShadowMethodAnnotationVisitor.Companion::shouldVisit to ::ShadowMethodAnnotationVisitor + )) + + hardRemapper.fieldAnnotationVisitors.addAll(listOf( + ShadowFieldAnnotationVisitor.Companion::shouldVisit to ::ShadowFieldAnnotationVisitor + )) + + } + + +} \ No newline at end of file diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/hard/HardTargetRemappingClassVisitor.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/hard/HardTargetRemappingClassVisitor.kt new file mode 100644 index 00000000..3d0a0607 --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/hard/HardTargetRemappingClassVisitor.kt @@ -0,0 +1,185 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension.mixin.hard + +import net.fabricmc.tinyremapper.extension.mixin.common.Logger +import net.fabricmc.tinyremapper.extension.mixin.common.data.CommonData +import net.fabricmc.tinyremapper.extension.mixin.common.data.Constant +import net.fabricmc.tinyremapper.extension.mixin.common.data.MxClass +import org.objectweb.asm.AnnotationVisitor +import org.objectweb.asm.ClassVisitor +import org.objectweb.asm.FieldVisitor +import org.objectweb.asm.MethodVisitor +import java.util.Deque +import java.util.concurrent.ConcurrentLinkedDeque +import java.util.concurrent.atomic.AtomicBoolean + +typealias ClassAnnotationPredicate = ( + descriptor: String, + visible: Boolean, + hardTargetRemapper: HardTargetRemappingClassVisitor +) -> Boolean +typealias ClassAnnotationVisitor = ( + descriptor: String, + visible: Boolean, + parent: AnnotationVisitor?, + hardTargetRemapper: HardTargetRemappingClassVisitor +) -> AnnotationVisitor + +typealias MethodAnnotationPredicate = ( + descriptor: String, + visible: Boolean, + methodAccess: Int, + methodName: String, + methodDescriptor: String, + methodSignature: String?, + methodExceptions: Array?, + hardTargetRemapper: HardTargetRemappingClassVisitor +) -> Boolean +typealias MethodAnnotationVisitor = ( + descriptor: String, + visible: Boolean, + parent: AnnotationVisitor?, + methodAccess: Int, + methodName: String, + methodDescriptor: String, + methodSignature: String?, + methodExceptions: Array?, + hardTargetRemapper: HardTargetRemappingClassVisitor +) -> AnnotationVisitor + +typealias FieldAnnotationPredicate = ( + descriptor: String, + visible: Boolean, + access: Int, + fieldName: String, + fieldDescriptor: String, + fieldSignature: String?, + fieldValue: Any?, + hardTargetRemapper: HardTargetRemappingClassVisitor +) -> Boolean +typealias FieldAnnotationVisitor = ( + descriptor: String, + visible: Boolean, + parent: AnnotationVisitor?, + fieldAccess: Int, + fieldName: String, + fieldDescriptor: String, + fieldSignature: String?, + fieldValue: Any?, + hardTargetRemapper: HardTargetRemappingClassVisitor +) -> AnnotationVisitor + +class HardTargetRemappingClassVisitor( + delegate: ClassVisitor?, + val mixinName: String, + val existingMappings: Map, + val logger: Logger, + val onEnd: () -> Unit = {} +) : ClassVisitor(Constant.ASM_VERSION, delegate) { + + lateinit var mxClass: MxClass + + val tasks: Deque<(CommonData) -> Unit> = ConcurrentLinkedDeque() + val targetClasses = mutableSetOf() + val remap = AtomicBoolean(true) + val extraData: MutableMap = mutableMapOf() + + + val classAnnotationVisitors: MutableList> = mutableListOf() + + val methodAnnotationVisitors: MutableList> = mutableListOf() + + val fieldAnnotationVisitors: MutableList> = mutableListOf() + + fun insertVisitor(visitor: (ClassVisitor) -> ClassVisitor) { + val old = this.cv + this.cv = visitor(old) + } + + fun addRemapTask(task: CommonData.() -> Unit) { + tasks.addLast(task) + } + + /** + * should only really be used by tasks that mutate {@link #targetClasses} + */ + fun addRemapTaskFirst(task: CommonData.() -> Unit) { + tasks.addFirst(task) + } + + override fun visit( + version: Int, + access: Int, + name: String, + signature: String?, + superName: String, + interfaces: Array? + ) { + mxClass = MxClass(name) + super.visit(version, access, name, signature, superName, interfaces) + } + + override fun visitAnnotation(descriptor: String?, visible: Boolean): AnnotationVisitor? { + if (descriptor != null) { + for ((predicate, visitor) in classAnnotationVisitors) { + if (predicate(descriptor, visible, this)) { + return visitor(descriptor, visible, super.visitAnnotation(descriptor, visible), this) + } + } + } + return super.visitAnnotation(descriptor, visible) + } + + override fun visitMethod( + access: Int, + methodName: String, + methodDescriptor: String, + signature: String?, + exceptions: Array? + ): MethodVisitor { + return object : MethodVisitor(Constant.ASM_VERSION, super.visitMethod(access, methodName, methodDescriptor, signature, exceptions)) { + override fun visitAnnotation(descriptor: String?, visible: Boolean): AnnotationVisitor? { + if (descriptor != null) { + for ((predicate, visitor) in methodAnnotationVisitors) { + if (predicate(descriptor, visible, access, methodName, methodDescriptor, signature, exceptions, this@HardTargetRemappingClassVisitor)) { + return visitor(descriptor, visible, super.visitAnnotation(descriptor, visible), access, methodName, methodDescriptor, signature, exceptions, this@HardTargetRemappingClassVisitor) + } + } + } + return super.visitAnnotation(descriptor, visible) + } + } + } + + override fun visitField( + access: Int, + fieldName: String, + fieldDescriptor: String, + signature: String?, + value: Any? + ): FieldVisitor { + return object : FieldVisitor(Constant.ASM_VERSION, super.visitField(access, fieldName, fieldDescriptor, signature, value)) { + override fun visitAnnotation(descriptor: String?, visible: Boolean): AnnotationVisitor? { + if (descriptor != null) { + for ((predicate, visitor) in fieldAnnotationVisitors) { + if (predicate(descriptor, visible, access, fieldName, fieldDescriptor, signature, value, this@HardTargetRemappingClassVisitor)) { + return visitor(descriptor, visible, super.visitAnnotation(descriptor, visible), access, fieldName, fieldDescriptor, signature, value, this@HardTargetRemappingClassVisitor) + } + } + } + return super.visitAnnotation(descriptor, visible) + } + } + } + + override fun visitEnd() { + super.visitEnd() + onEnd() + } + + fun runRemap(data: CommonData) { + for (task in tasks) { + task(data) + } + } + +} diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/hard/annotations/clazz/ImplementsAnnotationVisitor.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/hard/annotations/clazz/ImplementsAnnotationVisitor.kt new file mode 100644 index 00000000..4fe2203c --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/hard/annotations/clazz/ImplementsAnnotationVisitor.kt @@ -0,0 +1,103 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension.mixin.hard.annotations.clazz + +import net.fabricmc.tinyremapper.extension.mixin.common.ResolveUtility +import net.fabricmc.tinyremapper.extension.mixin.common.data.Annotation +import net.fabricmc.tinyremapper.extension.mixin.common.data.AnnotationElement +import net.fabricmc.tinyremapper.extension.mixin.common.data.Constant +import org.objectweb.asm.* +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.hard.HardTargetRemappingClassVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.ArrayVisitorWrapper + +class ImplementsAnnotationVisitor( + descriptor: String, + visible: Boolean, + parent: AnnotationVisitor?, + private val hardTargetRemapper: HardTargetRemappingClassVisitor +) : AnnotationVisitor(Constant.ASM_VERSION, parent) { + + companion object { + + fun shouldVisit(descriptor: String, visible: Boolean, hardTargetRemapper: HardTargetRemappingClassVisitor): Boolean { + return descriptor == Annotation.IMPLEMENTS + } + + } + + override fun visitArray(name: String?): AnnotationVisitor? { + val delegate: AnnotationVisitor? = super.visitArray(name) + if (name == AnnotationElement.VALUE) { + return ArrayVisitorWrapper(Constant.ASM_VERSION, delegate) { InterfaceAnnotationVisitor(it, hardTargetRemapper) } + } + return delegate + } + + class InterfaceAnnotationVisitor( + parent: AnnotationVisitor?, + private val hardTargetRemapper: HardTargetRemappingClassVisitor + ) : AnnotationVisitor(Constant.ASM_VERSION, parent) { + + lateinit var iface: Type + lateinit var prefix: String + var remap: Remap = Remap.ALL + + override fun visit(name: String?, value: Any) { + when (name) { + AnnotationElement.IFACE -> { + iface = value as Type + } + AnnotationElement.PREFIX -> { + prefix = value as String + } + } + super.visit(name, value) + } + + override fun visitEnum(name: String?, descriptor: String, value: String) { + super.visitEnum(name, descriptor, value) + when (name) { + AnnotationElement.REMAP -> { + remap = Remap.valueOf(value) + } + } + } + + override fun visitEnd() { + super.visitEnd() + hardTargetRemapper.insertVisitor { + object : ClassVisitor(Constant.ASM_VERSION, it) { + override fun visitMethod( + access: Int, + name: String, + descriptor: String, + signature: String?, + exceptions: Array? + ): MethodVisitor? { + hardTargetRemapper.addRemapTask { + val prefixed = name.startsWith(prefix) + if (remap == Remap.NONE || (remap == Remap.ONLY_PREFIXED && !prefixed)) return@addRemapTask + // TODO: force check + val fixedName = if (prefixed) name.substring(prefix.length) else name + // check to remap + val resolved = resolver.resolveMethod(iface.internalName, fixedName, descriptor, ResolveUtility.FLAG_UNIQUE or ResolveUtility.FLAG_RECURSIVE) + resolved.ifPresent { + val mappedName = if (prefixed) "${prefix}${mapper.mapName(resolved.get())}" else mapper.mapName(resolved.get()) + propagate(hardTargetRemapper.mxClass.getMethod(name, descriptor).asTrMember(resolver), mappedName) + } + } + return super.visitMethod(access, name, descriptor, signature, exceptions) + } + } + } + } + + + } + + enum class Remap { + ALL, // match all, not just prefixed + FORCE, // match prefixed, non-prefixed that match and are remapped will throw + ONLY_PREFIXED, // match prefixed only + NONE // match none + } + +} diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/hard/annotations/clazz/MixinAnnotationVisitor.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/hard/annotations/clazz/MixinAnnotationVisitor.kt new file mode 100644 index 00000000..21c03be3 --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/hard/annotations/clazz/MixinAnnotationVisitor.kt @@ -0,0 +1,95 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension.mixin.hard.annotations.clazz + +import net.fabricmc.tinyremapper.extension.mixin.common.data.Annotation +import net.fabricmc.tinyremapper.extension.mixin.common.data.AnnotationElement +import net.fabricmc.tinyremapper.extension.mixin.common.data.Constant +import org.objectweb.asm.AnnotationVisitor +import org.objectweb.asm.Type +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.hard.HardTargetRemappingClassVisitor +import xyz.wagyourtail.unimined.util.orElseOptional +import java.util.* + +class MixinAnnotationVisitor( + descriptor: String, + visible: Boolean, + parent: AnnotationVisitor?, + private val hardTargetRemapper: HardTargetRemappingClassVisitor +) : AnnotationVisitor(Constant.ASM_VERSION, parent) { + + + companion object { + + fun shouldVisit(descriptor: String, visible: Boolean, hardTargetRemapper: HardTargetRemappingClassVisitor): Boolean { + return descriptor == Annotation.MIXIN + } + + } + + private val remap = hardTargetRemapper.remap + private val logger = hardTargetRemapper.logger + private val existingMappings = hardTargetRemapper.existingMappings + private val targetClasses = hardTargetRemapper.targetClasses + private val mixinName = hardTargetRemapper.mixinName + + private val classTargets = mutableListOf() + private val classValues = mutableListOf() + + override fun visit(name: String, value: Any) { + super.visit(name, value) + logger.info("Found annotation value $name: $value") + if (name == AnnotationElement.REMAP) { + remap.set(value as Boolean) + } + } + + override fun visitArray(name: String?): AnnotationVisitor { + return when (name) { + AnnotationElement.TARGETS -> { + return object: AnnotationVisitor(Constant.ASM_VERSION, super.visitArray(name)) { + override fun visit(name: String?, value: Any) { + classTargets.add(value as String) + super.visit(name, value) + } + } + } + + AnnotationElement.VALUE, null -> { + return object: AnnotationVisitor(Constant.ASM_VERSION, super.visitArray(name)) { + override fun visit(name: String?, value: Any) { + classValues.add((value as Type).internalName) + super.visit(name, value) + } + } + } + + else -> { + super.visitArray(name) + } + } + } + + override fun visitEnd() { + super.visitEnd() + hardTargetRemapper.addRemapTaskFirst { + if (remap.get()) { + logger.info("existing mappings: $existingMappings") + for (target in classTargets.toSet()) { + val clz = resolver.resolveClass(target.replace('.', '/')) + .orElseOptional { + existingMappings[target]?.let { + logger.info("remapping $it from existing refmap") + classTargets.remove(target) + classTargets.add(it) + resolver.resolveClass(it) + } ?: Optional.empty() + } + if (!clz.isPresent) { + logger.warn("Failed to resolve class $target in mixin ${mixinName.replace('/', '.')}") + } + } + } + targetClasses.addAll((classValues + classTargets.map { it.replace('.', '/') }).toSet()) + } + } + +} \ No newline at end of file diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/hard/annotations/field/ShadowFieldAnnotationVisitor.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/hard/annotations/field/ShadowFieldAnnotationVisitor.kt new file mode 100644 index 00000000..80add92a --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/hard/annotations/field/ShadowFieldAnnotationVisitor.kt @@ -0,0 +1,82 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension.mixin.hard.annotations.field + +import net.fabricmc.tinyremapper.extension.mixin.common.ResolveUtility +import net.fabricmc.tinyremapper.extension.mixin.common.data.Annotation +import net.fabricmc.tinyremapper.extension.mixin.common.data.AnnotationElement +import net.fabricmc.tinyremapper.extension.mixin.common.data.Constant +import org.objectweb.asm.AnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.hard.HardTargetRemappingClassVisitor +import java.util.concurrent.atomic.AtomicBoolean + +class ShadowFieldAnnotationVisitor( + descriptor: String, + visible: Boolean, + parent: AnnotationVisitor?, + fieldAccess: Int, + protected val fieldName: String, + protected val fieldDescriptor: String, + fieldSignature: String?, + fieldValue: Any?, + protected val hardTargetRemapper: HardTargetRemappingClassVisitor, +) : AnnotationVisitor( + Constant.ASM_VERSION, + parent +) { + + protected val remap = AtomicBoolean(hardTargetRemapper.remap.get()) + protected val logger = hardTargetRemapper.logger + protected val existingMappings = hardTargetRemapper.existingMappings + protected val targetClasses = hardTargetRemapper.targetClasses + protected val mixinName = hardTargetRemapper.mixinName + + companion object { + + fun shouldVisit( + descriptor: String, + visible: Boolean, + access: Int, + fieldName: String, + fieldDescriptor: String, + fieldSignature: String?, + fieldValue: Any?, + hardTargetRemapper: HardTargetRemappingClassVisitor + ): Boolean { + return descriptor == Annotation.SHADOW + } + + } + + var prefix: String = "shadow$" + + override fun visit(name: String?, value: Any?) { + if (name == AnnotationElement.REMAP) { + remap.set(value as Boolean) + } + if (name == AnnotationElement.PREFIX) { + prefix = value as String + } + super.visit(name, value) + } + + override fun visitEnd() { + hardTargetRemapper.addRemapTask { + if (remap.get()) { + val prefixed = fieldName.startsWith(prefix) + val fixedName = if (prefixed) fieldName.substring(prefix.length) else fieldName + for (target in targetClasses) { + val resolved = resolver.resolveField(target, fixedName, fieldDescriptor, ResolveUtility.FLAG_UNIQUE or ResolveUtility.FLAG_RECURSIVE) + resolved.ifPresent { + val mappedName = if (prefixed) "${prefix}${mapper.mapName(resolved.get())}" else mapper.mapName(resolved.get()) + propagate(hardTargetRemapper.mxClass.getField(fieldName, fieldDescriptor).asTrMember(resolver), mappedName) + } + if (resolved.isPresent) { + return@addRemapTask + } + } + logger.warn("Could not find target field for @Shadow $fieldName:$fieldDescriptor in $mixinName") + } + } + super.visitEnd() + } + +} \ No newline at end of file diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/hard/annotations/method/AbstractMethodAnnotationVisitor.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/hard/annotations/method/AbstractMethodAnnotationVisitor.kt new file mode 100644 index 00000000..861a83fc --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/hard/annotations/method/AbstractMethodAnnotationVisitor.kt @@ -0,0 +1,28 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension.mixin.hard.annotations.method + +import net.fabricmc.tinyremapper.extension.mixin.common.data.Constant +import org.objectweb.asm.AnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.hard.HardTargetRemappingClassVisitor +import java.util.concurrent.atomic.AtomicBoolean + + +abstract class AbstractMethodAnnotationVisitor( + descriptor: String, + visible: Boolean, + parent: AnnotationVisitor?, + methodAccess: Int, + protected val methodName: String, + protected val methodDescriptor: String, + protected val methodSignature: String?, + methodExceptions: Array?, + protected val hardTargetRemapper: HardTargetRemappingClassVisitor +) : AnnotationVisitor(Constant.ASM_VERSION, parent) { + + protected open val remap = AtomicBoolean(hardTargetRemapper.remap.get()) + protected val logger = hardTargetRemapper.logger + protected val existingMappings = hardTargetRemapper.existingMappings + protected val targetClasses = hardTargetRemapper.targetClasses + protected val mixinName = hardTargetRemapper.mixinName + + +} \ No newline at end of file diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/hard/annotations/method/OverwriteAnnotationVisitor.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/hard/annotations/method/OverwriteAnnotationVisitor.kt new file mode 100644 index 00000000..12708fb0 --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/hard/annotations/method/OverwriteAnnotationVisitor.kt @@ -0,0 +1,73 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension.mixin.hard.annotations.method + +import net.fabricmc.tinyremapper.extension.mixin.common.ResolveUtility +import net.fabricmc.tinyremapper.extension.mixin.common.data.Annotation +import net.fabricmc.tinyremapper.extension.mixin.common.data.AnnotationElement +import org.objectweb.asm.AnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.hard.HardTargetRemappingClassVisitor + +class OverwriteAnnotationVisitor( + descriptor: String, + visible: Boolean, + parent: AnnotationVisitor?, + methodAccess: Int, + methodName: String, + methodDescriptor: String, + methodSignature: String?, + methodExceptions: Array?, + refmapBuilder: HardTargetRemappingClassVisitor, +) : AbstractMethodAnnotationVisitor( + descriptor, + visible, + parent, + methodAccess, + methodName, + methodDescriptor, + methodSignature, + methodExceptions, + refmapBuilder +) { + + companion object { + + fun shouldVisit( + descriptor: String, + visible: Boolean, + methodAccess: Int, + methodName: String, + methodDescriptor: String, + methodSignature: String?, + methodExceptions: Array?, + hardTargetRemapper: HardTargetRemappingClassVisitor + ): Boolean { + return descriptor == Annotation.OVERWRITE + } + + } + + override fun visit(name: String?, value: Any?) { + if (name == AnnotationElement.REMAP) { + remap.set(value as Boolean) + } + super.visit(name, value) + } + + override fun visitEnd() { + hardTargetRemapper.addRemapTask { + if (remap.get()) { + for (target in targetClasses) { + val resolved = resolver.resolveMethod(target, methodName, methodDescriptor, ResolveUtility.FLAG_UNIQUE or ResolveUtility.FLAG_RECURSIVE) + resolved.ifPresent { + propagate(hardTargetRemapper.mxClass.getMethod(methodName, methodDescriptor).asTrMember(resolver), mapper.mapName(it)) + } + if (resolved.isPresent) { + return@addRemapTask + } + } + logger.warn("Could not find target method for @Overwrite $methodName$methodDescriptor in $mixinName") + } + } + super.visitEnd() + } + +} \ No newline at end of file diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/hard/annotations/method/ShadowMethodAnnotationVisitor.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/hard/annotations/method/ShadowMethodAnnotationVisitor.kt new file mode 100644 index 00000000..8dfa8b7f --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/hard/annotations/method/ShadowMethodAnnotationVisitor.kt @@ -0,0 +1,81 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension.mixin.hard.annotations.method + +import net.fabricmc.tinyremapper.extension.mixin.common.ResolveUtility +import net.fabricmc.tinyremapper.extension.mixin.common.data.Annotation +import net.fabricmc.tinyremapper.extension.mixin.common.data.AnnotationElement +import org.objectweb.asm.AnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.hard.HardTargetRemappingClassVisitor + +class ShadowMethodAnnotationVisitor( + descriptor: String, + visible: Boolean, + parent: AnnotationVisitor?, + methodAccess: Int, + methodName: String, + methodDescriptor: String, + methodSignature: String?, + methodExceptions: Array?, + refmapBuilder: HardTargetRemappingClassVisitor, +) : AbstractMethodAnnotationVisitor( + descriptor, + visible, + parent, + methodAccess, + methodName, + methodDescriptor, + methodSignature, + methodExceptions, + refmapBuilder +) { + + companion object { + + fun shouldVisit( + descriptor: String, + visible: Boolean, + methodAccess: Int, + methodName: String, + methodDescriptor: String, + methodSignature: String?, + methodExceptions: Array?, + hardTargetRemapper: HardTargetRemappingClassVisitor + ): Boolean { + return descriptor == Annotation.SHADOW + } + + } + + var prefix: String = "shadow$" + + override fun visit(name: String?, value: Any?) { + if (name == AnnotationElement.REMAP) { + remap.set(value as Boolean) + } + if (name == AnnotationElement.PREFIX) { + prefix = value as String + } + super.visit(name, value) + } + + override fun visitEnd() { + hardTargetRemapper.addRemapTask { + if (remap.get()) { + val prefixed = methodName.startsWith(prefix) + val fixedName = if (prefixed) methodName.substring(prefix.length) else methodName + for (target in targetClasses) { + val resolved = resolver.resolveMethod(target, fixedName, methodDescriptor, ResolveUtility.FLAG_UNIQUE or ResolveUtility.FLAG_RECURSIVE) + resolved.ifPresent { + val mappedName = if (prefixed) "${prefix}${mapper.mapName(resolved.get())}" else mapper.mapName(resolved.get()) + propagate(hardTargetRemapper.mxClass.getMethod(methodName, methodDescriptor).asTrMember(resolver), mappedName) + } + if (resolved.isPresent) { + return@addRemapTask + } + } + logger.warn("Could not find target method for @Shadow $methodName$methodDescriptor in $mixinName") + } + } + super.visitEnd() + } + +} \ No newline at end of file diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/refmap/BaseMixinRefmap.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/refmap/BaseMixinRefmap.kt new file mode 100644 index 00000000..8a87936e --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/refmap/BaseMixinRefmap.kt @@ -0,0 +1,30 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap + +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.annotations.clazz.MixinAnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.annotations.method.* + + +object BaseMixinRefmap { + + + fun refmapBuilder(refmapBuilder: RefmapBuilderClassVisitor) { + + refmapBuilder.classAnnotationVisitors.addAll(listOf( + MixinAnnotationVisitor.Companion::shouldVisit to ::MixinAnnotationVisitor + )) + + refmapBuilder.methodAnnotationVisitors.addAll(listOf( + AccessorAnnotationVisitor.Companion::shouldVisit to ::AccessorAnnotationVisitor, + InvokerAnnotationVisitor.Companion::shouldVisit to ::InvokerAnnotationVisitor, + InjectAnnotationVisitor.Companion::shouldVisit to ::InjectAnnotationVisitor, + ModifyArgAnnotationVisitor.Companion::shouldVisit to ::ModifyArgAnnotationVisitor, + ModifyArgsAnnotationVisitor.Companion::shouldVisit to ::ModifyArgsAnnotationVisitor, + ModifyConstantAnnotationVisitor.Companion::shouldVisit to ::ModifyConstantAnnotationVisitor, + ModifyVariableAnnotationVisitor.Companion::shouldVisit to ::ModifyVariableAnnotationVisitor, + RedirectAnnotationVisitor.Companion::shouldVisit to ::RedirectAnnotationVisitor, + )) + + } + + +} \ No newline at end of file diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/refmap/RefmapBuilderClassVisitor.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/refmap/RefmapBuilderClassVisitor.kt new file mode 100644 index 00000000..69a1d091 --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/refmap/RefmapBuilderClassVisitor.kt @@ -0,0 +1,162 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap + +import com.google.gson.JsonObject +import net.fabricmc.tinyremapper.extension.mixin.common.data.CommonData +import net.fabricmc.tinyremapper.extension.mixin.common.data.Constant +import org.objectweb.asm.AnnotationVisitor +import org.objectweb.asm.ClassVisitor +import org.objectweb.asm.FieldVisitor +import org.objectweb.asm.MethodVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.MixinRemapExtension +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.annotations.clazz.MixinAnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.annotations.method.* +import java.util.concurrent.atomic.AtomicBoolean + + +typealias ClassAnnotationPredicate = ( + descriptor: String, + visible: Boolean, + refmapBuilder: RefmapBuilderClassVisitor + ) -> Boolean +typealias ClassAnnotationVisitor = ( + descriptor: String, + visible: Boolean, + parent: AnnotationVisitor, + refmapBuilder: RefmapBuilderClassVisitor + ) -> AnnotationVisitor + +typealias MethodAnnotationPredicate = ( + descriptor: String, + visible: Boolean, + methodAccess: Int, + methodName: String, + methodDescriptor: String, + methodSignature: String?, + methodExceptions: Array?, + refmapBuilder: RefmapBuilderClassVisitor + ) -> Boolean +typealias MethodAnnotationVisitor = ( + descriptor: String, + visible: Boolean, + parent: AnnotationVisitor, + methodAccess: Int, + methodName: String, + methodDescriptor: String, + methodSignature: String?, + methodExceptions: Array?, + refmapBuilder: RefmapBuilderClassVisitor + ) -> AnnotationVisitor + +typealias FieldAnnotationPredicate = ( + descriptor: String, + visible: Boolean, + access: Int, + fieldName: String, + fieldDescriptor: String, + fieldSignature: String?, + fieldValue: Any?, + refmapBuilder: RefmapBuilderClassVisitor + ) -> Boolean +typealias FieldAnnotationVisitor = ( + descriptor: String, + visible: Boolean, + parent: AnnotationVisitor, + fieldAccess: Int, + fieldName: String, + fieldDescriptor: String, + fieldSignature: String?, + fieldValue: Any?, + refmapBuilder: RefmapBuilderClassVisitor + ) -> AnnotationVisitor + +class RefmapBuilderClassVisitor( + commonData: CommonData, + val mixinName: String, + val refmap: JsonObject, + delegate: ClassVisitor, + val existingMappings: Map, + val mixinRemapExtension: MixinRemapExtension, + private val onEnd: () -> Unit = {}, + val allowImplicitWildcards: Boolean = false +) : ClassVisitor(Constant.ASM_VERSION, delegate) { + + val mapper = commonData.mapper + val resolver = commonData.resolver + val logger = commonData.logger + + val classAnnotationVisitors: MutableList> = mutableListOf() + + val fieldAnnotationVisitors: MutableList> = mutableListOf() + + val methodAnnotationVisitors: MutableList> = mutableListOf() + + val targetClasses = mutableSetOf() + val remap = AtomicBoolean(true) + + val extraData: MutableMap = mutableMapOf() + + fun insertVisitor(visitor: (ClassVisitor) -> ClassVisitor) { + val old = this.cv + this.cv = visitor(old) + } + + override fun visitAnnotation(descriptor: String?, visible: Boolean): AnnotationVisitor { + if (descriptor != null) { + for ((predicate, visitor) in classAnnotationVisitors) { + if (predicate(descriptor, visible, this)) { + return visitor(descriptor, visible, super.visitAnnotation(descriptor, visible), this) + } + } + } + return super.visitAnnotation(descriptor, visible) + } + + override fun visitMethod( + access: Int, + methodName: String, + methodDescriptor: String, + signature: String?, + exceptions: Array? + ): MethodVisitor { + return object : MethodVisitor(Constant.ASM_VERSION, super.visitMethod(access, methodName, methodDescriptor, signature, exceptions)) { + override fun visitAnnotation(descriptor: String?, visible: Boolean): AnnotationVisitor { + if (descriptor != null) { + for ((predicate, visitor) in methodAnnotationVisitors) { + if (predicate(descriptor, visible, access, methodName, methodDescriptor, signature, exceptions, this@RefmapBuilderClassVisitor)) { + return visitor(descriptor, visible, super.visitAnnotation(descriptor, visible), access, methodName, methodDescriptor, signature, exceptions, this@RefmapBuilderClassVisitor) + } + } + } + return super.visitAnnotation(descriptor, visible) + } + } + } + + override fun visitField( + access: Int, + fieldName: String, + fieldDescriptor: String, + signature: String?, + value: Any? + ): FieldVisitor { + return object : FieldVisitor(Constant.ASM_VERSION, super.visitField(access, fieldName, fieldDescriptor, signature, value)) { + override fun visitAnnotation(descriptor: String?, visible: Boolean): AnnotationVisitor { + if (descriptor != null) { + for ((predicate, visitor) in fieldAnnotationVisitors) { + if (predicate(descriptor, visible, access, fieldName, fieldDescriptor, signature, value, this@RefmapBuilderClassVisitor)) { + return visitor(descriptor, visible, super.visitAnnotation(descriptor, visible), access, fieldName, fieldDescriptor, signature, value, this@RefmapBuilderClassVisitor) + } + } + } + return super.visitAnnotation(descriptor, visible) + } + } + } + + override fun visitEnd() { + super.visitEnd() + onEnd() + } + + +} diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/refmap/annotations/AtAnnotationVisitor.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/refmap/annotations/AtAnnotationVisitor.kt new file mode 100644 index 00000000..9f02c07f --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/refmap/annotations/AtAnnotationVisitor.kt @@ -0,0 +1,267 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.annotations + +import net.fabricmc.tinyremapper.extension.mixin.common.ResolveUtility +import net.fabricmc.tinyremapper.extension.mixin.common.StringUtility +import net.fabricmc.tinyremapper.extension.mixin.common.data.AnnotationElement +import net.fabricmc.tinyremapper.extension.mixin.common.data.Constant +import org.objectweb.asm.AnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.RefmapBuilderClassVisitor +import xyz.wagyourtail.unimined.util.orElseOptional +import java.util.* +import java.util.concurrent.atomic.AtomicBoolean + +class AtAnnotationVisitor(parent: AnnotationVisitor?, remap: AtomicBoolean, private val refmapBuilder: RefmapBuilderClassVisitor) : AnnotationVisitor(Constant.ASM_VERSION, parent) { + private val remapAt by lazy { AtomicBoolean(remap.get()) } + private var targetName: String? = null + + private val resolver = refmapBuilder.resolver + private val logger = refmapBuilder.logger + private val existingMappings = refmapBuilder.existingMappings + private val mapper = refmapBuilder.mapper + private val refmap = refmapBuilder.refmap + private val mixinName = refmapBuilder.mixinName + private val noRefmap = refmapBuilder.mixinRemapExtension.noRefmap.contains("BaseMixin") + + override fun visit(name: String?, value: Any) { + when (name) { + AnnotationElement.REMAP -> { + super.visit(name, value) + remapAt.set(value as Boolean) + } + AnnotationElement.TARGET -> { + if (!noRefmap) super.visit(name, value) + targetName = (value as String).replace(" ", "") + } + else -> super.visit(name, value) + } + } + + override fun visitAnnotation(name: String, descriptor: String): AnnotationVisitor { + return if (name == AnnotationElement.DESC) { + return DescAnnotationVisitor(super.visitAnnotation(name, descriptor), remapAt, refmapBuilder) + } else { + logger.warn("Found annotation in target descriptor: $name $descriptor") + super.visitAnnotation(name, descriptor) + } + } + + private val targetField = Regex("^(L[^;]+;|[^.]+?\\.)([^:]+):(.+)$") + private val targetMethod = Regex("^(L[^;]+;|[^.]+?\\.)([^(]+)\\s*([^>]+)$") + + + private fun matchToParts(match: MatchResult): Triple { + val targetOwner = match.groupValues[1].let { + if (it.startsWith("L") && it.endsWith(";")) it.substring( + 1, + it.length - 1 + ) else it.substring(0, it.length - 1) + } + return Triple(targetOwner, match.groupValues[2], match.groupValues[3]) + } + + override fun visitEnd() { + if (remapAt.get() && targetName != null) { + val matchFd = targetField.matchEntire(targetName!!) + if (matchFd != null) { + logger.info("Found field target: $targetName") + var (targetOwner, targetName, targetDesc) = matchToParts(matchFd) + val target = resolver.resolveField( + targetOwner, + targetName, + targetDesc, + ResolveUtility.FLAG_UNIQUE or ResolveUtility.FLAG_RECURSIVE + ).orElseOptional { + existingMappings[this.targetName]?.let { existing -> + logger.info("remapping $existing from existing refmap") + val matchEFd = targetField.matchEntire(existing) + if (matchEFd != null) { + val matchResult = matchToParts(matchEFd) + targetOwner = matchResult.first + val fName = matchResult.second + val fDesc = matchResult.third + resolver.resolveField( + targetOwner, + fName, + fDesc, + ResolveUtility.FLAG_UNIQUE or ResolveUtility.FLAG_RECURSIVE + ) + } else { + Optional.empty() + } + } ?: Optional.empty() + } + val targetClass = resolver.resolveClass(targetOwner) + targetClass.ifPresent { clz -> + target.ifPresent { + val mappedOwner = mapper.mapName(clz) + val mappedName = mapper.mapName(it) + val mappedDesc = mapper.mapDesc(it) + refmap.addProperty(this.targetName, "L$mappedOwner;$mappedName:$mappedDesc") + if (noRefmap) { + super.visit(AnnotationElement.TARGET, "L$mappedOwner;$mappedName:$mappedDesc") + } + } + } + if (!target.isPresent || !targetClass.isPresent) { + logger.warn( + "Failed to resolve At target $targetName in mixin ${ + mixinName.replace( + '/', + '.' + ) + }" + ) + if (noRefmap) { + super.visit(AnnotationElement.TARGET, this.targetName!!) + } + } + super.visitEnd() + return + } + val matchMd = targetMethod.matchEntire(targetName!!) + if (matchMd != null) { + logger.info("Found method target: $targetName") + var (targetOwner, targetName, targetDesc) = matchToParts(matchMd) + val target = resolver.resolveMethod( + targetOwner, + targetName, + targetDesc, + ResolveUtility.FLAG_UNIQUE or ResolveUtility.FLAG_RECURSIVE + ).orElseOptional { + existingMappings[this.targetName]?.let { existing -> + logger.info("remapping $existing from existing refmap") + val matchEMd = targetMethod.matchEntire(existing) + if (matchEMd != null) { + val matchResult = matchToParts(matchEMd) + targetOwner = matchResult.first + val mName = matchResult.second + val mDesc = matchResult.third + resolver.resolveMethod( + targetOwner, + mName, + mDesc, + ResolveUtility.FLAG_UNIQUE or ResolveUtility.FLAG_RECURSIVE + ) + } else { + Optional.empty() + } + } ?: Optional.empty() + } + val targetClass = resolver.resolveClass(targetOwner) + targetClass.ifPresent { clz -> + target.ifPresent { + val mappedOwner = mapper.mapName(clz) + val mappedName = mapper.mapName(it) + val mappedDesc = mapper.mapDesc(it) + refmap.addProperty(this.targetName, "L$mappedOwner;$mappedName$mappedDesc") + if (noRefmap) { + super.visit(AnnotationElement.TARGET, "L$mappedOwner;$mappedName$mappedDesc") + } + } + } + if (!target.isPresent || !targetClass.isPresent) { + logger.warn( + "Failed to resolve At target $targetName in mixin ${ + mixinName.replace( + '/', + '.' + ) + }" + ) + if (noRefmap) { + super.visit(AnnotationElement.TARGET, this.targetName!!) + } + } + super.visitEnd() + return + } + if (targetName!!.startsWith("(")) { + logger.info("Found descriptor target: $targetName") + val existing = existingMappings[this.targetName] + if (existing != null) { + logger.info("remapping $existing from existing refmap") + val mapped = mapper.asTrRemapper().mapDesc(existing) + if (mapped == existing) { + logger.warn("Failed to remap $existing") + if (noRefmap) { + super.visit(AnnotationElement.TARGET, this.targetName!!) + } + super.visitEnd() + return + } + refmap.addProperty(this.targetName, mapped) + super.visitEnd() + return + } else { + val mapped = mapper.asTrRemapper().mapDesc(targetName) + if (mapped == targetName) { + logger.warn("Failed to remap $targetName") + if (noRefmap) { + super.visit(AnnotationElement.TARGET, this.targetName!!) + } + super.visitEnd() + return + } else { + refmap.addProperty(this.targetName, mapped) + if (noRefmap) { + super.visit(AnnotationElement.TARGET, mapped) + } + super.visitEnd() + return + } + } + } + + // else is probably a class + // we will count (NEW, ) as that too + // carefully, by modifying method so it doesn't capture it + val fixedTarget = if (targetName!!.startsWith("L")) { + targetName!!.substring(1).substringBefore("").substringBefore(";") + } else { + targetName!!.substringBefore(".") + }.replace('.', '/') + if (StringUtility.isClassName(fixedTarget)) { + val target = resolver.resolveClass(fixedTarget).orElseOptional { + existingMappings[this.targetName]?.let { + logger.info("remapping $it from existing refmap") + resolver.resolveClass( + if (it.startsWith("L") && it.endsWith(";")) { + it.substring(1, it.length - 1) + } else { + it + }.replace('.', '/') + ) + } ?: Optional.empty() + } + target.ifPresent { + val mapped = mapper.mapName(it) + refmap.addProperty(this.targetName, "L$mapped;") + if (noRefmap) { + super.visit(AnnotationElement.TARGET, "L$mapped;") + } + } + if (target.isPresent) { + super.visitEnd() + return + } + } + + logger.warn( + "Failed to parse target descriptor: $targetName ($fixedTarget) in mixin ${ + mixinName.replace( + '/', + '.' + ) + }" + ) + if (noRefmap) { + super.visit(AnnotationElement.TARGET, this.targetName!!) + } + } else if (targetName != null) { + if (noRefmap) { + super.visit(AnnotationElement.TARGET, this.targetName!!) + } + } + super.visitEnd() + } +} \ No newline at end of file diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/refmap/annotations/DescAnnotationVisitor.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/refmap/annotations/DescAnnotationVisitor.kt new file mode 100644 index 00000000..d3a51253 --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/refmap/annotations/DescAnnotationVisitor.kt @@ -0,0 +1,19 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.annotations + +import net.fabricmc.tinyremapper.extension.mixin.common.data.Constant +import org.objectweb.asm.AnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.RefmapBuilderClassVisitor +import java.util.concurrent.atomic.AtomicBoolean + +@Suppress("UNUSED_PARAMETER") +class DescAnnotationVisitor (parent: AnnotationVisitor?, remap: AtomicBoolean, refmapBuilder: RefmapBuilderClassVisitor) : AnnotationVisitor(Constant.ASM_VERSION, parent) { + + override fun visit(name: String, value: Any) { + TODO("Desc not supported yet.") + } + + override fun visitAnnotation(name: String, descriptor: String?): AnnotationVisitor { + TODO("Desc not supported yet.") + } + +} diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/refmap/annotations/SliceAnnotationVisitor.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/refmap/annotations/SliceAnnotationVisitor.kt new file mode 100644 index 00000000..4864f0f1 --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/refmap/annotations/SliceAnnotationVisitor.kt @@ -0,0 +1,19 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.annotations + +import net.fabricmc.tinyremapper.extension.mixin.common.data.AnnotationElement +import net.fabricmc.tinyremapper.extension.mixin.common.data.Constant +import org.objectweb.asm.AnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.RefmapBuilderClassVisitor +import java.util.concurrent.atomic.AtomicBoolean + +class SliceAnnotationVisitor(parent: AnnotationVisitor?, val remap: AtomicBoolean, private val refmapBuilder: RefmapBuilderClassVisitor) : AnnotationVisitor(Constant.ASM_VERSION, parent) { + + override fun visitAnnotation(name: String, descriptor: String): AnnotationVisitor { + return if (name == AnnotationElement.FROM || name == AnnotationElement.TO) { + AtAnnotationVisitor(super.visitAnnotation(name, descriptor), remap, refmapBuilder) + } else { + super.visitAnnotation(name, descriptor) + } + } + +} diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/refmap/annotations/clazz/MixinAnnotationVisitor.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/refmap/annotations/clazz/MixinAnnotationVisitor.kt new file mode 100644 index 00000000..036ee10f --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/refmap/annotations/clazz/MixinAnnotationVisitor.kt @@ -0,0 +1,111 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.annotations.clazz + +import net.fabricmc.tinyremapper.extension.mixin.common.data.Annotation +import net.fabricmc.tinyremapper.extension.mixin.common.data.AnnotationElement +import net.fabricmc.tinyremapper.extension.mixin.common.data.Constant +import org.objectweb.asm.AnnotationVisitor +import org.objectweb.asm.Type +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.RefmapBuilderClassVisitor +import xyz.wagyourtail.unimined.util.orElseOptional +import java.util.* + +@Suppress("UNUSED_PARAMETER") +class MixinAnnotationVisitor( + descriptor: String, + visible: Boolean, + parent: AnnotationVisitor, + refmapBuilder: RefmapBuilderClassVisitor +) : AnnotationVisitor(Constant.ASM_VERSION, parent) { + + companion object { + + fun shouldVisit(descriptor: String, visible: Boolean, refmapBuilder: RefmapBuilderClassVisitor): Boolean { + return descriptor == Annotation.MIXIN + } + + } + + private val remap = refmapBuilder.remap + private val resolver = refmapBuilder.resolver + private val logger = refmapBuilder.logger + private val existingMappings = refmapBuilder.existingMappings + private val mapper = refmapBuilder.mapper + private val refmap = refmapBuilder.refmap + private val mixinName = refmapBuilder.mixinName + private val targetClasses = refmapBuilder.targetClasses + private val noRefmap = refmapBuilder.mixinRemapExtension.noRefmap.contains("BaseMixin") + + private val classTargets = mutableListOf() + private val classValues = mutableListOf() + + override fun visit(name: String, value: Any) { + super.visit(name, value) + logger.info("Found annotation value $name: $value") + if (name == AnnotationElement.REMAP) { + remap.set(value as Boolean) + } + } + + override fun visitArray(name: String?): AnnotationVisitor { + return when (name) { + AnnotationElement.TARGETS -> { + return object : AnnotationVisitor(Constant.ASM_VERSION, if (noRefmap) null else super.visitArray(name)) { + override fun visit(name: String?, value: Any) { + classTargets.add(value as String) + super.visit(name, value) + } + } + } + + AnnotationElement.VALUE, null -> { + return object: AnnotationVisitor(Constant.ASM_VERSION, super.visitArray(name)) { + override fun visit(name: String?, value: Any) { + classValues.add((value as Type).internalName) + super.visit(name, value) + } + } + } + + else -> { + super.visitArray(name) + } + } + } + + override fun visitEnd() { + val targets = if (noRefmap && classTargets.isNotEmpty()) { + super.visitArray(AnnotationElement.TARGETS) + } else { + null + } + if (remap.get()) { + logger.info("existing mappings: $existingMappings") + for (target in classTargets.toSet()) { + val clz = resolver.resolveClass(target.replace('.', '/')) + .orElseOptional { + existingMappings[target]?.let { + logger.info("remapping $it from existing refmap") + classTargets.remove(target) + classTargets.add(it) + resolver.resolveClass(it) + } ?: Optional.empty() + } + clz.ifPresent { + refmap.addProperty(target, mapper.mapName(it)) + targets?.visit(null, mapper.mapName(it)) + } + if (!clz.isPresent) { + targets?.visit(null, target) + logger.warn("Failed to resolve class $target in mixin ${mixinName.replace('/', '.')}") + } + } + } else { + for (target in classTargets) { + targets?.visit(null, target) + } + } + targets?.visitEnd() + targetClasses.addAll((classValues + classTargets.map { it.replace('.', '/') }).toSet()) + super.visitEnd() + } +} diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/refmap/annotations/method/AbstractMethodAnnotationVisitor.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/refmap/annotations/method/AbstractMethodAnnotationVisitor.kt new file mode 100644 index 00000000..5353d87c --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/refmap/annotations/method/AbstractMethodAnnotationVisitor.kt @@ -0,0 +1,133 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.annotations.method + +import net.fabricmc.tinyremapper.extension.mixin.common.ResolveUtility +import net.fabricmc.tinyremapper.extension.mixin.common.data.AnnotationElement +import net.fabricmc.tinyremapper.extension.mixin.common.data.Constant +import org.objectweb.asm.AnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.RefmapBuilderClassVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.splitMethodNameAndDescriptor +import xyz.wagyourtail.unimined.util.orElseOptional +import java.util.* +import java.util.concurrent.atomic.AtomicBoolean + +abstract class AbstractMethodAnnotationVisitor( + descriptor: String, + visible: Boolean, + parent: AnnotationVisitor, + methodAccess: Int, + protected val methodName: String, + protected val methodDescriptor: String, + protected val methodSignature: String?, + methodExceptions: Array?, + protected val refmapBuilder: RefmapBuilderClassVisitor +) : AnnotationVisitor(Constant.ASM_VERSION, parent) { + + abstract val annotationName: String + + protected val remap = AtomicBoolean(refmapBuilder.remap.get()) + protected var targetNames = mutableListOf() + + protected val resolver = refmapBuilder.resolver + protected val logger = refmapBuilder.logger + protected val existingMappings = refmapBuilder.existingMappings + protected val mapper = refmapBuilder.mapper + protected val refmap = refmapBuilder.refmap + protected val mixinName = refmapBuilder.mixinName + protected val targetClasses = refmapBuilder.targetClasses + protected val allowImplicitWildcards = refmapBuilder.allowImplicitWildcards + protected val noRefmap = refmapBuilder.mixinRemapExtension.noRefmap.contains("BaseMixin") + + override fun visit(name: String?, value: Any) { + super.visit(name, value) + if (name == AnnotationElement.REMAP) remap.set(value as Boolean) + } + + open fun getTargetNameAndDescs(targetMethod: String, wildcard: Boolean): Pair> { + val targetDescs = setOf(if (targetMethod.contains("(")) { + "(" + targetMethod.substringAfter("(") + } else { + null + }) + val targetName = if (wildcard) { + targetMethod.substringBefore("*") + } else { + targetMethod.substringBefore("(") + } + return targetName to targetDescs + } + + open fun remapTargetNames(noRefmapAcceptor: (String) -> Unit) { + if (remap.get()) { + outer@for (targetMethod in targetNames) { + if (targetMethod == "" || targetMethod == "" || + targetMethod == "*" + ) { + noRefmapAcceptor(targetMethod) + continue + } + val wildcard = targetMethod.endsWith("*") + val (targetName, targetDescs) = getTargetNameAndDescs(targetMethod, wildcard) + for (targetDesc in targetDescs) { + var implicitWildcard = targetDesc == null && allowImplicitWildcards + for (targetClass in targetClasses) { + val target = resolver.resolveMethod( + targetClass, + targetName, + targetDesc, + (if (wildcard || implicitWildcard) ResolveUtility.FLAG_FIRST else ResolveUtility.FLAG_UNIQUE) or ResolveUtility.FLAG_RECURSIVE + ).orElseOptional { + existingMappings[targetMethod]?.let { existing -> + logger.info("Remapping using existing mapping for $targetMethod: $existing") + if (existing.endsWith("*")) { + val mname = existing.substringAfter(";").let { it.substring(0, it.length - 1 ) } + resolver.resolveMethod( + targetClass, + mname, + null, + ResolveUtility.FLAG_FIRST or ResolveUtility.FLAG_RECURSIVE + ) + } else { + val (mName, mDesc) = splitMethodNameAndDescriptor(existing) + if (mDesc == null && allowImplicitWildcards) { + implicitWildcard = true + } + resolver.resolveMethod( + targetClass, + mName, + mDesc, + (if (implicitWildcard) ResolveUtility.FLAG_FIRST else ResolveUtility.FLAG_UNIQUE) or ResolveUtility.FLAG_RECURSIVE + ) + } + } ?: Optional.empty() + } + target.ifPresent { targetVal -> + val mappedClass = resolver.resolveClass(targetClass) + .map { mapper.mapName(it) } + .orElse(targetClass) + val mappedName = mapper.mapName(targetVal) + val mappedDesc = /* if (implicitWildcard) "" else */ if (wildcard && mappedName != "") "*" else mapper.mapDesc(targetVal) + if (targetClasses.size > 1) { + refmap.addProperty(targetMethod, "$mappedName$mappedDesc") + noRefmapAcceptor("$mappedName$mappedDesc") + } else { + refmap.addProperty(targetMethod, "L$mappedClass;$mappedName$mappedDesc") + noRefmapAcceptor("L$mappedClass;$mappedName$mappedDesc") + } + } + + if (target.isPresent) continue@outer + } + } + logger.warn( + "Failed to resolve $annotationName $targetMethod ($targetDescs) on ($methodName$methodDescriptor) $methodSignature in $mixinName" + ) + noRefmapAcceptor(targetMethod) + } + } else { + for (targetMethod in targetNames) { + noRefmapAcceptor(targetMethod) + } + } + } + +} \ No newline at end of file diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/refmap/annotations/method/AccessorAnnotationVisitor.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/refmap/annotations/method/AccessorAnnotationVisitor.kt new file mode 100644 index 00000000..72ff05c1 --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/refmap/annotations/method/AccessorAnnotationVisitor.kt @@ -0,0 +1,162 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.annotations.method + +import net.fabricmc.tinyremapper.extension.mixin.common.ResolveUtility +import net.fabricmc.tinyremapper.extension.mixin.common.data.Annotation +import net.fabricmc.tinyremapper.extension.mixin.common.data.AnnotationElement +import org.objectweb.asm.AnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.RefmapBuilderClassVisitor +import xyz.wagyourtail.unimined.util.capitalized +import xyz.wagyourtail.unimined.util.decapitalized +import xyz.wagyourtail.unimined.util.orElseOptional +import java.util.* + +@Suppress("UNUSED_PARAMETER") +class AccessorAnnotationVisitor( + descriptor: String, + visible: Boolean, + parent: AnnotationVisitor, + methodAccess: Int, + methodName: String, + methodDescriptor: String, + methodSignature: String?, + methodExceptions: Array?, + refmapBuilder: RefmapBuilderClassVisitor, +) : AbstractMethodAnnotationVisitor( + descriptor, + visible, + parent, + methodAccess, + methodName, + methodDescriptor, + methodSignature, + methodExceptions, + refmapBuilder +) { + + companion object { + fun shouldVisit( + descriptor: String, + visible: Boolean, + methodAccess: Int, + methodName: String, + methodDescriptor: String, + methodSignature: String?, + methodExceptions: Array?, + refmapBuilder: RefmapBuilderClassVisitor + ): Boolean { + return descriptor == Annotation.ACCESSOR + } + + val validPrefixes = mutableSetOf( + "get", + "is", + "set" + ) + + } + + override val annotationName: String = "@Accessor" + + override fun visit(name: String?, value: Any) { + if (!noRefmap) { + super.visit(name, value) + } + if (name == AnnotationElement.VALUE || name == null) targetNames.add(value as String) + } + + override fun visitEnd() { + remapTargetNames { mappedName -> + if (noRefmap) { + super.visit(AnnotationElement.VALUE, mappedName) + } + } + super.visitEnd() + } + + override fun remapTargetNames(noRefmapAcceptor: (String) -> Unit) { + if (remap.get()) { + val prefix = validPrefixes.firstOrNull { methodName.startsWith(it) } + val targetNames = if (targetNames.isEmpty()) { + if (prefix == null) { + logger.warn( + "Failed to resolve accessor $methodName in mixin ${ + mixinName.replace( + '/', + '.' + ) + }, unknown prefix" + ) + return + } + listOf(methodName.substring(prefix.length).decapitalized(), methodName.substring(prefix.length)) + } else { + targetNames + } + for (targetClass in targetClasses) { + for (targetName in targetNames) { + val targetDesc = if (methodDescriptor.startsWith("()")) { + methodDescriptor.substringAfter(")") + } else { + methodDescriptor.substringBefore(")").substringAfter("(") + } + var implicitWildcard = false + val target = resolver.resolveField( + targetClass, + targetName, + targetDesc, + ResolveUtility.FLAG_UNIQUE or ResolveUtility.FLAG_RECURSIVE + ).orElseOptional { + existingMappings[targetName]?.let { + logger.info("remapping $it from existing refmap") + val mName = it.substringBefore(":") + val desc = if (it.contains(":")) { + it.substringAfter(":") + } else null + if (desc == null && allowImplicitWildcards) { + implicitWildcard = true + } + resolver.resolveField( + targetClass, + mName, + desc, + (if (implicitWildcard) ResolveUtility.FLAG_FIRST else ResolveUtility.FLAG_UNIQUE) or ResolveUtility.FLAG_RECURSIVE + ) + } ?: Optional.empty() + } + target.ifPresent { + val mappedName = mapper.mapName(it) + val mappedDesc = mapper.mapDesc(it) + if (implicitWildcard) { + // BUGFIX: it appears 1 length don't lowercase when checking the refmap + if (targetName.length == 1) { + refmap.addProperty(targetName.capitalized(), mappedName) + } + refmap.addProperty(targetName, mappedName) + noRefmapAcceptor(mappedName) + } else { + // BUGFIX: it appears 1 length don't lowercase when checking the refmap + if (targetName.length == 1) { + refmap.addProperty(targetName.capitalized(), "$mappedName:$mappedDesc") + } + refmap.addProperty(targetName, "$mappedName:$mappedDesc") + noRefmapAcceptor("$mappedName:$mappedDesc") + } + } + if (target.isPresent) return + } + } + logger.warn( + "Failed to resolve field accessor $targetNames ($methodName$methodDescriptor) in mixin ${ + mixinName.replace( + '/', + '.' + ) + }" + ) + if (targetNames.isNotEmpty()) { + noRefmapAcceptor(targetNames.first()) + } + } + } + +} \ No newline at end of file diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/refmap/annotations/method/InjectAnnotationVisitor.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/refmap/annotations/method/InjectAnnotationVisitor.kt new file mode 100644 index 00000000..9087e988 --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/refmap/annotations/method/InjectAnnotationVisitor.kt @@ -0,0 +1,163 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.annotations.method + +import net.fabricmc.tinyremapper.extension.mixin.common.data.Annotation +import net.fabricmc.tinyremapper.extension.mixin.common.data.AnnotationElement +import net.fabricmc.tinyremapper.extension.mixin.common.data.Constant +import org.objectweb.asm.AnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.ArrayVisitorWrapper +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.RefmapBuilderClassVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.annotations.AtAnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.annotations.DescAnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.annotations.SliceAnnotationVisitor + +@Suppress("UNUSED_PARAMETER") +class InjectAnnotationVisitor( + descriptor: String, + visible: Boolean, + parent: AnnotationVisitor, + methodAccess: Int, + methodName: String, + methodDescriptor: String, + methodSignature: String?, + methodExceptions: Array?, + refmapBuilder: RefmapBuilderClassVisitor, +) : AbstractMethodAnnotationVisitor( + descriptor, + visible, + parent, + methodAccess, + methodName, + methodDescriptor, + methodSignature, + methodExceptions, + refmapBuilder +) { + + override val annotationName: String = "@Inject" + + companion object{ + fun shouldVisit( + descriptor:String, + visible:Boolean, + methodAccess:Int, + methodName:String, + methodDescriptor:String, + methodSignature:String?, + methodExceptions:Array?, + refmapBuilder:RefmapBuilderClassVisitor + ): Boolean{ + return descriptor == Annotation.INJECT + } + } + + override fun visitArray(name: String): AnnotationVisitor { + return when (name) { + AnnotationElement.AT -> { + ArrayVisitorWrapper(Constant.ASM_VERSION, super.visitArray(name)) { AtAnnotationVisitor(it, remap, refmapBuilder) } + } + + AnnotationElement.SLICE -> { + ArrayVisitorWrapper(Constant.ASM_VERSION, super.visitArray(name)) { SliceAnnotationVisitor(it, remap, refmapBuilder) } + } + + AnnotationElement.TARGET -> { + ArrayVisitorWrapper(Constant.ASM_VERSION, super.visitArray(name)) { DescAnnotationVisitor(it, remap, refmapBuilder) } + } + + AnnotationElement.METHOD -> { + object: AnnotationVisitor(Constant.ASM_VERSION, if (noRefmap) null else super.visitArray(name)) { + override fun visit(name: String?, value: Any) { + super.visit(name, value) + targetNames.add(value as String) + } + } + } + + else -> { + super.visitArray(name) + } + } + } + + override fun visitEnd() { + val method = if (noRefmap) { + super.visitArray(AnnotationElement.METHOD) + } else { + null + } + remapTargetNames { mappedName -> + method?.visit(null, mappedName) + } + method?.visitEnd() + super.visitEnd() + } + + private val callbackInfo = "Lorg/spongepowered/asm/mixin/injection/callback/CallbackInfo" + private val callbackInfoReturn = "Lorg/spongepowered/asm/mixin/injection/callback/CallbackInfoReturnable" + + private fun parseCIRVal(): String? { + val remain = methodSignature?.substringAfterLast("$callbackInfoReturn<", "") ?: "" + if (remain.isEmpty()) { + return null + } + var valBuild = "" + var depth = 1 + for (c in remain) { + if (c == '<') { + depth++ + } else if (c == '>') { + depth-- + } + if (depth == 0) { + break + } + valBuild += c + } + return valBuild.substringBefore("<").substringBefore(";") + ";" + } + + private fun toPrimitive(sig: String): String? { + return when (sig) { + "Ljava/lang/Integer;" -> "I" + "Ljava/lang/Long;" -> "J" + "Ljava/lang/Short;" -> "S" + "Ljava/lang/Byte;" -> "B" + "Ljava/lang/Character;" -> "C" + "Ljava/lang/Float;" -> "F" + "Ljava/lang/Double;" -> "D" + "Ljava/lang/Boolean;" -> "Z" + else -> null + } + } + + private fun stripCallbackInfoFromDesc(): Set { + val desc = methodDescriptor.substringBeforeLast(callbackInfo) + ")V" + if (methodDescriptor.contains(callbackInfoReturn)) { + val returnType = parseCIRVal() + if (returnType == null) { + logger.warn("Failed to parse return type from ($methodName$methodDescriptor) $methodSignature on $mixinName") + return setOf(null) + } + val rets = setOfNotNull( + desc.replace(")V", ")$returnType"), + toPrimitive(returnType)?.let { desc.replace(")V", ")${it}") }) + logger.info("Found returnable inject, signatures $rets, return type $returnType") + return rets + } + return setOf(desc) + } + + override fun getTargetNameAndDescs(targetMethod: String, wildcard: Boolean): Pair> { + return if (targetMethod.contains("(")) { + val n = targetMethod.split("(") + (n[0] to setOf("(${n[1]}")) + } else { + if (wildcard) { + (targetMethod.substring(0, targetMethod.length - 1) to setOf(null)) + } else { + (targetMethod to stripCallbackInfoFromDesc() + setOf(null)) + } + } + } + +} diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/refmap/annotations/method/InvokerAnnotationVisitor.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/refmap/annotations/method/InvokerAnnotationVisitor.kt new file mode 100644 index 00000000..72f6d7a0 --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/refmap/annotations/method/InvokerAnnotationVisitor.kt @@ -0,0 +1,149 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.annotations.method + +import net.fabricmc.tinyremapper.extension.mixin.common.ResolveUtility +import net.fabricmc.tinyremapper.extension.mixin.common.data.Annotation +import net.fabricmc.tinyremapper.extension.mixin.common.data.AnnotationElement +import org.objectweb.asm.AnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.RefmapBuilderClassVisitor +import xyz.wagyourtail.unimined.util.decapitalized +import xyz.wagyourtail.unimined.util.orElseOptional +import java.util.* + +@Suppress("UNUSED_PARAMETER") +class InvokerAnnotationVisitor( + descriptor: String, + visible: Boolean, + parent: AnnotationVisitor, + methodAccess: Int, + methodName: String, + methodDescriptor: String, + methodSignature: String?, + methodExceptions: Array?, + refmapBuilder: RefmapBuilderClassVisitor, +) : AbstractMethodAnnotationVisitor( + descriptor, + visible, + parent, + methodAccess, + methodName, + methodDescriptor, + methodSignature, + methodExceptions, + refmapBuilder +) { + + override val annotationName: String = "@Invoker" + + companion object { + fun shouldVisit( + descriptor: String, + visible: Boolean, + methodAccess: Int, + methodName: String, + methodDescriptor: String, + methodSignature: String?, + methodExceptions: Array?, + refmapBuilder: RefmapBuilderClassVisitor + ): Boolean { + return descriptor == Annotation.INVOKER + } + + val validPrefixes = mutableSetOf( + "call", + "invoke", + "new", + "create" + ) + + } + + override fun visit(name: String?, value: Any) { + if (!noRefmap) { + super.visit(name, value) + } + if (name == AnnotationElement.VALUE || name == null) targetNames.add(value as String) + } + + override fun visitEnd() { + remapTargetNames { + if (noRefmap) { + super.visit(AnnotationElement.VALUE, it) + } + } + super.visitEnd() + } + + override fun remapTargetNames(noRefmapAcceptor: (String) -> Unit) { + if (remap.get()) { + val targetNames = if (targetNames.isEmpty()) { + val prefix = validPrefixes.firstOrNull { methodName.startsWith(it) } + if (prefix == null) { + logger.warn( + "Failed to resolve invoker $methodName in mixin ${ + mixinName.replace( + '/', + '.' + ) + }, unknown prefix" + ) + return + } + listOf(methodName.substring(prefix.length).decapitalized(), methodName.substring(prefix.length)) + } else { + targetNames + } + for (targetClass in targetClasses) { + for (targetName in targetNames) { + var implicitWildcard = false + val target = resolver.resolveMethod( + targetClass, + targetName, + if (targetName == "") "${methodDescriptor.substringBefore(")")})V" else methodDescriptor, + ResolveUtility.FLAG_UNIQUE or ResolveUtility.FLAG_RECURSIVE + ).orElseOptional { + existingMappings[targetName]?.let { + logger.info("remapping $it from existing refmap") + val mName = it.substringBefore("(") + val desc = if (it.contains("(")) { + "(${it.substringAfter("(")}" + } else null + if (desc == null && allowImplicitWildcards) { + implicitWildcard = true + } + resolver.resolveMethod( + targetClass, + mName, + desc, + (if (implicitWildcard) ResolveUtility.FLAG_FIRST else ResolveUtility.FLAG_UNIQUE) or ResolveUtility.FLAG_RECURSIVE + ) + } ?: Optional.empty() + } + target.ifPresent { targetVal -> + val mappedName = mapper.mapName(targetVal) + val mappedDesc = mapper.mapDesc(targetVal).let { if (mappedName == "") "" else it } +// if (implicitWildcard) { +// refmap.addProperty(targetName, mappedName) +// } else { + refmap.addProperty(targetName, "$mappedName$mappedDesc") + noRefmapAcceptor("$mappedName$mappedDesc") +// } + } + if (target.isPresent) return + } + } + logger.warn( + "Failed to resolve method invoker $targetNames ($methodName$methodDescriptor) in mixin ${ + mixinName.replace( + '/', + '.' + ) + }" + ) + noRefmapAcceptor(targetNames.first()) + } else { + if (targetNames.isNotEmpty()) { + noRefmapAcceptor(targetNames.first()) + } + } + } +} \ No newline at end of file diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/refmap/annotations/method/ModifyArgAnnotationVisitor.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/refmap/annotations/method/ModifyArgAnnotationVisitor.kt new file mode 100644 index 00000000..ba1799dd --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/refmap/annotations/method/ModifyArgAnnotationVisitor.kt @@ -0,0 +1,106 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.annotations.method + +import net.fabricmc.tinyremapper.extension.mixin.common.data.Annotation +import net.fabricmc.tinyremapper.extension.mixin.common.data.AnnotationElement +import net.fabricmc.tinyremapper.extension.mixin.common.data.Constant +import org.objectweb.asm.AnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.ArrayVisitorWrapper +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.RefmapBuilderClassVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.annotations.AtAnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.annotations.DescAnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.annotations.SliceAnnotationVisitor + +@Suppress("UNUSED_PARAMETER") +open class ModifyArgAnnotationVisitor( + descriptor: String, + visible: Boolean, + parent: AnnotationVisitor, + methodAccess: Int, + methodName: String, + methodDescriptor: String, + methodSignature: String?, + methodExceptions: Array?, + refmapBuilder: RefmapBuilderClassVisitor, +) : AbstractMethodAnnotationVisitor( + descriptor, + visible, + parent, + methodAccess, + methodName, + methodDescriptor, + methodSignature, + methodExceptions, + refmapBuilder +) { + + override val annotationName: String = "@ModifyArg" + + companion object { + fun shouldVisit( + descriptor: String, + visible: Boolean, + methodAccess: Int, + methodName: String, + methodDescriptor: String, + methodSignature: String?, + methodExceptions: Array?, + refmapBuilder: RefmapBuilderClassVisitor + ): Boolean { + return descriptor == Annotation.MODIFY_ARG + } + } + + + override fun visitAnnotation(name: String, descriptor: String): AnnotationVisitor { + return when (name) { + AnnotationElement.AT -> { + AtAnnotationVisitor(super.visitAnnotation(name, descriptor), remap, refmapBuilder) + } + + AnnotationElement.SLICE -> { + SliceAnnotationVisitor(super.visitAnnotation(name, descriptor), remap, refmapBuilder) + } + + else -> { + super.visitAnnotation(name, descriptor) + } + } + } + + override fun visitArray(name: String): AnnotationVisitor { + return when (name) { + AnnotationElement.TARGET -> { + ArrayVisitorWrapper(Constant.ASM_VERSION, super.visitArray(name)) { + DescAnnotationVisitor(it, remap, refmapBuilder) + } + } + + AnnotationElement.METHOD -> { + object: AnnotationVisitor(Constant.ASM_VERSION, if (noRefmap) null else super.visitArray(name)) { + override fun visit(name: String?, value: Any) { + super.visit(name, value) + targetNames.add(value as String) + } + } + } + + else -> { + super.visitArray(name) + } + } + } + + override fun visitEnd() { + val method = if (noRefmap) { + super.visitArray(AnnotationElement.METHOD) + } else { + null + } + remapTargetNames { mappedName -> + method?.visit(null, mappedName) + } + method?.visitEnd() + super.visitEnd() + } + +} diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/refmap/annotations/method/ModifyArgsAnnotationVisitor.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/refmap/annotations/method/ModifyArgsAnnotationVisitor.kt new file mode 100644 index 00000000..336d6edc --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/refmap/annotations/method/ModifyArgsAnnotationVisitor.kt @@ -0,0 +1,49 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.annotations.method + +import net.fabricmc.tinyremapper.extension.mixin.common.data.Annotation +import org.objectweb.asm.AnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.RefmapBuilderClassVisitor + +@Suppress("UNUSED_PARAMETER") +class ModifyArgsAnnotationVisitor( + descriptor: String, + visible: Boolean, + parent: AnnotationVisitor, + methodAccess: Int, + methodName: String, + methodDescriptor: String, + methodSignature: String?, + methodExceptions: Array?, + refmapBuilder: RefmapBuilderClassVisitor, +) : ModifyArgAnnotationVisitor( + descriptor, + visible, + parent, + methodAccess, + methodName, + methodDescriptor, + methodSignature, + methodExceptions, + refmapBuilder +) { + + override val annotationName: String = "@ModifyArgs" + + companion object { + + fun shouldVisit( + descriptor: String, + visible: Boolean, + methodAccess: Int, + methodName: String, + methodDescriptor: String, + methodSignature: String?, + methodExceptions: Array?, + refmapBuilder: RefmapBuilderClassVisitor + ): Boolean { + return descriptor == Annotation.MODIFY_ARGS + } + + } + +} \ No newline at end of file diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/refmap/annotations/method/ModifyConstantAnnotationVisitor.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/refmap/annotations/method/ModifyConstantAnnotationVisitor.kt new file mode 100644 index 00000000..f65049e0 --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/refmap/annotations/method/ModifyConstantAnnotationVisitor.kt @@ -0,0 +1,64 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.annotations.method + +import net.fabricmc.tinyremapper.extension.mixin.common.data.Annotation +import net.fabricmc.tinyremapper.extension.mixin.common.data.AnnotationElement +import net.fabricmc.tinyremapper.extension.mixin.common.data.Constant +import org.objectweb.asm.AnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.ArrayVisitorWrapper +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.RefmapBuilderClassVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.annotations.SliceAnnotationVisitor + +@Suppress("UNUSED_PARAMETER") +class ModifyConstantAnnotationVisitor( + descriptor: String, + visible: Boolean, + parent: AnnotationVisitor, + methodAccess: Int, + methodName: String, + methodDescriptor: String, + methodSignature: String?, + methodExceptions: Array?, + refmapBuilder: RefmapBuilderClassVisitor +) : ModifyArgAnnotationVisitor( + descriptor, + visible, + parent, + methodAccess, + methodName, + methodDescriptor, + methodSignature, + methodExceptions, + refmapBuilder +) { + + companion object { + fun shouldVisit( + descriptor: String, + visible: Boolean, + methodAccess: Int, + methodName: String, + methodDescriptor: String, + methodSignature: String?, + methodExceptions: Array?, + refmapBuilder: RefmapBuilderClassVisitor + ): Boolean { + return descriptor == Annotation.MODIFY_CONSTANT + } + } + + override val annotationName: String = "@ModifyConstant" + + override fun visitArray(name: String): AnnotationVisitor { + return when (name) { + AnnotationElement.SLICE -> { + ArrayVisitorWrapper(Constant.ASM_VERSION, super.visitArray(name)) { + SliceAnnotationVisitor(it, remap, refmapBuilder) + } + } + else -> { + super.visitArray(name) + } + } + } + +} \ No newline at end of file diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/refmap/annotations/method/ModifyVariableAnnotationVisitor.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/refmap/annotations/method/ModifyVariableAnnotationVisitor.kt new file mode 100644 index 00000000..614eb7e8 --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/refmap/annotations/method/ModifyVariableAnnotationVisitor.kt @@ -0,0 +1,49 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.annotations.method + +import net.fabricmc.tinyremapper.extension.mixin.common.data.Annotation +import org.objectweb.asm.AnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.RefmapBuilderClassVisitor + +@Suppress("UNUSED_PARAMETER") +class ModifyVariableAnnotationVisitor( + descriptor: String, + visible: Boolean, + parent: AnnotationVisitor, + methodAccess: Int, + methodName: String, + methodDescriptor: String, + methodSignature: String?, + methodExceptions: Array?, + refmapBuilder: RefmapBuilderClassVisitor, +) : ModifyArgAnnotationVisitor( + descriptor, + visible, + parent, + methodAccess, + methodName, + methodDescriptor, + methodSignature, + methodExceptions, + refmapBuilder +) { + + override val annotationName: String = "@ModifyVariable" + + companion object { + + fun shouldVisit( + descriptor: String, + visible: Boolean, + methodAccess: Int, + methodName: String, + methodDescriptor: String, + methodSignature: String?, + methodExceptions: Array?, + refmapBuilder: RefmapBuilderClassVisitor + ): Boolean { + return descriptor == Annotation.MODIFY_VARIABLE + } + + } + +} \ No newline at end of file diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/refmap/annotations/method/RedirectAnnotationVisitor.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/refmap/annotations/method/RedirectAnnotationVisitor.kt new file mode 100644 index 00000000..c56a014a --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixin/refmap/annotations/method/RedirectAnnotationVisitor.kt @@ -0,0 +1,49 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.annotations.method + +import net.fabricmc.tinyremapper.extension.mixin.common.data.Annotation +import org.objectweb.asm.AnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.RefmapBuilderClassVisitor + +@Suppress("UNUSED_PARAMETER") +class RedirectAnnotationVisitor( + descriptor: String, + visible: Boolean, + parent: AnnotationVisitor, + methodAccess: Int, + methodName: String, + methodDescriptor: String, + methodSignature: String?, + methodExceptions: Array?, + refmapBuilder: RefmapBuilderClassVisitor, +) : ModifyArgAnnotationVisitor( + descriptor, + visible, + parent, + methodAccess, + methodName, + methodDescriptor, + methodSignature, + methodExceptions, + refmapBuilder +) { + + override val annotationName: String = "@Redirect" + + companion object { + + fun shouldVisit( + descriptor: String, + visible: Boolean, + methodAccess: Int, + methodName: String, + methodDescriptor: String, + methodSignature: String?, + methodExceptions: Array?, + refmapBuilder: RefmapBuilderClassVisitor + ): Boolean { + return descriptor == Annotation.REDIRECT + } + + } + +} \ No newline at end of file diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixinextra/MixinExtra.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixinextra/MixinExtra.kt new file mode 100644 index 00000000..91a3fa28 --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixinextra/MixinExtra.kt @@ -0,0 +1,31 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension.mixinextra + +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.RefmapBuilderClassVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixinextra.annotations.method.* + +object MixinExtra { + + object Annotation { + + const val MODIFY_EXPRESSION_VALUE = "Lcom/llamalad7/mixinextras/injector/ModifyExpressionValue;" + const val MODIFY_RECIEVER = "Lcom/llamalad7/mixinextras/injector/ModifyReciever;" + const val MODIFY_RETURN_VALUE = "Lcom/llamalad7/mixinextras/injector/ModifyReturnValue;" + const val WRAP_WITH_CONDITION = "Lcom/llamalad7/mixinextras/injector/WrapWithCondition;" + const val WRAP_OPERATION = "Lcom/llamalad7/mixinextras/injector/wrapoperation/WrapOperation;" + + + } + + fun refmapBuilder(refmapBuilder: RefmapBuilderClassVisitor) { + + refmapBuilder.methodAnnotationVisitors.addAll(listOf( + ModifyExpressionValueAnnotationVisitor.Companion::shouldVisit to ::ModifyExpressionValueAnnotationVisitor, + ModifyRecieverAnnotationVisitor.Companion::shouldVisit to ::ModifyRecieverAnnotationVisitor, + ModifyReturnValueAnnotationVisitor.Companion::shouldVisit to ::ModifyReturnValueAnnotationVisitor, + WrapWithConditionAnnotationVisitor.Companion::shouldVisit to ::WrapWithConditionAnnotationVisitor, + WrapOperationAnnotationVisitor.Companion::shouldVisit to ::WrapOperationAnnotationVisitor, + )) + + } + +} \ No newline at end of file diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixinextra/annotations/method/ModifyExpressionValueAnnotationVisitor.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixinextra/annotations/method/ModifyExpressionValueAnnotationVisitor.kt new file mode 100644 index 00000000..8da4e0f9 --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixinextra/annotations/method/ModifyExpressionValueAnnotationVisitor.kt @@ -0,0 +1,91 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension.mixinextra.annotations.method + +import net.fabricmc.tinyremapper.extension.mixin.common.data.AnnotationElement +import net.fabricmc.tinyremapper.extension.mixin.common.data.Constant +import org.objectweb.asm.AnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.ArrayVisitorWrapper +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.RefmapBuilderClassVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.annotations.AtAnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.annotations.SliceAnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.annotations.method.AbstractMethodAnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixinextra.MixinExtra + +@Suppress("UNUSED_PARAMETER") +open class ModifyExpressionValueAnnotationVisitor( + descriptor: String, + visible: Boolean, + parent: AnnotationVisitor, + methodAccess: Int, + methodName: String, + methodDescriptor: String, + methodSignature: String?, + methodExceptions: Array?, + refmapBuilder: RefmapBuilderClassVisitor, +) : AbstractMethodAnnotationVisitor( + descriptor, + visible, + parent, + methodAccess, + methodName, + methodDescriptor, + methodSignature, + methodExceptions, + refmapBuilder +) { + + override val annotationName: String = "@ModifyExpressionValue" + + companion object { + fun shouldVisit( + descriptor: String, + visible: Boolean, + methodAccess: Int, + methodName: String, + methodDescriptor: String, + methodSignature: String?, + methodExceptions: Array?, + refmapBuilder: RefmapBuilderClassVisitor + ): Boolean { + return descriptor == MixinExtra.Annotation.MODIFY_EXPRESSION_VALUE + } + } + + override fun visitArray(name: String): AnnotationVisitor { + return when (name) { + AnnotationElement.AT -> { + ArrayVisitorWrapper(Constant.ASM_VERSION, super.visitArray(name)) { AtAnnotationVisitor(it, remap, refmapBuilder) } + } + + AnnotationElement.SLICE -> { + ArrayVisitorWrapper(Constant.ASM_VERSION, super.visitArray(name)) { SliceAnnotationVisitor(it, remap, refmapBuilder) } + } + AnnotationElement.METHOD -> { + object: AnnotationVisitor(Constant.ASM_VERSION, if (noRefmap) null else super.visitArray(name)) { + override fun visit(name: String?, value: Any) { + super.visit(name, value) + targetNames.add(value as String) + } + } + } + else -> { + super.visitArray(name) + } + } + } + + override fun visitEnd() { + val method = if (noRefmap) { + super.visitArray(AnnotationElement.METHOD) + } else { + null + } + remapTargetNames { mappedName -> + method?.visit(null, mappedName) + } + method?.visitEnd() + super.visitEnd() + } + + + +} \ No newline at end of file diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixinextra/annotations/method/ModifyRecieverAnnotationVisitor.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixinextra/annotations/method/ModifyRecieverAnnotationVisitor.kt new file mode 100644 index 00000000..ee72bb1a --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixinextra/annotations/method/ModifyRecieverAnnotationVisitor.kt @@ -0,0 +1,49 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension.mixinextra.annotations.method + +import org.objectweb.asm.AnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.RefmapBuilderClassVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixinextra.MixinExtra + +@Suppress("UNUSED_PARAMETER") +class ModifyRecieverAnnotationVisitor( + descriptor: String, + visible: Boolean, + parent: AnnotationVisitor, + methodAccess: Int, + methodName: String, + methodDescriptor: String, + methodSignature: String?, + methodExceptions: Array?, + refmapBuilder: RefmapBuilderClassVisitor, +) : ModifyExpressionValueAnnotationVisitor( + descriptor, + visible, + parent, + methodAccess, + methodName, + methodDescriptor, + methodSignature, + methodExceptions, + refmapBuilder +) { + + override val annotationName: String = "@ModifyReciever" + + companion object { + + fun shouldVisit( + descriptor: String, + visible: Boolean, + methodAccess: Int, + methodName: String, + methodDescriptor: String, + methodSignature: String?, + methodExceptions: Array?, + refmapBuilder: RefmapBuilderClassVisitor + ): Boolean { + return descriptor == MixinExtra.Annotation.MODIFY_RECIEVER + } + + } + +} \ No newline at end of file diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixinextra/annotations/method/ModifyReturnValueAnnotationVisitor.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixinextra/annotations/method/ModifyReturnValueAnnotationVisitor.kt new file mode 100644 index 00000000..cb4b841c --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixinextra/annotations/method/ModifyReturnValueAnnotationVisitor.kt @@ -0,0 +1,50 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension.mixinextra.annotations.method + +import net.fabricmc.tinyremapper.extension.mixin.common.data.Annotation +import org.objectweb.asm.AnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.RefmapBuilderClassVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixinextra.MixinExtra + +@Suppress("UNUSED_PARAMETER") +class ModifyReturnValueAnnotationVisitor( + descriptor: String, + visible: Boolean, + parent: AnnotationVisitor, + methodAccess: Int, + methodName: String, + methodDescriptor: String, + methodSignature: String?, + methodExceptions: Array?, + refmapBuilder: RefmapBuilderClassVisitor, +) : ModifyExpressionValueAnnotationVisitor( + descriptor, + visible, + parent, + methodAccess, + methodName, + methodDescriptor, + methodSignature, + methodExceptions, + refmapBuilder +) { + + override val annotationName: String = "@ModifyReturnValue" + + companion object { + + fun shouldVisit( + descriptor: String, + visible: Boolean, + methodAccess: Int, + methodName: String, + methodDescriptor: String, + methodSignature: String?, + methodExceptions: Array?, + refmapBuilder: RefmapBuilderClassVisitor + ): Boolean { + return descriptor == MixinExtra.Annotation.MODIFY_RETURN_VALUE + } + + } + +} \ No newline at end of file diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixinextra/annotations/method/WrapOperationAnnotationVisitor.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixinextra/annotations/method/WrapOperationAnnotationVisitor.kt new file mode 100644 index 00000000..8f9fa9fb --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixinextra/annotations/method/WrapOperationAnnotationVisitor.kt @@ -0,0 +1,49 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension.mixinextra.annotations.method + +import org.objectweb.asm.AnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.RefmapBuilderClassVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixinextra.MixinExtra + +@Suppress("UNUSED_PARAMETER") +class WrapOperationAnnotationVisitor( + descriptor: String, + visible: Boolean, + parent: AnnotationVisitor, + methodAccess: Int, + methodName: String, + methodDescriptor: String, + methodSignature: String?, + methodExceptions: Array?, + refmapBuilder: RefmapBuilderClassVisitor, +) : ModifyExpressionValueAnnotationVisitor( + descriptor, + visible, + parent, + methodAccess, + methodName, + methodDescriptor, + methodSignature, + methodExceptions, + refmapBuilder +) { + + override val annotationName: String = "@WrapOperation" + + companion object { + + fun shouldVisit( + descriptor: String, + visible: Boolean, + methodAccess: Int, + methodName: String, + methodDescriptor: String, + methodSignature: String?, + methodExceptions: Array?, + refmapBuilder: RefmapBuilderClassVisitor + ): Boolean { + return descriptor == MixinExtra.Annotation.WRAP_OPERATION + } + + } + +} \ No newline at end of file diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixinextra/annotations/method/WrapWithConditionAnnotationVisitor.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixinextra/annotations/method/WrapWithConditionAnnotationVisitor.kt new file mode 100644 index 00000000..6db55f39 --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/extension/mixinextra/annotations/method/WrapWithConditionAnnotationVisitor.kt @@ -0,0 +1,50 @@ +package xyz.wagyourtail.unimined.internal.mapping.extension.mixinextra.annotations.method + +import net.fabricmc.tinyremapper.extension.mixin.common.data.Annotation +import org.objectweb.asm.AnnotationVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixin.refmap.RefmapBuilderClassVisitor +import xyz.wagyourtail.unimined.internal.mapping.extension.mixinextra.MixinExtra + +@Suppress("UNUSED_PARAMETER") +class WrapWithConditionAnnotationVisitor( + descriptor: String, + visible: Boolean, + parent: AnnotationVisitor, + methodAccess: Int, + methodName: String, + methodDescriptor: String, + methodSignature: String?, + methodExceptions: Array?, + refmapBuilder: RefmapBuilderClassVisitor, +) : ModifyExpressionValueAnnotationVisitor( + descriptor, + visible, + parent, + methodAccess, + methodName, + methodDescriptor, + methodSignature, + methodExceptions, + refmapBuilder +) { + + override val annotationName: String = "@WrapWithCondition" + + companion object { + + fun shouldVisit( + descriptor: String, + visible: Boolean, + methodAccess: Int, + methodName: String, + methodDescriptor: String, + methodSignature: String?, + methodExceptions: Array?, + refmapBuilder: RefmapBuilderClassVisitor + ): Boolean { + return descriptor == MixinExtra.Annotation.WRAP_WITH_CONDITION + } + + } + +} \ No newline at end of file diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/ii/InterfaceInjectionMinecraftTransformer.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/ii/InterfaceInjectionMinecraftTransformer.kt new file mode 100644 index 00000000..6790239c --- /dev/null +++ b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/ii/InterfaceInjectionMinecraftTransformer.kt @@ -0,0 +1,84 @@ +package xyz.wagyourtail.unimined.internal.mapping.ii + +import org.gradle.api.logging.Logger +import org.objectweb.asm.ClassReader +import org.objectweb.asm.ClassWriter +import org.objectweb.asm.Opcodes +import org.objectweb.asm.tree.ClassNode +import xyz.wagyourtail.unimined.util.openZipFileSystem +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.StandardCopyOption +import java.nio.file.StandardOpenOption +import kotlin.io.path.inputStream + +object InterfaceInjectionMinecraftTransformer { + fun transform( + injections: Map>, + baseMinecraft: Path, + output: Path, + logger: Logger + ): Boolean { + if (injections.isNotEmpty()) { + Files.copy(baseMinecraft, output, StandardCopyOption.REPLACE_EXISTING) + output.openZipFileSystem(mapOf("mutable" to true)).use { fs -> + logger.debug("Transforming $output with ${injections.values.sumOf { it.size }} interface injections") + + for (target in injections.keys) { + try { + val targetClass = "/" + target.replace(".", "/") + ".class" + val targetPath = fs.getPath(targetClass) + logger.debug("Transforming $targetPath") + if (Files.exists(targetPath)) { + val reader = ClassReader(targetPath.inputStream()) + val writer = ClassWriter(0) + + val node = ClassNode(Opcodes.ASM9) + reader.accept(node, 0) + + if (node.interfaces == null) { + node.interfaces = arrayListOf(); + } + + for (injected in injections[target]!!) { + if (!node.interfaces.contains(injected)) node.interfaces.add(injected) + } + + if (node.signature != null) { + val resultingSignature = StringBuilder(node.signature) + + for (injected in injections[target]!!) { + val computedSignature = "L" + injected.replace(".", "/") + ";" + + if (!resultingSignature.contains(computedSignature)) resultingSignature.append(computedSignature) + } + + node.signature = resultingSignature.toString() + } + + node.accept(writer); + Files.write( + targetPath, + writer.toByteArray(), + StandardOpenOption.CREATE, + StandardOpenOption.TRUNCATE_EXISTING + ) + } else { + logger.warn("Could not find class $targetClass in $output") + } + } catch (e: Exception) { + logger.warn( + "An error occurred while transforming $target with interface injection in $output", + e + ) + } + } + } + + + return true + } + + return false + } +} \ No newline at end of file diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/mixin/refmap/BetterMixinExtension.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/mixin/refmap/BetterMixinExtension.kt deleted file mode 100644 index 8c1e8218..00000000 --- a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/mixin/refmap/BetterMixinExtension.kt +++ /dev/null @@ -1,330 +0,0 @@ -package xyz.wagyourtail.unimined.internal.mapping.mixin.refmap - -import com.google.gson.GsonBuilder -import com.google.gson.JsonObject -import com.google.gson.JsonParser -import net.fabricmc.tinyremapper.InputTag -import net.fabricmc.tinyremapper.OutputConsumerPath.ResourceRemapper -import net.fabricmc.tinyremapper.TinyRemapper -import net.fabricmc.tinyremapper.api.TrClass -import net.fabricmc.tinyremapper.api.TrEnvironment -import net.fabricmc.tinyremapper.extension.mixin.MixinExtension -import net.fabricmc.tinyremapper.extension.mixin.common.Logger -import net.fabricmc.tinyremapper.extension.mixin.common.data.Annotation -import net.fabricmc.tinyremapper.extension.mixin.common.data.CommonData -import net.fabricmc.tinyremapper.extension.mixin.common.data.Constant -import org.gradle.api.logging.LogLevel -import org.objectweb.asm.AnnotationVisitor -import org.objectweb.asm.ClassVisitor -import xyz.wagyourtail.unimined.util.defaultedMapOf -import xyz.wagyourtail.unimined.util.forEachInZip -import java.io.InputStream -import java.nio.charset.StandardCharsets -import java.nio.file.FileSystem -import java.nio.file.Path -import java.nio.file.StandardOpenOption -import java.util.function.Consumer -import kotlin.io.path.createDirectories -import kotlin.io.path.name -import kotlin.io.path.writeText - -class BetterMixinExtension( - val loggerLevel: LogLevel = LogLevel.WARN, - val targets: Set = MixinExtension.AnnotationTarget.values().toSet(), - val fallbackWhenNotInJson: Boolean = false, - val allowImplicitWildcards: Boolean = false, -): - TinyRemapper.Extension, - TinyRemapper.ApplyVisitorProvider, - TinyRemapper.AnalyzeVisitorProvider, - TinyRemapper.StateProcessor { - private val tasks: MutableMap>> = defaultedMapOf { mutableListOf() } - - private val defaultRefmapPath = mutableMapOf() - - companion object { - private val GSON = GsonBuilder().setPrettyPrinting().create() - - fun translateLogLevel(loggerLevel: LogLevel) = when (loggerLevel) { - LogLevel.DEBUG -> Logger.Level.INFO - LogLevel.INFO -> Logger.Level.INFO - LogLevel.WARN -> Logger.Level.WARN - LogLevel.ERROR -> Logger.Level.ERROR - LogLevel.QUIET -> Logger.Level.ERROR - else -> Logger.Level.WARN - } - } - - val fallback = MixinExtension() - - var defaultRefmap = defaultedMapOf { JsonObject() } - val refmaps = defaultedMapOf { - mutableMapOf(defaultRefmapPath[it]!! to defaultRefmap[it]!!) - } - val classesToRefmap = defaultedMapOf { - mutableMapOf>() - } - val mixinJsons = defaultedMapOf { - mutableMapOf() - } - val existingRefmaps = defaultedMapOf { - mutableMapOf() - } - - - private val logger: Logger = Logger(translateLogLevel(loggerLevel)) - - override fun attach(builder: TinyRemapper.Builder) { - - if (targets.contains(MixinExtension.AnnotationTarget.HARD)) { - builder.extraAnalyzeVisitor(this) - builder.extraStateProcessor(this) - } - - if (targets.contains(MixinExtension.AnnotationTarget.SOFT)) { - builder.extraPreApplyVisitor(this) - } - - } - - override fun insertApplyVisitor(cls: TrClass, next: ClassVisitor): ClassVisitor { - try { - // detect if class in class lists - val (tag, refmap) = synchronized(classesToRefmap) { - classesToRefmap.entries.firstOrNull { (_, v) -> - v.containsKey(cls.name.replace("/", ".")) - }?.toPair() ?: (null to null) - } - - return if (refmap != null) { - logger.info("[RefmapTarget] Found mixin class: ${cls.name}") - val refmapNames = refmap[cls.name.replace("/", ".")] - val target = JsonObject() - - val existingRefmaps = refmapNames!!.mapNotNull { existingRefmaps[tag][it] } - logger.info("Found ${existingRefmaps.size} existing refmaps") - val existingClassMappings = if (existingRefmaps.isNotEmpty()) { - existingRefmaps.mapNotNull { it.get("mappings").asJsonObject.get(cls.name)?.asJsonObject } - } else { - setOf() - } - // combine all mappings - val combinedMappings = mutableMapOf() - for (mapping in existingClassMappings) { - for ((key, value) in mapping.entrySet()) { - combinedMappings[key] = value.asString - } - } - MixinClassVisitorRefmapBuilder( - CommonData(cls.environment, logger), - cls.name, - target, - next, - combinedMappings, - onEnd = { - if (target.size() > 0) { - val refmaps = refmapNames.map { refmaps[tag][it]!! } - for (refmap in refmaps) { - if (!refmap.has("mappings")) { - refmap.add("mappings", JsonObject()) - } - val mappings = refmap.getAsJsonObject("mappings") - mappings.add(cls.name, target) - } - } - }, - allowImplicitWildcards = allowImplicitWildcards - ) - } else if (fallbackWhenNotInJson) { - fallback.preApplyVisitor(cls, next) - } else { - object : ClassVisitor(Constant.ASM_VERSION, next) { - override fun visitAnnotation(descriptor: String, visible: Boolean): AnnotationVisitor { - if (Annotation.MIXIN == descriptor) { - logger.error("[RefmapTarget] Found mixin class: ${cls.name}, but it is not in a mixin json file! This will cause issues and the mixin will not be remapped!") - } - return super.visitAnnotation(descriptor, visible) - } - } - } - } catch (e: Exception) { - logger.error("Error while processing class ${cls.name}: ${e.javaClass.simpleName}: ${e.message}") - throw e - } - } - - private val hardFallbackFunction = fallback::class.java.getDeclaredMethod( - "analyzeVisitor", - Int::class.java, - String::class.java, - ClassVisitor::class.java - ).apply { - isAccessible = true - } - - override fun insertAnalyzeVisitor(mrjVersion: Int, className: String, next: ClassVisitor?): ClassVisitor? { - try { - val (tag, refmap) = synchronized(classesToRefmap) { - classesToRefmap.entries.firstOrNull { (_, v) -> - v.containsKey(className.replace("/", ".")) - }?.toPair() ?: (null to null) - } - - if (refmap != null) { - val refmapNames = refmap[className.replace("/", ".")] - val existingRefmaps = refmapNames!!.mapNotNull { existingRefmaps[tag][it] } - logger.info("Found ${existingRefmaps.size} existing refmaps") - val existingClassMappings = if (existingRefmaps.isNotEmpty()) { - existingRefmaps.mapNotNull { it.get("mappings").asJsonObject.get(className)?.asJsonObject } - } else { - setOf() - } - // combine all mappings - val combinedMappings = mutableMapOf() - for (mapping in existingClassMappings) { - for ((key, value) in mapping.entrySet()) { - combinedMappings[key] = value.asString - } - } - logger.info("[HardTarget] Found mixin class: $className / $mrjVersion") - return HarderTargetMixinClassVisitor(tasks[mrjVersion]!!, next, combinedMappings, logger) - } else if (fallbackWhenNotInJson) { - return hardFallbackFunction(fallback, mrjVersion, className, next) as ClassVisitor? - } else { - return object : ClassVisitor(Constant.ASM_VERSION, next) { - override fun visitAnnotation(descriptor: String, visible: Boolean): AnnotationVisitor? { - if (Annotation.MIXIN == descriptor) { - logger.error("[HardTarget] Found mixin class: ${className}, but it is not in a mixin json file! This will cause issues and the mixin will not be remapped properly!") - } - return super.visitAnnotation(descriptor, visible) - } - } - } - } catch (e: Exception) { - throw IllegalStateException("Error while processing class $className: ${e.javaClass.simpleName}: ${e.message}") - } - } - - override fun process(environment: TrEnvironment) { - logger.info("Processing environment: ${environment.mrjVersion}") - val data = CommonData(environment, logger) - - for (task in tasks[environment.mrjVersion]!!) { - try { - task.accept(data) - } catch (e: RuntimeException) { - logger.error(e.message) - } - } - if (fallbackWhenNotInJson) { - val fallbackFn = fallback::class.java.getDeclaredMethod("stateProcessor", TrEnvironment::class.java) - fallbackFn.isAccessible = true - fallbackFn(fallback, environment) - } - } - - fun write(fs: FileSystem) { - write(null, fs) - } - - fun write(tag: InputTag?, fs: FileSystem) { - for ((path, refmap) in refmaps[tag]) { - if (refmap.size() > 0) - fs.getPath(path) - .writeText( - GSON.toJson(refmap), - StandardCharsets.UTF_8, - StandardOpenOption.CREATE, - StandardOpenOption.TRUNCATE_EXISTING - ) - } - } - - - - fun mixinCheck(relativePath: String): Boolean = - relativePath.contains("mixins") && relativePath.endsWith(".json") - - fun refmapCheck(relativePath: String): Boolean = - relativePath.contains("refmap") && relativePath.endsWith(".json") - - - fun preRead(path: Path, defaultRefmap: String) { - preRead(null, path, defaultRefmap) - } - - fun preRead(tag: InputTag?, path: Path, defaultRefmap: String) { - logger.info("[PreRead] Reading $path") - defaultRefmapPath[tag] = defaultRefmap - path.forEachInZip { file, input -> - preReadIntl(tag, file, input) - } - } - - private fun preReadIntl(tag: InputTag?, file: String, input: InputStream) { - if (refmapCheck(file)) { - try { - logger.info("[PreRead] Found refmap: $file") - val json = JsonParser.parseReader(input.reader()).asJsonObject - existingRefmaps[tag][file] = json - } catch (e: Exception) { - logger.error("[PreRead] Error while processing refmap ($file): ${e.message}") - } - } else if (mixinCheck(file)) { - logger.info("[PreRead] Found mixin config: ${file.substringAfterLast("/")}") - val json = JsonParser.parseReader(input.reader()).asJsonObject - val refmap = json.get("refmap")?.asString ?: defaultRefmapPath[tag]!! - val pkg = json.get("package").asString - refmaps[tag].computeIfAbsent(refmap) { JsonObject() } - val mixins = (json.getAsJsonArray("mixins") ?: listOf()) + - (json.getAsJsonArray("client") ?: listOf()) + - (json.getAsJsonArray("server") ?: listOf()) - - logger.info("[PreRead] ${mixins.size} mixins:") - for (mixin in mixins) { - synchronized(classesToRefmap) { - classesToRefmap[tag].computeIfAbsent("$pkg.${mixin.asString}") { mutableSetOf() } += refmap - } - logger.info("[PreRead] $pkg.${mixin.asString}") - } - json.addProperty("refmap", refmap) - mixinJsons[tag][file] = json - } - } - - fun resourceRemapper() = resourceReampper(null) - - fun resourceReampper(tag: InputTag?) = object : ResourceRemapper { - override fun canTransform(remapper: TinyRemapper, relativePath: Path): Boolean { - return mixinCheck(relativePath.name) || refmapCheck(relativePath.name) - } - - override fun transform( - destinationDirectory: Path, - relativePath: Path, - input: InputStream, - remapper: TinyRemapper - ) { - if (refmapCheck(relativePath.name)) { - logger.info("[Transform] Found refmap: $relativePath") - } else if (mixinCheck(relativePath.name)) { - try { - val json = mixinJsons[tag][relativePath.toString()]!! - val output = destinationDirectory.resolve(relativePath) - output.parent.createDirectories() - output.writeText( - GSON.toJson(json), - StandardCharsets.UTF_8, - StandardOpenOption.CREATE, - StandardOpenOption.TRUNCATE_EXISTING - ) - } catch (e: Exception) { - logger.error("[Transform] Error while processing mixin config ($relativePath): ${e.message}") - } - } else { - throw IllegalStateException("Unexpected path: $relativePath") - } - } - - } -} \ No newline at end of file diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/mixin/refmap/HarderTargetMixinClassVisitor.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/mixin/refmap/HarderTargetMixinClassVisitor.kt deleted file mode 100644 index e2d0d89a..00000000 --- a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/mixin/refmap/HarderTargetMixinClassVisitor.kt +++ /dev/null @@ -1,201 +0,0 @@ -package xyz.wagyourtail.unimined.internal.mapping.mixin.refmap - -import net.fabricmc.tinyremapper.extension.mixin.common.Logger -import net.fabricmc.tinyremapper.extension.mixin.common.MapUtility -import net.fabricmc.tinyremapper.extension.mixin.common.data.* -import net.fabricmc.tinyremapper.extension.mixin.common.data.Annotation -import net.fabricmc.tinyremapper.extension.mixin.hard.annotation.ImplementsAnnotationVisitor -import net.fabricmc.tinyremapper.extension.mixin.hard.annotation.OverwriteAnnotationVisitor -import net.fabricmc.tinyremapper.extension.mixin.hard.annotation.ShadowAnnotationVisitor -import net.fabricmc.tinyremapper.extension.mixin.hard.data.SoftInterface -import org.objectweb.asm.* -import java.util.* -import java.util.concurrent.atomic.AtomicBoolean -import java.util.function.Consumer - - -class HarderTargetMixinClassVisitor( - private val tasks: MutableList>, - delegate: ClassVisitor?, - private val existingMappings: Map, - private val logger: Logger -): - ClassVisitor(Constant.ASM_VERSION, delegate) { - private var _class: MxClass? = null - - // @Mixin - private val remap = AtomicBoolean() - private val targets: MutableList = mutableListOf() - - // @Implements - private val interfaces: MutableList = mutableListOf() - - - /** - * This is called before visitAnnotation. - */ - override fun visit( - version: Int, - access: Int, - name: String, - signature: String?, - superName: String, - interfaces: Array - ) { - _class = MxClass(name) - super.visit(version, access, name, signature, superName, interfaces) - } - - /** - * This is called before visitMethod & visitField. - */ - override fun visitAnnotation(descriptor: String, visible: Boolean): AnnotationVisitor? { - var av = super.visitAnnotation(descriptor, visible) - if (Annotation.MIXIN == descriptor) { - av = MixinAnnotationVisitor(av, remap, targets, existingMappings, logger) - for (target in targets.toSet()) { - existingMappings[target]?.let { - targets.remove(target) - targets.add(it) - } - existingMappings[target.replace('/', '.')]?.let { - targets.remove(target) - targets.add(it.replace('.', '/')) - } - } - } else if (Annotation.IMPLEMENTS == descriptor) { - av = ImplementsAnnotationVisitor(av, interfaces) - } - return av - } - - override fun visitField( - access: Int, - name: String, - descriptor: String, - signature: String?, - value: Any? - ): FieldVisitor? { - val fv = super.visitField(access, name, descriptor, signature, value) - val field = _class!!.getField(name, descriptor) - return if (targets.isEmpty()) { - fv - } else { - HardTargetMixinFieldVisitor(tasks, fv, field, remap.get(), Collections.unmodifiableList(targets), logger) - } - } - - override fun visitMethod( - access: Int, - name: String, - descriptor: String, - signature: String?, - exceptions: Array? - ): MethodVisitor? { - val mv = super.visitMethod(access, name, descriptor, signature, exceptions) - val method = _class!!.getMethod(name, descriptor) - if (interfaces.isNotEmpty() && !MapUtility.IGNORED_NAME.contains(name)) { - ImplementsAnnotationVisitor.visitMethod(tasks, method, interfaces) - } - return if (targets.isEmpty()) { - mv - } else { - HarderTargetMixinMethodVisitor(tasks, mv, method, remap.get(), Collections.unmodifiableList(targets), logger) - } - } - - - internal class HarderTargetMixinMethodVisitor( - private val data: MutableList>, - delegate: MethodVisitor?, - private val method: MxMember, - private val remap: Boolean, - private val targets: List, - private val logger: Logger - ): - MethodVisitor(Constant.ASM_VERSION, delegate) { - - override fun visitAnnotation(descriptor: String, visible: Boolean): AnnotationVisitor? { - var av = super.visitAnnotation(descriptor, visible) - if (Annotation.SHADOW == descriptor) { - logger.info("Found shadow annotation on method ${method.name}") - av = ShadowAnnotationVisitor(data, av, method, remap, targets) - } else if (Annotation.OVERWRITE == descriptor) { - logger.info("Found overwrite annotation on method ${method.name}") - av = OverwriteAnnotationVisitor(data, av, method, remap, targets) - } - return av - } - } - - internal class HardTargetMixinFieldVisitor( - private val tasks: MutableList>, delegate: FieldVisitor?, private val field: MxMember, - private val remap: Boolean, private val targets: List, - private val logger: Logger - ): FieldVisitor(Constant.ASM_VERSION, delegate) { - - - override fun visitAnnotation(descriptor: String, visible: Boolean): AnnotationVisitor? { - var av = super.visitAnnotation(descriptor, visible) - if (Annotation.SHADOW == descriptor) { - logger.info("Found shadow annotation on field ${field.name}") - av = ShadowAnnotationVisitor(tasks, av, field, remap, targets) - } - return av - } - } - - class MixinAnnotationVisitor( - delegate: AnnotationVisitor?, - remapOut: AtomicBoolean, - targetsOut: MutableList, - val existingMappings: Map, - private val logger: Logger - ): - AnnotationVisitor(Constant.ASM_VERSION, delegate) { - private val remap0: AtomicBoolean - private val targets: MutableList - - init { - remap0 = Objects.requireNonNull(remapOut) - targets = Objects.requireNonNull(targetsOut) - remap0.set(true) // default value is true. - } - - override fun visit(name: String?, value: Any) { - if (name == AnnotationElement.REMAP) { - remap0.set(Objects.requireNonNull(value as Boolean)) - } - super.visit(name, value) - } - - override fun visitArray(name: String?): AnnotationVisitor { - val visitor = super.visitArray(name) - return when (name) { - AnnotationElement.TARGETS -> { - object: AnnotationVisitor(Constant.ASM_VERSION, visitor) { - override fun visit(name: String?, value: Any) { - val srcName = existingMappings[value as String] ?: value.replace("\\s".toRegex(), "").replace('.', '/') - logger.info("Found mixin annotation target $srcName") - targets.add(srcName) - super.visit(name, value) - } - } - } - AnnotationElement.VALUE, null -> { - object: AnnotationVisitor(Constant.ASM_VERSION, visitor) { - override fun visit(name: String?, value: Any) { - val srcType = Objects.requireNonNull(value as Type) - targets.add(srcType.internalName) - logger.info("Found mixin annotation target $srcType") - super.visit(name, value) - } - } - } - else -> { - visitor - } - } - } - } -} \ No newline at end of file diff --git a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/mixin/refmap/MixinClassVisitorRefmapBuilder.kt b/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/mixin/refmap/MixinClassVisitorRefmapBuilder.kt deleted file mode 100644 index 13886ef0..00000000 --- a/src/mapping/kotlin/xyz/wagyourtail/unimined/internal/mapping/mixin/refmap/MixinClassVisitorRefmapBuilder.kt +++ /dev/null @@ -1,984 +0,0 @@ -package xyz.wagyourtail.unimined.internal.mapping.mixin.refmap - -import com.google.gson.JsonObject -import net.fabricmc.tinyremapper.extension.mixin.common.ResolveUtility -import net.fabricmc.tinyremapper.extension.mixin.common.data.Annotation -import net.fabricmc.tinyremapper.extension.mixin.common.data.AnnotationElement -import net.fabricmc.tinyremapper.extension.mixin.common.data.CommonData -import net.fabricmc.tinyremapper.extension.mixin.common.data.Constant -import org.objectweb.asm.AnnotationVisitor -import org.objectweb.asm.ClassVisitor -import org.objectweb.asm.MethodVisitor -import org.objectweb.asm.Type -import xyz.wagyourtail.unimined.util.decapitalized -import xyz.wagyourtail.unimined.util.orElse -import java.util.* -import java.util.concurrent.atomic.AtomicBoolean - -class MixinClassVisitorRefmapBuilder( - commonData: CommonData, - val mixinName: String, - val refmap: JsonObject, - delegate: ClassVisitor, - val existingMappings: Map, - private val onEnd: () -> Unit = {}, - val allowImplicitWildcards: Boolean = false -): ClassVisitor(Constant.ASM_VERSION, delegate) { - private val mapper = commonData.mapper - private val resolver = commonData.resolver - private val logger = commonData.logger - private var remap = AtomicBoolean(true) - - val classTargets = mutableListOf() - val classValues = mutableListOf() - - val targetClasses: Set - get() = (classValues + classTargets.map { it.replace('.', '/') }).toSet() - - override fun visitAnnotation(descriptor: String?, visible: Boolean): AnnotationVisitor { - val av = if (Annotation.MIXIN == descriptor) { - object: AnnotationVisitor(Constant.ASM_VERSION, super.visitAnnotation(descriptor, visible)) { - override fun visit(name: String, value: Any) { - super.visit(name, value) - logger.info("Found annotation value $name: $value") - if (name == AnnotationElement.REMAP) { - remap.set(value as Boolean) - } - } - - override fun visitArray(name: String?): AnnotationVisitor { - return when (name) { - AnnotationElement.TARGETS -> { - return object: AnnotationVisitor(Constant.ASM_VERSION, super.visitArray(name)) { - override fun visit(name: String?, value: Any) { - if (remap.get()) { - classTargets.add(value as String) - } - super.visit(name, value) - } - } - } - - AnnotationElement.VALUE, null -> { - return object: AnnotationVisitor(Constant.ASM_VERSION, super.visitArray(name)) { - override fun visit(name: String?, value: Any) { - if (remap.get()) { - classValues.add((value as Type).internalName) - } - super.visit(name, value) - } - } - } - - else -> { - super.visitArray(name) - } - } - } - - override fun visitEnd() { - super.visitEnd() - if (remap.get()) { - logger.info("existing mappings: $existingMappings") - for (target in classTargets.toSet()) { - val clz = resolver.resolveClass(target.replace('.', '/')) - .orElse { - existingMappings[target]?.let { - logger.info("remapping $it from existing refmap") - classTargets.remove(target) - classTargets.add(it) - resolver.resolveClass(it) - } ?: Optional.empty() - } - clz.ifPresent { - refmap.addProperty(target, mapper.mapName(it)) - } - if (!clz.isPresent) { - logger.warn("Failed to resolve class $target in mixin ${mixinName.replace('/', '.')}") - } - } - } - } - } - } else { - super.visitAnnotation(descriptor, visible) - } - return av - } - - override fun visitMethod( - access: Int, name: String, descriptor: String, signature: String?, exceptions: Array? - ): MethodVisitor { - val remap = AtomicBoolean(remap.get()) - return object: MethodVisitor( - Constant.ASM_VERSION, super.visitMethod(access, name, descriptor, signature, exceptions) - ) { - - val softTargets = mapOf AnnotationVisitor>( - Annotation.ACCESSOR to ::visitAccessor, - Annotation.INVOKER to ::visitInvoker, - Annotation.INJECT to ::visitInject, - Annotation.MODIFY_ARG to ::visitModifyArg, - Annotation.MODIFY_ARGS to ::visitModifyArgs, - Annotation.MODIFY_CONSTANT to ::visitModifyConstant, - Annotation.MODIFY_VARIABLE to ::visitModifyVariable, - Annotation.REDIRECT to ::visitRedirect, - ) - - override fun visitAnnotation(descriptor: String, visible: Boolean): AnnotationVisitor { - return softTargets[descriptor]?.invoke(super.visitAnnotation(descriptor, visible)) - ?: super.visitAnnotation(descriptor, visible) - } - - fun visitAccessor(visitor: AnnotationVisitor) = object: AnnotationVisitor(Constant.ASM_VERSION, visitor) { - val remapAccessor = AtomicBoolean(remap.get()) - var targetName: String? = null - override fun visit(name: String?, value: Any) { - super.visit(name, value) - if (name == AnnotationElement.VALUE || name == null) targetName = value as String - if (name == AnnotationElement.REMAP) remapAccessor.set(value as Boolean) - } - - override fun visitEnd() { - super.visitEnd() - if (remapAccessor.get()) { - val targetNames = if (targetName != null) { - listOf(targetName!!.split(":")[0]) - } else { - val prefix = if (name.startsWith("get")) { - "get" - } else if (name.startsWith("is")) { - "is" - } else if (name.startsWith("set")) { - "set" - } else { - logger.warn( - "Failed to resolve accessor $name in mixin ${ - mixinName.replace( - '/', - '.' - ) - }, unknown prefix" - ) - return - } - listOf(name.substring(prefix.length).decapitalized(), name.substring(prefix.length)) - } - for (targetClass in targetClasses) { - for (targetName in targetNames) { - val targetDesc = if (descriptor.startsWith("()")) { - descriptor.split(')')[1] - } else { - descriptor.split(")")[0].substring(1) - } - var implicitWildcard = false - val target = resolver.resolveField( - targetClass, - targetName, - targetDesc, - ResolveUtility.FLAG_UNIQUE or ResolveUtility.FLAG_RECURSIVE - ).orElse { - existingMappings[targetName]?.let { - logger.info("remapping $it from existing refmap") - val mName = it.substringBefore(":") - val desc = if (it.contains(":")) { - it.substringAfter(":") - } else null - if (desc == null && allowImplicitWildcards) { - implicitWildcard = true - } - resolver.resolveField( - targetClass, - mName, - desc, - (if (implicitWildcard) ResolveUtility.FLAG_FIRST else ResolveUtility.FLAG_UNIQUE) or ResolveUtility.FLAG_RECURSIVE - ) - } ?: Optional.empty() - } - target.ifPresent { - val mappedName = mapper.mapName(it) - val mappedDesc = mapper.mapDesc(it) - if (implicitWildcard) { - // BUGFIX: it appears 1 length don't lowercase when checking the refmap - if (targetName.length == 1) { - refmap.addProperty(targetName.capitalize(), mappedName) - } - refmap.addProperty(targetName, mappedName) - } else { - // BUGFIX: it appears 1 length don't lowercase when checking the refmap - if (targetName.length == 1) { - refmap.addProperty(targetName.capitalize(), "$mappedName:$mappedDesc") - } - refmap.addProperty(targetName, "$mappedName:$mappedDesc") - } - } - if (target.isPresent) return - } - } - logger.warn( - "Failed to resolve field accessor $targetName ($name$descriptor) $targetNames in mixin ${ - mixinName.replace( - '/', - '.' - ) - }" - ) - } - } - } - - fun visitInvoker(visitor: AnnotationVisitor) = object: AnnotationVisitor(Constant.ASM_VERSION, visitor) { - val remapInvoker = AtomicBoolean(remap.get()) - var targetName: String? = null - override fun visit(name: String?, value: Any) { - super.visit(name, value) - if (name == AnnotationElement.VALUE || name == null) targetName = value as String - if (name == AnnotationElement.REMAP) remapInvoker.set(value as Boolean) - } - - override fun visitEnd() { - super.visitEnd() - if (remapInvoker.get()) { - val targetNames = if (targetName != null) { - listOf(targetName) - } else { - val prefix = if (name.startsWith("call")) { - "call" - } else if (name.startsWith("invoke")) { - "invoke" - } else if (name.startsWith("new")) { - "new" - } else if (name.startsWith("create")) { - "create" - } else { - logger.warn( - "Failed to resolve invoker $name in mixin ${ - mixinName.replace( - '/', - '.' - ) - }, unknown prefix" - ) - return - } - listOf(name.substring(prefix.length).decapitalized(), name.substring(prefix.length)) - } - for (targetClass in targetClasses) { - for (targetName in targetNames) { - var implicitWildcard = false - val target = resolver.resolveMethod( - targetClass, - targetName, - if (targetName == "") "${descriptor.substringBefore(")")})V" else descriptor, - ResolveUtility.FLAG_UNIQUE or ResolveUtility.FLAG_RECURSIVE - ).orElse { - existingMappings[targetName]?.let { - logger.info("remapping $it from existing refmap") - val mName = it.substringBefore("(") - val desc = if (it.contains("(")) { - "(${it.substringAfter("(")}" - } else null - if (desc == null && allowImplicitWildcards) { - implicitWildcard = true - } - resolver.resolveMethod( - targetClass, - mName, - desc, - (if (implicitWildcard) ResolveUtility.FLAG_FIRST else ResolveUtility.FLAG_UNIQUE) or ResolveUtility.FLAG_RECURSIVE - ) - } ?: Optional.empty() - } - target.ifPresent { - val mappedName = mapper.mapName(it) - val mappedDesc = mapper.mapDesc(it).let { if (mappedName == "") "" else it } -// if (implicitWildcard) { -// refmap.addProperty(targetName, mappedName) -// } else { - refmap.addProperty(targetName, "$mappedName$mappedDesc") -// } - } - if (target.isPresent) { - return - } - } - } - logger.warn( - "Failed to resolve invoker $targetName ($name$descriptor) $targetNames in mixin ${ - mixinName.replace( - '/', - '.' - ) - }" - ) - } - } - } - - fun visitInject(visitor: AnnotationVisitor) = object: AnnotationVisitor(Constant.ASM_VERSION, visitor) { - val remapInject = AtomicBoolean(remap.get()) - var targetNames = mutableListOf() - override fun visit(name: String, value: Any) { - super.visit(name, value) - if (name == AnnotationElement.REMAP) remapInject.set(value as Boolean) - } - - override fun visitArray(name: String): AnnotationVisitor { - val delegate = super.visitArray(name) - return when (name) { - AnnotationElement.AT -> { - ArrayVisitorWrapper(Constant.ASM_VERSION, delegate) { visitAt(it, remap) } - } - - AnnotationElement.SLICE -> { - ArrayVisitorWrapper(Constant.ASM_VERSION, delegate) { visitSlice(it, remap) } - } - - AnnotationElement.TARGET -> { - ArrayVisitorWrapper(Constant.ASM_VERSION, delegate) { visitDesc(it, remap) } - } - - AnnotationElement.METHOD -> { - object: AnnotationVisitor(Constant.ASM_VERSION, delegate) { - override fun visit(name: String?, value: Any) { - super.visit(name, value) - targetNames.add(value as String) - } - } - } - - else -> { - delegate - } - } - } - - val callbackInfo = "Lorg/spongepowered/asm/mixin/injection/callback/CallbackInfo" - val callbackInfoReturn = "Lorg/spongepowered/asm/mixin/injection/callback/CallbackInfoReturnable" - - private fun parseCIRVal(): String? { - val remain = signature?.substringAfterLast("$callbackInfoReturn<", "") ?: "" - if (remain.isEmpty()) { - return null - } - var valBuild = "" - var depth = 1 - for (c in remain) { - if (c == '<') { - depth++ - } else if (c == '>') { - depth-- - } - if (depth == 0) { - break - } - valBuild += c - } - return valBuild.substringBefore("<").substringBefore(";") + ";" - } - - private fun toPrimitive(sig: String): String? { - return when (sig) { - "Ljava/lang/Integer;" -> "I" - "Ljava/lang/Long;" -> "J" - "Ljava/lang/Short;" -> "S" - "Ljava/lang/Byte;" -> "B" - "Ljava/lang/Character;" -> "C" - "Ljava/lang/Float;" -> "F" - "Ljava/lang/Double;" -> "D" - "Ljava/lang/Boolean;" -> "Z" - else -> null - } - } - - private fun stripCallbackInfoFromDesc(): Set { - val desc = descriptor.substringBeforeLast(callbackInfo) + ")V" - if (descriptor.contains(callbackInfoReturn)) { - val returnType = parseCIRVal() - if (returnType == null) { - logger.warn("Failed to parse return type from $descriptor on $mixinName") - return setOf(null) - } - val rets = setOfNotNull( - desc.replace(")V", ")$returnType"), - toPrimitive(returnType)?.let { desc.replace(")V", ")${it}") }) - logger.info("Found returnable inject, signatures $rets, return type $returnType") - return rets - } - return setOf(desc) - } - - override fun visitEnd() { - super.visitEnd() - if (remapInject.get()) { - targetNames.forEach { targetMethod -> - if (targetMethod == "" || targetMethod == "" || - targetMethod == "*" - ) { - return@forEach - } - val wildcard = targetMethod.endsWith("*") - val (targetName, targetDescs) = if (targetMethod.contains("(")) { - val n = targetMethod.split("(") - (n[0] to setOf("(${n[1]}")) - } else { - if (wildcard) { - (targetMethod.substring(0, targetMethod.length - 1) to setOf(null)) - } else { - (targetMethod to stripCallbackInfoFromDesc() + setOf(null)) - } - } - for (targetDesc in targetDescs) { - var implicitWildcard = targetDesc == null && allowImplicitWildcards - for (targetClass in targetClasses) { - val target = resolver.resolveMethod( - targetClass, - targetName, - targetDesc, - (if (wildcard || implicitWildcard) ResolveUtility.FLAG_FIRST else ResolveUtility.FLAG_UNIQUE) or ResolveUtility.FLAG_RECURSIVE - ).orElse { - existingMappings[targetMethod]?.let { - if (wildcard) { - val mName = it.substringAfter(";").let { it.substring(0, it.length - 1) } - logger.info("remapping $targetMethod from existing refmap") - resolver.resolveMethod( - targetClass, - mName, - null, - ResolveUtility.FLAG_FIRST or ResolveUtility.FLAG_RECURSIVE - ) - } else { - val mName = it.substringBefore("(").substringAfter(";") - val desc = if (it.contains("(")) "(${it.substringAfter("(")}" else null - if (desc == null && allowImplicitWildcards) { - implicitWildcard = true - } - logger.info("remapping $targetMethod from existing refmap") - resolver.resolveMethod( - targetClass, - mName, - desc, - (if (implicitWildcard) ResolveUtility.FLAG_FIRST else ResolveUtility.FLAG_UNIQUE) or ResolveUtility.FLAG_RECURSIVE - ) - } - } ?: Optional.empty() - } - target.ifPresent { - val mappedClass = resolver.resolveClass(targetClass) - .map { mapper.mapName(it) } - .orElse(targetClass) - val mappedName = mapper.mapName(it) - val mappedDesc = /* if (implicitWildcard) "" else */ if (wildcard && mappedName != "") "*" else mapper.mapDesc(it) - if (targetClasses.size > 1) { - refmap.addProperty(targetMethod, "$mappedName$mappedDesc") - } else { - refmap.addProperty(targetMethod, "L$mappedClass;$mappedName$mappedDesc") - } - } - if (target.isPresent) { - return@forEach - } - } - } - logger.warn( - "Failed to resolve Inject $targetMethod ($name$descriptor) $signature $targetDescs in mixin ${ - mixinName.replace( - '/', - '.' - ) - }" - ) - } - } - } - } - - fun visitModifyArg(visitor: AnnotationVisitor) = object: AnnotationVisitor(Constant.ASM_VERSION, visitor) { - val remapModifyArg = AtomicBoolean(remap.get()) - var targetNames = mutableListOf() - - override fun visit(name: String, value: Any) { - super.visit(name, value) - if (name == AnnotationElement.REMAP) remapModifyArg.set(value as Boolean) - } - - override fun visitAnnotation(name: String, descriptor: String): AnnotationVisitor { - return when (name) { - AnnotationElement.AT -> { - visitAt(super.visitAnnotation(name, descriptor), remap) - } - - AnnotationElement.SLICE -> { - visitSlice(super.visitAnnotation(name, descriptor), remap) - } - - else -> { - super.visitAnnotation(name, descriptor) - } - } - } - - override fun visitArray(name: String): AnnotationVisitor { - return when (name) { - AnnotationElement.TARGET -> { - ArrayVisitorWrapper(Constant.ASM_VERSION, super.visitArray(name)) { visitDesc(it, remap) } - } - - AnnotationElement.METHOD -> { - object: AnnotationVisitor(Constant.ASM_VERSION, super.visitArray(name)) { - override fun visit(name: String?, value: Any) { - super.visit(name, value) - targetNames.add(value as String) - } - } - } - - else -> { - super.visitArray(name) - } - } - } - - override fun visitEnd() { - super.visitEnd() - if (remapModifyArg.get()) { - targetNames.forEach { targetMethod -> - for (targetClass in targetClasses) { - val targetDesc = if (targetMethod.contains("(")) { - "(" + targetMethod.split("(")[1] - } else { - null - } - val wildcard = targetMethod.endsWith("*") - var implicitWildcard = targetDesc == null && allowImplicitWildcards - val targetName = if (wildcard) { - targetMethod.substring(0, targetMethod.length - 1) - } else { - targetMethod.split("(")[0] - } - - val target = resolver.resolveMethod( - targetClass, - targetName, - targetDesc, - (if (wildcard || implicitWildcard) ResolveUtility.FLAG_FIRST else ResolveUtility.FLAG_UNIQUE) or ResolveUtility.FLAG_RECURSIVE - ).orElse { - existingMappings[targetMethod]?.let { - logger.info("remapping $it from existing refmap") - if (wildcard) { - val mName = it.substringAfter(";").let { it.substring(0, it.length - 1) } - resolver.resolveMethod( - targetClass, - mName, - null, - ResolveUtility.FLAG_FIRST or ResolveUtility.FLAG_RECURSIVE - ) - } else { - val mName = it.substringBefore("(").substringAfter(";") - val desc = if (it.contains("(")) "(${it.substringAfter("(")}" else null - if (desc == null && allowImplicitWildcards) { - implicitWildcard = true - } - resolver.resolveMethod( - targetClass, - mName, - desc, - (if (implicitWildcard) ResolveUtility.FLAG_FIRST else ResolveUtility.FLAG_UNIQUE) or ResolveUtility.FLAG_RECURSIVE - ) - } - } ?: Optional.empty() - } - target.ifPresent { - val mappedClass = resolver.resolveClass(targetClass) - .map { mapper.mapName(it) } - .orElse(targetClass) - val mappedName = mapper.mapName(it) - val mappedDesc = /* if (implicitWildcard) "" else */ if (wildcard) "*" else mapper.mapDesc(it) - if (targetClasses.size > 1) { - refmap.addProperty(targetMethod, "$mappedName$mappedDesc") - } else { - refmap.addProperty(targetMethod, "L$mappedClass;$mappedName$mappedDesc") - } - } - if (target.isPresent) { - return@forEach - } - } - logger.warn( - "Failed to resolve ModifyArg(s)/Redirect $targetMethod ($name$descriptor) in mixin ${ - mixinName.replace( - '/', - '.' - ) - }" - ) - } - } - } - } - - fun visitModifyArgs(visitor: AnnotationVisitor) = visitModifyArg(visitor) - - fun visitModifyConstant(visitor: AnnotationVisitor) = - object: AnnotationVisitor(Constant.ASM_VERSION, visitor) { - val remapModifyConstant = AtomicBoolean(remap.get()) - var targetNames = mutableListOf() - - override fun visit(name: String, value: Any) { - super.visit(name, value) - if (name == AnnotationElement.REMAP) remapModifyConstant.set(value as Boolean) - } - - override fun visitArray(name: String): AnnotationVisitor { - return when (name) { - AnnotationElement.TARGET -> { - ArrayVisitorWrapper(Constant.ASM_VERSION, super.visitArray(name)) { visitDesc(it, remap) } - } - AnnotationElement.SLICE -> { - ArrayVisitorWrapper(Constant.ASM_VERSION, super.visitArray(name)) { visitSlice(it, remap) } - } - AnnotationElement.METHOD -> { - object: AnnotationVisitor(Constant.ASM_VERSION, super.visitArray(name)) { - override fun visit(name: String?, value: Any) { - super.visit(name, value) - targetNames.add(value as String) - } - } - } - else -> { - super.visitArray(name) - } - } - } - - override fun visitEnd() { - super.visitEnd() - if (remapModifyConstant.get()) { - targetNames.forEach { targetMethod -> - for (targetClass in targetClasses) { - val targetDesc = if (targetMethod.contains("(")) { - "(" + targetMethod.split("(")[1] - } else { - null - } - - val wildcard = targetMethod.endsWith("*") - var implicitWildcard = targetDesc == null && allowImplicitWildcards - val targetName = if (wildcard) { - targetMethod.substring(0, targetMethod.length - 1) - } else { - targetMethod.split("(")[0] - } - - val target = resolver.resolveMethod( - targetClass, - targetName, - targetDesc, - (if (wildcard || implicitWildcard) ResolveUtility.FLAG_FIRST else ResolveUtility.FLAG_UNIQUE) or ResolveUtility.FLAG_RECURSIVE - ).orElse { - existingMappings[targetMethod]?.let { - logger.info("remapping $it from existing refmap") - if (wildcard) { - val mName = it.substringAfter(";").let { it.substring(0, it.length - 1) } - resolver.resolveMethod( - targetClass, - mName, - null, - ResolveUtility.FLAG_FIRST or ResolveUtility.FLAG_RECURSIVE - ) - } else { - val mName = it.substringBefore("(").substringAfter(";") - val desc = if (it.contains("(")) "(${it.substringAfter("(")}" else null - if (desc == null && allowImplicitWildcards) { - implicitWildcard = true - } - resolver.resolveMethod( - targetClass, - mName, - desc, - (if (implicitWildcard) ResolveUtility.FLAG_FIRST else ResolveUtility.FLAG_UNIQUE) or ResolveUtility.FLAG_RECURSIVE - ) - } - } ?: Optional.empty() - } - - - target.ifPresent { - val mappedClass = resolver.resolveClass(targetClass) - .map { mapper.mapName(it) } - .orElse(targetClass) - val mappedName = mapper.mapName(it) - val mappedDesc = /* if (implicitWildcard) "" else */ if (wildcard) "*" else mapper.mapDesc(it) - if (targetClasses.size > 1) { - refmap.addProperty(targetMethod, "$mappedName$mappedDesc") - } else { - refmap.addProperty(targetMethod, "L$mappedClass;$mappedName$mappedDesc") - } - } - if (target.isPresent) { - return@forEach - } - } - logger.warn( - "Failed to resolve ModifyConstant $targetMethod ($name$descriptor) in mixin ${ - mixinName.replace( - '/', - '.' - ) - }" - ) - } - } - } - } - - fun visitModifyVariable(visitor: AnnotationVisitor) = visitModifyArg(visitor) - - fun visitRedirect(visitor: AnnotationVisitor) = visitModifyArg(visitor) - } - } - - fun visitAt(visitor: AnnotationVisitor, remap: AtomicBoolean) = - object: AnnotationVisitor(Constant.ASM_VERSION, visitor) { - val remapAt = AtomicBoolean(remap.get()) - var targetName: String? = null - override fun visit(name: String, value: Any) { - super.visit(name, value) - if (name == AnnotationElement.REMAP) remapAt.set(value as Boolean) - if (name == AnnotationElement.TARGET) targetName = (value as String).replace(" ", "") - } - - override fun visitAnnotation(name: String, descriptor: String): AnnotationVisitor { - return if (name == AnnotationElement.DESC) { - visitDesc(super.visitAnnotation(name, descriptor), remapAt) - } else { - logger.warn("Found annotation in target descriptor: $name $descriptor") - super.visitAnnotation(name, descriptor) - } - } - - val targetField = Regex("^(L[^;]+;|[^.]+?\\.)([^:]+):(.+)$") - val targetMethod = Regex("^(L[^;]+;|[^.]+?\\.)([^(]+)\\s*([^>]+)$") - - override fun visitEnd() { - super.visitEnd() - if (remapAt.get() && targetName != null) { - val matchFd = targetField.matchEntire(targetName!!) - if (matchFd != null) { - var targetOwner = matchFd.groupValues[1].let { - if (it.startsWith("L") && it.endsWith(";")) it.substring( - 1, - it.length - 1 - ) else it.substring(0, it.length - 1) - } - val targetName = matchFd.groupValues[2] - val targetDesc = matchFd.groupValues[3] - val target = resolver.resolveField( - targetOwner, - targetName, - targetDesc, - ResolveUtility.FLAG_UNIQUE or ResolveUtility.FLAG_RECURSIVE - ).orElse { - existingMappings[this.targetName]?.let { - logger.info("remapping $it from existing refmap") - val matchEFd = targetField.matchEntire(it) - if (matchEFd != null) { - targetOwner = matchEFd.groupValues[1].let { - if (it.startsWith("L") && it.endsWith(";")) it.substring( - 1, - it.length - 1 - ) else it.substring(0, it.length - 1) - } - val fName = matchEFd.groupValues[2] - val fDesc = matchEFd.groupValues[3] - resolver.resolveField( - targetOwner, - fName, - fDesc, - ResolveUtility.FLAG_UNIQUE or ResolveUtility.FLAG_RECURSIVE - ) - } else { - Optional.empty() - } - } ?: Optional.empty() - } - val targetClass = resolver.resolveClass(targetOwner) - targetClass.ifPresent { clz -> - target.ifPresent { - val mappedOwner = mapper.mapName(clz) - val mappedName = mapper.mapName(it) - val mappedDesc = mapper.mapDesc(it) - refmap.addProperty(this.targetName, "L$mappedOwner;$mappedName:$mappedDesc") - } - } - if (!target.isPresent || !targetClass.isPresent) { - logger.warn( - "Failed to resolve At target $targetName in mixin ${ - mixinName.replace( - '/', - '.' - ) - }" - ) - } - return - } - val matchMd = targetMethod.matchEntire(targetName!!) - if (matchMd != null) { - var targetOwner = matchMd.groupValues[1].let { - if (it.startsWith("L") && it.endsWith(";")) it.substring( - 1, - it.length - 1 - ) else it.substring(0, it.length - 1) - } - val targetName = matchMd.groupValues[2] - val targetDesc = matchMd.groupValues[3] - val target = resolver.resolveMethod( - targetOwner, - targetName, - targetDesc, - ResolveUtility.FLAG_UNIQUE or ResolveUtility.FLAG_RECURSIVE - ).orElse { - existingMappings[this.targetName]?.let { - logger.info("remapping $it from existing refmap") - val matchEMd = targetMethod.matchEntire(it) - if (matchEMd != null) { - targetOwner = matchEMd.groupValues[1].let { - if (it.startsWith("L") && it.endsWith(";")) it.substring( - 1, - it.length - 1 - ) else it.substring(0, it.length - 1) - } - val mName = matchEMd.groupValues[2] - val mDesc = matchEMd.groupValues[3] - resolver.resolveMethod( - targetOwner, - mName, - mDesc, - ResolveUtility.FLAG_UNIQUE or ResolveUtility.FLAG_RECURSIVE - ) - } else { - Optional.empty() - } - } ?: Optional.empty() - } - val targetClass = resolver.resolveClass(targetOwner) - targetClass.ifPresent { clz -> - target.ifPresent { - val mappedOwner = mapper.mapName(clz) - val mappedName = mapper.mapName(it) - val mappedDesc = mapper.mapDesc(it) - refmap.addProperty(this.targetName, "L$mappedOwner;$mappedName$mappedDesc") - } - } - if (!target.isPresent || !targetClass.isPresent) { - logger.warn( - "Failed to resolve At target $targetName in mixin ${ - mixinName.replace( - '/', - '.' - ) - }" - ) - } - return - } - if (targetName!!.startsWith("(")) { - val existing = existingMappings[this.targetName] - if (existing != null) { - logger.info("remapping $existing from existing refmap") - val mapped = mapper.asTrRemapper().mapDesc(existing) - if (mapped == existing) { - logger.warn("Failed to remap $existing") - return - } - return - } else { - val mapped = mapper.asTrRemapper().mapDesc(targetName) - if (mapped == targetName) { - logger.warn("Failed to remap $targetName") - return - } else { - refmap.addProperty(this.targetName, mapped) - return - } - } - } - - // else is probably a class - // we will count (NEW, ) as that too - // carefully, by modifying method so it doesn't capture it - val fixedTarget = if (targetName!!.startsWith("L")) { - targetName!!.substring(1).substringBefore("").substringBefore(";") - } else { - targetName!!.substringBefore(".") - }.replace('.', '/') - val target = resolver.resolveClass(fixedTarget).orElse { - existingMappings[this.targetName]?.let { - logger.info("remapping $it from existing refmap") - resolver.resolveClass(if (it.startsWith("L") && it.endsWith(";")) { - it.substring(1, it.length - 1) - } else { - it - }.replace('.', '/')) - } ?: Optional.empty() - } - target.ifPresent { - val mapped = mapper.mapName(it) - refmap.addProperty(this.targetName, "L$mapped;") - } - if (target.isPresent) { - return - } - - logger.warn( - "Failed to parse target descriptor: $targetName ($fixedTarget) in mixin ${ - mixinName.replace( - '/', - '.' - ) - }" - ) - } - } - } - - fun visitDesc(visitor: AnnotationVisitor, remap: AtomicBoolean) = - object: AnnotationVisitor(Constant.ASM_VERSION, visitor) { - @Suppress("UNUSED") val remapDesc = AtomicBoolean(remap.get()) - override fun visit(name: String, value: Any) { - TODO() - } - - override fun visitAnnotation(name: String?, descriptor: String?): AnnotationVisitor { - TODO() - } - } - - fun visitSlice(visitor: AnnotationVisitor, remap: AtomicBoolean) = - object: AnnotationVisitor(Constant.ASM_VERSION, visitor) { - override fun visitAnnotation(name: String?, descriptor: String?): AnnotationVisitor { - return if (name == AnnotationElement.FROM || name == AnnotationElement.TO) { - visitAt(super.visitAnnotation(name, descriptor), remap) - } else { - super.visitAnnotation(name, descriptor) - } - } - } - - override fun visitEnd() { - super.visitEnd() - onEnd() - } - - class ArrayVisitorWrapper( - val api: Int, - delegate: AnnotationVisitor, - val delegateCreator: (AnnotationVisitor) -> AnnotationVisitor - ): AnnotationVisitor(api, delegate) { - override fun visitAnnotation(name: String?, descriptor: String): AnnotationVisitor { - return delegateCreator(super.visitAnnotation(name, descriptor)) - } - } - -} \ No newline at end of file diff --git a/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/MinecraftProvider.kt b/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/MinecraftProvider.kt index 4c768ba3..56fbdcbc 100644 --- a/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/MinecraftProvider.kt +++ b/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/MinecraftProvider.kt @@ -5,6 +5,7 @@ import org.gradle.api.Task import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.Dependency import org.gradle.api.artifacts.ModuleDependency +import org.gradle.api.file.FileCollection import org.gradle.api.tasks.SourceSet import org.gradle.configurationcache.extensions.capitalized import org.gradle.jvm.tasks.Jar @@ -16,6 +17,8 @@ import xyz.wagyourtail.unimined.api.minecraft.MinecraftConfig import xyz.wagyourtail.unimined.api.minecraft.patch.* import xyz.wagyourtail.unimined.api.runs.RunConfig import xyz.wagyourtail.unimined.api.task.RemapJarTask +import xyz.wagyourtail.unimined.api.unimined +import xyz.wagyourtail.unimined.api.uniminedMaybe import xyz.wagyourtail.unimined.internal.mapping.MappingsProvider import xyz.wagyourtail.unimined.internal.mapping.task.ExportMappingsTaskImpl import xyz.wagyourtail.unimined.internal.minecraft.patch.AbstractMinecraftTransformer @@ -67,10 +70,12 @@ class MinecraftProvider(project: Project, sourceSet: SourceSet) : MinecraftConfi private val patcherActions = ArrayDeque<() -> Unit>() private var lateActionsRunning by FinalizeOnWrite(false) + override val combinedWithList = mutableListOf>() + var applied: Boolean by FinalizeOnWrite(false) private set - val minecraft: Configuration = project.configurations.maybeCreate("minecraft".withSourceSet(sourceSet)).also { + override val minecraft: Configuration = project.configurations.maybeCreate("minecraft".withSourceSet(sourceSet)).also { sourceSet.compileClasspath += it sourceSet.runtimeClasspath += it } @@ -81,6 +86,32 @@ class MinecraftProvider(project: Project, sourceSet: SourceSet) : MinecraftConfi it.setTransitive(false) } + override fun from(project: Project, sourceSet: SourceSet) { + val delegate = MinecraftProvider::class.getField("mcPatcher")!!.getDelegate(this) as FinalizeOnRead> + if (delegate.finalized || (delegate.value as FinalizeOnWrite).finalized) { + throw IllegalStateException("mcPatcher is already finalized before from() call, from should really be called at the top...") + } + if (project != this.project) { + this.project.evaluationDependsOn(project.path) + } + project.unimined.minecraftConfiguration[sourceSet]!!(this) + if (delegate.finalized) { + project.logger.warn("[Unimined/Minecraft ${project.path}:${sourceSet.name}] un-finalizing mcPatcher set in from() call") + } + delegate.finalized = false + (delegate.value as FinalizeOnWrite).finalized = false + } + + override fun combineWith(project: Project, sourceSet: SourceSet) { + combinedWithList.add(project to sourceSet) + if (project.uniminedMaybe != null && project.unimined.minecrafts.contains(sourceSet)) { + from(project, sourceSet) + } + this.sourceSet.compileClasspath += sourceSet.output + this.sourceSet.runtimeClasspath += sourceSet.output + // remove unimined deps + } + override fun remap(task: Task, name: String, action: RemapJarTask.() -> Unit) { val remapTask = project.tasks.register(name, RemapJarTaskImpl::class.java, this) remapTask.configure { @@ -89,6 +120,7 @@ class MinecraftProvider(project: Project, sourceSet: SourceSet) : MinecraftConfi } it.action() it.dependsOn(task) + mcPatcher.configureRemapJar(it) } } @@ -104,7 +136,7 @@ class MinecraftProvider(project: Project, sourceSet: SourceSet) : MinecraftConfi } private val minecraftFiles: Map, Path> = defaultedMapOf { - project.logger.info("[Unimined/Minecraft] Providing minecraft files for $it") + project.logger.info("[Unimined/Minecraft ${project.path}:${sourceSet.name}] Providing minecraft files for $it") val mc = if (side == EnvType.COMBINED) { val client = minecraftData.minecraftClient val server = minecraftData.minecraftServer @@ -174,11 +206,11 @@ class MinecraftProvider(project: Project, sourceSet: SourceSet) : MinecraftConfi } @Deprecated("Please specify which forge.", replaceWith = ReplaceWith("minecraftForge(action)")) - override fun forge(action: ForgeLikePatcher.() -> Unit) { + override fun forge(action: ForgeLikePatcher<*>.() -> Unit) { minecraftForge(action) } - override fun minecraftForge(action: MinecraftForgePatcher.() -> Unit) { + override fun minecraftForge(action: MinecraftForgePatcher<*>.() -> Unit) { mcPatcher = MinecraftForgeMinecraftTransformer(project, this).also { patcherActions.addFirst { action(it) @@ -186,7 +218,7 @@ class MinecraftProvider(project: Project, sourceSet: SourceSet) : MinecraftConfi } } - override fun neoForged(action: NeoForgedPatcher.() -> Unit) { + override fun neoForged(action: NeoForgedPatcher<*>.() -> Unit) { mcPatcher = NeoForgedMinecraftTransformer(project, this).also { patcherActions.addFirst { action(it) @@ -224,9 +256,9 @@ class MinecraftProvider(project: Project, sourceSet: SourceSet) : MinecraftConfi fun addLibraries(libraries: List) { for (library in libraries) { if (library.rules.all { it.testRule() }) { - project.logger.info("[Unimined/Minecraft] Added dependency ${library.name}") + project.logger.info("[Unimined/Minecraft ${project.path}:${sourceSet.name}] Added dependency ${library.name}") if (!(mcPatcher as AbstractMinecraftTransformer).libraryFilter(library)) { - project.logger.info("[Unimined/Minecraft] Excluding dependency ${library.name} as it is filtered by the patcher") + project.logger.info("[Unimined/Minecraft ${project.path}:${sourceSet.name}] Excluding dependency ${library.name} as it is filtered by the patcher") continue } val native = library.natives[OSUtils.oSId] @@ -236,7 +268,7 @@ class MinecraftProvider(project: Project, sourceSet: SourceSet) : MinecraftConfi library.extract?.let { extractDependencies[dep] = it } } if (native != null) { - project.logger.info("[Unimined/Minecraft] Added native dependency ${library.name}:$native") + project.logger.info("[Unimined/Minecraft ${project.path}:${sourceSet.name}] Added native dependency ${library.name}:$native") val nativeDep = project.dependencies.create("${library.name}:$native") minecraftLibraries.dependencies.add(nativeDep) library.extract?.let { extractDependencies[nativeDep] = it } @@ -246,19 +278,19 @@ class MinecraftProvider(project: Project, sourceSet: SourceSet) : MinecraftConfi } private fun clientRun() { - project.logger.info("[Unimined/Minecraft] client config") + project.logger.info("[Unimined/Minecraft ${project.path}:${sourceSet.name}] client config") runs.addTarget(provideVanillaRunClientTask("client", project.file("run/client"))) runs.configFirst("client", (mcPatcher as AbstractMinecraftTransformer)::applyClientRunTransform) } private fun serverRun() { - project.logger.info("[Unimined/Minecraft] server config") + project.logger.info("[Unimined/Minecraft ${project.path}:${sourceSet.name}] server config") runs.addTarget(provideVanillaRunServerTask("server", project.file("run/server"))) runs.configFirst("server", (mcPatcher as AbstractMinecraftTransformer)::applyServerRunTransform) } fun applyRunConfigs() { - project.logger.lifecycle("[Unimined/Minecraft] Applying run configs") + project.logger.lifecycle("[Unimined/Minecraft ${project.path}:${sourceSet.name}] Applying run configs") when (side) { EnvType.CLIENT -> { clientRun() @@ -278,7 +310,7 @@ class MinecraftProvider(project: Project, sourceSet: SourceSet) : MinecraftConfi fun apply() { if (applied) return applied = true - project.logger.lifecycle("[Unimined/Minecraft] Applying minecraft config for $sourceSet") + project.logger.lifecycle("[Unimined/Minecraft ${project.path}:${sourceSet.name}] Applying minecraft config for $sourceSet") lateActionsRunning = true @@ -286,21 +318,21 @@ class MinecraftProvider(project: Project, sourceSet: SourceSet) : MinecraftConfi patcherActions.removeFirst().invoke() } - project.logger.info("[Unimined/MappingProvider] before mappings $sourceSet") + project.logger.info("[Unimined/MappingProvider ${project.path}:${sourceSet.name}] before mappings $sourceSet") (mcPatcher as AbstractMinecraftTransformer).beforeMappingsResolve() // finalize mapping deps - project.logger.info("[Unimined/MappingProvider] $sourceSet mappings: ${mappings.getNamespaces()}") + project.logger.info("[Unimined/MappingProvider ${project.path}:${sourceSet.name}] $sourceSet mappings: ${mappings.getNamespaces()}") // late actions done // ensure minecraft deps are clear if (minecraft.dependencies.isNotEmpty()) { - project.logger.warn("[Unimined/Minecraft] $minecraft dependencies are not empty! clearing...") + project.logger.warn("[Unimined/Minecraft ${project.path}:${sourceSet.name}] $minecraft dependencies are not empty! clearing...") minecraft.dependencies.clear() } if (minecraftLibraries.dependencies.isNotEmpty()) { - project.logger.warn("[Unimined/Minecraft] $minecraftLibraries dependencies are not empty! clearing...") + project.logger.warn("[Unimined/Minecraft ${project.path}:${sourceSet.name}] $minecraftLibraries dependencies are not empty! clearing...") minecraftLibraries.dependencies.clear() } @@ -323,12 +355,23 @@ class MinecraftProvider(project: Project, sourceSet: SourceSet) : MinecraftConfi // create remapjar task if (defaultRemapJar) { - val task = project.tasks.findByName("jar".withSourceSet(sourceSet)) + var task = project.tasks.findByName("jar".withSourceSet(sourceSet)) + if (task == null && createJarTask) { + project.logger.info("[Unimined/Minecraft ${project.path}:${sourceSet.name}] Creating default jar task for $sourceSet") + task = project.tasks.create("jar".withSourceSet(sourceSet), Jar::class.java) { + it.group = "build" + it.from(sourceSet.output) + it.archiveClassifier.set(sourceSet.name) + for ((_, sourceSet) in combinedWithList) { + it.from(sourceSet.output) + } + } + } if (task != null && task is Jar) { val classifier: String= task.archiveClassifier.getOrElse("") task.apply { if (classifier.isNotEmpty()) { - archiveClassifier.set(archiveClassifier.get() + "-dev") + archiveClassifier.set("$classifier-dev") } else { archiveClassifier.set("dev") } @@ -341,18 +384,18 @@ class MinecraftProvider(project: Project, sourceSet: SourceSet) : MinecraftConfi project.tasks.getByName("build").dependsOn("remap" + "jar".withSourceSet(sourceSet).capitalized()) } else { project.logger.warn( - "[Unimined/Minecraft] Could not find default jar task for $sourceSet. named: ${ + "[Unimined/Minecraft ${project.path}:${sourceSet.name}] Could not find default jar task for $sourceSet. named: ${ "jar".withSourceSet( sourceSet ) }." ) - project.logger.warn("[Unimined/Minecraft] add manually with `remap(task)` in the minecraft block for $sourceSet") + project.logger.warn("[Unimined/Minecraft ${project.path}:${sourceSet.name}] add manually with `remap(task)` in the minecraft block for $sourceSet") } } // apply minecraft patcher changes - project.logger.lifecycle("[Unimined/Minecraft] Applying ${mcPatcher.name()}") + project.logger.lifecycle("[Unimined/Minecraft ${project.path}:${sourceSet.name}] Applying ${mcPatcher.name()}") (mcPatcher as AbstractMinecraftTransformer).apply() // create run configs @@ -378,7 +421,7 @@ class MinecraftProvider(project: Project, sourceSet: SourceSet) : MinecraftConfi fun afterEvaluate() { if (!applied) throw IllegalStateException("minecraft config never applied for $sourceSet") - project.logger.info("[Unimined/MinecraftProvider] minecraft file: $minecraftFileDev") + project.logger.info("[Unimined/MinecraftProvider ${project.path}:${sourceSet.name}] minecraft file: $minecraftFileDev") // remap mods mods.afterEvaluate() @@ -388,9 +431,9 @@ class MinecraftProvider(project: Project, sourceSet: SourceSet) : MinecraftConfi } override val minecraftFileDev: File by lazy { - project.logger.info("[Unimined/Minecraft] Providing minecraft dev file to $sourceSet") + project.logger.info("[Unimined/Minecraft ${project.path}:${sourceSet.name}] Providing minecraft dev file to $sourceSet") getMinecraft(mappings.devNamespace, mappings.devFallbackNamespace).toFile().also { - project.logger.info("[Unimined/Minecraft] Provided minecraft dev file $it") + project.logger.info("[Unimined/Minecraft ${project.path}:${sourceSet.name}] Provided minecraft dev file $it") } } diff --git a/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/AbstractMinecraftTransformer.kt b/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/AbstractMinecraftTransformer.kt index fe405eeb..51b37976 100644 --- a/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/AbstractMinecraftTransformer.kt +++ b/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/AbstractMinecraftTransformer.kt @@ -1,16 +1,20 @@ package xyz.wagyourtail.unimined.internal.minecraft.patch import org.gradle.api.Project +import org.gradle.api.tasks.SourceSet +import org.gradle.api.tasks.SourceSetContainer import org.jetbrains.annotations.ApiStatus import org.objectweb.asm.ClassReader import org.objectweb.asm.ClassWriter import org.objectweb.asm.tree.ClassNode import xyz.wagyourtail.unimined.api.mapping.MappingNamespaceTree import xyz.wagyourtail.unimined.api.minecraft.EnvType +import xyz.wagyourtail.unimined.api.minecraft.MinecraftConfig import xyz.wagyourtail.unimined.api.minecraft.patch.MinecraftPatcher import xyz.wagyourtail.unimined.api.runs.RunConfig import xyz.wagyourtail.unimined.api.task.RemapJarTask import xyz.wagyourtail.unimined.api.unimined +import xyz.wagyourtail.unimined.api.uniminedMaybe import xyz.wagyourtail.unimined.internal.minecraft.MinecraftProvider import xyz.wagyourtail.unimined.internal.minecraft.resolver.Library import xyz.wagyourtail.unimined.internal.minecraft.transform.fixes.FixParamAnnotations @@ -163,6 +167,8 @@ abstract class AbstractMinecraftTransformer protected constructor( open fun applyExtraLaunches() { } + fun Pair.toPath() = first.path + ":" + second.name + @ApiStatus.Internal open fun applyClientRunTransform(config: RunConfig) { if (unprotectRuntime) { @@ -231,4 +237,68 @@ abstract class AbstractMinecraftTransformer protected constructor( open fun libraryFilter(library: Library): Boolean { return true } + + protected fun detectProjectSourceSets(): Set> { + val sourceSets = mutableSetOf>() + val projects = project.rootProject.allprojects + for (project in projects) { + for (sourceSet in project.extensions.findByType(SourceSetContainer::class.java)?.asMap?.values + ?: listOf()) { + if (sourceSet.output.files.intersect(provider.sourceSet.runtimeClasspath.files).isNotEmpty()) { + sourceSets.add(project to sourceSet) + } + } + } + return sourceSets + } + + /** + * this function organizes sourceSets based on their combinedWith sourceSets + */ + protected fun sortProjectSourceSets(): Map, Set>> { + val minecraftConfigs = mutableMapOf, MinecraftConfig?>() + for ((project, sourceSet) in detectProjectSourceSets()) { + minecraftConfigs[project to sourceSet] = project.uniminedMaybe?.minecrafts?.map?.get(sourceSet) + } + // ensure all minecraft ones on same mappings + // get current mappings + for ((sourceSet, minecraftConfig) in minecraftConfigs.nonNullValues()) { + if (provider.mappings.devNamespace != minecraftConfig.mappings.devNamespace || + provider.mappings.devFallbackNamespace != minecraftConfig.mappings.devFallbackNamespace) { + throw IllegalArgumentException("All combined minecraft configs must be on the same mappings, found ${provider.sourceSet} on ${provider.mappings.devNamespace}/${provider.mappings.devFallbackNamespace} and ${sourceSet} on ${minecraftConfig.mappings.devNamespace}/${minecraftConfig.mappings.devFallbackNamespace}") + } + if (provider.version != minecraftConfig.version) { + throw IllegalArgumentException("All combined minecraft configs must be on the same version, found ${provider.sourceSet} on ${provider.version} and ${sourceSet} on ${minecraftConfig.version}") + } + } + val map = mutableMapOf, Set>>() + val resolveQueue = minecraftConfigs.keys.toMutableSet() + while (resolveQueue.isNotEmpty()) { + val first = resolveQueue.first() + resolveProjectDependents(minecraftConfigs, first, resolveQueue, map) + } + return map + } + + private fun resolveProjectDependents(minecraftConfigs: Map, MinecraftConfig?>, sourceSet: Pair, resolveQueue: MutableSet>, output: MutableMap, Set>>) { + resolveQueue.remove(sourceSet) + val config = minecraftConfigs[sourceSet] + val out = if (config == null) { + mutableSetOf() + } else { + val out = config.combinedWithList.intersect(minecraftConfigs.keys).toMutableSet() + for (dependent in out.toSet()) { + if (dependent in resolveQueue) { + resolveProjectDependents(minecraftConfigs, dependent, resolveQueue, output) + } + out.addAll(output[dependent] ?: listOf()) + output.remove(dependent) + } + out + } + out.add(sourceSet) + output[sourceSet] = out + } + + override fun configureRemapJar(task: RemapJarTask) {} } \ No newline at end of file diff --git a/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/fabric/BabricMinecraftTransformer.kt b/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/fabric/BabricMinecraftTransformer.kt index e381406a..9a526dc0 100644 --- a/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/fabric/BabricMinecraftTransformer.kt +++ b/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/fabric/BabricMinecraftTransformer.kt @@ -60,7 +60,7 @@ class BabricMinecraftTransformer(project: Project, provider: MinecraftProvider): override fun addMavens() { super.addMavens() - project.unimined.babricMaven() + project.unimined.glassLauncherMaven("babric") } override val includeGlobs: List diff --git a/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/fabric/FabricLikeMinecraftTransformer.kt b/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/fabric/FabricLikeMinecraftTransformer.kt index e0d9fde9..689a226f 100644 --- a/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/fabric/FabricLikeMinecraftTransformer.kt +++ b/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/fabric/FabricLikeMinecraftTransformer.kt @@ -14,8 +14,10 @@ import xyz.wagyourtail.unimined.api.runs.RunConfig import xyz.wagyourtail.unimined.api.task.ExportMappingsTask import xyz.wagyourtail.unimined.api.task.RemapJarTask import xyz.wagyourtail.unimined.api.unimined +import xyz.wagyourtail.unimined.api.uniminedMaybe import xyz.wagyourtail.unimined.internal.mapping.at.AccessTransformerMinecraftTransformer import xyz.wagyourtail.unimined.internal.mapping.aw.AccessWidenerMinecraftTransformer +import xyz.wagyourtail.unimined.internal.mapping.ii.InterfaceInjectionMinecraftTransformer import xyz.wagyourtail.unimined.internal.mapping.task.ExportMappingsTaskImpl import xyz.wagyourtail.unimined.internal.minecraft.MinecraftProvider import xyz.wagyourtail.unimined.internal.minecraft.patch.AbstractMinecraftTransformer @@ -134,10 +136,7 @@ abstract class FabricLikeMinecraftTransformer( } } - override fun apply() { - val client = provider.side == EnvType.CLIENT || provider.side == EnvType.COMBINED - val server = provider.side == EnvType.SERVER || provider.side == EnvType.COMBINED - + val fabricDep by lazy { val dependencies = fabric.dependencies if (dependencies.isEmpty()) { @@ -148,14 +147,20 @@ abstract class FabricLikeMinecraftTransformer( throw IllegalStateException("Multiple dependencies found for fabric provider") } - val dependency = dependencies.first() + dependencies.first() + } + + override fun apply() { + val client = provider.side == EnvType.CLIENT || provider.side == EnvType.COMBINED + val server = provider.side == EnvType.SERVER || provider.side == EnvType.COMBINED + var artifactString = "" - if (dependency.group != null) { - artifactString += dependency.group + ":" + if (fabricDep.group != null) { + artifactString += fabricDep.group + ":" } - artifactString += dependency.name - if (dependency.version != null) { - artifactString += ":" + dependency.version + artifactString += fabricDep.name + if (fabricDep.version != null) { + artifactString += ":" + fabricDep.version } artifactString += "@json" @@ -190,6 +195,9 @@ abstract class FabricLikeMinecraftTransformer( createFabricLoaderDependency(it) } } + libraries.get("development")?.asJsonArray?.forEach { + createFabricLoaderDependency(it) + } } mainClass = json.get("mainClass")?.asJsonObject @@ -200,6 +208,13 @@ abstract class FabricLikeMinecraftTransformer( ) } + // mixins get remapped at runtime, so we don't need to on fabric + provider.mods.default { + mixinRemap { + off() + } + } + super.apply() } @@ -211,7 +226,9 @@ abstract class FabricLikeMinecraftTransformer( provider.minecraftLibraries.dependencies.add(dep) } - override fun afterRemap(baseMinecraft: MinecraftJar): MinecraftJar = + override fun afterRemap(baseMinecraft: MinecraftJar): MinecraftJar = applyInterfaceInjection(applyAccessWideners(baseMinecraft)) + + private fun applyAccessWideners(baseMinecraft: MinecraftJar): MinecraftJar = if (accessWidener != null) { val output = MinecraftJar( baseMinecraft, @@ -237,6 +254,71 @@ abstract class FabricLikeMinecraftTransformer( } } else baseMinecraft + private fun applyInterfaceInjection(baseMinecraft: MinecraftJar): MinecraftJar { + val injections = hashMapOf>() + + this.collectInterfaceInjections(baseMinecraft, injections) + + return if (injections.isNotEmpty()) { + val oldSuffix = if (baseMinecraft.awOrAt != null) baseMinecraft.awOrAt + "+" else "" + + val output = MinecraftJar( + baseMinecraft, + parentPath = provider.localCache.resolve("fabric").createDirectories(), + awOrAt = "${oldSuffix}ii+${injections.getShortSha1()}" + ) + + if (!output.path.exists() || project.unimined.forceReload) { + if (InterfaceInjectionMinecraftTransformer.transform( + injections, + baseMinecraft.path, + output.path, + project.logger + ) + ) { + output + } else baseMinecraft + } else output + } else baseMinecraft + } + + abstract fun collectInterfaceInjections(baseMinecraft: MinecraftJar, injections: HashMap>) + fun collectInterfaceInjections(baseMinecraft: MinecraftJar, injections: HashMap>, interfaces: JsonObject) { + injections.putAll(interfaces.entrySet() + .filterNotNull() + .filter { it.key != null && it.value != null && it.value.isJsonArray } + .map { + val element = it.value!! + + Pair(it.key!!, if (element.isJsonArray) { + element.asJsonArray.mapNotNull { name -> name.asString } + } else arrayListOf()) + } + .map { + var target = it.first + + val clazz = provider.mappings.mappingTree.getClass( + target, + provider.mappings.mappingTree.getNamespaceId(prodNamespace.name) + ) + + if (clazz != null) { + var newTarget = clazz.getName(provider.mappings.mappingTree.getNamespaceId(baseMinecraft.mappingNamespace.name)) + + if (newTarget == null) { + newTarget = clazz.getName(provider.mappings.mappingTree.getNamespaceId(baseMinecraft.fallbackNamespace.name)) + } + + if (newTarget != null) { + target = newTarget + } + } + + Pair(target, it.second) + } + ) + } + val intermediaryClasspath: Path = provider.localCache.resolve("remapClasspath.txt".withSourceSet(provider.sourceSet)) override fun afterEvaluate() { @@ -293,6 +375,7 @@ abstract class FabricLikeMinecraftTransformer( innerjson.addProperty("name", dep.name) val custom = JsonObject() custom.addProperty("fabric-loom:generated", true) + custom.addProperty("unimined:generated", true) innerjson.add("custom", custom) Files.write( innermod, @@ -392,6 +475,21 @@ abstract class FabricLikeMinecraftTransformer( ).toFile() } + val groups: String by lazy { + val groups = sortProjectSourceSets().mapValues { it.value.toMutableSet() }.toMutableMap() + // detect non-fabric groups + for ((proj, sourceSet) in groups.keys.toSet()) { + if (proj.uniminedMaybe?.minecrafts?.map?.get(sourceSet)?.mcPatcher !is FabricLikePatcher) { + // merge with current + proj.logger.warn("[Unimined/FabricLike] Non-fabric ${(proj to sourceSet).toPath()} found in fabric classpath groups, merging with current (${(project to provider.sourceSet).toPath()}), this should've been manually specified with `combineWith`") + groups[this.project to this.provider.sourceSet]!! += groups[proj to sourceSet]!! + groups.remove(proj to sourceSet) + } + } + project.logger.info("[Unimined/FabricLike] Classpath groups: ${groups.map { it.key.toPath() + " -> " + it.value.joinToString(", ") { it.toPath() } }.joinToString("\n ")}") + groups.map { entry -> entry.value.flatMap { it.second.output }.joinToString(File.pathSeparator) { it.absolutePath } }.joinToString(File.pathSeparator.repeat(2)) + } + override fun applyClientRunTransform(config: RunConfig) { config.mainClass = mainClass?.get("client")?.asString ?: config.mainClass } @@ -404,4 +502,13 @@ abstract class FabricLikeMinecraftTransformer( // fabric provides its own asm, exclude asm-all from vanilla minecraftLibraries return !library.name.startsWith("org.ow2.asm:asm-all") } + + fun getModJsonPath(): File? { + val json = provider.sourceSet.resources.firstOrNull { it.name.equals(modJsonName) } + if (json == null) { + project.logger.warn("[Unimined/FabricLike] $modJsonName not found in sourceSet ${provider.project.path} ${provider.sourceSet.name}") + return null + } + return json + } } \ No newline at end of file diff --git a/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/fabric/FabricMinecraftTransformer.kt b/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/fabric/FabricMinecraftTransformer.kt index e96e25fc..141b1fd6 100644 --- a/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/fabric/FabricMinecraftTransformer.kt +++ b/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/fabric/FabricMinecraftTransformer.kt @@ -2,12 +2,20 @@ package xyz.wagyourtail.unimined.internal.minecraft.patch.fabric import com.google.gson.JsonArray import com.google.gson.JsonObject +import com.google.gson.JsonParser import org.gradle.api.Project import org.gradle.api.artifacts.Dependency import xyz.wagyourtail.unimined.api.minecraft.EnvType import xyz.wagyourtail.unimined.api.runs.RunConfig import xyz.wagyourtail.unimined.api.unimined +import xyz.wagyourtail.unimined.internal.mapping.ii.InterfaceInjectionMinecraftTransformer import xyz.wagyourtail.unimined.internal.minecraft.MinecraftProvider +import xyz.wagyourtail.unimined.internal.minecraft.patch.MinecraftJar +import xyz.wagyourtail.unimined.util.getShortSha1 +import java.io.InputStreamReader +import java.nio.file.Files +import kotlin.io.path.createDirectories +import kotlin.io.path.exists abstract class FabricMinecraftTransformer( project: Project, @@ -49,7 +57,8 @@ abstract class FabricMinecraftTransformer( super.applyClientRunTransform(config) config.jvmArgs += listOf( "-Dfabric.development=true", - "-Dfabric.remapClasspathFile=${intermediaryClasspath}" + "-Dfabric.remapClasspathFile=${intermediaryClasspath}", + "-Dfabric.classPathGroups=${groups}" ) } @@ -57,7 +66,26 @@ abstract class FabricMinecraftTransformer( super.applyServerRunTransform(config) config.jvmArgs += listOf( "-Dfabric.development=true", - "-Dfabric.remapClasspathFile=${intermediaryClasspath}" + "-Dfabric.remapClasspathFile=${intermediaryClasspath}", + "-Dfabric.classPathGroups=${groups}" ) } + + override fun collectInterfaceInjections(baseMinecraft: MinecraftJar, injections: HashMap>) { + val modJsonPath = this.getModJsonPath() + + if (modJsonPath != null && modJsonPath.exists()) { + val json = JsonParser.parseReader(InputStreamReader(Files.newInputStream(modJsonPath.toPath()))).asJsonObject + + val custom = json.getAsJsonObject("custom") + + if (custom != null) { + val interfaces = custom.getAsJsonObject("loom:injected_interfaces") + + if (interfaces != null) { + collectInterfaceInjections(baseMinecraft, injections, interfaces) + } + } + } + } } \ No newline at end of file diff --git a/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/fabric/OfficialFabricMinecraftTransformer.kt b/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/fabric/OfficialFabricMinecraftTransformer.kt index 286fa621..d0e428a7 100644 --- a/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/fabric/OfficialFabricMinecraftTransformer.kt +++ b/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/fabric/OfficialFabricMinecraftTransformer.kt @@ -2,7 +2,9 @@ package xyz.wagyourtail.unimined.internal.minecraft.patch.fabric import org.gradle.api.Project import org.gradle.api.artifacts.Dependency +import xyz.wagyourtail.unimined.api.task.RemapJarTask import xyz.wagyourtail.unimined.internal.minecraft.MinecraftProvider +import xyz.wagyourtail.unimined.util.SemVerUtils class OfficialFabricMinecraftTransformer( project: Project, @@ -22,4 +24,13 @@ class OfficialFabricMinecraftTransformer( } else project.dependencies.create(dep)).apply(action) ) } + + override fun configureRemapJar(task: RemapJarTask) { + if (fabricDep.version?.let { SemVerUtils.matches(it, ">=0.15.0") } == true) { + project.logger.info("enabling mixin extra") + task.mixinRemap { + enableMixinExtra() + } + } + } } \ No newline at end of file diff --git a/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/fabric/QuiltMinecraftTransformer.kt b/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/fabric/QuiltMinecraftTransformer.kt index 809b7e9d..3cc68770 100644 --- a/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/fabric/QuiltMinecraftTransformer.kt +++ b/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/fabric/QuiltMinecraftTransformer.kt @@ -2,12 +2,16 @@ package xyz.wagyourtail.unimined.internal.minecraft.patch.fabric import com.google.gson.JsonArray import com.google.gson.JsonObject +import com.google.gson.JsonParser import org.gradle.api.Project import org.gradle.api.artifacts.Dependency import xyz.wagyourtail.unimined.api.minecraft.EnvType import xyz.wagyourtail.unimined.api.runs.RunConfig import xyz.wagyourtail.unimined.api.unimined import xyz.wagyourtail.unimined.internal.minecraft.MinecraftProvider +import xyz.wagyourtail.unimined.internal.minecraft.patch.MinecraftJar +import java.io.InputStreamReader +import java.nio.file.Files class QuiltMinecraftTransformer( project: Project, @@ -29,6 +33,34 @@ class QuiltMinecraftTransformer( } } + override fun collectInterfaceInjections(baseMinecraft: MinecraftJar, injections: HashMap>) { + val modJsonPath = this.getModJsonPath() + + if (modJsonPath != null && modJsonPath.exists()) { + val json = JsonParser.parseReader(InputStreamReader(Files.newInputStream(modJsonPath.toPath()))).asJsonObject + + val custom = json.getAsJsonObject("custom") + + if (custom != null) { + val quiltLoom = custom.getAsJsonObject("quilt_loom") + + if (quiltLoom != null) { + val interfaces = quiltLoom.getAsJsonObject("injected_interfaces") + + if (interfaces != null) collectInterfaceInjections(baseMinecraft, injections, interfaces) + } + } + + val quiltLoom = json.getAsJsonObject("quilt_loom") + + if (quiltLoom != null) { + val interfaces = quiltLoom.getAsJsonObject("injected_interfaces") + + if (interfaces != null) collectInterfaceInjections(baseMinecraft, injections, interfaces) + } + } + } + override fun loader(dep: Any, action: Dependency.() -> Unit) { fabric.dependencies.add( (if (dep is String && !dep.contains(":")) { @@ -64,7 +96,8 @@ class QuiltMinecraftTransformer( super.applyClientRunTransform(config) config.jvmArgs += listOf( "-Dloader.development=true", - "-Dloader.remapClasspathFile=${intermediaryClasspath}" + "-Dloader.remapClasspathFile=${intermediaryClasspath}", + "-Dloader.classPathGroups=${groups}" ) } @@ -72,7 +105,8 @@ class QuiltMinecraftTransformer( super.applyServerRunTransform(config) config.jvmArgs += listOf( "-Dloader.development=true", - "-Dloader.remapClasspathFile=${intermediaryClasspath}" + "-Dloader.remapClasspathFile=${intermediaryClasspath}", + "-Dloader.classPathGroups=${groups}" ) } diff --git a/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/forge/ForgeLikeMinecraftTransformer.kt b/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/forge/ForgeLikeMinecraftTransformer.kt index aea35b82..5b88a828 100644 --- a/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/forge/ForgeLikeMinecraftTransformer.kt +++ b/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/forge/ForgeLikeMinecraftTransformer.kt @@ -11,11 +11,13 @@ import org.jetbrains.annotations.ApiStatus import org.objectweb.asm.AnnotationVisitor import xyz.wagyourtail.unimined.api.mapping.MappingNamespaceTree import xyz.wagyourtail.unimined.api.minecraft.EnvType +import xyz.wagyourtail.unimined.api.minecraft.patch.FabricLikePatcher import xyz.wagyourtail.unimined.api.minecraft.patch.ForgeLikePatcher import xyz.wagyourtail.unimined.api.runs.RunConfig import xyz.wagyourtail.unimined.api.task.ExportMappingsTask import xyz.wagyourtail.unimined.api.task.RemapJarTask import xyz.wagyourtail.unimined.api.unimined +import xyz.wagyourtail.unimined.api.uniminedMaybe import xyz.wagyourtail.unimined.internal.mapping.at.AccessTransformerMinecraftTransformer import xyz.wagyourtail.unimined.internal.mapping.task.ExportMappingsTaskImpl import xyz.wagyourtail.unimined.internal.minecraft.MinecraftProvider @@ -32,12 +34,11 @@ import kotlin.io.path.createDirectories import kotlin.io.path.exists abstract class ForgeLikeMinecraftTransformer(project: Project, provider: MinecraftProvider, providerName: String,): - AbstractMinecraftTransformer(project, provider, providerName), ForgeLikePatcher { + AbstractMinecraftTransformer(project, provider, providerName), ForgeLikePatcher { - val forge: Configuration = project.configurations.maybeCreate("forge".withSourceSet(provider.sourceSet)) - - @get:ApiStatus.Internal - abstract var forgeTransformer: JarModMinecraftTransformer + val forge: Configuration = project.configurations.maybeCreate("forge".withSourceSet(provider.sourceSet)).apply { + isTransitive = false + } override var accessTransformer: File? = null @@ -271,7 +272,15 @@ abstract class ForgeLikeMinecraftTransformer(project: Project, provider: Minecra } } + if (mixinConfig.isNotEmpty()) { + val task = project.tasks.findByName("jar".withSourceSet(provider.sourceSet)) + if (task != null && task is Jar) { + task.manifest.attributes["MixinConfigs"] = mixinConfig.joinToString(",") + } + } + forgeTransformer.apply() + super.apply() } @@ -354,6 +363,7 @@ abstract class ForgeLikeMinecraftTransformer(project: Project, provider: Minecra ) if (!output.path.exists() || project.unimined.forceReload) { AccessTransformerMinecraftTransformer.transform( + project, ats + listOf(accessTransformer!!.toPath()), baseMinecraft.path, output.path @@ -366,12 +376,27 @@ abstract class ForgeLikeMinecraftTransformer(project: Project, provider: Minecra } val output = MinecraftJar(baseMinecraft, awOrAt = "at") if (!output.path.exists() || project.unimined.forceReload) { - AccessTransformerMinecraftTransformer.transform(ats, baseMinecraft.path, output.path) + AccessTransformerMinecraftTransformer.transform(project, ats, baseMinecraft.path, output.path) } output } } + val groups: String by lazy { + val groups = sortProjectSourceSets().mapValues { it.value.toMutableSet() }.toMutableMap() + // detect non-fabric groups + for ((proj, sourceSet) in groups.keys.toSet()) { + if (proj.uniminedMaybe?.minecrafts?.map?.get(sourceSet)?.mcPatcher !is ForgeLikePatcher<*>) { + // merge with current + proj.logger.warn("[Unimined/ForgeLike] Non-forge ${(proj to sourceSet).toPath()} found in fabric classpath groups, merging with current (${(project to provider.sourceSet).toPath()}), this should've been manually specified with `combineWith`") + groups[this.project to this.provider.sourceSet]!! += groups[proj to sourceSet]!! + groups.remove(proj to sourceSet) + } + } + project.logger.info("[Unimined/FabricLike] Classpath groups: ${groups.map { it.key.toPath() + " -> " + it.value.joinToString(", ") { it.toPath() } }.joinToString("\n ")}") + groups.map { entry -> entry.value.flatMap { listOf(it.second.output.resourcesDir) + it.second.output.classesDirs }.joinToString(File.pathSeparator) { "${entry.key.toPath().replace(":", "_")}%%${it!!.absolutePath}" } }.joinToString(File.pathSeparator) + } + override fun applyClientRunTransform(config: RunConfig) { project.logger.info("[Unimined/ForgeTransformer] Adding mixin config $mixinConfig to client run config") forgeTransformer.applyClientRunTransform(config) diff --git a/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/forge/MinecraftForgeMinecraftTransformer.kt b/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/forge/MinecraftForgeMinecraftTransformer.kt index 1d90337f..64195cf3 100644 --- a/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/forge/MinecraftForgeMinecraftTransformer.kt +++ b/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/forge/MinecraftForgeMinecraftTransformer.kt @@ -17,12 +17,13 @@ import xyz.wagyourtail.unimined.util.MustSet import xyz.wagyourtail.unimined.util.forEachInZip import java.io.File -class MinecraftForgeMinecraftTransformer(project: Project, provider: MinecraftProvider) : ForgeLikeMinecraftTransformer(project, provider, "MinecraftForge"), MinecraftForgePatcher { +class MinecraftForgeMinecraftTransformer(project: Project, provider: MinecraftProvider) : ForgeLikeMinecraftTransformer(project, provider, "MinecraftForge"), MinecraftForgePatcher { override var forgeTransformer: JarModMinecraftTransformer by FinalizeOnWrite(MustSet()) override fun addMavens() { project.unimined.minecraftForgeMaven() + project.unimined.neoForgedMaven() } override fun loader(dep: Any, action: Dependency.() -> Unit) { diff --git a/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/forge/NeoForgedMinecraftTransformer.kt b/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/forge/NeoForgedMinecraftTransformer.kt index 814fac22..14cef479 100644 --- a/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/forge/NeoForgedMinecraftTransformer.kt +++ b/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/forge/NeoForgedMinecraftTransformer.kt @@ -4,6 +4,7 @@ import com.google.gson.JsonObject import org.gradle.api.Project import org.gradle.api.artifacts.Dependency import xyz.wagyourtail.unimined.api.minecraft.patch.NeoForgedPatcher +import xyz.wagyourtail.unimined.api.task.RemapJarTask import xyz.wagyourtail.unimined.api.unimined import xyz.wagyourtail.unimined.internal.minecraft.MinecraftProvider import xyz.wagyourtail.unimined.internal.minecraft.patch.forge.fg3.FG3MinecraftTransformer @@ -11,8 +12,9 @@ import xyz.wagyourtail.unimined.internal.minecraft.patch.jarmod.JarModMinecraftT import xyz.wagyourtail.unimined.internal.minecraft.resolver.parseAllLibraries import xyz.wagyourtail.unimined.util.FinalizeOnWrite import xyz.wagyourtail.unimined.util.MustSet +import xyz.wagyourtail.unimined.util.SemVerUtils -class NeoForgedMinecraftTransformer(project: Project, provider: MinecraftProvider) : ForgeLikeMinecraftTransformer(project, provider, "NeoForged"), NeoForgedPatcher { +class NeoForgedMinecraftTransformer(project: Project, provider: MinecraftProvider) : ForgeLikeMinecraftTransformer(project, provider, "NeoForged"), NeoForgedPatcher { override var forgeTransformer: JarModMinecraftTransformer by FinalizeOnWrite(MustSet()) @@ -22,7 +24,11 @@ class NeoForgedMinecraftTransformer(project: Project, provider: MinecraftProvide override fun loader(dep: Any, action: Dependency.() -> Unit) { forge.dependencies.add(if (dep is String && !dep.contains(":")) { - project.dependencies.create("net.neoforged:forge:${provider.version}-$dep:universal") + if (provider.version == "1.20.1") { + project.dependencies.create("net.neoforged:forge:${provider.version}-$dep:universal") + } else { + project.dependencies.create("net.neoforged:neoforge:${provider.version.removePrefix("1.")}.$dep:universal") + } } else { project.dependencies.create(dep) }.apply(action)) @@ -37,7 +43,7 @@ class NeoForgedMinecraftTransformer(project: Project, provider: MinecraftProvide val forgeDep = forge.dependencies.first() - if (forgeDep.group != "net.neoforged" || forgeDep.name != "forge") { + if (forgeDep.group != "net.neoforged" || (forgeDep.name != "forge" && forgeDep.name != "neoforge")) { throw IllegalStateException("Invalid forge dependency found, if you are using multiple dependencies in the forge configuration, make sure the last one is the forge dependency!") } @@ -49,8 +55,21 @@ class NeoForgedMinecraftTransformer(project: Project, provider: MinecraftProvide val libraries = parseAllLibraries(json.getAsJsonArray("libraries")) mainClass = json.get("mainClass").asString val args = json.get("minecraftArguments").asString - provider.addLibraries(libraries.filter { !it.name.startsWith("net.neoforged:forge:") }) + provider.addLibraries(libraries.filter { !it.name.startsWith("net.neoforged:forge:") || !it.name.startsWith("net.neoforged:neoforge:") }) tweakClassClient = args.split("--tweakClass")[1].trim() } + override fun configureRemapJar(task: RemapJarTask) { + val forgeDep = forge.dependencies.first() + if (provider.version != "1.20.1") { + project.logger.info("setting `disableRefmap()` in mixinRemap") + task.mixinRemap { + if (SemVerUtils.matches(forgeDep.version!!.substringBefore("-"), ">=20.2.84")) { + enableMixinExtra() + } + disableRefmap() + } + } + } + } diff --git a/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/forge/fg1/FG1MinecraftTransformer.kt b/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/forge/fg1/FG1MinecraftTransformer.kt index a7fc16f7..73879176 100644 --- a/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/forge/fg1/FG1MinecraftTransformer.kt +++ b/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/forge/fg1/FG1MinecraftTransformer.kt @@ -203,6 +203,10 @@ class FG1MinecraftTransformer(project: Project, val parent: ForgeLikeMinecraftTr val out = fixForge(baseMinecraft) return out.path.openZipFileSystem().use { fs -> val ats = listOf(fs.getPath("forge_at.cfg"), fs.getPath("fml_at.cfg")).filter { Files.exists(it) } + // make sure remap at's to modern + for (at in ats) { + AccessTransformerMinecraftTransformer.toModern(at) + } // apply at's parent.applyATs( out, diff --git a/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/forge/fg3/FG3MinecraftTransformer.kt b/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/forge/fg3/FG3MinecraftTransformer.kt index 15dba3fe..2baa92f1 100644 --- a/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/forge/fg3/FG3MinecraftTransformer.kt +++ b/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/forge/fg3/FG3MinecraftTransformer.kt @@ -13,6 +13,7 @@ import xyz.wagyourtail.unimined.internal.minecraft.patch.MinecraftJar import xyz.wagyourtail.unimined.api.runs.RunConfig import xyz.wagyourtail.unimined.api.unimined import xyz.wagyourtail.unimined.internal.minecraft.patch.forge.ForgeLikeMinecraftTransformer +import xyz.wagyourtail.unimined.internal.minecraft.patch.forge.NeoForgedMinecraftTransformer import xyz.wagyourtail.unimined.internal.minecraft.patch.forge.fg3.mcpconfig.McpConfigData import xyz.wagyourtail.unimined.internal.minecraft.patch.forge.fg3.mcpconfig.McpConfigStep import xyz.wagyourtail.unimined.internal.minecraft.patch.forge.fg3.mcpconfig.McpExecutor @@ -36,14 +37,23 @@ class FG3MinecraftTransformer(project: Project, val parent: ForgeLikeMinecraftTr init { project.logger.lifecycle("[Unimined/Forge] Using FG3 transformer") parent.provider.minecraftRemapper.addResourceRemapper { JsCoreModRemapper(project.logger) } + val forgeHardcodedNames = setOf("net/minecraftforge/registries/ObjectHolderRegistry", "net/neoforged/neoforge/registries/ObjectHolderRegistry") parent.provider.minecraftRemapper.addExtension { StringClassNameRemapExtension(project.gradle.startParameter.logLevel) { // it.matches(Regex("^net/minecraftforge/.*")) - it == "net/minecraftforge/registries/ObjectHolderRegistry" + forgeHardcodedNames.contains(it) } } unprotectRuntime = true } - override val prodNamespace by lazy { provider.mappings.getNamespace("searge") } + override val prodNamespace by lazy { + if (userdevCfg["mcp"].asString.contains("neoform")) { + provider.mappings.getNamespace("mojmap") + } else { + provider.mappings.getNamespace("searge") + } + } + + var binpatchFile: Path? = null override val merger: ClassMerger get() = throw UnsupportedOperationException("FG3+ does not support merging with unofficial merger.") @@ -60,6 +70,12 @@ class FG3MinecraftTransformer(project: Project, val parent: ForgeLikeMinecraftTr ) } + val obfNamespace by lazy { + if (userdevCfg["notchObf"]?.asBoolean == true) "official" + else if (userdevCfg["mcp"].asString.contains("neoform")) "mojmap" + else "searge" + } + val mcpConfigData by lazy { val configuration = project.configurations.detachedConfiguration() configuration.dependencies.add(mcpConfig) @@ -99,14 +115,18 @@ class FG3MinecraftTransformer(project: Project, val parent: ForgeLikeMinecraftTr override fun beforeMappingsResolve() { project.logger.info("[Unimined/ForgeTransformer] FG3: beforeMappingsResolve") provider.mappings { - val mcpConfigUserSpecified = mappingsDeps.entries.firstOrNull { it.value.dep.group == "de.oceanlabs.mcp" && it.value.dep.name == "mcp_config" } - if (mcpConfigUserSpecified != null && !parent.customSearge) { - if (mcpConfigUserSpecified.value.dep.version != mcpConfig.version) { - project.logger.warn("[Unimined/ForgeTransformer] FG3 does not support custom mcp_config (searge) version specification. Using ${mcpConfig.version} from userdev.") + if (obfNamespace == "mojmap") { + mojmap() + } else { + val mcpConfigUserSpecified = mappingsDeps.entries.firstOrNull { it.value.dep.group == "de.oceanlabs.mcp" && it.value.dep.name == "mcp_config" } + if (mcpConfigUserSpecified != null && !parent.customSearge) { + if (mcpConfigUserSpecified.value.dep.version != mcpConfig.version) { + project.logger.warn("[Unimined/ForgeTransformer] FG3 does not support custom mcp_config (searge) version specification. Using ${mcpConfig.version} from userdev.") + } + mappingsDeps.remove(mcpConfigUserSpecified.key) } - mappingsDeps.remove(mcpConfigUserSpecified.key) + if (!parent.customSearge) searge(mcpConfig.version!!) } - if (!parent.customSearge) searge(mcpConfig.version!!) } } @@ -142,9 +162,11 @@ class FG3MinecraftTransformer(project: Project, val parent: ForgeLikeMinecraftTr val mainClass = get("main").asString if (!mainClass.startsWith("net.minecraftforge.legacydev")) { project.logger.info("[Unimined/ForgeTransformer] Inserting mcp mappings") - provider.minecraftLibraries.dependencies.add( - project.dependencies.create(project.files(parent.srgToMCPAsMCP)) - ) + if (obfNamespace != "mojmap") { + provider.minecraftLibraries.dependencies.add( + project.dependencies.create(project.files(parent.srgToMCPAsMCP)) + ) + } } } @@ -184,7 +206,7 @@ class FG3MinecraftTransformer(project: Project, val parent: ForgeLikeMinecraftTr parentPath = provider.minecraftData.mcVersionFolder .resolve(providerName), envType = EnvType.COMBINED, - mappingNamespace = if (userdevCfg["notchObf"]?.asBoolean == true) provider.mappings.OFFICIAL else provider.mappings.getNamespace("searge"), + mappingNamespace = provider.mappings.getNamespace(obfNamespace), fallbackNamespace = provider.mappings.OFFICIAL ) createClientExtra(clientjar, serverjar, output.path) @@ -246,7 +268,7 @@ class FG3MinecraftTransformer(project: Project, val parent: ForgeLikeMinecraftTr val forgeUniversal = parent.forge.dependencies.last() - val outFolder = minecraft.path.parent.resolve(providerName).resolve(forgeUniversal.version).createDirectories() + val outFolder = minecraft.path.parent.resolve(providerName).resolve(forgeUniversal.version!!).createDirectories() val inputMC = if (minecraft.envType != EnvType.COMBINED) { // if userdev cfg says notch @@ -259,7 +281,7 @@ class FG3MinecraftTransformer(project: Project, val parent: ForgeLikeMinecraftTr minecraft, parentPath = provider.minecraftData.mcVersionFolder .resolve(providerName), - mappingNamespace = provider.mappings.getNamespace("searge"), + mappingNamespace = if (obfNamespace == "mojmap") provider.mappings.OFFICIAL else provider.mappings.getNamespace("searge"), fallbackNamespace = provider.mappings.OFFICIAL, patches = minecraft.patches + "mcp_config" ) @@ -276,13 +298,14 @@ class FG3MinecraftTransformer(project: Project, val parent: ForgeLikeMinecraftTr val patchedMC = MinecraftJar( inputMC, - name = "forge", + name = if (parent is NeoForgedMinecraftTransformer) "neoforge" else "forge", + version = forgeUniversal.version!!, parentPath = outFolder ) // extract binpatches - val binPatchFile = if (patchedMC.envType == EnvType.COMBINED) { + val binPatchFile = this.binpatchFile ?: if (patchedMC.envType == EnvType.COMBINED) { forgeUd.toPath().readZipInputStreamFor(userdevCfg["binpatches"].asString) { outFolder.resolve("binpatches-joined.lzma").apply { writeBytes( @@ -353,7 +376,7 @@ class FG3MinecraftTransformer(project: Project, val parent: ForgeLikeMinecraftTr val libs = mapOf(*provider.minecraftLibraries.dependencies.map { it.group + ":" + it.name + ":" + it.version to it } .toTypedArray()) userdevCfg.get("modules").asJsonArray.joinToString(File.pathSeparator) { - val dep = libs[it.asString] + val dep = libs[it.asString.removeSuffix("@jar")] ?: throw IllegalStateException("Module ${it.asString} not found in mc libraries") provider.minecraftLibraries.getFile(dep).toString() } @@ -367,16 +390,7 @@ class FG3MinecraftTransformer(project: Project, val parent: ForgeLikeMinecraftTr } "{asset_index}" -> provider.minecraftData.metadata.assetIndex?.id ?: "" - "{source_roots}" -> { - (detectProjectSourceSets().flatMap { - listOf( - it.output.resourcesDir - ) + it.output.classesDirs - }).joinToString( - File.pathSeparator - ) { "mod%%$it" } - } - + "{source_roots}" -> parent.groups "{mcp_mappings}" -> "unimined.stub" "{natives}" -> { val nativesDir = config.workingDir.resolve("natives").toPath() @@ -430,6 +444,9 @@ class FG3MinecraftTransformer(project: Project, val parent: ForgeLikeMinecraftTr config.jvmArgs += props.map { "-D${it.key}=${getArgValue(config, it.value)}" } config.env += mapOf("FORGE_SPEC" to userdevCfg.get("spec").asNumber.toString()) config.env += env.map { it.key to getArgValue(config, it.value) } + config.env.computeIfAbsent("MOD_CLASSES") { + getArgValue(config, "{source_roots}") + } } } @@ -462,6 +479,9 @@ class FG3MinecraftTransformer(project: Project, val parent: ForgeLikeMinecraftTr config.jvmArgs += props.map { "-D${it.key}=${getArgValue(config, it.value)}" } config.env += mapOf("FORGE_SPEC" to userdevCfg.get("spec").asNumber.toString()) config.env += env.map { it.key to getArgValue(config, it.value) } + config.env.computeIfAbsent("MOD_CLASSES") { + getArgValue(config, "{source_roots}") + } } } } diff --git a/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/forge/fg3/StringClassNameRemapExtension.kt b/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/forge/fg3/StringClassNameRemapExtension.kt index 00ebb792..6387c281 100644 --- a/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/forge/fg3/StringClassNameRemapExtension.kt +++ b/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/forge/fg3/StringClassNameRemapExtension.kt @@ -7,14 +7,14 @@ import net.fabricmc.tinyremapper.extension.mixin.common.data.Constant import org.gradle.api.logging.LogLevel import org.objectweb.asm.ClassVisitor import org.objectweb.asm.MethodVisitor -import xyz.wagyourtail.unimined.internal.mapping.mixin.refmap.BetterMixinExtension +import xyz.wagyourtail.unimined.internal.mapping.extension.MixinRemapExtension class StringClassNameRemapExtension( loggerLevel: LogLevel = LogLevel.WARN, val classFilter: (String) -> Boolean = { true } ) : TinyRemapper.Extension, TinyRemapper.ApplyVisitorProvider { - private val logger: Logger = Logger(BetterMixinExtension.translateLogLevel(loggerLevel)) + private val logger: Logger = Logger(MixinRemapExtension.translateLogLevel(loggerLevel)) override fun attach(builder: TinyRemapper.Builder) { builder.extraPreApplyVisitor(this) } diff --git a/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/forge/fg3/mcpconfig/McpConfigFunction.kt b/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/forge/fg3/mcpconfig/McpConfigFunction.kt index 9949c596..bae420bb 100644 --- a/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/forge/fg3/mcpconfig/McpConfigFunction.kt +++ b/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/forge/fg3/mcpconfig/McpConfigFunction.kt @@ -19,7 +19,7 @@ data class McpConfigFunction( val version = json[VERSION_KEY].asString val args = if (json.has(ARGS_KEY)) configValuesFromJson(json.getAsJsonArray(ARGS_KEY)) else listOf() val jvmArgs = if (json.has(JVM_ARGS_KEY)) configValuesFromJson(json.getAsJsonArray(JVM_ARGS_KEY)) else listOf() - val repo = json[REPO_KEY].asString + val repo = if (json[REPO_KEY].isJsonNull) "https://maven.neoforged.net/releases/" else json[REPO_KEY].asString return McpConfigFunction(version, args, jvmArgs, repo) } diff --git a/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/jarmod/JarModAgentMinecraftTransformer.kt b/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/jarmod/JarModAgentMinecraftTransformer.kt index 3296a696..15667344 100644 --- a/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/jarmod/JarModAgentMinecraftTransformer.kt +++ b/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/jarmod/JarModAgentMinecraftTransformer.kt @@ -32,6 +32,14 @@ open class JarModAgentMinecraftTransformer( private const val JMA_DEBUG = "jma.debug" } + init { + provider.mods.modImplementation { + mixinRemap { + enableJarModAgent() + } + } + } + @Deprecated("may violate mojang's EULA... use at your own risk. this is not recommended and is only here for legacy reasons and testing.") override var compiletimeTransforms: Boolean = false @@ -53,12 +61,24 @@ open class JarModAgentMinecraftTransformer( this.transforms.addAll(transforms) } + override fun agentVersion(vers: String) { + project.unimined.wagYourMaven("releases") + project.unimined.wagYourMaven("snapshots") + jarModAgent.dependencies.add( + project.dependencies.create( + "xyz.wagyourtail.unimined:jarmod-agent:$vers:all" + ).also { + (it as ExternalDependency).isTransitive = false + } + ) + } + override fun apply() { if (jarModAgent.dependencies.isEmpty()) { project.unimined.wagYourMaven("snapshots") jarModAgent.dependencies.add( project.dependencies.create( - "xyz.wagyourtail.unimined:jarmod-agent:0.1.3-SNAPSHOT:all" + "xyz.wagyourtail.unimined:jarmod-agent:0.1.4-SNAPSHOT:all" ).also { (it as ExternalDependency).isTransitive = false } @@ -83,7 +103,7 @@ open class JarModAgentMinecraftTransformer( config.jvmArgs.add("-D${JMA_TRANSFORMERS}=${transforms.joinToString(File.pathSeparator)}") } // priority classpath - val priorityClasspath = detectProjectSourceSets().map { it.output.classesDirs.toMutableSet().also {set-> it.output.resourcesDir.let { set.add(it) } } }.flatten() + val priorityClasspath = detectProjectSourceSets().map { it.second.output.classesDirs.toMutableSet().also {set-> it.second.output.resourcesDir.let { set.add(it) } } }.flatten() if (priorityClasspath.isNotEmpty()) { config.jvmArgs.add("-D${JMA_PRIORITY_CLASSPATH}=${priorityClasspath.joinToString(File.pathSeparator) { it.absolutePath }}") } @@ -92,6 +112,9 @@ open class JarModAgentMinecraftTransformer( } override fun beforeRemapJarTask(remapJarTask: RemapJarTask, input: Path): Path { + remapJarTask.mixinRemap { + enableJarModAgent() + } @Suppress("DEPRECATION") return if (compiletimeTransforms && transforms.isNotEmpty()) { project.logger.lifecycle("[Unimined/JarModAgentTransformer] Running compile time transforms for ${remapJarTask.name}...") diff --git a/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/jarmod/JarModMinecraftTransformer.kt b/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/jarmod/JarModMinecraftTransformer.kt index 4661c438..dfa4accb 100644 --- a/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/jarmod/JarModMinecraftTransformer.kt +++ b/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/jarmod/JarModMinecraftTransformer.kt @@ -32,7 +32,11 @@ open class JarModMinecraftTransformer( override var deleteMetaInf: Boolean = false - val jarModConfiguration = project.configurations.maybeCreate(jarModProvider.withSourceSet(provider.sourceSet)) + val jarModConfiguration = project.configurations.maybeCreate(jarModProvider.withSourceSet(provider.sourceSet)).apply { + if (isTransitive) { + isTransitive = false + } + } override val transform = (listOf<(FileSystem) -> Unit>( ModLoaderPatches::fixURIisNotHierarchicalException, @@ -99,18 +103,4 @@ open class JarModMinecraftTransformer( target }) } - - protected fun detectProjectSourceSets(): Set { - val sourceSets = mutableSetOf() - val projects = project.rootProject.allprojects - for (project in projects) { - for (sourceSet in project.extensions.findByType(SourceSetContainer::class.java)?.asMap?.values - ?: listOf()) { - if (sourceSet.output.files.intersect(provider.sourceSet.runtimeClasspath.files).isNotEmpty()) { - sourceSets.add(sourceSet) - } - } - } - return sourceSets - } } \ No newline at end of file diff --git a/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/merged/MergedMinecraftTransformer.kt b/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/merged/MergedMinecraftTransformer.kt index 54166f26..1ec41363 100644 --- a/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/merged/MergedMinecraftTransformer.kt +++ b/src/minecraft/kotlin/xyz/wagyourtail/unimined/internal/minecraft/patch/merged/MergedMinecraftTransformer.kt @@ -105,17 +105,17 @@ class MergedMinecraftTransformer(project: Project, provider: MinecraftProvider): } @Deprecated("Please specify which forge.", replaceWith = ReplaceWith("minecraftForge(action)")) - override fun forge(action: ForgeLikePatcher.() -> Unit) { + override fun forge(action: ForgeLikePatcher<*>.() -> Unit) { minecraftForge(action) } - override fun minecraftForge(action: MinecraftForgePatcher.() -> Unit) { + override fun minecraftForge(action: MinecraftForgePatcher<*>.() -> Unit) { val forge = MinecraftForgeMinecraftTransformer(project, provider) forge.action() patchers.add(forge) } - override fun neoForged(action: NeoForgedPatcher.() -> Unit) { + override fun neoForged(action: NeoForgedPatcher<*>.() -> Unit) { val forge = NeoForgedMinecraftTransformer(project, provider) forge.action() patchers.add(forge) diff --git a/src/mods/kotlin/xyz/wagyourtail/unimined/internal/mods/ModRemapProvider.kt b/src/mods/kotlin/xyz/wagyourtail/unimined/internal/mods/ModRemapProvider.kt index bd88edd3..3e7a423e 100644 --- a/src/mods/kotlin/xyz/wagyourtail/unimined/internal/mods/ModRemapProvider.kt +++ b/src/mods/kotlin/xyz/wagyourtail/unimined/internal/mods/ModRemapProvider.kt @@ -8,25 +8,26 @@ import net.fabricmc.tinyremapper.InputTag import net.fabricmc.tinyremapper.NonClassCopyMode import net.fabricmc.tinyremapper.OutputConsumerPath import net.fabricmc.tinyremapper.TinyRemapper -import net.fabricmc.tinyremapper.extension.mixin.MixinExtension import org.gradle.api.InvalidUserDataException import org.gradle.api.Project import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.Dependency import org.gradle.api.artifacts.ResolvedArtifact import xyz.wagyourtail.unimined.api.mapping.MappingNamespaceTree +import xyz.wagyourtail.unimined.api.mapping.mixin.MixinRemapOptions import xyz.wagyourtail.unimined.api.minecraft.MinecraftConfig import xyz.wagyourtail.unimined.api.minecraft.patch.ForgeLikePatcher import xyz.wagyourtail.unimined.api.mod.ModRemapConfig import xyz.wagyourtail.unimined.api.unimined import xyz.wagyourtail.unimined.internal.mapping.at.AccessTransformerMinecraftTransformer import xyz.wagyourtail.unimined.internal.mapping.aw.AccessWidenerMinecraftTransformer -import xyz.wagyourtail.unimined.internal.mapping.mixin.refmap.BetterMixinExtension +import xyz.wagyourtail.unimined.internal.mapping.extension.MixinRemapExtension import xyz.wagyourtail.unimined.util.* import java.io.* import java.nio.file.Files import java.nio.file.Path import java.nio.file.StandardOpenOption +import java.util.concurrent.CompletableFuture import java.util.jar.JarFile import kotlin.io.path.createDirectories import kotlin.io.path.exists @@ -56,8 +57,12 @@ class ModRemapProvider(config: Set, val project: Project, val pro catchAWNs = true } - override var remapAtToLegacy: Boolean by FinalizeOnRead(LazyMutable { (provider.mcPatcher as? ForgeLikePatcher)?.remapAtToLegacy == true }) + override var remapAtToLegacy: Boolean by FinalizeOnRead(LazyMutable { (provider.mcPatcher as? ForgeLikePatcher<*>)?.remapAtToLegacy == true }) + override fun mixinRemap(action: MixinRemapOptions.() -> Unit) { + mixinRemap = action + } + var mixinRemap: MixinRemapOptions.() -> Unit by FinalizeOnRead {} var tinyRemapSettings: TinyRemapper.Builder.() -> Unit by FinalizeOnRead {} var config: Configuration.() -> Unit by FinalizeOnRead { @@ -119,7 +124,7 @@ class ModRemapProvider(config: Set, val project: Project, val pro fromNs: MappingNamespaceTree.Namespace, toNs: MappingNamespaceTree.Namespace, mc: Path - ): Pair { + ): CompletableFuture> { val remapperB = TinyRemapper.newRemapper() .withMappings( provider.mappings.getTRMappings( @@ -134,44 +139,21 @@ class ModRemapProvider(config: Set, val project: Project, val pro if (classpath != null) { remapperB.extension(KotlinRemapperClassloader.create(classpath).tinyRemapperExtension) } - val mixinExtension = when (mixinRemap) { - MixinRemap.UNIMINED -> { - val mixin = BetterMixinExtension( - project.gradle.startParameter.logLevel, - fallbackWhenNotInJson = true, - allowImplicitWildcards = true - ) - remapperB.extension(mixin) - mixin - } - MixinRemap.NONE -> null - MixinRemap.TINY_HARD, MixinRemap.TINY_HARDSOFT -> { - remapperB.extension( - MixinExtension( - when (mixinRemap) { - MixinRemap.TINY_HARD -> setOf(MixinExtension.AnnotationTarget.HARD) - MixinRemap.TINY_HARDSOFT -> setOf( - MixinExtension.AnnotationTarget.HARD, - MixinExtension.AnnotationTarget.SOFT - ) - - else -> throw InvalidUserDataException("Invalid MixinRemap value: $mixinRemap") - }, - BetterMixinExtension.translateLogLevel(project.gradle.startParameter.logLevel) - ) - ) - null - } + val mixinExtension = MixinRemapExtension( + project.gradle.startParameter.logLevel, + allowImplicitWildcards = true + ) + mixinExtension.enableBaseMixin() + mixinRemap(mixinExtension) + remapperB.extension(mixinExtension) - else -> throw InvalidUserDataException("Invalid MixinRemap value: $mixinRemap") - } tinyRemapSettings(remapperB) val remapper = remapperB.build() - remapper.readClassPathAsync( - *provider.minecraftLibraries.files.map { it.toPath() }.toTypedArray() - ) - remapper.readClassPathAsync(mc) - return remapper to mixinExtension + val future = mixinExtension.readClassPath(remapper, + *(provider.minecraftLibraries.files.map { it.toPath() } + listOf(mc)) + .toTypedArray() + ) + return future.thenApply { remapper to mixinExtension } } override fun remapper(remapperBuilder: TinyRemapper.Builder.() -> Unit) { @@ -244,13 +226,13 @@ class ModRemapProvider(config: Set, val project: Project, val pro mods.clear() mods.putAll( remapInternal( - remapper, - tags.nonNullValues(), + remapper.join(), + tags.join().nonNullValues(), prevNamespace to step ) ) mods.putAll( - tags.filterValues { it == null }.mapValues { targets[it.key]!!.second.first.toFile() } + tags.join().filterValues { it == null }.mapValues { targets[it.key]!!.second.first.toFile() } ) prevNamespace = step prevPrevNamespace = mcNamespace @@ -282,41 +264,50 @@ class ModRemapProvider(config: Set, val project: Project, val pro } private fun preRemapInternal( - remapper: Pair, + remapper: CompletableFuture>, deps: Map>> - ): Map>?> { + ): CompletableFuture>?>> { val output = mutableMapOf>?>() + var future = remapper + val futures = mutableListOf>() for ((artifact, data) in deps) { val file = data.first val target = data.second.first val needsRemap = !data.second.second - project.logger.info("[Unimined/ModRemapper] remap ${needsRemap}; ${file} -> ${target}") + project.logger.info("[Unimined/ModRemapper] remap ${needsRemap}; $file -> $target") if (file.isDirectory) { throw InvalidUserDataException("Cannot remap directory ${file.absolutePath}") } else { - if (needsRemap) { - val tag = remapper.first.createInputTag() - remapper.second?.preRead(tag, file.toPath(), "error.${artifact.name}.refmap.json") - remapper.first.readInputsAsync(tag, file.toPath()) - output[artifact] = tag to (file to target) - } else { - remapper.first.readClassPathAsync(file.toPath()) - output[artifact] = null + futures += future.thenCompose { + if (needsRemap) { + val tag = it.first.createInputTag() + output[artifact] = tag to (file to target) + it.second.readInput(it.first, tag, file.toPath()) + } else { + output[artifact] = null + it.second.readClassPath(it.first, file.toPath()) + } } } } - return output + return CompletableFuture.allOf(*futures.toTypedArray()).thenApply { output } } + private fun ResolvedArtifact.stringify() = "${this.moduleVersion.id.group}:${this.name}:${this.moduleVersion.id.version}${this.classifier?.let { ":$it" } ?: ""}${this.extension?.let { "@$it" } ?: ""}" + private fun remapInternal( - remapper: Pair, + remapper: Pair, deps: Map>>, remap: Pair ): Map { val output = mutableMapOf() project.logger.info("[Unimined/ModRemapper] Remapping mods to ${remap.first}/${remap.second}") for ((artifact, tag) in deps) { - remapModInternal(remapper, artifact, tag, remap) + try { + remapModInternal(remapper, artifact, tag, remap) + } catch (e: Exception) { + throw IllegalStateException("Failed to remap ${artifact.stringify()} to ${remap.first}/${remap.second}", e) + } output[artifact] = tag.second.second.toFile() } remapper.first.finish() @@ -324,7 +315,7 @@ class ModRemapProvider(config: Set, val project: Project, val pro } private fun remapModInternal( - remapper: Pair, + remapper: Pair, dep: ResolvedArtifact, input: Pair>, remap: Pair, @@ -346,20 +337,13 @@ class ModRemapProvider(config: Set, val project: Project, val pro ), innerJarStripper, AccessTransformerMinecraftTransformer.AtRemapper(project.logger, remapAtToLegacy, manifest) - ) + NonClassCopyMode.FIX_META_INF.remappers + ( - if (remapper.second != null) { - listOf(remapper.second!!.resourceReampper(input.first)) - } else { - emptyList() - } - ) + ) + NonClassCopyMode.FIX_META_INF.remappers ) remapper.first.apply(it, input.first) } - if (remapper.second != null) { - targetFile.openZipFileSystem(mapOf("mutable" to true)).use { - remapper.second!!.write(input.first, it) - } + + targetFile.openZipFileSystem(mapOf("mutable" to true)).use { + remapper.second.insertExtra(input.first, it) } } diff --git a/src/mods/kotlin/xyz/wagyourtail/unimined/internal/mods/ModsProvider.kt b/src/mods/kotlin/xyz/wagyourtail/unimined/internal/mods/ModsProvider.kt index e7a4c5ae..4102eb10 100644 --- a/src/mods/kotlin/xyz/wagyourtail/unimined/internal/mods/ModsProvider.kt +++ b/src/mods/kotlin/xyz/wagyourtail/unimined/internal/mods/ModsProvider.kt @@ -2,12 +2,15 @@ package xyz.wagyourtail.unimined.internal.mods import org.gradle.api.Project import org.gradle.api.artifacts.Configuration +import org.jetbrains.annotations.ApiStatus import xyz.wagyourtail.unimined.api.mapping.MappingNamespaceTree import xyz.wagyourtail.unimined.api.minecraft.MinecraftConfig import xyz.wagyourtail.unimined.api.mod.ModRemapConfig import xyz.wagyourtail.unimined.api.mod.ModsConfig import xyz.wagyourtail.unimined.api.unimined +import xyz.wagyourtail.unimined.util.FinalizeOnRead import xyz.wagyourtail.unimined.util.defaultedMapOf +import xyz.wagyourtail.unimined.util.getField import xyz.wagyourtail.unimined.util.withSourceSet import java.io.File import java.nio.file.Path @@ -25,6 +28,8 @@ class ModsProvider(val project: Project, val minecraft: MinecraftConfig) : ModsC remap(it) } + private var default by FinalizeOnRead Unit> {} + private val remapConfigsResolved = mutableMapOf() fun modTransformFolder(): Path { @@ -39,11 +44,23 @@ class ModsProvider(val project: Project, val minecraft: MinecraftConfig) : ModsC remapConfigs[config.toSet()] = action } + @ApiStatus.Internal + fun default(action: ModRemapConfig.() -> Unit) { + val prev: FinalizeOnRead Unit> = ModsProvider::class.getField("default")!!.getDelegate(this) as FinalizeOnRead Unit> + val old: ModRemapProvider.() -> Unit = prev.value as ModRemapProvider.() -> Unit + default = { + old(this) + action() + } + } + override fun modImplementation(action: ModRemapConfig.() -> Unit) { val old = remapConfigs[setOf(modImplementation)] remapConfigs[setOf(modImplementation)] = { if (old != null) { old() + } else { + default() } action() } @@ -79,7 +96,7 @@ class ModsProvider(val project: Project, val minecraft: MinecraftConfig) : ModsC } val files = remapOutputs.flatMap { it.resolve() } for (file in nonRemap) { - project.logger.info("[Unimined/ModRemapper] unremapped: $file") + project.logger.info("[Unimined/ModRemapper] unremapped: $file") } for (file in files) { project.logger.info("[Unimined/ModRemapper] remapped: $file") diff --git a/src/mods/kotlin/xyz/wagyourtail/unimined/internal/mods/task/RemapJarTaskImpl.kt b/src/mods/kotlin/xyz/wagyourtail/unimined/internal/mods/task/RemapJarTaskImpl.kt index 42b8a03b..a9335b8d 100644 --- a/src/mods/kotlin/xyz/wagyourtail/unimined/internal/mods/task/RemapJarTaskImpl.kt +++ b/src/mods/kotlin/xyz/wagyourtail/unimined/internal/mods/task/RemapJarTaskImpl.kt @@ -7,12 +7,13 @@ import net.fabricmc.tinyremapper.TinyRemapper import org.gradle.api.tasks.Internal import org.gradle.api.tasks.TaskAction import xyz.wagyourtail.unimined.api.mapping.MappingNamespaceTree +import xyz.wagyourtail.unimined.api.mapping.mixin.MixinRemapOptions import xyz.wagyourtail.unimined.api.minecraft.MinecraftConfig import xyz.wagyourtail.unimined.api.minecraft.patch.ForgeLikePatcher import xyz.wagyourtail.unimined.api.task.RemapJarTask import xyz.wagyourtail.unimined.internal.mapping.at.AccessTransformerMinecraftTransformer import xyz.wagyourtail.unimined.internal.mapping.aw.AccessWidenerMinecraftTransformer -import xyz.wagyourtail.unimined.internal.mapping.mixin.refmap.BetterMixinExtension +import xyz.wagyourtail.unimined.internal.mapping.extension.MixinRemapExtension import xyz.wagyourtail.unimined.util.* import java.nio.file.Path import java.nio.file.StandardOpenOption @@ -21,6 +22,8 @@ import kotlin.io.path.* abstract class RemapJarTaskImpl @Inject constructor(@get:Internal val provider: MinecraftConfig): RemapJarTask() { + private var mixinRemapOptions: MixinRemapOptions.() -> Unit by FinalizeOnRead {} + override fun devNamespace(namespace: String) { val delegate: FinalizeOnRead = RemapJarTask::class.getField("devNamespace")!!.getDelegate(this) as FinalizeOnRead delegate.setValueIntl(LazyMutable { provider.mappings.getNamespace(namespace) }) @@ -36,6 +39,10 @@ abstract class RemapJarTaskImpl @Inject constructor(@get:Internal val provider: delegate.setValueIntl(LazyMutable { provider.mappings.getNamespace(namespace) }) } + override fun mixinRemap(action: MixinRemapOptions.() -> Unit) { + mixinRemapOptions = action + } + override var allowImplicitWildcards by FinalizeOnRead(false) @TaskAction @@ -52,35 +59,24 @@ abstract class RemapJarTaskImpl @Inject constructor(@get:Internal val provider: prodNs ) - // merge in manifest from input jar - inputFile.get().asFile.toPath().readZipInputStreamFor("META-INF/MANIFEST.MF", false) { inp -> - // write to temp file - val inpTmp = project.buildDir.resolve("tmp").resolve(name).toPath().resolve("input-manifest.MF") - inpTmp.outputStream(StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING).use { out -> - inp.copyTo(out) - } - this.manifest { - it.from(inpTmp) - } - } val inputFile = provider.mcPatcher.beforeRemapJarTask(this, inputFile.get().asFile.toPath()) if (path.isEmpty()) { - // copy into output - from(project.zipTree(inputFile)) - copy() + project.logger.lifecycle("[Unimined/RemapJar ${this.path}] detected empty remap path, jumping to after remap tasks") + provider.mcPatcher.afterRemapJarTask(this, inputFile) + afterRemap(inputFile, inputFile) return } val last = path.last() project.logger.lifecycle("[Unimined/RemapJar ${this.path}] remapping output ${inputFile.name} from $devNs/$devFNs to $prodNs") - project.logger.info("[Unimined/RemapJar] $devNs -> ${path.joinToString(" -> ") { it.name }}") + project.logger.info("[Unimined/RemapJar ${this.path}] $devNs -> ${path.joinToString(" -> ") { it.name }}") var prevTarget = inputFile var prevNamespace = devNs var prevPrevNamespace = devFNs for (i in path.indices) { val step = path[i] - project.logger.info("[Unimined/RemapJar] $step") + project.logger.info("[Unimined/RemapJar ${this.path}] $step") val nextTarget = project.buildDir.resolve("tmp").resolve(name).toPath().resolve("${inputFile.nameWithoutExtension}-temp-${step.name}.jar") nextTarget.deleteIfExists() val mcNamespace = prevNamespace @@ -95,8 +91,26 @@ abstract class RemapJarTaskImpl @Inject constructor(@get:Internal val provider: prevPrevNamespace = prevNamespace prevNamespace = step } + project.logger.info("[Unimined/RemapJar ${path}] after remap tasks started ${System.currentTimeMillis()}") provider.mcPatcher.afterRemapJarTask(this, prevTarget) - from(project.zipTree(prevTarget)) + afterRemap(inputFile, prevTarget) + project.logger.info("[Unimined/RemapJar ${path}] after remap tasks finished ${System.currentTimeMillis()}") + } + + private fun afterRemap(inputFile: Path, afterRemapJar: Path) { + // merge in manifest from input jar + inputFile.readZipInputStreamFor("META-INF/MANIFEST.MF", false) { inp -> + // write to temp file + val inpTmp = project.buildDir.resolve("tmp").resolve(name).toPath().resolve("input-manifest.MF") + inpTmp.outputStream(StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING).use { out -> + inp.copyTo(out) + } + this.manifest { + it.from(inpTmp) + } + } + // copy into output + from(project.zipTree(afterRemapJar)) copy() } @@ -108,6 +122,7 @@ abstract class RemapJarTaskImpl @Inject constructor(@get:Internal val provider: toNs: MappingNamespaceTree.Namespace, mc: Path ) { + project.logger.info("[Unimined/RemapJar ${path}] remapping $fromNs -> $toNs (start time: ${System.currentTimeMillis()})") val remapperB = TinyRemapper.newRemapper() .withMappings( provider.mappings.getTRMappings( @@ -122,45 +137,62 @@ abstract class RemapJarTaskImpl @Inject constructor(@get:Internal val provider: if (classpath != null) { remapperB.extension(KotlinRemapperClassloader.create(classpath).tinyRemapperExtension) } - val betterMixinExtension = BetterMixinExtension(project.gradle.startParameter.logLevel, allowImplicitWildcards = allowImplicitWildcards) + val betterMixinExtension = MixinRemapExtension( + project.gradle.startParameter.logLevel, + allowImplicitWildcards + ) + betterMixinExtension.enableBaseMixin() + mixinRemapOptions(betterMixinExtension) remapperB.extension(betterMixinExtension) provider.minecraftRemapper.tinyRemapperConf(remapperB) val remapper = remapperB.build() - remapper.readClassPathAsync( - *provider.sourceSet.runtimeClasspath.files.map { it.toPath() } + val tag = remapper.createInputTag() + project.logger.debug("[Unimined/RemapJar ${path}] classpath: ") + (provider.sourceSet.runtimeClasspath.files.map { it.toPath() } + .filter { !provider.isMinecraftJar(it) } + .filter { it.exists() } + listOf(mc)) + .joinToString { "\n[Unimined/RemapJar ${path}] - $it" } + .let { project.logger.debug(it) } + project.logger.debug("[Unimined/RemapJar ${path}] input: $from") + betterMixinExtension.readClassPath(remapper, + *(provider.sourceSet.runtimeClasspath.files.map { it.toPath() } .filter { !provider.isMinecraftJar(it) } - .filter { it.exists() } + .filter { it.exists() } + listOf(mc)) .toTypedArray() - ) - remapper.readClassPathAsync(mc) - betterMixinExtension.preRead(from, "${project.rootProject.name}.refmap.json") - remapper.readInputsAsync(from) - target.parent.createDirectories() - try { - OutputConsumerPath.Builder(target).build().use { - it.addNonClassFiles( - from, - remapper, - listOf( - AccessWidenerMinecraftTransformer.AwRemapper( - if (fromNs.named) "named" else fromNs.name, - if (toNs.named) "named" else toNs.name - ), - AccessTransformerMinecraftTransformer.AtRemapper(project.logger, remapATToLegacy.getOrElse((provider.mcPatcher as? ForgeLikePatcher)?.remapAtToLegacy == true)!!), - betterMixinExtension.resourceRemapper() + ).thenCompose { + project.logger.info("[Unimined/RemapJar ${path}] reading input: $from (time: ${System.currentTimeMillis()})") + betterMixinExtension.readInput(remapper, tag, from) + }.thenRun { + project.logger.info("[Unimined/RemapJar ${path}] writing output: $target (time: ${System.currentTimeMillis()})") + target.parent.createDirectories() + try { + OutputConsumerPath.Builder(target).build().use { + it.addNonClassFiles( + from, + remapper, + listOf( + AccessWidenerMinecraftTransformer.AwRemapper( + if (fromNs.named) "named" else fromNs.name, + if (toNs.named) "named" else toNs.name + ), + AccessTransformerMinecraftTransformer.AtRemapper( + project.logger, + remapATToLegacy.getOrElse((provider.mcPatcher as? ForgeLikePatcher<*>)?.remapAtToLegacy == true)!! + ), + ) ) - ) - remapper.apply(it) + remapper.apply(it, tag) + } + } catch (e: Exception) { + target.deleteIfExists() + throw e } - } catch (e: Exception) { - target.deleteIfExists() - throw e - } - remapper.finish() - - target.openZipFileSystem(mapOf("mutable" to true)).use { - betterMixinExtension.write(it) - } + remapper.finish() + target.openZipFileSystem(mapOf("mutable" to true)).use { + betterMixinExtension.insertExtra(tag, it) + } + project.logger.info("[Unimined/RemapJar ${path}] remapped $fromNs -> $toNs (end time: ${System.currentTimeMillis()})") + }.join() } } \ No newline at end of file diff --git a/src/runs/kotlin/xyz/wagyourtail/unimined/internal/runs/RunsProvider.kt b/src/runs/kotlin/xyz/wagyourtail/unimined/internal/runs/RunsProvider.kt index b9b4e6f5..ae466712 100644 --- a/src/runs/kotlin/xyz/wagyourtail/unimined/internal/runs/RunsProvider.kt +++ b/src/runs/kotlin/xyz/wagyourtail/unimined/internal/runs/RunsProvider.kt @@ -86,7 +86,7 @@ class RunsProvider(val project: Project, val minecraft: MinecraftConfig) : RunsC val genIntellijRuns = project.tasks.register("genIntellijRuns".withSourceSet(minecraft.sourceSet)) { if (minecraft.sourceSet == project.sourceSets.getByName("main")) { it.group = "unimined_runs" - it.dependsOn(*project.unimined.minecrafts.keys.map { "genIntellijRuns".withSourceSet(minecraft.sourceSet) }.mapNotNull { project.tasks.findByName(it) }.toTypedArray()) + it.dependsOn(*project.unimined.minecrafts.keys.filter { it != minecraft.sourceSet }.map { "genIntellijRuns".withSourceSet(minecraft.sourceSet) }.mapNotNull { project.tasks.findByName(it) }.toTypedArray()) } else { it.group = "unimined_internal" } diff --git a/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/BabricModloaderB1_7_3Test.kt b/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/BabricModloaderB1_7_3Test.kt index 61ccb44f..c7379fb3 100644 --- a/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/BabricModloaderB1_7_3Test.kt +++ b/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/BabricModloaderB1_7_3Test.kt @@ -1,15 +1,27 @@ package xyz.wagyourtail.unimined.test.integration import org.gradle.testkit.runner.TaskOutcome +import org.gradle.testkit.runner.UnexpectedBuildFailure import org.junit.jupiter.api.Test import xyz.wagyourtail.unimined.util.runTestProject class BabricModloaderB1_7_3Test { @Test fun test_babric_modloader_b1_7_3() { - val result = runTestProject("b1.7.3-Babric-Modloader") - result.task(":build")?.outcome?.let { - if (it != TaskOutcome.SUCCESS) throw Exception("build failed") - } ?: throw Exception("build failed") + try { + val result = runTestProject("b1.7.3-Babric-Modloader") + + try { + result.task(":build")?.outcome?.let { + if (it != TaskOutcome.SUCCESS) throw Exception("build failed") + } ?: throw Exception("build failed") + } catch (e: Exception) { + println(result.output) + throw Exception(e) + } + } catch (e: UnexpectedBuildFailure) { + println(e) + throw Exception("build failed", e) + } } } \ No newline at end of file diff --git a/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/FabricInterfaceInjectionTest.kt b/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/FabricInterfaceInjectionTest.kt new file mode 100644 index 00000000..13b8a7b9 --- /dev/null +++ b/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/FabricInterfaceInjectionTest.kt @@ -0,0 +1,56 @@ +package xyz.wagyourtail.unimined.test.integration + +import org.gradle.testkit.runner.TaskOutcome +import org.gradle.testkit.runner.UnexpectedBuildFailure +import org.junit.jupiter.api.Test +import org.objectweb.asm.ClassReader +import org.objectweb.asm.Opcodes +import org.objectweb.asm.tree.ClassNode +import xyz.wagyourtail.unimined.util.openZipFileSystem +import xyz.wagyourtail.unimined.util.runTestProject +import java.nio.file.Files +import kotlin.io.path.inputStream +import kotlin.test.* + +class FabricInterfaceInjectionTest { + @Test + fun test_fabric_interface_injection() { + val projectName = "Fabric-Interface-Injection" + try { + val result = runTestProject(projectName) + + try { + result.task(":build")?.outcome?.let { + if (it != TaskOutcome.SUCCESS) throw Exception("build failed") + } ?: throw Exception("build failed") + } catch (e: Exception) { + println(result.output) + throw Exception(e) + } + } catch (e: UnexpectedBuildFailure) { + println(e) + throw Exception("build failed", e) + } + + val fs = openZipFileSystem(projectName, ".gradle/unimined/local/fabric/fabric/minecraft-1.14.4-fabric-merged+fixed-mojmap+intermediary-ii+49c3b85.jar") + + assertNotNull(fs, "Couldn't find the interface injected jar!") + + fs.use { + val target = it.getPath("/net/minecraft/advancements/Advancement.class") + + assertNotNull(target, "Couldn't find the injected class in mc jar!") + assertTrue(Files.exists(target), "Couldn't find the injected class in mc jar!") + + val reader = ClassReader(target.inputStream()) + val node = ClassNode(Opcodes.ASM9) + reader.accept(node, 0) + + assertNotNull(node.interfaces, "Injected class doesn't have any interface!") + assertEquals(1, node.interfaces.size) + assertContains(node.interfaces, "com/example/fabric/ExampleInterface") + } + + fs.close() + } +} \ No newline at end of file diff --git a/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/Forge1_3_2Test.kt b/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/Forge1_3_2Test.kt index acab62d7..d2b5b3fb 100644 --- a/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/Forge1_3_2Test.kt +++ b/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/Forge1_3_2Test.kt @@ -1,15 +1,27 @@ package xyz.wagyourtail.unimined.test.integration import org.gradle.testkit.runner.TaskOutcome +import org.gradle.testkit.runner.UnexpectedBuildFailure import org.junit.jupiter.api.Test import xyz.wagyourtail.unimined.util.runTestProject class Forge1_3_2Test { @Test fun test_forge_1_3_2() { - val result = runTestProject("1.3.2-Forge") - result.task(":build")?.outcome?.let { - if (it != TaskOutcome.SUCCESS) throw Exception("build failed") - } ?: throw Exception("build failed") + try { + val result = runTestProject("1.3.2-Forge") + + try { + result.task(":build")?.outcome?.let { + if (it != TaskOutcome.SUCCESS) throw Exception("build failed") + } ?: throw Exception("build failed") + } catch (e: Exception) { + println(result.output) + throw Exception(e) + } + } catch (e: UnexpectedBuildFailure) { + println(e) + throw Exception("build failed", e) + } } } \ No newline at end of file diff --git a/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/Forge1_6_4Test.kt b/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/Forge1_6_4Test.kt index 15b69f59..09a8fd14 100644 --- a/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/Forge1_6_4Test.kt +++ b/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/Forge1_6_4Test.kt @@ -1,15 +1,27 @@ package xyz.wagyourtail.unimined.test.integration import org.gradle.testkit.runner.TaskOutcome +import org.gradle.testkit.runner.UnexpectedBuildFailure import org.junit.jupiter.api.Test import xyz.wagyourtail.unimined.util.runTestProject class Forge1_6_4Test { @Test fun test_forge_1_6_4() { - val result = runTestProject("1.6.4-Forge") - result.task(":build")?.outcome?.let { - if (it != TaskOutcome.SUCCESS) throw Exception("build failed") - } ?: throw Exception("build failed") + try { + val result = runTestProject("1.6.4-Forge") + + try { + result.task(":build")?.outcome?.let { + if (it != TaskOutcome.SUCCESS) throw Exception("build failed") + } ?: throw Exception("build failed") + } catch (e: Exception) { + println(result.output) + throw Exception(e) + } + } catch (e: UnexpectedBuildFailure) { + println(e) + throw Exception("build failed", e) + } } } \ No newline at end of file diff --git a/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/ForgeFabric1_12_2Test.kt b/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/ForgeFabric1_12_2Test.kt index 05d2f397..25e61ee4 100644 --- a/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/ForgeFabric1_12_2Test.kt +++ b/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/ForgeFabric1_12_2Test.kt @@ -1,15 +1,27 @@ package xyz.wagyourtail.unimined.test.integration import org.gradle.testkit.runner.TaskOutcome +import org.gradle.testkit.runner.UnexpectedBuildFailure import org.junit.jupiter.api.Test import xyz.wagyourtail.unimined.util.runTestProject class ForgeFabric1_12_2Test { @Test fun test_forge_fabric_1_12_2() { - val result = runTestProject("1.12.2-Forge-Fabric") - result.task(":build")?.outcome?.let { - if (it != TaskOutcome.SUCCESS) throw Exception("build failed") - } ?: throw Exception("build failed") + try { + val result = runTestProject("1.12.2-Forge-Fabric") + + try { + result.task(":build")?.outcome?.let { + if (it != TaskOutcome.SUCCESS) throw Exception("build failed") + } ?: throw Exception("build failed") + } catch (e: Exception) { + println(result.output) + throw Exception(e) + } + } catch (e: UnexpectedBuildFailure) { + println(e) + throw Exception("build failed", e) + } } } \ No newline at end of file diff --git a/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/ForgeFabric1_14_4Test.kt b/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/ForgeFabric1_14_4Test.kt index 38b9c72b..6bdfe2a2 100644 --- a/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/ForgeFabric1_14_4Test.kt +++ b/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/ForgeFabric1_14_4Test.kt @@ -1,15 +1,27 @@ package xyz.wagyourtail.unimined.test.integration import org.gradle.testkit.runner.TaskOutcome +import org.gradle.testkit.runner.UnexpectedBuildFailure import org.junit.jupiter.api.Test import xyz.wagyourtail.unimined.util.runTestProject class ForgeFabric1_14_4Test { @Test fun test_forge_fabric_1_14_4() { - val result = runTestProject("1.14.4-Forge-Fabric") - result.task(":build")?.outcome?.let { - if (it != TaskOutcome.SUCCESS) throw Exception("build failed") - } ?: throw Exception("build failed") + try { + val result = runTestProject("1.14.4-Forge-Fabric") + + try { + result.task(":build")?.outcome?.let { + if (it != TaskOutcome.SUCCESS) throw Exception("build failed") + } ?: throw Exception("build failed") + } catch (e: Exception) { + println(result.output) + throw Exception(e) + } + } catch (e: UnexpectedBuildFailure) { + println(e) + throw Exception("build failed", e) + } } } \ No newline at end of file diff --git a/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/ForgeFabric1_15_2Test.kt b/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/ForgeFabric1_15_2Test.kt index 50b4e5e4..514c7816 100644 --- a/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/ForgeFabric1_15_2Test.kt +++ b/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/ForgeFabric1_15_2Test.kt @@ -1,15 +1,27 @@ package xyz.wagyourtail.unimined.test.integration import org.gradle.testkit.runner.TaskOutcome +import org.gradle.testkit.runner.UnexpectedBuildFailure import org.junit.jupiter.api.Test import xyz.wagyourtail.unimined.util.runTestProject class ForgeFabric1_15_2Test { @Test fun test_forge_fabric_1_15_2() { - val result = runTestProject("1.15.2-Forge-Fabric") - result.task(":build")?.outcome?.let { - if (it != TaskOutcome.SUCCESS) throw Exception("build failed") - } ?: throw Exception("build failed") + try { + val result = runTestProject("1.15.2-Forge-Fabric") + + try { + result.task(":build")?.outcome?.let { + if (it != TaskOutcome.SUCCESS) throw Exception("build failed") + } ?: throw Exception("build failed") + } catch (e: Exception) { + println(result.output) + throw Exception(e) + } + } catch (e: UnexpectedBuildFailure) { + println(e) + throw Exception("build failed", e) + } } } \ No newline at end of file diff --git a/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/ForgeFabric1_16_5Test.kt b/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/ForgeFabric1_16_5Test.kt index b348e013..4af6ff2f 100644 --- a/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/ForgeFabric1_16_5Test.kt +++ b/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/ForgeFabric1_16_5Test.kt @@ -1,15 +1,27 @@ package xyz.wagyourtail.unimined.test.integration import org.gradle.testkit.runner.TaskOutcome +import org.gradle.testkit.runner.UnexpectedBuildFailure import org.junit.jupiter.api.Test import xyz.wagyourtail.unimined.util.runTestProject class ForgeFabric1_16_5Test { @Test fun test_forge_fabric_1_16_5() { - val result = runTestProject("1.16.5-Forge-Fabric") - result.task(":build")?.outcome?.let { - if (it != TaskOutcome.SUCCESS) throw Exception("build failed") - } ?: throw Exception("build failed") + try { + val result = runTestProject("1.16.5-Forge-Fabric") + + try { + result.task(":build")?.outcome?.let { + if (it != TaskOutcome.SUCCESS) throw Exception("build failed") + } ?: throw Exception("build failed") + } catch (e: Exception) { + println(result.output) + throw Exception(e) + } + } catch (e: UnexpectedBuildFailure) { + println(e) + throw Exception("build failed", e) + } } } \ No newline at end of file diff --git a/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/ForgeFabric1_17_1Test.kt b/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/ForgeFabric1_17_1Test.kt index 0842033e..2668fb47 100644 --- a/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/ForgeFabric1_17_1Test.kt +++ b/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/ForgeFabric1_17_1Test.kt @@ -1,15 +1,27 @@ package xyz.wagyourtail.unimined.test.integration import org.gradle.testkit.runner.TaskOutcome +import org.gradle.testkit.runner.UnexpectedBuildFailure import org.junit.jupiter.api.Test import xyz.wagyourtail.unimined.util.runTestProject class ForgeFabric1_17_1Test { @Test fun test_forge_fabric_1_17_1() { - val result = runTestProject("1.17.1-Forge-Fabric") - result.task(":build")?.outcome?.let { - if (it != TaskOutcome.SUCCESS) throw Exception("build failed") - } ?: throw Exception("build failed") + try { + val result = runTestProject("1.17.1-Forge-Fabric") + + try { + result.task(":build")?.outcome?.let { + if (it != TaskOutcome.SUCCESS) throw Exception("build failed") + } ?: throw Exception("build failed") + } catch (e: Exception) { + println(result.output) + throw Exception(e) + } + } catch (e: UnexpectedBuildFailure) { + println(e) + throw Exception("build failed", e) + } } } \ No newline at end of file diff --git a/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/ForgeFabric1_20Test.kt b/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/ForgeFabric1_20Test.kt index cc2e5971..db0bca78 100644 --- a/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/ForgeFabric1_20Test.kt +++ b/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/ForgeFabric1_20Test.kt @@ -1,6 +1,7 @@ package xyz.wagyourtail.unimined.test.integration import org.gradle.testkit.runner.TaskOutcome +import org.gradle.testkit.runner.UnexpectedBuildFailure import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test import xyz.wagyourtail.unimined.util.runTestProject @@ -9,9 +10,20 @@ class ForgeFabric1_20Test { @Test @Disabled fun test_forge_fabric_1_20() { - val result = runTestProject("1.20-Forge-Fabric") - result.task(":build")?.outcome?.let { - if (it != TaskOutcome.SUCCESS) throw Exception("build failed") - } ?: throw Exception("build failed") + try { + val result = runTestProject("1.20-Forge-Fabric") + + try { + result.task(":build")?.outcome?.let { + if (it != TaskOutcome.SUCCESS) throw Exception("build failed") + } ?: throw Exception("build failed") + } catch (e: Exception) { + println(result.output) + throw Exception(e) + } + } catch (e: UnexpectedBuildFailure) { + println(e) + throw Exception("build failed", e) + } } } \ No newline at end of file diff --git a/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/ForgeFabric1_7_10Test.kt b/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/ForgeFabric1_7_10Test.kt index c8c5058f..b9b80a9f 100644 --- a/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/ForgeFabric1_7_10Test.kt +++ b/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/ForgeFabric1_7_10Test.kt @@ -1,15 +1,27 @@ package xyz.wagyourtail.unimined.test.integration import org.gradle.testkit.runner.TaskOutcome +import org.gradle.testkit.runner.UnexpectedBuildFailure import org.junit.jupiter.api.Test import xyz.wagyourtail.unimined.util.runTestProject class ForgeFabric1_7_10Test { @Test fun test_forge_fabric_1_7_10() { - val result = runTestProject("1.7.10-Forge-Fabric") - result.task(":build")?.outcome?.let { - if (it != TaskOutcome.SUCCESS) throw Exception("build failed") - } ?: throw Exception("build failed") + try { + val result = runTestProject("1.7.10-Forge-Fabric") + + try { + result.task(":build")?.outcome?.let { + if (it != TaskOutcome.SUCCESS) throw Exception("build failed") + } ?: throw Exception("build failed") + } catch (e: Exception) { + println(result.output) + throw Exception(e) + } + } catch (e: UnexpectedBuildFailure) { + println(e) + throw Exception("build failed", e) + } } } \ No newline at end of file diff --git a/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/ForgeFabric1_8_9Test.kt b/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/ForgeFabric1_8_9Test.kt index 1907d8ba..2d625984 100644 --- a/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/ForgeFabric1_8_9Test.kt +++ b/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/ForgeFabric1_8_9Test.kt @@ -1,15 +1,27 @@ package xyz.wagyourtail.unimined.test.integration import org.gradle.testkit.runner.TaskOutcome +import org.gradle.testkit.runner.UnexpectedBuildFailure import org.junit.jupiter.api.Test import xyz.wagyourtail.unimined.util.runTestProject class ForgeFabric1_8_9Test { @Test fun test_forge_fabric_1_8_9() { - val result = runTestProject("1.8.9-Forge-Fabric") - result.task(":build")?.outcome?.let { - if (it != TaskOutcome.SUCCESS) throw Exception("build failed") - } ?: throw Exception("build failed") + try { + val result = runTestProject("1.8.9-Forge-Fabric") + + try { + result.task(":build")?.outcome?.let { + if (it != TaskOutcome.SUCCESS) throw Exception("build failed") + } ?: throw Exception("build failed") + } catch (e: Exception) { + println(result.output) + throw Exception(e) + } + } catch (e: UnexpectedBuildFailure) { + println(e) + throw Exception("build failed", e) + } } } \ No newline at end of file diff --git a/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/ForgeModloader1_2_5Test.kt b/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/ForgeModloader1_2_5Test.kt index 5fb65e22..672e54b0 100644 --- a/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/ForgeModloader1_2_5Test.kt +++ b/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/ForgeModloader1_2_5Test.kt @@ -1,15 +1,27 @@ package xyz.wagyourtail.unimined.test.integration import org.gradle.testkit.runner.TaskOutcome +import org.gradle.testkit.runner.UnexpectedBuildFailure import org.junit.jupiter.api.Test import xyz.wagyourtail.unimined.util.runTestProject class ForgeModloader1_2_5Test { @Test fun test_forge_modloader_1_2_5() { - val result = runTestProject("1.2.5-Forge-Modloader") - result.task(":build")?.outcome?.let { - if (it != TaskOutcome.SUCCESS) throw Exception("build failed") - } ?: throw Exception("build failed") + try { + val result = runTestProject("1.2.5-Forge-Modloader") + + try { + result.task(":build")?.outcome?.let { + if (it != TaskOutcome.SUCCESS) throw Exception("build failed") + } ?: throw Exception("build failed") + } catch (e: Exception) { + println(result.output) + throw Exception(e) + } + } catch (e: UnexpectedBuildFailure) { + println(e) + throw Exception("build failed", e) + } } } \ No newline at end of file diff --git a/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/NeoForgedFabric1_20_1Test.kt b/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/NeoForgedFabric1_20_1Test.kt index edc7504b..4e3b6f82 100644 --- a/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/NeoForgedFabric1_20_1Test.kt +++ b/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/NeoForgedFabric1_20_1Test.kt @@ -1,15 +1,27 @@ package xyz.wagyourtail.unimined.test.integration import org.gradle.testkit.runner.TaskOutcome +import org.gradle.testkit.runner.UnexpectedBuildFailure import org.junit.jupiter.api.Test import xyz.wagyourtail.unimined.util.runTestProject class NeoForgedFabric1_20_1Test { @Test fun test_neoforged_fabric_1_20_1() { - val result = runTestProject("1.20.1-NeoForged-Fabric") - result.task(":build")?.outcome?.let { - if (it != TaskOutcome.SUCCESS) throw Exception("build failed") - } ?: throw Exception("build failed") + try { + val result = runTestProject("1.20.1-NeoForged-Fabric") + + try { + result.task(":build")?.outcome?.let { + if (it != TaskOutcome.SUCCESS) throw Exception("build failed") + } ?: throw Exception("build failed") + } catch (e: Exception) { + println(result.output) + throw Exception(e) + } + } catch (e: UnexpectedBuildFailure) { + println(e) + throw Exception("build failed", e) + } } } \ No newline at end of file diff --git a/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/NeoForgedForgeFabric1_20_2Test.kt b/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/NeoForgedForgeFabric1_20_2Test.kt new file mode 100644 index 00000000..c44ec183 --- /dev/null +++ b/src/test/kotlin/xyz/wagyourtail/unimined/test/integration/NeoForgedForgeFabric1_20_2Test.kt @@ -0,0 +1,27 @@ +package xyz.wagyourtail.unimined.test.integration + +import org.gradle.testkit.runner.TaskOutcome +import org.gradle.testkit.runner.UnexpectedBuildFailure +import org.junit.jupiter.api.Test +import xyz.wagyourtail.unimined.util.runTestProject + +class NeoForgedForgeFabric1_20_2Test { + @Test + fun test_neoforged_fabric_1_20_2() { + try { + val result = runTestProject("1.20.2-NeoForged-Forge-Fabric") + + try { + result.task(":build")?.outcome?.let { + if (it != TaskOutcome.SUCCESS) throw Exception("build failed") + } ?: throw Exception("build failed") + } catch (e: Exception) { + println(result.output) + throw Exception(e) + } + } catch (e: UnexpectedBuildFailure) { + println(e) + throw Exception("build failed", e) + } + } +} \ No newline at end of file diff --git a/src/test/kotlin/xyz/wagyourtail/unimined/util/IntegrationTestUtils.kt b/src/test/kotlin/xyz/wagyourtail/unimined/util/IntegrationTestUtils.kt index 22cc791b..c3588428 100644 --- a/src/test/kotlin/xyz/wagyourtail/unimined/util/IntegrationTestUtils.kt +++ b/src/test/kotlin/xyz/wagyourtail/unimined/util/IntegrationTestUtils.kt @@ -3,13 +3,26 @@ package xyz.wagyourtail.unimined.util import org.gradle.testkit.runner.BuildResult import org.gradle.testkit.runner.GradleRunner import java.io.File +import java.nio.file.FileSystem import java.nio.file.Path import java.nio.file.Paths import kotlin.io.path.exists import kotlin.io.path.writeText fun runTestProject(name: String): BuildResult { - return runGradle(Paths.get(".").resolve("testing").resolve(name)); + return runGradle(getTestProjectPath(name)); +} + +fun getTestProjectPath(name: String): Path { + return Paths.get(".").resolve("testing").resolve(name) +} + +fun openZipFileSystem(project: String, path: String): FileSystem? { + val fullPath = getTestProjectPath(project).resolve(path) + + if (!fullPath.exists()) return null + + return fullPath.openZipFileSystem(mapOf("mutable" to false)) } fun runGradle(dir: Path): BuildResult { diff --git a/testing/1.12.2-Forge-Fabric/build.gradle b/testing/1.12.2-Forge-Fabric/build.gradle index 1f17c29d..6ef256cc 100644 --- a/testing/1.12.2-Forge-Fabric/build.gradle +++ b/testing/1.12.2-Forge-Fabric/build.gradle @@ -4,9 +4,12 @@ plugins { } group 'com.example' -archivesBaseName 'UniminedExampleMod' version '1.0-SNAPSHOT' +base { + archivesName = 'UniminedExampleMod' +} + // this is just here so we can test the outputs easier and clean between tests unimined.useGlobalCache = false @@ -24,18 +27,6 @@ sourceSets { } } -tasks.register("fabricJar", Jar) { - from sourceSets.fabric.output, sourceSets.main.output - - archiveClassifier = "fabric" -} - -tasks.register("forgeJar", Jar) { - from sourceSets.forge.output, sourceSets.main.output - - archiveClassifier = "forge" -} - repositories { maven { name = "wagyourtail releases" @@ -60,29 +51,24 @@ unimined.minecraft { } unimined.minecraft(sourceSets.fabric) { - version project.minecraft_version - - mappings { - searge() - mcp("stable", "39-1.12") - } + combineWith(sourceSets.main) legacyFabric { loader project.fabric_version } + + defaultRemapJar = true } unimined.minecraft(sourceSets.forge) { - version project.minecraft_version - - mappings { - mcp("stable", "39-1.12") - } + combineWith(sourceSets.main) minecraftForge { loader project.forge_version mixinConfig "modid.mixins.json" } + + defaultRemapJar = true } configurations { diff --git a/testing/1.14.4-Forge-Fabric/build.gradle b/testing/1.14.4-Forge-Fabric/build.gradle index 24a4d271..6e8ae223 100644 --- a/testing/1.14.4-Forge-Fabric/build.gradle +++ b/testing/1.14.4-Forge-Fabric/build.gradle @@ -4,9 +4,12 @@ plugins { } group 'com.example' -archivesBaseName 'UniminedExampleMod' version '1.0-SNAPSHOT' +base { + archivesName = 'UniminedExampleMod' +} + // this is just here so we can test the outputs easier and clean between tests unimined.useGlobalCache = false @@ -24,18 +27,6 @@ sourceSets { } } -tasks.register("fabricJar", Jar) { - from sourceSets.fabric.output, sourceSets.main.output - - archiveClassifier = "fabric" -} - -tasks.register("forgeJar", Jar) { - from sourceSets.forge.output, sourceSets.main.output - - archiveClassifier = "forge" -} - repositories { mavenCentral() maven { @@ -58,15 +49,13 @@ unimined.minecraft { } unimined.minecraft(sourceSets.fabric) { - version project.minecraft_version - - mappings { - mojmap() - } + combineWith(sourceSets.main) fabric { loader project.fabric_version } + + defaultRemapJar = true } unimined.minecraft(sourceSets.forge) { diff --git a/testing/1.15.2-Forge-Fabric/build.gradle b/testing/1.15.2-Forge-Fabric/build.gradle index b588c018..084d77c6 100644 --- a/testing/1.15.2-Forge-Fabric/build.gradle +++ b/testing/1.15.2-Forge-Fabric/build.gradle @@ -4,9 +4,12 @@ plugins { } group 'com.example' -archivesBaseName 'UniminedExampleMod' version '1.0-SNAPSHOT' +base { + archivesName = 'UniminedExampleMod' +} + // this is just here so we can test the outputs easier and clean between tests unimined.useGlobalCache = false @@ -24,18 +27,6 @@ sourceSets { } } -tasks.register("fabricJar", Jar) { - from sourceSets.fabric.output, sourceSets.main.output - - archiveClassifier = "fabric" -} - -tasks.register("forgeJar", Jar) { - from sourceSets.forge.output, sourceSets.main.output - - archiveClassifier = "forge" -} - repositories { mavenCentral() maven { @@ -58,31 +49,24 @@ unimined.minecraft { } unimined.minecraft(sourceSets.fabric) { - version project.minecraft_version - - mappings { - yarn(project.mapping_version) - } + combineWith(sourceSets.main) fabric { loader project.fabric_version } + + defaultRemapJar = true } unimined.minecraft(sourceSets.forge) { - version project.minecraft_version - - mappings { - intermediary() - yarn(project.mapping_version) - - devFallbackNamespace "intermediary" - } + combineWith(sourceSets.main) minecraftForge { loader project.forge_version mixinConfig "modid.mixins.json" } + + defaultRemapJar = true } configurations { diff --git a/testing/1.16.5-Forge-Fabric/build.gradle b/testing/1.16.5-Forge-Fabric/build.gradle index b588c018..084d77c6 100644 --- a/testing/1.16.5-Forge-Fabric/build.gradle +++ b/testing/1.16.5-Forge-Fabric/build.gradle @@ -4,9 +4,12 @@ plugins { } group 'com.example' -archivesBaseName 'UniminedExampleMod' version '1.0-SNAPSHOT' +base { + archivesName = 'UniminedExampleMod' +} + // this is just here so we can test the outputs easier and clean between tests unimined.useGlobalCache = false @@ -24,18 +27,6 @@ sourceSets { } } -tasks.register("fabricJar", Jar) { - from sourceSets.fabric.output, sourceSets.main.output - - archiveClassifier = "fabric" -} - -tasks.register("forgeJar", Jar) { - from sourceSets.forge.output, sourceSets.main.output - - archiveClassifier = "forge" -} - repositories { mavenCentral() maven { @@ -58,31 +49,24 @@ unimined.minecraft { } unimined.minecraft(sourceSets.fabric) { - version project.minecraft_version - - mappings { - yarn(project.mapping_version) - } + combineWith(sourceSets.main) fabric { loader project.fabric_version } + + defaultRemapJar = true } unimined.minecraft(sourceSets.forge) { - version project.minecraft_version - - mappings { - intermediary() - yarn(project.mapping_version) - - devFallbackNamespace "intermediary" - } + combineWith(sourceSets.main) minecraftForge { loader project.forge_version mixinConfig "modid.mixins.json" } + + defaultRemapJar = true } configurations { diff --git a/testing/1.17.1-Forge-Fabric/build.gradle b/testing/1.17.1-Forge-Fabric/build.gradle index b588c018..f7e9053e 100644 --- a/testing/1.17.1-Forge-Fabric/build.gradle +++ b/testing/1.17.1-Forge-Fabric/build.gradle @@ -4,36 +4,18 @@ plugins { } group 'com.example' -archivesBaseName 'UniminedExampleMod' version '1.0-SNAPSHOT' +base { + archivesName = "UniminedExampleMod" +} + // this is just here so we can test the outputs easier and clean between tests unimined.useGlobalCache = false -sourceCompatibility = JavaVersion.VERSION_1_8 -targetCompatibility = JavaVersion.VERSION_1_8 - sourceSets { - fabric { - compileClasspath += sourceSets.main.output - runtimeClasspath += sourceSets.main.output - } - forge { - compileClasspath += sourceSets.main.output - runtimeClasspath += sourceSets.main.output - } -} - -tasks.register("fabricJar", Jar) { - from sourceSets.fabric.output, sourceSets.main.output - - archiveClassifier = "fabric" -} - -tasks.register("forgeJar", Jar) { - from sourceSets.forge.output, sourceSets.main.output - - archiveClassifier = "forge" + fabric + forge } repositories { @@ -58,31 +40,24 @@ unimined.minecraft { } unimined.minecraft(sourceSets.fabric) { - version project.minecraft_version - - mappings { - yarn(project.mapping_version) - } + combineWith(sourceSets.main) fabric { loader project.fabric_version } + + defaultRemapJar = true } unimined.minecraft(sourceSets.forge) { - version project.minecraft_version - - mappings { - intermediary() - yarn(project.mapping_version) - - devFallbackNamespace "intermediary" - } + combineWith(sourceSets.main) minecraftForge { loader project.forge_version mixinConfig "modid.mixins.json" } + + defaultRemapJar = true } configurations { diff --git a/testing/1.2.5-Fabric-Quilt/build.gradle b/testing/1.2.5-Fabric-Quilt/build.gradle new file mode 100644 index 00000000..5c9faec0 --- /dev/null +++ b/testing/1.2.5-Fabric-Quilt/build.gradle @@ -0,0 +1,110 @@ +plugins { + id 'java' + id 'xyz.wagyourtail.unimined' // version '1.0.0' +} + +group 'com.example' +version '1.0-SNAPSHOT' + +base { + archivesName = 'UniminedExampleMod' +} + +// this is just here so we can test the outputs easier and clean between tests +unimined.useGlobalCache = false + +sourceCompatibility = JavaVersion.VERSION_1_8 +targetCompatibility = JavaVersion.VERSION_1_8 + +sourceSets { + fabric { + compileClasspath += sourceSets.main.output + runtimeClasspath += sourceSets.main.output + } + quilt { + compileClasspath += sourceSets.main.output + sourceSets.fabric.output + runtimeClasspath += sourceSets.main.output + sourceSets.fabric.output + } +} + +repositories { + mavenLocal() + mavenCentral() +} + +unimined.minecraft() { + version "1.2.5" + side "client" + + mappings { + calamus() + feather(20) + + devFallbackNamespace "intermediary" + } + + minecraftRemapper.config { + ignoreConflicts(true) + } + + defaultRemapJar = false + +} + +unimined.minecraft(sourceSets.fabric) { + version "1.2.5" + side "client" + + mappings { + calamus() + feather(20) + } + + minecraftRemapper.config { + ignoreConflicts(true) + } + + fabric { + loader '0.14.24' + } + + defaultRemapJar = false +} + +unimined.minecraft(sourceSets.quilt) { + version "1.2.5" + side "client" + + mappings { + calamus() + feather(20) + } + + minecraftRemapper.config { + ignoreConflicts(true) + } + + quilt { + loader '0.21.2' + } + + defaultRemapJar = false +} + +repositories { +} + +dependencies { +} + +jar { + enabled = false +} + +processResources { + inputs.property "version", project.version + + filesMatching("fabric.mod.json") { + expand "version": project.version + } +} diff --git a/testing/1.2.5-Fabric-Quilt/gradle.properties b/testing/1.2.5-Fabric-Quilt/gradle.properties new file mode 100644 index 00000000..4687f10d --- /dev/null +++ b/testing/1.2.5-Fabric-Quilt/gradle.properties @@ -0,0 +1 @@ +org.gradle.jvmargs=-Xmx2G diff --git a/testing/1.2.5-Fabric-Quilt/gradle/wrapper/gradle-wrapper.properties b/testing/1.2.5-Fabric-Quilt/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..3fa8f862 --- /dev/null +++ b/testing/1.2.5-Fabric-Quilt/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/testing/1.2.5-Fabric-Quilt/gradlew b/testing/1.2.5-Fabric-Quilt/gradlew new file mode 100644 index 00000000..1aa94a42 --- /dev/null +++ b/testing/1.2.5-Fabric-Quilt/gradlew @@ -0,0 +1,249 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/testing/1.2.5-Fabric-Quilt/gradlew.bat b/testing/1.2.5-Fabric-Quilt/gradlew.bat new file mode 100644 index 00000000..93e3f59f --- /dev/null +++ b/testing/1.2.5-Fabric-Quilt/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/testing/1.2.5-Fabric-Quilt/settings.gradle b/testing/1.2.5-Fabric-Quilt/settings.gradle new file mode 100644 index 00000000..80f7e5b0 --- /dev/null +++ b/testing/1.2.5-Fabric-Quilt/settings.gradle @@ -0,0 +1,30 @@ +pluginManagement { + repositories { + mavenCentral() + maven { + url = "https://maven.neoforged.net/releases" + } + maven { + url = "https://maven.minecraftforge.net/" + } + maven { + url = "https://maven.fabricmc.net/" + } + maven { + url = "https://maven.wagyourtail.xyz/releases" + } + maven { + url = "https://maven.wagyourtail.xyz/snapshots" + } + gradlePluginPortal() { + content { + excludeGroup("org.apache.logging.log4j") + } + } + } +} + +// so we can use the unimined directly provided by the super project +includeBuild('../../') + +rootProject.name = '1.2.5-Fabric-Quilt' diff --git a/testing/1.2.5-Fabric-Quilt/src/fabric/java/com/example/fabric/ExampleModFabric.java b/testing/1.2.5-Fabric-Quilt/src/fabric/java/com/example/fabric/ExampleModFabric.java new file mode 100644 index 00000000..e6be8422 --- /dev/null +++ b/testing/1.2.5-Fabric-Quilt/src/fabric/java/com/example/fabric/ExampleModFabric.java @@ -0,0 +1,14 @@ +package com.example.fabric; + +import net.fabricmc.api.ModInitializer; + +import java.util.logging.Logger; + +public class ExampleModFabric implements ModInitializer { + public static Logger LOGGER = Logger.getLogger("ExampleMod"); + + @Override + public void onInitialize() { + LOGGER.info("Hello from Fabric!"); + } +} diff --git a/testing/1.2.5-Fabric-Quilt/src/fabric/java/com/example/mixin/ExampleMixin.java b/testing/1.2.5-Fabric-Quilt/src/fabric/java/com/example/mixin/ExampleMixin.java new file mode 100644 index 00000000..bfac4680 --- /dev/null +++ b/testing/1.2.5-Fabric-Quilt/src/fabric/java/com/example/mixin/ExampleMixin.java @@ -0,0 +1,16 @@ +package com.example.mixin; + +import com.example.fabric.ExampleModFabric; +import net.minecraft.client.gui.screen.TitleScreen; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(TitleScreen.class) +public class ExampleMixin { + @Inject(method = "init", at = @At("HEAD")) + private void onInit(CallbackInfo ci) { + ExampleModFabric.LOGGER.info("This is the main menu!"); + } +} diff --git a/testing/1.2.5-Fabric-Quilt/src/fabric/resources/fabric.mod.json b/testing/1.2.5-Fabric-Quilt/src/fabric/resources/fabric.mod.json new file mode 100644 index 00000000..05fce7da --- /dev/null +++ b/testing/1.2.5-Fabric-Quilt/src/fabric/resources/fabric.mod.json @@ -0,0 +1,29 @@ +{ + "schemaVersion": 1, + "id": "modid", + "version": "${version}", + "name": "Example Mod", + "description": "This is an example description! Tell everyone what your mod is about!", + "authors": [ + "Me!" + ], + "contact": { + "homepage": "https://fabricmc.net/", + "sources": "https://github.com/FabricMC/fabric-example-mod" + }, + "license": "CC0-1.0", + "environment": "*", + "entrypoints": { + "main": [ + "com.example.fabric.ExampleModFabric" + ] + }, + "mixins": [ + "modid.mixins.json" + ], + "depends": { + "fabricloader": ">=0.14.19", + "minecraft": "1.2.5", + "java": ">=8" + } +} diff --git a/testing/1.2.5-Fabric-Quilt/src/fabric/resources/modid.mixins.json b/testing/1.2.5-Fabric-Quilt/src/fabric/resources/modid.mixins.json new file mode 100644 index 00000000..100ab8dd --- /dev/null +++ b/testing/1.2.5-Fabric-Quilt/src/fabric/resources/modid.mixins.json @@ -0,0 +1,14 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "com.example.mixin", + "compatibilityLevel": "JAVA_8", + "mixins": [ + ], + "client": [ + "ExampleMixin" + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/testing/1.2.5-Forge-Modloader/build.gradle b/testing/1.2.5-Forge-Modloader/build.gradle index 042205f0..d6f440b4 100644 --- a/testing/1.2.5-Forge-Modloader/build.gradle +++ b/testing/1.2.5-Forge-Modloader/build.gradle @@ -4,9 +4,12 @@ plugins { } group 'com.example' -archivesBaseName 'UniminedExampleMod' version '1.0-SNAPSHOT' +base { + archivesName = 'UniminedExampleMod' +} + // this is just here so we can test the outputs easier and clean between tests unimined.useGlobalCache = false @@ -29,19 +32,6 @@ repositories { mavenCentral() } -tasks.register("modloaderJar", Jar) { - from sourceSets.modloader.output, sourceSets.main.output - - archiveClassifier = "modloader" -} - -tasks.register("forgeJar", Jar) { - from sourceSets.forge.output, sourceSets.main.output - - archiveClassifier = "forge" - -} - unimined.minecraft() { version "1.2.5" side "client" @@ -55,6 +45,7 @@ unimined.minecraft() { } defaultRemapJar = false + } unimined.minecraft(sourceSets.forge) { @@ -107,6 +98,7 @@ unimined.minecraft(sourceSets.modloader) { } repositories { + unimined.wagYourMaven('releases') } dependencies { @@ -127,4 +119,4 @@ processResources { filesMatching("mcmod.info") { expand "version": project.version } -} \ No newline at end of file +} diff --git a/testing/1.20.1-NeoForged-Fabric/build.gradle b/testing/1.20.1-NeoForged-Fabric/build.gradle index 55006501..b02a167e 100644 --- a/testing/1.20.1-NeoForged-Fabric/build.gradle +++ b/testing/1.20.1-NeoForged-Fabric/build.gradle @@ -4,36 +4,18 @@ plugins { } group 'com.example' -archivesBaseName 'UniminedExampleMod' version '1.0-SNAPSHOT' +base { + archivesName = "UniminedExampleMod" +} + // this is just here so we can test the outputs easier and clean between tests unimined.useGlobalCache = false -sourceCompatibility = JavaVersion.VERSION_1_8 -targetCompatibility = JavaVersion.VERSION_1_8 - sourceSets { - fabric { - compileClasspath += sourceSets.main.output - runtimeClasspath += sourceSets.main.output - } - forge { - compileClasspath += sourceSets.main.output - runtimeClasspath += sourceSets.main.output - } -} - -tasks.register("fabricJar", Jar) { - from sourceSets.fabric.output, sourceSets.main.output - - archiveClassifier = "fabric" -} - -tasks.register("forgeJar", Jar) { - from sourceSets.forge.output, sourceSets.main.output - - archiveClassifier = "forge" + fabric + forge } repositories { @@ -59,53 +41,29 @@ unimined.minecraft { } unimined.minecraft(sourceSets.fabric) { - version project.minecraft_version - - mappings { - mojmap() - parchment("1.19.3", "2023.03.12-nightly-SNAPSHOT") - } + combineWith(sourceSets.main) fabric { loader project.fabric_version } + + defaultRemapJar = true } unimined.minecraft(sourceSets.forge) { - version project.minecraft_version - - mappings { - intermediary() - mojmap() - parchment("1.19.3", "2023.03.12-nightly-SNAPSHOT") - - devFallbackNamespace "intermediary" - } + combineWith(sourceSets.main) neoForged { loader project.forge_version mixinConfig "modid.mixins.json" } -} - -configurations { - mainImplementation - implementation { - extendsFrom forgeImplementation - extendsFrom fabricImplementation - } -} -sourceSets { - main { - compileClasspath += configurations.mainImplementation - runtimeClasspath += configurations.mainImplementation - } + defaultRemapJar = true } dependencies { // we need this in main where it isn't by default - mainImplementation "org.spongepowered:mixin:0.8.5-SNAPSHOT" + implementation "org.spongepowered:mixin:0.8.5-SNAPSHOT" fabricModImplementation "net.fabricmc.fabric-api:fabric-api:0.83.0+1.20" } diff --git a/testing/1.20.2-NeoForged-Forge-Fabric/build.gradle b/testing/1.20.2-NeoForged-Forge-Fabric/build.gradle new file mode 100644 index 00000000..15ed928a --- /dev/null +++ b/testing/1.20.2-NeoForged-Forge-Fabric/build.gradle @@ -0,0 +1,130 @@ +plugins { + id 'java' + id 'xyz.wagyourtail.unimined' // version '1.0.0' +} + +group 'com.example' +version '1.0-SNAPSHOT' + +base { + archivesName = "UniminedExampleMod" +} + +// this is just here so we can test the outputs easier and clean between tests +unimined.useGlobalCache = false + +sourceSets { + fabric + neoforge + lexforge +} + +repositories { + mavenCentral() + maven { + name = "sponge" + url = "https://repo.spongepowered.org/maven" + } +} + +unimined.minecraft { + version project.minecraft_version + + mappings { + intermediary() + mojmap() + parchment("1.19.3", "2023.03.12-nightly-SNAPSHOT") + + devFallbackNamespace "intermediary" + } + + defaultRemapJar = false +} + +unimined.minecraft(sourceSets.fabric) { + combineWith(sourceSets.main) + + fabric { + loader project.fabric_version + } + + defaultRemapJar = true +} + +unimined.minecraft(sourceSets.neoforge) { + combineWith(sourceSets.main) + + neoForged { + loader project.neoforge_version + mixinConfig "modid.mixins.json" + } + + minecraftRemapper.config { + // neoforge adds 1 conflict, where 2 interfaces have a method with the same name on yarn/mojmap, + // but the method has different names in the intermediary mappings. + // this is a conflict because they have a class that extends both interfaces. + // this shouldn't be a problem as long as named mappings don't make the name of those 2 methods different. + ignoreConflicts(true) + } + + defaultRemapJar = true +} + +unimined.minecraft(sourceSets.lexforge) { + version project.minecraft_version + + mappings { + intermediary() + mojmap() + parchment("1.19.3", "2023.03.12-nightly-SNAPSHOT") + + devFallbackNamespace "intermediary" + } + + minecraftForge { + loader project.lexforge_version + mixinConfig "modid.mixins.json" + } + + minecraftRemapper.config { + ignoreConflicts(true) + } +} + +configurations { + mainImplementation +} + + +dependencies { + // we need this in main where it isn't by default + implementation "org.spongepowered:mixin:0.8.5-SNAPSHOT" +} + +jar { + enabled = false +} + +processFabricResources { + inputs.property "version", project.version + + filesMatching("fabric.mod.json") { + expand "version": project.version + } +} + +processNeoforgeResources { + inputs.property "version", project.version + + filesMatching("META-INF/mods.toml") { + expand "version": project.version + } +} + +processLexforgeResources { + inputs.property "version", project.version + + filesMatching("META-INF/mods.toml") { + expand "version": project.version + } +} \ No newline at end of file diff --git a/testing/1.20.2-NeoForged-Forge-Fabric/gradle.properties b/testing/1.20.2-NeoForged-Forge-Fabric/gradle.properties new file mode 100644 index 00000000..4ede3ed7 --- /dev/null +++ b/testing/1.20.2-NeoForged-Forge-Fabric/gradle.properties @@ -0,0 +1,7 @@ +org.gradle.jvmargs = -Xmx2G + +minecraft_version = 1.20.2 +mapping_version = 1 +neoforge_version = 60-beta +lexforge_version = 48.0.34 +fabric_version = 0.14.24 \ No newline at end of file diff --git a/testing/1.20.2-NeoForged-Forge-Fabric/gradle/wrapper/gradle-wrapper.properties b/testing/1.20.2-NeoForged-Forge-Fabric/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..42defcc9 --- /dev/null +++ b/testing/1.20.2-NeoForged-Forge-Fabric/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip +networkTimeout=10000 +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/testing/1.20.2-NeoForged-Forge-Fabric/gradlew b/testing/1.20.2-NeoForged-Forge-Fabric/gradlew new file mode 100755 index 00000000..79a61d42 --- /dev/null +++ b/testing/1.20.2-NeoForged-Forge-Fabric/gradlew @@ -0,0 +1,244 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/testing/1.20.2-NeoForged-Forge-Fabric/gradlew.bat b/testing/1.20.2-NeoForged-Forge-Fabric/gradlew.bat new file mode 100644 index 00000000..6689b85b --- /dev/null +++ b/testing/1.20.2-NeoForged-Forge-Fabric/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/testing/1.20.2-NeoForged-Forge-Fabric/settings.gradle b/testing/1.20.2-NeoForged-Forge-Fabric/settings.gradle new file mode 100644 index 00000000..2c0b8f8e --- /dev/null +++ b/testing/1.20.2-NeoForged-Forge-Fabric/settings.gradle @@ -0,0 +1,31 @@ + +pluginManagement { + repositories { + mavenCentral() + maven { + url = "https://maven.neoforged.net/releases" + } + maven { + url = "https://maven.minecraftforge.net/" + } + maven { + url = "https://maven.fabricmc.net/" + } + maven { + url = "https://maven.wagyourtail.xyz/releases" + } + maven { + url = "https://maven.wagyourtail.xyz/snapshots" + } + gradlePluginPortal() { + content { + excludeGroup("org.apache.logging.log4j") + } + } + } +} + +// so we can use the unimined directly provided by the super project +includeBuild('../../') + +rootProject.name = '1.20.2-NeoForged-Forge-Fabric' \ No newline at end of file diff --git a/testing/1.20.2-NeoForged-Forge-Fabric/src/fabric/java/com/example/fabric/ExampleModFabric.java b/testing/1.20.2-NeoForged-Forge-Fabric/src/fabric/java/com/example/fabric/ExampleModFabric.java new file mode 100644 index 00000000..51bca524 --- /dev/null +++ b/testing/1.20.2-NeoForged-Forge-Fabric/src/fabric/java/com/example/fabric/ExampleModFabric.java @@ -0,0 +1,13 @@ +package com.example.fabric; + +import com.example.ExampleMod; +import net.fabricmc.api.ModInitializer; + +public class ExampleModFabric implements ModInitializer { + + @Override + public void onInitialize() { + ExampleMod.LOGGER.info("Hello from Fabric!"); + } + +} diff --git a/testing/1.20.2-NeoForged-Forge-Fabric/src/fabric/resources/fabric.mod.json b/testing/1.20.2-NeoForged-Forge-Fabric/src/fabric/resources/fabric.mod.json new file mode 100644 index 00000000..fe443883 --- /dev/null +++ b/testing/1.20.2-NeoForged-Forge-Fabric/src/fabric/resources/fabric.mod.json @@ -0,0 +1,34 @@ +{ + "schemaVersion": 1, + "id": "modid", + "version": "${version}", + + "name": "Example Mod", + "description": "This is an example description! Tell everyone what your mod is about!", + "authors": [ + "Me!" + ], + "contact": { + "homepage": "https://fabricmc.net/", + "sources": "https://github.com/FabricMC/fabric-example-mod" + }, + + "license": "CC0-1.0", + "icon": "assets/modid/icon.png", + + "environment": "*", + "entrypoints": { + "main": [ + "com.example.fabric.ExampleModFabric" + ] + }, + "mixins": [ + "modid.mixins.json" + ], + + "depends": { + "fabricloader": ">=0.14.19", + "minecraft": "~1.16.5", + "java": ">=8" + } +} \ No newline at end of file diff --git a/testing/1.20.2-NeoForged-Forge-Fabric/src/lexforge/java/com/example/forge/ExampleModForge.java b/testing/1.20.2-NeoForged-Forge-Fabric/src/lexforge/java/com/example/forge/ExampleModForge.java new file mode 100644 index 00000000..cbaaf6c3 --- /dev/null +++ b/testing/1.20.2-NeoForged-Forge-Fabric/src/lexforge/java/com/example/forge/ExampleModForge.java @@ -0,0 +1,19 @@ +package com.example.forge; + +import com.example.ExampleMod; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; + +@Mod("modid") +public class ExampleModForge { + + public ExampleModForge() { + } + + @SubscribeEvent + public void onInit(FMLCommonSetupEvent event) { + ExampleMod.LOGGER.info("Hello from Forge!"); + } + +} diff --git a/testing/1.20.2-NeoForged-Forge-Fabric/src/lexforge/resources/META-INF/mods.toml b/testing/1.20.2-NeoForged-Forge-Fabric/src/lexforge/resources/META-INF/mods.toml new file mode 100644 index 00000000..cea4b02b --- /dev/null +++ b/testing/1.20.2-NeoForged-Forge-Fabric/src/lexforge/resources/META-INF/mods.toml @@ -0,0 +1,20 @@ +modLoader="javafml" +loaderVersion="[48,)" +issueTrackerURL="https://github.com/wagyourtail/jsmacros" +license = "MPL-2.0" + +[[mods]] +modId = "modid" +version = "${version}" +displayName = "Example Mod" +authors = "Me!" +description = ''' + This is an example description! Tell everyone what your mod is about! +''' + +[[dependencies.modid]] +modId="minecraft" +mandatory=true +versionRange="[1.20.2]" +ordering="NONE" +side="BOTH" \ No newline at end of file diff --git a/testing/1.20.2-NeoForged-Forge-Fabric/src/lexforge/resources/pack.mcmeta b/testing/1.20.2-NeoForged-Forge-Fabric/src/lexforge/resources/pack.mcmeta new file mode 100644 index 00000000..41a98dde --- /dev/null +++ b/testing/1.20.2-NeoForged-Forge-Fabric/src/lexforge/resources/pack.mcmeta @@ -0,0 +1,6 @@ +{ + "pack": { + "description": "example resources", + "pack_format": 6 + } +} \ No newline at end of file diff --git a/testing/1.20.2-NeoForged-Forge-Fabric/src/main/java/com/example/ExampleMod.java b/testing/1.20.2-NeoForged-Forge-Fabric/src/main/java/com/example/ExampleMod.java new file mode 100644 index 00000000..ef3ef249 --- /dev/null +++ b/testing/1.20.2-NeoForged-Forge-Fabric/src/main/java/com/example/ExampleMod.java @@ -0,0 +1,9 @@ +package com.example; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class ExampleMod { + public static final Logger LOGGER = LogManager.getLogger("modid"); + +} diff --git a/testing/1.20.2-NeoForged-Forge-Fabric/src/main/java/com/example/mixin/ExampleMixin.java b/testing/1.20.2-NeoForged-Forge-Fabric/src/main/java/com/example/mixin/ExampleMixin.java new file mode 100644 index 00000000..428dc930 --- /dev/null +++ b/testing/1.20.2-NeoForged-Forge-Fabric/src/main/java/com/example/mixin/ExampleMixin.java @@ -0,0 +1,17 @@ +package com.example.mixin; + +import com.example.ExampleMod; +import net.minecraft.client.gui.screens.TitleScreen; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(TitleScreen.class) +public class ExampleMixin { + + @Inject(method = "init()V", at = @At("HEAD")) + private void init(CallbackInfo info) { + ExampleMod.LOGGER.info("This line is printed by an example mod mixin!"); + } +} diff --git a/testing/1.20.2-NeoForged-Forge-Fabric/src/main/resources/modid.mixins.json b/testing/1.20.2-NeoForged-Forge-Fabric/src/main/resources/modid.mixins.json new file mode 100644 index 00000000..a08e9c51 --- /dev/null +++ b/testing/1.20.2-NeoForged-Forge-Fabric/src/main/resources/modid.mixins.json @@ -0,0 +1,14 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "com.example.mixin", + "compatibilityLevel": "JAVA_8", + "mixins": [ + ], + "client": [ + "ExampleMixin" + ], + "injectors": { + "defaultRequire": 1 + } +} \ No newline at end of file diff --git a/testing/1.20.2-NeoForged-Forge-Fabric/src/neoforge/java/com/example/forge/ExampleModForge.java b/testing/1.20.2-NeoForged-Forge-Fabric/src/neoforge/java/com/example/forge/ExampleModForge.java new file mode 100644 index 00000000..31e56aea --- /dev/null +++ b/testing/1.20.2-NeoForged-Forge-Fabric/src/neoforge/java/com/example/forge/ExampleModForge.java @@ -0,0 +1,19 @@ +package com.example.forge; + +import com.example.ExampleMod; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.common.Mod; +import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent; + +@Mod("modid") +public class ExampleModForge { + + public ExampleModForge() { + } + + @SubscribeEvent + public void onInit(FMLCommonSetupEvent event) { + ExampleMod.LOGGER.info("Hello from Forge!"); + } + +} diff --git a/testing/1.20.2-NeoForged-Forge-Fabric/src/neoforge/resources/META-INF/mods.toml b/testing/1.20.2-NeoForged-Forge-Fabric/src/neoforge/resources/META-INF/mods.toml new file mode 100644 index 00000000..70516579 --- /dev/null +++ b/testing/1.20.2-NeoForged-Forge-Fabric/src/neoforge/resources/META-INF/mods.toml @@ -0,0 +1,20 @@ +modLoader="javafml" +loaderVersion="[1,)" +issueTrackerURL="https://github.com/wagyourtail/jsmacros" +license = "MPL-2.0" + +[[mods]] +modId = "modid" +version = "${version}" +displayName = "Example Mod" +authors = "Me!" +description = ''' + This is an example description! Tell everyone what your mod is about! +''' + +[[dependencies.modid]] +modId="minecraft" +mandatory=true +versionRange="[1.20.2]" +ordering="NONE" +side="BOTH" \ No newline at end of file diff --git a/testing/1.20.2-NeoForged-Forge-Fabric/src/neoforge/resources/pack.mcmeta b/testing/1.20.2-NeoForged-Forge-Fabric/src/neoforge/resources/pack.mcmeta new file mode 100644 index 00000000..41a98dde --- /dev/null +++ b/testing/1.20.2-NeoForged-Forge-Fabric/src/neoforge/resources/pack.mcmeta @@ -0,0 +1,6 @@ +{ + "pack": { + "description": "example resources", + "pack_format": 6 + } +} \ No newline at end of file diff --git a/testing/1.3.2-Forge/build.gradle b/testing/1.3.2-Forge/build.gradle index d5d0c24e..b4c6f053 100644 --- a/testing/1.3.2-Forge/build.gradle +++ b/testing/1.3.2-Forge/build.gradle @@ -4,9 +4,12 @@ plugins { } group 'com.example' -archivesBaseName 'UniminedExampleMod' version '1.0-SNAPSHOT' +base { + archivesName = 'UniminedExampleMod' +} + // this is just here so we can test the outputs easier and clean between tests unimined.useGlobalCache = false diff --git a/testing/1.6.4-Forge/build.gradle b/testing/1.6.4-Forge/build.gradle index 953152c4..fa4f61e8 100644 --- a/testing/1.6.4-Forge/build.gradle +++ b/testing/1.6.4-Forge/build.gradle @@ -4,9 +4,12 @@ plugins { } group 'com.example' -archivesBaseName 'UniminedExampleMod' version '1.0-SNAPSHOT' +base { + archivesName = 'UniminedExampleMod' +} + // this is just here so we can test the outputs easier and clean between tests unimined.useGlobalCache = false diff --git a/testing/1.7.10-Forge-Fabric/build.gradle b/testing/1.7.10-Forge-Fabric/build.gradle index 1086461d..7986916b 100644 --- a/testing/1.7.10-Forge-Fabric/build.gradle +++ b/testing/1.7.10-Forge-Fabric/build.gradle @@ -4,9 +4,12 @@ plugins { } group 'com.example' -archivesBaseName 'UniminedExampleMod' version '1.0-SNAPSHOT' +base { + archivesName = 'UniminedExampleMod' +} + // this is just here so we can test the outputs easier and clean between tests unimined.useGlobalCache = false @@ -24,18 +27,6 @@ sourceSets { } } -tasks.register("fabricJar", Jar) { - from sourceSets.fabric.output, sourceSets.main.output - - archiveClassifier = "fabric" -} - -tasks.register("forgeJar", Jar) { - from sourceSets.forge.output, sourceSets.main.output - - archiveClassifier = "forge" -} - repositories { maven { name = "wagyourtail releases" @@ -60,29 +51,24 @@ unimined.minecraft { } unimined.minecraft(sourceSets.fabric) { - version project.minecraft_version - - mappings { - searge() - mcp("stable", "12-1.7.10") - } + combineWith(sourceSets.main) legacyFabric { loader project.fabric_version } + + defaultRemapJar = true } unimined.minecraft(sourceSets.forge) { - version project.minecraft_version - - mappings { - mcp("stable", "12-1.7.10") - } + combineWith(sourceSets.main) minecraftForge { loader project.forge_version mixinConfig "modid.mixins.json" } + + defaultRemapJar = true } configurations { diff --git a/testing/1.8.9-Forge-Fabric/build.gradle b/testing/1.8.9-Forge-Fabric/build.gradle index e81fd749..d71ff88c 100644 --- a/testing/1.8.9-Forge-Fabric/build.gradle +++ b/testing/1.8.9-Forge-Fabric/build.gradle @@ -4,9 +4,12 @@ plugins { } group 'com.example' -archivesBaseName 'UniminedExampleMod' version '1.0-SNAPSHOT' +base { + archivesName = 'UniminedExampleMod' +} + // this is just here so we can test the outputs easier and clean between tests unimined.useGlobalCache = false @@ -24,18 +27,6 @@ sourceSets { } } -tasks.register("fabricJar", Jar) { - from sourceSets.fabric.output, sourceSets.main.output - - archiveClassifier = "fabric" -} - -tasks.register("forgeJar", Jar) { - from sourceSets.forge.output, sourceSets.main.output - - archiveClassifier = "forge" -} - repositories { maven { name = "wagyourtail releases" @@ -60,29 +51,24 @@ unimined.minecraft { } unimined.minecraft(sourceSets.fabric) { - version project.minecraft_version - - mappings { - searge() - mcp("stable", "22-1.8.9") - } + combineWith(sourceSets.main) legacyFabric { loader project.fabric_version } + + defaultRemapJar = true } unimined.minecraft(sourceSets.forge) { - version project.minecraft_version - - mappings { - mcp("stable", "22-1.8.9") - } + combineWith(sourceSets.main) minecraftForge { loader project.forge_version mixinConfig "modid.mixins.json" } + + defaultRemapJar = true } configurations { diff --git a/testing/Fabric-Interface-Injection/build.gradle b/testing/Fabric-Interface-Injection/build.gradle new file mode 100644 index 00000000..4e1275f0 --- /dev/null +++ b/testing/Fabric-Interface-Injection/build.gradle @@ -0,0 +1,88 @@ +plugins { + id 'java' + id 'xyz.wagyourtail.unimined' // version '1.0.0' +} + +group 'com.example' +version '1.0-SNAPSHOT' + +base { + archivesName.set('UniminedExampleMod') +} + +// this is just here so we can test the outputs easier and clean between tests +unimined.useGlobalCache = false + +sourceCompatibility = JavaVersion.VERSION_1_8 +targetCompatibility = JavaVersion.VERSION_1_8 + +sourceSets { + fabric { + compileClasspath += sourceSets.main.output + runtimeClasspath += sourceSets.main.output + } +} + +repositories { + mavenCentral() + maven { + name = "sponge" + url = "https://repo.spongepowered.org/maven" + } +} + +unimined.minecraft { + version project.minecraft_version + + mappings { + intermediary() + mojmap() + + devFallbackNamespace "intermediary" + } + + defaultRemapJar = false +} + +unimined.minecraft(sourceSets.fabric) { + version project.minecraft_version + + mappings { + mojmap() + } + + fabric { + loader project.fabric_version + } +} + +configurations { + mainImplementation + implementation { + extendsFrom fabricImplementation + } +} + +sourceSets { + main { + compileClasspath += configurations.mainImplementation + runtimeClasspath += configurations.mainImplementation + } +} + +dependencies { + // we need this in main where it isn't by default + mainImplementation "org.spongepowered:mixin:0.8.5-SNAPSHOT" +} + +jar { + enabled = false +} + +processFabricResources { + inputs.property "version", project.version + + filesMatching("fabric.mod.json") { + expand "version": project.version + } +} \ No newline at end of file diff --git a/testing/Fabric-Interface-Injection/gradle.properties b/testing/Fabric-Interface-Injection/gradle.properties new file mode 100644 index 00000000..2a999ef3 --- /dev/null +++ b/testing/Fabric-Interface-Injection/gradle.properties @@ -0,0 +1,4 @@ +org.gradle.jvmargs = -Xmx2G + +minecraft_version = 1.14.4 +fabric_version = 0.14.24 \ No newline at end of file diff --git a/testing/Fabric-Interface-Injection/gradle/wrapper/gradle-wrapper.properties b/testing/Fabric-Interface-Injection/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..42defcc9 --- /dev/null +++ b/testing/Fabric-Interface-Injection/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip +networkTimeout=10000 +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/testing/Fabric-Interface-Injection/gradlew b/testing/Fabric-Interface-Injection/gradlew new file mode 100755 index 00000000..79a61d42 --- /dev/null +++ b/testing/Fabric-Interface-Injection/gradlew @@ -0,0 +1,244 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/testing/Fabric-Interface-Injection/gradlew.bat b/testing/Fabric-Interface-Injection/gradlew.bat new file mode 100644 index 00000000..93e3f59f --- /dev/null +++ b/testing/Fabric-Interface-Injection/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/testing/Fabric-Interface-Injection/settings.gradle b/testing/Fabric-Interface-Injection/settings.gradle new file mode 100644 index 00000000..dbfcacdf --- /dev/null +++ b/testing/Fabric-Interface-Injection/settings.gradle @@ -0,0 +1,31 @@ + +pluginManagement { + repositories { + mavenCentral() + maven { + url = "https://maven.neoforged.net/releases" + } + maven { + url = "https://maven.minecraftforge.net/" + } + maven { + url = "https://maven.fabricmc.net/" + } + maven { + url = "https://maven.wagyourtail.xyz/releases" + } + maven { + url = "https://maven.wagyourtail.xyz/snapshots" + } + gradlePluginPortal() { + content { + excludeGroup("org.apache.logging.log4j") + } + } + } +} + +// so we can use the unimined directly provided by the super project +includeBuild('../../') + +rootProject.name = 'Fabric-Interface-Injection' \ No newline at end of file diff --git a/testing/Fabric-Interface-Injection/src/fabric/java/com/example/fabric/ExampleInterface.java b/testing/Fabric-Interface-Injection/src/fabric/java/com/example/fabric/ExampleInterface.java new file mode 100644 index 00000000..086ab183 --- /dev/null +++ b/testing/Fabric-Interface-Injection/src/fabric/java/com/example/fabric/ExampleInterface.java @@ -0,0 +1,5 @@ +package com.example.fabric; + +public interface ExampleInterface { + String test(); +} \ No newline at end of file diff --git a/testing/Fabric-Interface-Injection/src/fabric/java/com/example/fabric/ExampleModFabric.java b/testing/Fabric-Interface-Injection/src/fabric/java/com/example/fabric/ExampleModFabric.java new file mode 100644 index 00000000..51bca524 --- /dev/null +++ b/testing/Fabric-Interface-Injection/src/fabric/java/com/example/fabric/ExampleModFabric.java @@ -0,0 +1,13 @@ +package com.example.fabric; + +import com.example.ExampleMod; +import net.fabricmc.api.ModInitializer; + +public class ExampleModFabric implements ModInitializer { + + @Override + public void onInitialize() { + ExampleMod.LOGGER.info("Hello from Fabric!"); + } + +} diff --git a/testing/Fabric-Interface-Injection/src/fabric/resources/fabric.mod.json b/testing/Fabric-Interface-Injection/src/fabric/resources/fabric.mod.json new file mode 100644 index 00000000..3a58e439 --- /dev/null +++ b/testing/Fabric-Interface-Injection/src/fabric/resources/fabric.mod.json @@ -0,0 +1,42 @@ +{ + "schemaVersion": 1, + "id": "modid", + "version": "${version}", + + "name": "Example Mod", + "description": "This is an example description! Tell everyone what your mod is about!", + "authors": [ + "Me!" + ], + "contact": { + "homepage": "https://fabricmc.net/", + "sources": "https://github.com/FabricMC/fabric-example-mod" + }, + + "license": "CC0-1.0", + "icon": "assets/modid/icon.png", + + "environment": "*", + "entrypoints": { + "main": [ + "com.example.fabric.ExampleModFabric" + ] + }, + "mixins": [ + "modid.mixins.json" + ], + + "depends": { + "fabricloader": ">=0.14.19", + "minecraft": "~1.16.5", + "java": ">=8" + }, + + "custom": { + "loom:injected_interfaces": { + "net/minecraft/class_161": [ + "com/example/fabric/ExampleInterface" + ] + } + } +} \ No newline at end of file diff --git a/testing/Fabric-Interface-Injection/src/main/java/com/example/ExampleMod.java b/testing/Fabric-Interface-Injection/src/main/java/com/example/ExampleMod.java new file mode 100644 index 00000000..ef3ef249 --- /dev/null +++ b/testing/Fabric-Interface-Injection/src/main/java/com/example/ExampleMod.java @@ -0,0 +1,9 @@ +package com.example; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class ExampleMod { + public static final Logger LOGGER = LogManager.getLogger("modid"); + +} diff --git a/testing/Fabric-Interface-Injection/src/main/java/com/example/mixin/ExampleMixin.java b/testing/Fabric-Interface-Injection/src/main/java/com/example/mixin/ExampleMixin.java new file mode 100644 index 00000000..428dc930 --- /dev/null +++ b/testing/Fabric-Interface-Injection/src/main/java/com/example/mixin/ExampleMixin.java @@ -0,0 +1,17 @@ +package com.example.mixin; + +import com.example.ExampleMod; +import net.minecraft.client.gui.screens.TitleScreen; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(TitleScreen.class) +public class ExampleMixin { + + @Inject(method = "init()V", at = @At("HEAD")) + private void init(CallbackInfo info) { + ExampleMod.LOGGER.info("This line is printed by an example mod mixin!"); + } +} diff --git a/testing/Fabric-Interface-Injection/src/main/resources/modid.mixins.json b/testing/Fabric-Interface-Injection/src/main/resources/modid.mixins.json new file mode 100644 index 00000000..a08e9c51 --- /dev/null +++ b/testing/Fabric-Interface-Injection/src/main/resources/modid.mixins.json @@ -0,0 +1,14 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "com.example.mixin", + "compatibilityLevel": "JAVA_8", + "mixins": [ + ], + "client": [ + "ExampleMixin" + ], + "injectors": { + "defaultRequire": 1 + } +} \ No newline at end of file diff --git a/testing/b1.7.3-Babric-Modloader/build.gradle b/testing/b1.7.3-Babric-Modloader/build.gradle index 438ea0f0..07f46737 100644 --- a/testing/b1.7.3-Babric-Modloader/build.gradle +++ b/testing/b1.7.3-Babric-Modloader/build.gradle @@ -4,9 +4,12 @@ plugins { } group 'com.example' -archivesBaseName 'UniminedExampleMod' version '1.0-SNAPSHOT' +base { + archivesName = 'UniminedExampleMod' +} + // this is just here so we can test the outputs easier and clean between tests unimined.useGlobalCache = false @@ -24,18 +27,6 @@ sourceSets { } } -tasks.register("babricJar", Jar) { - from sourceSets.babric.output, sourceSets.main.output - - archiveClassifier = "babric" -} - -tasks.register("modloaderJar", Jar) { - from sourceSets.modloader.output, sourceSets.main.output - - archiveClassifier = "modloader" -} - repositories { mavenLocal() maven { @@ -50,7 +41,7 @@ unimined.minecraft() { mappings { babricIntermediary() - barn(9) + biny("a00e3b0") } minecraftRemapper.config { @@ -64,11 +55,11 @@ unimined.minecraft(sourceSets.babric) { version "b1.7.3" mappings { - barn(9) + biny("a00e3b0") } babric { - loader "0.14.19-babric.1" + loader "0.14.24-babric.1" } minecraftRemapper.config { @@ -105,15 +96,10 @@ jar { enabled = false } - -processResources { +processBabricResources { inputs.property "version", project.version - filesMatching("META-INF/mods.toml") { - expand "version": project.version - } - - filesMatching("mcmod.info") { + filesMatching("fabric.mod.json") { expand "version": project.version } } \ No newline at end of file