From 281a6426208b770c3983eb23354ae0cbf8e89c12 Mon Sep 17 00:00:00 2001 From: Mgazul Date: Fri, 22 Mar 2024 17:33:20 +0800 Subject: [PATCH] Update to Forge 36.2.42 --- README.md | 4 +- build.gradle | 181 +++++++--- buildSrc/build.gradle | 8 +- .../forge/tasks/BytecodeFinder.groovy | 17 +- .../forge/tasks/CheckATs.groovy | 265 --------------- .../forge/tasks/CheckExcs.groovy | 88 ----- .../forge/tasks/CheckPatches.groovy | 219 ------------- .../forge/tasks/CheckSAS.groovy | 49 --- .../minecraftforge/forge/tasks/Crowdin.groovy | 73 +++++ .../forge/tasks/DownloadLibraries.groovy | 56 ++-- .../forge/tasks/FieldCompareFinder.groovy | 2 +- .../forge/tasks/InheritanceData.groovy | 49 +++ .../forge/tasks/MergeJars.groovy | 44 +++ .../forge/tasks/ObjectTarget.groovy | 2 + .../tasks/SetupCheckJarCompatibility.groovy | 47 +++ .../minecraftforge/forge/tasks/Util.groovy | 40 +-- .../forge/tasks/checks/CheckATs.groovy | 310 ++++++++++++++++++ .../forge/tasks/checks/CheckExcs.groovy | 93 ++++++ .../forge/tasks/checks/CheckMode.groovy | 9 + .../forge/tasks/checks/CheckPatches.groovy | 177 ++++++++++ .../forge/tasks/checks/CheckSAS.groovy | 110 +++++++ .../forge/tasks/checks/CheckTask.groovy | 109 ++++++ gradle/wrapper/gradle-wrapper.properties | 2 +- licenses/LICENSE-header.txt | 18 +- settings.gradle | 14 + src/fmllauncher/resources/libraries.txt | 15 +- .../resources/META-INF/accesstransformer.cfg | 2 +- .../resources/coremods/add_bouncer_method.js | 70 +++- src/main/resources/forge.exc | 2 +- src/main/resources/forge.sas | 7 +- 30 files changed, 1321 insertions(+), 761 deletions(-) delete mode 100644 buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/CheckATs.groovy delete mode 100644 buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/CheckExcs.groovy delete mode 100644 buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/CheckPatches.groovy delete mode 100644 buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/CheckSAS.groovy create mode 100644 buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/Crowdin.groovy create mode 100644 buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/InheritanceData.groovy create mode 100644 buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/MergeJars.groovy create mode 100644 buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/SetupCheckJarCompatibility.groovy create mode 100644 buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/checks/CheckATs.groovy create mode 100644 buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/checks/CheckExcs.groovy create mode 100644 buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/checks/CheckMode.groovy create mode 100644 buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/checks/CheckPatches.groovy create mode 100644 buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/checks/CheckSAS.groovy create mode 100644 buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/checks/CheckTask.groovy diff --git a/README.md b/README.md index 20eece42ff..a152e1baf9 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ [![](https://img.shields.io/jenkins/build?jobUrl=https%3A%2F%2Fci.codemc.io%2Fjob%2FMohistMC%2Fjob%2FMohist-1.16.5)](https://ci.codemc.io/job/MohistMC/job/Mohist-1.16.5) [![](https://img.shields.io/github/stars/MohistMC/Mohist.svg?label=Stars&logo=github)](https://github.com/MohistMC/Mohist/stargazers) [![](https://img.shields.io/github/license/MohistMC/Mohist?)](https://github.com/MohistMC/Mohist/blob/1.16.5/LICENSE) -[![](https://img.shields.io/badge/Forge-1.16.5--36.2.41-brightgreen.svg?colorB=26303d&logo=Conda-Forge)](https://files.minecraftforge.net/net/minecraftforge/forge/index_1.16.5.html) +[![](https://img.shields.io/badge/Forge-1.16.5--36.2.42-brightgreen.svg?colorB=26303d&logo=Conda-Forge)](https://files.minecraftforge.net/net/minecraftforge/forge/index_1.16.5.html) [![](https://img.shields.io/badge/AdoptOpenJDK-11.0.15+10-brightgreen.svg?colorB=469C00&logo=java)](https://adoptopenjdk.net/?variant=openjdk11&) [![](https://img.shields.io/badge/Gradle-7.3.3-brightgreen.svg?colorB=469C00&logo=gradle)](https://docs.gradle.org/7.3.3/release-notes.html) [![](https://img.shields.io/bstats/servers/6762?label=bStats)](https://bstats.org/plugin/server-implementation/Mohist/6762) @@ -23,7 +23,7 @@ Progress ------ -- [x] Rectify Forge [09f1b8d43](https://github.com/MinecraftForge/MinecraftForge/commit/09f1b8d43) +- [x] Rectify Forge [54233da14](https://github.com/MinecraftForge/MinecraftForge/commit/54233da14) - [x] Start patch * CraftBukkit(f4830a16) * Bukkit(7e29f765) diff --git a/build.gradle b/build.gradle index 517352c633..ad5d6cca36 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { maven { url = "https://maven.minecraftforge.net/" } } dependencies { - classpath 'net.minecraftforge.gradle:ForgeGradle:5.1.+' + classpath 'net.minecraftforge.gradle:ForgeGradle:[6.0.16,6.2)' } } @@ -13,38 +13,45 @@ import com.mohistmc.tasks.JenkinsNumber import groovy.json.JsonBuilder import org.apache.tools.ant.filters.ReplaceTokens +import net.minecraftforge.forge.tasks.checks.CheckATs +import net.minecraftforge.forge.tasks.checks.CheckExcs +import net.minecraftforge.forge.tasks.checks.CheckPatches +import net.minecraftforge.forge.tasks.checks.CheckSAS +import net.minecraftforge.forge.tasks.checks.CheckTask import java.nio.file.Files import net.minecraftforge.forge.tasks.* import static net.minecraftforge.forge.tasks.Util.* +import net.minecraftforge.gradle.common.tasks.ApplyBinPatches +import net.minecraftforge.gradle.common.tasks.CheckJarCompatibility import net.minecraftforge.gradle.common.tasks.DownloadMavenArtifact import net.minecraftforge.gradle.common.tasks.ExtractInheritance +import net.minecraftforge.gradle.patcher.tasks.GeneratePatches import net.minecraftforge.gradle.common.tasks.SignJar import net.minecraftforge.gradle.patcher.tasks.ReobfuscateJar import org.gradle.plugins.ide.eclipse.model.SourceFolder plugins { - id 'net.minecraftforge.gradleutils' version '1.+' - id 'com.github.ben-manes.versions' version '0.36.0' + id 'org.cadixdev.licenser' version '0.6.1' + id 'net.minecraftforge.gradleutils' version '[2.2,2.3)' + id 'com.github.ben-manes.versions' version '0.46.0' id 'eclipse' + id 'de.undercouch.download' version '5.4.0' } Util.init() //Init all our extension methods println('Java: ' + System.getProperty('java.version') + ' JVM: ' + System.getProperty('java.vm.version') + '(' + System.getProperty('java.vendor') + ') Arch: ' + System.getProperty('os.arch')) -if (System.getProperty("_JAVA_OPTIONS") == null) { - System.setProperty("_JAVA_OPTIONS", "-Xmx3500M") -} - - ext { JAR_SIGNER = null MAPPING_CHANNEL = 'official' MAPPING_VERSION = '1.16.5' MC_VERSION = '1.16.5' MCP_VERSION = '20210115.111550' + ASM_VERSION = '9.6' SPI_VERSION = '3.2.0' + GIT_INFO = gradleutils.gitInfo MOHIST_VERSION = JenkinsNumber.info() } @@ -101,6 +108,7 @@ project(':mohist') { apply plugin: 'eclipse' apply plugin: 'net.minecraftforge.gradle.patcher' + java.toolchain.languageVersion = JavaLanguageVersion.of(8) group = 'com.mohistmc' sourceSets { @@ -154,15 +162,14 @@ project(':mohist') { // ForgeMC is a unique identifier for every MC version we have supported. // Essentially, the same as the old, except dropping the first number, and the builds are no longer unique. MCP_ARTIFACT = project(':mcp').mcp.config.get() - SPECIAL_SOURCE = 'net.md-5:SpecialSource:1.10.0' SPECIAL_SOURCE_MCP = 'net.md-5:SpecialSource:1.8.5' VERSION_JSON = project(':mcp').file('build/mcp/downloadJson/version.json') BINPATCH_TOOL = 'net.minecraftforge:binarypatcher:1.0.12:fatjar' - INSTALLER_TOOLS = 'net.minecraftforge:installertools:1.2.6' + INSTALLER_TOOLS = 'net.minecraftforge:installertools:1.3.0' } version = "1.16.5-" + MOHIST_VERSION - String forge_version = "36.2.41" + String forge_version = "36.2.42" println('Forge Version: ' + forge_version) patcher { @@ -181,6 +188,12 @@ project(':mohist') { main 'net.minecraftforge.userdev.LaunchTesting' workingDirectory project.file('run') + // Support Forge devs running on Java 8 and up, including 16 + jvmArgs '-XX:+IgnoreUnrecognizedVMOptions' + jvmArgs '--add-exports=java.base/sun.security.util=ALL-UNNAMED' + jvmArgs '--add-exports=jdk.naming.dns/com.sun.jndi.dns=java.naming' + jvmArgs '--add-opens=java.base/java.util.jar=ALL-UNNAMED' + environment 'target', 'fmldevserver' environment 'MC_VERSION', MC_VERSION @@ -206,6 +219,12 @@ project(':mohist') { main 'net.minecraftforge.userdev.LaunchTesting' workingDirectory project.file('run') + // Support Forge devs running on Java 8 and up, including 16 + jvmArgs '-XX:+IgnoreUnrecognizedVMOptions' + jvmArgs '--add-exports=java.base/sun.security.util=ALL-UNNAMED' + jvmArgs '--add-exports=jdk.naming.dns/com.sun.jndi.dns=java.naming' + jvmArgs '--add-opens=java.base/java.util.jar=ALL-UNNAMED' + environment 'target', 'fmldevdata' environment 'assetIndex', '{asset_index}' environment 'assetDirectory', downloadAssets.output @@ -230,6 +249,33 @@ project(':mohist') { } } + afterEvaluate { + if (!patcher.srgPatches) { + srg2mcpClean { + dependsOn = [] + input = project(':mcp').setupMCP.output + } + userdevJar { + onlyIf = { t -> true } + } + def patches = project.file('build/genPatchesForUserdev/output/') + patches.mkdirs() + def genPatchesForUserdev = tasks.register('genPatchesForUserdev', GeneratePatches){ + base = project(':mcp').setupMCP.output + modified = applyRangeMapBase.output + originalPrefix = genPatches.originalPrefix + modifiedPrefix = genPatches.modifiedPrefix + output = patches + autoHeader true + lineEnding = '\n' + } + bakePatches { + dependsOn = [] + input = genPatchesForUserdev.get().output + } + } + } + ext { MANIFESTS = [ '/': [ @@ -312,11 +358,11 @@ project(':mohist') { fmllauncherImplementation.extendsFrom(installer, implementation) } dependencies { - installer 'org.ow2.asm:asm:9.5' - installer 'org.ow2.asm:asm-commons:9.5' - installer 'org.ow2.asm:asm-tree:9.5' - installer 'org.ow2.asm:asm-util:9.5' - installer 'org.ow2.asm:asm-analysis:9.5' + installer 'org.ow2.asm:asm:' + ASM_VERSION + installer 'org.ow2.asm:asm-commons:' + ASM_VERSION + installer 'org.ow2.asm:asm-tree:' + ASM_VERSION + installer 'org.ow2.asm:asm-util:' + ASM_VERSION + installer 'org.ow2.asm:asm-analysis:' + ASM_VERSION installer 'cpw.mods:modlauncher:mohist-8.1.3' installer 'cpw.mods:grossjava9hacks:1.3.3' installer 'net.minecraftforge:accesstransformers:3.0.1' @@ -392,13 +438,6 @@ project(':mohist') { } } - tasks.named('compileFmllauncherJava') { - java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 - } - } - task downloadLibraries(type: DownloadLibraries, dependsOn: ':mcp:setupMCP') { input = VERSION_JSON output = rootProject.file('build/libraries/') @@ -406,6 +445,7 @@ project(':mohist') { task extractInheritance(type: ExtractInheritance, dependsOn: [genJoinedBinPatches, downloadLibraries]) { tool = INSTALLER_TOOLS + ':fatjar' + args.add '--annotations' input = genJoinedBinPatches.cleanJar libraries.addAll downloadLibraries.librariesOutput.map { rf -> Files.readAllLines(rf.asFile.toPath()).stream().map(File::new).collect(java.util.stream.Collectors.toList()) } } @@ -446,29 +486,97 @@ project(':mohist') { } } - task checkATs(type: CheckATs, dependsOn: [extractInheritance, createSrg2Mcp]) { - inheritance = extractInheritance.output + tasks.register('checkAll') { + dependsOn 'checkLicenses' + group = 'checks' + } + tasks.register('checkAllAndFix') { + dependsOn 'findFieldInstanceChecks', 'checkLicenses' + group = 'checks' + } + + CheckTask.registerTask(tasks, 'ATs', CheckATs) { + dependsOn extractInheritance, createSrg2Mcp ats.from patcher.accessTransformers + inheritance = extractInheritance.output mappings = createSrg2Mcp.output } - task checkSAS(type: CheckSAS, dependsOn: extractInheritance) { - inheritance = extractInheritance.output + CheckTask.registerTask(tasks, 'SAS', CheckSAS) { + dependsOn extractInheritance sass.from patcher.sideAnnotationStrippers + inheritance = extractInheritance.output } - task checkExcs(type: CheckExcs, dependsOn: jar) { - binary = jar.archiveFile + CheckTask.registerTask(tasks, 'Excs', CheckExcs) { + dependsOn jar + binary = jar.archiveFile.get().asFile excs.from patcher.excs } - task checkAll(dependsOn: [checkATs, checkSAS, checkExcs, findFieldInstanceChecks]){} + CheckTask.registerTask(tasks, 'Patches', CheckPatches) { + dependsOn genPatches + patchDir = file("$rootDir/patches") + patchesWithS2SArtifact = [ + 'minecraft/net/minecraft/client/renderer/ViewFrustum.java.patch', + 'minecraft/net/minecraft/data/BlockModelDefinition.java.patch', + ] + } genPatches { autoHeader true lineEnding = '\n' } + def baseForgeVersionProperty = project.objects.property(String) + baseForgeVersionProperty.set(project.provider { getLatestForgeVersion(MC_VERSION) }.map { MC_VERSION + '-' + it }) + baseForgeVersionProperty.finalizeValueOnRead() + def jarCompatibilityTaskSetup = { task -> + task.group = 'jar compatibility' + task.onlyIf { + baseForgeVersionProperty.getOrNull() != null + } + } + + tasks.register('setupCheckJarCompatibility', SetupCheckJarCompatibility) { + inputVersion = baseForgeVersionProperty + } + + tasks.register('applyBaseCompatibilityJarBinPatches', ApplyBinPatches) { + jarCompatibilityTaskSetup(it) + + clean = project.tasks.createJoinedSRG.output + patch = project.tasks.named('setupCheckJarCompatibility').flatMap { it.baseBinPatchesOutput } + output = project.layout.buildDirectory.dir(name).map { it.file('output.jar') } + } + + tasks.register('mergeBaseForgeJar', MergeJars) { + jarCompatibilityTaskSetup(it) + + inputJars.from(project.tasks.named('applyBaseCompatibilityJarBinPatches').flatMap { it.output }) + inputJars.from(baseForgeVersionProperty.map { inputVersion -> + def output = project.layout.buildDirectory.dir(name).map { it.file("forge-${inputVersion}-universal.jar") }.get().asFile + project.rootProject.extensions.download.run { + src "https://maven.minecraftforge.net/net/minecraftforge/forge/${inputVersion}/forge-${inputVersion}-universal.jar" + dest output + } + return output + }) + } + + tasks.register('checkJarCompatibility', CheckJarCompatibility) { + jarCompatibilityTaskSetup(it) + dependsOn 'setupCheckJarCompatibility' + + baseJar = project.tasks.named('mergeBaseForgeJar').flatMap { it.output } + baseLibraries.from(project.tasks.named('createJoinedSRG').flatMap { it.output }) + + inputJar = project.tasks.named('reobfJar').flatMap { it.output } + + commonLibraries.from(project.configurations.minecraftImplementation) + commonLibraries.from(project.configurations.installer) + } + task launcherJson(dependsOn: ['signUniversalJar', 'signLauncherJar']) { inputs.file universalJar.archiveFile inputs.file { launcherJar.archiveFile } @@ -564,7 +672,7 @@ project(':mohist') { task launcherJar(type: Jar) { archiveBaseName = 'mohist' - archiveClassifier = 'launcher' + archiveClassifier.set('launcher') from sourceSets.fmllauncher.output doFirst { def classpath = new StringBuilder() @@ -599,7 +707,7 @@ project(':mohist') { } task downloadInstaller(type: DownloadMavenArtifact) { - artifact = 'net.minecraftforge:installer:2.1.+:shrunk' + artifact = 'net.minecraftforge:installer:2.2.+:fatjar' changing = true } @@ -733,11 +841,7 @@ project(':mohist') { task userdevExtras(type:Jar) { dependsOn classes from sourceSets.userdev.output - classifier 'userdev-temp' - } - - reobfJar { - tool = SPECIAL_SOURCE + ':shaded' + archiveClassifier.set('userdev-temp') } task userdevExtrasReobf(type: ReobfuscateJar) { @@ -745,7 +849,6 @@ project(':mohist') { input = tasks.userdevExtras.archiveFile classpath.from project.configurations.userdevCompileClasspath srg = tasks.createMcp2Srg.output - tool = SPECIAL_SOURCE + ':shaded' } userdevJar { @@ -834,4 +937,4 @@ project(':mohist') { task setup() { dependsOn ':clean:extractMapped' dependsOn ':mohist:extractMapped' //These must be strings so that we can do lazy resolution. Else we need evaluationDependsOnChildren above -} +} \ No newline at end of file diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index 6d1728d06f..ea4857869d 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -1,7 +1,11 @@ repositories { + maven { url = 'https://maven.minecraftforge.net/' } mavenCentral() } dependencies { - implementation 'org.ow2.asm:asm:9.0' - implementation 'org.ow2.asm:asm-tree:9.0' + implementation 'org.ow2.asm:asm:9.6' + implementation 'org.ow2.asm:asm-tree:9.6' + implementation 'net.minecraftforge:srgutils:0.4.+' + implementation 'commons-io:commons-io:2.12.0' + implementation 'com.google.code.gson:gson:2.10' } \ No newline at end of file diff --git a/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/BytecodeFinder.groovy b/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/BytecodeFinder.groovy index 6f04b3bada..d217608de8 100644 --- a/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/BytecodeFinder.groovy +++ b/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/BytecodeFinder.groovy @@ -4,7 +4,7 @@ import groovy.json.JsonBuilder import org.gradle.api.DefaultTask import org.gradle.api.file.RegularFileProperty import org.gradle.api.tasks.InputFile -import org.gradle.api.tasks.OutputFile +import org.gradle.api.tasks.Internal import org.gradle.api.tasks.TaskAction import org.objectweb.asm.ClassReader import org.objectweb.asm.Opcodes @@ -17,11 +17,14 @@ import java.util.zip.ZipInputStream abstract class BytecodeFinder extends DefaultTask { @InputFile abstract RegularFileProperty getJar() - @OutputFile abstract RegularFileProperty getOutput() + // It should be fine to mark the output as internal as we want to control when we run it anyways. + // This also shuts Gradle 8 up about implicit task dependencies. + @Internal abstract RegularFileProperty getOutput() BytecodeFinder() { output.convention(project.layout.buildDirectory.dir(name).map { it.file("output.json") }) } + @TaskAction protected void exec() { Util.init() @@ -29,7 +32,7 @@ abstract class BytecodeFinder extends DefaultTask { def outputFile = output.get().asFile if (outputFile.exists()) outputFile.delete() - + pre() jar.get().asFile.withInputStream { i -> @@ -44,17 +47,17 @@ abstract class BytecodeFinder extends DefaultTask { } } } - + post() outputFile.text = new JsonBuilder(getData()).toPrettyString() } - - + + protected process(ClassNode node) { if (node.fields != null) node.fields.each { process(node, it) } if (node.methods != null) node.methods.each { process(node, it) } } - + protected pre() {} protected process(ClassNode parent, FieldNode node) {} protected process(ClassNode parent, MethodNode node) {} diff --git a/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/CheckATs.groovy b/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/CheckATs.groovy deleted file mode 100644 index 3be58925cf..0000000000 --- a/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/CheckATs.groovy +++ /dev/null @@ -1,265 +0,0 @@ -package net.minecraftforge.forge.tasks - -import org.gradle.api.DefaultTask -import org.gradle.api.file.ConfigurableFileCollection -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.tasks.InputFile -import org.gradle.api.tasks.InputFiles -import org.gradle.api.tasks.Optional -import org.gradle.api.tasks.TaskAction -import org.objectweb.asm.Opcodes - -abstract class CheckATs extends DefaultTask { - @InputFile abstract RegularFileProperty getInheritance() - @InputFiles abstract ConfigurableFileCollection getAts() - @InputFile @Optional abstract RegularFileProperty getMappings() - - @TaskAction - protected void exec() { - def mappings = mappings.map { IMappingFile.load(it.asFile) }.getOrNull() - Util.init() - def parse = { line -> - def idx = line.indexOf('#') - def comment = idx == -1 ? null : line.substring(idx) - if (idx != -1) line = line.substring(0, idx - 1) - def (modifier, cls, desc) = (line.trim() + ' ').split(' ', -1) - def key = cls + (desc.isEmpty() ? '' : ' ' + desc) - return [modifier, cls, desc, comment, key] - } - def accessLevel = { access -> - if ((access & Opcodes.ACC_PUBLIC) != 0) return 3 - if ((access & Opcodes.ACC_PROTECTED) != 0) return 2 - if ((access & Opcodes.ACC_PRIVATE) != 0) return 0 - return 1 - } - def accessStr = { access -> - if (access.endsWith('-f') || access.endsWith('+f')) - return 4 - switch (access.toLowerCase()) { - case 'public': return 3 - case 'protected': return 2 - case 'default': return 1 - case 'private': return 0 - default: return -1 - } - } - def json = inheritance.get().asFile.json() - - ats.each { f -> - TreeMap lines = [:] - def group = null - for (def line : f.readLines()) { - if (line.isEmpty()) continue - if (line.startsWith('#group ')) { - def (modifier, cls, desc, comment, key) = parse.call(line.substring(7)) - - if (desc != '*' && desc != '*()' && desc != '') - throw new IllegalStateException('Invalid group: ' + line) - - group = [modifier: modifier, cls: cls, desc: desc, comment: comment, - existing: [] as Set, - children: [] as TreeSet, - group: true - ] - if (lines.containsKey(key)) - throw new IllegalStateException('Duplicate group: ' + line) - - lines[key] = group - } else if (group != null) { - if (line.startsWith('#endgroup')) { - group = null - } else { - def (modifier, cls, desc, comment, key) = parse.call(line) - group['existing'].add(key) - } - } else if (line.startsWith('#endgroup')) { - throw new IllegalStateException('Invalid group ending: ' + line) - } else if (line.startsWith('#')) { - //Nom - } else { - def (modifier, cls, desc, comment, key) = parse.call(line) - if (lines.containsKey(key)) { - println('Duplicate: ' + line) - continue - } - lines[key] = [modifier: modifier, cls: cls, desc: desc, comment: comment, group: false] - } - } - - // Process Groups, this will remove any entries outside the group that is covered by the group - for (def key : new ArrayList<>(lines.keySet())) { - def entry = lines.get(key) - if (entry != null && entry['group']) { - def cls = entry['cls'] - def jcls = json.get(cls.replaceAll('\\.', '/')) - if (jcls == null) { - lines.remove(key) - println('Invalid Group: ' + key) - } else if ('*' == entry['desc']) { - if (!jcls.containsKey('fields')) { - lines.remove(key) - println('Invalid Group, Class has no fields: ' + key) - } else { - jcls['fields'].each { field, value -> - def fkey = cls + ' ' + field - if (accessLevel.call(value['access']) < accessStr.call(entry['modifier'])) { - if (lines.containsKey(fkey)) { - lines.remove(fkey) - } else if (!entry['existing'].contains(fkey)) { - println('Added: ' + fkey) - } - entry['children'].add(fkey) - } else if (lines.containsKey(fkey)) { - lines.remove(fkey) - println('Removed: ' + fkey) - } - } - entry['existing'].stream().findAll{ !entry['children'].contains(it) }.each{ println('Removed: ' + it) } - } - } else if ('*()' == entry['desc']) { - if (!jcls.containsKey('methods')) { - lines.remove(key) - println('Invalid Group, Class has no methods: ' + key) - } else { - jcls['methods'].each{ mtd, value -> - if (mtd.startsWith('') || mtd.startsWith('lambda$')) - return - key = cls + ' ' + mtd.replace(' ', '') - if (accessLevel.call(value['access']) < accessStr.call(entry['modifier'])) { - if (lines.containsKey(key)) { - lines.remove(key) - } else if (!entry['existing'].contains(key)) { - println('Added: ' + key) - } - entry['children'].add(key) - } else if (lines.containsKey(key)) { - lines.remove(key) - println('Removed: ' + key) - } - } - entry['existing'].stream().findAll{ !entry['children'].contains(it) }.each{ println('Removed: ' + it) } - } - } else if ('' == entry['desc']) { //Make all public non-abstract subclasses - json.each{ tcls,value -> - if (!value.containsKey('methods') || ((value['access'] & Opcodes.ACC_ABSTRACT) != 0)) - return - def parents = [] as Set - def parent = tcls - while (parent != null && json.containsKey(parent)) { - parents.add(parent) - def p = json[parent] - parent = p == null ? null : p['superName'] - } - if (parents.contains(cls.replaceAll('\\.', '/'))) { - value['methods'].each{ mtd, v -> - if (mtd.startsWith('')) { - def child = tcls.replaceAll('/', '\\.') + ' ' + mtd.replace(' ', '') - if (accessLevel.call(v['access']) < 3) { - if (lines.containsKey(child)) { - lines.remove(child) - } else if (!entry['existing'].contains(child)) { - println('Added: ' + child) - } - entry['children'].add(child) - } else if (lines.containsKey(child)) { - lines.remove(child) - println('Removed: ' + child) - } - } - } - } - } - entry['existing'].stream().findAll{ !entry['children'].contains(it) }.each{ println('Removed: ' + it) } - } - } - } - - // Process normal lines, remove invalid and remove narrowing - for (def key : new ArrayList<>(lines.keySet())) { - def entry = lines.get(key) - if (entry != null && !entry['group']) { - def cls = entry['cls'] - def jcls = json.get(cls.replaceAll('\\.', '/')) - if (jcls == null) { - lines.remove(key) - println('Invalid: ' + key) - } else if (entry['desc'] == '') { - if (accessLevel.call(jcls['access']) > accessStr.call(entry['modifier']) && (entry.comment == null || !entry.comment.startsWith('#force '))) { - lines.remove(key) - println('Invalid Narrowing: ' + key) - } - } else if (!entry['desc'].contains('(')) { - if (!jcls.containsKey('fields') || !jcls['fields'].containsKey(entry['desc'])) { - lines.remove(key) - println('Invalid: ' + key) - } else { - def value = jcls['fields'][entry['desc']] - if (accessLevel.call(value['access']) > accessStr.call(entry['modifier']) && (entry.comment == null || !entry.comment.startsWith('#force '))) { - lines.remove(key) - println('Invalid Narrowing: ' + key) - println(entry.comment) - } - } - } else { - def jdesc = entry['desc'].replace('(', ' (') - if (!jcls.containsKey('methods') || !jcls['methods'].containsKey(jdesc)) { - lines.remove(key) - println('Invalid: ' + key) - } else { - def value = jcls['methods'][jdesc] - if (accessLevel.call(value['access']) > accessStr.call(entry['modifier']) && (entry.comment == null || !entry.comment.startsWith('#force '))) { - lines.remove(key) - println('Invalid Narrowing: ' + key) - } - } - } - } - } - - - def data = [] - def remapComment = { entry -> - if (!mappings || !entry || !entry.desc) return null - def comment = entry.comment?.substring(1)?.trim() - def jsonCls = json.get(entry.cls.replaceAll('\\.', '/')) - def mappingsClass = mappings?.getClass(jsonCls.name) - if (!mappingsClass) return entry.comment - def idx = entry.desc.indexOf('(') - def mappedName = idx == -1 - ? mappingsClass.remapField(entry.desc) - : mappingsClass.remapMethod(entry.desc.substring(0, idx), entry.desc.substring(idx)) - if (!mappedName) return entry.comment - if (mappedName == '') - mappedName = 'constructor' - - if (comment?.startsWith(mappedName)) - return '# ' + comment - if (comment && comment.indexOf(' ') != -1) { - def split = comment.split(" - ").toList() - if (split[0].indexOf(' ') != -1) - // The first string is more than one word, so append before it - return "# ${mappedName} - ${comment}" - split.remove(0) - return "# ${mappedName} - ${String.join(' - ', split)}" - } - return '# ' + mappedName - } - lines.each { key,value -> - if (!value.group) { - def comment = remapComment.call(value) - data.add(value.modifier + ' ' + key + (comment ? ' ' + comment : '')) - } else { - data.add('#group ' + value.modifier + ' ' + key + (value.comment == null ? '' : ' ' + value.comment)) - value.children.each { - def line = value.modifier + ' ' + it - def (modifier, cls, desc, comment) = parse.call(line) - comment = remapComment.call([modifier: modifier, cls: cls, desc: desc, comment: comment]) - data.add(line + (comment ? ' ' + comment : '')) - } - data.add('#endgroup') - } - } - f.text = data.join('\n') - } - } -} diff --git a/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/CheckExcs.groovy b/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/CheckExcs.groovy deleted file mode 100644 index c9b7fd4bbc..0000000000 --- a/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/CheckExcs.groovy +++ /dev/null @@ -1,88 +0,0 @@ -package net.minecraftforge.forge.tasks - -import org.gradle.api.DefaultTask -import org.gradle.api.file.ConfigurableFileCollection -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.tasks.InputFile -import org.gradle.api.tasks.InputFiles -import org.gradle.api.tasks.TaskAction -import org.objectweb.asm.* - -import java.util.zip.ZipEntry -import java.util.zip.ZipInputStream - -abstract class CheckExcs extends DefaultTask { - @InputFile abstract RegularFileProperty getBinary() - @InputFiles abstract ConfigurableFileCollection getExcs() - - @TaskAction - protected void exec() { - Util.init() - def known = [] - binary.get().asFile.withInputStream { i -> - new ZipInputStream(i).withCloseable { zin -> - def visitor = new ClassVisitor(Opcodes.ASM9) { - private String cls - @Override - public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { - this.cls = name - } - - @Override - public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { - known.add(this.cls + '.' + name + descriptor) - super.visitMethod(access, name, descriptor, signature, exceptions) - } - } - ZipEntry zein - while ((zein = zin.nextEntry) != null) { - if (zein.name.endsWith('.class')) { - ClassReader reader = new ClassReader(zin) - reader.accept(visitor, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES) - } - } - } - } - - excs.each { f -> - def lines = [] - f.eachLine { line -> - def idx = line.indexOf('#') - if (idx == 0 || line.isEmpty()) { - return - } - - def comment = idx == -1 ? null : line.substring(idx) - if (idx != -1) line = line.substring(0, idx - 1) - - if (!line.contains('=')) { - println('Invalid: ' + line) - return - } - - def (key, value) = line.split('=', 2) - if (!known.contains(key)) { - println('Unknown: ' + line) - return - } - - def (cls, desc) = key.split('\\.', 2) - if (!desc.contains('(')) { - println('Invalid: ' + line) - return - } - def name = desc.split('\\(', 2)[0] - desc = '(' + desc.split('\\(', 2)[1] - - def (exceptions, args) = value.contains('|') ? value.split('|', 2) : [value, ''] - - if (args.split(',').length != Type.getArgumentTypes(desc).length) { - println('Invalid: ' + line) - return - } - lines.add(line) - } - f.text = lines.sort().join('\n') - } - } -} diff --git a/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/CheckPatches.groovy b/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/CheckPatches.groovy deleted file mode 100644 index e6633cf074..0000000000 --- a/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/CheckPatches.groovy +++ /dev/null @@ -1,219 +0,0 @@ -package net.minecraftforge.forge.tasks - -import org.gradle.api.DefaultTask -import org.gradle.api.file.DirectoryProperty -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.InputDirectory -import org.gradle.api.tasks.TaskAction - -import java.nio.file.Files -import java.nio.file.Paths -import java.util.regex.Pattern - -abstract class CheckPatches extends DefaultTask { - @InputDirectory abstract DirectoryProperty getPatchDir() - @Input boolean autoFix = false - - @TaskAction - protected void exec() { - def hasS2SArtifact = [ - Paths.get("patches/minecraft/net/minecraft/client/renderer/ViewFrustum.java.patch"), - Paths.get("patches/minecraft/net/minecraft/data/BlockModelDefinition.java.patch") - ] - - def verified = true; - patchDir.get().asFileTree.each { patch -> - def patchPath = project.rootDir.toPath().relativize(patch.toPath()) - verified &= verifyPatch(patch, autoFix, patchPath.toString(), hasS2SArtifact.contains(patchPath)) - } - - if (!verified) - throw new RuntimeException('One or more patches failed verification. Check the log for errors.') - } - - def verifyPatch(patch, fix, patchPath, hasS2SArtifact) { - def hunk_start_pattern = Pattern.compile('^@@ -[0-9,]* \\+[0-9,_]* @@$') - def white_space_pattern = Pattern.compile('^[+\\-]\\s*$') - def import_pattern = Pattern.compile('^[+\\-]\\s*import.*') - def field_pattern = Pattern.compile('^[+\\-][\\s]*((public|protected|private)[\\s]*)?(static[\\s]*)?(final)?([^=;]*)(=.*)?;\\s*$') - def method_pattern = Pattern.compile('^[+\\-][\\s]*((public|protected|private)[\\s]*)?(static[\\s]*)?(final)?([^(]*)[(]([^)]*)?[)]\\s*[{]\\s*$') - def class_pattern = Pattern.compile('^[+\\-][\\s]*((public|protected|private)[\\s]*)?(static[\\s]*)?(final[\\s]*)?(class|interface)([^{]*)[{]\\s*$') - - def accessMap = [("private"):0, (null):1, ("protected"):2, ("public"):3] - - def hasProblem = false; - def lines = patch.readLines() - - def hunksStart = 0 - def onlyWhiteSpace = false - - def didFix = false - def newLines = [] - def whiteSpaceErrors = [] - - // First two lines are file name ++/-- and we do not care - newLines.add(lines[0] + '\n') - newLines.add(lines[1] + '\n') - - int i = 2 - for (; i < lines.size(); ++i) { - def line = lines[i] - newLines.add(line + '\n') - - if (hunk_start_pattern.matcher(line).find()) { - if (onlyWhiteSpace) { - if (fix || hasS2SArtifact) { - logger.lifecycle("Removing white space hunk starting at line {}, file: {}", hunksStart + 1, patchPath) - def toRemove = i - hunksStart; - while (toRemove-- > 0) - newLines.remove(newLines.size() - 1) - didFix = true - } - else { - logger.lifecycle("Patch contains only white space hunk starting at line {}, file: {}", hunksStart + 1, patchPath) - hasProblem = true - } - } - else { - if (!whiteSpaceErrors.empty) - hasProblem = true - whiteSpaceErrors.each { error -> logger.lifecycle(error) } - whiteSpaceErrors.clear() - } - hunksStart = i - onlyWhiteSpace = true - continue - } - - if (line.startsWithAny('+','-')) { - def prefixChange = false - def prevLine = lines[i - 1] - - if (line.charAt(0) == (char)'+' && prevLine.charAt(0) == (char)'-') { - def prevTrim = prevLine.substring(1).replaceAll("\\s", "") - def currTrim = line.substring(1).replaceAll("\\s", "") - - if (prevTrim.equals(currTrim)) { - prefixChange = true - } - - def pMatcher = field_pattern.matcher(prevLine) - def cMatcher = field_pattern.matcher(line) - - if (pMatcher.find() && cMatcher.find() && - pMatcher.group(6) == cMatcher.group(6) && // = ... - pMatcher.group(5) == cMatcher.group(5) && // field name - pMatcher.group(3) == cMatcher.group(3) && // static - (accessMap[pMatcher.group(2)] < accessMap[cMatcher.group(2)] || pMatcher.group(4) != cMatcher.group(4))) { - logger.lifecycle("Patch contains access changes or final removal at line {}, file: {}", i + 1, patchPath) - hasProblem = true - } - - pMatcher = method_pattern.matcher(prevLine) - cMatcher = method_pattern.matcher(line) - - if (pMatcher.find() && cMatcher.find() && - pMatcher.group(6) == cMatcher.group(6) && // params - pMatcher.group(5) == cMatcher.group(5) && // void name - pMatcher.group(3) == cMatcher.group(3) && // static - (accessMap[pMatcher.group(2)] < accessMap[cMatcher.group(2)] || pMatcher.group(4) != cMatcher.group(4))) { - logger.lifecycle("Patch contains access changes or final removal at line {}, file: {}", i + 1, patchPath) - hasProblem = true - } - - pMatcher = class_pattern.matcher(prevLine) - cMatcher = class_pattern.matcher(line) - - if (pMatcher.find() && cMatcher.find() && - pMatcher.group(6) == cMatcher.group(6) && // ClassName<> extends ... - pMatcher.group(5) == cMatcher.group(5) && // class | interface - pMatcher.group(3) == cMatcher.group(3) && // static - (accessMap[pMatcher.group(2)] < accessMap[cMatcher.group(2)] || pMatcher.group(4) != cMatcher.group(4))) { - logger.lifecycle("Patch contains access changes or final removal at line {}, file: {}", i + 1, patchPath) - hasProblem = true - } - } - - if (line.charAt(0) == (char)'-' && i + 1 < lines.size()) { - def nextLine = lines[i + 1] - if (nextLine.charAt(0) == (char)'+') { - def nextTrim = nextLine.substring(1).replaceAll("\\s", "") - def currTrim = line.substring(1).replaceAll("\\s", "") - - if (nextTrim.equals(currTrim)) { - prefixChange = true - } - } - } - - def isWhiteSpaceChange = white_space_pattern.matcher(line).find() - - if (!prefixChange && !isWhiteSpaceChange) { - onlyWhiteSpace = hasS2SArtifact && import_pattern.matcher(line).find() - } - else if (isWhiteSpaceChange) { - def prevLineChange = prevLine.startsWithAny('+','-') - def nextLineChange = i + 1 < lines.size() && lines[i + 1].startsWithAny('+','-') - - if (!prevLineChange && !nextLineChange) { - whiteSpaceErrors.add(String.format("Patch contains white space change in valid hunk at line %d (cannot auto fix), file: %s", i + 1, patchPath)) - } - } - - if (line.contains("\t")) { - if (!fix) { - logger.lifecycle("Patch contains tabs on line {}, file: {}", i + 1, patchPath) - hasProblem = true - } - else { - logger.lifecycle("Fixing tabs on line {}, file: {}", i + 1, patchPath) - line = line.replaceAll('\t', ' ') - newLines.remove(newLines.size() - 1) - newLines.add(line + '\n') - didFix = true - } - } - - if (import_pattern.matcher(line).find() && !hasS2SArtifact) { - logger.lifecycle("Patch contains import change on line {}, file: {}", i + 1, patchPath) - hasProblem = true - } - } - } - - if (onlyWhiteSpace) { - if (fix || hasS2SArtifact) { - logger.lifecycle("Removing white space hunk starting at line {}, file: {}", hunksStart + 1, patchPath) - def toRemove = i - hunksStart; - while (toRemove-- > 0) - newLines.remove(newLines.size() - 1) - didFix = true - } - else { - logger.lifecycle("Patch contains only white space hunk starting at line {}, file: {}", hunksStart + 1, patchPath) - hasProblem = true - } - } - else { - if (!whiteSpaceErrors.empty) - hasProblem = true - whiteSpaceErrors.each { error -> logger.lifecycle(error) } - } - - if (didFix) { - if (newLines.size() <= 2) { - logger.lifecycle("Patch is now empty removing, file: {}", patchPath) - Files.delete(patch.toPath()) - } - else { - if (!hasS2SArtifact) - logger.lifecycle("*** Updating patch file. Please run setup then genPatches again. ***") - patch.withWriter('UTF-8') { writer -> - newLines.each { l -> writer.write(l) } - } - } - } - - return !hasProblem - } -} diff --git a/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/CheckSAS.groovy b/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/CheckSAS.groovy deleted file mode 100644 index 74adaca64f..0000000000 --- a/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/CheckSAS.groovy +++ /dev/null @@ -1,49 +0,0 @@ -package net.minecraftforge.forge.tasks - -import org.gradle.api.DefaultTask -import org.gradle.api.file.ConfigurableFileCollection -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.tasks.InputFile -import org.gradle.api.tasks.InputFiles -import org.gradle.api.tasks.TaskAction - -abstract class CheckSAS extends DefaultTask { - @InputFile abstract RegularFileProperty getInheritance() - @InputFiles abstract ConfigurableFileCollection getSass() - - @TaskAction - protected void exec() { - Util.init() - def json = inheritance.get().asFile.json() - - sass.each { f -> - def lines = [] - f.eachLine { line -> - if (line[0] == '\t') return //Skip any tabed lines, those are ones we add - def idx = line.indexOf('#') - if (idx == 0 || line.isEmpty()) { - lines.add(line) - return - } - - def comment = idx == -1 ? null : line.substring(idx) - if (idx != -1) line = line.substring(0, idx - 1) - - def (cls, desc) = (line.trim() + ' ').split(' ', -1) - cls = cls.replaceAll('\\.', '/') - desc = desc.replace('(', ' (') - if (desc.isEmpty() || json[cls] == null || json[cls]['methods'] == null || json[cls]['methods'][desc] == null) { - println('Invalid: ' + line) - return - } - - def mtd = json[cls]['methods'][desc] - lines.add(cls + ' ' + desc.replace(' ', '') + (comment == null ? '' : ' ' + comment)) - def children = json.values().findAll{ it.methods != null && it.methods[desc] != null && it.methods[desc].override == cls} - .collect { it.name + ' ' + desc.replace(' ', '') } as TreeSet - children.each { lines.add('\t' + it) } - } - f.text = lines.join('\n') - } - } -} diff --git a/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/Crowdin.groovy b/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/Crowdin.groovy new file mode 100644 index 0000000000..5a7afc16f6 --- /dev/null +++ b/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/Crowdin.groovy @@ -0,0 +1,73 @@ +package net.minecraftforge.forge.tasks + +import org.gradle.api.DefaultTask +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Optional +import org.gradle.api.tasks.OutputFile +import org.gradle.api.tasks.TaskAction + +import java.util.zip.ZipEntry +import java.util.zip.ZipInputStream +import java.util.zip.ZipOutputStream + +abstract class Crowdin extends DefaultTask { + @Input abstract Property getId() + @Input @Optional abstract Property getKey() + @Input boolean json = true + @OutputFile abstract RegularFileProperty getOutput() + @OutputFile abstract RegularFileProperty getExport() + + Crowdin() { + outputs.upToDateWhen{ false } + id.convention('minecraft-forge') + output.convention(project.layout.buildDirectory.dir(name).map {it.file("output.zip") }) + export.convention(project.layout.buildDirectory.dir(name).map {it.file("export.json") }) + } + + @TaskAction + def run() { + File outputFile = output.get().asFile + File exportFile = export.get().asFile + if (outputFile.exists()) + outputFile.delete() + + if (!key.isPresent()) + return + String key = this.key.get() + String id = this.id.get() + + // Force an export + new URL("https://api.crowdin.com/api/project/${id}/export?key=${key}").withInputStream { i -> + exportFile.withOutputStream { it << i } + } + + if (!exportFile.text.contains('success')) { + throw new RuntimeException("Crowdin export failed, see ${exportFile} for more info") + } + + new URL("https://api.crowdin.com/api/project/${id}/download/all.zip?key=${key}").withInputStream { i -> + new ZipInputStream(i).withCloseable { zin -> + outputFile.withOutputStream { out -> + new ZipOutputStream(out).withCloseable { zout -> + ZipEntry zein + while ((zein = zin.nextEntry) != null) { + if (zein.isDirectory()) { + zout.putNextEntry(new ZipEntry(zein.name)) + } else { + // 1.13+ uses json + if (zein.name.endsWith('.json') == json) { + ZipEntry zeout = new ZipEntry(json ? zein.name.toLowerCase() : zein.name) + zeout.time = 1 + zout.putNextEntry(zeout) + zout << zin + } + } + } + } + } + } + } + } +} diff --git a/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/DownloadLibraries.groovy b/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/DownloadLibraries.groovy index 749f6c609e..342146a5f3 100644 --- a/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/DownloadLibraries.groovy +++ b/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/DownloadLibraries.groovy @@ -11,41 +11,41 @@ import org.gradle.api.tasks.TaskAction import java.nio.file.Files abstract class DownloadLibraries extends DefaultTask { - @InputFile abstract RegularFileProperty getInput() - @OutputDirectory abstract DirectoryProperty getOutput() - @OutputFile abstract RegularFileProperty getLibrariesOutput() + @InputFile abstract RegularFileProperty getInput() + @OutputDirectory abstract DirectoryProperty getOutput() + @OutputFile abstract RegularFileProperty getLibrariesOutput() DownloadLibraries() { - output.convention(project.layout.buildDirectory.dir(name)) + output.convention(project.layout.buildDirectory.dir(name)) librariesOutput.convention(project.layout.buildDirectory.dir(name).map(d -> d.file('libraries.txt'))) - } + } @TaskAction def run() { - Util.init() - File outputDir = output.get().asFile + Util.init() + File outputDir = output.get().asFile def libraries = new ArrayList() - def json = input.get().asFile.json().libraries.each { lib -> - //TODO: Thread? - def artifacts = [lib.downloads.artifact] + lib.downloads.get('classifiers', [:]).values() - artifacts.each{ art -> - def target = new File(outputDir, art.path) - libraries.add(target.absolutePath) - if (!target.exists() || !art.sha1.equals(target.sha1())) { - project.logger.lifecycle("Downloading ${art.url}") - if (!target.parentFile.exists()) { - target.parentFile.mkdirs() - } - new URL(art.url).withInputStream { i -> - target.withOutputStream { it << i } - } - if (!art.sha1.equals(target.sha1())) { - throw new IllegalStateException("Failed to download ${art.url} to ${target.canonicalPath} SHA Mismatch") - } - } - } - } + def json = input.get().asFile.json().libraries.each { lib -> + //TODO: Thread? + def artifacts = [lib.downloads.artifact] + lib.downloads.get('classifiers', [:]).values() + artifacts.each{ art -> + def target = new File(outputDir, art.path) + libraries.add(target.absolutePath) + if (!target.exists() || !art.sha1.equals(target.sha1())) { + project.logger.lifecycle("Downloading ${art.url}") + if (!target.parentFile.exists()) { + target.parentFile.mkdirs() + } + new URL(art.url).withInputStream { i -> + target.withOutputStream { it << i } + } + if (!art.sha1.equals(target.sha1())) { + throw new IllegalStateException("Failed to download ${art.url} to ${target.canonicalPath} SHA Mismatch") + } + } + } + } Files.write(librariesOutput.get().asFile.toPath(), libraries) } -} \ No newline at end of file +} diff --git a/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/FieldCompareFinder.groovy b/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/FieldCompareFinder.groovy index e4844107bc..9134db369d 100644 --- a/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/FieldCompareFinder.groovy +++ b/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/FieldCompareFinder.groovy @@ -43,7 +43,7 @@ abstract class FieldCompareFinder extends BytecodeFinder { last = current } } - + @Internal @Override protected Object getData() { diff --git a/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/InheritanceData.groovy b/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/InheritanceData.groovy new file mode 100644 index 0000000000..6f17cee292 --- /dev/null +++ b/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/InheritanceData.groovy @@ -0,0 +1,49 @@ +package net.minecraftforge.forge.tasks + +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import groovy.transform.CompileStatic +import groovy.transform.TupleConstructor + +@CompileStatic +@TupleConstructor +final class InheritanceData implements Annotatable { + String name + int access + String superName + List interfaces = [] + Map methods = [:] + Map fields = [:] + List annotations = [] + + @TupleConstructor + static final class Method implements Annotatable { + int access + String override + List annotations = [] + } + + @TupleConstructor + static final class Field implements Annotatable { + int access + String desc + List annotations = [] + } + + @TupleConstructor + static final class Annotation { + String desc + } + + private static final Gson GSON = new Gson() + static Map parse(File file) { + try (final reader = file.newReader()) { + return GSON.fromJson(reader, new TypeToken>() {}) + } + } +} + +@CompileStatic +interface Annotatable { + List getAnnotations() +} \ No newline at end of file diff --git a/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/MergeJars.groovy b/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/MergeJars.groovy new file mode 100644 index 0000000000..38c578c851 --- /dev/null +++ b/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/MergeJars.groovy @@ -0,0 +1,44 @@ +package net.minecraftforge.forge.tasks + +import org.apache.commons.io.IOUtils +import org.gradle.api.DefaultTask +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.tasks.InputFiles +import org.gradle.api.tasks.OutputFile +import org.gradle.api.tasks.TaskAction + +import java.util.zip.ZipEntry +import java.util.zip.ZipInputStream +import java.util.zip.ZipOutputStream + +abstract class MergeJars extends DefaultTask { + MergeJars() { + output.convention(project.layout.buildDirectory.dir(name).map { it.file('output.jar') }) + } + + @TaskAction + void run() { + def jars = inputJars.files + + try (ZipOutputStream zout = new ZipOutputStream(new FileOutputStream(output.get().asFile))) { + for (def jar : jars) { + try (ZipInputStream zin = new ZipInputStream(new FileInputStream(jar))) { + def entry + while ((entry = zin.getNextEntry()) != null) { + ZipEntry _new = new ZipEntry(entry.getName()) + _new.setTime(0) //SHOULD be the same time as the main entry, but NOOOO _new.setTime(entry.getTime()) throws DateTimeException, so you get 0, screw you! + zout.putNextEntry(_new) + IOUtils.copy(zin, zout) + } + } + } + } + } + + @InputFiles + abstract ConfigurableFileCollection getInputJars() + + @OutputFile + abstract RegularFileProperty getOutput() +} \ No newline at end of file diff --git a/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/ObjectTarget.groovy b/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/ObjectTarget.groovy index c82c1a7060..611b1d7669 100644 --- a/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/ObjectTarget.groovy +++ b/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/ObjectTarget.groovy @@ -1,7 +1,9 @@ package net.minecraftforge.forge.tasks import groovy.transform.EqualsAndHashCode + import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Nested import org.gradle.api.tasks.Optional @EqualsAndHashCode diff --git a/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/SetupCheckJarCompatibility.groovy b/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/SetupCheckJarCompatibility.groovy new file mode 100644 index 0000000000..4ef0f8ffcb --- /dev/null +++ b/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/SetupCheckJarCompatibility.groovy @@ -0,0 +1,47 @@ +package net.minecraftforge.forge.tasks + +import org.gradle.api.DefaultTask +import org.gradle.api.artifacts.Dependency +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Optional +import org.gradle.api.tasks.OutputFile +import org.gradle.api.tasks.TaskAction + +import java.nio.file.Files +import java.nio.file.StandardCopyOption + +abstract class SetupCheckJarCompatibility extends DefaultTask { + SetupCheckJarCompatibility() { + group = 'jar compatibility' + onlyIf { + inputVersion.getOrNull() != null + } + outputs.upToDateWhen { false } // Never up to date, because this setup task should always run + + baseBinPatchesOutput.convention(project.layout.buildDirectory.dir(name).map { it.file('joined.lzma') }) + } + + @TaskAction + void run() { + def inputVersion = inputVersion.get() + + def baseForgeUserdev = project.layout.buildDirectory.dir(name).map { it.file("forge-${inputVersion}-userdev.jar") }.get().asFile + project.rootProject.extensions.download.run { + src "https://maven.minecraftforge.net/net/minecraftforge/forge/${inputVersion}/forge-${inputVersion}-userdev.jar" + dest baseForgeUserdev + } + + def joinedLzma = project.zipTree(baseForgeUserdev).matching { it.include('joined.lzma') }.singleFile + + Files.copy(joinedLzma.toPath(), baseBinPatchesOutput.get().asFile.toPath(), StandardCopyOption.REPLACE_EXISTING) + } + + @Input + @Optional + abstract Property getInputVersion() + + @OutputFile + abstract RegularFileProperty getBaseBinPatchesOutput() +} \ No newline at end of file diff --git a/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/Util.groovy b/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/Util.groovy index 2014fd2517..b973782eec 100644 --- a/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/Util.groovy +++ b/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/Util.groovy @@ -22,18 +22,18 @@ public class Util { def result = format.format(delegate) return result[0..21] + ':' + result[22..-1] } - - String.metaClass.rsplit = { String del, int limit = -1 -> - def lst = new ArrayList() - def x = 0, idx - def tmp = delegate - while ((idx = tmp.lastIndexOf(del)) != -1 && (limit == -1 || x++ < limit)) { - lst.add(0, tmp.substring(idx + del.length(), tmp.length())) - tmp = tmp.substring(0, idx) - } - lst.add(0, tmp) - return lst - } + + String.metaClass.rsplit = { String del, int limit = -1 -> + def lst = new ArrayList() + def x = 0, idx + def tmp = delegate + while ((idx = tmp.lastIndexOf(del)) != -1 && (limit == -1 || x++ < limit)) { + lst.add(0, tmp.substring(idx + del.length(), tmp.length())) + tmp = tmp.substring(0, idx) + } + lst.add(0, tmp) + return lst + } } public static String[] getClasspath(project, libs, artifact) { @@ -67,18 +67,9 @@ public class Util { def url = "https://libraries.minecraft.net/${path}" if (!checkExists(url)) { url = "https://maven.minecraftforge.net/${path}" - if (!checkExists(url)) { - url = "https://hub.spigotmc.org/nexus/content/groups/public/${path}" - if (!checkExists(url)) { - url = "https://repository.raincubic.com/repository/maven_public/${path}" - if (!checkExists(url)) { - url = "https://maven.mohistmc.com/${path}" - } - } - } } //TODO remove when Mojang launcher is updated - if (!classifiers && art.classifier != null) { + if (!classifiers && art.classifier != null) { //Mojang launcher doesn't currently support classifiers, so... move it to part of the version, and force the extension to 'jar' // However, keep the path normal so that our mirror system works. art.version = "${art.version}-${art.classifier}" @@ -134,4 +125,9 @@ public class Util { throw e } } + + public static String getLatestForgeVersion(mcVersion) { + def json = new JsonSlurper().parseText(new URL("https://files.minecraftforge.net/net/minecraftforge/forge/promotions_slim.json").getText("UTF-8")) + return json.promos["$mcVersion-latest"] + } } \ No newline at end of file diff --git a/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/checks/CheckATs.groovy b/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/checks/CheckATs.groovy new file mode 100644 index 0000000000..55046da0aa --- /dev/null +++ b/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/checks/CheckATs.groovy @@ -0,0 +1,310 @@ +package net.minecraftforge.forge.tasks.checks + +import groovy.transform.CompileStatic +import groovy.transform.TupleConstructor +import net.minecraftforge.forge.tasks.InheritanceData +import net.minecraftforge.srgutils.IMappingFile +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.RegularFile +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.InputFiles +import org.gradle.api.tasks.Optional +import org.objectweb.asm.Opcodes + +@CompileStatic +abstract class CheckATs extends CheckTask { + + @InputFile abstract RegularFileProperty getInheritance() + @InputFiles abstract ConfigurableFileCollection getAts() + @InputFile @Optional abstract RegularFileProperty getMappings() + + @Override + void check(Reporter reporter, boolean fix) { + final IMappingFile mappings = mappings.map { RegularFile it -> IMappingFile.load(it.asFile) }.getOrNull() + final inheritance = InheritanceData.parse(this.inheritance.get().asFile) + + ats.each { + final lines = process(it, reporter, inheritance) + if (fix) { + it.text = joinBack(lines, inheritance, mappings).join('\n') + } + } + } + + private static TreeMap process(File file, Reporter reporter, Map inheritance) { + final TreeMap lines = ATParser.parse(file.readLines(), reporter) + final Map constructorGroups = [:] + + final itr = lines.entrySet().iterator() + final toRemove = [] + while (itr.hasNext()) { + final next = itr.next() + String key = next.key + final entry = next.value + if (entry === null) continue + + final binaryName = entry.cls.replaceAll('\\.', '/') + + // Process Groups, this will remove any entries outside the group that is covered by the group + if (entry.group) { + final jcls = inheritance[binaryName] + if (jcls === null) { + itr.remove() + reporter.report("Invalid group: $key") + } else if ('*' == entry.desc) { + if (!jcls.fields) { + itr.remove() + reporter.report("Invalid group, class has no fields: $key") + } else { + jcls.fields.each { field, value -> + final fkey = entry.cls + ' ' + field + if (accessLevel(value.access) < accessStr(entry.modifier)) { + if (lines.containsKey(fkey)) { + toRemove.add(fkey) + } else if (!entry.existing.contains(fkey)) { + reporter.report("Missing group entry: $fkey") + } + entry.children.add(fkey) + } else if (lines.containsKey(fkey)) { + toRemove.add(fkey) + reporter.report("Found invalid group entry: $fkey") + } + } + entry.existing.findAll { it !in entry.children }.each { println('Removed: ' + it) } + } + } else if ('*()' == entry.desc) { + if (!jcls.methods) { + itr.remove() + reporter.report("Invalid group, class has no methods: $key") + } else { + jcls.methods.each { mtd, value -> + if (mtd.startsWith('') || mtd.startsWith('lambda$')) return + key = entry.cls + ' ' + mtd.replace(' ', '') + if (accessLevel(value.access) < accessStr(entry.modifier)) { + if (lines.containsKey(key)) { + toRemove.add(key) + } else if (!entry.existing.contains(key)) { + reporter.report("Missing group entry: $key") + } + entry.children.add(key) + } else if (lines.containsKey(key)) { + toRemove.add(key) + reporter.report("Found invalid group entry: $key") + } + } + entry.existing.findAll { it !in entry.children }.each { println('Removed: ' + it) } + } + } else if ('' == entry.desc) { //Make all public non-abstract subclasses + constructorGroups.put(binaryName, entry) + } + } + + // Process normal lines, remove invalid and remove narrowing + else { + def jcls = inheritance.get(binaryName) + if (jcls === null) { + itr.remove() + reporter.report("Invalid: $key") + } else if (entry.desc == '') { + if (accessLevel(jcls.access) > accessStr(entry.modifier) && (entry.comment === null || !entry.comment.startsWith('#force '))) { + itr.remove() + reporter.report("Invalid Narrowing: $key") + } + } else if (!entry.desc.contains('(')) { + if (!jcls.fields || !jcls.fields.containsKey(entry.desc)) { + itr.remove() + reporter.report("Invalid: $key") + } else { + final value = jcls.fields[entry.desc] + if (accessLevel(value.access) > accessStr(entry.modifier) && (entry.comment === null || !entry.comment.startsWith('#force '))) { + itr.remove() + reporter.report("Invalid Narrowing: $key - ${entry.comment}") + } + } + } else { + final jdesc = entry.desc.replace('(', ' (') + if (!jcls.methods || !jcls.methods.containsKey(jdesc)) { + itr.remove() + reporter.report("Invalid: $key") + } else { + final value = jcls.methods[jdesc] + if (accessLevel(value.access) > accessStr(entry.modifier) && (entry.comment === null || !entry.comment.startsWith('#force '))) { + itr.remove() + reporter.report("Invalid Narrowing: $key") + } + } + } + } + } + + inheritance.each { tcls, value -> + if (!value.methods || ((value.access & Opcodes.ACC_ABSTRACT) !== 0)) return + String parent = tcls + while (parent !== null) { + constructorGroups[parent]?.tap { entry -> + value.methods.each { mtd, v -> + if (mtd.startsWith('')) { + final child = tcls.replaceAll('/', '\\.') + ' ' + mtd.replace(' ', '') + if (accessLevel(v.access) < 3) { + if (lines.containsKey(child)) { + toRemove.add(child) + } else if (child !in entry.existing) { + reporter.report("Missing group entry: $child") + } + entry.children.add(child) + } else if (lines.containsKey(child)) { + toRemove.add(child) + reporter.report("Found invalid group entry: $child") + } + } + } + } + parent = inheritance[parent]?.superName + } + } + constructorGroups.values().each { entry -> entry.existing.findAll { it !in entry.children }.each{ reporter.report("Found invalid group entry: $it") } } + + toRemove.each(lines.&remove) + + return lines + } + + private static List joinBack(TreeMap lines, Map inheritance, IMappingFile mappings) { + final List data = [] + final remapComment = { ATParser.Entry entry -> + if (!mappings || !entry || !entry.desc) return null + final comment = entry.comment?.substring(1)?.trim() + final jsonCls = inheritance.get(entry.cls.replaceAll('\\.', '/')) + final mappingsClass = mappings?.getClass(jsonCls.name) + if (mappingsClass === null) return entry.comment + final idx = entry.desc.indexOf('(') + + String mappedName = idx == -1 + ? mappingsClass.remapField(entry.desc) + : mappingsClass.remapMethod(entry.desc.substring(0, idx), entry.desc.substring(idx)) + if (!mappedName) return entry.comment + if (mappedName == '') + mappedName = 'constructor' + + if (comment?.startsWith(mappedName)) + return '# ' + comment + if (comment && comment.indexOf(' ') !== -1) { + def split = comment.split(' - ').toList() + if (split[0].indexOf(' ') !== -1) + // The first string is more than one word, so append before it + return "# ${mappedName} - ${comment}" + split.remove(0) + return "# ${mappedName} - ${String.join(' - ', split)}" + } + return '# ' + mappedName + } + lines.each { key, value -> + if (!value.group) { + def comment = remapComment.call(value) + data.add(value.modifier + ' ' + key + (comment ? ' ' + comment : '')) + } else { + data.add(('#group ' + value.modifier + ' ' + key + ' ' + (value.comment ?: '')).trim()) + value.children.each { + final line = value.modifier + ' ' + it + final entry = ATParser.parseEntry(line) + final comment = remapComment(entry) + data.add(line + (comment ? ' ' + comment : '')) + } + data.add('#endgroup') + } + } + return data + } + + static int accessStr(String access) { + if (access.endsWith('-f') || access.endsWith('+f')) return 4 + switch (access.toLowerCase()) { + case 'public': return 3 + case 'protected': return 2 + case 'default': return 1 + case 'private': return 0 + default: return -1 + } + } + + static int accessLevel(int access) { + if ((access & Opcodes.ACC_PUBLIC) !== 0) return 3 + if ((access & Opcodes.ACC_PROTECTED) !== 0) return 2 + if ((access & Opcodes.ACC_PRIVATE) !== 0) return 0 + return 1 + } +} + +@CompileStatic +class ATParser { + static TreeMap parse(List lines, CheckTask.Reporter reporter) { + TreeMap outLines = new TreeMap<>() + Entry group = null + for (final line : lines) { + if (line.isEmpty()) continue + if (line.startsWith('#group ')) { + final entry = parseEntry(line.substring(7)) + + if (entry.desc != '*' && entry.desc != '*()' && entry.desc != '') { + reporter.report("Invalid group: $line", false) + } + + entry.group = true + entry.children = [] + entry.existing = [] + + group = entry + + if (outLines.containsKey(entry.key)) { + reporter.report("Duplicate group: $line", false) + } + + outLines[entry.key] = group + } else if (group !== null) { + if (line.startsWith('#endgroup')) { + group = null + } else { + final key = parseEntry(line).key + group.existing.add(key) + } + } else if (line.startsWith('#endgroup')) { + reporter.report("Invalid group ending: $line", false) + } else if (line.startsWith('#')) { + //Nom + } else { + final entry = parseEntry(line) + if (outLines.containsKey(entry.key)) { + reporter.report("Found duplicate: $line") + continue + } + outLines[entry.key] = entry + } + } + return outLines + } + + static Entry parseEntry(String line) { + final idx = line.indexOf('#') + final String comment = idx === -1 ? null : line.substring(idx) + if (idx !== -1) line = line.substring(0, idx - 1) + final data = (line.trim() + ' ').split(' ', -1) + new Entry(data[0], data[1], data[2], comment) + } + + @TupleConstructor + static final class Entry { + String modifier, cls, desc, comment + + Set existing + TreeSet children + boolean group = false + + @Lazy + String key = {cls + (desc.isEmpty() ? '' : ' ' + desc)}() + + Object getAt(String key) { + return getProperty(key) + } + } +} diff --git a/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/checks/CheckExcs.groovy b/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/checks/CheckExcs.groovy new file mode 100644 index 0000000000..254596bdd8 --- /dev/null +++ b/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/checks/CheckExcs.groovy @@ -0,0 +1,93 @@ +package net.minecraftforge.forge.tasks.checks + +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.InputFiles +import org.objectweb.asm.ClassReader +import org.objectweb.asm.ClassVisitor +import org.objectweb.asm.MethodVisitor +import org.objectweb.asm.Opcodes +import org.objectweb.asm.Type + +import java.util.zip.ZipEntry +import java.util.zip.ZipInputStream + +abstract class CheckExcs extends CheckTask { + + @InputFile abstract RegularFileProperty getBinary() + @InputFiles abstract ConfigurableFileCollection getExcs() + + @Override + void check(Reporter reporter, boolean fix) { + final Set known = [] + collectKnown(known) + + excs.each { f -> + final lines = [] + f.eachLine { line -> + def idx = line.indexOf('#') + if (idx == 0 || line.isEmpty()) { + return + } + + if (idx != -1) line = line.substring(0, idx - 1) + + if (!line.contains('=')) { + reporter.report("Invalid: $line") + return + } + + def (String key, String value) = line.split('=', 2) + if (!known.contains(key)) { + reporter.report("Unknown: $line") + return + } + + String desc = key.split('\\.', 2)[1] + if (!desc.contains('(')) { + reporter.report("Invalid: $line") + return + } + desc = '(' + desc.split('\\(', 2)[1] + + def (exceptions, String args) = value.contains('|') ? value.split('|', 2) : [value, ''] + + if (args.split(',').length !== Type.getArgumentTypes(desc).length) { + reporter.report("Invalid: $line") + return + } + lines.add(line) + } + + if (fix) f.text = lines.sort().join('\n') + } + } + + private void collectKnown(Collection known) { + binary.get().asFile.withInputStream { i -> + new ZipInputStream(i).withCloseable { zin -> + final visitor = new ClassVisitor(Opcodes.ASM9) { + private String cls + @Override + void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { + this.cls = name + } + + @Override + MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { + known.add(this.cls + '.' + name + descriptor) + super.visitMethod(access, name, descriptor, signature, exceptions) + } + } + ZipEntry zein + while ((zein = zin.nextEntry) !== null) { + if (zein.name.endsWith('.class')) { + ClassReader reader = new ClassReader(zin) + reader.accept(visitor, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES) + } + } + } + } + } +} diff --git a/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/checks/CheckMode.groovy b/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/checks/CheckMode.groovy new file mode 100644 index 0000000000..7778bfe789 --- /dev/null +++ b/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/checks/CheckMode.groovy @@ -0,0 +1,9 @@ +package net.minecraftforge.forge.tasks.checks + +import groovy.transform.CompileStatic + +@CompileStatic +enum CheckMode { + CHECK, + FIX +} diff --git a/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/checks/CheckPatches.groovy b/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/checks/CheckPatches.groovy new file mode 100644 index 0000000000..afcbe4e580 --- /dev/null +++ b/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/checks/CheckPatches.groovy @@ -0,0 +1,177 @@ +package net.minecraftforge.forge.tasks.checks + +import groovy.transform.CompileStatic +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.provider.ListProperty +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputDirectory +import org.gradle.api.tasks.Optional + +import java.nio.file.Files +import java.nio.file.Path +import java.util.regex.Pattern + +@CompileStatic +abstract class CheckPatches extends CheckTask { + + private static final Pattern HUNK_START_PATTERN = Pattern.compile('^@@ -[0-9,]* \\+[0-9,_]* @@$') + private static final Pattern WHITESPACE_PATTERN = Pattern.compile('^[+\\-]\\s*$') + private static final Pattern IMPORT_PATTERN = Pattern.compile('^[+\\-]\\s*import.*') + private static final Pattern FIELD_PATTERN = Pattern.compile('^[+\\-][\\s]*((public|protected|private)[\\s]*)?(static[\\s]*)?(final)?([^=;]*)(=.*)?;\\s*$') + private static final Pattern METHOD_PATTERN = Pattern.compile('^[+\\-][\\s]*((public|protected|private)[\\s]*)?(static[\\s]*)?(final)?([^(]*)[(]([^)]*)?[)]\\s*[{]\\s*$') + private static final Pattern CLASS_PATTERN = Pattern.compile('^[+\\-][\\s]*((public|protected|private)[\\s]*)?(static[\\s]*)?(final[\\s]*)?(class|interface)([^{]*)[{]\\s*$') + private static final Map ACCESS_MAP = [private: 0, protected: 2, public: 3].tap { it.put(null, 1) } + + @InputDirectory abstract DirectoryProperty getPatchDir() + @Input @Optional abstract ListProperty getPatchesWithS2SArtifact() + + @Override + void check(Reporter reporter, boolean fix) { + final patchDir = getPatchDir().get().asFile.toPath() + Files.walk(patchDir).withCloseable { + it.filter(Files.&isRegularFile).forEach { path -> + final String relativeName = patchDir.relativize(path).toString() + verifyPatch(path, reporter, fix, relativeName, patchesWithS2SArtifact.get().contains(relativeName.replace('\\', '/'))) + } + } + } + + void verifyPatch(Path patch, Reporter reporter, boolean fix, String patchPath, boolean hasS2SArtifact) { + final oldFixedErrors = reporter.fixed.size() + + final lines = Files.readAllLines(patch) + + int hunksStart = 0 + boolean onlyWhiteSpace = false + + final List newLines = [] + + // First two lines are file name ++/-- and we do not care + newLines.add(lines[0] + '\n') + newLines.add(lines[1] + '\n') + + int i + for (i = 2; i < lines.size(); ++i) { + def line = lines[i] + newLines.add(line + '\n') + + if (HUNK_START_PATTERN.matcher(line).find()) { + if (onlyWhiteSpace) { + if (!hasS2SArtifact) + reporter.report("Patch contains only white space hunk starting at line ${hunksStart + 1}, file: $patchPath") + int toRemove = i - hunksStart + while (toRemove-- > 0) + newLines.remove(newLines.size() - 1) + } + hunksStart = i + onlyWhiteSpace = true + continue + } + + if (line.startsWithAny('+','-')) { + def prefixChange = false + def prevLine = lines[i - 1] + + if (line.charAt(0) == (char)'+' && prevLine.charAt(0) == (char)'-') { + def prevTrim = prevLine.substring(1).replaceAll("\\s", "") + def currTrim = line.substring(1).replaceAll("\\s", "") + + if (prevTrim == currTrim) { + prefixChange = true + } + + def pMatcher = FIELD_PATTERN.matcher(prevLine) + def cMatcher = FIELD_PATTERN.matcher(line) + + if (pMatcher.find() && cMatcher.find() && + pMatcher.group(6) == cMatcher.group(6) && // = ... + pMatcher.group(5) == cMatcher.group(5) && // field name + pMatcher.group(3) == cMatcher.group(3) && // static + (ACCESS_MAP[pMatcher.group(2)] < ACCESS_MAP[cMatcher.group(2)] || pMatcher.group(4) != cMatcher.group(4))) { + reporter.report("Patch contains access changes or final removal at line ${i + 1}, file: $patchPath", false) + } + + pMatcher = METHOD_PATTERN.matcher(prevLine) + cMatcher = METHOD_PATTERN.matcher(line) + + if (pMatcher.find() && cMatcher.find() && + pMatcher.group(6) == cMatcher.group(6) && // params + pMatcher.group(5) == cMatcher.group(5) && // void name + pMatcher.group(3) == cMatcher.group(3) && // static + (ACCESS_MAP[pMatcher.group(2)] < ACCESS_MAP[cMatcher.group(2)] || pMatcher.group(4) != cMatcher.group(4))) { + reporter.report("Patch contains access changes or final removal at line ${i + 1}, file: $patchPath", false) + } + + pMatcher = CLASS_PATTERN.matcher(prevLine) + cMatcher = CLASS_PATTERN.matcher(line) + + if (pMatcher.find() && cMatcher.find() && + pMatcher.group(6) == cMatcher.group(6) && // ClassName<> extends ... + pMatcher.group(5) == cMatcher.group(5) && // class | interface + pMatcher.group(3) == cMatcher.group(3) && // static + (ACCESS_MAP[pMatcher.group(2)] < ACCESS_MAP[cMatcher.group(2)] || pMatcher.group(4) != cMatcher.group(4))) { + reporter.report("Patch contains access changes or final removal at line ${i + 1}, file: $patchPath", false) + } + } + + if (line.charAt(0) == (char)'-' && i + 1 < lines.size()) { + final nextLine = lines[i + 1] + if (nextLine.charAt(0) == (char)'+') { + final nextTrim = nextLine.substring(1).replaceAll("\\s", "") + final currTrim = line.substring(1).replaceAll("\\s", "") + + if (nextTrim == currTrim) { + prefixChange = true + } + } + } + + final isWhiteSpaceChange = WHITESPACE_PATTERN.matcher(line).find() + + if (!prefixChange && !isWhiteSpaceChange) { + onlyWhiteSpace = hasS2SArtifact && IMPORT_PATTERN.matcher(line).find() + } else if (isWhiteSpaceChange) { + final prevLineChange = prevLine.startsWithAny('+','-') + final nextLineChange = i + 1 < lines.size() && lines[i + 1].startsWithAny('+','-') + + if (!prevLineChange && !nextLineChange) { + reporter.report("Patch contains white space change in valid hunk at line ${i + 1}, file: $patchPath", false) + } + } + + if (line.contains('\t')) { + reporter.report("Patch contains tabs on line ${i + 1}, file: $patchPath") + line = line.replaceAll('\t', ' ') + newLines.remove(newLines.size() - 1) + newLines.add(line + '\n') + } + + if (IMPORT_PATTERN.matcher(line).find() && !hasS2SArtifact) { + reporter.report("Patch contains import change on line ${i + 1}, file: $patchPath", false) + } + } + } + + if (onlyWhiteSpace) { + if (!hasS2SArtifact) + reporter.report("Patch contains only white space hunk starting at line ${hunksStart + 1}, file: $patchPath") + def toRemove = i - hunksStart; + while (toRemove-- > 0) + newLines.remove(newLines.size() - 1) + } + + if ((reporter.fixed.size() > oldFixedErrors && fix) || hasS2SArtifact) { + if (newLines.size() <= 2) { + logger.lifecycle("Patch is now empty removing, file: {}", patchPath) + Files.delete(patch) + } + else { + if (!hasS2SArtifact) + logger.lifecycle("*** Updating patch file. Please run setup then genPatches again. ***") + Files.newBufferedWriter(patch).withCloseable { + newLines.each { l -> it.write(l) } + } + } + } + } +} diff --git a/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/checks/CheckSAS.groovy b/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/checks/CheckSAS.groovy new file mode 100644 index 0000000000..062221a609 --- /dev/null +++ b/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/checks/CheckSAS.groovy @@ -0,0 +1,110 @@ +package net.minecraftforge.forge.tasks.checks + +import groovy.transform.CompileStatic +import net.minecraftforge.forge.tasks.Annotatable +import net.minecraftforge.forge.tasks.InheritanceData +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.InputFiles + +@CompileStatic +abstract class CheckSAS extends CheckTask { + + @InputFile abstract RegularFileProperty getInheritance() + @InputFiles abstract ConfigurableFileCollection getSass() + + @Override + void check(Reporter reporter, boolean fix) { + final inheritance = InheritanceData.parse(this.inheritance.get().asFile) + + sass.each { f -> + final lines = [] + f.eachLine { line -> + if (line[0] == '\t') return // Skip any tabbed lines, those are ones we add + final idx = line.indexOf('#') + if (idx == 0 || line.isEmpty()) { + lines.add(line) + return + } + def comment = idx == -1 ? null : line.substring(idx) + if (idx != -1) line = line.substring(0, idx - 1) + final spl = (line.trim().replace('(', ' (') + ' ').split(' ', -1) + def (String cls, String name, String desc) = [spl[0], spl[1], spl[2]] + cls = cls.replaceAll('\\.', '/') + + if (inheritance[cls] === null) { + reporter.report("Invalid: $line") + } else if (name.isEmpty()) { //Class SAS + final toAdd = [] + final clsSided = isSided(inheritance[cls]) + boolean sided = clsSided + + /* TODO: MergeTool doesn't do fields + if (json[cls]['fields'] != null) { + for (entry in json[cls]['fields']) { + if (isSided(entry.value)) { + sided = true + toAdd.add('\t' + cls + ' ' + entry.key) + } + } + } */ + + final clsInh = inheritance[cls] + if (clsInh.methods) { + for (entry in clsInh.methods) { + if (isSided(entry.value)) { + sided = true + toAdd.add('\t' + cls + ' ' + entry.key.replaceAll(' ', '')) + findChildMethods(inheritance, cls, entry.key).each { lines.add('\t' + it) } + findChildMethods(inheritance, cls, entry.key).each { println(line + ' -- ' + it) } + } else if (clsSided) { + findChildMethods(inheritance, cls, entry.key).each { lines.add('\t' + it) } + findChildMethods(inheritance, cls, entry.key).each { println(line + ' -- ' + it) } + } + } + } + + if (sided) { + lines.add(cls + (comment == null ? '' : ' ' + comment)) + lines.addAll(toAdd.sort()) + } else { + reporter.report("Invalid: $line") + } + + } else if (desc.isEmpty()) { // Fields + /* TODO: MergeTool doesn't do fields + if (json[cls]['fields'] != null && isSided(json[cls]['fields'][name])) + lines.add(cls + ' ' + name + (comment == null ? '' : ' ' + comment)) + else */ + reporter.report("Invalid: $line") + } else { // Methods + final clsInh = inheritance[cls] + if (clsInh.methods === null || !isSided(clsInh.methods[name + ' ' + desc])) + reporter.report("Invalid: $line") + else { + lines.add(cls + ' ' + name + desc + (comment == null ? '' : ' ' + comment)) + findChildMethods(inheritance, cls, name + ' ' + desc).each { println(line + ' -- ' + it) } + findChildMethods(inheritance, cls, name + ' ' + desc).each { lines.add('\t' + it) } + } + } + } + + if (fix) f.text = lines.join('\n') + } + } + + protected static isSided(Annotatable annotatable) { + if (annotatable === null) return false + for (ann in annotatable.annotations) { + if ('Lnet/minecraftforge/api/distmarker/OnlyIn;' == ann.desc) + return true + } + return false + } + + protected static findChildMethods(Map json, String cls, String desc) { + return json.values().findAll{ it.methods != null && it.methods[desc] != null && it.methods[desc].override == cls && isSided(it.methods[desc]) } + .collect { it.name + ' ' + desc.replace(' ', '') } as TreeSet + } +} diff --git a/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/checks/CheckTask.groovy b/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/checks/CheckTask.groovy new file mode 100644 index 0000000000..27548c04f3 --- /dev/null +++ b/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/checks/CheckTask.groovy @@ -0,0 +1,109 @@ +package net.minecraftforge.forge.tasks.checks + +import groovy.transform.CompileStatic +import net.minecraftforge.forge.tasks.Util +import org.gradle.api.Action +import org.gradle.api.DefaultTask +import org.gradle.api.logging.LogLevel +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.TaskAction +import org.gradle.api.tasks.TaskContainer +import org.gradle.api.tasks.VerificationTask + +@CompileStatic +abstract class CheckTask extends DefaultTask implements VerificationTask { + @Input + abstract Property getMode() + + private boolean ignoreFailures = false + + @Input + @Override + boolean getIgnoreFailures() { + return ignoreFailures + } + + @Override + void setIgnoreFailures(boolean ignoreFailures) { + this.ignoreFailures = ignoreFailures + } + + @TaskAction + void run() { + Util.init() + + final doFix = getMode().get() === CheckMode.FIX + final Reporter reporter = new Reporter(doFix) + check(reporter, doFix) + + if (reporter.messages) { + if (getMode().get() === CheckMode.CHECK) { + logger.error("Check task '{}' found errors:\n{}", name, reporter.messages.join('\n')) + if (!ignoreFailures) { + throw new IllegalArgumentException("${reporter.messages.size()} errors were found!") + } + } else { + if (logger.isEnabled(LogLevel.DEBUG)) { + logger.warn("Check task '{}' found {} errors and fixed {}:\n{}", name, reporter.messages.size(), reporter.fixed.size(), reporter.fixed.join('\n')) + } else { + logger.warn("Check task '{}' found {} errors and fixed {}.", name, reporter.messages.size(), reporter.fixed.size()) + } + + if (reporter.notFixed) { + logger.error('{} errors could not be fixed:\n{}', reporter.notFixed.size(), reporter.notFixed.join('\n')) + if (!ignoreFailures) { + throw new IllegalArgumentException("${reporter.notFixed.size()} errors which cannot be fixed were found!") + } + } + } + } + } + + abstract void check(Reporter reporter, boolean fix) + + @CompileStatic + static final class Reporter { + final boolean trackFixed + Reporter(boolean trackFixed) { + this.trackFixed = trackFixed + } + + public final List messages = [] + + public final List fixed = [] + public final List notFixed = [] + + void report(String message, boolean canBeFixed = true) { + messages.add(message) + + if (trackFixed) { + if (canBeFixed) { + fixed.add(message) + } else { + notFixed.add(message) + } + } + } + } + + static void registerTask(TaskContainer tasks, String taskName, @DelegatesTo.Target('type') Class clazz, + @DelegatesTo(genericTypeIndex = 0, target = 'type') Closure configuration) { + taskName = taskName.capitalize() + tasks.register("check$taskName", clazz) { CheckTask task -> + configuration.setDelegate((T) task) + configuration.call(task) + task.mode.set(CheckMode.CHECK) + task.group = 'checks' + } + tasks.named('checkAll').configure { it.dependsOn("check$taskName") } + + tasks.register("checkAndFix$taskName", clazz) { CheckTask task -> + configuration.setDelegate((T) task) + configuration.call(task) + task.mode.set(CheckMode.FIX) + task.group = 'checks' + } + tasks.named('checkAllAndFix').configure { it.dependsOn("checkAndFix$taskName") } + } +} diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ae04661ee7..e411586a54 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-7.5.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/licenses/LICENSE-header.txt b/licenses/LICENSE-header.txt index 278e170f79..299818fdbb 100644 --- a/licenses/LICENSE-header.txt +++ b/licenses/LICENSE-header.txt @@ -1,16 +1,2 @@ -Minecraft Forge -Copyright (c) 2016-2022. - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation version 2.1 -of the License. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +Copyright (c) Forge Development LLC and contributors +SPDX-License-Identifier: LGPL-2.1-only \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index b58c2cccff..dac692d7d9 100644 --- a/settings.gradle +++ b/settings.gradle @@ -5,6 +5,20 @@ pluginManagement { } } +buildscript { + dependencies { + classpath('com.google.code.gson:gson') { + version { + strictly '2.10.1' + } + } + } +} + +plugins { + id 'org.gradle.toolchains.foojay-resolver-convention' version '0.7.0' +} + rootProject.name = 'Mohist' include ':mcp' diff --git a/src/fmllauncher/resources/libraries.txt b/src/fmllauncher/resources/libraries.txt index 30e9862144..c4409c2f5b 100644 --- a/src/fmllauncher/resources/libraries.txt +++ b/src/fmllauncher/resources/libraries.txt @@ -29,6 +29,7 @@ libraries/net/minecraftforge/coremods/4.0.6/coremods-4.0.6.jar|e56551f2586d3ff08 libraries/net/minecraftforge/eventbus/4.0.0/eventbus-4.0.0.jar|84c4758e6139b70146104be5bb1300b5|42873 libraries/net/minecraftforge/forgespi/3.2.0/forgespi-3.2.0.jar|8f1f6942d17d88d704324e8000666a1a|20172 libraries/net/minecraftforge/installertools/1.1.11/installertools-1.1.11.jar|98a5eabc02648dccc7dbbbd046ff7da9|54303 +libraries/net/minecraftforge/installertools/1.3.0/installertools-1.3.0.jar|8b8b7d048620ac3f7ffc5d1255b91f60|83178 libraries/net/minecraftforge/jarsplitter/1.1.2/jarsplitter-1.1.2.jar|6336b555885a01ec1b129a0e4074428f|7981 libraries/net/minecraftforge/nashorn-core-compat/15.1.1.1/nashorn-core-compat-15.1.1.1.jar|ae37ed8407982eefc9f85fdb1c95a3a0|2194057 libraries/net/minecraftforge/unsafe/0.2.0/unsafe-0.2.0.jar|2d1016ebe4c1a63dd50a59d26bd12db1|2834 @@ -48,15 +49,11 @@ libraries/org/checkerframework/checker-qual/2.0.0/checker-qual-2.0.0.jar|94fe1af libraries/org/codehaus/mojo/animal-sniffer-annotations/1.14/animal-sniffer-annotations-1.14.jar|9d42e46845c874f1710a9f6a741f6c14|3482 libraries/org/fusesource/jansi/jansi/1.18/jansi-1.18.jar|6ee32de8880da9f02552474f60ab6fbd|287352 libraries/org/jline/jline/3.12.1/jline-3.12.1.jar|ffd8c9d6eb8a8456ef505c8ef9fc777d|732926 -libraries/org/ow2/asm/asm/6.1.1/asm-6.1.1.jar|04b72e489b64c54d5776ab59f330bd23|108252 -libraries/org/ow2/asm/asm/9.5/asm-9.5.jar|29721ee4b5eacf0a34b204c345c8bc69|121863 -libraries/org/ow2/asm/asm-analysis/6.1.1/asm-analysis-6.1.1.jar|b58c31c1ca0496e6773cc0311f97ceda|31979 -libraries/org/ow2/asm/asm-analysis/9.5/asm-analysis-9.5.jar|4df0adafc78ebba404d4037987d36b61|33978 -libraries/org/ow2/asm/asm-commons/6.1.1/asm-commons-6.1.1.jar|ca6299118bc7b85201671502526bd68b|77745 -libraries/org/ow2/asm/asm-commons/9.5/asm-commons-9.5.jar|7d1fce986192f71722b19754e4cb9e61|72209 -libraries/org/ow2/asm/asm-tree/6.1.1/asm-tree-6.1.1.jar|d640f7d0efc57192870f73a87f576469|50050 -libraries/org/ow2/asm/asm-tree/9.5/asm-tree-9.5.jar|44755681b7d6fa7143afbb438e55c20c|51944 -libraries/org/ow2/asm/asm-util/9.5/asm-util-9.5.jar|ad0016249fb68bb9196babefd47b80dc|91076 +libraries/org/ow2/asm/asm/9.6/asm-9.6.jar|6f8bccf756f170d4185bb24c8c2d2020|123598 +libraries/org/ow2/asm/asm-analysis/9.6/asm-analysis-9.6.jar|31c84ef7cc893fb278952ae2d6a2674f|34041 +libraries/org/ow2/asm/asm-commons/9.6/asm-commons-9.6.jar|9e317c75534bd1da8c00a67c618ab288|72194 +libraries/org/ow2/asm/asm-tree/9.6/asm-tree-9.6.jar|6062608f1a98afe1e853d01fa1221a9e|51935 +libraries/org/ow2/asm/asm-util/9.6/asm-util-9.6.jar|bd3bc1c176a787373e9a031073c9574b|91131 libraries/org/slf4j/slf4j-api/1.8.0-beta4/slf4j-api-1.8.0-beta4.jar|e8147675316a5bc78d1ba5d8cdb9e578|44213 libraries/org/spongepowered/mixin/0.8.4/mixin-0.8.4.jar|2333a4ce6c6969e8c11dfd524112f2c0|993541 libraries/org/xerial/sqlite-jdbc/3.34.0/sqlite-jdbc-3.34.0.jar|743bacfa02e66cad1027e80b065c45ad|7296329 diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg index 2728c74ba1..4a31a8bef7 100644 --- a/src/main/resources/META-INF/accesstransformer.cfg +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -151,7 +151,7 @@ public net.minecraft.client.particle.ParticleManager func_199283_a(Lnet/minecraf public net.minecraft.client.particle.ParticleManager func_215234_a(Lnet/minecraft/particles/ParticleType;Lnet/minecraft/client/particle/ParticleManager$IParticleMetaFactory;)V # registerFactory public net.minecraft.client.particle.ParticleManager$IParticleMetaFactory public net.minecraft.client.renderer.GameRenderer func_175069_a(Lnet/minecraft/util/ResourceLocation;)V # loadEffect -private net.minecraft.client.renderer.ItemModelMesher field_199313_a # shapes - force public -> private +private net.minecraft.client.renderer.ItemModelMesher field_199313_a # - force public -> private public net.minecraft.client.renderer.ItemRenderer func_229112_a_(Lcom/mojang/blaze3d/matrix/MatrixStack;Lcom/mojang/blaze3d/vertex/IVertexBuilder;Ljava/util/List;Lnet/minecraft/item/ItemStack;II)V # renderQuadList public net.minecraft.client.renderer.ItemRenderer func_229114_a_(Lnet/minecraft/client/renderer/model/IBakedModel;Lnet/minecraft/item/ItemStack;IILcom/mojang/blaze3d/matrix/MatrixStack;Lcom/mojang/blaze3d/vertex/IVertexBuilder;)V # renderModelLists protected-f net.minecraft.client.renderer.RenderState field_228507_Q_ # setupState diff --git a/src/main/resources/coremods/add_bouncer_method.js b/src/main/resources/coremods/add_bouncer_method.js index 5fdc228665..aab1eeed35 100644 --- a/src/main/resources/coremods/add_bouncer_method.js +++ b/src/main/resources/coremods/add_bouncer_method.js @@ -8,6 +8,8 @@ var InsnNode = Java.type('org.objectweb.asm.tree.InsnNode') function initializeCoreMod() { return { "getLanguage_bouncer": addBouncer("net.minecraft.network.play.client.CClientSettingsPacket", "func_149524_c", "getLanguage", "()Ljava/lang/String;"), + "compose_bouncer": addRedirect("net.minecraft.util.math.vector.TransformationMatrix", "func_227985_a_", "composeVanilla", "(Lnet/minecraft/util/math/vector/TransformationMatrix;)Lnet/minecraft/util/math/vector/TransformationMatrix;", 2), + "inverse_bouncer": addRedirect("net.minecraft.util.math.vector.TransformationMatrix", "func_227987_b_", "inverseVanilla", "()Lnet/minecraft/util/math/vector/TransformationMatrix;", 1), } } @@ -15,11 +17,11 @@ function addBouncer(className, conflictedName, expectedName, descriptor, signatu if (signature == null) signature = descriptor; return { - 'target': { - 'type': 'CLASS', - 'name': className - }, - 'transformer': function(node) { + 'target': { + 'type': 'CLASS', + 'name': className + }, + 'transformer': function(node) { var mappedName = ASMAPI.mapMethod(conflictedName); if (mappedName == expectedName) return node; // No work to do! @@ -39,6 +41,60 @@ function addBouncer(className, conflictedName, expectedName, descriptor, signatu node.methods.add(method); return node; - } - } + } + } +} + +// Generate a method with name oldName that redirects to expectedName. expectedName is not remapped. +// A redirect will be generated for oldName and whatever deobfuscated name it maps to, +// provided these methods do not exist already. +function addRedirect(className, oldName, expectedName, descriptor, numArgs) { + return { + 'target': { + 'type': 'CLASS', + 'name': className + }, + 'transformer': function(node) { + var mappedName = ASMAPI.mapMethod(oldName); + + var names = [oldName, mappedName]; + + for(var i = 0; i < names.length; i++) { + var name = names[i]; + + // Skip generating stub if method name exists in this class + var exists = false; + for(var j = 0; j < node.methods.length; j++) { + if(node.methods[i].name == name) { + exists = true; + break; + } + } + + if(exists) { + continue; + } + + // Generate stub + + var method = new MethodNode( + /* access = */ Opcodes.ACC_PUBLIC, + /* name = */ name, + /* descriptor = */ descriptor, + /* signature = */ descriptor, + /* exceptions = */ null + ); + + for(var i = 0; i < numArgs; i++) { + method.instructions.add(new VarInsnNode(Opcodes.ALOAD, i)); + } + method.instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, className.replaceAll("\\.","/"), expectedName, descriptor)); + method.instructions.add(new InsnNode(Opcodes.ARETURN)); + + node.methods.add(method); + } + + return node; + } + } } \ No newline at end of file diff --git a/src/main/resources/forge.exc b/src/main/resources/forge.exc index 289c596659..446e7e021a 100644 --- a/src/main/resources/forge.exc +++ b/src/main/resources/forge.exc @@ -4,7 +4,7 @@ net/minecraft/block/FlowingFluidBlock.(Ljava/util/function/Supplier;Lnet/m net/minecraft/block/RedstoneWireBlock.canConnectTo(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/IBlockReader;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/Direction;)Z=|p_176343_0_,world,pos,p_176343_1_ net/minecraft/client/Minecraft.loadWorld(Ljava/lang/String;Lnet/minecraft/util/registry/DynamicRegistries$Impl;Ljava/util/function/Function;Lcom/mojang/datafixers/util/Function4;ZLnet/minecraft/client/Minecraft$WorldSelectionType;Z)V=|p_238195_1_,p_238195_2_,p_238195_3_,p_238195_4_,p_238195_5_,p_238195_6_,creating net/minecraft/client/gui/ScreenManager.getScreenFactory(Lnet/minecraft/inventory/container/ContainerType;Lnet/minecraft/client/Minecraft;ILnet/minecraft/util/text/ITextComponent;)Ljava/util/Optional;=|p_216909_0_,p_216909_1_,p_216909_2_,p_216909_3_ -net/minecraft/client/gui/screens/Screen.renderToolTip(Lcom/mojang/blaze3d/vertex/PoseStack;Ljava/util/List;IILnet/minecraft/client/gui/Font;)V=|p_238654_1_,p_238654_2_,p_238654_3_,p_238654_4_,fontnet/minecraft/client/particle/ParticleManager.renderParticles(Lcom/mojang/blaze3d/matrix/MatrixStack;Lnet/minecraft/client/renderer/IRenderTypeBuffer$Impl;Lnet/minecraft/client/renderer/LightTexture;Lnet/minecraft/client/renderer/ActiveRenderInfo;FLnet/minecraft/client/renderer/culling/ClippingHelper;)V=|p_228345_1_,p_228345_2_,p_228345_3_,p_228345_4_,p_228345_5_,clippingHelper +net/minecraft/client/gui/screen/Screen.renderToolTip(Lcom/mojang/blaze3d/vertex/PoseStack;Ljava/util/List;IILnet/minecraft/client/gui/Font;)V=|p_238654_1_,p_238654_2_,p_238654_3_,p_238654_4_,fontnet/minecraft/client/particle/ParticleManager.renderParticles(Lcom/mojang/blaze3d/matrix/MatrixStack;Lnet/minecraft/client/renderer/IRenderTypeBuffer$Impl;Lnet/minecraft/client/renderer/LightTexture;Lnet/minecraft/client/renderer/ActiveRenderInfo;FLnet/minecraft/client/renderer/culling/ClippingHelper;)V=|p_228345_1_,p_228345_2_,p_228345_3_,p_228345_4_,p_228345_5_,clippingHelper net/minecraft/client/renderer/BlockModelRenderer.renderModel(Lcom/mojang/blaze3d/matrix/MatrixStack$Entry;Lcom/mojang/blaze3d/vertex/IVertexBuilder;Lnet/minecraft/block/BlockState;Lnet/minecraft/client/renderer/model/IBakedModel;FFFIILnet/minecraftforge/client/model/data/IModelData;)V=|p_228804_1_,p_228804_2_,p_228804_3_,p_228804_4_,p_228804_5_,p_228804_6_,p_228804_7_,p_228804_8_,p_228804_9_,modelData net/minecraft/client/renderer/BlockRendererDispatcher.renderBlock(Lnet/minecraft/block/BlockState;Lcom/mojang/blaze3d/matrix/MatrixStack;Lnet/minecraft/client/renderer/IRenderTypeBuffer;IILnet/minecraftforge/client/model/data/IModelData;)V=|p_228791_1_,p_228791_2_,p_228791_3_,p_228791_4_,p_228791_5_,modelData net/minecraft/client/renderer/FogRenderer.setupFog(Lnet/minecraft/client/renderer/ActiveRenderInfo;Lnet/minecraft/client/renderer/FogRenderer$FogType;FZF)V=|p_228372_0_,p_228372_1_,p_228372_2_,p_228372_3_,partialTicks diff --git a/src/main/resources/forge.sas b/src/main/resources/forge.sas index 044638ee03..95342330bb 100644 --- a/src/main/resources/forge.sas +++ b/src/main/resources/forge.sas @@ -1,8 +1,8 @@ # Forge Sided Annotation Stripper Config -# Please keep this file organized. And use the forge:checkSAS task to generate/validate inheretance. +# Please keep this file organized. And use the forge:checkAndFixSAS task to generate/validate inheritance. # So only add the root function that needs to have the annotation stripped. -# checkSAS will populate all overrides as nessasary. Prefixing added lines with \t -# checkSAS also supports names using . so simplest way to find the correct line to add is to get the AT line from the bot +# checkAndFixSAS will populate all overrides as nessasary. Prefixing added lines with \t +# checkAndFixSAS also supports names using . so simplest way to find the correct line to add is to get the AT line from the bot # and remove the access modifier. #================================================================================================================================== # Block.getItem called from Block.getPickBlock. @@ -97,7 +97,6 @@ net/minecraft/resources/IResourceManager func_219533_b(Lnet/minecraft/util/Resou net/minecraft/resources/SimpleReloadableResourceManager func_219533_b(Lnet/minecraft/util/ResourceLocation;)Z net/minecraft/tags/ITagCollection func_199913_a(Ljava/lang/Object;)Ljava/util/Collection; # getOwningTags net/minecraft/util/Direction func_176739_a(Ljava/lang/String;)Lnet/minecraft/util/Direction; # byName -net/minecraft/util/Direction$Axis func_176717_a(Ljava/lang/String;)Lnet/minecraft/util/Direction$Axis; # byName net/minecraft/util/math/vector/Vector3d func_216371_e()Lnet/minecraft/util/math/vector/Vector3d; net/minecraft/util/math/vector/Vector3d func_189984_a(Lnet/minecraft/util/math/vector/Vector2f;)Lnet/minecraft/util/math/vector/Vector3d; # fromPitchYaw net/minecraft/util/math/vector/Vector3d func_189986_a(FF)Lnet/minecraft/util/math/vector/Vector3d; # fromPitchYaw