mirror of
https://github.com/MinecraftForge/MinecraftForge.git
synced 2026-11-14 17:14:44 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bce2aa405b | ||
|
|
94f2c3df41 | ||
|
|
c7ea5c8bb1 | ||
|
|
424fa2bdf6 | ||
|
|
1dab50ca00 | ||
|
|
3b981f4725 | ||
|
|
bca7eff231 | ||
|
|
5df71fb5df | ||
|
|
5e6d92a099 | ||
|
|
a171012411 | ||
|
|
44b30daa05 | ||
|
|
2d33dba36e | ||
|
|
c538816698 | ||
|
|
4f936fb5f7 | ||
|
|
66e4dab83b | ||
|
|
78d8d5495c | ||
|
|
e61aa67600 |
No files matched your search
+13
-3
@@ -1,3 +1,6 @@
|
||||
#forgedev dev if the folder exists in here
|
||||
forgedev
|
||||
|
||||
#eclipse
|
||||
**/bin
|
||||
**/.settings
|
||||
@@ -22,10 +25,12 @@
|
||||
/*/build
|
||||
/*/.gradle
|
||||
|
||||
# Minecraft
|
||||
/run
|
||||
/*.launch
|
||||
|
||||
# Projects repo, either ignore it, or ignore patches.
|
||||
/projects/mcp/
|
||||
/projects/clean/
|
||||
/projects/forge/
|
||||
src/minecraft
|
||||
|
||||
#occupational hazards
|
||||
/projects/**/build/
|
||||
@@ -46,3 +51,8 @@ src/*/generated/**/.cache/
|
||||
/*/.gitignore
|
||||
/*/.factorypath
|
||||
/*/.apt_generated/
|
||||
/lib/
|
||||
/_actual/
|
||||
/runs/
|
||||
/forge.ipr
|
||||
/forge.iws
|
||||
+1
-1
@@ -3,7 +3,7 @@ parts herein are licensed under the terms of the LGPL 2.1 found
|
||||
here http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt and
|
||||
copied below.
|
||||
|
||||
Homepage: http://minecraftforge.net/
|
||||
Homepage: https://minecraftforge.net/
|
||||
https://github.com/MinecraftForge/MinecraftForge
|
||||
|
||||
|
||||
|
||||
+857
-35
@@ -1,54 +1,876 @@
|
||||
import net.minecraftforge.forge.tasks.*
|
||||
import net.minecraftforge.gradleutils.PomUtils
|
||||
import org.apache.tools.ant.filters.ReplaceTokens
|
||||
|
||||
plugins {
|
||||
id 'net.minecraftforge.licenser' version '1.0.1'
|
||||
id 'com.github.ben-manes.versions' version '0.46.0'
|
||||
id 'net.minecraftforge.gradleutils' version '[2.6.4,2.7)'
|
||||
id 'java-library'
|
||||
id 'idea'
|
||||
id 'eclipse'
|
||||
id 'de.undercouch.download' version '5.4.0'
|
||||
id 'net.minecraftforge.gradle.patcher' version '[6.0.46,6.2)' apply false
|
||||
id 'net.minecraftforge.gradle.mcp' version '[6.0.46,6.2)' apply false
|
||||
id 'net.minecraftforge.gradlejarsigner' version '1.0.4'
|
||||
id 'org.barfuin.gradle.taskinfo' version '2.1.0'
|
||||
id 'maven-publish'
|
||||
alias libs.plugins.licenser
|
||||
alias libs.plugins.versions
|
||||
alias libs.plugins.gradleutils
|
||||
alias libs.plugins.gitversion
|
||||
alias libs.plugins.changelog
|
||||
alias libs.plugins.renamer
|
||||
alias libs.plugins.jarsigner
|
||||
id 'net.minecraftforge.forgedev'
|
||||
id 'net.minecraftforge.forge.build.convention'
|
||||
}
|
||||
|
||||
Util.init() //Init all our extension methods!
|
||||
gradleutils.displayName = 'Forge'
|
||||
group = 'net.minecraftforge'
|
||||
description = 'Modifications to Minecraft to enable mod developers.'
|
||||
|
||||
ext {
|
||||
VERSION = gitversion.getMCTagOffsetBranch(MC_VERSION)
|
||||
FORGE_VERSION = VERSION.substring(MC_VERSION.length() + 1)
|
||||
// NOTE: See settings.gradle for all the referenceable version variables.
|
||||
// All subprojects (that apply 'net.minecraftforge.forge.build.convention') will have these available.
|
||||
println "Version: $version"
|
||||
|
||||
java.toolchain.languageVersion = JavaLanguageVersion.of(javaVersion)
|
||||
|
||||
jarSigner.autoDetect('forge')
|
||||
|
||||
// We have to tell ForgeDev to use the exact versions of tools we ship in the installer
|
||||
fdtools {
|
||||
configure('binpatcher') {
|
||||
version = buildLibs.binarypatcher.get().version
|
||||
}
|
||||
configure('installertools') {
|
||||
version = buildLibs.installertools.get().version
|
||||
}
|
||||
configure('jarcompatibilitychecker') {
|
||||
javaLauncher = javaToolchains.launcherFor(java.toolchain)
|
||||
}
|
||||
}
|
||||
renamerTools {
|
||||
configure('classes') {
|
||||
artifact = buildLibs.renamer.get().toString() + ':all'
|
||||
}
|
||||
configure('srg2source') {
|
||||
version = buildLibs.srg2source.get().version
|
||||
javaLauncher = javaToolchains.launcherFor(java.toolchain)
|
||||
}
|
||||
}
|
||||
|
||||
changelog {
|
||||
from '47.999'
|
||||
/* This is used for debugging, comparing all published artifacts against actual production
|
||||
forgedev.validatePublish {
|
||||
task {
|
||||
baseBranch = 'upstream/1.21.11'
|
||||
versionPrefix = minecraftVersion
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
final minecraftFiles = forgedev.minecraftFiles(minecraftVersion)
|
||||
|
||||
final mcpBase = forgedev.mcpBase {
|
||||
mcpVersion = "${minecraftVersion}-${project.ext.mcpVersion}"
|
||||
mappingChannel = mappingsChannel
|
||||
mappingVersion = mappingsVersion
|
||||
accessTransformers.from file('src/main/resources/META-INF/accesstransformer.cfg')
|
||||
sideAnnotationStrippers.from file('src/main/resources/forge.sas')
|
||||
}
|
||||
|
||||
forgedev.patches {
|
||||
base = mcpBase
|
||||
patches = file('patches/minecraft')
|
||||
patched = file('src/minecraft/java')
|
||||
sourceSets.main.java.srcDir patched
|
||||
}
|
||||
|
||||
tasks.register('setup') {
|
||||
dependsOn ':forge:extractMapped'
|
||||
if (findProject(':clean'))
|
||||
dependsOn ':clean:extractMapped'
|
||||
dependsOn forgedev.patches.apply
|
||||
}
|
||||
|
||||
tasks.register('doChecks') {
|
||||
dependsOn ':forge:checkJarCompatibility'
|
||||
dependsOn ':forge:publish'
|
||||
}
|
||||
|
||||
project(':mcp') {
|
||||
apply plugin: 'net.minecraftforge.gradle.mcp'
|
||||
mcp {
|
||||
config MC_VERSION + '-' + MCP_VERSION
|
||||
pipeline = 'joined'
|
||||
sourceSets {
|
||||
named('main') {
|
||||
resources.srcDir 'src/main/generated'
|
||||
}
|
||||
named('test') {
|
||||
resources.srcDir 'src/test/generated'
|
||||
}
|
||||
}
|
||||
|
||||
if (System.env.TEAMCITY_VERSION) {
|
||||
//Only setup the CI environment if and only if the environment variables are set.
|
||||
tasks.named('configureTeamCity').configure {
|
||||
doLast {
|
||||
println "##teamcity[buildNumber '${project(':forge').version}']"
|
||||
println "##teamcity[setParameter name='env.PUBLISHED_JAVA_ARTIFACT_VERSION' value='${project(':forge').version}']"
|
||||
license {
|
||||
header = file('LICENSE-header.txt')
|
||||
|
||||
include 'net/minecraftforge/'
|
||||
exclude 'net/minecraftforge/common/LenientUnboundedMapCodec.java'
|
||||
|
||||
tasks.tap {
|
||||
register('main') {
|
||||
files.from files('src/main/java')
|
||||
}
|
||||
|
||||
register('test') {
|
||||
files.from files('src/test/java')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
changelog {
|
||||
from changelogBase
|
||||
publishAll = false
|
||||
}
|
||||
|
||||
// TODO [ForgeDev] Support compileOnlyApi, runtimeOnly, and a consumable annotation processor configuration
|
||||
configurations {
|
||||
// Don't pull all libraries, if we're missing something, add it to the installer list so the installer knows to download it.
|
||||
register('bootstrap') {
|
||||
transitive = false
|
||||
canBeDeclared = true
|
||||
canBeResolved = true
|
||||
}
|
||||
|
||||
register('installer') {
|
||||
transitive = false
|
||||
canBeDeclared = true
|
||||
canBeResolved = true
|
||||
extendsFrom(bootstrap)
|
||||
}
|
||||
|
||||
register('installerextra') {
|
||||
transitive = false
|
||||
extendsFrom(bootstrap)
|
||||
}
|
||||
|
||||
named('api') {
|
||||
extendsFrom(installer)
|
||||
}
|
||||
|
||||
named('implementation') {
|
||||
// Inherit vanilla Minecraft's dependencies
|
||||
extendsFrom(mcpBase.dependencyConfiguration)
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// These need to actually be on the classpath at the start. This is only used for the server shim jar.
|
||||
// And this is only needed because custom file systems are REQUIRED to be on the boot classloader.
|
||||
// This has ASM/BootStrap/Unsafe all because I haven't gotten around to moving UnionFileSystem out to its own project.
|
||||
bootstrap libs.jarjar.fs // JarInJar file system
|
||||
bootstrap libs.roimfs // JarInJar File System - FileSystems need to be on boot loader.
|
||||
bootstrap libs.bundles.jimfs // In memory file system used for ForgeDev launches
|
||||
bootstrap libs.securemodules // Has Union file system in it
|
||||
bootstrap libs.unsafe // Needed by securemodules
|
||||
bootstrap libs.bundles.asm // Needed by securemodules
|
||||
|
||||
implementation libs.jopt.simple
|
||||
|
||||
installer libs.bootstrap
|
||||
installer libs.bootstrap.api // Needed by securemodules
|
||||
installer libs.accesstransformers
|
||||
installer libs.eventbus
|
||||
installer libs.jspecify // Dep of EventBus
|
||||
installer libs.forgespi
|
||||
installer libs.coremods.api
|
||||
installer libs.modlauncher
|
||||
installer libs.mergetool.api
|
||||
installer libs.bundles.night.config
|
||||
installer libs.maven.artifact
|
||||
installer libs.bundles.terminalconsoleappender
|
||||
installer libs.mixin
|
||||
installer libs.mixinextras.forge
|
||||
installer libs.bundles.jarjar
|
||||
installer libs.roimfs
|
||||
|
||||
installer projects.fmlcore
|
||||
installer projects.fmlloader
|
||||
installer projects.fmlearlydisplay
|
||||
installer projects.javafmllanguage
|
||||
installer projects.lowcodelanguage
|
||||
installer projects.mclanguage
|
||||
installer projects.forgeTransformers
|
||||
|
||||
// Remove when we bump to a version with no srg names, as this is needed to map srg->official at runtimes
|
||||
installerextra("de.oceanlabs.mcp:mcp_config:${mcpBase.mcpVersion.get().toString()}:srg2off")
|
||||
|
||||
// Get the zip file with csv's of srg->mapepd for Runtime SRG->Mapped reflection remapping
|
||||
// This is also only needed during dev time, and automatically added to the dep list by Mavenizer for userdev
|
||||
// We shouldn't need this anymore now that we use runtime official mappings, but some of our code still references SRG names via reflection at dev time
|
||||
runtimeOnly files(mcpBase.mappingZip)
|
||||
runtimeOnly files(mcpBase.extra)
|
||||
|
||||
runtimeOnly libs.bootstrap
|
||||
runtimeOnly libs.bootstrap.dev
|
||||
|
||||
annotationProcessor libs.eventbus.validator
|
||||
}
|
||||
|
||||
final EXTRA_TXTS = [
|
||||
file('LICENSE.txt'),
|
||||
changelog.task.flatMap(task -> task.outputFile)
|
||||
]
|
||||
forgedev.runs {
|
||||
configureEach {
|
||||
options {
|
||||
workingDir = layout.projectDirectory.dir("runs/$name")
|
||||
|
||||
mainClass = 'net.minecraftforge.bootstrap.ForgeBootstrap'
|
||||
args '--gameDir', '.'
|
||||
jvmArgs '-Djava.net.preferIPv6Addresses=system'
|
||||
|
||||
systemProperty 'bsl.debug', 'true'
|
||||
systemProperty 'terminal.jline', 'true'
|
||||
with(sourceSets.test) {
|
||||
systemProperty 'forge.enableGameTest', 'true'
|
||||
systemProperty 'forgedev.enableTestMods', 'true'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
create('client') {
|
||||
options {
|
||||
systemProperty 'eventbus.api.strictRuntimeChecks', 'true'
|
||||
systemProperty 'org.lwjgl.system.SharedLibraryExtractDirectory', 'lwjgl_dll'
|
||||
|
||||
args '--launchTarget', 'forge_dev_client',
|
||||
'--username', 'Dev',
|
||||
'--version', project.name,
|
||||
'--accessToken', '0',
|
||||
'--userType', 'mojang',
|
||||
'--versionType', 'release',
|
||||
'--assetsDir', '{assets_root}',
|
||||
'--assetIndex', '{asset_index}'
|
||||
}
|
||||
}
|
||||
|
||||
create('server') {
|
||||
options {
|
||||
args '--launchTarget', 'forge_dev_server'
|
||||
}
|
||||
}
|
||||
|
||||
create('gameTestServer') {
|
||||
options {
|
||||
args '--launchTarget', 'forge_dev_server_gametest'
|
||||
args '--uniqueWorld' // Unique world is used so that the world regenerates, as well as the world config isn't influenced by other runs
|
||||
}
|
||||
}
|
||||
|
||||
create('data') {
|
||||
options {
|
||||
args '--launchTarget', 'forge_dev_data',
|
||||
'--all',
|
||||
'--existing', sourceSets.main.resources.srcDirs[0],
|
||||
'--assetsDir', '{assets_root}',
|
||||
'--assetIndex', '{asset_index}'
|
||||
|
||||
with(sourceSets.main) {
|
||||
args '--mod', 'forge',
|
||||
'--output', rootProject.file('src/main/generated/')
|
||||
}
|
||||
|
||||
with(sourceSets.test) {
|
||||
args '--mod', '.+',
|
||||
'--existing', sourceSets.test.resources.srcDirs[0],
|
||||
'--output', rootProject.file('src/test/generated/')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
create('clientData') {
|
||||
options {
|
||||
args '--all',
|
||||
'--existing', sourceSets.main.resources.srcDirs[0],
|
||||
'--assetsDir', '{assets_root}',
|
||||
'--assetIndex', '{asset_index}'
|
||||
|
||||
with(sourceSets.main) {
|
||||
args '--launchTarget', 'forge_dev_client_data',
|
||||
'--mod', 'forge',
|
||||
'--output', rootProject.file('src/main/generated/'),
|
||||
'--mod', 'forge'
|
||||
}
|
||||
|
||||
with(sourceSets.test) {
|
||||
args '--launchTarget', 'forge_dev',
|
||||
'--launchEntry', 'minecraft/net.minecraft.client.data.Main',
|
||||
'--launchData',
|
||||
'--mod', '.+',
|
||||
'--existing', sourceSets.test.resources.srcDirs[0],
|
||||
'--output', rootProject.file('src/test/generated/')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
tasks.register('genAllData') {
|
||||
dependsOn forgedev.runs.data.run, forgedev.runs.data.runTest, forgedev.runs.clientData.run, forgedev.runs.clientData.runTest
|
||||
}
|
||||
|
||||
final serverShim = forgedev.shim {
|
||||
config {
|
||||
mainClass = 'net.minecraftforge.bootstrap.ForgeBootstrap'
|
||||
args = ['--launchTarget', 'forge_server']
|
||||
}
|
||||
classpath {
|
||||
libraries configurations.installer
|
||||
libraries configurations.installerextra
|
||||
library tasks.named('universalJar', Jar)
|
||||
library forgedev.binaryPatches('server').apply, 'server'
|
||||
serverBundle.fileProvider(minecraftFiles.server)
|
||||
}
|
||||
bootstrapClasspath(configurations.bootstrap)
|
||||
jar {
|
||||
jarSigner.sign(it)
|
||||
}
|
||||
}
|
||||
|
||||
final finalizeSpawn = forgedev.methodCallFinder('findFinalizeSpawnTargets') {
|
||||
jar = tasks.named('renameClientOfficial').flatMap{ it.output } //.fileProvider(mcpBase.classes)
|
||||
output = rootProject.file('forge-transformers/src/main/resources/coremods/finalize_spawn_targets.json')
|
||||
blacklist 'net/minecraft/world/level/BaseSpawner' // Ignore this class as we special case it.
|
||||
invokeVirtual('finalizeSpawn', '(Lnet/minecraft/world/level/ServerLevelAccessor;Lnet/minecraft/world/DifficultyInstance;Lnet/minecraft/world/entity/EntitySpawnReason;Lnet/minecraft/world/entity/SpawnGroupData;)Lnet/minecraft/world/entity/SpawnGroupData;')
|
||||
}
|
||||
|
||||
forgedev.validateDeprecations(tasks.named('jar', Jar)) {
|
||||
mcVersion = minecraftVersion
|
||||
}
|
||||
|
||||
forgedev.checks() {
|
||||
base = mcpBase
|
||||
ats {
|
||||
mappings.fileProvider(mcpBase.map2Srg)
|
||||
}
|
||||
sas {}
|
||||
execs { }
|
||||
|
||||
patches {
|
||||
dependsOn forgedev.patches.make
|
||||
patchDir = forgedev.patches.patches
|
||||
patchesWithS2SArtifact = [
|
||||
'minecraft/net/minecraft/client/renderer/ViewArea.java.patch',
|
||||
'minecraft/net/minecraft/data/models/blockstates/Variant.java.patch',
|
||||
]
|
||||
}
|
||||
|
||||
fix {
|
||||
dependsOn finalizeSpawn
|
||||
}
|
||||
|
||||
forgedev.patches.make.configure {
|
||||
finalizedBy forgedev.checks.patches.fix
|
||||
}
|
||||
}
|
||||
|
||||
final crowdin = forgedev.crowdin // Create the crowdin tasks
|
||||
tasks.named('jar', Jar) {
|
||||
from(EXTRA_TXTS)
|
||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||
exclude '.cache'
|
||||
|
||||
from(zipTree(crowdin.flatMap { it.output } )) {
|
||||
include 'assets/forge/lang/*.json'
|
||||
}
|
||||
|
||||
manifest {
|
||||
attributes([
|
||||
'Automatic-Module-Name': 'net.minecraftforge.forge'
|
||||
])
|
||||
attributes([
|
||||
'Specification-Title' : gradleutils.displayName.get(),
|
||||
'Specification-Vendor' : gradleutils.vendor.get(),
|
||||
'Specification-Version' : gitversion.info.tag,
|
||||
'Implementation-Title' : project.group,
|
||||
'Implementation-Vendor' : gradleutils.vendor.get(),
|
||||
'Implementation-Version': forgeVersion
|
||||
], 'net/minecraftforge/versions/forge/')
|
||||
attributes([
|
||||
'Specification-Title' : 'Minecraft',
|
||||
'Specification-Vendor' : gradleutils.vendor.get(),
|
||||
'Specification-Version' : minecraftVersion,
|
||||
'Implementation-Title' : 'MCP',
|
||||
'Implementation-Vendor' : gradleutils.vendor.get(),
|
||||
'Implementation-Version': mcpVersion
|
||||
], 'net/minecraftforge/versions/mcp/')
|
||||
}
|
||||
jarSigner.sign(it)
|
||||
}
|
||||
|
||||
final universalJar = tasks.register('universalJar', Jar) {
|
||||
from (zipTree(tasks.named('jar', Jar).flatMap { it.archiveFile } )) {
|
||||
exclude(forgedev.filterVanilla(mcpBase.classes))
|
||||
}
|
||||
manifest = tasks.named('jar', Jar).get().manifest
|
||||
archiveClassifier = 'universal'
|
||||
jarSigner.sign(it)
|
||||
}
|
||||
|
||||
final map2Tsrg = renamer.convert('map2Tsrg', mcpBase.map2Srg) // Remove when we bump renamer to version that supports gziped files (on MC version change)
|
||||
final reobfJar = renamer.classes('srgJar', tasks.named('jar', Jar)) {
|
||||
libraries.setFrom(configurations.compileClasspath)
|
||||
mappings map2Tsrg
|
||||
archiveClassifier = 'srg'
|
||||
}
|
||||
final universalJarSrg = tasks.register('universalJarSrg', Jar) {
|
||||
from (zipTree(reobfJar.flatMap { it.output } )) {
|
||||
exclude(forgedev.filterVanilla(mcpBase.classes))
|
||||
}
|
||||
manifest = universalJar.get().manifest
|
||||
archiveClassifier = 'universal-srg'
|
||||
jarSigner.sign(it)
|
||||
}
|
||||
final sourcesSrg = renamer.sources(sourceSets.main) {
|
||||
apply {
|
||||
archiveClassifier = 'sources-srg'
|
||||
mappings mcpBase.map2Srg
|
||||
excFiles.from files('src/main/resources/forge.exc')
|
||||
}
|
||||
extract {
|
||||
mustRunAfter(forgedev.patches.apply)
|
||||
}
|
||||
}
|
||||
final sourcesJar = tasks.register('sourcesJar', Jar) {
|
||||
archiveClassifier = 'sources'
|
||||
// If we don't need to reobf we can just do this
|
||||
//from(sourceSets.main.allJava.srcDirs - forgedev.patches.patched.asFile.get())
|
||||
dependsOn(sourcesSrg.apply)
|
||||
from(zipTree(sourcesSrg.apply.flatMap{ it.output })) {
|
||||
exclude { forgedev.patches.patched.file(it.path).get().asFile.exists() }
|
||||
}
|
||||
from(forgedev.userDev.patches.flatMap{ it.output }) {
|
||||
into 'patches/'
|
||||
}
|
||||
}
|
||||
|
||||
forgedev.userDev {
|
||||
base(mcpBase)
|
||||
|
||||
config {
|
||||
universal universalJarSrg
|
||||
sources sourcesJar
|
||||
addLibraries(configurations.installer)
|
||||
// TODO [ForgeDev][UserDev] Split API/Runtime elements in userdev config
|
||||
// so we don't gotta keep making exceptions for MixinExtras
|
||||
addCompileDependency(libs.mixinextras.common)
|
||||
addAnnotationProcessorDependency(libs.mixinextras.common)
|
||||
|
||||
// TODO [ForgeDev] Consolidate Patcher + UserDev Config runs in a sane way
|
||||
runs {
|
||||
configureEach {
|
||||
parents = []
|
||||
main = 'net.minecraftforge.bootstrap.ForgeBootstrap'
|
||||
args '--gameDir', '.'
|
||||
jvmArgs '-Djava.net.preferIPv6Addresses=system'
|
||||
client = it.name.containsIgnoreCase('client')
|
||||
environment 'MCP_MAPPINGS', '{mcp_mappings}'
|
||||
}
|
||||
|
||||
register('clientData') {
|
||||
args '--launchTarget', "forge_userdev_client_data"
|
||||
args '--assetIndex', '{asset_index}'
|
||||
args '--assetsDir', '{assets_root}'
|
||||
}
|
||||
|
||||
register('client') {
|
||||
property 'forge.enableGameTest', 'true'
|
||||
args '--launchTarget', "forge_userdev_client"
|
||||
args '--version', 'MOD_DEV'
|
||||
args '--assetIndex', '{asset_index}'
|
||||
args '--assetsDir', '{assets_root}'
|
||||
}
|
||||
|
||||
register('server') {
|
||||
property 'forge.enableGameTest', 'true'
|
||||
args '--launchTarget', "forge_userdev_server"
|
||||
}
|
||||
|
||||
register('gameTestServer') {
|
||||
args '--launchTarget', "forge_userdev_server_gametest"
|
||||
}
|
||||
|
||||
register('data') {
|
||||
args '--launchTarget', "forge_userdev_data"
|
||||
args '--assetIndex', '{asset_index}'
|
||||
args '--assetsDir', '{assets_root}'
|
||||
}
|
||||
}
|
||||
}
|
||||
patches {
|
||||
modified.setFrom(sourcesSrg.apply.flatMap{ it.output })
|
||||
}
|
||||
binaryPatches {
|
||||
patches.setFrom(forgedev.patches.apply.flatMap { it.output })
|
||||
dirty.setFrom(reobfJar.flatMap { it.output })
|
||||
}
|
||||
}
|
||||
|
||||
forgedev.userdevCompatibility(minecraftVersion) {
|
||||
clean = mcpBase.classesRaw
|
||||
dirty = reobfJar
|
||||
}
|
||||
|
||||
// region Installer tasks, Not needed when we simplify the installer/remove obf
|
||||
final renameOfficial = (side, inputProvider, mappingsProvider) -> {
|
||||
renamer.classes('rename' + side + 'Official') {
|
||||
additionalArgs = ['--ann-fix', '--ids-fix', '--src-fix', '--record-fix', '--strip-sigs', '--reverse']
|
||||
input.fileProvider(inputProvider)
|
||||
libraries = mcpBase.dependencyConfiguration
|
||||
mappings mappingsProvider
|
||||
output = layout.buildDirectory.file("libs/rename-${side.toLowerCase()}-official.jar")
|
||||
logFile = layout.buildDirectory.file("libs/rename-${side.toLowerCase()}-official.log")
|
||||
}
|
||||
}
|
||||
final clientOfficial = renameOfficial('Client', minecraftFiles.client, minecraftFiles.clientMappings)
|
||||
final serverOfficial = renameOfficial('Server', minecraftFiles.serverExtracted, minecraftFiles.serverMappings)
|
||||
final clientBinPatches = forgedev.binaryPatches('client') {
|
||||
clean = clientOfficial.flatMap { it.output }
|
||||
}
|
||||
final serverBinPatches = forgedev.binaryPatches('server') {
|
||||
clean = serverOfficial.flatMap { it.output }
|
||||
}
|
||||
final sanitizedClientMappings = renamer.convert("cleanClientMappings", minecraftFiles.clientMappings)
|
||||
final sanitizedServerMappings = renamer.convert("cleanServerMappings", minecraftFiles.serverMappings)
|
||||
// endregion
|
||||
|
||||
forgedev.installer {
|
||||
dev = !forgedev.ci
|
||||
offline = false
|
||||
baseVersion = buildLibs.installer.get().version.toString()
|
||||
|
||||
// Add extra libraries
|
||||
pack serverShim.jar // The Server executable jar IS needed to be packed, as the installer would need a spec bump in order to download it
|
||||
pack universalJar // I don't think this is strictly needed to be packed anymore, will need to double check later
|
||||
// This isn't actually needed, but its here for some reason, probably wasn't deleted when then installer process was cleaned up
|
||||
library mcpBase.mcpArtifact.get()
|
||||
|
||||
launcherLibrary universalJar
|
||||
// We configure the launcher here to keep the order for easier diff, after publishing, move this to the LauncherJson configuration below
|
||||
launcherJson {
|
||||
// We create this during install, so it's not downloadable
|
||||
generated(clientBinPatches.apply, 'client')
|
||||
}
|
||||
|
||||
launcherLibraries configurations.installerextra
|
||||
launcherLibraries configurations.installer
|
||||
|
||||
final launcherId = "${minecraftVersion}-${project.name}-${forgeVersion}"
|
||||
final installertools = tool(buildLibs.installertools)
|
||||
final binarypatcher = tool(buildLibs.binarypatcher)
|
||||
final renamer = tool(buildLibs.renamer)
|
||||
|
||||
jar {
|
||||
from(EXTRA_TXTS)
|
||||
from(rootProject.file('/forge_installer_logo.png')) {
|
||||
rename { 'big_logo.png' }
|
||||
}
|
||||
from(clientBinPatches.create.flatMap { t -> t.output }) {
|
||||
rename { 'data/client.lzma' }
|
||||
}
|
||||
from(serverBinPatches.create.flatMap { t -> t.output }) {
|
||||
rename { 'data/server.lzma' }
|
||||
}
|
||||
|
||||
final argsFile = rootProject.file('server_files/args.txt')
|
||||
final tokens = [tokens: [
|
||||
SHIM_JAR_FILE: serverShim.jar.get().archiveFileName.get(),
|
||||
MAVEN_PATH: forgedev.mavenArtifact.directory
|
||||
]]
|
||||
from(argsFile) {
|
||||
filter(tokens, ReplaceTokens)
|
||||
into 'data'
|
||||
rename { 'unix_args.txt' }
|
||||
}
|
||||
from(argsFile) {
|
||||
filter(tokens, ReplaceTokens)
|
||||
into 'data'
|
||||
rename { 'win_args.txt' }
|
||||
}
|
||||
|
||||
from(rootProject.file('server_files')) {
|
||||
filter(tokens, ReplaceTokens)
|
||||
into 'data'
|
||||
exclude 'args.txt'
|
||||
}
|
||||
|
||||
jarSigner.sign(it)
|
||||
}
|
||||
json {
|
||||
icon = rootProject.file('icon.ico')
|
||||
executable serverShim.jar
|
||||
minecraft = minecraftVersion
|
||||
profileVersion = launcherId
|
||||
|
||||
final projectArtifact = "${project.group}:${project.name}:${project.version}"
|
||||
|
||||
data 'MOJMAPS', "[net.minecraft:client:${minecraftVersion}:mappings@tsrg]", "[net.minecraft:server:${minecraftVersion}:mappings@tsrg]"
|
||||
data 'MOJMAPS_SHA', sanitizedClientMappings.flatMap{ it.output }, sanitizedServerMappings.flatMap{ it.output }
|
||||
|
||||
data 'MC_UNPACKED', "[net.minecraft:client:${minecraftVersion}]", "[net.minecraft:server:${minecraftVersion}:unpacked]"
|
||||
data 'MC_UNPACKED_SHA', minecraftFiles.client, minecraftFiles.serverExtracted
|
||||
|
||||
data 'MC_OFF', "[net.minecraft:client:${minecraftVersion}:official]", "[net.minecraft:server:${minecraftVersion}:official]"
|
||||
data 'MC_OFF_SHA', clientOfficial.flatMap { it.output }, serverOfficial.flatMap { it.output }
|
||||
|
||||
data 'BINPATCH', '/data/client.lzma', '/data/server.lzma'
|
||||
data 'PATCHED', "[$projectArtifact:client]", "[$projectArtifact:server]"
|
||||
data 'PATCHED_SHA', clientBinPatches.apply.flatMap { it.output }, serverBinPatches.apply.flatMap { it.output }
|
||||
|
||||
def rename = (side, input) -> {
|
||||
step(renamer) {
|
||||
sides = [side]
|
||||
args = [
|
||||
'--input', input,
|
||||
'--output', '{MC_OFF}',
|
||||
'--names', '{MOJMAPS}',
|
||||
'--ann-fix', '--ids-fix', '--src-fix', '--record-fix', '--strip-sigs', '--reverse'
|
||||
]
|
||||
cache '{MC_OFF}', '{MC_OFF_SHA}'
|
||||
}
|
||||
}
|
||||
|
||||
steps = [
|
||||
extract(installertools) {
|
||||
side = 'server'
|
||||
archive '{INSTALLER}'
|
||||
extract 'data/README.txt', '{ROOT}/README.txt'
|
||||
extract 'data/run.sh', '{ROOT}/run.sh'
|
||||
executable '{ROOT}/run.sh'
|
||||
extract 'data/run.bat', '{ROOT}/run.bat'
|
||||
optional 'data/user_jvm_args.txt', '{ROOT}/user_jvm_args.txt'
|
||||
extract 'data/unix_args.txt', "{ROOT}/libraries/${forgedev.mavenArtifact.directory}/unix_args.txt"
|
||||
extract 'data/win_args.txt', "{ROOT}/libraries/${forgedev.mavenArtifact.directory}/win_args.txt"
|
||||
},
|
||||
extractBundle(installertools) {
|
||||
side = 'server'
|
||||
libraries()
|
||||
},
|
||||
extractBundle(installertools) {
|
||||
side = 'server'
|
||||
jar '{MINECRAFT_JAR}', '{MC_UNPACKED}'
|
||||
cache '{MC_UNPACKED}', '{MC_UNPACKED_SHA}'
|
||||
},
|
||||
step(installertools) {
|
||||
args = [
|
||||
'--task', 'DOWNLOAD_MOJMAPS',
|
||||
'--sanitize',
|
||||
'--version', minecraftVersion,
|
||||
'--side', '{SIDE}',
|
||||
'--output', '{MOJMAPS}'
|
||||
]
|
||||
cache '{MOJMAPS}', '{MOJMAPS_SHA}'
|
||||
},
|
||||
rename.call('server', '{MC_UNPACKED}'),
|
||||
rename.call('client', '{MINECRAFT_JAR}'),
|
||||
step(binarypatcher) {
|
||||
args = [
|
||||
'--clean', '{MC_OFF}',
|
||||
'--output', '{PATCHED}',
|
||||
'--apply', '{BINPATCH}',
|
||||
'--data', '--unpatched'
|
||||
]
|
||||
cache '{PATCHED}', '{PATCHED_SHA}'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
launcherJson {
|
||||
// These are flags to preserve glitchy old behavior to make diffing easier, removing when migrating to production is recommended
|
||||
sortLibraries = false // Don't sort, makes things non-deterministic, but easier to diff old versions
|
||||
duplicateLibraries = true // Our 'bootstrap' layer go added twice
|
||||
librariesLast = false // Makes libraries be added before args
|
||||
|
||||
id = launcherId
|
||||
inheritsFrom = minecraftVersion
|
||||
mainClass = 'net.minecraftforge.bootstrap.ForgeBootstrap'
|
||||
gameArgs = [ '--launchTarget', 'forge_client' ]
|
||||
jvmArgs = [ '-Djava.net.preferIPv6Addresses=system' ]
|
||||
}
|
||||
}
|
||||
|
||||
final mdkGradleWrapper = tasks.register('mdkGradleWrapper', Wrapper) {
|
||||
gradleVersion = '9.5.0'
|
||||
scriptFile = layout.buildDirectory.dir("wrapper/" + gradleVersion + "/gradlew")
|
||||
jarFile = layout.buildDirectory.dir("wrapper/" + gradleVersion + "/gradle/wrapper/gradle-wrapper.jar")
|
||||
}
|
||||
|
||||
final mdkZip = tasks.register('mdkZip', Zip) {
|
||||
dependsOn mdkGradleWrapper
|
||||
|
||||
archiveBaseName = project.name
|
||||
archiveClassifier = 'mdk'
|
||||
archiveVersion = project.version
|
||||
destinationDirectory = file('build/libs')
|
||||
from(EXTRA_TXTS)
|
||||
|
||||
from mdkGradleWrapper.map(Wrapper.&getScriptFile)
|
||||
from mdkGradleWrapper.map(Wrapper.&getBatchScript)
|
||||
into('gradle/wrapper/') {
|
||||
from mdkGradleWrapper.map(Wrapper.&getJarFile)
|
||||
from mdkGradleWrapper.map(Wrapper.&getPropertiesFile)
|
||||
}
|
||||
from(rootProject.file('mdk/')){
|
||||
rootProject.file('mdk/gitignore.txt').eachLine {
|
||||
if (!it.trim().isEmpty() && !it.trim().startsWith('#'))
|
||||
exclude it
|
||||
}
|
||||
filter(ReplaceTokens, tokens: [
|
||||
FORGE_VERSION: forgeVersion,
|
||||
FORGE_GROUP: project.group,
|
||||
FORGE_NAME: project.name,
|
||||
MC_VERSION: minecraftVersion,
|
||||
MAPPING_CHANNEL: mappingsChannel,
|
||||
MAPPING_VERSION: mappingsVersion,
|
||||
FORGE_SPEC_VERSION: gitversion.info.tag.split("\\.")[0],
|
||||
MC_NEXT_VERSION: minecraftNextVersion,
|
||||
EVENTBUS_VERSION: libs.versions.eventbus.get()
|
||||
])
|
||||
rename 'gitignore\\.txt', '.gitignore'
|
||||
rename 'gitattributes\\.txt', '.gitattributes'
|
||||
}
|
||||
from(rootProject.file('src/test/java/com/example/examplemod/')) {
|
||||
into('src/main/java/com/example/examplemod/')
|
||||
}
|
||||
from(rootProject.file('src/test/generated/mdk_datagen/')) {
|
||||
into('src/main/resources/')
|
||||
exclude '**/.cache/'
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
tasks.named('javadoc', Javadoc) {
|
||||
description = 'Generates the combined javadocs for the FML projects and the main Forge project'
|
||||
var includedProjects = [ ':fmlcore', ':fmlloader', ':javafmllanguage', ':mclanguage', ':forge-transformers' ]
|
||||
source includedProjects.collect { project(it).sourceSets.main.allJava }
|
||||
classpath = classpath + files(includedProjects.collect { project(it).sourceSets.main.compileClasspath })
|
||||
|
||||
var docsDir = rootProject.file('src/docs/')
|
||||
inputs.dir(docsDir)
|
||||
.withPropertyName('docs resources directory')
|
||||
.withPathSensitivity(PathSensitivity.RELATIVE)
|
||||
.optional()
|
||||
|
||||
failOnError = false
|
||||
|
||||
// Exclude the Minecraft classes if not enabled
|
||||
if (!project.hasProperty('generateAllDocumentation')) {
|
||||
exclude 'net/minecraft/**'
|
||||
exclude 'com/mojang/**'
|
||||
}
|
||||
exclude 'mcp/**'
|
||||
|
||||
options {
|
||||
stylesheetFile = new File(docsDir, 'stylesheet.css')
|
||||
|
||||
tags = [
|
||||
'apiNote:a:<em>API Note:</em>',
|
||||
'implSpec:a:<em>Implementation Requirements:</em>',
|
||||
'implNote:a:<em>Implementation Note:</em>'
|
||||
]
|
||||
|
||||
groups = [
|
||||
'Forge Mod Loader': [
|
||||
'net.minecraftforge.fml.common.asm*',
|
||||
'net.minecraftforge.fml.loading*',
|
||||
'net.minecraftforge.fml.server*'
|
||||
],
|
||||
'FML Core': [
|
||||
'net.minecraftforge.fml',
|
||||
'net.minecraftforge.fml.config*',
|
||||
'net.minecraftforge.fml.event*',
|
||||
'net.minecraftforge.fml.util*'
|
||||
],
|
||||
'FML Common': [
|
||||
'net.minecraftforge.fml.core',
|
||||
'net.minecraftforge.fml.event.config',
|
||||
'net.minecraftforge.fml.event.lifecycle'
|
||||
],
|
||||
'FML Java/MC Language Providers': [
|
||||
'net.minecraftforge.fml.common',
|
||||
'net.minecraftforge.fml.javafmlmod',
|
||||
'net.minecraftforge.fml.mclanguageprovider'
|
||||
],
|
||||
'Minecraft Forge API': [
|
||||
'net.minecraftforge*'
|
||||
]
|
||||
]
|
||||
|
||||
author = false
|
||||
noSince = true
|
||||
noHelp = true
|
||||
|
||||
bottom = "Minecraft Forge is an open source modding API for Minecraft: Java Edition, licensed under the Lesser GNU General Public License, version 2.1."
|
||||
windowTitle = "Minecraft Forge API ${version}"
|
||||
docTitle = "Minecraft Forge API - ${forgeVersion} for Minecraft ${minecraftVersion}"
|
||||
header = "<div style=\"margin-top: 9px;padding: 5px 6px;\"><strong>${forgeVersion} for Minecraft ${minecraftVersion}</strong></div>"
|
||||
}
|
||||
|
||||
doLast {
|
||||
project.copy {
|
||||
from docsDir
|
||||
exclude '/stylesheet.css'
|
||||
into destinationDir
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
publishing {
|
||||
repositories {
|
||||
maven gradleutils.publishingForgeMaven
|
||||
}
|
||||
|
||||
publications.register('mavenJava', MavenPublication) {
|
||||
changelog.publish(it)
|
||||
gradleutils.promote(it)
|
||||
|
||||
artifact universalJar
|
||||
artifact universalJarSrg
|
||||
artifact forgedev.installer.jar
|
||||
artifact mdkZip
|
||||
artifact forgedev.userDev.jar
|
||||
artifact sourcesJar
|
||||
artifact serverShim.jar
|
||||
|
||||
pom {
|
||||
description = project.description
|
||||
|
||||
gradleutils.pom.addRemoteDetails(pom)
|
||||
|
||||
licenses {
|
||||
license gradleutils.pom.licenses.LGPLv2_1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
// These are debugging tasks that are only used when developing ForgeDevPlugin, to bulk test task's config/caching
|
||||
// region ForgeDev Tests
|
||||
def all = tasks.register('all') {}
|
||||
afterEvaluate {
|
||||
if (gradle.startParameter.taskNames.contains('all')) {
|
||||
tasks.forEach { task -> // This forces everything to be realized, which is why we have the startParameter check
|
||||
if (!gradle.startParameter.isTaskGraph() && task.name.startsWith('run')) return
|
||||
if (task.name in [all.name, 'dependencyInsight', 'dependencies', 'dependencyUpdates', 'init', 'buildEnvironment', 'idea',
|
||||
'eclipse', 'javaToolchains', 'outgoingVariants', 'projects', 'properties', 'resolvableConfigurations', 'tasks', 'updateDaemonJvm',
|
||||
'generateActionsWorkflow'
|
||||
]) return
|
||||
if (task.name.startsWith('clean')) return
|
||||
if (task.name == 'checkJarCompatibility') return // This doesn't work in FG6 dev, I think it's cuz we check vs srg names. Will need to look at later
|
||||
all.configure { dependsOn(task) }
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
// endregion
|
||||
// We dont have any unit tests, so disable this error
|
||||
tasks.withType(AbstractTestTask).configureEach {
|
||||
failOnNoDiscoveredTests = false
|
||||
}
|
||||
// This shouldn't be needed according to the Gradle devs, as we never run these commands together.
|
||||
// But in another case of Gradle being gradle, here we are
|
||||
[ 'makePatches', 'compileJava', 'checkLicenseMain', 'updateLicenseMain', 'sourcesJar' ]
|
||||
.each{ tasks.named(it).configure{ mustRunAfter forgedev.patches.apply }}
|
||||
forgedev.runs.configureEach {
|
||||
run.configure { dependsOn finalizeSpawn}
|
||||
runTest.configure { dependsOn finalizeSpawn}
|
||||
}
|
||||
// run gradlew dependencyUpdates --no-parallel
|
||||
//noinspection UnnecessaryQualifiedReference
|
||||
tasks.named('dependencyUpdates', com.github.benmanes.gradle.versions.updates.DependencyUpdatesTask) {
|
||||
resolutionStrategy {
|
||||
mcpBase.dependencies.get().forEach {dep -> force(dep) } // Force all vanilla dependencies to their correct versions
|
||||
force(earlyDisplayLibs.slf4j.jdk14) // Force slf4j implementation, its tied to the api from the manifest
|
||||
force('de.oceanlabs.mcp:mcp_config:' + bootLibs.versions.mcp) // Don't warn abou MCPConfig updates
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
/.gradle/
|
||||
/build/
|
||||
/out/
|
||||
@@ -1,14 +0,0 @@
|
||||
repositories {
|
||||
maven { url = 'https://maven.minecraftforge.net/' }
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'org.ow2.asm:asm:9.8'
|
||||
implementation 'org.ow2.asm:asm-tree:9.8'
|
||||
implementation 'net.minecraftforge:srgutils:0.6.2'
|
||||
implementation 'net.minecraftforge:JarJarMetadata:0.3.27'
|
||||
implementation 'commons-io:commons-io:2.13.0'
|
||||
implementation 'com.google.code.gson:gson:2.10.1'
|
||||
implementation 'org.eclipse.jgit:org.eclipse.jgit:6.7.0.202309050840-r'
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
package net.minecraftforge.forge.tasks
|
||||
|
||||
import org.gradle.api.tasks.*
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.file.ConfigurableFileCollection
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.tasks.bundling.AbstractArchiveTask
|
||||
|
||||
import java.util.zip.ZipFile
|
||||
|
||||
abstract class BundleList extends DefaultTask {
|
||||
@InputFiles abstract ConfigurableFileCollection getConfig()
|
||||
@InputFiles abstract ConfigurableFileCollection getConfigExtra()
|
||||
@InputFile abstract RegularFileProperty getServerBundle()
|
||||
@OutputFile abstract RegularFileProperty getOutput()
|
||||
|
||||
BundleList() {
|
||||
config.setFrom(project.configurations.installer)
|
||||
configExtra.setFrom(project.configurations.installerextra)
|
||||
output.convention(project.layout.buildDirectory.file("$name/output.list"))
|
||||
configure {
|
||||
dependsOn(project.tasks.universalJar)
|
||||
inputs.file(project.tasks.universalJar.archiveFile)
|
||||
dependsOn(project.tasks.applyServerBinPatches)
|
||||
inputs.file(project.tasks.applyServerBinPatches.output)
|
||||
}
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
void run() {
|
||||
def entries = [:] as TreeMap
|
||||
def resolved = project.configurations.installer.resolvedConfiguration.resolvedArtifacts
|
||||
for (def dep : resolved) {
|
||||
def info = Util.getMavenInfoFromDep(dep)
|
||||
//println("$dep.file.sha1\t$info.name\t$info.path")
|
||||
entries.put("$info.art.group:$info.art.name", "$dep.file.sha256\t$info.name\t$info.path")
|
||||
}
|
||||
|
||||
resolved = project.configurations.installerextra.resolvedConfiguration.resolvedArtifacts
|
||||
for (def dep : resolved) {
|
||||
def info = Util.getMavenInfoFromDep(dep)
|
||||
//println("$dep.file.sha1\t$info.name\t$info.path")
|
||||
entries.put("$info.art.group:$info.art.name", "$dep.file.sha256\t$info.name\t$info.path")
|
||||
}
|
||||
|
||||
var packed = (AbstractArchiveTask) project.tasks.universalJar
|
||||
var info = Util.getMavenInfoFromTask(packed)
|
||||
def file = packed.archiveFile.get().asFile
|
||||
entries.put("$info.art.group:$info.art.name:$info.art.classifier", "$file.sha256\t$info.name\t$info.path")
|
||||
|
||||
var classifier = 'server'
|
||||
var genned = project.tasks.applyServerBinPatches
|
||||
info = Util.getMavenInfoFromTask(genned, classifier)
|
||||
file = genned.output.get().asFile
|
||||
entries.put("$info.art.group:$info.art.name:$info.art.classifier", "$file.sha256\t$info.name\t$info.path")
|
||||
|
||||
try (def zip = new ZipFile(serverBundle.get().asFile)) {
|
||||
def entry = zip.getEntry('META-INF/libraries.list')
|
||||
def data = zip.getInputStream(entry).text.split('\n')
|
||||
for (def line : data) {
|
||||
def (sha, artifact, path) = line.split('\t')
|
||||
def (group, name, other) = artifact.split(':', 3)
|
||||
//println("Group: $group Name: $name")
|
||||
def key = "$group:$name"
|
||||
if (other.indexOf(':') != -1){
|
||||
key += ':' + other.split(':', 2)[1]
|
||||
//println("\tKey: $key")
|
||||
}
|
||||
if (!entries.containsKey(key))
|
||||
entries.put(key, line)
|
||||
}
|
||||
}
|
||||
|
||||
output.get().asFile.text = entries.values().join('\n')
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package net.minecraftforge.forge.tasks
|
||||
|
||||
import groovy.json.JsonBuilder
|
||||
import groovy.transform.CompileStatic
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.tasks.InputFile
|
||||
import org.gradle.api.tasks.Internal
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.objectweb.asm.tree.ClassNode
|
||||
import org.objectweb.asm.tree.FieldNode
|
||||
import org.objectweb.asm.tree.MethodNode
|
||||
|
||||
@CompileStatic
|
||||
abstract class BytecodeFinder extends DefaultTask {
|
||||
@InputFile abstract RegularFileProperty getJar()
|
||||
// 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()
|
||||
|
||||
var outputFile = output.get().asFile
|
||||
if (outputFile.exists())
|
||||
outputFile.delete()
|
||||
|
||||
pre()
|
||||
|
||||
Util.processClassNodes(jar.get().asFile, this.&process)
|
||||
|
||||
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) {}
|
||||
protected post() {}
|
||||
protected abstract Object getData()
|
||||
}
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
package net.minecraftforge.forge.tasks
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.tasks.Internal
|
||||
import org.objectweb.asm.Opcodes
|
||||
import org.objectweb.asm.tree.ClassNode
|
||||
import org.objectweb.asm.tree.MethodNode
|
||||
|
||||
@CompileStatic
|
||||
abstract class BytecodePredicateFinder extends BytecodeFinder {
|
||||
|
||||
@Internal
|
||||
abstract Property<Closure<Boolean>> getPredicate()
|
||||
|
||||
private final Map<String, List<String>> matches = new TreeMap<>(Comparator.naturalOrder())
|
||||
|
||||
@Override
|
||||
protected process(ClassNode parent, MethodNode node) {
|
||||
for (final current : node.instructions) {
|
||||
if (predicate.get().call(parent, node, current)) {
|
||||
var methods = matches.compute(parent.name, { k, v -> v ?: new ArrayList<>() })
|
||||
|
||||
// only add non-synthetic methods
|
||||
if ((node.access & Opcodes.ACC_SYNTHETIC) == 0) {
|
||||
methods.add(node.name + node.desc)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Internal
|
||||
@Override
|
||||
protected Object getData() {
|
||||
var array = new ArrayList<Object>()
|
||||
matches.forEach { c, m ->
|
||||
{
|
||||
array.add(
|
||||
'class': c,
|
||||
'methods': m
|
||||
)
|
||||
}
|
||||
}
|
||||
return array ?: { throw new RuntimeException('Failed to find any targets, please ensure that method names and descriptors are correct.') }()
|
||||
}
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
package net.minecraftforge.forge.tasks;
|
||||
|
||||
import groovy.transform.CompileDynamic;
|
||||
import groovy.transform.CompileStatic;
|
||||
|
||||
import java.util.Properties;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Map;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.Reader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Writer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Eclipse config files are literally just java properties, with the header cleaned up.
|
||||
* https://github.com/eclipse/buildship/blob/5b2c7fca7fa86cd74d71b3c099c7b1559eba038e/org.eclipse.buildship.core/src/main/java/org/eclipse/buildship/core/internal/configuration/PreferenceStore.java#L238
|
||||
*
|
||||
* This does the same thing, as well as sorting alphabetically.
|
||||
* It also ignores all comments. We can add them latter if someone cares.
|
||||
*/
|
||||
@CompileStatic
|
||||
public class CleanProperties extends Properties {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final String LINE_SEP = System.getProperty("line.separator");
|
||||
private static final String UNIX_LINE_SEP = "\n";
|
||||
private static final Charset ENCODING = StandardCharsets.UTF_8;
|
||||
|
||||
public CleanProperties load(File input) throws IOException {
|
||||
if (input.exists()) {
|
||||
try (Reader is = new InputStreamReader(new FileInputStream(input), ENCODING)) {
|
||||
super.load(is);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public void store(File out) throws IOException {
|
||||
if (!out.getParentFile().exists())
|
||||
out.getParentFile().mkdirs();
|
||||
|
||||
try (OutputStream os = new FileOutputStream(out)) {
|
||||
store(os, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Enumeration<Object> keys() {
|
||||
Set<Object> ret = new TreeSet<>();
|
||||
for (Enumeration<?> e = super.keys(); e.hasMoreElements();)
|
||||
ret.add(e.nextElement());
|
||||
return Collections.enumeration(ret);
|
||||
}
|
||||
|
||||
@CompileDynamic
|
||||
@Override
|
||||
public Set<Map.Entry<Object, Object>> entrySet() {
|
||||
Set<Map.Entry<Object, Object>> ret = new TreeSet<>((l, r) -> ((String)l.getKey()).compareTo((String)r.getKey()));
|
||||
ret.addAll(super.entrySet());
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void store(OutputStream out, String comments) throws IOException {
|
||||
out.write(clean().getBytes(ENCODING));
|
||||
out.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void store(Writer out, String comments) throws IOException {
|
||||
out.write(clean());
|
||||
out.flush();
|
||||
}
|
||||
|
||||
private String clean() throws IOException {
|
||||
ByteArrayOutputStream tmp = new ByteArrayOutputStream();
|
||||
try {
|
||||
super.store(tmp, null);
|
||||
} finally {
|
||||
tmp.close();
|
||||
}
|
||||
|
||||
String ret = tmp.toString(ENCODING).replace(LINE_SEP, UNIX_LINE_SEP);
|
||||
ret = ret.substring(ret.indexOf(UNIX_LINE_SEP) + 1);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package net.minecraftforge.forge.tasks
|
||||
|
||||
import groovy.transform.stc.FirstParam
|
||||
|
||||
import java.util.function.BiConsumer
|
||||
|
||||
public class ClosureHelper {
|
||||
BiConsumer<String, Closure> callback
|
||||
|
||||
public ClosureHelper(Closure cl, BiConsumer<String, Closure> callback) {
|
||||
this.callback = callback
|
||||
apply(this, cl)
|
||||
}
|
||||
|
||||
|
||||
def methodMissing(String name, Object args) {
|
||||
if (!args.class.isArray()) return
|
||||
Object[] aargs = (Object[])args
|
||||
|
||||
if (aargs.length == 1 && aargs[0] instanceof Closure) {
|
||||
this.callback.accept(name, (Closure)aargs[0])
|
||||
} else {
|
||||
throw new IllegalArgumentException('Unknown method: "' + name + '" with arguments ' + args + ' for ' + this)
|
||||
}
|
||||
}
|
||||
|
||||
static <T> T apply(T obj, @DelegatesTo(value = FirstParam, strategy = Closure.DELEGATE_FIRST) Closure cl) {
|
||||
cl.delegate = obj
|
||||
cl.resolveStrategy = Closure.DELEGATE_FIRST
|
||||
cl()
|
||||
return obj
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
package net.minecraftforge.forge.tasks
|
||||
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.file.ConfigurableFileCollection
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.tasks.InputFile
|
||||
import org.gradle.api.tasks.Internal
|
||||
import org.gradle.api.tasks.OutputFile
|
||||
import org.gradle.api.tasks.OutputFiles
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.gradle.api.tasks.OutputDirectory
|
||||
|
||||
import java.nio.file.Files
|
||||
|
||||
abstract class DownloadLibraries extends DefaultTask {
|
||||
@InputFile abstract RegularFileProperty getInput()
|
||||
@OutputDirectory abstract DirectoryProperty getOutput()
|
||||
@OutputFile abstract RegularFileProperty getLibrariesOutput()
|
||||
|
||||
DownloadLibraries() {
|
||||
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
|
||||
var libraries = new ArrayList<String>()
|
||||
|
||||
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 != 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 != target.sha1()) {
|
||||
throw new IllegalStateException("Failed to download ${art.url} to ${target.canonicalPath} SHA Mismatch")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Files.write(librariesOutput.get().asFile.toPath(), libraries)
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package net.minecraftforge.forge.tasks
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.tasks.*
|
||||
|
||||
import java.util.zip.ZipFile
|
||||
|
||||
@CompileStatic
|
||||
abstract class ExtractFile extends DefaultTask {
|
||||
@InputFile abstract RegularFileProperty getInput()
|
||||
@Input abstract Property<String> getTarget()
|
||||
@OutputFile abstract RegularFileProperty getOutput()
|
||||
|
||||
@TaskAction
|
||||
protected void exec() {
|
||||
var zip = new ZipFile(input.get().asFile);
|
||||
var entry = zip.getEntry(target.get())
|
||||
if (entry == null)
|
||||
throw new IllegalStateException(input.get().asFile.absolutePath + " does not contain " + target.get())
|
||||
var stream = zip.getInputStream(entry)
|
||||
output.get().asFile.withOutputStream { out ->
|
||||
stream.transferTo(out)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
package net.minecraftforge.forge.tasks
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
import groovy.transform.EqualsAndHashCode
|
||||
|
||||
import org.gradle.api.tasks.*
|
||||
import org.objectweb.asm.tree.AbstractInsnNode
|
||||
import org.objectweb.asm.tree.ClassNode
|
||||
import org.objectweb.asm.tree.MethodNode
|
||||
|
||||
import static org.objectweb.asm.Opcodes.*
|
||||
|
||||
abstract class FieldCompareFinder extends BytecodeFinder {
|
||||
@Nested
|
||||
Map<String, Search> fields = [:] as HashMap
|
||||
@Internal
|
||||
Map<Search, String> fieldsReverse = [:] as HashMap
|
||||
@Internal
|
||||
Map<String, Set<ObjectTarget>> targets = [:] as TreeMap
|
||||
|
||||
@Override
|
||||
protected pre() {
|
||||
//fields.each{ k,v -> logger.lifecycle("Fields: " + k + ' ' + v) }
|
||||
}
|
||||
|
||||
@Override
|
||||
protected process(ClassNode parent, MethodNode node) {
|
||||
AbstractInsnNode last = null
|
||||
def parentInstance = new ObjectTarget(owner: parent.name, name: '', desc: '')
|
||||
for (int x = 0; x < node.instructions.size(); x++) {
|
||||
def current = node.instructions.get(x)
|
||||
if (current.opcode === IF_ACMPEQ || current.opcode === IF_ACMPNE) {
|
||||
if (last !== null && (last.opcode === GETSTATIC || last.opcode === GETFIELD)) {
|
||||
def target = new Search(cls: last.owner, name: last.name)
|
||||
def wanted = fieldsReverse.get(target)
|
||||
def original = fields.get(wanted)
|
||||
def instance = new ObjectTarget(owner: parent.name, name: node.name, desc: node.desc)
|
||||
if (wanted !== null && (original.blacklist === null || (!original.blacklist.contains(instance) && !original.blacklist.contains(parentInstance)))) {
|
||||
targets.computeIfAbsent(wanted, { k -> new TreeSet() }).add(instance)
|
||||
}
|
||||
}
|
||||
}
|
||||
last = current
|
||||
}
|
||||
}
|
||||
|
||||
@Internal
|
||||
@Override
|
||||
protected Object getData() {
|
||||
def ret = [:] as HashMap
|
||||
targets.forEach{ k, v ->
|
||||
def e = fields.get(k)
|
||||
ret[k] = [
|
||||
cls: e.cls,
|
||||
name: e.name,
|
||||
replacement: e.replacement,
|
||||
targets: v
|
||||
]
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
@CompileStatic
|
||||
@EqualsAndHashCode(excludes = ['replacement', 'blacklist'])
|
||||
static class Search {
|
||||
@Input
|
||||
String cls
|
||||
|
||||
@Input
|
||||
String name
|
||||
|
||||
@Input
|
||||
String replacement
|
||||
|
||||
@Nested
|
||||
@Optional
|
||||
Set<ObjectTarget> blacklist
|
||||
|
||||
@Override
|
||||
String toString() {
|
||||
return cls + '.' + name
|
||||
}
|
||||
|
||||
def blacklist(String owner, String name, String desc) {
|
||||
if (blacklist === null)
|
||||
blacklist = new HashSet<>()
|
||||
blacklist.add(new ObjectTarget(owner: owner, name: name, desc: desc))
|
||||
}
|
||||
def blacklist(String owner) {
|
||||
blacklist(owner, '', '')
|
||||
}
|
||||
}
|
||||
|
||||
void fields(Closure cl) {
|
||||
new ClosureHelper(cl, {name, ccl ->
|
||||
def search = ClosureHelper.apply(new Search(), ccl)
|
||||
this.fields.put(name, search)
|
||||
this.fieldsReverse.put(search, name)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
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<String> interfaces = []
|
||||
Map<String, Method> methods = [:]
|
||||
Map<String, Field> fields = [:]
|
||||
List<Annotation> annotations = []
|
||||
|
||||
@TupleConstructor
|
||||
static final class Method implements Annotatable {
|
||||
int access
|
||||
String override
|
||||
List<Annotation> annotations = []
|
||||
}
|
||||
|
||||
@TupleConstructor
|
||||
static final class Field implements Annotatable {
|
||||
int access
|
||||
String desc
|
||||
List<Annotation> annotations = []
|
||||
}
|
||||
|
||||
@TupleConstructor
|
||||
static final class Annotation {
|
||||
String desc
|
||||
}
|
||||
|
||||
private static final Gson GSON = new Gson()
|
||||
static Map<String, InheritanceData> parse(File file) {
|
||||
try (final reader = file.newReader()) {
|
||||
return GSON.fromJson(reader, new TypeToken<Map<String, InheritanceData>>() {})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@CompileStatic
|
||||
interface Annotatable {
|
||||
List<InheritanceData.Annotation> getAnnotations()
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
package net.minecraftforge.forge.tasks
|
||||
|
||||
import org.gradle.api.file.DuplicatesStrategy
|
||||
import org.gradle.api.tasks.bundling.AbstractArchiveTask
|
||||
import org.gradle.api.tasks.bundling.Zip
|
||||
import org.gradle.api.tasks.*
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.provider.Property
|
||||
import groovy.json.JsonSlurper
|
||||
|
||||
abstract class InstallerJar extends Zip {
|
||||
@Input @Optional abstract Property<Boolean> getFat()
|
||||
@Input @Optional abstract Property<Boolean> getOffline()
|
||||
|
||||
InstallerJar() {
|
||||
archiveClassifier.set('installer')
|
||||
archiveExtension.set('jar') // Needs to be Zip task to not override Manifest, so set extension
|
||||
destinationDirectory.set(project.layout.buildDirectory.dir('libs'))
|
||||
fat.convention(false)
|
||||
offline.convention(false)
|
||||
|
||||
def installerJson = project.tasks.installerJson
|
||||
def launcherJson = project.tasks.launcherJson
|
||||
def downloadInstaller = project.tasks.downloadInstaller
|
||||
|
||||
dependsOn(installerJson, launcherJson, downloadInstaller, project.configurations.installer)
|
||||
from(installerJson, launcherJson)
|
||||
|
||||
from(project.rootProject.file('/src/main/resources/url.png'))
|
||||
project.afterEvaluate {
|
||||
from(project.zipTree(downloadInstaller.output)) {
|
||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||
}
|
||||
|
||||
if (fat.get() || offline.get()) {
|
||||
def cfg = project.tasks.register(name + "Config", Configure)
|
||||
cfg.get().configure {
|
||||
parent = this
|
||||
dependsOn(project.tasks.installerJson, project.tasks.launcherJson)
|
||||
}
|
||||
dependsOn(cfg)
|
||||
} else {
|
||||
// Things we ALWAYS bundle, this just the server shim jar, because the installer spec only says to extract the file.
|
||||
// I should make it allow downloads but thats a spec break, and this is just a ~14KB jar
|
||||
[
|
||||
project.tasks.serverShimJar // Server bootstrap executable jar
|
||||
].forEach { AbstractArchiveTask packed ->
|
||||
def path = Util.getMavenInfoFromTask(packed).path
|
||||
from(packed) {
|
||||
rename { "maven/$path" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static abstract class Configure extends DefaultTask {
|
||||
public Zip parent
|
||||
private int count = 0;
|
||||
|
||||
@TaskAction
|
||||
protected void exec() {
|
||||
def deps = [:] as java.util.TreeMap
|
||||
|
||||
// Gather all things that need downloading
|
||||
[
|
||||
project.tasks.installerJson,
|
||||
project.tasks.launcherJson
|
||||
].each { task ->
|
||||
def json = task.output.get().asFile.json
|
||||
json.libraries.each { lib ->
|
||||
if (lib.downloads?.artifact?.url !== null && !lib.downloads.artifact.url.isEmpty())
|
||||
deps.put(lib.name, lib.downloads.artifact)
|
||||
}
|
||||
}
|
||||
|
||||
// First find things we build in this project.
|
||||
[
|
||||
project.tasks.universalJar, // Forge runtime code
|
||||
project.tasks.serverShimJar // Shim jar for dedicated server
|
||||
].forEach { AbstractArchiveTask packed ->
|
||||
def name = Util.getMavenInfoFromTask(packed).name
|
||||
def info = deps.remove(name)
|
||||
if (info !== null) {
|
||||
println("Adding: $packed.path $name")
|
||||
parent.from(packed) {
|
||||
rename { "maven/$info.path" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find any artifacts from the 'installer' config
|
||||
// This config specifies the runtime files we intend for the interaller to have.
|
||||
// And are typically what we would be developing and testing alongside Forge.
|
||||
// So we may have local modified versions
|
||||
def cfg = project.configurations.installer
|
||||
while (cfg != null) {
|
||||
//println('')
|
||||
def resolved = cfg.resolvedConfiguration.resolvedArtifacts
|
||||
int found = 0
|
||||
for (def dep : resolved) {
|
||||
def name = Util.getMavenInfoFromDep(dep).name
|
||||
def info = deps.remove(name)
|
||||
if (info == null) {
|
||||
//println("Skipping: $name")
|
||||
continue
|
||||
}
|
||||
//println("-$name")
|
||||
found++
|
||||
addFile(dep.file, info)
|
||||
}
|
||||
|
||||
if (deps.isEmpty()) {
|
||||
cfg = null
|
||||
continue
|
||||
}
|
||||
|
||||
// Prevent infinite loops if something fucky happens
|
||||
if (found == 0)
|
||||
throw new IllegalStateException("Failed to find any installer dependencies")
|
||||
|
||||
def seen = [] as Set
|
||||
cfg = project.configurations.detachedConfiguration()
|
||||
cfg.transitive = false
|
||||
for (def key : deps.keySet()) {
|
||||
def (group, artifact, other) = key.split(':', 3)
|
||||
// Only resolve unique group:artifact so we don't get version overrides
|
||||
if (seen.add(group + ':' + artifact)) {
|
||||
//println("+$key")
|
||||
cfg.dependencies.add(project.dependencies.create(key))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void addFile(file, info) {
|
||||
boolean pack = parent.offline.get() || info.url.isEmpty()
|
||||
|
||||
// If it's a offline jar just always pack
|
||||
if (!pack) {
|
||||
try {
|
||||
var remote = new URL("${info.url}.sha1").getText('UTF-8')
|
||||
pack = info.sha1 != remote
|
||||
} catch (FileNotFoundException e) {
|
||||
pack = !info.url.startsWith('https://libraries.minecraft.net/')
|
||||
// Oh noes its not there!
|
||||
}
|
||||
}
|
||||
|
||||
if (pack) {
|
||||
println("Adding: $file.absolutePath")
|
||||
parent.from(file) {
|
||||
rename { "maven/$info.path" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
package net.minecraftforge.forge.tasks
|
||||
|
||||
import groovy.json.JsonBuilder
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.file.ConfigurableFileCollection
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.provider.SetProperty
|
||||
import org.gradle.api.tasks.*
|
||||
import org.gradle.api.tasks.bundling.AbstractArchiveTask
|
||||
|
||||
import java.nio.file.Files
|
||||
|
||||
abstract class InstallerJson extends DefaultTask {
|
||||
@OutputFile abstract RegularFileProperty getOutput()
|
||||
@InputFiles abstract ConfigurableFileCollection getInput()
|
||||
@Input @Optional final Map<String, Object> libraries = new LinkedHashMap<>()
|
||||
@Input Map<String, Object> json = new LinkedHashMap<>()
|
||||
@InputFile abstract RegularFileProperty getIcon()
|
||||
@Input abstract Property<String> getLauncherJsonName()
|
||||
@Input abstract Property<String> getLogo()
|
||||
@Input abstract Property<String> getMirrors()
|
||||
@Input abstract Property<String> getWelcome()
|
||||
|
||||
InstallerJson() {
|
||||
launcherJsonName.convention('/version.json')
|
||||
logo.convention('/big_logo.png')
|
||||
mirrors.convention('https://files.minecraftforge.net/mirrors-2.0.json')
|
||||
welcome.convention("Welcome to the ${project.name.capitalize()} installer.")
|
||||
output.convention(project.layout.buildDirectory.file('libs/install_profile.json'))
|
||||
|
||||
project.afterEvaluate {
|
||||
[
|
||||
project.tasks.universalJar,
|
||||
project.tasks.serverShimJar
|
||||
].forEach { packed ->
|
||||
dependsOn(packed)
|
||||
input.from packed.archiveFile
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
protected void exec() {
|
||||
def libs = libraries
|
||||
[
|
||||
project.tasks.universalJar,
|
||||
project.tasks.serverShimJar
|
||||
].forEach { AbstractArchiveTask packed ->
|
||||
def info = Util.getMavenInfoFromTask(packed)
|
||||
libs.put(info.name, [
|
||||
name: info.name,
|
||||
downloads: [
|
||||
artifact: [
|
||||
path: info.path,
|
||||
url: "https://maven.minecraftforge.net/$info.path",
|
||||
sha1: packed.archiveFile.get().asFile.sha1(),
|
||||
size: packed.archiveFile.get().asFile.length()
|
||||
]
|
||||
]
|
||||
])
|
||||
}
|
||||
json.libraries = libs.values().sort{a,b -> a.name.compareTo(b.name)}
|
||||
json.icon = "data:image/png;base64," + new String(Base64.getEncoder().encode(Files.readAllBytes(icon.get().asFile.toPath())))
|
||||
json.json = launcherJsonName.get()
|
||||
json.logo = logo.get()
|
||||
if (!mirrors.get().isEmpty())
|
||||
json.mirrorList = mirrors.get()
|
||||
json.welcome = welcome.get()
|
||||
|
||||
Files.writeString(output.get().getAsFile().toPath(), new JsonBuilder(json).toPrettyString())
|
||||
}
|
||||
}
|
||||
@@ -1,415 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.forge.tasks;
|
||||
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import net.minecraftforge.jarjar.metadata.ContainedJarIdentifier;
|
||||
import net.minecraftforge.jarjar.metadata.ContainedJarMetadata;
|
||||
import net.minecraftforge.jarjar.metadata.ContainedVersion;
|
||||
import net.minecraftforge.jarjar.metadata.Metadata;
|
||||
import net.minecraftforge.jarjar.metadata.MetadataIOHandler;
|
||||
import net.minecraftforge.jarjar.metadata.json.ArtifactVersionSerializer;
|
||||
import net.minecraftforge.jarjar.metadata.json.ContainedJarIdentifierSerializer;
|
||||
import net.minecraftforge.jarjar.metadata.json.ContainedJarMetadataSerializer;
|
||||
import net.minecraftforge.jarjar.metadata.json.ContainedVersionSerializer;
|
||||
import net.minecraftforge.jarjar.metadata.json.MetadataSerializer;
|
||||
import net.minecraftforge.jarjar.metadata.json.VersionRangeSerializer;
|
||||
import org.apache.maven.artifact.versioning.ArtifactVersion;
|
||||
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
|
||||
import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
|
||||
import org.apache.maven.artifact.versioning.VersionRange;
|
||||
import org.codehaus.groovy.runtime.InvokerHelper;
|
||||
import org.gradle.api.Action;
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.artifacts.ConfigurationContainer;
|
||||
import org.gradle.api.artifacts.Dependency;
|
||||
import org.gradle.api.artifacts.ExternalModuleDependency;
|
||||
import org.gradle.api.artifacts.FileCollectionDependency;
|
||||
import org.gradle.api.artifacts.MinimalExternalModuleDependency;
|
||||
import org.gradle.api.artifacts.ModuleDependency;
|
||||
import org.gradle.api.artifacts.ModuleIdentifier;
|
||||
import org.gradle.api.artifacts.ModuleVersionIdentifier;
|
||||
import org.gradle.api.artifacts.ResolvedArtifact;
|
||||
import org.gradle.api.file.ProjectLayout;
|
||||
import org.gradle.api.file.RegularFileProperty;
|
||||
import org.gradle.api.provider.Provider;
|
||||
import org.gradle.api.provider.SetProperty;
|
||||
import org.gradle.api.tasks.Input;
|
||||
import org.gradle.api.tasks.OutputFile;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
// TODO SUPER SUPER SUPER UGLY, CLEAN UP IN FORGEDEV 7
|
||||
@Deprecated(forRemoval = true) // Will be moved to JarJar plugin in ForgeDev 7
|
||||
public abstract class JarJarMetadataOptions extends DefaultTask {
|
||||
private static final Gson GSON = new GsonBuilder()
|
||||
.registerTypeAdapter(VersionRange.class, new VersionRangeSerializer())
|
||||
.registerTypeAdapter(ArtifactVersion.class, new ArtifactVersionSerializer())
|
||||
.registerTypeAdapter(DefaultArtifactVersion.class, new ArtifactVersionSerializer())
|
||||
.registerTypeAdapter(ContainedJarIdentifier.class, new ContainedJarIdentifierSerializer())
|
||||
.registerTypeAdapter(ContainedJarMetadata.class, new ContainedJarMetadataSerializer())
|
||||
.registerTypeAdapter(ContainedVersion.class, new ContainedVersionSerializer())
|
||||
.registerTypeAdapter(Metadata.class, new MetadataSerializer())
|
||||
.setPrettyPrinting()
|
||||
.create();
|
||||
|
||||
protected abstract @Input SetProperty<ResolvedDependencyInfoImpl> getResolvedDependencies();
|
||||
|
||||
protected abstract @OutputFile RegularFileProperty getMetadataFile();
|
||||
|
||||
protected abstract @Inject ProjectLayout getLayout();
|
||||
|
||||
// NOTE: I'm not adding a non-provider version. please just use the version catalog entries for now.
|
||||
public void add(Provider<MinimalExternalModuleDependency> dependency, Action<? super ResolvedDependencyInfo> action) {
|
||||
this.getResolvedDependencies().add(dependency.map(d -> {
|
||||
var ret = ResolvedDependencyInfoImpl.from(this.getProject().getConfigurations(), d);
|
||||
action.execute(ret);
|
||||
return ret;
|
||||
}));
|
||||
}
|
||||
|
||||
@Inject
|
||||
public JarJarMetadataOptions() {
|
||||
this.getMetadataFile().convention(this.getLayout().getBuildDirectory().file(this.getName() + "/options.json"));
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
protected void exec() {
|
||||
record ForgeLocaterOptions(String resource, String layer, String id, List<ContainedJarMetadata> deps, ContainedJarMetadata meta, boolean nested) { }
|
||||
|
||||
var resolved = this.getResolvedDependencies().get();
|
||||
var jars = new ArrayList<ForgeLocaterOptions>(resolved.size());
|
||||
for (var dependency : resolved) {
|
||||
var deps = new ArrayList<ContainedJarMetadata>();
|
||||
try (var zip = new ZipFile(dependency.artifact)) {
|
||||
var entry = zip.getEntry("META-INF/jarjar/metadata.json");
|
||||
if (entry != null) {
|
||||
try (var stream = zip.getInputStream(entry)) {
|
||||
var meta = MetadataIOHandler.fromStream(stream).orElse(null);
|
||||
if (meta == null)
|
||||
throw new IllegalStateException("Corrupt metadata.json in " + dependency.artifact.getAbsolutePath());
|
||||
for (var dep : meta.jars())
|
||||
deps.add(new ContainedJarMetadata(dep.identifier(), dep.version(), "", dep.isObfuscated()));
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
jars.add(new ForgeLocaterOptions(
|
||||
dependency.resource,
|
||||
dependency.layer,
|
||||
dependency.identifier,
|
||||
deps,
|
||||
new ContainedJarMetadata(
|
||||
new ContainedJarIdentifier(validateGroup(dependency), dependency.module.getName()),
|
||||
new ContainedVersion(null, parseVersion(dependency)),
|
||||
Objects.requireNonNull(dependency.path, "Dependency path is unspecified: " + dependency.asString),
|
||||
false
|
||||
),
|
||||
dependency.nested
|
||||
));
|
||||
}
|
||||
|
||||
try {
|
||||
record Meta(List<ForgeLocaterOptions> options){}
|
||||
|
||||
Files.writeString(
|
||||
this.getMetadataFile().getAsFile().get().toPath(),
|
||||
GSON.toJson(new Meta(jars), Meta.class)
|
||||
);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private String validateGroup(ResolvedDependencyInfoImpl dependency) {
|
||||
try {
|
||||
return Objects.requireNonNull(dependency.module.getGroup());
|
||||
} catch (NullPointerException e) {
|
||||
throw new IllegalArgumentException("Module dependency has no group: " + dependency.asString, e);
|
||||
}
|
||||
}
|
||||
|
||||
private ArtifactVersion parseVersion(ResolvedDependencyInfoImpl resolved) {
|
||||
try {
|
||||
return VersionRange.createFromVersionSpec(Objects.requireNonNull(resolved.version)).getRecommendedVersion();
|
||||
} catch (InvalidVersionSpecificationException e) {
|
||||
throw new IllegalArgumentException("Version is invalid for: " + resolved.asString, e);
|
||||
} catch (NullPointerException e) {
|
||||
throw new IllegalArgumentException("Version is unspecified for: " + resolved.asString, e);
|
||||
}
|
||||
}
|
||||
|
||||
private VersionRange parseVersionRange(ResolvedDependencyInfoImpl dependency) {
|
||||
if (dependency.hasManuallySpecifiedRange) {
|
||||
try {
|
||||
return VersionRange.createFromVersionSpec(dependency.versionRange);
|
||||
} catch (InvalidVersionSpecificationException e) {
|
||||
throw new IllegalArgumentException("Version is invalid for: " + dependency.asString, e);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
return VersionRange.createFromVersionSpec("[%s,)".formatted(Objects.requireNonNull(dependency.versionRange)));
|
||||
} catch (InvalidVersionSpecificationException e) {
|
||||
throw new IllegalArgumentException("Version range is invalid for: " + dependency.asString, e);
|
||||
} catch (NullPointerException e) {
|
||||
throw new IllegalArgumentException("Version is unspecified for: " + dependency.asString, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface ResolvedDependencyInfo {
|
||||
void containedJarMetadata(Action<? super ContainedJarMetadataInfo> action);
|
||||
|
||||
void setResource(String resource);
|
||||
|
||||
void setLayer(String layer);
|
||||
|
||||
void setId(String identifier);
|
||||
|
||||
void setNested(boolean nested);
|
||||
|
||||
interface ContainedJarMetadataInfo {
|
||||
void setGroup(String group);
|
||||
|
||||
void setName(String name);
|
||||
|
||||
void setPath(String version);
|
||||
}
|
||||
}
|
||||
|
||||
static final class ResolvedDependencyInfoImpl implements ResolvedDependencyInfo, ResolvedDependencyInfo.ContainedJarMetadataInfo, Serializable {
|
||||
private static final @Serial long serialVersionUID = -7577318115877822993L;
|
||||
|
||||
final MinimalModuleVersionIdentifier module;
|
||||
final String version;
|
||||
String versionRange;
|
||||
boolean hasManuallySpecifiedRange;
|
||||
boolean nested;
|
||||
final File artifact;
|
||||
String path;
|
||||
String resource;
|
||||
String layer;
|
||||
String identifier;
|
||||
final String asString;
|
||||
|
||||
public ResolvedDependencyInfoImpl(MinimalModuleVersionIdentifier module, String version, File artifact, String asString) {
|
||||
this.module = module;
|
||||
this.version = version;
|
||||
this.artifact = artifact;
|
||||
this.asString = asString;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void containedJarMetadata(Action<? super ContainedJarMetadataInfo> action) {
|
||||
action.execute(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGroup(String group) {
|
||||
this.module.group = group;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setName(String name) {
|
||||
this.module.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPath(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setResource(String resource) {
|
||||
this.resource = resource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLayer(String layer) {
|
||||
this.layer = layer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setId(String identifier) {
|
||||
this.identifier = identifier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNested(boolean nested) {
|
||||
this.nested = nested;
|
||||
}
|
||||
|
||||
static Set<File> getFiles(Set<ResolvedDependencyInfoImpl> resolvedDependencies) {
|
||||
var ret = new HashSet<File>(resolvedDependencies.size());
|
||||
for (var dependency : resolvedDependencies) {
|
||||
ret.add(dependency.artifact);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ResolvedDependencyInfoImpl from(ConfigurationContainer configurations, Dependency dependency) {
|
||||
var group = dependency.getGroup();
|
||||
var name = dependency.getName();
|
||||
var version = dependency.getVersion();
|
||||
|
||||
if (dependency instanceof FileCollectionDependency filesDependency) {
|
||||
File artifact;
|
||||
try {
|
||||
artifact = filesDependency.getFiles().getSingleFile();
|
||||
} catch (IllegalStateException e) {
|
||||
// TODO fileCollectionDependencyIsNotSingleFile
|
||||
throw e;
|
||||
}
|
||||
|
||||
return new ResolvedDependencyInfoImpl(
|
||||
new MinimalModuleVersionIdentifier(group, name, version),
|
||||
version,
|
||||
artifact,
|
||||
filesDependency.toString()
|
||||
);
|
||||
} else if (dependency instanceof ModuleDependency moduleDependency) {
|
||||
moduleDependency = moduleDependency.copy();
|
||||
if (moduleDependency instanceof ExternalModuleDependency externalModuleDependency) {
|
||||
externalModuleDependency.version(v -> v.strictly(version.toString()));
|
||||
}
|
||||
|
||||
var detachedConfiguration = configurations.detachedConfiguration(moduleDependency);
|
||||
detachedConfiguration.setTransitive(false);
|
||||
|
||||
ResolvedDependencyInfoImpl ret = null;
|
||||
for (var artifact : detachedConfiguration.getResolvedConfiguration().getFirstLevelModuleDependencies().iterator().next().getModuleArtifacts()) {
|
||||
var fileName = getFileName(artifact);
|
||||
if (!fileName.endsWith(".jar"))
|
||||
continue;
|
||||
|
||||
if (ret != null)
|
||||
throw new IllegalArgumentException("Module dependency has too many Jar artifacts: " + moduleDependency);
|
||||
|
||||
ret = new ResolvedDependencyInfoImpl(
|
||||
new MinimalModuleVersionIdentifier(group, name, artifact.getModuleVersion().getId().getVersion()),
|
||||
version,
|
||||
artifact.getFile(),
|
||||
moduleDependency.toString()
|
||||
);
|
||||
}
|
||||
if (ret == null)
|
||||
throw new IllegalArgumentException("Module dependency has no Jar artifacts: " + moduleDependency);
|
||||
|
||||
return ret;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unsupported dependency type: " + dependency.getClass().getName() + " -- " + dependency);
|
||||
}
|
||||
}
|
||||
|
||||
private static String getFileName(ResolvedArtifact artifact) {
|
||||
try {
|
||||
return InvokerHelper.getProperty(artifact.getId(), "fileName").toString();
|
||||
} catch (Throwable e) {
|
||||
// NOTE: Why not just use this to begin with?
|
||||
// ComponentArtifactIdentifier can have a getFileName() method, which doesn't necessarily resolve the file itself.
|
||||
// This allows us to get the name of the file to be used without asking Gradle to download the file.
|
||||
// So, if a file is not a JAR file, we can check the name without actually downloading it.
|
||||
return artifact.getFile().getName();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) return true;
|
||||
if (obj == null || obj.getClass() != this.getClass()) return false;
|
||||
var that = (ResolvedDependencyInfoImpl) obj;
|
||||
return Objects.equals(this.module, that.module) &&
|
||||
Objects.equals(this.version, that.version) &&
|
||||
Objects.equals(this.versionRange, that.versionRange) &&
|
||||
Objects.equals(this.artifact, that.artifact);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(module, version, versionRange, artifact);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ResolvedDependencyInfo[" +
|
||||
"module=" + module + ", " +
|
||||
"fixedVersion=" + version + ", " +
|
||||
"versionRange=" + versionRange + ", " +
|
||||
"artifact=" + artifact + ']';
|
||||
}
|
||||
|
||||
static final class MinimalModuleVersionIdentifier implements ModuleIdentifier, ModuleVersionIdentifier {
|
||||
private static final @Serial long serialVersionUID = -955346236759069739L;
|
||||
|
||||
private String group;
|
||||
private String name;
|
||||
private final String version;
|
||||
|
||||
@Inject
|
||||
public MinimalModuleVersionIdentifier(String group, String name, String version) {
|
||||
this.group = group;
|
||||
this.name = name;
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModuleIdentifier getModule() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getGroup() {
|
||||
return this.group;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return this.version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return this == obj || obj instanceof MinimalModuleVersionIdentifier o
|
||||
&& Objects.equals(this.group, o.group)
|
||||
&& Objects.equals(this.name, o.name)
|
||||
&& Objects.equals(this.version, o.version);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(group, name, version);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MinimalModuleVersionIdentifier[" +
|
||||
"group=" + group + ", " +
|
||||
"name=" + name + ", " +
|
||||
"version=" + version + ']';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
package net.minecraftforge.forge.tasks
|
||||
|
||||
import groovy.json.JsonBuilder
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.file.ConfigurableFileCollection
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.provider.SetProperty
|
||||
import org.gradle.api.tasks.*
|
||||
import org.gradle.api.tasks.bundling.AbstractArchiveTask
|
||||
|
||||
import java.nio.file.Files
|
||||
|
||||
import static net.minecraftforge.forge.tasks.Util.getArtifacts
|
||||
import static net.minecraftforge.forge.tasks.Util.iso8601Now
|
||||
|
||||
abstract class LauncherJson extends DefaultTask {
|
||||
@OutputFile abstract RegularFileProperty getOutput()
|
||||
@InputFiles abstract ConfigurableFileCollection getInput()
|
||||
@Input Map<String, Object> json = new LinkedHashMap<>()
|
||||
|
||||
LauncherJson() {
|
||||
output.convention(project.layout.buildDirectory.file('libs/version.json'))
|
||||
|
||||
dependsOn(project.tasks.universalJar)
|
||||
input.from(project.tasks.universalJar.archiveFile)
|
||||
input.from(project.configurations.installer)
|
||||
input.from(project.configurations.installerextra)
|
||||
configure {
|
||||
def mc = project.rootProject.ext.MC_VERSION
|
||||
def forge = project.rootProject.ext.FORGE_VERSION
|
||||
def timestamp = iso8601Now()
|
||||
json.putAll([
|
||||
_comment: [
|
||||
"Please do not automate the download and installation of Forge.",
|
||||
"Our efforts are supported by ads from the download page.",
|
||||
"If you MUST automate this, please consider supporting the project through https://www.patreon.com/LexManos/"
|
||||
],
|
||||
id: "$mc-$project.name-$forge",
|
||||
time: timestamp,
|
||||
releaseTime: timestamp,
|
||||
inheritsFrom: mc,
|
||||
type: 'release',
|
||||
logging: [:],
|
||||
mainClass: '',
|
||||
libraries: []
|
||||
] as LinkedHashMap)
|
||||
|
||||
[
|
||||
project.tasks.universalJar
|
||||
].forEach { packed ->
|
||||
dependsOn(packed)
|
||||
input.from packed.archiveFile
|
||||
}
|
||||
|
||||
def patched = project.tasks.applyClientBinPatches
|
||||
dependsOn(patched)
|
||||
input.from patched.output
|
||||
}
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
protected void exec() {
|
||||
var packed = (AbstractArchiveTask) project.tasks.universalJar
|
||||
def info = Util.getMavenInfoFromTask(packed)
|
||||
json.libraries.add([
|
||||
name: info.name,
|
||||
downloads: [
|
||||
artifact: [
|
||||
path: info.path,
|
||||
url: "https://maven.minecraftforge.net/$info.path",
|
||||
sha1: packed.archiveFile.get().asFile.sha1(),
|
||||
size: packed.archiveFile.get().asFile.length()
|
||||
]
|
||||
]
|
||||
])
|
||||
|
||||
var classifier = 'client'
|
||||
var genned = project.tasks.applyClientBinPatches
|
||||
info = Util.getMavenInfoFromTask(genned, classifier)
|
||||
json.libraries.add([
|
||||
name: info.name,
|
||||
downloads: [
|
||||
artifact: [
|
||||
path: info.path,
|
||||
url: "",
|
||||
sha1: genned.output.get().asFile.sha1(),
|
||||
size: genned.output.get().asFile.length()
|
||||
]
|
||||
]
|
||||
])
|
||||
|
||||
json.libraries.addAll(getArtifacts(project.configurations.installerextra).values())
|
||||
json.libraries.addAll(getArtifacts(project.configurations.installer).values())
|
||||
Files.writeString(output.get().asFile.toPath(), new JsonBuilder(json).toPrettyString())
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
package net.minecraftforge.forge.tasks
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
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
|
||||
|
||||
@CompileStatic
|
||||
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))) {
|
||||
ZipEntry 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()
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
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
|
||||
public class ObjectTarget implements Comparable<ObjectTarget> {
|
||||
@Input
|
||||
String owner
|
||||
|
||||
@Input
|
||||
String name
|
||||
|
||||
@Input
|
||||
@Optional
|
||||
String desc
|
||||
|
||||
@Override
|
||||
String toString() {
|
||||
if (desc == null)
|
||||
return owner + '.' + name
|
||||
return owner + '.' + name + desc
|
||||
}
|
||||
|
||||
@Override
|
||||
int compareTo(ObjectTarget o) {
|
||||
return toString() <=> o.toString()
|
||||
}
|
||||
}
|
||||
-47
@@ -1,47 +0,0 @@
|
||||
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<String> getInputVersion()
|
||||
|
||||
@OutputFile
|
||||
abstract RegularFileProperty getBaseBinPatchesOutput()
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
package net.minecraftforge.forge.tasks
|
||||
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import groovy.transform.CompileStatic
|
||||
import org.eclipse.jgit.api.Git
|
||||
|
||||
import javax.annotation.Nullable
|
||||
import java.util.stream.Collectors
|
||||
|
||||
@CompileStatic
|
||||
class TeamcityRequests {
|
||||
public static final Gson GSON = new Gson()
|
||||
|
||||
@Nullable
|
||||
static <T> T jsonRequest(TypeToken<T> clazz, String url) throws Exception {
|
||||
final HttpURLConnection conn = (HttpURLConnection) URI.create(url).toURL().openConnection()
|
||||
conn.setRequestProperty('Accept', 'application/json')
|
||||
conn.setReadTimeout(5 * 1000)
|
||||
conn.setConnectTimeout(5 * 1000)
|
||||
conn.connect()
|
||||
|
||||
if (conn.responseCode !== 200) {
|
||||
return null
|
||||
}
|
||||
|
||||
try (final InputStream is = conn.getInputStream()) {
|
||||
GSON.fromJson(new InputStreamReader(is), clazz.getType())
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static Map<String, Build> buildsByCommit() throws IOException {
|
||||
final Map<String, Build> builds = [:]
|
||||
jsonRequest(new TypeToken<Builds>() {}, 'https://teamcity.minecraftforge.net/guestAuth/app/rest/builds?fields=build:(revisions,number)&locator=buildType:(id:MinecraftForge_MinecraftForge_MinecraftForge_MinecraftForge__Build),defaultFilter:false,count:300,status:SUCCESS')
|
||||
?.build?.forEach {
|
||||
it.revisions.revision.each { rev -> builds[rev.version] = it }
|
||||
}
|
||||
return builds
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static String attemptFindBase(File gitPath) {
|
||||
try (final git = Git.open(gitPath)) {
|
||||
final Map<String, Build> buildByCommit = buildsByCommit()
|
||||
for (final commit in git.log().setMaxCount(100).call()) {
|
||||
// Find the first commit which was built on CI
|
||||
final build = buildByCommit[commit.id.name]
|
||||
if (build) {
|
||||
return build.number
|
||||
}
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
return null
|
||||
}
|
||||
|
||||
static final class Builds {
|
||||
public List<Build> build
|
||||
}
|
||||
|
||||
static final class Build {
|
||||
public String number
|
||||
public Revisions revisions
|
||||
}
|
||||
|
||||
static final class Revisions {
|
||||
public int count
|
||||
public List<Revision> revision
|
||||
}
|
||||
|
||||
static final class Revision {
|
||||
public String version
|
||||
}
|
||||
}
|
||||
@@ -1,230 +0,0 @@
|
||||
package net.minecraftforge.forge.tasks
|
||||
|
||||
import groovy.json.JsonBuilder
|
||||
import groovy.json.JsonSlurper
|
||||
import groovy.transform.CompileStatic
|
||||
import groovy.transform.stc.ClosureParams
|
||||
import groovy.transform.stc.SimpleType
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.Task
|
||||
import org.gradle.api.artifacts.Configuration
|
||||
import org.gradle.api.artifacts.ResolvedArtifact
|
||||
import org.gradle.api.tasks.bundling.AbstractArchiveTask
|
||||
import org.objectweb.asm.ClassReader
|
||||
import org.objectweb.asm.Opcodes
|
||||
import org.objectweb.asm.tree.ClassNode
|
||||
|
||||
import java.net.http.HttpClient
|
||||
import java.net.http.HttpRequest
|
||||
import java.net.http.HttpResponse
|
||||
import java.security.MessageDigest
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.concurrent.Semaphore
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipInputStream
|
||||
|
||||
final class Util {
|
||||
public static final int ASM_LEVEL = Opcodes.ASM9
|
||||
private static final HttpClient HTTP = HttpClient.newBuilder().build()
|
||||
|
||||
static void init() {
|
||||
File.metaClass.sha1 = { ->
|
||||
MessageDigest md = MessageDigest.getInstance('SHA-1')
|
||||
delegate.eachByte(4096) { byte[] bytes, int size ->
|
||||
md.update(bytes, 0, size)
|
||||
}
|
||||
return md.digest().collect(this.&toHex).join('')
|
||||
}
|
||||
File.metaClass.getSha1 = { !delegate.exists() ? null : delegate.sha1() }
|
||||
File.metaClass.sha256 = { ->
|
||||
MessageDigest md = MessageDigest.getInstance('SHA-256')
|
||||
delegate.eachByte(4096) { byte[] bytes, int size ->
|
||||
md.update(bytes, 0, size)
|
||||
}
|
||||
return md.digest().collect(this.&toHex).join('')
|
||||
}
|
||||
File.metaClass.getSha256 = { !delegate.exists() ? null : delegate.sha256() }
|
||||
|
||||
File.metaClass.json = { -> new JsonSlurper().parseText(delegate.text) }
|
||||
File.metaClass.getJson = { return delegate.exists() ? new JsonSlurper().parse(delegate) : [:] }
|
||||
File.metaClass.setJson = { json -> delegate.text = new JsonBuilder(json).toPrettyString() }
|
||||
|
||||
Date.metaClass.iso8601 = { ->
|
||||
var format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
|
||||
var result = format.format(delegate)
|
||||
return result[0..21] + ':' + result[22..-1]
|
||||
}
|
||||
|
||||
String.metaClass.rsplit = { String del, int limit = -1 ->
|
||||
var lst = new ArrayList<String>()
|
||||
int x = 0
|
||||
int idx
|
||||
String 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
|
||||
}
|
||||
}
|
||||
|
||||
static String[] getClasspath(Project project, Map libs, String artifact) {
|
||||
def ret = []
|
||||
artifactTree(project, artifact).each { key, lib ->
|
||||
libs[lib.name] = lib
|
||||
if (lib.name != artifact)
|
||||
ret.add(lib.name)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
@CompileStatic
|
||||
static Map getArtifacts(Configuration config) {
|
||||
var ret = [:]
|
||||
var semaphore = new Semaphore(1, true)
|
||||
config.resolvedConfiguration.resolvedArtifacts.parallelStream().forEachOrdered(dep -> {
|
||||
var info = getMavenInfoFromDep(dep)
|
||||
var domain = 'libraries.minecraft.net'
|
||||
var url = "https://$domain/$info.path"
|
||||
if (!checkExists(url))
|
||||
url.values[0] = 'maven.minecraftforge.net'
|
||||
|
||||
var sha1 = sha1(dep.file)
|
||||
|
||||
semaphore.acquire()
|
||||
ret[info.key] = [
|
||||
name: info.name,
|
||||
downloads: [
|
||||
artifact: [
|
||||
path: info.path,
|
||||
url: url.toString(),
|
||||
sha1: sha1,
|
||||
size: dep.file.length()
|
||||
]
|
||||
]
|
||||
]
|
||||
semaphore.release()
|
||||
})
|
||||
return ret
|
||||
}
|
||||
|
||||
@CompileStatic
|
||||
static Map getMavenInfoFromDep(ResolvedArtifact dep) {
|
||||
return getMavenInfoFromMap([
|
||||
group: dep.moduleVersion.id.group,
|
||||
name: dep.moduleVersion.id.name,
|
||||
version: dep.moduleVersion.id.version,
|
||||
classifier: dep.classifier,
|
||||
extension: dep.extension
|
||||
])
|
||||
}
|
||||
|
||||
@CompileStatic
|
||||
static Map getMavenInfoFromTask(AbstractArchiveTask task) {
|
||||
return getMavenInfoFromMap([
|
||||
group: task.project.group.toString(),
|
||||
name: task.project.name,
|
||||
version: task.project.version.toString(),
|
||||
classifier: task.archiveClassifier.get(),
|
||||
extension: task.archiveExtension.get()
|
||||
])
|
||||
}
|
||||
|
||||
@CompileStatic
|
||||
static Map getMavenInfoFromTask(Task task, String classifier) {
|
||||
return getMavenInfoFromMap([
|
||||
group: task.project.group.toString(),
|
||||
name: task.project.name,
|
||||
version: task.project.version.toString(),
|
||||
classifier: classifier,
|
||||
extension: 'jar'
|
||||
])
|
||||
}
|
||||
|
||||
@CompileStatic
|
||||
private static Map getMavenInfoFromMap(Map<String, String> art) {
|
||||
var key = "$art.group:$art.name"
|
||||
var name = "$art.group:$art.name:$art.version"
|
||||
var path = "${art.group.replace('.', '/')}/$art.name/$art.version/$art.name-$art.version"
|
||||
if (art.classifier !== null) {
|
||||
name += ":$art.classifier"
|
||||
path += "-$art.classifier"
|
||||
}
|
||||
if ('jar' != art.extension) {
|
||||
name += "@$art.extension"
|
||||
path += ".$art.extension"
|
||||
} else {
|
||||
path += ".jar"
|
||||
}
|
||||
return [
|
||||
key: key.toString(),
|
||||
name: name.toString(),
|
||||
path: path.toString(),
|
||||
art: art
|
||||
]
|
||||
}
|
||||
|
||||
static String iso8601Now() { new Date().iso8601() }
|
||||
|
||||
@CompileStatic
|
||||
static String sha1(File file) {
|
||||
MessageDigest md = MessageDigest.getInstance('SHA-1')
|
||||
file.eachByte(4096) { byte[] bytes, int size ->
|
||||
md.update(bytes, 0, size)
|
||||
}
|
||||
return md.digest().collect(this.&toHex).join('')
|
||||
}
|
||||
|
||||
@CompileStatic
|
||||
private static String toHex(byte bite) {
|
||||
return String.format('%02x', bite)
|
||||
}
|
||||
|
||||
private static Map artifactTree(Project project, String artifact, boolean transitive = true) {
|
||||
if (!project.ext.has('tree_resolver'))
|
||||
project.ext.tree_resolver = 1
|
||||
def cfg = project.configurations.create('tree_resolver_' + project.ext.tree_resolver++)
|
||||
cfg.transitive = transitive
|
||||
def dep = project.dependencies.create(artifact)
|
||||
cfg.dependencies.add(dep)
|
||||
def files = cfg.resolve()
|
||||
return getArtifacts(cfg)
|
||||
}
|
||||
|
||||
@CompileStatic
|
||||
static boolean checkExists(String url) {
|
||||
try {
|
||||
return HTTP.send(HttpRequest.newBuilder(new URI(url))
|
||||
.method('HEAD', HttpRequest.BodyPublishers.noBody()).build(), HttpResponse.BodyHandlers.discarding()
|
||||
).statusCode() === 200
|
||||
} catch (Exception e) {
|
||||
if (e.toString().contains('unable to find valid certification path to requested target'))
|
||||
throw new RuntimeException("Failed to connect to $url: Missing certificate root authority, try updating Java")
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
static String getLatestForgeVersion(String mcVersion) {
|
||||
final json = new JsonSlurper().parseText(new URL('https://files.minecraftforge.net/net/minecraftforge/forge/promotions_slim.json').getText('UTF-8'))
|
||||
final ver = json.promos["$mcVersion-latest"]
|
||||
ver === null ? null : (mcVersion + '-' + ver)
|
||||
}
|
||||
|
||||
@CompileStatic
|
||||
static void processClassNodes(File file, @ClosureParams(value = SimpleType, options = 'org.objectweb.asm.tree.ClassNode') Closure process) {
|
||||
file.withInputStream { i ->
|
||||
new ZipInputStream(i).withCloseable { zin ->
|
||||
ZipEntry zein
|
||||
while ((zein = zin.nextEntry) !== null) {
|
||||
if (zein.name.endsWith('.class')) {
|
||||
var node = new ClassNode(ASM_LEVEL)
|
||||
new ClassReader(zin).accept(node, 0)
|
||||
process(node)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.forge.tasks
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
import net.minecraftforge.srgutils.MinecraftVersion
|
||||
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.InputFile
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.objectweb.asm.tree.AnnotationNode
|
||||
import org.objectweb.asm.tree.ClassNode
|
||||
|
||||
abstract class ValidateDeprecations extends DefaultTask {
|
||||
@InputFile
|
||||
abstract RegularFileProperty getInput()
|
||||
|
||||
@Input
|
||||
abstract Property<String> getMcVersion()
|
||||
|
||||
ValidateDeprecations() {
|
||||
this.onlyIf { !System.env.TEAMCITY_VERSION }
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
protected void exec() {
|
||||
var mcVer = MinecraftVersion.from(mcVersion.get())
|
||||
List<String> errors = []
|
||||
|
||||
Util.processClassNodes(input.get().asFile) {
|
||||
processNode(mcVer, errors, it)
|
||||
}
|
||||
|
||||
if (!errors.isEmpty()) {
|
||||
errors.forEach {
|
||||
this.logger.error("Deprecated ${it[0]} is marked for removal in ${it[1]} but is not yet removed")
|
||||
}
|
||||
throw new IllegalStateException("Found deprecated members marked for removal but not yet removed in ${mcVer}; see log for details")
|
||||
}
|
||||
}
|
||||
|
||||
protected processNode(MinecraftVersion mcVer, List<String> errors, ClassNode node) {
|
||||
node.visibleAnnotations?.each { annotation ->
|
||||
ValidateDeprecations.processAnnotations(annotation, mcVer, errors) {
|
||||
"class ${node.name}"
|
||||
}
|
||||
}
|
||||
node.fields?.each { field ->
|
||||
field.visibleAnnotations?.each { annotation ->
|
||||
ValidateDeprecations.processAnnotations(annotation, mcVer, errors) {
|
||||
"field ${node.name}#${field.name}"
|
||||
}
|
||||
}
|
||||
}
|
||||
node.methods?.each { method ->
|
||||
method.visibleAnnotations?.each { annotation ->
|
||||
ValidateDeprecations.processAnnotations(annotation, mcVer, errors) {
|
||||
"method ${node.name}#${method.name}${method.desc}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void processAnnotations(AnnotationNode annotation, MinecraftVersion mcVer, List<String> errors, Closure<String> context) {
|
||||
def values = annotation.values
|
||||
if (values === null)
|
||||
return
|
||||
int forRemoval = values.indexOf('forRemoval')
|
||||
int since = values.indexOf('since')
|
||||
if (annotation.desc == 'Ljava/lang/Deprecated;' && forRemoval !== -1 && since !== -1 && values.size() >= 4 && values[forRemoval + 1] === true) {
|
||||
def oldVersion = MinecraftVersion.from(values[since + 1])
|
||||
int[] split = ValidateDeprecations.splitDots(oldVersion.toString())
|
||||
if (split.length < 2)
|
||||
return
|
||||
def removeVersion = MinecraftVersion.from("${split[0]}.${split[1] + 1}")
|
||||
if (removeVersion <= mcVer)
|
||||
errors.add([context(), removeVersion])
|
||||
}
|
||||
}
|
||||
|
||||
@CompileStatic
|
||||
private static int[] splitDots(String version) {
|
||||
String[] pts = version.split('\\.')
|
||||
int[] values = new int[pts.length]
|
||||
for (int x = 0; x < pts.length; x++)
|
||||
values[x] = Integer.parseInt(pts[x])
|
||||
return values
|
||||
}
|
||||
}
|
||||
@@ -1,311 +0,0 @@
|
||||
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<String, ATParser.Entry> process(File file, Reporter reporter, Map<String, InheritanceData> inheritance) {
|
||||
final TreeMap<String, ATParser.Entry> lines = ATParser.parse(file.readLines(), reporter)
|
||||
final Map<String, ATParser.Entry> 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('<clinit>') || 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 ('<init>' == 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('<init>')) {
|
||||
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<String> joinBack(TreeMap<String, ATParser.Entry> lines, Map<String, InheritanceData> inheritance, IMappingFile mappings) {
|
||||
final data = [] as List<String>
|
||||
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 == '<init>')
|
||||
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<String, Entry> parse(List<String> lines, CheckTask.Reporter reporter) {
|
||||
TreeMap<String, Entry> 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 != '<init>') {
|
||||
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)
|
||||
data[1] = data[1].replaceAll('/', '.') // Convert to Source names, internal names are fine by spec, but not supported by old AST based AT implementations.
|
||||
new Entry(data[0], data[1], data[2], comment)
|
||||
}
|
||||
|
||||
@TupleConstructor
|
||||
static final class Entry {
|
||||
String modifier, cls, desc, comment
|
||||
|
||||
Set<String> existing
|
||||
TreeSet<String> children
|
||||
boolean group = false
|
||||
|
||||
@Lazy
|
||||
String key = {cls + (desc.isEmpty() ? '' : ' ' + desc)}()
|
||||
|
||||
Object getAt(String key) {
|
||||
return getProperty(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
package net.minecraftforge.forge.tasks.checks
|
||||
|
||||
import net.minecraftforge.forge.tasks.Util
|
||||
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.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<String> known = []
|
||||
collectKnown(known)
|
||||
|
||||
excs.each { f ->
|
||||
final lines = []
|
||||
f.eachLine { line ->
|
||||
int 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)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if (fix) f.text = lines.sort().join('\n')
|
||||
}
|
||||
}
|
||||
|
||||
private void collectKnown(Collection<String> known) {
|
||||
binary.get().asFile.withInputStream { i ->
|
||||
new ZipInputStream(i).withCloseable { zin ->
|
||||
final visitor = new ClassVisitor(Util.ASM_LEVEL) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package net.minecraftforge.forge.tasks.checks
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
|
||||
@CompileStatic
|
||||
enum CheckMode {
|
||||
CHECK,
|
||||
FIX
|
||||
|
||||
CheckMode() {}
|
||||
}
|
||||
@@ -1,182 +0,0 @@
|
||||
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<String, Integer> ACCESS_MAP = [private: 0, protected: 2, public: 3].tap { it.put(null, 1) }
|
||||
|
||||
@InputDirectory abstract DirectoryProperty getPatchDir()
|
||||
@Input @Optional abstract ListProperty<String> 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('\\', '/')))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static boolean accessChange(String previous, String current) {
|
||||
//return ACCESS_MAP[previous] < ACCESS_MAP[current]
|
||||
return previous != current
|
||||
}
|
||||
|
||||
void verifyPatch(Path patch, Reporter reporter, boolean fix, String patchPath, boolean hasS2SArtifact) {
|
||||
int oldFixedErrors = reporter.fixed.size()
|
||||
|
||||
final lines = Files.readAllLines(patch)
|
||||
|
||||
int hunksStart = 0
|
||||
boolean onlyWhiteSpace = false
|
||||
|
||||
final List<String> 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
|
||||
(accessChange(pMatcher.group(2), 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) && // <T> void name
|
||||
pMatcher.group(3) == cMatcher.group(3) && // static
|
||||
(accessChange(pMatcher.group(2), 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
|
||||
(accessChange(pMatcher.group(2), 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 \n$prevLine\n$line\n${lines[i+1]}", 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) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
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 boolean 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<String, InheritanceData> 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
|
||||
}
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
package net.minecraftforge.forge.tasks.checks
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
import groovy.transform.stc.ClosureParams
|
||||
import groovy.transform.stc.ThirdParam
|
||||
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<CheckMode> getMode()
|
||||
|
||||
private boolean ignoreFailures = false
|
||||
|
||||
@Input
|
||||
@Override
|
||||
boolean getIgnoreFailures() {
|
||||
return ignoreFailures
|
||||
}
|
||||
|
||||
@Override
|
||||
void setIgnoreFailures(boolean ignoreFailures) {
|
||||
this.ignoreFailures = ignoreFailures
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
void run() {
|
||||
Util.init()
|
||||
|
||||
boolean 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<String> messages = []
|
||||
|
||||
public final List<String> fixed = []
|
||||
public final List<String> notFixed = []
|
||||
|
||||
void report(String message, boolean canBeFixed = true) {
|
||||
messages.add(message)
|
||||
|
||||
if (trackFixed) {
|
||||
if (canBeFixed) {
|
||||
fixed.add(message)
|
||||
} else {
|
||||
notFixed.add(message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static <T extends CheckTask> void registerTask(TaskContainer tasks, String taskName, @DelegatesTo.Target('type') Class<T> clazz,
|
||||
@DelegatesTo(genericTypeIndex = 0, target = 'type')
|
||||
@ClosureParams(ThirdParam.FirstGenericType) Closure configuration) {
|
||||
taskName = taskName.capitalize()
|
||||
tasks.register("check$taskName", clazz) { CheckTask task ->
|
||||
def castedTask = task as T
|
||||
configuration.setDelegate(castedTask)
|
||||
configuration.call(castedTask)
|
||||
castedTask.mode.set(CheckMode.CHECK)
|
||||
castedTask.group = 'checks'
|
||||
}
|
||||
tasks.named('checkAll').configure { it.dependsOn("check$taskName") }
|
||||
|
||||
tasks.register("checkAndFix$taskName", clazz) { CheckTask task ->
|
||||
def castedTask = task as T
|
||||
configuration.setDelegate(castedTask)
|
||||
configuration.call(castedTask)
|
||||
castedTask.mode.set(CheckMode.FIX)
|
||||
castedTask.group = 'checks'
|
||||
}
|
||||
tasks.named('checkAllAndFix').configure { it.dependsOn("checkAndFix$taskName") }
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
plugins {
|
||||
id 'java-library'
|
||||
id 'net.minecraftforge.gradle.patcher'
|
||||
}
|
||||
|
||||
evaluationDependsOn(':mcp')
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven gradleutils.forgeMaven
|
||||
maven gradleutils.minecraftLibsMaven
|
||||
}
|
||||
|
||||
java.toolchain.languageVersion = JavaLanguageVersion.of(JAVA_VERSION)
|
||||
|
||||
dependencies {
|
||||
implementation(libs.forgespi)
|
||||
}
|
||||
|
||||
patcher {
|
||||
parent = project(':mcp')
|
||||
mcVersion = MC_VERSION
|
||||
patchedSrc = file('src/main/java')
|
||||
|
||||
mappings channel: MAPPING_CHANNEL, version: MAPPING_VERSION
|
||||
|
||||
runs {
|
||||
clean_client {
|
||||
client true
|
||||
taskName 'clean_client'
|
||||
ideaModule "${rootProject.name}.${project.name}.main"
|
||||
|
||||
main 'net.minecraft.client.main.Main'
|
||||
workingDirectory project.file('run/client')
|
||||
|
||||
args '--gameDir', '.'
|
||||
args '--version', MC_VERSION
|
||||
args '--assetsDir', downloadAssets.output
|
||||
args '--assetIndex', '{asset_index}'
|
||||
args '--accessToken', '0'
|
||||
}
|
||||
|
||||
clean_server {
|
||||
client false
|
||||
taskName 'clean_server'
|
||||
ideaModule "${rootProject.name}.${project.name}.main"
|
||||
|
||||
main 'net.minecraft.server.Main'
|
||||
workingDirectory project.file('run/server')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile).configureEach {
|
||||
options.encoding = 'UTF-8' // Use the UTF-8 charset for Java compilation
|
||||
options.warnings = false // Shutup deprecated for removal warnings
|
||||
}
|
||||
|
||||
eclipse.classpath.file.whenMerged {
|
||||
// Disable optional warnings on the minecraft decompiled source code. It just muddies the warning window and hides warnings in our own codebase
|
||||
def src = entries.find { it.path == 'src/main/java' }
|
||||
if (src == null) throw new IllegalStateException("You must run `setup` task before importing")
|
||||
src.entryAttributes['ignore_optional_problems'] = 'true'
|
||||
}
|
||||
|
||||
tasks.named('genPatches').configure {
|
||||
onlyIf { false }
|
||||
}
|
||||
|
||||
tasks.named('extractRangeMap').configure {
|
||||
onlyIf { false }
|
||||
tool = 'net.minecraftforge:Srg2Source:8.1.0:fatjar'
|
||||
}
|
||||
-1122
File diff suppressed because it is too large.
Load diff
@@ -1,89 +0,0 @@
|
||||
import net.minecraftforge.forge.tasks.CleanProperties
|
||||
|
||||
apply plugin: 'eclipse'
|
||||
apply plugin: 'idea'
|
||||
apply plugin: 'net.minecraftforge.gradleutils'
|
||||
|
||||
group = 'net.minecraftforge'
|
||||
version = VERSION
|
||||
println("Version: $version")
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven gradleutils.forgeMaven
|
||||
maven gradleutils.minecraftLibsMaven
|
||||
//mavenLocal()
|
||||
}
|
||||
|
||||
tasks.withType(Javadoc).configureEach {
|
||||
options.tags = [
|
||||
'apiNote:a:<em>API Note:</em>',
|
||||
'implSpec:a:<em>Implementation Requirements:</em>',
|
||||
'implNote:a:<em>Implementation Note:</em>'
|
||||
]
|
||||
options.addStringOption('Xdoclint:all,-missing', '-public')
|
||||
}
|
||||
|
||||
// We need to write the manifest to the binary file so we have properly versioned packaged at dev time.
|
||||
tasks.register('writeManifest') {
|
||||
doLast {
|
||||
if (plugins.findPlugin('net.minecraftforge.gradle.patcher')) // Forge project
|
||||
universalJar.manifest.writeTo(rootProject.file('src/main/resources/META-INF/MANIFEST.MF'))
|
||||
else
|
||||
jar.manifest.writeTo(project.file('src/main/resources/META-INF/MANIFEST.MF'))
|
||||
}
|
||||
}
|
||||
|
||||
tasks.register('generateResources') {
|
||||
dependsOn('writeManifest')
|
||||
}
|
||||
|
||||
tasks.named('processResources') {
|
||||
dependsOn(generateResources)
|
||||
}
|
||||
|
||||
// Make sure out manifests get written before compiling the code, IDEA calls this task if you tell it to use the gradle build.
|
||||
tasks.withType(JavaCompile).configureEach {
|
||||
dependsOn 'generateResources'
|
||||
dependsOn 'processResources' // Needed because we merge the output of this with the output of the compile task. And gradle detects downstream tasks using the output without a hard dep
|
||||
options.encoding = 'UTF-8' // Use the UTF-8 charset for Java compilation
|
||||
options.warnings = false // Shutup deprecated for removal warnings
|
||||
options.forkOptions.jvmArgs += '-Xmx6G' // Needed to make compiling faster, and not run out of heap space in some cases.
|
||||
}
|
||||
|
||||
// Merge the resources and classes into the same directory. We'll need to split them at runtime because
|
||||
// Minecraft and Forge are in the same sourceSet as they are inter dependent.. for now..
|
||||
sourceSets.each {
|
||||
def dir = layout.buildDirectory.dir("classes/java/$it.name")
|
||||
it.output.resourcesDir = dir
|
||||
it.java.destinationDirectory = dir
|
||||
}
|
||||
|
||||
tasks.register('copyEclipseSettings') {
|
||||
doLast {
|
||||
rootProject.fileTree('ide/eclipse/template/.settings/').matching { include '**/*.prefs' }.each { file ->
|
||||
def target = project.file('.settings/' + file.name)
|
||||
def temp = new CleanProperties().load(file)
|
||||
def exst = new CleanProperties().load(target)
|
||||
exst.put('eclipse.preferences.version', '1')
|
||||
exst.putAll(temp)
|
||||
exst.store(target)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: [Gradle][IntelliJ] Auto trigger these tasks on import.
|
||||
eclipse {
|
||||
// Run everytime eclipse builds the code
|
||||
//autoBuildTasks writeManifest
|
||||
// Run when importing the project
|
||||
synchronizationTasks generateResources, copyEclipseSettings, eclipseClasspath, eclipseProject
|
||||
}
|
||||
|
||||
idea {
|
||||
module {
|
||||
// IntelliJ IDEA does not do this by itself anymore...
|
||||
downloadJavadoc = true
|
||||
downloadSources = true
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
for file in $(find repo | grep --invert-match -e md5 -e sha | grep -e .jar -e .zip); do
|
||||
file_path=$(realpath $file)
|
||||
file_name=$(basename $file)
|
||||
|
||||
echo "$file_name"
|
||||
files=$(unzip -l $file | awk '{print $4}')
|
||||
for f in $files; do
|
||||
out="_actual/$file_name/$f"
|
||||
parent=$(dirname "$out")
|
||||
echo $out
|
||||
|
||||
if [ ! -d "$parent" ]; then
|
||||
mkdir -p "$parent"
|
||||
fi
|
||||
|
||||
if [[ $f == *.class ]]; then
|
||||
javap -v -p jar:file://$file_path!/$f | tail -n +4 >$out.txt
|
||||
elif [[ $f != */ && $f != "Name" && $f != "----" ]]; then
|
||||
unzip -o -q $file $f -d _actual/$file_name
|
||||
fi
|
||||
done
|
||||
done
|
||||
+50
-40
@@ -1,68 +1,78 @@
|
||||
import net.minecraftforge.gradleutils.PomUtils
|
||||
|
||||
plugins {
|
||||
id 'java-library'
|
||||
id 'maven-publish'
|
||||
id 'net.minecraftforge.licenser'
|
||||
id 'net.minecraftforge.gradleutils'
|
||||
alias libs.plugins.licenser
|
||||
alias libs.plugins.gradleutils
|
||||
alias libs.plugins.gitversion
|
||||
alias libs.plugins.changelog
|
||||
id 'net.minecraftforge.forge.build.convention'
|
||||
}
|
||||
|
||||
apply from: rootProject.file('build_shared.gradle')
|
||||
gradleutils.displayName = 'FML'
|
||||
final vendor = 'Forge Development LLC'
|
||||
description = 'Modifications to Minecraft to enable mod developers.'
|
||||
|
||||
dependencies {
|
||||
compileOnly(libs.jetbrains.annotations)
|
||||
compileOnly libs.jetbrains.annotations
|
||||
|
||||
api(libs.eventbus)
|
||||
annotationProcessor(libs.eventbus.validator)
|
||||
api libs.eventbus
|
||||
|
||||
implementation(project(':fmlloader'))
|
||||
implementation(libs.commons.io)
|
||||
implementation projects.fmlloader
|
||||
implementation libs.commons.io
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain.languageVersion = JavaLanguageVersion.of(JAVA_VERSION)
|
||||
toolchain.languageVersion = JavaLanguageVersion.of(javaVersion)
|
||||
withSourcesJar()
|
||||
}
|
||||
|
||||
tasks.named('jar', Jar).configure {
|
||||
manifest {
|
||||
attributes([
|
||||
'Automatic-Module-Name': 'net.minecraftforge.fmlcore',
|
||||
'FMLModType': 'LIBRARY'
|
||||
] as LinkedHashMap)
|
||||
attributes([
|
||||
'Specification-Title': 'FML',
|
||||
'Specification-Vendor': 'Forge Development LLC',
|
||||
'Specification-Version': '1',
|
||||
'Implementation-Title': 'FML',
|
||||
'Implementation-Version': '1.0',
|
||||
'Implementation-Vendor': 'Forge Development LLC'
|
||||
] as LinkedHashMap, 'net/minecraftforge/fml/')
|
||||
}
|
||||
license {
|
||||
header = rootProject.file('LICENSE-header.txt')
|
||||
}
|
||||
changelog {
|
||||
from changelogBase
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile).configureEach {
|
||||
options.compilerArgs << '-Xlint:-unchecked'
|
||||
}
|
||||
|
||||
license {
|
||||
header = rootProject.file('LICENSE-header.txt')
|
||||
tasks.named('jar', Jar) {
|
||||
manifest {
|
||||
attributes([
|
||||
'Automatic-Module-Name': 'net.minecraftforge.fmlcore',
|
||||
'FMLModType' : 'LIBRARY'
|
||||
])
|
||||
attributes([
|
||||
'Specification-Title' : gradleutils.displayName.get(),
|
||||
'Specification-Vendor' : vendor,
|
||||
'Specification-Version' : '1',
|
||||
'Implementation-Title' : gradleutils.displayName.get(),
|
||||
'Implementation-Version': '1.0',
|
||||
'Implementation-Vendor' : vendor
|
||||
], 'net/minecraftforge/fml/')
|
||||
}
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications.register('mavenJava', MavenPublication).configure {
|
||||
from components.java
|
||||
artifactId = 'fmlcore'
|
||||
pom {
|
||||
name = project.name
|
||||
description = 'Modifactions to Minecraft to enable mod developers.'
|
||||
url = 'https://github.com/MinecraftForge/MinecraftForge'
|
||||
PomUtils.setGitHubDetails(pom, 'MinecraftForge')
|
||||
license PomUtils.Licenses.LGPLv2_1
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven gradleutils.publishingForgeMaven
|
||||
}
|
||||
|
||||
publications.register('mavenJava', MavenPublication) {
|
||||
changelog.publish(it)
|
||||
gradleutils.promote(it)
|
||||
|
||||
from components.java
|
||||
|
||||
pom {
|
||||
description = project.description
|
||||
|
||||
gradleutils.pom.addRemoteDetails(pom)
|
||||
|
||||
licenses {
|
||||
license gradleutils.pom.licenses.LGPLv2_1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,67 +1,74 @@
|
||||
import net.minecraftforge.gradleutils.PomUtils
|
||||
import org.gradle.api.plugins.jvm.JvmTestSuite
|
||||
import org.gradle.internal.os.OperatingSystem
|
||||
|
||||
plugins {
|
||||
id 'java-library'
|
||||
id 'jvm-test-suite'
|
||||
id 'maven-publish'
|
||||
id 'net.minecraftforge.licenser'
|
||||
id 'net.minecraftforge.gradleutils'
|
||||
alias libs.plugins.licenser
|
||||
alias libs.plugins.gradleutils
|
||||
alias libs.plugins.gitversion
|
||||
alias libs.plugins.changelog
|
||||
id 'net.minecraftforge.forge.build.convention'
|
||||
}
|
||||
|
||||
apply from: rootProject.file('build_shared.gradle')
|
||||
|
||||
import org.gradle.internal.os.OperatingSystem
|
||||
switch (OperatingSystem.current()) {
|
||||
case OperatingSystem.LINUX:
|
||||
project.ext.lwjglNatives = "natives-linux"
|
||||
break
|
||||
case OperatingSystem.MAC_OS:
|
||||
project.ext.lwjglNatives = "natives-macos"
|
||||
break
|
||||
case OperatingSystem.WINDOWS:
|
||||
project.ext.lwjglNatives = "natives-windows"
|
||||
break
|
||||
case OperatingSystem.LINUX:
|
||||
project.ext.lwjglNatives = "natives-linux"
|
||||
break
|
||||
case OperatingSystem.MAC_OS:
|
||||
project.ext.lwjglNatives = "natives-macos"
|
||||
break
|
||||
case OperatingSystem.WINDOWS:
|
||||
project.ext.lwjglNatives = "natives-windows"
|
||||
break
|
||||
}
|
||||
|
||||
gradleutils.displayName = 'FML Early Display'
|
||||
final vendor = 'Forge Development LLC'
|
||||
description = 'A pretty looking, but prone to erroring screen displayed during Forge loading.'
|
||||
|
||||
java {
|
||||
toolchain.languageVersion = JavaLanguageVersion.of(JAVA_VERSION)
|
||||
toolchain.languageVersion = JavaLanguageVersion.of(javaVersion)
|
||||
withSourcesJar()
|
||||
}
|
||||
|
||||
license {
|
||||
header = rootProject.file('LICENSE-header.txt')
|
||||
}
|
||||
|
||||
changelog {
|
||||
from changelogBase
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(libs.jetbrains.annotations)
|
||||
implementation(project(':fmlloader'))
|
||||
implementation(project(':fmlcore'))
|
||||
implementation(libs.bundles.lwjgl)
|
||||
implementation('org.slf4j:slf4j-api:1.8.0-beta4')
|
||||
implementation(libs.jopt.simple)
|
||||
testImplementation('org.junit.jupiter:junit-jupiter-api:5.8.2')
|
||||
testImplementation('org.powermock:powermock-core:2.0.9')
|
||||
testRuntimeOnly('org.junit.jupiter:junit-jupiter-engine:5.8.2')
|
||||
testRuntimeOnly('org.slf4j:slf4j-jdk14:1.8.0-beta4')
|
||||
testRuntimeOnly("org.lwjgl:lwjgl::$lwjglNatives")
|
||||
testRuntimeOnly("org.lwjgl:lwjgl-glfw::$lwjglNatives")
|
||||
testRuntimeOnly("org.lwjgl:lwjgl-opengl::$lwjglNatives")
|
||||
testRuntimeOnly("org.lwjgl:lwjgl-stb::$lwjglNatives")
|
||||
compileOnly libs.jetbrains.annotations
|
||||
|
||||
implementation projects.fmlloader
|
||||
implementation projects.fmlcore
|
||||
implementation libs.bundles.lwjgl
|
||||
implementation earlyDisplayLibs.slf4j.api
|
||||
implementation libs.jopt.simple
|
||||
}
|
||||
|
||||
tasks.named('test', Test).configure {
|
||||
useJUnitPlatform()
|
||||
// See gradle/gradle#35070 (https://github.com/gradle/gradle/issues/35070)
|
||||
// We are making the list of providers first BEFORE mapping them to a provider of the resolved objects so that they can be finalized after configuration.
|
||||
var lwjglTestLibs = [
|
||||
dependencies.variantOf(earlyDisplayTestLibs.lwjgl) { classifier lwjglNatives },
|
||||
dependencies.variantOf(earlyDisplayTestLibs.lwjgl.glfw) { classifier lwjglNatives },
|
||||
dependencies.variantOf(earlyDisplayTestLibs.lwjgl.opengl) { classifier lwjglNatives },
|
||||
dependencies.variantOf(earlyDisplayTestLibs.lwjgl.stb) { classifier lwjglNatives }
|
||||
].with { list ->
|
||||
provider { list.collect { it.get() } }
|
||||
}
|
||||
|
||||
tasks.named('jar', Jar).configure {
|
||||
manifest {
|
||||
attributes([
|
||||
'Automatic-Module-Name': 'net.minecraftforge.earlydisplay',
|
||||
'Forge-Module-Layer': 'boot'
|
||||
] as LinkedHashMap)
|
||||
attributes([
|
||||
'Specification-Title': 'FML Early Display',
|
||||
'Specification-Vendor': 'Forge Development LLC',
|
||||
'Specification-Version': '1',
|
||||
'Implementation-Title': 'FML Early Display',
|
||||
'Implementation-Vendor': 'Forge Development LLC',
|
||||
'Implementation-Version': '1.0'
|
||||
] as LinkedHashMap, 'net/minecraftforge/fml/earlydisplay/')
|
||||
testing.suites.named('test', JvmTestSuite) {
|
||||
useJUnitJupiter('5.8.2')
|
||||
|
||||
dependencies {
|
||||
implementation earlyDisplayTestLibs.powermock.core
|
||||
runtimeOnly earlyDisplayLibs.slf4j.jdk14
|
||||
runtimeOnly.bundle lwjglTestLibs
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,24 +76,42 @@ tasks.withType(JavaCompile).configureEach {
|
||||
options.compilerArgs << '-Xlint:unchecked'
|
||||
}
|
||||
|
||||
license {
|
||||
header = rootProject.file('LICENSE-header.txt')
|
||||
tasks.named('jar', Jar) {
|
||||
manifest {
|
||||
attributes([
|
||||
'Automatic-Module-Name': 'net.minecraftforge.earlydisplay',
|
||||
'Forge-Module-Layer' : 'boot'
|
||||
])
|
||||
attributes([
|
||||
'Specification-Title' : gradleutils.displayName.get(),
|
||||
'Specification-Vendor' : vendor,
|
||||
'Specification-Version' : '1',
|
||||
'Implementation-Title' : gradleutils.displayName.get(),
|
||||
'Implementation-Vendor' : vendor,
|
||||
'Implementation-Version': '1.0'
|
||||
], 'net/minecraftforge/fml/earlydisplay/')
|
||||
}
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications.register('mavenJava', MavenPublication).configure {
|
||||
from components.java
|
||||
artifactId = 'fmlearlydisplay'
|
||||
pom {
|
||||
name = project.name
|
||||
description = 'A pretty looking, but prone to erroring screen displayed during Forge loading.'
|
||||
url = 'https://github.com/MinecraftForge/MinecraftForge'
|
||||
PomUtils.setGitHubDetails(pom, 'MinecraftForge')
|
||||
license PomUtils.Licenses.LGPLv2_1
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven gradleutils.publishingForgeMaven
|
||||
}
|
||||
|
||||
publications.register('mavenJava', MavenPublication) {
|
||||
changelog.publish(it)
|
||||
gradleutils.promote(it)
|
||||
|
||||
from components.java
|
||||
|
||||
pom {
|
||||
description = project.description
|
||||
|
||||
gradleutils.pom.addRemoteDetails(pom)
|
||||
|
||||
licenses {
|
||||
license gradleutils.pom.licenses.LGPLv2_1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+143
-100
@@ -1,69 +1,74 @@
|
||||
import net.minecraftforge.gradleutils.PomUtils
|
||||
import groovy.transform.CompileDynamic
|
||||
import groovy.transform.CompileStatic
|
||||
import groovy.transform.PackageScope
|
||||
import net.minecraftforge.forgedev.legacy.tasks.JarJarMetadataOptions
|
||||
import org.gradle.api.plugins.jvm.JvmTestSuite
|
||||
|
||||
import javax.inject.Inject
|
||||
|
||||
plugins {
|
||||
id 'java-library'
|
||||
id 'jvm-test-suite'
|
||||
id 'maven-publish'
|
||||
id 'net.minecraftforge.licenser'
|
||||
id 'net.minecraftforge.gradleutils'
|
||||
alias(libs.plugins.apt)
|
||||
alias libs.plugins.licenser
|
||||
alias libs.plugins.gradleutils
|
||||
alias libs.plugins.gitversion
|
||||
alias libs.plugins.changelog
|
||||
alias libs.plugins.apt
|
||||
id 'net.minecraftforge.forge.build.convention'
|
||||
}
|
||||
|
||||
apply from: rootProject.file('build_shared.gradle')
|
||||
|
||||
configurations.forEach{ it.transitive = false }
|
||||
|
||||
dependencies {
|
||||
compileOnly(libs.jetbrains.annotations)
|
||||
|
||||
api(libs.bundles.asm) // Needed by all the black magic
|
||||
api(libs.forgespi)
|
||||
api(libs.mergetool.api)
|
||||
api(libs.log4j.api)
|
||||
api(libs.slf4j.api)
|
||||
api(libs.guava)
|
||||
api(libs.gson)
|
||||
api(libs.maven.artifact)
|
||||
api(libs.apache.commons)
|
||||
api(libs.bundles.night.config)
|
||||
api(libs.modlauncher)
|
||||
api(libs.mojang.logging)
|
||||
api(libs.jarjar.selector)
|
||||
api(libs.jarjar.meta)
|
||||
|
||||
implementation(libs.jopt.simple)
|
||||
implementation(libs.securemodules)
|
||||
implementation(libs.accesstransformers)
|
||||
implementation(libs.terminalconsoleappender)
|
||||
implementation(libs.jimfs)
|
||||
implementation(libs.roimfs)
|
||||
|
||||
// Needed because we have a custom log4j plugin, and they removed package scanning and require a data file to be generated
|
||||
implementation(libs.log4j.core)
|
||||
annotationProcessor(libs.bundles.log4j)
|
||||
|
||||
testCompileOnly(libs.jetbrains.annotations)
|
||||
testRuntimeOnly(libs.bootstrap)
|
||||
}
|
||||
gradleutils.displayName = 'FMLLoader'
|
||||
final vendor = 'Forge Development LLC'
|
||||
description = 'Modifications to Minecraft to enable mod developers.'
|
||||
|
||||
java {
|
||||
toolchain.languageVersion = JavaLanguageVersion.of(JAVA_VERSION)
|
||||
toolchain.languageVersion = JavaLanguageVersion.of(javaVersion)
|
||||
withSourcesJar()
|
||||
}
|
||||
|
||||
tasks.named('jar', Jar).configure {
|
||||
manifest {
|
||||
attributes([
|
||||
'Automatic-Module-Name': 'net.minecraftforge.fmlloader',
|
||||
'Forge-Module-Layer': 'boot'
|
||||
] as LinkedHashMap)
|
||||
attributes([
|
||||
'Specification-Title': 'FMLLoader',
|
||||
'Specification-Vendor': 'Forge Development LLC',
|
||||
'Specification-Version': '1',
|
||||
'Implementation-Title': 'FMLLoader',
|
||||
'Implementation-Vendor': 'Forge Development LLC',
|
||||
'Implementation-Version': FORGE_VERSION
|
||||
] as LinkedHashMap, 'net/minecraftforge/fml/loading/')
|
||||
license {
|
||||
header = rootProject.file('LICENSE-header.txt')
|
||||
}
|
||||
|
||||
changelog {
|
||||
from changelogBase
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly libs.jetbrains.annotations
|
||||
|
||||
// TODO [FMLLoader] Figure out a better way to lock transitive dependencies to the explicit versions shipped by the isntaller
|
||||
api(libs.bundles.asm ){ transitive = false } // Needed by all the black magic
|
||||
api(libs.forgespi ){ transitive = false }
|
||||
api(libs.mergetool.api ){ transitive = false }
|
||||
api(libs.log4j.api ){ transitive = false }
|
||||
api(libs.slf4j.api ){ transitive = false }
|
||||
api(libs.guava ){ transitive = false }
|
||||
api(libs.gson ){ transitive = false }
|
||||
api(libs.maven.artifact ){ transitive = false }
|
||||
api(libs.apache.commons ){ transitive = false }
|
||||
api(libs.bundles.night.config){ transitive = false }
|
||||
api(libs.modlauncher ){ transitive = false }
|
||||
api(libs.mojang.logging ){ transitive = false }
|
||||
api(libs.jarjar.selector ){ transitive = false }
|
||||
api(libs.jarjar.meta ){ transitive = false }
|
||||
|
||||
implementation(libs.jopt.simple ){ transitive = false }
|
||||
implementation(libs.securemodules ){ transitive = false }
|
||||
implementation(libs.accesstransformers ){ transitive = false }
|
||||
implementation(libs.terminalconsoleappender){ transitive = false }
|
||||
implementation(libs.jimfs ){ transitive = false }
|
||||
|
||||
// Needed because we have a custom log4j plugin, and they removed package scanning and require a data file to be generated
|
||||
implementation(libs.log4j.core ){ transitive = false }
|
||||
annotationProcessor(libs.bundles.log4j){ transitive = false }
|
||||
}
|
||||
|
||||
testing.suites.named('test', JvmTestSuite) {
|
||||
dependencies {
|
||||
compileOnly libs.jetbrains.annotations
|
||||
runtimeOnly libs.bootstrap
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,39 +76,81 @@ tasks.withType(JavaCompile).configureEach {
|
||||
options.compilerArgs << '-Xlint:unchecked'
|
||||
}
|
||||
|
||||
license {
|
||||
header = rootProject.file('LICENSE-header.txt')
|
||||
tasks.named('jar', Jar) {
|
||||
manifest {
|
||||
attributes([
|
||||
'Automatic-Module-Name': 'net.minecraftforge.fmlloader',
|
||||
'Forge-Module-Layer' : 'boot'
|
||||
])
|
||||
attributes([
|
||||
'Specification-Title' : gradleutils.displayName.get(),
|
||||
'Specification-Vendor' : vendor,
|
||||
'Specification-Version' : '1',
|
||||
'Implementation-Title' : gradleutils.displayName.get(),
|
||||
'Implementation-Vendor' : vendor,
|
||||
'Implementation-Version': forgeVersion
|
||||
], 'net/minecraftforge/fml/loading/')
|
||||
}
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications.register('mavenJava', MavenPublication).configure {
|
||||
from components.java
|
||||
artifactId = 'fmlloader'
|
||||
pom {
|
||||
name = project.name
|
||||
description = 'Modifactions to Minecraft to enable mod developers.'
|
||||
url = 'https://github.com/MinecraftForge/MinecraftForge'
|
||||
PomUtils.setGitHubDetails(pom, 'MinecraftForge')
|
||||
license PomUtils.Licenses.LGPLv2_1
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven gradleutils.publishingForgeMaven
|
||||
}
|
||||
}
|
||||
|
||||
tasks.register('writeForgeVersionJson') {
|
||||
doLast {
|
||||
file('src/main/resources/forge_version.json').json = [
|
||||
forge: FORGE_VERSION,
|
||||
mc: MC_VERSION,
|
||||
mcp: MCP_VERSION
|
||||
]
|
||||
publications.register('mavenJava', MavenPublication) {
|
||||
changelog.publish(it)
|
||||
gradleutils.promote(it)
|
||||
|
||||
from components.java
|
||||
|
||||
pom {
|
||||
description = project.description
|
||||
|
||||
gradleutils.pom.addRemoteDetails(pom)
|
||||
|
||||
licenses {
|
||||
license gradleutils.pom.licenses.LGPLv2_1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.register('jarJarOptionsJson', net.minecraftforge.forge.tasks.JarJarMetadataOptions) {
|
||||
// A simple task to write JSON to a file. Using Task#doLast to output files is no longer supported.
|
||||
// This code may seem verbose, but it's better this way. If more projects need this, we can move it to buildSrc.
|
||||
@CompileStatic
|
||||
@PackageScope abstract class WriteForgeVersionJson extends DefaultTask {
|
||||
abstract @Input Property<String> getForgeVersion()
|
||||
abstract @Input Property<String> getMinecraftVersion()
|
||||
abstract @Input Property<String> getMcpVersion()
|
||||
|
||||
abstract @OutputFile RegularFileProperty getOutputFile()
|
||||
|
||||
@Inject
|
||||
WriteForgeVersionJson() {}
|
||||
|
||||
@TaskAction
|
||||
@CompileDynamic
|
||||
void exec() {
|
||||
var json = new groovy.json.JsonBuilder()
|
||||
json {
|
||||
forge this.forgeVersion.get()
|
||||
mc this.minecraftVersion.get()
|
||||
mcp this.mcpVersion.get()
|
||||
}
|
||||
this.outputFile.asFile.get().text = json.toPrettyString()
|
||||
}
|
||||
}
|
||||
|
||||
tasks.register('writeForgeVersionJson', WriteForgeVersionJson) {
|
||||
forgeVersion = project.forgeVersion
|
||||
minecraftVersion = project.minecraftVersion
|
||||
mcpVersion = project.mcpVersion
|
||||
|
||||
outputFile = file('src/main/resources/forge_version.json')
|
||||
}
|
||||
|
||||
tasks.register('jarJarOptionsJson', JarJarMetadataOptions) {
|
||||
metadataFile = project.file('src/main/resources/jarjar_options.json')
|
||||
// This is resolved too early by cpw.mods.modlauncher.TransformationServicesHandler.discoverServices(DiscoveryData) But eventually...
|
||||
//add(libs.mixin, 'org/spongepowered/asm/mixin/Mixin.class')
|
||||
@@ -118,29 +165,25 @@ tasks.register('jarJarOptionsJson', net.minecraftforge.forge.tasks.JarJarMetadat
|
||||
}
|
||||
}
|
||||
|
||||
tasks.named('generateResources').configure {
|
||||
dependsOn('eclipseJdt')
|
||||
dependsOn('eclipseJdtApt')
|
||||
dependsOn('eclipseFactorypath')
|
||||
dependsOn('writeForgeVersionJson')
|
||||
dependsOn('jarJarOptionsJson')
|
||||
tasks.named('generateResources') {
|
||||
dependsOn(
|
||||
tasks.named('eclipseJdt'),
|
||||
tasks.named('eclipseJdtApt'),
|
||||
tasks.named('eclipseFactorypath'),
|
||||
tasks.named('writeForgeVersionJson', WriteForgeVersionJson),
|
||||
tasks.named('jarJarOptionsJson')
|
||||
)
|
||||
}
|
||||
|
||||
tasks.named('sourcesJar') {
|
||||
dependsOn('jarJarOptionsJson')
|
||||
}
|
||||
|
||||
eclipse {
|
||||
classpath {
|
||||
// We need to set the default output directory for the log4j annotation processor
|
||||
// Ideally we'd just set the value, but the eclipse plugin deduplicates the output directories
|
||||
// So we have to do a hacky whenMerged
|
||||
//defaultOutputDir = file('bin/main')
|
||||
file.whenMerged {
|
||||
entries.each { entry ->
|
||||
if (entry.kind == 'output' && entry.hasProperty('path'))
|
||||
entry.path = 'bin/main'
|
||||
}
|
||||
}
|
||||
eclipse.classpath {
|
||||
// We need to set the default output directory for the log4j annotation processor
|
||||
// Ideally we'd just set the value, but the eclipse plugin deduplicates the output directories
|
||||
// So we have to do a hacky whenMerged
|
||||
//defaultOutputDir = file('bin/main')
|
||||
file.whenMerged {
|
||||
entries.each { entry ->
|
||||
if (entry.kind == 'output' && entry.hasProperty('path'))
|
||||
entry.path = 'bin/main'
|
||||
}
|
||||
}
|
||||
}
|
||||
+13
-4
@@ -6,11 +6,13 @@
|
||||
package net.minecraftforge.fml.loading.moddiscovery;
|
||||
|
||||
import com.electronwill.nightconfig.core.file.FileConfig;
|
||||
import com.electronwill.nightconfig.core.io.ParsingException;
|
||||
import com.mojang.logging.LogUtils;
|
||||
import net.minecraftforge.fml.loading.LogMarkers;
|
||||
import net.minecraftforge.forgespi.language.IModFileInfo;
|
||||
import net.minecraftforge.forgespi.locating.IModFile;
|
||||
import net.minecraftforge.forgespi.locating.ModFileFactory;
|
||||
import net.minecraftforge.forgespi.locating.ModFileLoadingException;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
@@ -24,6 +26,7 @@ public class ModFileParser {
|
||||
return parser.build(modFile);
|
||||
}
|
||||
|
||||
// Note: Although @Nullable, several other places in FML assume ModFileInfo is not null. Keep this in mind
|
||||
public static @Nullable IModFileInfo modsTomlParser(final IModFile imodFile) {
|
||||
ModFile modFile = (ModFile) imodFile;
|
||||
LOGGER.debug(LogMarkers.LOADING,"Considering mod file candidate {}", modFile.getFilePath());
|
||||
@@ -34,9 +37,15 @@ public class ModFileParser {
|
||||
}
|
||||
|
||||
final FileConfig fileConfig = FileConfig.builder(modsjson).build();
|
||||
fileConfig.load();
|
||||
fileConfig.close();
|
||||
final NightConfigWrapper configWrapper = new NightConfigWrapper(fileConfig);
|
||||
return new ModFileInfo(modFile, configWrapper, configWrapper::setFile);
|
||||
try {
|
||||
fileConfig.load();
|
||||
fileConfig.close();
|
||||
final NightConfigWrapper configWrapper = new NightConfigWrapper(fileConfig);
|
||||
return new ModFileInfo(modFile, configWrapper, configWrapper::setFile);
|
||||
} catch (ParsingException e) {
|
||||
LOGGER.error("Mod candidate {} contains a corrupt or misconfigured toml.", modFile.getFileName());
|
||||
throw new ModFileLoadingException("Mod candidate " + modFile.getFileName() + " contains a corrupt or misconfigured toml.");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -11,8 +11,8 @@
|
||||
"artifact": "MixinExtras"
|
||||
},
|
||||
"version": {
|
||||
"range": "[0.5.3,)",
|
||||
"artifactVersion": "0.5.3"
|
||||
"range": "[0.5.4,)",
|
||||
"artifactVersion": "0.5.4"
|
||||
},
|
||||
"path": "",
|
||||
"isObfuscated": false
|
||||
@@ -24,7 +24,7 @@
|
||||
"artifact": "mixinextras-forge"
|
||||
},
|
||||
"version": {
|
||||
"artifactVersion": "0.5.3"
|
||||
"artifactVersion": "0.5.4"
|
||||
},
|
||||
"path": "",
|
||||
"isObfuscated": false
|
||||
|
||||
@@ -1,73 +1,78 @@
|
||||
import net.minecraftforge.gradleutils.PomUtils
|
||||
|
||||
plugins {
|
||||
id 'java-library'
|
||||
id 'maven-publish'
|
||||
id 'net.minecraftforge.licenser'
|
||||
id 'net.minecraftforge.gradleutils'
|
||||
alias(libs.plugins.apt)
|
||||
alias libs.plugins.licenser
|
||||
alias libs.plugins.gradleutils
|
||||
alias libs.plugins.gitversion
|
||||
alias libs.plugins.changelog
|
||||
id 'net.minecraftforge.forge.build.convention'
|
||||
}
|
||||
|
||||
apply from: rootProject.file('build_shared.gradle')
|
||||
|
||||
configurations.forEach{ it.transitive = false }
|
||||
|
||||
dependencies {
|
||||
compileOnly(libs.jetbrains.annotations)
|
||||
|
||||
implementation(libs.bundles.asm) // Needed by all the black magic
|
||||
implementation(libs.log4j.api)
|
||||
implementation(libs.gson)
|
||||
implementation(libs.modlauncher)
|
||||
implementation(libs.coremods.api)
|
||||
|
||||
testCompileOnly(libs.jetbrains.annotations)
|
||||
}
|
||||
gradleutils.displayName = 'Forge Transformers'
|
||||
final vendor = 'Forge Development LLC'
|
||||
description = 'Forge-specific transformers unrelated to the FML project.'
|
||||
|
||||
java {
|
||||
toolchain.languageVersion = JavaLanguageVersion.of(JAVA_VERSION)
|
||||
toolchain.languageVersion = JavaLanguageVersion.of(javaVersion)
|
||||
withSourcesJar()
|
||||
}
|
||||
|
||||
tasks.named('jar', Jar) {
|
||||
manifest {
|
||||
attributes([
|
||||
'Automatic-Module-Name': 'net.minecraftforge.forge.transformers',
|
||||
'Forge-Module-Layer': 'boot'
|
||||
] as LinkedHashMap)
|
||||
attributes([
|
||||
'Specification-Title': 'Forge Transformers',
|
||||
'Specification-Vendor': 'Forge Development LLC',
|
||||
'Specification-Version': '1',
|
||||
'Implementation-Title': 'Forge Transformers',
|
||||
'Implementation-Vendor': 'Forge Development LLC',
|
||||
'Implementation-Version': FORGE_VERSION
|
||||
] as LinkedHashMap, 'net/minecraftforge/forge/transformers/')
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile).configureEach {
|
||||
options.compilerArgs << '-Xlint:unchecked'
|
||||
}
|
||||
|
||||
license {
|
||||
header = rootProject.file('LICENSE-header.txt')
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications.register('mavenJava', MavenPublication) {
|
||||
from components.java
|
||||
artifactId = 'forge-transformers'
|
||||
pom {
|
||||
name = project.name
|
||||
description = 'Forge-specific transformers unrelated to the FML project.'
|
||||
url = 'https://github.com/MinecraftForge/MinecraftForge'
|
||||
PomUtils.setGitHubDetails(pom, 'MinecraftForge')
|
||||
license PomUtils.Licenses.LGPLv2_1
|
||||
}
|
||||
}
|
||||
changelog {
|
||||
from changelogBase
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(libs.jetbrains.annotations)
|
||||
|
||||
implementation(libs.bundles.asm ){ transitive = false } // Needed by all the black magic
|
||||
implementation(libs.log4j.api ){ transitive = false }
|
||||
implementation(libs.gson ){ transitive = false }
|
||||
implementation(libs.modlauncher ){ transitive = false }
|
||||
implementation(libs.coremods.api){ transitive = false }
|
||||
|
||||
testCompileOnly(libs.jetbrains.annotations)
|
||||
}
|
||||
|
||||
tasks.named('jar', Jar) {
|
||||
manifest {
|
||||
attributes([
|
||||
'Automatic-Module-Name': 'net.minecraftforge.forge.transformers',
|
||||
'Forge-Module-Layer' : 'boot'
|
||||
])
|
||||
attributes([
|
||||
'Specification-Title' : gradleutils.displayName.get(),
|
||||
'Specification-Vendor' : vendor,
|
||||
'Specification-Version' : '1',
|
||||
'Implementation-Title' : gradleutils.displayName.get(),
|
||||
'Implementation-Vendor' : vendor,
|
||||
'Implementation-Version': forgeVersion
|
||||
], 'net/minecraftforge/forge/transformers/')
|
||||
}
|
||||
}
|
||||
|
||||
publishing {
|
||||
repositories {
|
||||
maven gradleutils.publishingForgeMaven
|
||||
}
|
||||
|
||||
publications.register('mavenJava', MavenPublication) {
|
||||
changelog.publish(it)
|
||||
gradleutils.promote(it)
|
||||
|
||||
from components.java
|
||||
|
||||
pom { pom ->
|
||||
description = project.description
|
||||
|
||||
gradleutils.pom.addRemoteDetails(pom)
|
||||
|
||||
licenses {
|
||||
license gradleutils.pom.licenses.LGPLv2_1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,9 +44,7 @@
|
||||
},
|
||||
{
|
||||
"class": "net/minecraft/world/entity/animal/frog/Tadpole",
|
||||
"methods": [
|
||||
|
||||
]
|
||||
"methods": []
|
||||
},
|
||||
{
|
||||
"class": "net/minecraft/world/entity/monster/Strider",
|
||||
@@ -87,9 +85,7 @@
|
||||
},
|
||||
{
|
||||
"class": "net/minecraft/world/entity/monster/zombie/ZombieVillager",
|
||||
"methods": [
|
||||
|
||||
]
|
||||
"methods": []
|
||||
},
|
||||
{
|
||||
"class": "net/minecraft/world/entity/npc/CatSpawner",
|
||||
@@ -161,8 +157,6 @@
|
||||
},
|
||||
{
|
||||
"class": "net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate",
|
||||
"methods": [
|
||||
|
||||
]
|
||||
"methods": []
|
||||
}
|
||||
]
|
||||
+23
-11
@@ -1,17 +1,29 @@
|
||||
# TODO Re-evaluate
|
||||
# Sets default memory used for gradle commands. Can be overridden by user or command line properties.
|
||||
# This is required to provide enough memory for the Minecraft decompilation process.
|
||||
org.gradle.jvmargs=-Xmx6G
|
||||
org.gradle.daemon=false
|
||||
|
||||
org.gradle.warning.mode=all
|
||||
org.gradle.caching=true
|
||||
org.gradle.parallel=true
|
||||
org.gradle.configureondemand=true
|
||||
|
||||
JAVA_VERSION=21
|
||||
MC_VERSION=1.21.11
|
||||
MC_NEXT_VERSION=1.22
|
||||
MCP_VERSION=20251223.124241
|
||||
MAPPING_CHANNEL=official
|
||||
MAPPING_VERSION=1.21.11
|
||||
# TODO [ForgeDev] Enable this once ForgeDev 7 tasks are fully migrated
|
||||
org.gradle.configuration-cache=false
|
||||
org.gradle.configuration-cache.parallel=false
|
||||
#org.gradle.configuration-cache.problems=warn
|
||||
#org.gradle.configuration-cache.integrity-check=true
|
||||
|
||||
# Set to true before the first build of a new MC version, so we don't do compatibility checks
|
||||
CHECK_COMPATIBILITY=false
|
||||
# Set to true to allow for fuzzy patching. Useful during updateing
|
||||
UPDATING=false
|
||||
net.minecraftforge.gradleutils.ide.automatic.sources=true
|
||||
net.minecraftforge.gradleutils.compilation.defaults=true
|
||||
|
||||
net.minecraftforge.gradle.merge-source-sets=true
|
||||
|
||||
# Controls if the compatibility checks are auto-added to the 'check' task.
|
||||
# The check tasks are still created and can be run, they just are not grouped with the normal 'check' task.
|
||||
# Set to false for the first builds of a new Minecraft version.
|
||||
net.minecraftforge.forge.build.check.compatibility=false
|
||||
# Set to true to allow for fuzzy patching. Useful during updating.
|
||||
net.minecraftforge.forge.build.updating=false
|
||||
# Set to true to enable the 'validatePublish' task which is currently a work in progress
|
||||
net.minecraftforge.forgedev.validate.publish=false
|
||||
Vendored
BIN
Binary file not shown.
+1
-1
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12.1-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.0-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
# Copyright © 2015 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -15,6 +15,8 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
@@ -55,7 +57,7 @@
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
@@ -84,7 +86,7 @@ done
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
@@ -112,7 +114,6 @@ case "$( uname )" in #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
@@ -170,7 +171,6 @@ fi
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
@@ -203,15 +203,14 @@ fi
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
|
||||
Vendored
+3
-2
@@ -13,6 +13,8 @@
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
@rem SPDX-License-Identifier: Apache-2.0
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@@ -68,11 +70,10 @@ goto fail
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
|
||||
@@ -1,66 +1,79 @@
|
||||
import net.minecraftforge.gradleutils.PomUtils
|
||||
|
||||
plugins {
|
||||
id 'java-library'
|
||||
id 'maven-publish'
|
||||
id 'net.minecraftforge.licenser'
|
||||
id 'net.minecraftforge.gradleutils'
|
||||
alias libs.plugins.licenser
|
||||
alias libs.plugins.gradleutils
|
||||
alias libs.plugins.gitversion
|
||||
alias libs.plugins.changelog
|
||||
id 'net.minecraftforge.forge.build.convention'
|
||||
}
|
||||
|
||||
apply from: rootProject.file('build_shared.gradle')
|
||||
|
||||
dependencies {
|
||||
compileOnly(libs.jetbrains.annotations)
|
||||
implementation(project(':fmlloader'))
|
||||
implementation(project(':fmlcore'))
|
||||
implementation(libs.unsafe)
|
||||
implementation(libs.securemodules)
|
||||
}
|
||||
gradleutils.displayName = 'JavaFMLMod'
|
||||
final vendor = 'Forge Development LLC'
|
||||
description = 'Language provider for Minecraft Forge that provides basic Java mod functionality.'
|
||||
|
||||
java {
|
||||
toolchain.languageVersion = JavaLanguageVersion.of(JAVA_VERSION)
|
||||
toolchain.languageVersion = JavaLanguageVersion.of(javaVersion)
|
||||
withSourcesJar()
|
||||
}
|
||||
|
||||
tasks.named('jar', Jar).configure {
|
||||
manifest {
|
||||
attributes([
|
||||
'Automatic-Module-Name': 'net.minecraftforge.javafmlmod',
|
||||
'FMLModType': 'LANGPROVIDER'
|
||||
] as LinkedHashMap)
|
||||
attributes([
|
||||
'Specification-Title': 'JavaFMLMod',
|
||||
'Specification-Vendor': 'Forge Development LLC',
|
||||
'Specification-Version': '1',
|
||||
'Implementation-Title': 'JavaFMLMod',
|
||||
'Implementation-Vendor': 'Forge Development LLC',
|
||||
'Implementation-Version': FORGE_VERSION
|
||||
] as LinkedHashMap, 'net/minecraftforge/fml/javafmlmod/')
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile).configureEach {
|
||||
options.compilerArgs << '-Xlint:unchecked'
|
||||
}
|
||||
|
||||
license {
|
||||
header = rootProject.file('LICENSE-header.txt')
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications.register('mavenJava', MavenPublication).configure {
|
||||
from components.java
|
||||
artifactId = 'javafmllanguage'
|
||||
pom {
|
||||
name = project.name
|
||||
description = 'Language provider for Minecraft Forge that provides basic java mod functionality'
|
||||
url = 'https://github.com/MinecraftForge/MinecraftForge'
|
||||
PomUtils.setGitHubDetails(pom, 'MinecraftForge')
|
||||
license PomUtils.Licenses.LGPLv2_1
|
||||
}
|
||||
}
|
||||
changelog {
|
||||
from changelogBase
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly libs.jetbrains.annotations
|
||||
|
||||
implementation projects.fmlloader
|
||||
implementation projects.fmlcore
|
||||
implementation libs.unsafe
|
||||
implementation libs.securemodules
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile).configureEach {
|
||||
options.compilerArgs << '-Xlint:unchecked'
|
||||
}
|
||||
|
||||
tasks.named('jar', Jar) {
|
||||
manifest {
|
||||
attributes([
|
||||
'Automatic-Module-Name': 'net.minecraftforge.javafmlmod',
|
||||
'FMLModType' : 'LANGPROVIDER'
|
||||
])
|
||||
attributes([
|
||||
'Specification-Title' : gradleutils.displayName.get(),
|
||||
'Specification-Vendor' : vendor,
|
||||
'Specification-Version' : '1',
|
||||
'Implementation-Title' : gradleutils.displayName.get(),
|
||||
'Implementation-Vendor' : vendor,
|
||||
'Implementation-Version': forgeVersion
|
||||
], 'net/minecraftforge/fml/javafmlmod/')
|
||||
}
|
||||
}
|
||||
|
||||
publishing {
|
||||
repositories {
|
||||
maven gradleutils.publishingForgeMaven
|
||||
}
|
||||
|
||||
publications.register('mavenJava', MavenPublication) {
|
||||
changelog.publish(it)
|
||||
gradleutils.promote(it)
|
||||
|
||||
from components.java
|
||||
|
||||
pom {
|
||||
description = project.description
|
||||
|
||||
gradleutils.pom.addRemoteDetails(pom)
|
||||
|
||||
licenses {
|
||||
license gradleutils.pom.licenses.LGPLv2_1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,65 +1,77 @@
|
||||
import net.minecraftforge.gradleutils.PomUtils
|
||||
|
||||
plugins {
|
||||
id 'java-library'
|
||||
id 'maven-publish'
|
||||
id 'net.minecraftforge.licenser'
|
||||
id 'net.minecraftforge.gradleutils'
|
||||
alias libs.plugins.licenser
|
||||
alias libs.plugins.gradleutils
|
||||
alias libs.plugins.gitversion
|
||||
alias libs.plugins.changelog
|
||||
id 'net.minecraftforge.forge.build.convention'
|
||||
}
|
||||
|
||||
apply from: rootProject.file('build_shared.gradle')
|
||||
gradleutils.displayName = 'LowCodeMod'
|
||||
final vendor = 'Forge Development LLC'
|
||||
description = 'Language provider for Minecraft Forge that loads mods without a Java entrypoint.'
|
||||
|
||||
java {
|
||||
toolchain.languageVersion = JavaLanguageVersion.of(JAVA_VERSION)
|
||||
toolchain.languageVersion = JavaLanguageVersion.of(javaVersion)
|
||||
withSourcesJar()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(libs.jetbrains.annotations)
|
||||
|
||||
implementation(project(':fmlloader'))
|
||||
implementation(project(':fmlcore'))
|
||||
}
|
||||
|
||||
tasks.named('jar', Jar).configure {
|
||||
manifest {
|
||||
attributes([
|
||||
'Automatic-Module-Name': 'net.minecraftforge.lowcodemod',
|
||||
'FMLModType': 'LANGPROVIDER'
|
||||
] as LinkedHashMap)
|
||||
attributes([
|
||||
'Specification-Title': 'LowCodeMod',
|
||||
'Specification-Vendor': 'Forge Development LLC',
|
||||
'Specification-Version': '1.0',
|
||||
'Implementation-Title': project.name,
|
||||
'Implementation-Vendor': 'Forge Development LLC',
|
||||
'Implementation-Version': FORGE_VERSION.split('\\.')[0]
|
||||
] as java.util.LinkedHashMap, 'net/minecraftforge/fml/lowcodemod/')
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile).configureEach {
|
||||
options.compilerArgs << '-Xlint:unchecked'
|
||||
}
|
||||
|
||||
license {
|
||||
header = rootProject.file('LICENSE-header.txt')
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications.register('mavenJava', MavenPublication).configure {
|
||||
from components.java
|
||||
artifactId = 'lowcodelanguage'
|
||||
pom {
|
||||
name = project.name
|
||||
description = 'Language provider for Minecraft Forge that loads resource packs as mods'
|
||||
url = 'https://github.com/MinecraftForge/MinecraftForge'
|
||||
PomUtils.setGitHubDetails(pom, 'MinecraftForge')
|
||||
license PomUtils.Licenses.LGPLv2_1
|
||||
}
|
||||
changelog {
|
||||
from changelogBase
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly libs.jetbrains.annotations
|
||||
|
||||
implementation projects.fmlloader
|
||||
implementation projects.fmlcore
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile).configureEach {
|
||||
options.compilerArgs << '-Xlint:unchecked'
|
||||
}
|
||||
|
||||
tasks.named('jar', Jar) {
|
||||
manifest {
|
||||
attributes([
|
||||
'Automatic-Module-Name': 'net.minecraftforge.lowcodemod',
|
||||
'FMLModType' : 'LANGPROVIDER'
|
||||
])
|
||||
attributes([
|
||||
'Specification-Title' : gradleutils.displayName.get(),
|
||||
'Specification-Vendor' : vendor,
|
||||
'Specification-Version' : '1.0',
|
||||
'Implementation-Title' : project.name,
|
||||
'Implementation-Vendor' : vendor,
|
||||
'Implementation-Version': forgeVersion.split('\\.')[0]
|
||||
], 'net/minecraftforge/fml/lowcodemod/')
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
publishing {
|
||||
repositories {
|
||||
maven gradleutils.publishingForgeMaven
|
||||
}
|
||||
|
||||
publications.register('mavenJava', MavenPublication) {
|
||||
changelog.publish(it)
|
||||
gradleutils.promote(it)
|
||||
|
||||
from components.java
|
||||
|
||||
pom {
|
||||
description = project.description
|
||||
|
||||
gradleutils.pom.addRemoteDetails(pom)
|
||||
|
||||
licenses {
|
||||
license gradleutils.pom.licenses.LGPLv2_1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+58
-45
@@ -1,64 +1,77 @@
|
||||
import net.minecraftforge.gradleutils.PomUtils
|
||||
|
||||
plugins {
|
||||
id 'java-library'
|
||||
id 'maven-publish'
|
||||
id 'net.minecraftforge.licenser'
|
||||
id 'net.minecraftforge.gradleutils'
|
||||
alias libs.plugins.licenser
|
||||
alias libs.plugins.gradleutils
|
||||
alias libs.plugins.gitversion
|
||||
alias libs.plugins.changelog
|
||||
id 'net.minecraftforge.forge.build.convention'
|
||||
}
|
||||
|
||||
apply from: rootProject.file('build_shared.gradle')
|
||||
|
||||
dependencies {
|
||||
compileOnly(libs.jetbrains.annotations)
|
||||
implementation(project(':fmlloader'))
|
||||
implementation(project(':fmlcore'))
|
||||
}
|
||||
gradleutils.displayName = 'MCLanguage'
|
||||
final vendor = 'Forge Development LLC'
|
||||
description = 'Language provider for Minecraft Forge that provides Minecraft Itself.'
|
||||
|
||||
java {
|
||||
toolchain.languageVersion = JavaLanguageVersion.of(JAVA_VERSION)
|
||||
toolchain.languageVersion = JavaLanguageVersion.of(javaVersion)
|
||||
withSourcesJar()
|
||||
}
|
||||
|
||||
tasks.named('jar', Jar).configure {
|
||||
manifest {
|
||||
attributes([
|
||||
'Automatic-Module-Name': 'net.minecraftforge.mclanguageprovider',
|
||||
'FMLModType': 'LANGPROVIDER'
|
||||
] as LinkedHashMap)
|
||||
attributes([
|
||||
'Specification-Title': 'MCLanguage',
|
||||
'Specification-Vendor': 'Forge Development LLC',
|
||||
'Specification-Version': '1',
|
||||
'Implementation-Title': 'MCLanguage',
|
||||
'Implementation-Vendor': 'Forge Development LLC',
|
||||
'Implementation-Version': '1.0'
|
||||
] as LinkedHashMap, 'net/minecraftforge/fml/mclanguageprovider/')
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile).configureEach {
|
||||
options.compilerArgs << '-Xlint:unchecked'
|
||||
}
|
||||
|
||||
license {
|
||||
header = rootProject.file('LICENSE-header.txt')
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications.register('mavenJava', MavenPublication).configure {
|
||||
from components.java
|
||||
artifactId = 'mclanguage'
|
||||
pom {
|
||||
name = project.name
|
||||
description = 'Language provider for Minecraft Forge that provides Minecraft Itself'
|
||||
url = 'https://github.com/MinecraftForge/MinecraftForge'
|
||||
PomUtils.setGitHubDetails(pom, 'MinecraftForge')
|
||||
license PomUtils.Licenses.LGPLv2_1
|
||||
}
|
||||
changelog {
|
||||
from changelogBase
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly libs.jetbrains.annotations
|
||||
|
||||
implementation projects.fmlloader
|
||||
implementation projects.fmlcore
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile).configureEach {
|
||||
options.compilerArgs << '-Xlint:unchecked'
|
||||
}
|
||||
|
||||
tasks.named('jar', Jar) {
|
||||
manifest {
|
||||
attributes([
|
||||
'Automatic-Module-Name': 'net.minecraftforge.mclanguageprovider',
|
||||
'FMLModType' : 'LANGPROVIDER'
|
||||
])
|
||||
attributes([
|
||||
'Specification-Title' : gradleutils.displayName.get(),
|
||||
'Specification-Vendor' : vendor,
|
||||
'Specification-Version' : '1',
|
||||
'Implementation-Title' : gradleutils.displayName.get(),
|
||||
'Implementation-Vendor' : vendor,
|
||||
'Implementation-Version': '1.0'
|
||||
], 'net/minecraftforge/fml/mclanguageprovider/')
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
publishing {
|
||||
repositories {
|
||||
maven gradleutils.publishingForgeMaven
|
||||
}
|
||||
|
||||
publications.register('mavenJava', MavenPublication) {
|
||||
changelog.publish(it)
|
||||
gradleutils.promote(it)
|
||||
|
||||
from components.java
|
||||
|
||||
pom {
|
||||
description = project.description
|
||||
|
||||
gradleutils.pom.addRemoteDetails(pom)
|
||||
|
||||
licenses {
|
||||
license gradleutils.pom.licenses.LGPLv2_1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
[versions]
|
||||
java = "21"
|
||||
minecraft = "1.21.11"
|
||||
minecraft-next = "1.22"
|
||||
mcp = "20251223.124241"
|
||||
mappings-channel = "official"
|
||||
mappings-version = "1.21.11"
|
||||
changelog-base = "61.0"
|
||||
@@ -54,9 +54,24 @@
|
||||
p_282812_.blitSprite(RenderPipelines.GUI_TEXTURED, getMobEffectSprite(holder), k + 3, l + 3, 18, 18, ARGB.white(f));
|
||||
}
|
||||
}
|
||||
@@ -602,6 +_,10 @@
|
||||
@@ -601,7 +_,25 @@
|
||||
}
|
||||
}
|
||||
|
||||
+ // Forge: Maintain vanilla parity during FLD#BACKGROUND
|
||||
+ public final void updateContextualInfo(final GuiGraphics graphics, DeltaTracker deltaTracker) {
|
||||
+ Gui.ContextualInfo nextContextualInfo = this.nextContextualInfoState();
|
||||
+ if (nextContextualInfo != this.contextualInfoBar.getKey()) {
|
||||
+ this.contextualInfoBar = Pair.of(nextContextualInfo, this.contextualInfoBarRenderers.get(nextContextualInfo).get());
|
||||
+ }
|
||||
+
|
||||
+ this.contextualInfoBar.getValue().renderBackground(graphics, deltaTracker);
|
||||
+ }
|
||||
+ // Forge: Same as above but for FLD#CONTEXTUAL_INFO
|
||||
+ public final void extractContextualInfoState(final GuiGraphics graphics, DeltaTracker deltaTracker) {
|
||||
+ this.contextualInfoBar.getValue().render(graphics, deltaTracker);
|
||||
+ }
|
||||
+
|
||||
public void renderSelectedItemName(GuiGraphics p_283501_) {
|
||||
+ renderSelectedItemName(p_283501_, 0);
|
||||
+ }
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
}
|
||||
|
||||
this.addLateDebugPass(framegraphbuilder, this.levelRenderState.cameraRenderState, p_407881_, p_254120_);
|
||||
+ net.minecraftforge.client.FramePassManager.insertForgePasses(framegraphbuilder, targets, this.levelRenderState); // Forge: Modded passes are inserted here.
|
||||
+ net.minecraftforge.client.FramePassManager.insertForgePasses(framegraphbuilder, targets, this.levelRenderState, p_342180_); // Forge: Modded passes are inserted here.
|
||||
profilerfiller.popPush("executeFrameGraph");
|
||||
framegraphbuilder.execute(p_367325_, new FrameGraphBuilder.Inspector() {
|
||||
@Override
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
--- a/net/minecraft/client/renderer/block/BlockModelShaper.java
|
||||
+++ b/net/minecraft/client/renderer/block/BlockModelShaper.java
|
||||
@@ -17,8 +_,13 @@
|
||||
@@ -17,8 +_,15 @@
|
||||
this.modelManager = p_110880_;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,9 @@
|
||||
+ }
|
||||
+
|
||||
+ public TextureAtlasSprite getParticleIcon(BlockState p_110883_, net.minecraft.world.level.Level level, net.minecraft.core.BlockPos pos) {
|
||||
+ return this.getBlockModel(p_110883_).particleIcon(level.getModelDataManager().getAt(pos));
|
||||
+ net.minecraftforge.client.model.data.ModelData data = level.getModelDataManager().getAtOrEmpty(pos);
|
||||
+ BlockStateModel model = this.getBlockModel(p_110883_);
|
||||
+ return model.particleIcon(model.getModelData(level, pos, p_110883_, data));
|
||||
}
|
||||
|
||||
public BlockStateModel getBlockModel(BlockState p_110894_) {
|
||||
@@ -0,0 +1,15 @@
|
||||
--- a/net/minecraft/server/jsonrpc/api/MethodInfo.java
|
||||
+++ b/net/minecraft/server/jsonrpc/api/MethodInfo.java
|
||||
@@ -27,10 +_,10 @@
|
||||
}
|
||||
|
||||
static <Params, Result> MapCodec<MethodInfo<Params, Result>> typedCodec() {
|
||||
- return (MapCodec)RecordCodecBuilder.<MethodInfo>mapCodec(
|
||||
+ return RecordCodecBuilder.mapCodec(
|
||||
p_449116_ -> p_449116_.group(
|
||||
Codec.STRING.fieldOf("description").forGetter(MethodInfo::description),
|
||||
- paramsTypedCodec().fieldOf("params").forGetter(MethodInfo::params),
|
||||
+ MethodInfo.<Params>paramsTypedCodec().fieldOf("params").forGetter(MethodInfo::params),
|
||||
ResultInfo.<Result>typedCodec().optionalFieldOf("result").forGetter(MethodInfo::result)
|
||||
)
|
||||
.apply(p_449116_, MethodInfo::new)
|
||||
@@ -0,0 +1,23 @@
|
||||
--- a/net/minecraft/server/jsonrpc/api/Schema.java
|
||||
+++ b/net/minecraft/server/jsonrpc/api/Schema.java
|
||||
@@ -46,15 +_,15 @@
|
||||
"Schema",
|
||||
p_422846_ -> RecordCodecBuilder.create(
|
||||
p_449128_ -> p_449128_.group(
|
||||
- ReferenceUtil.REFERENCE_CODEC.optionalFieldOf("$ref").<Schema>forGetter(Schema::reference),
|
||||
+ ReferenceUtil.REFERENCE_CODEC.optionalFieldOf("$ref").forGetter(Schema::reference),
|
||||
ExtraCodecs.compactListCodec(Codec.STRING)
|
||||
.optionalFieldOf("type", List.of())
|
||||
- .<Schema>forGetter(Schema::type),
|
||||
- p_422846_.optionalFieldOf("items").<Schema>forGetter(Schema::items),
|
||||
+ .forGetter(Schema::type),
|
||||
+ p_422846_.optionalFieldOf("items").forGetter(Schema::items),
|
||||
Codec.unboundedMap(Codec.STRING, p_422846_)
|
||||
.optionalFieldOf("properties", Map.of())
|
||||
- .<Schema>forGetter(Schema::properties),
|
||||
- Codec.STRING.listOf().optionalFieldOf("enum", List.<String>of()).<Schema>forGetter(Schema::enumValues)
|
||||
+ .forGetter(Schema::properties),
|
||||
+ Codec.STRING.listOf().optionalFieldOf("enum", List.of()).forGetter(Schema::enumValues)
|
||||
)
|
||||
.apply(p_449128_, (p_449121_, p_449122_, p_449123_, p_449124_, p_449125_) -> null)
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
--- a/net/minecraft/world/level/chunk/PalettedContainer.java
|
||||
+++ b/net/minecraft/world/level/chunk/PalettedContainer.java
|
||||
@@ -52,7 +_,7 @@
|
||||
private static <T, C extends PalettedContainerRO<T>> Codec<C> codec(
|
||||
Codec<T> p_238429_, Strategy<T> p_430459_, T p_238431_, PalettedContainerRO.Unpacker<T, C> p_238432_
|
||||
) {
|
||||
- return RecordCodecBuilder.<PalettedContainerRO.PackedData>create(
|
||||
+ return RecordCodecBuilder.<PalettedContainerRO.PackedData<T>>create(
|
||||
p_327417_ -> p_327417_.group(
|
||||
p_238429_.mapResult(ExtraCodecs.orElsePartial(p_238431_)).listOf().fieldOf("palette").forGetter(PalettedContainerRO.PackedData::paletteEntries),
|
||||
Codec.LONG_STREAM.lenientOptionalFieldOf("data").forGetter(PalettedContainerRO.PackedData::storage)
|
||||
+196
-115
@@ -1,119 +1,34 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
//mavenLocal()
|
||||
maven { url = 'https://maven.minecraftforge.net/' }
|
||||
}
|
||||
}
|
||||
import groovy.transform.Field
|
||||
|
||||
buildscript {
|
||||
dependencies {
|
||||
classpath('com.google.code.gson:gson') {
|
||||
version {
|
||||
strictly '2.11.0'
|
||||
pluginManagement {
|
||||
private static final @Field FORGEDEV_VERSION = '7.0.0-beta.51'
|
||||
|
||||
if (new File('forgedev/settings.gradle').exists()) {
|
||||
includeBuild 'forgedev'
|
||||
} else {
|
||||
resolutionStrategy.eachPlugin {
|
||||
// TODO [ForgeDev] Consolidate these entry-points
|
||||
switch (requested.id.id) {
|
||||
case 'net.minecraftforge.forgedev':
|
||||
case 'net.minecraftforge.forge.build.convention':
|
||||
useVersion(FORGEDEV_VERSION)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
maven { url = 'https://maven.minecraftforge.net/' }
|
||||
//mavenLocal()
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0'
|
||||
}
|
||||
|
||||
dependencyResolutionManagement {
|
||||
versionCatalogs {
|
||||
libs {
|
||||
plugin('apt', 'com.diffplug.eclipse.apt').version('4.2.0')
|
||||
|
||||
library('forgespi', 'net.minecraftforge:forgespi:8.0.0') // Needs modlauncher
|
||||
library('modlauncher', 'net.minecraftforge:modlauncher:10.2.4') // Needs securemodules
|
||||
library('securemodules', 'net.minecraftforge:securemodules:2.2.24') // Needs unsafe
|
||||
library('unsafe', 'net.minecraftforge:unsafe:0.9.2')
|
||||
library('accesstransformers', 'net.minecraftforge:accesstransformers:8.2.2')
|
||||
library('coremods-api', 'net.minecraftforge:coremods-api:5.3.0')
|
||||
version('eventbus', '7.0.1')
|
||||
library('eventbus', 'net.minecraftforge', 'eventbus').versionRef('eventbus')
|
||||
library('eventbus-validator', 'net.minecraftforge', 'eventbus-validator').versionRef('eventbus')
|
||||
library('mergetool-api', 'net.minecraftforge:mergetool-api:1.0')
|
||||
|
||||
library('roimfs', 'net.minecraftforge:roimfs:1.0.0') // ReadOnlyInMemoryFileSystem - Used for JarInJar extraction at runtime
|
||||
library('mixin', 'org.spongepowered:mixin:0.8.7')
|
||||
version('mixinextras', '0.5.3')
|
||||
library('mixinextras-forge', 'io.github.llamalad7', 'mixinextras-forge').versionRef('mixinextras')
|
||||
library('mixinextras-common', 'io.github.llamalad7', 'mixinextras-common').versionRef('mixinextras')
|
||||
library('jetbrains-annotations', 'org.jetbrains:annotations:24.1.0') // for ApiStatus annotations
|
||||
library('jspecify', 'org.jspecify:jspecify:1.0.0') // for nullability annotations
|
||||
library('slf4j-api', 'org.slf4j:slf4j-api:2.0.7')
|
||||
library('maven-artifact', 'org.apache.maven:maven-artifact:3.8.8')
|
||||
|
||||
// Google's InMemory File System. Used by ForgeDev Tests for now, but could be useful for a lot of things.
|
||||
library('jimfs', 'com.google.jimfs:jimfs:1.3.0')
|
||||
bundle('jimfs', ['guava', 'failureaccess'])
|
||||
|
||||
version('bootstrap', '2.1.8')
|
||||
library('bootstrap', 'net.minecraftforge', 'bootstrap' ).versionRef('bootstrap') // Needs modlauncher
|
||||
library('bootstrap-api', 'net.minecraftforge', 'bootstrap-api' ).versionRef('bootstrap')
|
||||
library('bootstrap-dev', 'net.minecraftforge', 'bootstrap-dev' ).versionRef('bootstrap')
|
||||
library('bootstrap-shim', 'net.minecraftforge', 'bootstrap-shim').versionRef('bootstrap')
|
||||
|
||||
// ASM it's used for so many of our hacks
|
||||
version('asm', '9.9.1')
|
||||
library('asm', 'org.ow2.asm', 'asm' ).versionRef('asm')
|
||||
library('asm-tree', 'org.ow2.asm', 'asm-tree' ).versionRef('asm')
|
||||
library('asm-util', 'org.ow2.asm', 'asm-util' ).versionRef('asm')
|
||||
library('asm-commons', 'org.ow2.asm', 'asm-commons' ).versionRef('asm')
|
||||
library('asm-analysis', 'org.ow2.asm', 'asm-analysis').versionRef('asm')
|
||||
bundle('asm', ['asm', 'asm-tree', 'asm-util', 'asm-commons', 'asm-analysis'])
|
||||
|
||||
// Terminal Console Appender.. essentually make pretty colors in the console, but it's been a PITA
|
||||
library('terminalconsoleappender', 'net.minecrell:terminalconsoleappender:1.2.0')
|
||||
version('jline', '3.25.1')
|
||||
library('jline-reader', 'org.jline', 'jline-reader' ).versionRef('jline')
|
||||
library('jline-terminal', 'org.jline', 'jline-terminal' ).versionRef('jline')
|
||||
library('jline-terminal-jna', 'org.jline', 'jline-terminal-jna').versionRef('jline') // Colors and tab completeion
|
||||
bundle('terminalconsoleappender', ['terminalconsoleappender', 'jline-reader', 'jline-terminal', 'jline-terminal-jna'])
|
||||
|
||||
// The core of our configuration system, it has many flaws, but it works for the most part
|
||||
version('night-config', '3.7.4')
|
||||
library('night-config-toml', 'com.electronwill.night-config', 'toml').versionRef('night-config')
|
||||
library('night-config-core', 'com.electronwill.night-config', 'core').versionRef('night-config')
|
||||
bundle('night-config', ['night-config-toml', 'night-config-core'])
|
||||
|
||||
// Jar in Jar FileSystem
|
||||
version('jarjar', '0.4.2')
|
||||
library('jarjar-fs', 'net.minecraftforge', 'JarJarFileSystems').versionRef('jarjar')
|
||||
library('jarjar-meta', 'net.minecraftforge', 'JarJarMetadata' ).versionRef('jarjar')
|
||||
library('jarjar-selector', 'net.minecraftforge', 'JarJarSelector' ).versionRef('jarjar')
|
||||
bundle('jarjar', ['jarjar-fs', 'jarjar-selector', 'jarjar-meta'])
|
||||
|
||||
// These are libraries shipped by the MC launcher, try and keep them in sync with the manifest
|
||||
// but honestly if we don't it just means we ship them as normal libraries by adding them to the installer config in the forge project
|
||||
library('gson', 'com.google.code.gson:gson:2.11.0')
|
||||
library('guava', 'com.google.guava:guava:33.5.0-jre')
|
||||
library('failureaccess', 'com.google.guava:failureaccess:1.0.3')
|
||||
|
||||
version('log4j', '2.24.3')
|
||||
library('log4j-api', 'org.apache.logging.log4j', 'log4j-api' ).versionRef('log4j')
|
||||
library('log4j-core', 'org.apache.logging.log4j', 'log4j-core').versionRef('log4j')
|
||||
bundle('log4j', ['log4j-api', 'log4j-core'])
|
||||
|
||||
library('apache-commons', 'org.apache.commons:commons-lang3:3.17.0')
|
||||
library('mojang-logging', 'com.mojang:logging:1.5.10')
|
||||
library('jopt-simple', 'net.sf.jopt-simple:jopt-simple:5.0.4')
|
||||
library('commons-io', 'commons-io:commons-io:2.17.0')
|
||||
|
||||
version('lwjgl', '3.3.3')
|
||||
library('lwjgl', 'org.lwjgl', 'lwjgl' ).versionRef('lwjgl')
|
||||
library('lwjgl-glfw', 'org.lwjgl', 'lwjgl-glfw' ).versionRef('lwjgl')
|
||||
library('lwjgl-opengl', 'org.lwjgl', 'lwjgl-opengl').versionRef('lwjgl')
|
||||
library('lwjgl-stb', 'org.lwjgl', 'lwjgl-stb' ).versionRef('lwjgl')
|
||||
library('lwjgl-tinyfd', 'org.lwjgl', 'lwjgl-tinyfd').versionRef('lwjgl')
|
||||
bundle('lwjgl', ['lwjgl', 'lwjgl-glfw', 'lwjgl-opengl', 'lwjgl-stb', 'lwjgl-tinyfd'])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = 'ForgeRoot'
|
||||
rootProject.name = 'forge'
|
||||
|
||||
include 'fmlloader'
|
||||
include 'fmlcore'
|
||||
@@ -123,15 +38,181 @@ include 'lowcodelanguage'
|
||||
include 'fmlearlydisplay'
|
||||
include 'forge-transformers'
|
||||
|
||||
include ':mcp'
|
||||
project(":mcp").projectDir = file("projects/mcp")
|
||||
enableFeaturePreview 'TYPESAFE_PROJECT_ACCESSORS'
|
||||
|
||||
include ':forge'
|
||||
project(":forge").projectDir = file("projects/forge")
|
||||
project(':forge').buildFileName = '../../build_forge.gradle'
|
||||
gradle.beforeProject { Project project ->
|
||||
project.pluginManager.withPlugin('net.minecraftforge.forge.build.convention') {
|
||||
// NOTE: We are defining these variables in here so that the projects can stay isolated, increasing build time.
|
||||
// See minecraft.versions.toml for the versions where you can change these values.
|
||||
//@formatter:off
|
||||
project.ext.javaVersion = project.gradleutils.unpack(project.bootLibs.versions.java)
|
||||
project.ext.minecraftVersion = project.gradleutils.unpack(project.bootLibs.versions.minecraft)
|
||||
project.version = project.gitversion.getMCTagOffsetBranch(project.ext.minecraftVersion)
|
||||
project.ext.forgeVersion = project.version.substring(project.ext.minecraftVersion.length() + 1)
|
||||
project.ext.minecraftNextVersion = project.gradleutils.unpack(project.bootLibs.versions.minecraft.next)
|
||||
project.ext.mcpVersion = project.gradleutils.unpack(project.bootLibs.versions.mcp)
|
||||
project.ext.mappingsChannel = project.gradleutils.unpack(project.bootLibs.versions.mappings.channel)
|
||||
project.ext.mappingsVersion = project.gradleutils.unpack(project.bootLibs.versions.mappings.version)
|
||||
project.ext.changelogBase = project.gradleutils.unpack(project.bootLibs.versions.changelog.base)
|
||||
//@formatter:on
|
||||
}
|
||||
|
||||
if (false && !System.env.TEAMCITY_VERSION) {
|
||||
include ':clean'
|
||||
project(':clean').projectDir = file('projects/clean')
|
||||
project(':clean').buildFileName = '../../build_clean.gradle'
|
||||
// NOTE: We are adding dependencies to projects instead of through settings, so we don't have to apply forgedev in settings.
|
||||
// Applying forgedev in settings will break IDE linting support, which makes it very hard to work with.
|
||||
// We can reconsider this if/when switching to Kotlin DSL, or if JetBrains gets their shit together.
|
||||
project.repositories {
|
||||
// Libraries has to be before maven central because Mojang hosts a classifer that central doesn't (org.lwjgl:lwjgl-freetype:3.3.3:natives-macos-patch)
|
||||
maven { url = 'https://libraries.minecraft.net/' }
|
||||
mavenCentral()
|
||||
|
||||
project.pluginManager.withPlugin('net.minecraftforge.gradleutils') {
|
||||
maven project.gradleutils.forgeMaven
|
||||
}
|
||||
|
||||
project.pluginManager.withPlugin('net.minecraftforge.forgedev') {
|
||||
maven project.forgedev.mavenizer
|
||||
}
|
||||
|
||||
mavenLocal()
|
||||
}
|
||||
}
|
||||
|
||||
dependencyResolutionManagement.versionCatalogs {
|
||||
register('bootLibs') {
|
||||
description = 'Important version numbers that are externally declared for easy modification.'
|
||||
|
||||
// Contains Java, Minecraft, and MCP versions
|
||||
from(files('minecraft.versions.toml'))
|
||||
}
|
||||
|
||||
register('buildLibs') {
|
||||
description = 'Libraries used solely during the build and publish process.'
|
||||
|
||||
library 'binarypatcher', 'net.minecraftforge', 'binarypatcher' version '1.3.1'
|
||||
library 'installer', 'net.minecraftforge', 'installer' version '2.2.15'
|
||||
library 'installertools', 'net.minecraftforge', 'installertools' version '1.4.3'
|
||||
library 'renamer', 'net.minecraftforge', 'ForgeAutoRenamingTool' version '1.0.6'
|
||||
library 'srg2source', 'net.minecraftforge', 'Srg2Source' version '8.2.3'
|
||||
}
|
||||
|
||||
//@formatter:off
|
||||
register('libs') {
|
||||
description = 'Libraries used by Forge and its subprojects.'
|
||||
|
||||
plugin 'licenser', 'net.minecraftforge.licenser' version '1.2.0'
|
||||
plugin 'versions', 'io.github.ben-manes.versions' version '0.59.0'
|
||||
plugin 'gradleutils', 'net.minecraftforge.gradleutils' version '3.4.5'
|
||||
plugin 'gitversion', 'net.minecraftforge.gitversion' version '3.1.7'
|
||||
plugin 'changelog', 'net.minecraftforge.changelog' version '3.2.2'
|
||||
plugin 'renamer', 'net.minecraftforge.renamer' version '1.1.7'
|
||||
plugin 'download', 'de.undercouch.download' version '5.6.0'
|
||||
plugin 'jarsigner', 'net.minecraftforge.gradlejarsigner' version '1.2.1'
|
||||
plugin 'apt', 'com.diffplug.eclipse.apt' version '4.4.1'
|
||||
|
||||
// TODO [Forge][Buildscript We should not be needing to manually add transitive dependencies
|
||||
// If we need to make strict versions for dependencies, we should do so using dependency constraints
|
||||
library 'forgespi', 'net.minecraftforge', 'forgespi' version '8.0.0' // Needs modlauncher
|
||||
library 'modlauncher', 'net.minecraftforge', 'modlauncher' version '10.2.6' // Needs securemodules
|
||||
library 'securemodules', 'net.minecraftforge', 'securemodules' version '2.2.24' // Needs unsafe
|
||||
library 'unsafe', 'net.minecraftforge', 'unsafe' version '0.9.2'
|
||||
library 'accesstransformers', 'net.minecraftforge', 'accesstransformers' version '8.2.17'
|
||||
library 'coremods-api', 'net.minecraftforge', 'coremods-api' version '5.3.1'
|
||||
|
||||
version 'eventbus', '7.0.5'
|
||||
library 'eventbus', 'net.minecraftforge', 'eventbus' versionRef 'eventbus'
|
||||
library 'eventbus-validator', 'net.minecraftforge', 'eventbus-validator' versionRef 'eventbus'
|
||||
library 'mergetool-api', 'net.minecraftforge', 'mergetool-api' version '1.0'
|
||||
|
||||
library 'roimfs', 'net.minecraftforge', 'roimfs' version '1.0.0' // ReadOnlyInMemoryFileSystem - Used for JarInJar extraction at runtime
|
||||
library 'mixin', 'org.spongepowered', 'mixin' version '0.8.7'
|
||||
|
||||
version 'mixinextras', '0.5.4'
|
||||
library 'mixinextras-forge', 'io.github.llamalad7', 'mixinextras-forge' versionRef 'mixinextras'
|
||||
library 'mixinextras-common', 'io.github.llamalad7', 'mixinextras-common' versionRef 'mixinextras'
|
||||
library 'jetbrains-annotations', 'org.jetbrains', 'annotations' version '24.0.1' // for ApiStatus annotations
|
||||
library 'jspecify', 'org.jspecify', 'jspecify' version '1.0.0' // for nullability annotations
|
||||
library 'slf4j-api', 'org.slf4j', 'slf4j-api' version '2.0.17'
|
||||
library 'maven-artifact', 'org.apache.maven', 'maven-artifact' version '3.8.8'
|
||||
|
||||
// Google's InMemory File System. Used by ForgeDev Tests for now, but could be useful for a lot of things.
|
||||
library 'jimfs', 'com.google.jimfs:jimfs:1.3.0'
|
||||
bundle 'jimfs', ['guava', 'failureaccess']
|
||||
|
||||
version 'bootstrap', '2.1.8'
|
||||
library 'bootstrap', 'net.minecraftforge', 'bootstrap' versionRef 'bootstrap' // Needs modlauncher
|
||||
library 'bootstrap-api', 'net.minecraftforge', 'bootstrap-api' versionRef 'bootstrap'
|
||||
library 'bootstrap-dev', 'net.minecraftforge', 'bootstrap-dev' versionRef 'bootstrap'
|
||||
library 'bootstrap-shim', 'net.minecraftforge', 'bootstrap-shim' versionRef 'bootstrap'
|
||||
|
||||
// ASM it's used for so many of our hacks
|
||||
version 'asm', '9.10.1'
|
||||
library 'asm', 'org.ow2.asm', 'asm' versionRef 'asm'
|
||||
library 'asm-tree', 'org.ow2.asm', 'asm-tree' versionRef 'asm'
|
||||
library 'asm-util', 'org.ow2.asm', 'asm-util' versionRef 'asm'
|
||||
library 'asm-commons', 'org.ow2.asm', 'asm-commons' versionRef 'asm'
|
||||
library 'asm-analysis', 'org.ow2.asm', 'asm-analysis' versionRef 'asm'
|
||||
bundle 'asm', ['asm', 'asm-tree', 'asm-util', 'asm-commons', 'asm-analysis']
|
||||
|
||||
// Terminal Console Appender. essentially make pretty colors in the console, but it's been a PITA
|
||||
library 'terminalconsoleappender', 'net.minecrell', 'terminalconsoleappender' version '1.2.0'
|
||||
version 'jline', '3.25.1'
|
||||
library 'jline-reader', 'org.jline', 'jline-reader' versionRef 'jline'
|
||||
library 'jline-terminal', 'org.jline', 'jline-terminal' versionRef 'jline'
|
||||
library 'jline-terminal-jna', 'org.jline', 'jline-terminal-jna' versionRef 'jline' // Colors and tab completeion
|
||||
bundle 'terminalconsoleappender', ['terminalconsoleappender', 'jline-reader', 'jline-terminal', 'jline-terminal-jna']
|
||||
|
||||
// The core of our configuration system, it has many flaws, but it works for the most part
|
||||
version 'night-config', '3.7.4'
|
||||
library 'night-config-toml', 'com.electronwill.night-config', 'toml' versionRef 'night-config'
|
||||
library 'night-config-core', 'com.electronwill.night-config', 'core' versionRef 'night-config'
|
||||
bundle 'night-config', ['night-config-toml', 'night-config-core']
|
||||
|
||||
// Jar in Jar FileSystem
|
||||
version 'jarjar', '0.4.2'
|
||||
library 'jarjar-fs', 'net.minecraftforge', 'JarJarFileSystems' versionRef 'jarjar'
|
||||
library 'jarjar-meta', 'net.minecraftforge', 'JarJarMetadata' versionRef 'jarjar'
|
||||
library 'jarjar-selector', 'net.minecraftforge', 'JarJarSelector' versionRef 'jarjar'
|
||||
bundle 'jarjar', ['jarjar-fs', 'jarjar-selector', 'jarjar-meta']
|
||||
|
||||
// These are libraries shipped by the MC launcher, try and keep them in sync with the manifest
|
||||
// but honestly if we don't it just means we ship them as normal libraries by adding them to the installer config in the forge project
|
||||
library 'gson', 'com.google.code.gson', 'gson' version '2.13.2'
|
||||
library 'guava', 'com.google.guava', 'guava' version '33.5.0-jre'
|
||||
library 'failureaccess', 'com.google.guava', 'failureaccess' version '1.0.3'
|
||||
|
||||
version 'log4j', '2.25.2'
|
||||
library 'log4j-api', 'org.apache.logging.log4j', 'log4j-api' versionRef 'log4j'
|
||||
library 'log4j-core', 'org.apache.logging.log4j', 'log4j-core' versionRef 'log4j'
|
||||
bundle 'log4j', ['log4j-api', 'log4j-core']
|
||||
|
||||
library 'apache-commons', 'org.apache.commons', 'commons-lang3' version '3.19.0'
|
||||
library 'mojang-logging', 'com.mojang', 'logging' version '1.6.11'
|
||||
library 'jopt-simple', 'net.sf.jopt-simple', 'jopt-simple' version '5.0.4'
|
||||
library 'commons-io', 'commons-io', 'commons-io' version '2.20.0'
|
||||
|
||||
version 'lwjgl', '3.3.3'
|
||||
library 'lwjgl', 'org.lwjgl', 'lwjgl' versionRef 'lwjgl'
|
||||
library 'lwjgl-glfw', 'org.lwjgl', 'lwjgl-glfw' versionRef 'lwjgl'
|
||||
library 'lwjgl-opengl', 'org.lwjgl', 'lwjgl-opengl' versionRef 'lwjgl'
|
||||
library 'lwjgl-stb', 'org.lwjgl', 'lwjgl-stb' versionRef 'lwjgl'
|
||||
library 'lwjgl-tinyfd', 'org.lwjgl', 'lwjgl-tinyfd' versionRef 'lwjgl'
|
||||
bundle 'lwjgl', ['lwjgl', 'lwjgl-glfw', 'lwjgl-opengl', 'lwjgl-stb', 'lwjgl-tinyfd']
|
||||
}
|
||||
|
||||
register('earlyDisplayLibs') {
|
||||
version 'slf4j', '2.0.17'
|
||||
library 'slf4j-api', 'org.slf4j', 'slf4j-api' versionRef 'slf4j'
|
||||
library 'slf4j-jdk14', 'org.slf4j', 'slf4j-jdk14' versionRef 'slf4j'
|
||||
}
|
||||
|
||||
register('earlyDisplayTestLibs') {
|
||||
library 'powermock-core', 'org.powermock', 'powermock-core' version '2.0.9'
|
||||
|
||||
library 'lwjgl', 'org.lwjgl', 'lwjgl' withoutVersion()
|
||||
library 'lwjgl-glfw', 'org.lwjgl', 'lwjgl-glfw' withoutVersion()
|
||||
library 'lwjgl-opengl', 'org.lwjgl', 'lwjgl-opengl' withoutVersion()
|
||||
library 'lwjgl-stb', 'org.lwjgl', 'lwjgl-stb' withoutVersion()
|
||||
bundle 'lwjgl', ['lwjgl', 'lwjgl-glfw', 'lwjgl-opengl', 'lwjgl-stb']
|
||||
}
|
||||
//@formatter:on
|
||||
}
|
||||
@@ -109,7 +109,11 @@ public class ClientCommandHandler {
|
||||
new CommandSource() {
|
||||
@Override
|
||||
public void sendSystemMessage(Component message) {
|
||||
mc.gui.getChat().addMessage(message);
|
||||
var chat = mc.gui.getChat();
|
||||
if (mc.isSameThread())
|
||||
chat.addMessage(message);
|
||||
else
|
||||
mc.execute(() -> chat.addMessage(message));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -7,12 +7,12 @@ package net.minecraftforge.client;
|
||||
|
||||
import com.mojang.blaze3d.framegraph.FrameGraphBuilder;
|
||||
import com.mojang.blaze3d.framegraph.FramePass;
|
||||
import com.mojang.blaze3d.resource.ResourceHandle;
|
||||
import net.minecraft.client.DeltaTracker;
|
||||
import net.minecraft.client.renderer.LevelTargetBundle;
|
||||
import net.minecraft.client.renderer.state.LevelRenderState;
|
||||
import net.minecraft.resources.Identifier;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -30,43 +30,95 @@ public class FramePassManager {
|
||||
|
||||
// Note: Pass order is determined automatically within FrameGraphBuilder. It's unclear what must be done to guarantee ordering.
|
||||
@ApiStatus.Internal
|
||||
public static void insertForgePasses(FrameGraphBuilder graphBuilder, LevelTargetBundle bundle, LevelRenderState state) {
|
||||
public static void insertForgePasses(FrameGraphBuilder graphBuilder, LevelTargetBundle bundle, LevelRenderState state, DeltaTracker deltaTracker) {
|
||||
for (PassInfo info : addedPasses) {
|
||||
FramePass pass = graphBuilder.addPass(info.name);
|
||||
PassDefinition forgePass = info.pass;
|
||||
forgePass.extracts(bundle, pass);
|
||||
forgePass.extracts(bundle, pass, deltaTracker);
|
||||
pass.executes(() -> forgePass.executes(state));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// ### A PassDefinition must satisfy 3 things.
|
||||
///
|
||||
/// 1. The rendering order for the purpose of translucency sorting. Read further for specific details.
|
||||
/// 2. A render state definition supplier; "what" will be rendered.
|
||||
/// 3. A render state definition consumer; "how" it will be rendered.
|
||||
///
|
||||
///
|
||||
/// To satisfy #1, all FramePasses (and thus PassDefinitions) must bind against at least one target.
|
||||
/// The list of targets can be found in {@linkplain LevelTargetBundle}.
|
||||
/// HOWEVER!!! Only {@linkplain LevelTargetBundle#main} is used if the graphics mode is not set to Fabulous!
|
||||
/// So at minimum, all passes must guarantee a fallback binding to the main target, like the below.
|
||||
/// ```
|
||||
/// @Override
|
||||
/// void extracts(LevelTargetBundle bundle, FramePass pass, DeltaTracker deltaTracker) {
|
||||
/// if (bundle.clouds != null) { // Perhaps we want to bind to clouds if Fabulous! is on.
|
||||
/// bundle.clouds = pass.readsAndWrites(bundle.clouds);
|
||||
/// }
|
||||
/// // An else clause could be used, but is not mandatory. It is fine to bind to more than one target.
|
||||
/// bundle.main = pass.readsAndWrites(bundle.main);
|
||||
/// }
|
||||
/// ```
|
||||
/// </pre>
|
||||
///
|
||||
/// It is up to the user to decide how their pass should bind to targets, but it must always bind to at least one.
|
||||
/// Failure to satisfy this requirement will result in {@linkplain FrameGraphBuilder#resolvePassOrder} exploding.
|
||||
/// Custom render targets are possible but not documented, see the implementation of {@linkplain LevelTargetBundle}
|
||||
/// if you want to take a crack at it.
|
||||
///
|
||||
/// Satisfying #2 is simple. Any state information you need to extract
|
||||
/// should be done during {@linkplain PassDefinition#extracts(LevelTargetBundle, FramePass, DeltaTracker)}.
|
||||
/// No actual rendering should be done at this time. This can happen before or after binding to a target.
|
||||
/// The specific implementation of your render state is up to you, it can even be done with some instance variables.
|
||||
///
|
||||
/// Satisfying #3 is also simple, this is the actual rendering that will consume the state created
|
||||
/// during the extracts phase.
|
||||
@NullMarked
|
||||
public interface PassDefinition {
|
||||
/**
|
||||
* Use to define which targets your pass will bind against, see {@link FramePass#reads} and {@link FramePass#readsAndWrites}
|
||||
* A FramePass must bind to at least ONE target. Otherwise, you get freaky issues with >1 modded passes.
|
||||
* Additionally, this method should be used for extracting render states into instance variables if desired.
|
||||
* @deprecated Prefer {@linkplain PassDefinition#extracts(LevelTargetBundle, FramePass, DeltaTracker)}
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since="1.21.10")
|
||||
default void targets(LevelTargetBundle bundle, FramePass pass) {};
|
||||
|
||||
/**
|
||||
* Use to define which targets your pass will bind against, see {@link FramePass#reads} and {@link FramePass#readsAndWrites}
|
||||
* A FramePass must bind to at least ONE target. Otherwise, you get freaky issues with >1 modded passes.
|
||||
* Additionally, this method should be used for extracting render states into instance variables if desired.
|
||||
* @deprecated Prefer {@linkplain PassDefinition#extracts(LevelTargetBundle, FramePass, DeltaTracker)}
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since="26.2")
|
||||
default void extracts(LevelTargetBundle bundle, FramePass pass) {
|
||||
targets(bundle, pass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer {@link PassDefinition#extracts}
|
||||
* This method exists to do render state extraction. Your instance of a PassDefinition should have
|
||||
* locals or some filled record instance that represents the render state created.
|
||||
* The resulting render state should be consumed by {@linkplain PassDefinition#executes(LevelRenderState)}
|
||||
* You must also use this to define which targets your pass will bind against. See PassDefinition javadocs for details.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "1.21.10")
|
||||
default void targets(LevelTargetBundle bundle, FramePass pass) {}
|
||||
|
||||
/**
|
||||
* Use to define what your pass does during the render stage.
|
||||
*/
|
||||
default void executes(LevelRenderState state) {
|
||||
executes();
|
||||
default void extracts(LevelTargetBundle bundle, FramePass pass, DeltaTracker deltaTracker) {
|
||||
targets(bundle, pass);
|
||||
};
|
||||
|
||||
/**
|
||||
* Use to define what your pass does during the render stage, prefer {@link PassDefinition#executes(LevelRenderState)}.
|
||||
* Use to define what your pass does during the render stage. This should consume the render state created
|
||||
* during the extracts phase.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "1.21.10")
|
||||
default void executes(){};
|
||||
void executes(LevelRenderState state);
|
||||
|
||||
/**
|
||||
* Use to define what your pass does during the render stage, prefer {@link PassDefinition#executes(LevelRenderState)}.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "1.21.10")
|
||||
default void executes(){};
|
||||
}
|
||||
|
||||
private record PassInfo(String name, PassDefinition pass){}
|
||||
|
||||
@@ -13,7 +13,7 @@ import net.minecraftforge.eventbus.api.event.RecordEvent;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
/**
|
||||
* Fired after all vanilla frame passes are added into the pass list.
|
||||
* Fired during the construction of {@linkplain net.minecraft.client.renderer.LevelRenderer}.
|
||||
*
|
||||
* <p>This event is fired on the {@linkplain net.minecraftforge.common.MinecraftForge#EVENT_BUS main Forge event bus},
|
||||
* only on the {@linkplain net.minecraftforge.fml.LogicalSide#CLIENT logical client}.
|
||||
@@ -24,8 +24,9 @@ public record AddFramePassEvent() implements RecordEvent {
|
||||
/**
|
||||
* Adds a frame pass to pass list.
|
||||
* Create a new {@linkplain FramePassManager.PassDefinition} to handle render targets and render code.
|
||||
* See javadocs for details on what creating a PassDefinition entails.
|
||||
*
|
||||
* @param rl Resource location for frame pass name. Use RLs to avoid duplicate names.
|
||||
* @param rl Identifier for frame pass name. Use RLs to avoid duplicate names.
|
||||
* @param definition see usages of {@linkplain com.mojang.blaze3d.framegraph.FramePass} in {@linkplain net.minecraft.client.renderer.LevelRenderer}
|
||||
* @throws IllegalArgumentException If the name is a duplicate.
|
||||
*/
|
||||
|
||||
@@ -23,6 +23,7 @@ import net.minecraftforge.client.gui.widget.ModListWidget;
|
||||
import net.minecraftforge.client.gui.widget.ScrollPanel;
|
||||
import net.minecraftforge.fml.loading.moddiscovery.ModFileInfo;
|
||||
import org.apache.maven.artifact.versioning.ComparableVersion;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.ActiveTextCollector;
|
||||
@@ -299,8 +300,6 @@ public class ModListScreen extends Screen {
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
modList.setSelected(selected);
|
||||
|
||||
if (!search.getValue().equals(lastFilterText)) {
|
||||
reloadMods();
|
||||
sorted = false;
|
||||
@@ -311,11 +310,11 @@ public class ModListScreen extends Screen {
|
||||
mods.sort(sortType);
|
||||
modList.refreshList();
|
||||
if (selected != null) {
|
||||
selected = modList.children().stream()
|
||||
final var newSelected = modList.children().stream()
|
||||
.filter(e -> e.getInfo() == selected.getInfo())
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
updateCache();
|
||||
this.modList.setSelected(newSelected);
|
||||
}
|
||||
sorted = true;
|
||||
}
|
||||
@@ -365,8 +364,14 @@ public class ModListScreen extends Screen {
|
||||
}
|
||||
|
||||
public void setSelected(ModListWidget.ModEntry entry) {
|
||||
this.selected = entry == this.selected ? null : entry;
|
||||
updateCache();
|
||||
if (this.selected != entry) {
|
||||
this.selected = entry;
|
||||
updateCache();
|
||||
}
|
||||
}
|
||||
|
||||
public ModListWidget.@Nullable ModEntry getSelected() {
|
||||
return this.selected;
|
||||
}
|
||||
|
||||
record Logo(Identifier texture, Size2i size) {}
|
||||
@@ -473,7 +478,6 @@ public class ModListScreen extends Screen {
|
||||
ModListWidget.ModEntry selected = this.selected;
|
||||
this.init(width, height);
|
||||
this.search.setValue(s);
|
||||
this.selected = selected;
|
||||
|
||||
if (!this.search.getValue().isEmpty())
|
||||
reloadMods();
|
||||
@@ -481,7 +485,7 @@ public class ModListScreen extends Screen {
|
||||
if (sort != SortType.NORMAL)
|
||||
resortMods(sort);
|
||||
|
||||
updateCache();
|
||||
this.modList.setSelected(selected);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -10,6 +10,7 @@ import net.minecraft.client.DeltaTracker;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.Gui;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.gui.contextualbar.ContextualBarRenderer;
|
||||
import net.minecraft.resources.Identifier;
|
||||
import net.minecraftforge.client.event.ForgeEventFactoryClient;
|
||||
import net.minecraftforge.client.event.AddGuiOverlayLayersEvent;
|
||||
@@ -38,14 +39,27 @@ public final class ForgeLayeredDraw implements ForgeLayer {
|
||||
private final List<ForgeLayer> bakedLayers = new ArrayList<>();
|
||||
private final Identifier name;
|
||||
|
||||
// Begin pre-sleep overlay
|
||||
public static final Identifier PRE_SLEEP_STACK = Identifier.withDefaultNamespace("pre_sleep_phase");
|
||||
public static final Identifier CAMERA_OVERLAY = Identifier.withDefaultNamespace("camera_overlay");
|
||||
public static final Identifier CROSSHAIR = Identifier.withDefaultNamespace("crosshair");
|
||||
public static final Identifier CHANGE_STRATUM = Identifier.withDefaultNamespace("stratum_change");
|
||||
// Begin hotbar
|
||||
public static final Identifier HOTBAR_AND_DECOS = Identifier.withDefaultNamespace("hotbar");
|
||||
public static final Identifier ITEM_HOTBAR = Identifier.withDefaultNamespace("item_hotbar");
|
||||
public static final Identifier SPECTATOR_HOTBAR = Identifier.withDefaultNamespace("spectator_hotbar");
|
||||
public static final Identifier HEALTH_BAR = Identifier.withDefaultNamespace("health_bar");
|
||||
public static final Identifier VEHICLE_HEALTH = Identifier.withDefaultNamespace("vehicle_health");
|
||||
public static final Identifier BACKGROUND = Identifier.withDefaultNamespace("background"); // Any layer needing contextual info should order at some point after this layer.
|
||||
public static final Identifier EXPERIENCE_LEVEL = Identifier.withDefaultNamespace("experience_level");
|
||||
public static final Identifier CONTEXTUAL_INFO = Identifier.withDefaultNamespace("contextual_info");
|
||||
public static final Identifier SELECTED_ITEM_NAME = Identifier.withDefaultNamespace("selected_item_name");
|
||||
public static final Identifier SPECTATOR_ACTION = Identifier.withDefaultNamespace("spectator_action");
|
||||
// End hotbar
|
||||
public static final Identifier POTION_EFFECTS = Identifier.withDefaultNamespace("potion_effects");
|
||||
public static final Identifier BOSS_OVERLAY = Identifier.withDefaultNamespace("boss_overlay");
|
||||
|
||||
// End pre-sleep overlay
|
||||
// Begin post-sleep overlay
|
||||
public static final Identifier POST_SLEEP_STACK = Identifier.withDefaultNamespace("post_sleep_phase");
|
||||
public static final Identifier DEMO_OVERLAY = Identifier.withDefaultNamespace("demo");
|
||||
public static final Identifier DEBUG_OVERLAY = Identifier.withDefaultNamespace("debug");
|
||||
@@ -55,6 +69,7 @@ public final class ForgeLayeredDraw implements ForgeLayer {
|
||||
public static final Identifier CHAT_OVERLAY = Identifier.withDefaultNamespace("chat_overlay");
|
||||
public static final Identifier TAB_LIST = Identifier.withDefaultNamespace("tab_list");
|
||||
public static final Identifier SUBTITLE_OVERLAY = Identifier.withDefaultNamespace("subtitle");
|
||||
// End post-sleep overlay
|
||||
|
||||
public static final Identifier VANILLA_ROOT = Identifier.withDefaultNamespace("vanilla_root");
|
||||
public static final Identifier SLEEP_OVERLAY = Identifier.withDefaultNamespace("sleep_overlay");
|
||||
@@ -87,6 +102,17 @@ public final class ForgeLayeredDraw implements ForgeLayer {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a full draw stack and assumes condition is always true.
|
||||
* Use {@linkplain ForgeLayeredDraw#putAbove} and {@linkplain ForgeLayeredDraw#putBelow} for fine location adjustment.
|
||||
* @param name RL of the name to identify this stack with.
|
||||
* @param layeredDraw the draw stack
|
||||
* @return this
|
||||
*/
|
||||
public ForgeLayeredDraw add(Identifier name, ForgeLayeredDraw layeredDraw) {
|
||||
return add(name, layeredDraw, () -> true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a layer to the layer list. This layer will be at the end of the list, which means
|
||||
* it will be rendered last (on top) of already added layers.
|
||||
@@ -279,6 +305,28 @@ public final class ForgeLayeredDraw implements ForgeLayer {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the renderer of a single layer and logs whodunnit, this is not recommended for obvious reasons.
|
||||
* Will not work if target is a ForgeLayeredDraw
|
||||
* Prefer {@linkplain ForgeLayeredDraw#addConditionTo}
|
||||
* @param expectedLocation Layer stack where the target should be.
|
||||
* @param targetLayer Target whose renderer should be replaced.
|
||||
* @param replacementRenderer Renderer to use instead.
|
||||
* @return this
|
||||
*/
|
||||
public ForgeLayeredDraw replace(Identifier expectedLocation, Identifier targetLayer, ForgeLayer replacementRenderer) {
|
||||
locateStack(expectedLocation).ifPresentOrElse((stack) -> {
|
||||
if (stack.namedLayers.get(targetLayer) != null) {
|
||||
stack.namedLayers.put(targetLayer, replacementRenderer);
|
||||
LogUtils.getLogger().debug("ForgeLayer {} in {} was replaced by {}.", targetLayer, expectedLocation, replacementRenderer);
|
||||
} else {
|
||||
LogUtils.getLogger().debug("ForgeLayer {} in {} was attempted to be replaced by {}, but it did not exist.", targetLayer, expectedLocation, replacementRenderer);
|
||||
}
|
||||
}, () -> stackNotPresentWarning(expectedLocation));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Propagate the layer order down to the inner render list after providing modders an opportunity to alter the list as they wish.
|
||||
* @apiNote Modders should <emph>NEVER</emph> be calling this method.
|
||||
@@ -381,11 +429,22 @@ public final class ForgeLayeredDraw implements ForgeLayer {
|
||||
|
||||
@ApiStatus.Internal
|
||||
public static void init(Gui gui, Minecraft minecraft) {
|
||||
BooleanSupplier spectator = () -> minecraft.gameMode.isSpectator();
|
||||
var hotbarCluster = new ForgeLayeredDraw(HOTBAR_AND_DECOS)
|
||||
.addWithCondition(SPECTATOR_HOTBAR, (gg, dt) -> gui.getSpectatorGui().renderHotbar(gg), spectator)
|
||||
.addWithCondition(ITEM_HOTBAR, gui::renderItemHotbar, () -> !spectator.getAsBoolean())
|
||||
.addWithCondition(HEALTH_BAR, (gg, dt) -> gui.renderPlayerHealth(gg), () -> minecraft.gameMode.canHurtPlayer())
|
||||
.add(VEHICLE_HEALTH, (gg, dt) -> gui.renderVehicleHealth(gg))
|
||||
.add(BACKGROUND, gui::updateContextualInfo)
|
||||
.addWithCondition(EXPERIENCE_LEVEL, (gg, dt) -> ContextualBarRenderer.renderExperienceLevel(gg, minecraft.font, minecraft.player.experienceLevel), () -> minecraft.gameMode.hasExperience() && minecraft.player.experienceLevel > 0)
|
||||
.add(CONTEXTUAL_INFO, gui::extractContextualInfoState)
|
||||
.addWithCondition(SELECTED_ITEM_NAME, (gg, dt) -> gui.renderSelectedItemName(gg), () -> !spectator.getAsBoolean())
|
||||
.addWithCondition(SPECTATOR_ACTION, (gg, dt) -> gui.getSpectatorGui().renderAction(gg), spectator);
|
||||
var preSleepDraw = new ForgeLayeredDraw(PRE_SLEEP_STACK)
|
||||
.add(CAMERA_OVERLAY, gui::renderCameraOverlays)
|
||||
.add(CROSSHAIR, gui::renderCrosshair)
|
||||
.add(CHANGE_STRATUM, (gg, dt) -> gg.nextStratum())
|
||||
.add(HOTBAR_AND_DECOS, gui::renderHotbarAndDecorations)
|
||||
.add(HOTBAR_AND_DECOS, hotbarCluster)
|
||||
.add(POTION_EFFECTS, gui::renderEffects)
|
||||
.add(BOSS_OVERLAY, gui::renderBossOverlay);
|
||||
var postSleepDraw = new ForgeLayeredDraw(POST_SLEEP_STACK)
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
package net.minecraftforge.client.gui.widget;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import net.minecraft.client.gui.Font;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.gui.components.ObjectSelectionList;
|
||||
@@ -60,6 +62,12 @@ public class ModListWidget extends ObjectSelectionList<ModListWidget.ModEntry> {
|
||||
parent.buildModList(this::addEntry, mod->new ModEntry(mod, this.parent));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelected(final @Nullable ModEntry selected) {
|
||||
parent.setSelected(selected);
|
||||
super.setSelected(selected);
|
||||
}
|
||||
|
||||
public class ModEntry extends ObjectSelectionList.Entry<ModEntry> {
|
||||
private final IModInfo modInfo;
|
||||
private final ModListScreen parent;
|
||||
@@ -92,14 +100,15 @@ public class ModListWidget extends ObjectSelectionList<ModListWidget.ModEntry> {
|
||||
guiGraphics.pose().pushMatrix();
|
||||
guiGraphics.blit(RenderPipelines.GUI_TEXTURED, VERSION_CHECK_ICONS, getX() + width - 12 - barOffset, top + entryHeight / 4, vercheck.status().getSheetOffset() * 8, (vercheck.status().isAnimated() && ((System.currentTimeMillis() / 800 & 1)) == 1) ? 8 : 0, 8, 8, 64, 16);
|
||||
guiGraphics.pose().popMatrix();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseClicked(MouseButtonEvent info, boolean recent) {
|
||||
parent.setSelected(this);
|
||||
ModListWidget.this.setSelected(this);
|
||||
if (this.parent.getSelected() == this)
|
||||
ModListWidget.this.setSelected(null);
|
||||
else
|
||||
ModListWidget.this.setSelected(this);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -75,6 +75,7 @@ public class ForgeConfig {
|
||||
}
|
||||
|
||||
public final ForgeConfigSpec.EnumValue<MigrationHelperMode> migrationHelperMode;
|
||||
public final ForgeConfigSpec.LongValue channelListLimit;
|
||||
|
||||
Common(ForgeConfigSpec.Builder builder) {
|
||||
builder.comment("General configuration settings")
|
||||
@@ -85,6 +86,17 @@ public class ForgeConfig {
|
||||
.translation("forge.configgui.migrationHelperMode")
|
||||
.defineEnum("logLegacyTagWarnings", MigrationHelperMode.OFF);
|
||||
|
||||
channelListLimit = builder
|
||||
.comment(
|
||||
"Maximum size to use to store the channel list for a network connection.",
|
||||
"The other end of the connection may send as many channels as it wishes.",
|
||||
"Some malicious actors may use this to use up all your memory causing OutOfMemoryException",
|
||||
"A sane default is 1KB (1024), but may need to be expanded for larger modpacks.",
|
||||
"Forge will auto-adjust this if mods register their channels using our channel manager."
|
||||
)
|
||||
.translation("forge.configgui.channellistlimit")
|
||||
.defineInRange("channelListLimit", 1024, 1, Long.MAX_VALUE);
|
||||
|
||||
builder.pop();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Matcher;
|
||||
@@ -54,6 +55,7 @@ import net.minecraft.core.particles.ParticleTypes;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.network.Connection;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.PacketListener;
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.VarInt;
|
||||
import net.minecraft.network.chat.ClickEvent;
|
||||
@@ -107,6 +109,7 @@ import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.network.protocol.PacketFlow;
|
||||
import net.minecraft.network.protocol.PacketType;
|
||||
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||
import net.minecraft.network.protocol.common.custom.DiscardedPayload;
|
||||
import net.minecraft.network.syncher.EntityDataSerializer;
|
||||
@@ -1305,4 +1308,32 @@ public final class ForgeHooks {
|
||||
|
||||
return Optional.of(ret);
|
||||
}
|
||||
|
||||
// This is similar to PacketUtils.ensureRunningOnSameThread, we can't use the normal LogicalSidedProvider.WORKQUEUE because it is processed after packets.
|
||||
// So any vanilla packets that are received after this packet, will be processed before our enqueued packet.
|
||||
public static <MSG> void enqueuePacket(BiConsumer<MSG, CustomPayloadEvent.Context> handler, MSG packet, CustomPayloadEvent.Context context) {
|
||||
var processor = LogicalSidedProvider.PACKETS.get(context.isClientSide());
|
||||
if (!processor.isSameThread())
|
||||
processor.scheduleIfPossible(context.getConnection().getPacketListener(), new DummyPacket<>(handler, packet, context));
|
||||
else
|
||||
handler.accept(packet, context);
|
||||
}
|
||||
|
||||
private static final record DummyPacket<MSG>(
|
||||
BiConsumer<MSG, CustomPayloadEvent.Context> handler,
|
||||
MSG packet,
|
||||
CustomPayloadEvent.Context context
|
||||
) implements Packet<PacketListener> {
|
||||
private static final PacketType<DummyPacket<?>> TYPE = new PacketType<>(PacketFlow.CLIENTBOUND, Identifier.fromNamespaceAndPath("forge", "dummy_for_schedualing"));
|
||||
|
||||
@Override
|
||||
public PacketType<DummyPacket<?>> type() {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(PacketListener listener) {
|
||||
handler.accept(packet, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
package net.minecraftforge.common.util;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.network.PacketProcessor;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.TickTask;
|
||||
import net.minecraft.util.thread.BlockableEventLoop;
|
||||
@@ -21,6 +22,7 @@ import org.jetbrains.annotations.ApiStatus;
|
||||
public class LogicalSidedProvider<T> {
|
||||
public static final LogicalSidedProvider<BlockableEventLoop<? super TickTask>> WORKQUEUE = new LogicalSidedProvider<>(Supplier::get, Supplier::get);
|
||||
public static final LogicalSidedProvider<Optional<Level>> CLIENTWORLD = new LogicalSidedProvider<>((c)-> Optional.of(c.get().level), (s)->Optional.empty());
|
||||
public static final LogicalSidedProvider<PacketProcessor> PACKETS = new LogicalSidedProvider<>(client -> client.get().packetProcessor(), server -> server.get().packetProcessor());
|
||||
|
||||
public T get(LogicalSide side) {
|
||||
return side == LogicalSide.CLIENT ? clientSide.apply(client) : serverSide.apply(server);
|
||||
|
||||
@@ -5,17 +5,16 @@
|
||||
|
||||
package net.minecraftforge.gametest;
|
||||
|
||||
import static java.lang.annotation.ElementType.METHOD;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import net.minecraft.gametest.framework.GameTestEnvironments;
|
||||
import net.minecraft.world.level.block.Rotation;
|
||||
|
||||
@Retention(RUNTIME)
|
||||
@Target(METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface GameTest {
|
||||
String name() default "";
|
||||
|
||||
|
||||
@@ -9,9 +9,11 @@ import com.mojang.logging.LogUtils;
|
||||
import net.minecraft.IdentifierException;
|
||||
import net.minecraft.network.Connection;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||
import net.minecraft.resources.Identifier;
|
||||
import net.minecraftforge.common.ForgeConfig;
|
||||
import net.minecraftforge.event.ForgeEventFactory;
|
||||
import net.minecraftforge.event.network.ChannelRegistrationChangeEvent;
|
||||
import net.minecraftforge.event.network.CustomPayloadEvent;
|
||||
@@ -26,6 +28,7 @@ import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ApiStatus.Internal // TODO: Decide what I want to have public, Right now it should just be add/remove channels.
|
||||
public class ChannelListManager {
|
||||
@@ -70,7 +73,7 @@ public class ChannelListManager {
|
||||
}
|
||||
|
||||
private static void register(Register payload, CustomPayloadEvent.Context ctx) {
|
||||
updateFrom(ctx, payload.channels(), ChannelRegistrationChangeEvent.Type.REGISTER);
|
||||
updateFrom(ctx, payload.channels(), ChannelRegistrationChangeEvent.Type.REGISTER, ctx.isClientSide());
|
||||
ctx.setPacketHandled(true);
|
||||
// Send the client's channels to the server whenever asked. If we're in the login state then our login wrapper will unwrap for us.
|
||||
if (ctx.isClientSide())
|
||||
@@ -78,7 +81,7 @@ public class ChannelListManager {
|
||||
}
|
||||
|
||||
private static void unregister(Unregister payload, CustomPayloadEvent.Context ctx) {
|
||||
updateFrom(ctx, payload.channels(), ChannelRegistrationChangeEvent.Type.UNREGISTER);
|
||||
updateFrom(ctx, payload.channels(), ChannelRegistrationChangeEvent.Type.UNREGISTER, ctx.isClientSide());
|
||||
ctx.setPacketHandled(true);
|
||||
}
|
||||
|
||||
@@ -145,7 +148,7 @@ public class ChannelListManager {
|
||||
return Collections.unmodifiableList(channels);
|
||||
}
|
||||
|
||||
private static void updateFrom(CustomPayloadEvent.Context source, List<String> channels, final ChannelRegistrationChangeEvent.Type changeType) {
|
||||
private static void updateFrom(CustomPayloadEvent.Context source, List<String> channels, final ChannelRegistrationChangeEvent.Type changeType, boolean isClient) {
|
||||
var changed = new HashSet<Identifier>();
|
||||
for (var channel : channels) {
|
||||
// It also says nothing about the format of channels so ignore bad channels.
|
||||
@@ -162,11 +165,20 @@ public class ChannelListManager {
|
||||
|
||||
ForgeEventFactory.onChannelRegistrationChange(source.getConnection(), changeType, changed);
|
||||
|
||||
// It is a known issue, that I don't really care about, that we don't get the true in-memory size of each channel due to character encoding.
|
||||
// A sane limit of 1KB will be enough to catch any malicious actors even with non-8-bit strings.
|
||||
var list = NetworkContext.get(source.getConnection());
|
||||
for (var channel : changed) {
|
||||
boolean fire = changeType == ChannelRegistrationChangeEvent.Type.UNREGISTER
|
||||
? list.remoteChannels.remove(channel)
|
||||
: list.remoteChannels.add(channel);
|
||||
boolean fire = false;
|
||||
if (changeType == ChannelRegistrationChangeEvent.Type.UNREGISTER) {
|
||||
fire = list.remoteChannels.remove(channel);
|
||||
if (fire)
|
||||
list.remoteChannelsSize -= channel.toString().length();
|
||||
} else {
|
||||
fire = list.remoteChannels.add(channel);
|
||||
if (fire)
|
||||
list.remoteChannelsSize += channel.toString().length();
|
||||
}
|
||||
|
||||
if (fire) {
|
||||
var target = NetworkRegistry.findTarget(channel);
|
||||
@@ -174,5 +186,21 @@ public class ChannelListManager {
|
||||
target.registrationChange(channel, changeType == ChannelRegistrationChangeEvent.Type.REGISTER);
|
||||
}
|
||||
}
|
||||
|
||||
if (list.remoteChannelsSize >= NetworkRegistry.getChannelMemorySize() && list.remoteChannelsSize >= ForgeConfig.COMMON.channelListLimit.get()) {
|
||||
LOGGER.warn("Received too many network channels, {} channels using {} bytes, Limit is currently {}, to change edit the Forge {} in config\forge_common.toml",
|
||||
list.remoteChannels.size(), list.remoteChannelsSize, ForgeConfig.COMMON.channelListLimit.get(), ForgeConfig.COMMON.channelListLimit.getPath().stream().collect(Collectors.joining("."))
|
||||
);
|
||||
|
||||
if (isClient) {
|
||||
// We don't want to allow the server to screw with us, so just remove the added channels
|
||||
for (var channel : changed) {
|
||||
if (list.remoteChannels.remove(channel))
|
||||
list.remoteChannelsSize -= channel.toString().length();
|
||||
}
|
||||
} else {
|
||||
source.getSender().connection.disconnect(Component.translatable("forge.server.channellistlimit.kick"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,10 @@ import net.minecraftforge.network.packets.ModVersions;
|
||||
|
||||
public class NetworkContext {
|
||||
private static final String MARKER = "FORGE";
|
||||
/**
|
||||
* Versions 1, 2 and 3 were used by older Forge versions,
|
||||
* so if this ever needs to be bumped, start from 4.
|
||||
*/
|
||||
public static final int NET_VERSION = 0;
|
||||
private static final AttributeKey<NetworkContext> CHANNEL_KEY = AttributeKey.valueOf("forge:netcontext");
|
||||
|
||||
@@ -111,6 +115,8 @@ public class NetworkContext {
|
||||
|
||||
@ApiStatus.Internal
|
||||
Set<Identifier> remoteChannels = new HashSet<>();
|
||||
@ApiStatus.Internal
|
||||
long remoteChannelsSize = 0;
|
||||
private Set<Identifier> remoteChannelsView = Collections.unmodifiableSet(this.remoteChannels);
|
||||
|
||||
@ApiStatus.Internal
|
||||
|
||||
@@ -43,6 +43,7 @@ public class NetworkRegistry {
|
||||
|
||||
private static Map<Identifier, NetworkInstance> instances = Collections.synchronizedMap(new HashMap<>());
|
||||
private static Map<Identifier, NetworkInstance> byName = Collections.synchronizedMap(new HashMap<>());
|
||||
private static long memorySize = 0;
|
||||
|
||||
public static boolean acceptsVanillaClientConnections() {
|
||||
return listRejectedVanillaMods(n -> n.clientAcceptedVersions).isEmpty() && DataPackRegistriesHooks.getSyncedCustomRegistries().isEmpty();
|
||||
@@ -199,9 +200,14 @@ public class NetworkRegistry {
|
||||
if (NetworkRegistry.byName.containsKey(name))
|
||||
error("Payload name " + name + " already registered.");
|
||||
|
||||
memorySize += name.toString().length(); // This can fail if people don't use our api. So this is a best effort.
|
||||
byName.put(name, instance);
|
||||
}
|
||||
|
||||
static long getChannelMemorySize() {
|
||||
return memorySize;
|
||||
}
|
||||
|
||||
static void register(NetworkInstance instance) {
|
||||
checkLock(instance);
|
||||
|
||||
|
||||
@@ -225,7 +225,7 @@ public record ServerStatusPing(
|
||||
var modVersion = isIgnoreServerOnly ? IExtensionPoint.DisplayTest.IGNORESERVERONLY : buf.readUtf();
|
||||
for (var i1 = 0; i1 < channelSize; i1++) {
|
||||
var channelName = buf.readUtf();
|
||||
var channelVersion = buf.readVarInt();
|
||||
var channelVersion = readChannelVersion(fmlNetworkVersion, buf);
|
||||
var requiredOnClient = buf.readBoolean();
|
||||
final Identifier id = Identifier.fromNamespaceAndPath(modId, channelName);
|
||||
channels.put(id, new ChannelData(id, channelVersion, requiredOnClient));
|
||||
@@ -237,7 +237,7 @@ public record ServerStatusPing(
|
||||
var nonModChannelCount = buf.readVarInt();
|
||||
for (var i = 0; i < nonModChannelCount; i++) {
|
||||
var channelName = buf.readIdentifier();
|
||||
var channelVersion = buf.readVarInt();
|
||||
var channelVersion = readChannelVersion(fmlNetworkVersion, buf);
|
||||
var requiredOnClient = buf.readBoolean();
|
||||
channels.put(channelName, new ChannelData(channelName, channelVersion, requiredOnClient));
|
||||
}
|
||||
@@ -248,6 +248,22 @@ public record ServerStatusPing(
|
||||
return new ServerStatusPing(channels, mods, fmlNetworkVersion, truncated);
|
||||
}
|
||||
|
||||
private static int readChannelVersion(int fmlNetworkVersion, FriendlyByteBuf buf) {
|
||||
// For versions 1-3, before re-writing the network code, the version was any arbitrary String the mod provided,
|
||||
// and a stupid list of emojies for "missing" or "don't care". So do a best effort to convert the string to a int
|
||||
// but if that fails return -1
|
||||
if (fmlNetworkVersion >= 1 && fmlNetworkVersion <= 3) {
|
||||
var versionStr = buf.readUtf(); // This can throw a DecoderException, if it reads a invalid string, this propagating is intentional
|
||||
try {
|
||||
return Integer.parseInt(versionStr);
|
||||
} catch (NumberFormatException e) {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
return buf.readVarInt();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode given ByteBuf to a String. This is optimized for UTF-16 Code-Point count.
|
||||
* Supports at most 2^30 bytes in length
|
||||
|
||||
@@ -28,7 +28,7 @@ public interface SimpleFlow<BUF extends FriendlyByteBuf, BASE> extends SimplePro
|
||||
*/
|
||||
default <MSG extends BASE> SimpleFlow<BUF, BASE> addMain(Class<MSG> type, StreamCodec<BUF, MSG> codec, BiConsumer<MSG, CustomPayloadEvent.Context> handler) {
|
||||
return add(type, codec, (msg, ctx) -> {
|
||||
ctx.enqueueWork(() -> handler.accept(msg, ctx));
|
||||
net.minecraftforge.common.ForgeHooks.enqueuePacket(handler, msg, ctx);
|
||||
ctx.setPacketHandled(true);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -350,7 +350,7 @@ public class GameData {
|
||||
if (forgeRegistry != null)
|
||||
forgeRegistry.freeze();
|
||||
LOGGER.debug(REGISTRIES, "Applying holder lookups: {}", registryKey.identifier());
|
||||
ObjectHolderRegistry.applyObjectHolders(registryKey.identifier()::equals);
|
||||
ObjectHolderRegistry.applyObjectHolders(registryKey.identifier());
|
||||
LOGGER.debug(REGISTRIES, "Holder lookups applied: {}", registryKey.identifier());
|
||||
} catch (Throwable t) {
|
||||
aggregate.addSuppressed(t);
|
||||
|
||||
@@ -5,10 +5,12 @@
|
||||
|
||||
package net.minecraftforge.registries;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import net.minecraft.resources.Identifier;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
@@ -16,32 +18,34 @@ import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
/**
|
||||
* Internal registry for tracking {@link ObjectHolder} references
|
||||
* Internal registry for tracking {@link RegistryObject} references
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
class ObjectHolderRegistry {
|
||||
/**
|
||||
* Exposed to allow modders to register their own notification handlers.
|
||||
* This runnable will be called after a registry snapshot has been injected and finalized.
|
||||
* The internal list is backed by a HashSet so it is HIGHLY recommended you implement a proper equals
|
||||
* and hashCode function to de-duplicate callers here.
|
||||
* The default @ObjectHolder implementation uses the hashCode/equals for the field the annotation is on.
|
||||
* Registers a handler that is run after the named registry has been injected and finalized.
|
||||
* Handlers are kept in a HashSet per registry, so the same instance is only ever registered once.
|
||||
*/
|
||||
static synchronized void addHandler(Consumer<Predicate<Identifier>> ref) {
|
||||
objectHolders.add(ref);
|
||||
static synchronized void addHandler(Identifier registry, Runnable ref) {
|
||||
namedHandlers.computeIfAbsent(registry, id -> new HashSet<>()).add(ref);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removed the specified handler from the notification list.
|
||||
*
|
||||
* The internal list is backed by a hash set, and so proper hashCode and equals operations are required for success.
|
||||
*
|
||||
* The default @ObjectHolder implementation uses the hashCode/equals for the field the annotation is on.
|
||||
* Removes a handler registered with {@link #addHandler(Identifier, Runnable)}.
|
||||
*
|
||||
* @return true if handler was matched and removed.
|
||||
*/
|
||||
static synchronized boolean removeHandler(Consumer<Predicate<Identifier>> ref) {
|
||||
return objectHolders.remove(ref);
|
||||
static synchronized boolean removeHandler(Identifier registry, Runnable ref) {
|
||||
var handlers = namedHandlers.get(registry);
|
||||
return handlers != null && handlers.remove(ref);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a check that is run once, before the handlers of the next holder pass.
|
||||
* Used to report holders that point at a registry that does not exist.
|
||||
*/
|
||||
static synchronized void addValidator(Runnable ref) {
|
||||
validators.add(ref);
|
||||
}
|
||||
|
||||
//==============================================================
|
||||
@@ -49,12 +53,19 @@ class ObjectHolderRegistry {
|
||||
//==============================================================
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
private static final Set<Consumer<Predicate<Identifier>>> objectHolders = new HashSet<>();
|
||||
// Order is not guaranteed, the old implementation kept every handler in one HashSet.
|
||||
private static final Map<Identifier, Set<Runnable>> namedHandlers = new HashMap<>();
|
||||
private static final List<Runnable> validators = new ArrayList<>();
|
||||
|
||||
/** Runs the handlers of every registry. */
|
||||
static void applyObjectHolders() {
|
||||
try {
|
||||
LOGGER.debug(ForgeRegistry.REGISTRIES, "Applying holder lookups");
|
||||
applyObjectHolders(key -> true);
|
||||
var exceptions = new ArrayList<Exception>();
|
||||
validate(exceptions);
|
||||
for (var handlers : namedHandlers.values())
|
||||
apply(handlers, exceptions);
|
||||
throwIfNeeded(exceptions);
|
||||
LOGGER.debug(ForgeRegistry.REGISTRIES, "Holder lookups applied");
|
||||
} catch (RuntimeException e) {
|
||||
// It is more important that the calling contexts continue without exception to prevent further cascading errors
|
||||
@@ -62,17 +73,52 @@ class ObjectHolderRegistry {
|
||||
}
|
||||
}
|
||||
|
||||
static void applyObjectHolders(Predicate<Identifier> filter) {
|
||||
RuntimeException aggregate = new RuntimeException("Failed to apply some object holders, see suppressed exceptions for details");
|
||||
for (Consumer<Predicate<Identifier>> objectHolder : objectHolders) {
|
||||
try {
|
||||
objectHolder.accept(filter);
|
||||
} catch (Exception e) {
|
||||
aggregate.addSuppressed(e);
|
||||
}
|
||||
/** Runs the handlers of one registry. */
|
||||
static void applyObjectHolders(Identifier registry) {
|
||||
var exceptions = new ArrayList<Exception>();
|
||||
validate(exceptions);
|
||||
var handlers = namedHandlers.get(registry);
|
||||
if (handlers != null)
|
||||
apply(handlers, exceptions);
|
||||
throwIfNeeded(exceptions);
|
||||
}
|
||||
|
||||
// Each validator runs once, at the first holder pass after it was registered.
|
||||
private static void validate(List<Exception> exceptions) {
|
||||
List<Runnable> pending;
|
||||
synchronized (ObjectHolderRegistry.class) {
|
||||
if (validators.isEmpty())
|
||||
return;
|
||||
pending = new ArrayList<>(validators);
|
||||
validators.clear();
|
||||
}
|
||||
|
||||
if (aggregate.getSuppressed().length > 0)
|
||||
throw aggregate;
|
||||
for (var validator : pending) {
|
||||
try {
|
||||
validator.run();
|
||||
} catch (Exception e) {
|
||||
exceptions.add(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void apply(Set<Runnable> handlers, List<Exception> exceptions) {
|
||||
for (var handler : handlers) {
|
||||
try {
|
||||
handler.run();
|
||||
} catch (Exception e) {
|
||||
exceptions.add(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void throwIfNeeded(List<Exception> exceptions) {
|
||||
if (exceptions.isEmpty())
|
||||
return;
|
||||
|
||||
var aggregate = new RuntimeException("Failed to apply some object holders, see suppressed exceptions for details");
|
||||
for (var e : exceptions)
|
||||
aggregate.addSuppressed(e);
|
||||
throw aggregate;
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,6 @@ public final class RegistryObject<T> implements Supplier<T> {
|
||||
private final Identifier name;
|
||||
@Nullable
|
||||
private final ResourceKey<T> key;
|
||||
private final boolean optionalRegistry;
|
||||
@Nullable
|
||||
private T value;
|
||||
@Nullable
|
||||
@@ -129,7 +128,6 @@ public final class RegistryObject<T> implements Supplier<T> {
|
||||
private RegistryObject() {
|
||||
this.name = null;
|
||||
this.key = null;
|
||||
this.optionalRegistry = false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -138,40 +136,21 @@ public final class RegistryObject<T> implements Supplier<T> {
|
||||
throw new IllegalArgumentException("Invalid registry argument, must not be null");
|
||||
this.name = name;
|
||||
this.key = (ResourceKey<T>) ResourceKey.create(registry.getRegistryKey(), name);
|
||||
this.optionalRegistry = false;
|
||||
ObjectHolderRegistry.addHandler(pred -> {
|
||||
if (pred.test(registry.getRegistryName()))
|
||||
this.updateReference((IForgeRegistry<? extends T>) registry);
|
||||
});
|
||||
ObjectHolderRegistry.addHandler(registry.getRegistryName(), () -> this.updateReference((IForgeRegistry<? extends T>) registry));
|
||||
this.updateReference((IForgeRegistry<? extends T>) registry);
|
||||
}
|
||||
|
||||
private RegistryObject(final Identifier name, final Identifier registryName, final String modid, boolean optionalRegistry) {
|
||||
this.name = name;
|
||||
this.key = ResourceKey.create(ResourceKey.createRegistryKey(registryName), name);
|
||||
this.optionalRegistry = optionalRegistry;
|
||||
final Throwable callerStack = new Throwable("Calling Site from mod: " + modid);
|
||||
ObjectHolderRegistry.addHandler(new Consumer<>() {
|
||||
private boolean registryExists = false;
|
||||
private boolean invalidRegistry = false;
|
||||
|
||||
@Override
|
||||
public void accept(Predicate<Identifier> pred) {
|
||||
if (invalidRegistry)
|
||||
return;
|
||||
|
||||
if (!RegistryObject.this.optionalRegistry && !registryExists) {
|
||||
if (!registryExists(registryName)) {
|
||||
invalidRegistry = true;
|
||||
throw new IllegalStateException("Unable to find registry with key " + registryName + " for mod \"" + modid + "\". Check the 'caused by' to see further stack.", callerStack);
|
||||
}
|
||||
registryExists = true;
|
||||
}
|
||||
|
||||
if (pred.test(registryName))
|
||||
RegistryObject.this.updateReference(registryName);
|
||||
}
|
||||
});
|
||||
if (!optionalRegistry) {
|
||||
final Throwable callerStack = new Throwable("Calling Site from mod: " + modid);
|
||||
ObjectHolderRegistry.addValidator(() -> {
|
||||
if (!registryExists(registryName))
|
||||
throw new IllegalStateException("Unable to find registry with key " + registryName + " for mod \"" + modid + "\". Check the 'caused by' to see further stack.", callerStack);
|
||||
});
|
||||
}
|
||||
ObjectHolderRegistry.addHandler(registryName, () -> this.updateReference(registryName));
|
||||
this.updateReference(registryName);
|
||||
}
|
||||
|
||||
@@ -181,7 +160,6 @@ public final class RegistryObject<T> implements Supplier<T> {
|
||||
@SuppressWarnings("unchecked")
|
||||
var typed = (ResourceKey<Registry<T>>)registry;
|
||||
this.key = ResourceKey.create(typed, name);
|
||||
this.optionalRegistry = true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -481,8 +459,8 @@ public final class RegistryObject<T> implements Supplier<T> {
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (obj instanceof RegistryObject o) {
|
||||
return Objects.equals(o.name, name);
|
||||
if (obj instanceof RegistryObject<?> o) {
|
||||
return o.key == key && Objects.equals(o.name, name);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -150,6 +150,10 @@
|
||||
"forge.configgui.removeErroringBlockEntities": "Remove Erroring Block Entities",
|
||||
"forge.configgui.fullBoundingBoxLadders.tooltip": "Set this to true to check the entire entity's collision bounding box for ladders instead of just the block they are in. Causes noticeable differences in mechanics so default is vanilla behavior. Default: false.",
|
||||
"forge.configgui.fullBoundingBoxLadders": "Full Bounding Box Ladders",
|
||||
"forge.configgui.permissionHandler.tooltip": "The permission handler used by the server. Defaults to forge:default_handler if no such handler with that name is registered.",
|
||||
"forge.configgui.permissionHandler": "Permission Handler",
|
||||
"forge.configgui.advertiseDedicatedServerToLan.tooltip": "Set this to true to enable advertising the dedicated server to local LAN clients so that it shows up in the Multiplayer screen automatically.",
|
||||
"forge.configgui.advertiseDedicatedServerToLan": "Advertise Dedicated Server to LAN",
|
||||
"forge.configgui.zombieBaseSummonChance.tooltip": "Base zombie summoning spawn chance. Allows changing the bonus zombie summoning mechanic.",
|
||||
"forge.configgui.zombieBaseSummonChance": "Zombie Summon Chance",
|
||||
"forge.configgui.zombieBabyChance.tooltip": "Chance that a zombie (or subclass) is a baby. Allows changing the zombie spawning mechanic.",
|
||||
@@ -200,6 +204,12 @@
|
||||
"forge.configgui.stabilizeDirectionGetNearest": "Stabilize Direction Get Nearest",
|
||||
"forge.configgui.stabilizeDirectionGetNearest.tooltip": "When enabled, a slightly biased Direction#getNearest calculation will be used to prevent normal fighting on 45 degree angle faces.",
|
||||
|
||||
"forge.configgui.channellistlimit": "Channel List Size Limit",
|
||||
"forge.configgui.channellistlimit.tooltip": "Maximum size to use to store the channel list for a network connection. Some malicious actors may use this to use up all your memory. A sane default is 1KB (1024), but may need to be expanded for larger modpacks.",
|
||||
"forge.configgui.migrationHelperMode": "Migration Helper Mode",
|
||||
"forge.configgui.migrationHelperMode.tooltip": "A config option to help developers find known legacy modded tags that have common convention equivalents when running on integrated server. Defaults to OFF.",
|
||||
"forge.server.channellistlimit.kick": "Received too many networking channels",
|
||||
|
||||
"forge.controlsgui.shift": "SHIFT + %s",
|
||||
"forge.controlsgui.control": "CTRL + %s",
|
||||
"forge.controlsgui.control.mac": "CMD + %s",
|
||||
|
||||
@@ -1,65 +1,4 @@
|
||||
com/mojang/blaze3d/vertex/VertexConsumer.putBulkData(Lcom/mojang/blaze3d/vertex/PoseStack$Pose;Lnet/minecraft/client/renderer/block/model/BakedQuad;[FFFFF[IIZ)V=|p_85996_,p_85997_,p_85998_,p_85999_,p_86000_,p_86001_,alpha,p_86002_,p_86003_,p_86004_
|
||||
net/minecraft/client/Options.processOptionsForge(Lnet/minecraft/client/Options$FieldAccess;)V=|p_168428_
|
||||
net/minecraft/client/gui/Gui.renderSelectedItemName(Lnet/minecraft/client/gui/GuiGraphics;I)V=|p_283501_,yShift
|
||||
net/minecraft/client/gui/screens/MenuScreens.getScreenFactory(Lnet/minecraft/world/inventory/MenuType;Lnet/minecraft/client/Minecraft;ILnet/minecraft/network/chat/Component;)Ljava/util/Optional;=|p_96202_,p_96203_,p_96204_,p_96205_
|
||||
net/minecraft/client/renderer/ScreenEffectRenderer.getOverlayBlock(Lnet/minecraft/world/entity/player/Player;)Lorg/apache/commons/lang3/tuple/Pair;=|p_110717_
|
||||
net/minecraft/client/renderer/ScreenEffectRenderer.renderFluid(Lnet/minecraft/client/Minecraft;Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/resources/ResourceLocation;)V=|p_110726_,p_110727_,texture
|
||||
net/minecraft/client/renderer/block/BlockModelShaper.getTexture(Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;)Lnet/minecraft/client/renderer/texture/TextureAtlasSprite;=|p_110883_,level,pos
|
||||
net/minecraft/client/renderer/block/BlockRenderDispatcher.renderBatched(Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/BlockAndTintGetter;Lcom/mojang/blaze3d/vertex/PoseStack;Lcom/mojang/blaze3d/vertex/VertexConsumer;ZLnet/minecraft/util/RandomSource;Lnet/minecraftforge/client/model/data/ModelData;Lnet/minecraft/client/renderer/RenderType;)V=|p_234356_,p_234357_,p_234358_,p_234359_,p_234360_,p_234361_,p_234362_,modelData,renderType
|
||||
net/minecraft/client/renderer/block/BlockRenderDispatcher.renderBreakingTexture(Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/BlockAndTintGetter;Lcom/mojang/blaze3d/vertex/PoseStack;Lcom/mojang/blaze3d/vertex/VertexConsumer;Lnet/minecraftforge/client/model/data/ModelData;)V=|p_110919_,p_110920_,p_110921_,p_110922_,p_110923_,modelData
|
||||
net/minecraft/client/renderer/block/BlockRenderDispatcher.renderSingleBlock(Lnet/minecraft/world/level/block/state/BlockState;Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;IILnet/minecraftforge/client/model/data/ModelData;Lnet/minecraft/client/renderer/RenderType;)V=|p_110913_,p_110914_,p_110915_,p_110916_,p_110917_,modelData,renderType
|
||||
net/minecraft/client/renderer/block/ModelBlockRenderer.renderModel(Lcom/mojang/blaze3d/vertex/PoseStack$Pose;Lcom/mojang/blaze3d/vertex/VertexConsumer;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/client/resources/model/BakedModel;FFFIILnet/minecraftforge/client/model/data/ModelData;Lnet/minecraft/client/renderer/RenderType;)V=|p_111068_,p_111069_,p_111070_,p_111071_,p_111072_,p_111073_,p_111074_,p_111075_,p_111076_,modelData,renderType
|
||||
net/minecraft/client/renderer/block/ModelBlockRenderer.tesselateBlock(Lnet/minecraft/world/level/BlockAndTintGetter;Lnet/minecraft/client/resources/model/BakedModel;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/core/BlockPos;Lcom/mojang/blaze3d/vertex/PoseStack;Lcom/mojang/blaze3d/vertex/VertexConsumer;ZLnet/minecraft/util/RandomSource;JILnet/minecraftforge/client/model/data/ModelData;Lnet/minecraft/client/renderer/RenderType;)V=|p_234380_,p_234381_,p_234382_,p_234383_,p_234384_,p_234385_,p_234386_,p_234387_,p_234388_,p_234389_,modelData,renderType
|
||||
net/minecraft/client/renderer/block/ModelBlockRenderer.tesselateWithAO(Lnet/minecraft/world/level/BlockAndTintGetter;Lnet/minecraft/client/resources/model/BakedModel;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/core/BlockPos;Lcom/mojang/blaze3d/vertex/PoseStack;Lcom/mojang/blaze3d/vertex/VertexConsumer;ZLnet/minecraft/util/RandomSource;JILnet/minecraftforge/client/model/data/ModelData;Lnet/minecraft/client/renderer/RenderType;)V=|p_111079_,p_111080_,p_111081_,p_111082_,p_111083_,p_111084_,p_111085_,p_111086_,p_111087_,p_111088_,modelData,renderType
|
||||
net/minecraft/client/renderer/block/ModelBlockRenderer.tesselateWithoutAO(Lnet/minecraft/world/level/BlockAndTintGetter;Lnet/minecraft/client/resources/model/BakedModel;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/core/BlockPos;Lcom/mojang/blaze3d/vertex/PoseStack;Lcom/mojang/blaze3d/vertex/VertexConsumer;ZLnet/minecraft/util/RandomSource;JILnet/minecraftforge/client/model/data/ModelData;Lnet/minecraft/client/renderer/RenderType;)V=|p_111091_,p_111092_,p_111093_,p_111094_,p_111095_,p_111096_,p_111097_,p_111098_,p_111099_,p_111100_,modelData,renderType
|
||||
net/minecraft/client/renderer/block/model/BlockElementFace.<init>(Lnet/minecraft/core/Direction;ILjava/lang/String;Lnet/minecraft/client/renderer/block/model/BlockFaceUV;Lnet/minecraftforge/client/model/ForgeFaceData;)V=|p_111359_,p_111360_,p_111361_,p_111362_,faceData
|
||||
net/minecraft/client/renderer/block/model/ItemTransform.<init>(Lorg/joml/Vector3f;Lorg/joml/Vector3f;Lorg/joml/Vector3f;Lorg/joml/Vector3f;)V=|p_254427_,p_254496_,p_254022_,rightRotation
|
||||
net/minecraft/client/renderer/block/model/ItemTransforms.<init>(Lnet/minecraft/client/renderer/block/model/ItemTransform;Lnet/minecraft/client/renderer/block/model/ItemTransform;Lnet/minecraft/client/renderer/block/model/ItemTransform;Lnet/minecraft/client/renderer/block/model/ItemTransform;Lnet/minecraft/client/renderer/block/model/ItemTransform;Lnet/minecraft/client/renderer/block/model/ItemTransform;Lnet/minecraft/client/renderer/block/model/ItemTransform;Lnet/minecraft/client/renderer/block/model/ItemTransform;Lcom/google/common/collect/ImmutableMap;)V=|p_111798_,p_111799_,p_111800_,p_111801_,p_111802_,p_111803_,p_111804_,p_111805_,moddedTransforms
|
||||
net/minecraft/client/resources/model/MultiPartBakedModel.getQuads(Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/core/Direction;Lnet/minecraft/util/RandomSource;Lnet/minecraftforge/client/model/data/ModelData;Lnet/minecraft/client/renderer/RenderType;)Ljava/util/List;=|p_235050_,p_235051_,p_235052_,modelData,renderType
|
||||
net/minecraft/client/resources/model/WeightedBakedModel.getQuads(Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/core/Direction;Lnet/minecraft/util/RandomSource;Lnet/minecraftforge/client/model/data/ModelData;Lnet/minecraft/client/renderer/RenderType;)Ljava/util/List;=|p_235058_,p_235059_,p_235060_,modelData,renderType
|
||||
net/minecraft/data/registries/RegistriesDatapackGenerator.<init>(Lnet/minecraft/data/PackOutput;Ljava/util/concurrent/CompletableFuture;Ljava/util/Set;)V=|p_256643_,p_255780_,modIds
|
||||
net/minecraft/data/tags/BannerPatternTagsProvider.<init>(Lnet/minecraft/data/PackOutput;Ljava/util/concurrent/CompletableFuture;Ljava/lang/String;Lnet/minecraftforge/common/data/ExistingFileHelper;)V=|p_256451_,p_256420_,modId,existingFileHelper
|
||||
net/minecraft/data/tags/BiomeTagsProvider.<init>(Lnet/minecraft/data/PackOutput;Ljava/util/concurrent/CompletableFuture;Ljava/lang/String;Lnet/minecraftforge/common/data/ExistingFileHelper;)V=|p_255800_,p_256205_,modId,existingFileHelper
|
||||
net/minecraft/data/tags/CatVariantTagsProvider.<init>(Lnet/minecraft/data/PackOutput;Ljava/util/concurrent/CompletableFuture;Ljava/lang/String;Lnet/minecraftforge/common/data/ExistingFileHelper;)V=|p_256547_,p_256090_,modId,existingFileHelper
|
||||
net/minecraft/data/tags/DamageTypeTagsProvider.<init>(Lnet/minecraft/data/PackOutput;Ljava/util/concurrent/CompletableFuture;Ljava/lang/String;Lnet/minecraftforge/common/data/ExistingFileHelper;)V=|p_270719_,p_270256_,modId,existingFileHelper
|
||||
net/minecraft/data/tags/EntityTypeTagsProvider.<init>(Lnet/minecraft/data/PackOutput;Ljava/util/concurrent/CompletableFuture;Ljava/lang/String;Lnet/minecraftforge/common/data/ExistingFileHelper;)V=|p_256095_,p_256572_,modId,existingFileHelper
|
||||
net/minecraft/data/tags/FlatLevelGeneratorPresetTagsProvider.<init>(Lnet/minecraft/data/PackOutput;Ljava/util/concurrent/CompletableFuture;Ljava/lang/String;Lnet/minecraftforge/common/data/ExistingFileHelper;)V=|p_256604_,p_255962_,modId,existingFileHelper
|
||||
net/minecraft/data/tags/FluidTagsProvider.<init>(Lnet/minecraft/data/PackOutput;Ljava/util/concurrent/CompletableFuture;Ljava/lang/String;Lnet/minecraftforge/common/data/ExistingFileHelper;)V=|p_255941_,p_256600_,modId,existingFileHelper
|
||||
net/minecraft/data/tags/GameEventTagsProvider.<init>(Lnet/minecraft/data/PackOutput;Ljava/util/concurrent/CompletableFuture;Ljava/lang/String;Lnet/minecraftforge/common/data/ExistingFileHelper;)V=|p_256060_,p_255621_,modId,existingFileHelper
|
||||
net/minecraft/data/tags/InstrumentTagsProvider.<init>(Lnet/minecraft/data/PackOutput;Ljava/util/concurrent/CompletableFuture;Ljava/lang/String;Lnet/minecraftforge/common/data/ExistingFileHelper;)V=|p_256418_,p_256038_,modId,existingFileHelper
|
||||
net/minecraft/data/tags/IntrinsicHolderTagsProvider$IntrinsicTagAppender.<init>(Lnet/minecraft/tags/TagBuilder;Ljava/util/function/Function;Ljava/lang/String;)V=|p_256108_,p_256433_,modId
|
||||
net/minecraft/data/tags/IntrinsicHolderTagsProvider.<init>(Lnet/minecraft/data/PackOutput;Lnet/minecraft/resources/ResourceKey;Ljava/util/concurrent/CompletableFuture;Ljava/util/concurrent/CompletableFuture;Ljava/util/function/Function;Ljava/lang/String;Lnet/minecraftforge/common/data/ExistingFileHelper;)V=|p_275304_,p_275709_,p_275227_,p_275311_,p_275566_,modId,existingFileHelper
|
||||
net/minecraft/data/tags/IntrinsicHolderTagsProvider.<init>(Lnet/minecraft/data/PackOutput;Lnet/minecraft/resources/ResourceKey;Ljava/util/concurrent/CompletableFuture;Ljava/util/function/Function;Ljava/lang/String;Lnet/minecraftforge/common/data/ExistingFileHelper;)V=|p_256164_,p_256155_,p_256488_,p_256168_,modId,existingFileHelper
|
||||
net/minecraft/data/tags/ItemTagsProvider.<init>(Lnet/minecraft/data/PackOutput;Ljava/util/concurrent/CompletableFuture;Ljava/util/concurrent/CompletableFuture;Ljava/lang/String;Lnet/minecraftforge/common/data/ExistingFileHelper;)V=|p_275343_,p_275729_,p_275322_,modId,existingFileHelper
|
||||
net/minecraft/data/tags/ItemTagsProvider.<init>(Lnet/minecraft/data/PackOutput;Ljava/util/concurrent/CompletableFuture;Ljava/util/concurrent/CompletableFuture;Ljava/util/concurrent/CompletableFuture;Ljava/lang/String;Lnet/minecraftforge/common/data/ExistingFileHelper;)V=|p_275204_,p_275194_,p_275207_,p_275634_,modId,existingFileHelper
|
||||
net/minecraft/data/tags/PaintingVariantTagsProvider.<init>(Lnet/minecraft/data/PackOutput;Ljava/util/concurrent/CompletableFuture;Ljava/lang/String;Lnet/minecraftforge/common/data/ExistingFileHelper;)V=|p_255750_,p_256184_,modId,existingFileHelper
|
||||
net/minecraft/data/tags/PoiTypeTagsProvider.<init>(Lnet/minecraft/data/PackOutput;Ljava/util/concurrent/CompletableFuture;Ljava/lang/String;Lnet/minecraftforge/common/data/ExistingFileHelper;)V=|p_256012_,p_256617_,modId,existingFileHelper
|
||||
net/minecraft/data/tags/StructureTagsProvider.<init>(Lnet/minecraft/data/PackOutput;Ljava/util/concurrent/CompletableFuture;Ljava/lang/String;Lnet/minecraftforge/common/data/ExistingFileHelper;)V=|p_256522_,p_256661_,modId,existingFileHelper
|
||||
net/minecraft/data/tags/TagsProvider$TagAppender.<init>(Lnet/minecraft/tags/TagBuilder;Ljava/lang/String;)V=|p_236454_,modId
|
||||
net/minecraft/data/tags/TagsProvider.<init>(Lnet/minecraft/data/PackOutput;Lnet/minecraft/resources/ResourceKey;Ljava/util/concurrent/CompletableFuture;Ljava/lang/String;Lnet/minecraftforge/common/data/ExistingFileHelper;)V=|p_256596_,p_255886_,p_256513_,modId,existingFileHelper
|
||||
net/minecraft/data/tags/TagsProvider.<init>(Lnet/minecraft/data/PackOutput;Lnet/minecraft/resources/ResourceKey;Ljava/util/concurrent/CompletableFuture;Ljava/util/concurrent/CompletableFuture;Ljava/lang/String;Lnet/minecraftforge/common/data/ExistingFileHelper;)V=|p_275432_,p_275476_,p_275222_,p_275565_,modId,existingFileHelper
|
||||
net/minecraft/data/tags/WorldPresetTagsProvider.<init>(Lnet/minecraft/data/PackOutput;Ljava/util/concurrent/CompletableFuture;Ljava/lang/String;Lnet/minecraftforge/common/data/ExistingFileHelper;)V=|p_255701_,p_255974_,modId,existingFileHelper
|
||||
net/minecraft/gametest/framework/GameTestRegistry.register(Ljava/lang/reflect/Method;Ljava/util/Set;)V=|p_177504_,allowedNamespaces
|
||||
net/minecraft/server/level/DistanceManager.addRegionTicket(Lnet/minecraft/server/level/TicketType;Lnet/minecraft/world/level/ChunkPos;ILjava/lang/Object;Z)V=|p_140841_,p_140842_,p_140843_,p_140844_,forceTicks
|
||||
net/minecraft/server/level/DistanceManager.removeRegionTicket(Lnet/minecraft/server/level/TicketType;Lnet/minecraft/world/level/ChunkPos;ILjava/lang/Object;Z)V=|p_140850_,p_140851_,p_140852_,p_140853_,forceTicks
|
||||
net/minecraft/server/level/ServerChunkCache.addRegionTicket(Lnet/minecraft/server/level/TicketType;Lnet/minecraft/world/level/ChunkPos;ILjava/lang/Object;Z)V=|p_8388_,p_8389_,p_8390_,p_8391_,forceTicks
|
||||
net/minecraft/server/level/ServerChunkCache.removeRegionTicket(Lnet/minecraft/server/level/TicketType;Lnet/minecraft/world/level/ChunkPos;ILjava/lang/Object;Z)V=|p_8439_,p_8440_,p_8441_,p_8442_,forceTicks
|
||||
net/minecraft/server/level/ServerPlayerGameMode.removeBlock(Lnet/minecraft/core/BlockPos;Z)Z=|p_180235_1_,canHarvest
|
||||
net/minecraft/server/level/Ticket.<init>(Lnet/minecraft/server/level/TicketType;ILjava/lang/Object;Z)V=|p_9425_,p_9426_,p_9427_,forceTicks
|
||||
net/minecraft/world/entity/Entity.playCombinationStepSounds(Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/core/BlockPos;Lnet/minecraft/core/BlockPos;)V=|p_277472_,p_277630_,primaryPos,secondaryPos
|
||||
net/minecraft/world/item/BoneMealItem.applyBonemeal(Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/entity/player/Player;)Z=|p_40628_,p_40629_,p_40630_,player
|
||||
net/minecraft/world/item/BucketItem.emptyContents(Lnet/minecraft/world/entity/player/Player;Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/phys/BlockHitResult;Lnet/minecraft/world/item/ItemStack;)Z=|p_150716_,p_150717_,p_150718_,p_150719_,container
|
||||
net/minecraft/world/item/ItemStack.onItemUse(Lnet/minecraft/world/item/context/UseOnContext;Ljava/util/function/Function;)Lnet/minecraft/world/InteractionResult;=|p_41662_,callback
|
||||
net/minecraft/world/item/ItemStack.onItemUseFirst(Lnet/minecraft/world/item/context/UseOnContext;)Lnet/minecraft/world/InteractionResult;=|p_41662_
|
||||
net/minecraft/world/level/Level.markAndNotifyBlock(Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/chunk/LevelChunk;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/block/state/BlockState;II)V=|p_46605_,levelchunk,blockstate,p_46606_,p_46607_,p_46608_
|
||||
net/minecraft/world/level/LevelSettings.<init>(Ljava/lang/String;Lnet/minecraft/world/level/GameType;ZLnet/minecraft/world/Difficulty;ZLnet/minecraft/world/level/GameRules;Lnet/minecraft/world/level/WorldDataConfiguration;Lcom/mojang/serialization/Lifecycle;)V=|p_250485_,p_250207_,p_251631_,p_252122_,p_248961_,p_248536_,p_249797_,lifecycle
|
||||
net/minecraft/world/level/block/Block.dropResources(Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/entity/BlockEntity;Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/item/ItemStack;Z)V=|p_49882_,p_49883_,p_49884_,p_49885_,p_49886_,p_49887_,dropXp
|
||||
net/minecraft/world/level/block/ConcretePowderBlock.shouldSolidify(Lnet/minecraft/world/level/BlockGetter;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/material/FluidState;)Z=|p_52081_,p_52082_,p_52083_,fluidState
|
||||
net/minecraft/world/level/block/ConcretePowderBlock.touchesLiquid(Lnet/minecraft/world/level/BlockGetter;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;)Z=|p_52065_,p_52066_,state
|
||||
net/minecraft/world/level/block/FlowerPotBlock.<init>(Ljava/util/function/Supplier;Ljava/util/function/Supplier;Lnet/minecraft/world/level/block/state/BlockBehaviour$Properties;)V=|emptyPot,p_53528_,properties
|
||||
net/minecraft/world/level/block/LiquidBlock.<init>(Ljava/util/function/Supplier;Lnet/minecraft/world/level/block/state/BlockBehaviour$Properties;)V=|p_54694_,p_54695_
|
||||
net/minecraft/world/level/block/PoweredRailBlock.<init>(Lnet/minecraft/world/level/block/state/BlockBehaviour$Properties;Z)V=|p_55218_,isPoweredRail
|
||||
net/minecraft/world/level/chunk/ChunkAccess.findBlocks(Ljava/util/function/BiPredicate;Ljava/util/function/BiConsumer;)V=|p_285343_,p_285030_
|
||||
net/minecraft/world/level/chunk/ImposterProtoChunk.findBlocks(Ljava/util/function/BiPredicate;Ljava/util/function/BiConsumer;)V=|p_285343_,p_285030_
|
||||
net/minecraft/world/level/entity/PersistentEntitySectionManager.addEntityWithoutEvent(Lnet/minecraft/world/level/entity/EntityAccess;Z)Z=|p_157539_,p_157540_
|
||||
net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.processEntityInfos(Lnet/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate;Lnet/minecraft/world/level/LevelAccessor;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/levelgen/structure/templatesystem/StructurePlaceSettings;Ljava/util/List;)Ljava/util/List;=|template,p_215387_0_,p_215387_1_,p_215387_2_,p_215387_3_
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"type": "minecraft:function",
|
||||
"environment": "minecraft:default",
|
||||
"function": "forge:modify_overlay_test/replace_renderer",
|
||||
"max_ticks": 100,
|
||||
"structure": "forge:empty3x3x3"
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
package net.minecraftforge.debug.client;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.gametest.framework.GameTestHelper;
|
||||
import net.minecraft.resources.Identifier;
|
||||
import net.minecraftforge.client.event.AddGuiOverlayLayersEvent;
|
||||
@@ -39,6 +40,12 @@ public class ModifyOverlayTest extends BaseTestMod {
|
||||
private static final ForgeLayer layerC = (gg,tr) -> {};
|
||||
private static final Identifier layerCName = name("layer_c");
|
||||
|
||||
private static final IForgeGameTestHelper.BoolFlag detectReplaceFlag = new IForgeGameTestHelper.BoolFlag("det_replace_flag");
|
||||
// Wrapper renderer since we just need to change the callee location to test
|
||||
private static final ForgeLayer replacementRenderer = (gg, tracker) -> {
|
||||
Minecraft.getInstance().gui.renderEffects(gg, tracker);
|
||||
detectReplaceFlag.set(true);
|
||||
};
|
||||
|
||||
private static final IForgeGameTestHelper.BoolFlag detectConditionFlag = new IForgeGameTestHelper.BoolFlag("det_cond_flag");
|
||||
private static final IForgeGameTestHelper.BoolFlag detectConditionStackFlag = new IForgeGameTestHelper.BoolFlag("det_cond_stack_flag");
|
||||
@@ -119,6 +126,13 @@ public class ModifyOverlayTest extends BaseTestMod {
|
||||
});
|
||||
}
|
||||
|
||||
@GameTest
|
||||
public static void replace_renderer(GameTestHelper helper) {
|
||||
helper.assertTrue(detectReplaceFlag.getBool(), "Replace flag was not set.");
|
||||
detectReplaceFlag.set(false);
|
||||
helper.succeed();
|
||||
}
|
||||
|
||||
private void overlayTestListener(AddGuiOverlayLayersEvent event) {
|
||||
drawStack = event.getLayeredDraw();
|
||||
var layeredDraw = event.getLayeredDraw();
|
||||
@@ -128,6 +142,7 @@ public class ModifyOverlayTest extends BaseTestMod {
|
||||
// Layers have to be present to be ordered against, of course, but we tested for that already above.
|
||||
pEffects.addAbove(layerBName, POTION_EFFECTS, layerB);
|
||||
pEffects.addAbove(layerCName, layerBName, layerC);
|
||||
layeredDraw.replace(PRE_SLEEP_STACK, POTION_EFFECTS, replacementRenderer);
|
||||
layeredDraw.addBelow(PRE_SLEEP_STACK, layerAName, layerBName, layerA);
|
||||
layeredDraw.addConditionTo(PRE_SLEEP_STACK, BOSS_OVERLAY, () -> {
|
||||
if (enableConditionFlag.getBool()) {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
package net.minecraftforge.debug.client;
|
||||
|
||||
import net.minecraft.client.DeltaTracker;
|
||||
import net.minecraft.client.renderer.LevelTargetBundle;
|
||||
import net.minecraft.client.renderer.state.LevelRenderState;
|
||||
import net.minecraftforge.client.FramePassManager;
|
||||
@@ -23,6 +24,8 @@ import net.minecraft.world.phys.AABB;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.blaze3d.framegraph.FramePass;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@GameTestNamespace("forge")
|
||||
@Mod(RenderFrameLayerTest.MODID)
|
||||
@@ -48,10 +51,11 @@ public class RenderFrameLayerTest extends BaseTestMod {
|
||||
/**
|
||||
* If this is working, two white line box cubes will be rendered at ground level in a superflat world around (0,0)
|
||||
*/
|
||||
@NullMarked
|
||||
public static void renderTest(AddFramePassEvent event) {
|
||||
FramePassManager.PassDefinition def = new FramePassManager.PassDefinition() {
|
||||
@Override
|
||||
public void extracts(LevelTargetBundle bundle, FramePass pass) {
|
||||
public void extracts(LevelTargetBundle bundle, FramePass pass, DeltaTracker dt) {
|
||||
bundle.main = pass.readsAndWrites(bundle.main);
|
||||
}
|
||||
|
||||
@@ -72,7 +76,7 @@ public class RenderFrameLayerTest extends BaseTestMod {
|
||||
event.addPass(rl(MODID), def);
|
||||
FramePassManager.PassDefinition def2 = new FramePassManager.PassDefinition() {
|
||||
@Override
|
||||
public void extracts(LevelTargetBundle bundle, FramePass pass) {
|
||||
public void extracts(@NotNull LevelTargetBundle bundle, FramePass pass, DeltaTracker dt) {
|
||||
bundle.main = pass.readsAndWrites(bundle.main);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user