Compare commits

..
30 Commits
Author SHA1 Message Date
LexManos 211d3aa692 26.1.2 RB 1 2026-07-27 13:34:36 -07:00
LexManos 88bb2217e5 Fix ModList screen selection getting out of sync. Closes #10855 2026-07-27 13:26:59 -07:00
LexManos 278bdfc132 Bump Dependencies, Notably ASM for Java 27 support 2026-07-27 13:24:33 -07:00
Matt 66d4d888eb Add DeltaTracker (partialTick) context to PassDefinitions (#10848) 2026-07-21 11:34:01 -07:00
Meme Man ee1e695124 Update equality check for RegistryObject to include RegistryObject#key. (#10859) 2026-07-21 10:44:44 -07:00
zznty 6435274d1e Handle glfw window pos errors gracefully for wayland (#10852) 2026-07-18 14:36:59 -07:00
6c3729d6e9 ForgeDev 7 (#10821)
Co-authored-by: LexManos <[email protected]>
Co-authored-by: Jonathing <[email protected]>
2026-06-28 13:21:48 -07:00
Matt 6a702686db Toml parsing errors are now visible in the latest log. (#10825) 2026-06-18 11:09:43 -07:00
Matt d3476f3f80 [26.1.2] Split hotbar rendering into components, allow replacing renders (#10824) 2026-06-09 11:21:51 -07:00
LexManos 6665cdcf4e Fix misaligned ClientLevel load patch 2026-05-04 09:26:36 -07:00
LexManos d9665f6447 Fix IForgeBlockStateModel#getModelData is never called Fixes #10817 2026-05-02 16:48:24 -07:00
LexManos ec73fd5240 Fix base bucket model not being flipped for gasses. Closes #10814 2026-04-27 10:45:07 -07:00
LexManos 3bbdbacb81 Fix being considered in water when not. Closes #10813 2026-04-23 14:36:51 -07:00
Paint_Ninja 478d8b3f18 Add c:bars/copper and c:bars/iron tags (#10810) 2026-04-15 22:20:21 +01:00
Paint_Ninja 03f928a3de Add c:foods/dough tag (#10811) 2026-04-15 22:19:57 +01:00
Paint_Ninja 4834f16aef Add c:drink_containing/bottle and c:drink_containing/bucket (#10812) 2026-04-15 22:19:44 +01:00
LexManos 7846d25606 Copy fluid and lava still textures to Item atlas for DynamicFluidContainerModel. Fixes #10809 2026-04-12 12:53:34 -07:00
LexManos 77cb0c5c4e 26.1.2 2026-04-09 10:16:29 -07:00
Paint_Ninja 5e421e8b8b Add c:tools/trident item tag, update contents of c:tools/melee_weapon, remove c:tools/spear (#10808)
- Breaking change: `c:tools/spear` has been renamed to `c:tools/trident` to match other loaders
- The contents of the `c:tools/melee_weapon` tag now includes spears
2026-04-08 20:26:14 +01:00
LexManos 5f37e8af53 Work around 3rd party launchers who build incorrect classpaths Closes #10797 2026-04-03 09:31:40 -07:00
LexManos 5ab792f1d7 26.1.1 2026-04-01 10:40:56 -07:00
LexManos 38ebbf189c Fix arrows not being used correctly. 2026-03-29 15:44:31 -07:00
LexManos f247eebd55 Fix dyed Items are not rendered with color. Closes #10799 2026-03-29 02:21:41 -07:00
LexManos 555989cf10 Fix not remembering experimental warning confirmation 2026-03-29 02:20:50 -07:00
LexManos 49fe93b1b5 Fix incorrect argument passed to LootTableLoadEvent 2026-03-29 02:19:17 -07:00
LexManos 48167e93b4 Update MCPConfig to fix local variable names 2026-03-28 23:28:36 -07:00
LexManos 2bd132a6f8 Fix not regaining air bubbles. Closes #10798 2026-03-27 16:05:55 -07:00
LexManos b8168a12df Fix misaligned PlayerTick hook. Closes #10796 2026-03-25 18:35:02 -07:00
LexManos df271e0265 Fix loop in LoadingErrorScreen Closes #10795 2026-03-25 10:30:27 -07:00
LexManos d7687a72d5 Fix MDK by bumping to FG 7.0.17 2026-03-24 21:14:22 -07:00
526 changed files with 6954 additions and 9217 deletions

No files matched your search

+1 -1
View File
@@ -2,7 +2,7 @@ name: Publish
on:
push:
branches: [ '26.1' ]
branches: [ '26.1.2' ]
permissions:
contents: read
+13 -3
View File
@@ -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/
@@ -47,3 +52,8 @@ src/*/generated/**/.cache/
/*/.factorypath
/*/.apt_generated/
/fmlcore/logs/
/lib/
/_actual/
/runs/
/forge.ipr
/forge.iws
+1 -1
View File
@@ -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
+776 -37
View File
@@ -1,57 +1,796 @@
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.51,6.2)' apply false
id 'net.minecraftforge.gradle.mcp' version '[6.0.51,6.2)' apply false
id 'net.minecraftforge.gradlejarsigner' version '1.0.4'
id 'org.barfuin.gradle.taskinfo' version '3.0.1'
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.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)
}
}
changelog {
from '47.999'
/* This is used for debugging, comparing all published artifacts against actual production
forgedev.validatePublish {
task {
baseBranch = 'upstream/26.1.2'
versionPrefix = minecraftVersion
}
}
*/
final minecraftFiles = forgedev.minecraftFiles(minecraftVersion)
final mcpBase = forgedev.mcpBase {
mcpVersion = "${minecraftVersion}-${project.ext.mcpVersion}"
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'
}
repositories {
mavenLocal()
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)
}
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
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', '-XX:+UseCompactObjectHeaders'
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
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.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 {}
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 sourcesJarAll = tasks.register('sourcesJarAll', Jar) {
archiveClassifier = 'sources-all'
from(sourceSets.main.allJava.srcDirs)
}
// Our sources, plus the patches we do to Vanilla Minecraft
final sourcesJar = tasks.register('sourcesJar', Jar) {
archiveClassifier = 'sources'
from(sourceSets.main.allJava.srcDirs - forgedev.patches.patched.asFile.get())
mustRunAfter(forgedev.patches.make)
from(forgedev.patches.patches) {
into 'patches/'
}
}
forgedev.userDev {
base(mcpBase)
config {
universal universalJar
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', '-XX:+UseCompactObjectHeaders'
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(sourcesJarAll.flatMap { it.archiveFile })
}
binaryPatches {
mustRunAfter(forgedev.patches.make)
patches.setFrom(forgedev.patches.patches)
dirty.setFrom(tasks.named('jar', Jar).flatMap { it.archiveFile })
}
}
forgedev.userdevCompatibility(minecraftVersion) {
clean = mcpBase.classes
dirty = tasks.named('jar', Jar)
}
// region Installer tasks, Not needed when we simplify the installer/remove obf
final clientBinPatches = forgedev.binaryPatches('client') {
clean = minecraftFiles.client
// Workaround for 3rd party launcher issues, marker needed to locate MC, store needed to fix zlib-ng issues
apply {
store = true
marker = '.forge_patched_minecraft'
}
}
final serverBinPatches = forgedev.binaryPatches('server') {
clean = minecraftFiles.serverExtracted
apply { store = true }
}
// 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
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.installer
final launcherId = "${minecraftVersion}-${project.name}-${forgeVersion}"
final installertools = tool(buildLibs.installertools)
final binarypatcher = tool(buildLibs.binarypatcher)
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 'MC_UNPACKED', "[net.minecraft:client:${minecraftVersion}]", "[net.minecraft:server:${minecraftVersion}:unpacked]"
data 'MC_UNPACKED_SHA', minecraftFiles.client, minecraftFiles.serverExtracted
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 }
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(binarypatcher) {
side = 'server'
args = [
'--clean', '{MC_UNPACKED}',
'--output', '{PATCHED}',
'--apply', '{BINPATCH}',
'--data', '--unpatched',
'--store'
]
cache '{PATCHED}', '{PATCHED_SHA}'
},
step(binarypatcher) {
side = 'client'
args = [
'--clean', '{MINECRAFT_JAR}',
'--output', '{PATCHED}',
'--apply', '{BINPATCH}',
'--data', '--unpatched',
'--store',
'--marker', '.forge_patched_minecraft'
]
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', '-XX:+UseCompactObjectHeaders' ]
}
}
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,
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 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', 'sourcesJarAll' ]
.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
}
}
-3
View File
@@ -1,3 +0,0 @@
/.gradle/
/build/
/out/
-14
View File
@@ -1,14 +0,0 @@
repositories {
maven { url = 'https://maven.minecraftforge.net/' }
mavenCentral()
}
dependencies {
implementation 'org.ow2.asm:asm:9.9.1'
implementation 'org.ow2.asm:asm-tree:9.9.1'
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,67 +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()
@InputFile abstract RegularFileProperty getServerBundle()
@OutputFile abstract RegularFileProperty getOutput()
BundleList() {
config.setFrom(project.configurations.installer)
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")
}
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()
}
@@ -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,94 +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)
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.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()
}
}
@@ -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,282 +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()
@Override
void check(Reporter reporter, boolean fix) {
final inheritance = InheritanceData.parse(this.inheritance.get().asFile)
ats.each {
final lines = process(it, reporter, inheritance)
if (fix) {
it.text = joinBack(lines, inheritance).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) {
final data = [] as List<String>
lines.each { key, value ->
if (!value.group) {
def comment = null //value.comment
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 = null //entry.comment
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") }
}
}
-73
View File
@@ -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'
}
-1021
View File
File diff suppressed because it is too large. Load diff
-89
View File
@@ -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
}
}
+2 -2
View File
@@ -2,8 +2,8 @@
MinecraftForge
=============
[![Stable Release](https://img.shields.io/badge/dynamic/json?url=https://files.minecraftforge.net/net/minecraftforge/forge/promotions_slim.json&label=Stable&prefix=1.21.11-&query=$.promos["1.21.11-recommended"]&color=brightgreen&logo=data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyBjbGFzcz0ic3ZnLWlubGluZS0tZmEgZmEtc3RhciBmYS13LTE4IiBhcmlhLWhpZGRlbj0idHJ1ZSIgZGF0YS1pY29uPSJzdGFyIiBkYXRhLXByZWZpeD0iZmFzIiBmb2N1c2FibGU9ImZhbHNlIiByb2xlPSJpbWciIHZpZXdCb3g9IjAgMCA1NzYgNTEyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8cGF0aCBkPSJNMjU5LjMgMTcuOEwxOTQgMTUwLjIgNDcuOSAxNzEuNWMtMjYuMiAzLjgtMzYuNyAzNi4xLTE3LjcgNTQuNmwxMDUuNyAxMDMtMjUgMTQ1LjVjLTQuNSAyNi4zIDIzLjIgNDYgNDYuNCAzMy43TDI4OCA0MzkuNmwxMzAuNyA2OC43YzIzLjIgMTIuMiA1MC45LTcuNCA0Ni40LTMzLjdsLTI1LTE0NS41IDEwNS43LTEwM2MxOS0xOC41IDguNS01MC44LTE3LjctNTQuNkwzODIgMTUwLjIgMzE2LjcgMTcuOGMtMTEuNy0yMy42LTQ1LjYtMjMuOS01Ny40IDB6IiBmaWxsPSJ3aGl0ZSIvPgo8L3N2Zz4K)][Download]
[![Latest Release](https://img.shields.io/badge/dynamic/json?url=https://files.minecraftforge.net/net/minecraftforge/forge/promotions_slim.json&label=Latest&prefix=26.1-&query=$.promos["26.1-latest"]&color=blue&logo=data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyBjbGFzcz0ic3ZnLWlubGluZS0tZmEgZmEtYnVnIGZhLXctMTYiIGFyaWEtaGlkZGVuPSJ0cnVlIiBkYXRhLWljb249ImJ1ZyIgZGF0YS1wcmVmaXg9ImZhcyIgZm9jdXNhYmxlPSJmYWxzZSIgcm9sZT0iaW1nIiB2aWV3Qm94PSIwIDAgNTEyIDUxMiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTUxMS45ODggMjg4LjljLS40NzggMTcuNDMtMTUuMjE3IDMxLjEtMzIuNjUzIDMxLjFINDI0djE2YzAgMjEuODY0LTQuODgyIDQyLjU4NC0xMy42IDYxLjE0NWw2MC4yMjggNjAuMjI4YzEyLjQ5NiAxMi40OTcgMTIuNDk2IDMyLjc1OCAwIDQ1LjI1NS0xMi40OTggMTIuNDk3LTMyLjc1OSAxMi40OTYtNDUuMjU2IDBsLTU0LjczNi01NC43MzZDMzQ1Ljg4NiA0NjcuOTY1IDMxNC4zNTEgNDgwIDI4MCA0ODBWMjM2YzAtNi42MjctNS4zNzMtMTItMTItMTJoLTI0Yy02LjYyNyAwLTEyIDUuMzczLTEyIDEydjI0NGMtMzQuMzUxIDAtNjUuODg2LTEyLjAzNS05MC42MzYtMzIuMTA4bC01NC43MzYgNTQuNzM2Yy0xMi40OTggMTIuNDk3LTMyLjc1OSAxMi40OTYtNDUuMjU2IDAtMTIuNDk2LTEyLjQ5Ny0xMi40OTYtMzIuNzU4IDAtNDUuMjU1bDYwLjIyOC02MC4yMjhDOTIuODgyIDM3OC41ODQgODggMzU3Ljg2NCA4OCAzMzZ2LTE2SDMyLjY2NkMxNS4yMyAzMjAgLjQ5MSAzMDYuMzMuMDEzIDI4OC45LS40ODQgMjcwLjgxNiAxNC4wMjggMjU2IDMyIDI1Nmg1NnYtNTguNzQ1bC00Ni42MjgtNDYuNjI4Yy0xMi40OTYtMTIuNDk3LTEyLjQ5Ni0zMi43NTggMC00NS4yNTUgMTIuNDk4LTEyLjQ5NyAzMi43NTgtMTIuNDk3IDQ1LjI1NiAwTDE0MS4yNTUgMTYwaDIyOS40ODlsNTQuNjI3LTU0LjYyN2MxMi40OTgtMTIuNDk3IDMyLjc1OC0xMi40OTcgNDUuMjU2IDAgMTIuNDk2IDEyLjQ5NyAxMi40OTYgMzIuNzU4IDAgNDUuMjU1TDQyNCAxOTcuMjU1VjI1Nmg1NmMxNy45NzIgMCAzMi40ODQgMTQuODE2IDMxLjk4OCAzMi45ek0yNTcgMGMtNjEuODU2IDAtMTEyIDUwLjE0NC0xMTIgMTEyaDIyNEMzNjkgNTAuMTQ0IDMxOC44NTYgMCAyNTcgMHoiIGZpbGw9IndoaXRlIi8+Cjwvc3ZnPgo=
[![Stable Release](https://img.shields.io/badge/dynamic/json?url=https://files.minecraftforge.net/net/minecraftforge/forge/promotions_slim.json&label=Stable&prefix=26.1.2-&query=$.promos["26.1.2-recommended"]&color=brightgreen&logo=data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyBjbGFzcz0ic3ZnLWlubGluZS0tZmEgZmEtc3RhciBmYS13LTE4IiBhcmlhLWhpZGRlbj0idHJ1ZSIgZGF0YS1pY29uPSJzdGFyIiBkYXRhLXByZWZpeD0iZmFzIiBmb2N1c2FibGU9ImZhbHNlIiByb2xlPSJpbWciIHZpZXdCb3g9IjAgMCA1NzYgNTEyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8cGF0aCBkPSJNMjU5LjMgMTcuOEwxOTQgMTUwLjIgNDcuOSAxNzEuNWMtMjYuMiAzLjgtMzYuNyAzNi4xLTE3LjcgNTQuNmwxMDUuNyAxMDMtMjUgMTQ1LjVjLTQuNSAyNi4zIDIzLjIgNDYgNDYuNCAzMy43TDI4OCA0MzkuNmwxMzAuNyA2OC43YzIzLjIgMTIuMiA1MC45LTcuNCA0Ni40LTMzLjdsLTI1LTE0NS41IDEwNS43LTEwM2MxOS0xOC41IDguNS01MC44LTE3LjctNTQuNkwzODIgMTUwLjIgMzE2LjcgMTcuOGMtMTEuNy0yMy42LTQ1LjYtMjMuOS01Ny40IDB6IiBmaWxsPSJ3aGl0ZSIvPgo8L3N2Zz4K)][Download]
[![Latest Release](https://img.shields.io/badge/dynamic/json?url=https://files.minecraftforge.net/net/minecraftforge/forge/promotions_slim.json&label=Latest&prefix=26.1.2-&query=$.promos["26.1.2-latest"]&color=blue&logo=data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyBjbGFzcz0ic3ZnLWlubGluZS0tZmEgZmEtYnVnIGZhLXctMTYiIGFyaWEtaGlkZGVuPSJ0cnVlIiBkYXRhLWljb249ImJ1ZyIgZGF0YS1wcmVmaXg9ImZhcyIgZm9jdXNhYmxlPSJmYWxzZSIgcm9sZT0iaW1nIiB2aWV3Qm94PSIwIDAgNTEyIDUxMiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTUxMS45ODggMjg4LjljLS40NzggMTcuNDMtMTUuMjE3IDMxLjEtMzIuNjUzIDMxLjFINDI0djE2YzAgMjEuODY0LTQuODgyIDQyLjU4NC0xMy42IDYxLjE0NWw2MC4yMjggNjAuMjI4YzEyLjQ5NiAxMi40OTcgMTIuNDk2IDMyLjc1OCAwIDQ1LjI1NS0xMi40OTggMTIuNDk3LTMyLjc1OSAxMi40OTYtNDUuMjU2IDBsLTU0LjczNi01NC43MzZDMzQ1Ljg4NiA0NjcuOTY1IDMxNC4zNTEgNDgwIDI4MCA0ODBWMjM2YzAtNi42MjctNS4zNzMtMTItMTItMTJoLTI0Yy02LjYyNyAwLTEyIDUuMzczLTEyIDEydjI0NGMtMzQuMzUxIDAtNjUuODg2LTEyLjAzNS05MC42MzYtMzIuMTA4bC01NC43MzYgNTQuNzM2Yy0xMi40OTggMTIuNDk3LTMyLjc1OSAxMi40OTYtNDUuMjU2IDAtMTIuNDk2LTEyLjQ5Ny0xMi40OTYtMzIuNzU4IDAtNDUuMjU1bDYwLjIyOC02MC4yMjhDOTIuODgyIDM3OC41ODQgODggMzU3Ljg2NCA4OCAzMzZ2LTE2SDMyLjY2NkMxNS4yMyAzMjAgLjQ5MSAzMDYuMzMuMDEzIDI4OC45LS40ODQgMjcwLjgxNiAxNC4wMjggMjU2IDMyIDI1Nmg1NnYtNTguNzQ1bC00Ni42MjgtNDYuNjI4Yy0xMi40OTYtMTIuNDk3LTEyLjQ5Ni0zMi43NTggMC00NS4yNTUgMTIuNDk4LTEyLjQ5NyAzMi43NTgtMTIuNDk3IDQ1LjI1NiAwTDE0MS4yNTUgMTYwaDIyOS40ODlsNTQuNjI3LTU0LjYyN2MxMi40OTgtMTIuNDk3IDMyLjc1OC0xMi40OTcgNDUuMjU2IDAgMTIuNDk2IDEyLjQ5NyAxMi40OTYgMzIuNzU4IDAgNDUuMjU1TDQyNCAxOTcuMjU1VjI1Nmg1NmMxNy45NzIgMCAzMi40ODQgMTQuODE2IDMxLjk4OCAzMi45ek0yNTcgMGMtNjEuODU2IDAtMTEyIDUwLjE0NC0xMTIgMTEyaDIyNEMzNjkgNTAuMTQ0IDMxOC44NTYgMCAyNTcgMHoiIGZpbGw9IndoaXRlIi8+Cjwvc3ZnPgo=
)][Download] [![Discord](https://img.shields.io/discord/313125603924639766.svg?color=%237289da&label=Discord&logo=discord&logoColor=%237289da)][Discord] [![Support](https://img.shields.io/badge/Patreon-Support-orange.svg?logo=Patreon)][Patreon]
Forge is a free, open-source modding API all of your favourite mods use!
+50 -40
View File
@@ -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
}
}
}
}
+86 -61
View File
@@ -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
}
}
}
}
@@ -459,7 +459,9 @@ public class DisplayWindow implements ImmediateWindowProvider {
this.winWidth = x[0];
this.winHeight = y[0];
// Setting the window position isn't supported on wayland, so check the error here
glfwSetWindowPos(window, (vidmode.width() - this.winWidth) / 2 + monitorX, (vidmode.height() - this.winHeight) / 2 + monitorY);
handleLastGLFWError();
// Attempt setting the icon
// int[] channels = new int[1];
@@ -482,7 +484,10 @@ public class DisplayWindow implements ImmediateWindowProvider {
// Show the window
glfwShowWindow(window);
// Getting the window position isn't supported on wayland, so check the error here
glfwGetWindowPos(window, x, y);
handleLastGLFWError();
this.winX = x[0];
this.winY = y[0];
glfwGetFramebufferSize(window, x, y);
@@ -491,6 +496,18 @@ public class DisplayWindow implements ImmediateWindowProvider {
glfwPollEvents();
}
private static void handleLastGLFWError() {
handleLastGLFWError((error, description) -> {
if (error == GLFW_FEATURE_UNAVAILABLE) {
// suppress window pos errors for unsupported platforms (wayland)
LOGGER.debug(String.format("Suppressing GLFW error: [0x%X]%s", error, description));
return;
}
throw new IllegalStateException(String.format("GLFW error: [0x%X]%s", error, description));
});
}
private void winResize(long window, int width, int height) {
if (window == this.window && width != 0 && height != 0) {
this.winWidth = width;
@@ -648,4 +665,4 @@ public class DisplayWindow implements ImmediateWindowProvider {
this.context.elementShader().close();
SimpleBufferBuilder.destroy();
}
}
}
+144 -100
View File
@@ -1,69 +1,75 @@
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 }
implementation(libs.roimfs ){ 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 +77,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 +166,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'
}
}
}
@@ -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.");
}
}
}
@@ -25,7 +25,9 @@ sealed abstract class ForgeProdLaunchHandler extends CommonLaunchHandler {
@Override
public List<Path> getMinecraftPaths() {
return List.of(getPathFromResource("net/minecraft/client/Minecraft.class"));
// We use a marker because 3rd party launcher don't build their class paths correctly
// https://github.com/MinecraftForge/MinecraftForge/issues/10797
return List.of(getPathFromResource(".forge_patched_minecraft"));
}
}
@@ -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
+59 -54
View File
@@ -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
}
}
}
}
@@ -42,12 +42,6 @@
"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;"
]
},
{
"class": "net/minecraft/world/entity/animal/frog/Tadpole",
"methods": [
]
},
{
"class": "net/minecraft/world/entity/monster/Strider",
"methods": [
@@ -85,24 +79,12 @@
"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;"
]
},
{
"class": "net/minecraft/world/entity/monster/zombie/ZombieVillager",
"methods": [
]
},
{
"class": "net/minecraft/world/entity/npc/CatSpawner",
"methods": [
"spawnCat(Lnet/minecraft/core/BlockPos;Lnet/minecraft/server/level/ServerLevel;Z)V"
]
},
{
"class": "net/minecraft/world/entity/npc/villager/Villager",
"methods": [
]
},
{
"class": "net/minecraft/world/entity/raid/Raid",
"methods": [
@@ -158,11 +140,5 @@
"methods": [
"handleDataMarker(Ljava/lang/String;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/ServerLevelAccessor;Lnet/minecraft/util/RandomSource;Lnet/minecraft/world/level/levelgen/structure/BoundingBox;)V"
]
},
{
"class": "net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate",
"methods": [
]
}
]
+23 -11
View File
@@ -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=25
MC_VERSION=26.1
MC_NEXT_VERSION=26.2
MCP_VERSION=20260324.123823
MAPPING_CHANNEL=official
MAPPING_VERSION=26.1
# 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
Binary file not shown.
+1 -1
View File
@@ -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
Vendored
+7 -8
View File
@@ -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
View File
@@ -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
+61 -48
View File
@@ -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
}
}
}
}
+58 -46
View File
@@ -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
View File
@@ -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
}
}
}
}
+1 -3
View File
@@ -6,7 +6,7 @@ plugins {
id 'java'
id 'idea'
id 'eclipse'
id 'net.minecraftforge.gradle' version '[7.0.3,8)'
id 'net.minecraftforge.gradle' version '[7.0.17,8)'
}
version = '1.0.0'
@@ -19,8 +19,6 @@ java.toolchain.languageVersion = JavaLanguageVersion.of(25)
sourceSets.main.resources { srcDir 'src/generated/resources' }
minecraft {
mappings channel: 'official', version: '@MC_VERSION@'
runs {
configureEach {
workingDir = layout.projectDirectory.dir('run')
+6
View File
@@ -0,0 +1,6 @@
[versions]
java = "25"
minecraft = "26.1.2"
minecraft-next = "26.2"
mcp = "20260409.101008"
changelog-base = "64.0"
@@ -18,14 +18,14 @@
@@ -103,6 +_,8 @@
}
LOGGER.info("OpenGL debug message: {}", gldebug$logentry);
LOGGER.info("OpenGL debug message: {}", entry);
+ // TODO: [VEN] Trim the stack trace
+ if (PRINT_STACKTRACE_ON_ERROR) LOGGER.info("Trace: ", new Throwable("GlDebug"));
}
public List<String> getLastOpenGlDebugMessages() {
@@ -126,7 +_,7 @@
GlDebug gldebug1 = new GlDebug();
GlDebug debug = new GlDebug();
enabledExtensions.add("GL_KHR_debug");
GL11.glEnable(37600);
- if (debugSynchronousGlLogs) {
@@ -34,8 +34,8 @@
}
@@ -140,7 +_,7 @@
} else if (glcapabilities.GL_ARB_debug_output && GlDevice.USE_GL_ARB_debug_output) {
GlDebug gldebug = new GlDebug();
} else if (caps.GL_ARB_debug_output && GlDevice.USE_GL_ARB_debug_output) {
GlDebug debug = new GlDebug();
enabledExtensions.add("GL_ARB_debug_output");
- if (debugSynchronousGlLogs) {
+ if (debugSynchronousGlLogs | PRINT_STACKTRACE_ON_ERROR) {
@@ -24,31 +24,39 @@
+ @Override
+ public GpuTexture createTexture(@Nullable String label, @GpuTexture.Usage final int usage, final TextureFormat format, final int width, final int height, final int depthOrLayers, final int mipLevels, boolean stencil) {
GlStateManager.clearGlErrors();
int i = GlStateManager._genTexture();
int id = GlStateManager._genTexture();
if (label == null) {
@@ -169,14 +_,14 @@
for (int k : GlConst.CUBEMAP_TARGETS) {
for (int l = 0; l < mipLevels; l++) {
@@ -171,12 +_,12 @@
GlStateManager._texImage2D(
- k, l, GlConst.toGlInternalId(format), width >> l, height >> l, 0, GlConst.toGlExternalId(format), GlConst.toGlType(format), null
+ k, l, GlConst.toGlInternalId(format, stencil), width >> l, height >> l, 0, GlConst.toGlExternalId(format, stencil), GlConst.toGlType(format, stencil), null
cubeTarget,
i,
- GlConst.toGlInternalId(format),
+ GlConst.toGlInternalId(format, stencil),
width >> i,
height >> i,
0,
- GlConst.toGlExternalId(format),
- GlConst.toGlType(format),
+ GlConst.toGlExternalId(format, stencil),
+ GlConst.toGlType(format, stencil),
null
);
}
}
@@ -184,7 +_,7 @@
} else {
for (int i1 = 0; i1 < mipLevels; i1++) {
for (int i = 0; i < mipLevels; i++) {
GlStateManager._texImage2D(
- j, i1, GlConst.toGlInternalId(format), width >> i1, height >> i1, 0, GlConst.toGlExternalId(format), GlConst.toGlType(format), null
+ j, i1, GlConst.toGlInternalId(format, stencil), width >> i1, height >> i1, 0, GlConst.toGlExternalId(format, stencil), GlConst.toGlType(format, stencil), null
- target, i, GlConst.toGlInternalId(format), width >> i, height >> i, 0, GlConst.toGlExternalId(format), GlConst.toGlType(format), null
+ target, i, GlConst.toGlInternalId(format, stencil), width >> i, height >> i, 0, GlConst.toGlExternalId(format, stencil), GlConst.toGlType(format, stencil), null
);
}
}
@@ -187,7 +_,7 @@
} else if (j1 != 0) {
throw new IllegalStateException("OpenGL error " + j1);
@@ -195,7 +_,7 @@
} else if (error != 0) {
throw new IllegalStateException("OpenGL error " + error);
} else {
- GlTexture gltexture = new GlTexture(usage, label, format, width, height, depthOrLayers, mipLevels, i);
+ GlTexture gltexture = new GlTexture(usage, label, format, width, height, depthOrLayers, mipLevels, i, stencil);
this.debugLabels.applyLabel(gltexture);
return gltexture;
- GlTexture texture = new GlTexture(usage, label, format, width, height, depthOrLayers, mipLevels, id);
+ GlTexture texture = new GlTexture(usage, label, format, width, height, depthOrLayers, mipLevels, id, stencil);
this.debugLabels.applyLabel(texture);
return texture;
}
@@ -4,9 +4,9 @@
this.width = width;
this.height = height;
if (this.useDepth) {
- this.depthTexture = gpudevice.createTexture(() -> this.label + " / Depth", 15, TextureFormat.DEPTH32, width, height, 1, 1);
+ this.depthTexture = gpudevice.createTexture(() -> this.label + " / Depth", 15, TextureFormat.DEPTH32, width, height, 1, 1, this.stencilEnabled);
this.depthTextureView = gpudevice.createTextureView(this.depthTexture);
- this.depthTexture = device.createTexture(() -> this.label + " / Depth", 15, TextureFormat.DEPTH32, width, height, 1, 1);
+ this.depthTexture = device.createTexture(() -> this.label + " / Depth", 15, TextureFormat.DEPTH32, width, height, 1, 1, this.stencilEnabled);
this.depthTextureView = device.createTextureView(this.depthTexture);
}
@@ -124,5 +_,26 @@
@@ -1,28 +1,28 @@
--- a/com/mojang/blaze3d/platform/Window.java
+++ b/com/mojang/blaze3d/platform/Window.java
@@ -97,8 +_,9 @@
Monitor monitor = this.screenManager.getMonitor(GLFW.glfwGetPrimaryMonitor());
Monitor initialMonitor = this.screenManager.getMonitor(GLFW.glfwGetPrimaryMonitor());
this.windowedWidth = this.width = allowedWindowMinSize(displayData.width());
this.windowedHeight = this.height = allowedWindowMinSize(displayData.height());
- this.handle = this.createWindow(backend, this.width, this.height, title, this.fullscreen && monitor != null ? monitor.getMonitor() : 0L);
+ this.handle = net.minecraftforge.fml.loading.ImmediateWindowHandler.setupMinecraftWindow(this.width, this.height, title, this.fullscreen && monitor != null ? monitor.getMonitor() : 0L, () -> backend);
- this.handle = this.createWindow(backend, this.width, this.height, title, this.fullscreen && initialMonitor != null ? initialMonitor.getMonitor() : 0L);
+ this.handle = net.minecraftforge.fml.loading.ImmediateWindowHandler.setupMinecraftWindow(this.width, this.height, title, this.fullscreen && initialMonitor != null ? initialMonitor.getMonitor() : 0L, () -> backend);
this.backend = backend;
+ if (!net.minecraftforge.fml.loading.ImmediateWindowHandler.positionWindow(Optional.ofNullable(monitor), w->this.width = this.windowedWidth = w, h->this.height = this.windowedHeight = h, x->this.x = this.windowedX = x, y->this.y = this.windowedY = y)) {
if (monitor != null) {
VideoMode videomode = monitor.getPreferredVidMode(this.fullscreen ? this.preferredFullscreenVideoMode : Optional.empty());
this.windowedX = this.x = monitor.getX() + videomode.getWidth() / 2 - this.width / 2;
+ if (!net.minecraftforge.fml.loading.ImmediateWindowHandler.positionWindow(Optional.ofNullable(initialMonitor), w->this.width = this.windowedWidth = w, h->this.height = this.windowedHeight = h, x->this.x = this.windowedX = x, y->this.y = this.windowedY = y)) {
if (initialMonitor != null) {
VideoMode mode = initialMonitor.getPreferredVidMode(this.fullscreen ? this.preferredFullscreenVideoMode : Optional.empty());
this.windowedX = this.x = initialMonitor.getX() + mode.getWidth() / 2 - this.width / 2;
@@ -110,6 +_,7 @@
this.windowedX = this.x = aint1[0];
this.windowedY = this.y = aint[0];
this.windowedX = this.x = actualX[0];
this.windowedY = this.y = actualY[0];
}
+ }
this.setMode();
this.refreshFramebufferSize();
@@ -300,6 +_,9 @@
GLFW.glfwGetFramebufferSize(this.handle, aint, aint1);
this.framebufferWidth = aint[0] > 0 ? aint[0] : 1;
this.framebufferHeight = aint1[0] > 0 ? aint1[0] : 1;
GLFW.glfwGetFramebufferSize(this.handle, outWidth, outHeight);
this.framebufferWidth = outWidth[0] > 0 ? outWidth[0] : 1;
this.framebufferHeight = outHeight[0] > 0 ? outHeight[0] : 1;
+ if (this.framebufferHeight == 0 || this.framebufferWidth == 0) {
+ net.minecraftforge.fml.loading.ImmediateWindowHandler.updateFBSize(w -> this.framebufferWidth = w, h -> this.framebufferHeight = h);
+ }
@@ -9,8 +9,8 @@
private VertexFormat(final List<VertexFormatElement> elements, final List<String> names, final IntList offsets, final int vertexSize) {
this.elements = elements;
@@ -41,6 +_,11 @@
int j = vertexformatelement != null ? elements.indexOf(vertexformatelement) : -1;
this.offsetsByElement[i] = j != -1 ? offsets.getInt(j) : -1;
int index = element != null ? elements.indexOf(element) : -1;
this.offsetsByElement[id] = index != -1 ? offsets.getInt(index) : -1;
}
+
+ ImmutableMap.Builder<String, VertexFormatElement> elementMapping = ImmutableMap.builder();
@@ -1,6 +1,6 @@
--- a/com/mojang/math/Transformation.java
+++ b/com/mojang/math/Transformation.java
@@ -17,7 +_,7 @@
@@ -16,7 +_,7 @@
import org.joml.Vector3fc;
import org.jspecify.annotations.Nullable;
@@ -9,7 +9,7 @@
private static final Vector3fc ZERO_TRANSLATION = new Vector3f();
private static final Vector3fc UNIT_SCALE = new Vector3f(1.0F, 1.0F, 1.0F);
private static final Quaternionfc ZERO_ROTATION = new Quaternionf();
@@ -172,6 +_,20 @@
@@ -171,6 +_,20 @@
this.scale().lerp(that.scale(), progress, new Vector3f()),
this.rightRotation().slerp(that.rightRotation(), progress, new Quaternionf())
);
@@ -1,11 +1,11 @@
--- a/com/mojang/realmsclient/gui/screens/RealmsResetWorldScreen.java
+++ b/com/mojang/realmsclient/gui/screens/RealmsResetWorldScreen.java
@@ -303,7 +_,7 @@
int k = this.getY();
graphics.blit(RenderPipelines.GUI_TEXTURED, this.image, j + 2, k + 2, 0.0F, 0.0F, 56, 56, 56, 56, 56, 56, i);
graphics.blitSprite(RenderPipelines.GUI_TEXTURED, SLOT_FRAME_SPRITE, j, k, 60, 60, i);
- int l = flag ? -6250336 : -1;
+ int l = getFGColor();
graphics.centeredText(RealmsResetWorldScreen.this.font, this.getMessage(), j + 28, k - 14, l);
@@ -302,7 +_,7 @@
int y = this.getY();
graphics.blit(RenderPipelines.GUI_TEXTURED, this.image, x + 2, y + 2, 0.0F, 0.0F, 56, 56, 56, 56, 56, 56, color);
graphics.blitSprite(RenderPipelines.GUI_TEXTURED, SLOT_FRAME_SPRITE, x, y, 60, 60, color);
- int textColor = hoveredOrFocused ? -6250336 : -1;
+ int textColor = getFGColor();
graphics.centeredText(RealmsResetWorldScreen.this.font, this.getMessage(), x + 28, y - 14, textColor);
}
}
@@ -6,8 +6,8 @@
builder.append("Thread: ").append(Thread.currentThread().getName()).append("\n");
- builder.append("Stacktrace:\n");
-
- for (StackTraceElement stacktraceelement : this.uncategorizedStackTrace) {
- builder.append("\t").append("at ").append(stacktraceelement);
- for (StackTraceElement element : this.uncategorizedStackTrace) {
- builder.append("\t").append("at ").append(element);
- builder.append("\n");
- }
-
@@ -17,7 +17,7 @@
+ builder.append(net.minecraftforge.logging.CrashReportExtender.generateEnhancedStackTrace(this.uncategorizedStackTrace));
}
for (CrashReportCategory crashreportcategory : this.details) {
for (CrashReportCategory entry : this.details) {
@@ -73,6 +_,7 @@
builder.append("\n\n");
}
@@ -27,22 +27,22 @@
}
@@ -92,18 +_,7 @@
throwable.setStackTrace(this.exception.getStackTrace());
exception.setStackTrace(this.exception.getStackTrace());
}
- String s;
- String var4;
- try {
- stringwriter = new StringWriter();
- printwriter = new PrintWriter(stringwriter);
- throwable.printStackTrace(printwriter);
- s = stringwriter.toString();
- writer = new StringWriter();
- printWriter = new PrintWriter(writer);
- exception.printStackTrace(printWriter);
- var4 = writer.toString();
- } finally {
- IOUtils.closeQuietly((Writer)stringwriter);
- IOUtils.closeQuietly((Writer)printwriter);
- IOUtils.closeQuietly((Writer)writer);
- IOUtils.closeQuietly((Writer)printWriter);
- }
-
- return s;
+ return net.minecraftforge.logging.CrashReportExtender.generateEnhancedStackTrace(throwable);
- return var4;
+ return net.minecraftforge.logging.CrashReportExtender.generateEnhancedStackTrace(exception);
}
public String getFriendlyReport(final ReportType reportType, final List<String> extraComments) {
@@ -1,26 +1,26 @@
--- a/net/minecraft/CrashReportCategory.java
+++ b/net/minecraft/CrashReportCategory.java
@@ -114,8 +_,10 @@
if (astacktraceelement.length <= 0) {
@@ -140,8 +_,10 @@
if (full.length <= 0) {
return 0;
} else {
- this.stackTrace = new StackTraceElement[astacktraceelement.length - 3 - nestedOffset];
- System.arraycopy(astacktraceelement, 3 + nestedOffset, this.stackTrace, 0, this.stackTrace.length);
+ int len = astacktraceelement.length - 3 - nestedOffset;
+ if (len <= 0) len = astacktraceelement.length;
- this.stackTrace = new StackTraceElement[full.length - 3 - nestedOffset];
- System.arraycopy(full, 3 + nestedOffset, this.stackTrace, 0, this.stackTrace.length);
+ int len = full.length - 3 - nestedOffset;
+ if (len <= 0) len = full.length;
+ this.stackTrace = new StackTraceElement[len];
+ System.arraycopy(astacktraceelement, astacktraceelement.length - len, this.stackTrace, 0, this.stackTrace.length);
+ System.arraycopy(full, full.length - len, this.stackTrace, 0, this.stackTrace.length);
return this.stackTrace.length;
}
}
@@ -162,16 +_,16 @@
@@ -188,16 +_,16 @@
if (this.stackTrace != null && this.stackTrace.length > 0) {
builder.append("\nStacktrace:");
-
- for (StackTraceElement stacktraceelement : this.stackTrace) {
- for (StackTraceElement element : this.stackTrace) {
- builder.append("\n\tat ");
- builder.append(stacktraceelement);
- builder.append(element);
- }
+ builder.append(net.minecraftforge.logging.CrashReportExtender.generateEnhancedStackTrace(this.stackTrace));
}
@@ -1,15 +1,15 @@
--- a/net/minecraft/advancements/Advancement.java
+++ b/net/minecraft/advancements/Advancement.java
@@ -227,7 +_,11 @@
@@ -219,7 +_,11 @@
}
public AdvancementHolder save(final Consumer<AdvancementHolder> output, final String name) {
- AdvancementHolder advancementholder = this.build(Identifier.parse(name));
- AdvancementHolder advancement = this.build(Identifier.parse(name));
+ return save(output, Identifier.parse(name));
+ }
+
+ public AdvancementHolder save(Consumer<AdvancementHolder> output, Identifier id) {
+ AdvancementHolder advancementholder = this.build(id);
output.accept(advancementholder);
return advancementholder;
+ AdvancementHolder advancement = this.build(id);
output.accept(advancement);
return advancement;
}
@@ -1,10 +1,10 @@
--- a/net/minecraft/advancements/AdvancementRewards.java
+++ b/net/minecraft/advancements/AdvancementRewards.java
@@ -44,6 +_,7 @@
LootParams lootparams = new LootParams.Builder(serverlevel)
@@ -43,6 +_,7 @@
LootParams params = new LootParams.Builder(level)
.withParameter(LootContextParams.THIS_ENTITY, player)
.withParameter(LootContextParams.ORIGIN, player.position())
+ .withLuck(player.getLuck())
.create(LootContextParamSets.ADVANCEMENT_REWARD);
boolean flag = false;
boolean changes = false;
@@ -3,9 +3,9 @@
@@ -217,12 +_,14 @@
return 90.0F;
} else {
float f = this.minecraft.options.fov().get().intValue() * Mth.lerp(partialTicks, this.oldFovModifier, this.fovModifier);
- return this.modifyFovBasedOnDeathOrFluid(partialTicks, f);
+ var ret = this.modifyFovBasedOnDeathOrFluid(partialTicks, f);
float fov = this.minecraft.options.fov().get().intValue() * Mth.lerp(partialTicks, this.oldFovModifier, this.fovModifier);
- return this.modifyFovBasedOnDeathOrFluid(partialTicks, fov);
+ var ret = this.modifyFovBasedOnDeathOrFluid(partialTicks, fov);
+ return net.minecraftforge.client.event.ForgeEventFactoryClient.fireComputeFov(this.minecraft.gameRenderer, this, partialTicks, ret, true).getFOV();
}
}
@@ -16,11 +16,11 @@
}
private static void forAllKeyMappings(final InputConstants.Key key, final Consumer<KeyMapping> operation) {
- List<KeyMapping> list = MAP.get(key);
+ List<KeyMapping> list = MAP.getAll(key);
if (list != null && !list.isEmpty()) {
for (KeyMapping keymapping : list) {
operation.accept(keymapping);
- List<KeyMapping> keyMappings = MAP.get(key);
+ List<KeyMapping> keyMappings = MAP.getAll(key);
if (keyMappings != null && !keyMappings.isEmpty()) {
for (KeyMapping keyMapping : keyMappings) {
operation.accept(keyMapping);
@@ -106,7 +_,7 @@
}
@@ -1,6 +1,6 @@
--- a/net/minecraft/client/KeyboardHandler.java
+++ b/net/minecraft/client/KeyboardHandler.java
@@ -516,7 +_,7 @@
@@ -514,7 +_,7 @@
if (screen != null) {
try {
if (action != 1 && action != 2) {
@@ -9,16 +9,16 @@
if (options.keyDebugModifier.matches(event)) {
this.usedDebugKeyAsModifier = false;
}
@@ -525,7 +_,7 @@
@@ -523,7 +_,7 @@
}
} else {
screen.afterKeyboardAction();
- if (screen.keyPressed(event)) {
+ if (net.minecraftforge.client.ForgeHooksClient.onScreenKeyPressed(screen, event)) {
if (this.minecraft.screen == null) {
InputConstants.Key inputconstants$key = InputConstants.getKey(event);
KeyMapping.set(inputconstants$key, false);
@@ -597,6 +_,7 @@
InputConstants.Key key = InputConstants.getKey(event);
KeyMapping.set(key, false);
@@ -595,6 +_,7 @@
}
}
}
@@ -26,12 +26,12 @@
}
}
@@ -605,7 +_,7 @@
@@ -603,7 +_,7 @@
Screen screen = this.minecraft.screen;
if (screen != null && this.minecraft.getOverlay() == null) {
try {
- screen.charTyped(event);
+ net.minecraftforge.client.ForgeHooksClient.onScreenCharTyped(screen, event);
} catch (Throwable throwable) {
CrashReport crashreport = CrashReport.forThrowable(throwable, "charTyped event handler");
screen.fillCrashDetails(crashreport);
} catch (Throwable var8) {
CrashReport report = CrashReport.forThrowable(var8, "charTyped event handler");
screen.fillCrashDetails(report);
@@ -1,6 +1,6 @@
--- a/net/minecraft/client/Minecraft.java
+++ b/net/minecraft/client/Minecraft.java
@@ -272,7 +_,7 @@
@@ -268,7 +_,7 @@
import org.slf4j.Logger;
@OnlyIn(Dist.CLIENT)
@@ -9,7 +9,7 @@
private static Minecraft instance;
private static final Logger LOGGER = LogUtils.getLogger();
private static final int MAX_TICKS_PER_UPDATE = 10;
@@ -436,7 +_,6 @@
@@ -432,7 +_,6 @@
}
}, Util.nonCriticalIoPool());
LOGGER.info("Setting user: {}", this.user.getName());
@@ -17,8 +17,8 @@
this.demo = gameConfig.game.demo;
this.allowsMultiplayer = !gameConfig.game.disableMultiplayer;
this.allowsChat = !gameConfig.game.disableChat;
@@ -522,14 +_,14 @@
LOGGER.error("Couldn't set icon", (Throwable)ioexception);
@@ -518,14 +_,14 @@
LOGGER.error("Couldn't set icon", (Throwable)var23);
}
+ // FORGE: Move mouse and keyboard handler setup further below
@@ -34,7 +34,7 @@
this.resourcePackRepository.reload();
this.options.loadSelectedResourcePacks(this.resourcePackRepository);
this.languageManager = new LanguageManager(this.options.languageCode, languageData -> {
@@ -617,6 +_,7 @@
@@ -613,6 +_,7 @@
this.particleResources = new ParticleResources();
this.resourceManager.registerReloadListener(this.particleResources);
this.particleEngine = new ParticleEngine(this.level, this.particleResources);
@@ -42,17 +42,17 @@
this.particleResources.onReload(this.particleEngine::clearParticles);
this.waypointStyles = new WaypointStyleManager();
this.resourceManager.registerReloadListener(this.waypointStyles);
@@ -638,6 +_,9 @@
@@ -634,6 +_,9 @@
this.resourceManager.registerReloadListener(this.gpuWarnlistManager);
this.resourceManager.registerReloadListener(this.regionalCompliancies);
this.gui = new Gui(this);
+ // FORGE: Moved keyboard and mouse handler setup below ingame gui creation to prevent NPEs in them.
+ this.mouseHandler.setup(this.window);
+ this.keyboardHandler.setup(this.window);
RealmsClient realmsclient = RealmsClient.getOrCreate(this);
this.realmsDataFetcher = new RealmsDataFetcher(realmsclient);
RealmsClient realmsClient = RealmsClient.getOrCreate(this);
this.realmsDataFetcher = new RealmsDataFetcher(realmsClient);
RenderSystem.setErrorCallback(this::onFullscreenError);
@@ -669,6 +_,7 @@
@@ -665,6 +_,7 @@
}
}
@@ -60,34 +60,33 @@
this.window.updateVsync(this.options.enableVsync().get());
this.window.updateRawMouseInput(this.options.rawMouseInput().get());
this.window.setAllowCursorChanges(this.options.allowCursorChanges().get());
@@ -695,14 +_,16 @@
@@ -690,14 +_,15 @@
.createReload(Util.backgroundExecutor().forName("resourceLoad"), this, RESOURCE_RELOAD_INITIAL_TASK, packs);
GameLoadTimesEvent.INSTANCE.beginStep(TelemetryProperty.LOAD_TIME_LOADING_OVERLAY_MS);
Minecraft.GameLoadCookie minecraft$gameloadcookie = new Minecraft.GameLoadCookie(realmsclient, gameConfig.quickPlay);
this.setOverlay(
- new LoadingOverlay(this, reloadinstance, maybeT -> Util.ifElse(maybeT, t -> this.rollbackResourcePacks(t, minecraft$gameloadcookie), () -> {
+ net.minecraftforge.fml.loading.ImmediateWindowHandler.<LoadingOverlay>loadingOverlay(
+ () -> this, () -> reloadinstance, maybeT -> Util.ifElse(maybeT, t -> this.rollbackResourcePacks(t, minecraft$gameloadcookie), () -> {
if (SharedConstants.IS_RUNNING_IN_IDE) {
this.selfTest();
}
Minecraft.GameLoadCookie loadCookie = new Minecraft.GameLoadCookie(realmsClient, gameConfig.quickPlay);
- this.setOverlay(new LoadingOverlay(this, reloadInstance, maybeT -> Util.ifElse(maybeT, t -> this.rollbackResourcePacks(t, loadCookie), () -> {
+ this.setOverlay(net.minecraftforge.fml.loading.ImmediateWindowHandler.<LoadingOverlay>loadingOverlay(() -> this, () ->reloadInstance, maybeT -> Util.ifElse(maybeT, t -> this.rollbackResourcePacks(t, loadCookie), () -> {
if (SharedConstants.IS_RUNNING_IN_IDE) {
this.selfTest();
}
this.reloadStateTracker.finishReload();
this.onResourceLoadFinished(minecraft$gameloadcookie);
- }), false)
+ net.minecraftforge.client.loading.ClientModLoader.completeModLoading();
+ }), false).get()
);
this.reloadStateTracker.finishReload();
this.onResourceLoadFinished(loadCookie);
- }), false));
+ net.minecraftforge.client.loading.ClientModLoader.completeModLoading();
+ }), false).get());
this.quickPlayLog = QuickPlayLog.of(gameConfig.quickPlay.logPath());
this.framerateLimitTracker = new FramerateLimitTracker(this.options, this);
@@ -822,6 +_,7 @@
StringBuilder stringbuilder = new StringBuilder("Minecraft");
this.fpsPieProfiler = new ContinuousProfiler(Util.timeSource, () -> this.fpsPieRenderTicks, this.framerateLimitTracker::isHeavilyThrottled);
@@ -816,6 +_,7 @@
StringBuilder builder = new StringBuilder("Minecraft");
if (checkModStatus().shouldReportAsModified()) {
stringbuilder.append("*");
+ stringbuilder.append(" Forge");
builder.append("*");
+ builder.append(" Forge");
}
stringbuilder.append(" ");
@@ -845,6 +_,8 @@
builder.append(" ");
@@ -839,6 +_,8 @@
}
private UserApiService createUserApiService(final YggdrasilAuthenticationService authService, final GameConfig config) {
@@ -96,7 +95,7 @@
return config.game.offlineDeveloperMode ? UserApiService.OFFLINE : authService.createUserApiService(config.user.user.getAccessToken());
}
@@ -857,7 +_,7 @@
@@ -851,7 +_,7 @@
}
private void rollbackResourcePacks(final Throwable t, final Minecraft.@Nullable GameLoadCookie loadCookie) {
@@ -105,7 +104,7 @@
this.clearResourcePacksOnError(t, null, loadCookie);
} else {
Util.throwAsRuntime(t);
@@ -1134,12 +_,6 @@
@@ -1128,12 +_,6 @@
LOGGER.error("setScreen called from non-game thread");
}
@@ -118,7 +117,7 @@
if (screen == null) {
if (this.clientLevelTeardownInProgress) {
throw new IllegalStateException("Trying to return to in-game GUI during disconnection");
@@ -1158,6 +_,23 @@
@@ -1152,6 +_,23 @@
}
}
@@ -142,33 +141,33 @@
this.screen = screen;
if (this.screen != null) {
this.screen.added();
@@ -1241,6 +_,7 @@
@@ -1235,6 +_,7 @@
Util.shutdownExecutors();
RenderSystem.getSamplerCache().close();
RenderSystem.getDevice().close();
+ net.minecraftforge.fml.config.ConfigTracker.forceUnload();
} catch (Throwable throwable) {
LOGGER.error("Shutdown failure!", throwable);
throw throwable;
@@ -1352,7 +_,9 @@
profilerfiller.popPush("gpuAsync");
} catch (Throwable var5) {
LOGGER.error("Shutdown failure!", var5);
throw var5;
@@ -1346,7 +_,9 @@
profiler.popPush("gpuAsync");
RenderSystem.executePendingTasks();
profilerfiller.pop();
profiler.pop();
+ net.minecraftforge.client.event.ForgeEventFactoryClient.onRenderTickStart(this.deltaTracker);
this.gameRenderer.render(this.deltaTracker, advanceGameTime);
+ net.minecraftforge.client.event.ForgeEventFactoryClient.onRenderTickEnd(this.deltaTracker);
profilerfiller.push("present");
profiler.push("present");
if (!this.gameRenderer.getGameRenderState().windowRenderState.isMinimized) {
this.mainRenderTarget.blitToScreen();
@@ -1456,6 +_,7 @@
this.window.setGuiScale(i);
@@ -1450,6 +_,7 @@
this.window.setGuiScale(guiScale);
if (this.screen != null) {
this.screen.resize(this.window.getGuiScaledWidth(), this.window.getGuiScaledHeight());
+ net.minecraftforge.client.ForgeHooksClient.resizeGuiLayers(this, this.window.getGuiScaledWidth(), this.window.getGuiScaledHeight());
}
this.mouseHandler.setIgnoreFirstMove();
@@ -1600,6 +_,7 @@
@@ -1601,6 +_,7 @@
}
public void stop() {
@@ -176,29 +175,29 @@
this.running = false;
}
@@ -1629,10 +_,18 @@
@@ -1630,10 +_,18 @@
if (down && this.hitResult != null && this.hitResult.getType() == HitResult.Type.BLOCK) {
BlockHitResult blockhitresult = (BlockHitResult)this.hitResult;
BlockPos blockpos = blockhitresult.getBlockPos();
- if (!this.level.getBlockState(blockpos).isAir()) {
+ if (!this.level.isEmptyBlock(blockpos)) {
BlockHitResult blockHit = (BlockHitResult)this.hitResult;
BlockPos pos = blockHit.getBlockPos();
- if (!this.level.getBlockState(pos).isAir()) {
+ if (!this.level.isEmptyBlock(pos)) {
+ var inputEvent = new net.minecraftforge.client.event.InputEvent.InteractionKeyMappingTriggered(0, this.options.keyAttack, InteractionHand.MAIN_HAND);
+ if (net.minecraftforge.client.event.InputEvent.InteractionKeyMappingTriggered.BUS.post(inputEvent)) {
+ if (inputEvent.shouldSwingHand()) {
+ this.level.addBreakingBlockEffect(blockpos, blockhitresult);
+ this.level.addBreakingBlockEffect(pos, blockHit);
+ this.player.swing(InteractionHand.MAIN_HAND);
+ }
+ return;
+ }
Direction direction = blockhitresult.getDirection();
- if (this.gameMode.continueDestroyBlock(blockpos, direction)) {
- this.level.addBreakingBlockEffect(blockpos, direction);
+ if (this.gameMode.continueDestroyBlock(blockpos, direction) && inputEvent.shouldSwingHand()) {
+ this.level.addBreakingBlockEffect(blockpos, blockhitresult);
Direction direction = blockHit.getDirection();
- if (this.gameMode.continueDestroyBlock(pos, direction)) {
- this.level.addBreakingBlockEffect(pos, direction);
+ if (this.gameMode.continueDestroyBlock(pos, direction) && inputEvent.shouldSwingHand()) {
+ this.level.addBreakingBlockEffect(pos, blockHit);
this.player.swing(InteractionHand.MAIN_HAND);
}
}
@@ -1675,6 +_,8 @@
@@ -1676,6 +_,8 @@
this.player.swing(InteractionHand.MAIN_HAND);
return true;
} else {
@@ -206,17 +205,17 @@
+ if (!net.minecraftforge.client.event.InputEvent.InteractionKeyMappingTriggered.BUS.post(inputEvent))
switch (this.hitResult.getType()) {
case ENTITY:
AttackRange attackrange = itemstack.get(DataComponents.ATTACK_RANGE);
@@ -1685,7 +_,7 @@
AttackRange customItemRange = heldItem.get(DataComponents.ATTACK_RANGE);
@@ -1686,7 +_,7 @@
case BLOCK:
BlockHitResult blockhitresult = (BlockHitResult)this.hitResult;
BlockPos blockpos = blockhitresult.getBlockPos();
- if (!this.level.getBlockState(blockpos).isAir()) {
+ if (!this.level.isEmptyBlock(blockpos)) {
this.gameMode.startDestroyBlock(blockpos, blockhitresult.getDirection());
if (this.level.getBlockState(blockpos).isAir()) {
flag = true;
@@ -1698,8 +_,10 @@
BlockHitResult blockHit = (BlockHitResult)this.hitResult;
BlockPos pos = blockHit.getBlockPos();
- if (!this.level.getBlockState(pos).isAir()) {
+ if (!this.level.isEmptyBlock(pos)) {
this.gameMode.startDestroyBlock(pos, blockHit.getDirection());
if (this.level.getBlockState(pos).isAir()) {
endAttack = true;
@@ -1699,8 +_,10 @@
}
this.player.resetAttackStrengthTicker();
@@ -225,83 +224,83 @@
+ if (inputEvent.shouldSwingHand())
this.player.swing(InteractionHand.MAIN_HAND);
return flag;
return endAttack;
}
@@ -1716,6 +_,12 @@
@@ -1717,6 +_,12 @@
}
for (InteractionHand interactionhand : InteractionHand.values()) {
+ var inputEvent = new net.minecraftforge.client.event.InputEvent.InteractionKeyMappingTriggered(1, this.options.keyUse, interactionhand);
for (InteractionHand hand : InteractionHand.values()) {
+ var inputEvent = new net.minecraftforge.client.event.InputEvent.InteractionKeyMappingTriggered(1, this.options.keyUse, hand);
+ if (net.minecraftforge.client.event.InputEvent.InteractionKeyMappingTriggered.BUS.post(inputEvent)) {
+ if (inputEvent.shouldSwingHand()) this.player.swing(interactionhand);
+ if (inputEvent.shouldSwingHand()) this.player.swing(hand);
+ return;
+ }
+
ItemStack itemstack = this.player.getItemInHand(interactionhand);
if (!itemstack.isItemEnabled(this.level.enabledFeatures())) {
ItemStack heldItem = this.player.getItemInHand(hand);
if (!heldItem.isItemEnabled(this.level.enabledFeatures())) {
return;
@@ -1734,7 +_,7 @@
&& this.gameMode.interact(this.player, entity, entityhitresult, interactionhand) instanceof InteractionResult.Success interactionresult$success2
)
{
- if (interactionresult$success2.swingSource() == InteractionResult.SwingSource.CLIENT) {
+ if (interactionresult$success2.swingSource() == InteractionResult.SwingSource.CLIENT && inputEvent.shouldSwingHand()) {
this.player.swing(interactionhand);
@@ -1733,7 +_,7 @@
if (this.player.isWithinEntityInteractionRange(entity, 0.0)
&& this.gameMode.interact(this.player, entity, entityHit, hand) instanceof InteractionResult.Success success) {
- if (success.swingSource() == InteractionResult.SwingSource.CLIENT) {
+ if (success.swingSource() == InteractionResult.SwingSource.CLIENT && inputEvent.shouldSwingHand()) {
this.player.swing(hand);
}
@@ -1746,7 +_,7 @@
int i = itemstack.getCount();
InteractionResult interactionresult = this.gameMode.useItemOn(this.player, interactionhand, blockhitresult);
if (interactionresult instanceof InteractionResult.Success interactionresult$success) {
- if (interactionresult$success.swingSource() == InteractionResult.SwingSource.CLIENT) {
+ if (interactionresult$success.swingSource() == InteractionResult.SwingSource.CLIENT && inputEvent.shouldSwingHand()) {
this.player.swing(interactionhand);
if (!itemstack.isEmpty() && (itemstack.getCount() != i || this.player.hasInfiniteMaterials())) {
this.gameRenderer.itemInHandRenderer.itemUsed(interactionhand);
@@ -1762,6 +_,9 @@
@@ -1745,7 +_,7 @@
int oldCount = heldItem.getCount();
InteractionResult useResult = this.gameMode.useItemOn(this.player, hand, blockHit);
if (useResult instanceof InteractionResult.Success success) {
- if (success.swingSource() == InteractionResult.SwingSource.CLIENT) {
+ if (success.swingSource() == InteractionResult.SwingSource.CLIENT && inputEvent.shouldSwingHand()) {
this.player.swing(hand);
if (!heldItem.isEmpty() && (heldItem.getCount() != oldCount || this.player.hasInfiniteMaterials())) {
this.gameRenderer.itemInHandRenderer.itemUsed(hand);
@@ -1761,6 +_,9 @@
}
}
+ if (itemstack.isEmpty() && (this.hitResult == null || this.hitResult.getType() == HitResult.Type.MISS))
+ net.minecraftforge.event.ForgeEventFactory.onRightClickEmpty(this.player, interactionhand);
+ if (heldItem.isEmpty() && (this.hitResult == null || this.hitResult.getType() == HitResult.Type.MISS))
+ net.minecraftforge.event.ForgeEventFactory.onRightClickEmpty(this.player, hand);
+
if (!itemstack.isEmpty()
&& this.gameMode.useItem(this.player, interactionhand) instanceof InteractionResult.Success interactionresult$success1) {
if (interactionresult$success1.swingSource() == InteractionResult.SwingSource.CLIENT) {
@@ -1791,6 +_,8 @@
if (!heldItem.isEmpty() && this.gameMode.useItem(this.player, hand) instanceof InteractionResult.Success success) {
if (success.swingSource() == InteractionResult.SwingSource.CLIENT) {
this.player.swing(hand);
@@ -1789,6 +_,8 @@
}
ProfilerFiller profilerfiller = Profiler.get();
ProfilerFiller profiler = Profiler.get();
+ net.minecraftforge.event.ForgeEventFactory.onPreClientTick();
+
profilerfiller.push("gui");
profiler.push("gui");
this.textInputManager.tick();
this.chatListener.tick();
@@ -1873,6 +_,7 @@
@@ -1871,6 +_,7 @@
this.tutorial.tick();
+ net.minecraftforge.event.ForgeEventFactory.onPreLevelTick(this.level, () -> true);
try {
this.level.tick(() -> true);
} catch (Throwable throwable1) {
@@ -1886,6 +_,7 @@
} catch (Throwable var6) {
@@ -1884,6 +_,7 @@
throw new ReportedException(crashreport1);
throw new ReportedException(report);
}
+ net.minecraftforge.event.ForgeEventFactory.onPostLevelTick(this.level, () -> true);
}
profilerfiller.popPush("animateTick");
@@ -1910,6 +_,7 @@
profilerfiller.popPush("keyboard");
profiler.popPush("animateTick");
@@ -1908,6 +_,7 @@
profiler.popPush("keyboard");
this.keyboardHandler.tick();
profilerfiller.pop();
profiler.pop();
+ net.minecraftforge.event.ForgeEventFactory.onPostClientTick();
}
private boolean isLevelRunningNormally() {
@@ -2140,6 +_,7 @@
@@ -2138,6 +_,7 @@
}
public void setLevel(final ClientLevel level) {
@@ -309,15 +308,15 @@
this.level = level;
this.updateLevelInEngines(level);
}
@@ -2201,6 +_,7 @@
IntegratedServer integratedserver = this.singleplayerServer;
@@ -2199,6 +_,7 @@
IntegratedServer server = this.singleplayerServer;
this.singleplayerServer = null;
this.gameRenderer.resetData();
+ net.minecraftforge.client.event.ForgeEventFactoryClient.firePlayerLogout(this.gameMode, this.player);
this.gameMode = null;
this.narrator.clear();
this.clientLevelTeardownInProgress = true;
@@ -2208,6 +_,7 @@
@@ -2206,6 +_,7 @@
try {
if (this.level != null) {
this.gui.onDisconnected();
@@ -325,19 +324,19 @@
}
this.level = null;
@@ -2222,6 +_,7 @@
@@ -2220,6 +_,7 @@
}
profilerfiller.pop();
profiler.pop();
+ net.minecraftforge.client.ForgeHooksClient.handleClientLevelClosing(this.level);
}
this.setScreenAndShow(screen);
@@ -2374,6 +_,7 @@
@@ -2372,6 +_,7 @@
private void pickBlockOrEntity() {
if (this.hitResult != null && this.hitResult.getType() != HitResult.Type.MISS) {
+ if (net.minecraftforge.client.event.ForgeEventFactoryClient.onClickInputPickBlock(this.options.keyPickItem)) return;
boolean flag = this.hasControlDown();
boolean includeData = this.hasControlDown();
switch (this.hitResult) {
case BlockHitResult blockhitresult:
case BlockHitResult blockHitResult:
@@ -4,65 +4,65 @@
this.activeButton = null;
}
+ if (net.minecraftforge.client.event.ForgeEventFactoryClient.onMouseButtonPre(mousebuttoninfo, action)) return;
+ if (net.minecraftforge.client.event.ForgeEventFactoryClient.onMouseButtonPre(buttonInfo, action)) return;
if (this.minecraft.getOverlay() == null) {
if (this.minecraft.screen == null) {
if (!this.mouseGrabbed && flag) {
if (!this.mouseGrabbed && pressed) {
@@ -106,7 +_,7 @@
&& i - this.lastClick.time() < 250L
&& currentTime - this.lastClick.time() < 250L
&& this.lastClick.screen() == screen
&& this.lastClickButton == mousebuttonevent.button();
- if (screen.mouseClicked(mousebuttonevent, flag1)) {
+ if (net.minecraftforge.client.event.ForgeEventFactoryClient.onScreenMouseClicked(screen, d0, d1, mousebuttonevent, flag1)) {
this.lastClick = new MouseHandler.LastClick(i, screen);
this.lastClickButton = mousebuttoninfo.button();
&& this.lastClickButton == event.button();
- if (screen.mouseClicked(event, doubleClick)) {
+ if (net.minecraftforge.client.event.ForgeEventFactoryClient.onScreenMouseClicked(screen, xm, ym, event, doubleClick)) {
this.lastClick = new MouseHandler.LastClick(currentTime, screen);
this.lastClickButton = buttonInfo.button();
return;
@@ -121,7 +_,7 @@
}
} else {
try {
- if (screen.mouseReleased(mousebuttonevent)) {
+ if (net.minecraftforge.client.event.ForgeEventFactoryClient.onScreenMouseReleased(screen, d0, d1, mousebuttonevent)) {
- if (screen.mouseReleased(event)) {
+ if (net.minecraftforge.client.event.ForgeEventFactoryClient.onScreenMouseReleased(screen, xm, ym, event)) {
return;
}
} catch (Throwable throwable) {
} catch (Throwable var17) {
@@ -151,6 +_,7 @@
KeyMapping.click(inputconstants$key);
KeyMapping.click(mouseKey);
}
}
+ net.minecraftforge.client.event.ForgeEventFactoryClient.onMouseButtonPost(mousebuttoninfo, action);
+ net.minecraftforge.client.event.ForgeEventFactoryClient.onMouseButtonPost(buttonInfo, action);
}
}
@@ -202,7 +_,9 @@
if (this.minecraft.screen != null) {
double d3 = this.getScaledXPos(this.minecraft.getWindow());
double d4 = this.getScaledYPos(this.minecraft.getWindow());
- this.minecraft.screen.mouseScrolled(d3, d4, d1, d2);
+ if (net.minecraftforge.client.event.ForgeEventFactoryClient.onScreenMouseScrollPre(this.minecraft.screen, d3, d4, d1, d2)) return;
+ if (this.minecraft.screen.mouseScrolled(d3, d4, d1, d2)) return;
+ net.minecraftforge.client.event.ForgeEventFactoryClient.onScreenMouseScrollPost(this.minecraft.screen, d3, d4, d1, d2);
double xm = this.getScaledXPos(this.minecraft.getWindow());
double ym = this.getScaledYPos(this.minecraft.getWindow());
- this.minecraft.screen.mouseScrolled(xm, ym, scaledXOffset, scaledYOffset);
+ if (net.minecraftforge.client.event.ForgeEventFactoryClient.onScreenMouseScrollPre(this.minecraft.screen, xm, ym, scaledXOffset, scaledYOffset)) return;
+ if (this.minecraft.screen.mouseScrolled(xm, ym, scaledXOffset, scaledYOffset)) return;
+ net.minecraftforge.client.event.ForgeEventFactoryClient.onScreenMouseScrollPost(this.minecraft.screen, xm, ym, scaledXOffset, scaledYOffset);
this.minecraft.screen.afterMouseAction();
} else if (this.minecraft.player != null) {
Vector2i vector2i = this.scrollWheelHandler.onMouseScroll(d1, d2);
Vector2i wheelXY = this.scrollWheelHandler.onMouseScroll(scaledXOffset, scaledYOffset);
@@ -211,6 +_,7 @@
}
int i = vector2i.y == 0 ? -vector2i.x : vector2i.y;
+ if (net.minecraftforge.client.event.ForgeEventFactoryClient.onMouseScroll(this, d1, d2)) return;
int wheel = wheelXY.y == 0 ? -wheelXY.x : wheelXY.y;
+ if (net.minecraftforge.client.event.ForgeEventFactoryClient.onMouseScroll(this, scaledXOffset, scaledYOffset)) return;
if (this.minecraft.player.isSpectator()) {
if (this.minecraft.gui.getSpectatorGui().isMenuActive()) {
this.minecraft.gui.getSpectatorGui().onMouseScrolled(-i);
@@ -315,7 +_,7 @@
double d5 = getScaledYPos(window, this.accumulatedDY);
this.minecraft.gui.getSpectatorGui().onMouseScrolled(-wheel);
@@ -317,7 +_,7 @@
double dy = getScaledYPos(window, this.accumulatedDY);
try {
- screen.mouseDragged(new MouseButtonEvent(d2, d3, this.activeButton), d4, d5);
+ net.minecraftforge.client.ForgeHooksClient.onScreenMouseDrag(screen, new MouseButtonEvent(d2, d3, this.activeButton), d4, d5);
} catch (Throwable throwable) {
CrashReport crashreport1 = CrashReport.forThrowable(throwable, "mouseDragged event handler");
screen.fillCrashDetails(crashreport1);
@@ -400,6 +_,14 @@
- screen.mouseDragged(new MouseButtonEvent(xm, ym, this.activeButton), dx, dy);
+ net.minecraftforge.client.ForgeHooksClient.onScreenMouseDrag(screen, new MouseButtonEvent(xm, ym, this.activeButton), dx, dy);
} catch (Throwable var19) {
CrashReport report = CrashReport.forThrowable(var19, "mouseDragged event handler");
screen.fillCrashDetails(report);
@@ -402,6 +_,14 @@
public double ypos() {
return this.ypos;
@@ -1,6 +1,6 @@
--- a/net/minecraft/client/Options.java
+++ b/net/minecraft/client/Options.java
@@ -921,6 +_,7 @@
@@ -929,6 +_,7 @@
);
public boolean syncWrites;
public boolean startedCleanly = true;
@@ -8,15 +8,15 @@
public static boolean isSoundDeviceDefault(final String deviceName) {
return deviceName.equals("");
@@ -1396,6 +_,7 @@
@@ -1404,6 +_,7 @@
}
public Options(final Minecraft minecraft, final File workingDirectory) {
+ setForgeKeybindProperties();
this.minecraft = minecraft;
this.optionsFile = new File(workingDirectory, "options.txt");
boolean flag = Runtime.getRuntime().maxMemory() >= 1000000000L;
@@ -1548,15 +_,28 @@
boolean largeDistances = Runtime.getRuntime().maxMemory() >= 1000000000L;
@@ -1556,15 +_,28 @@
this.startedCleanly = access.process("startedCleanly", this.startedCleanly);
access.process("musicToast", this.musicToast);
access.process("musicFrequency", this.musicFrequency);
@@ -26,27 +26,27 @@
+ // FORGE: split off to allow reloading keys after mod loading is done
+ private void processOptionsKeysOnly(Options.FieldAccess access) {
for (KeyMapping keymapping : this.keyMappings) {
- String s = keymapping.saveString();
+ String s = keymapping.saveString() + (keymapping.getKeyModifier() != net.minecraftforge.client.settings.KeyModifier.NONE ? ":" + keymapping.getKeyModifier() : "");
String s1 = access.process("key_" + keymapping.getName(), s);
if (!s.equals(s1)) {
keymapping.setKey(InputConstants.getKey(s1));
+ if (s1.indexOf(':') != -1) {
+ String[] pts = s1.split(":");
+ keymapping.setKeyModifierAndCode(net.minecraftforge.client.settings.KeyModifier.valueFromString(pts[1]), InputConstants.getKey(pts[0]));
for (KeyMapping keyMapping : this.keyMappings) {
- String currentValue = keyMapping.saveString();
+ String currentValue = keyMapping.saveString() + (keyMapping.getKeyModifier() != net.minecraftforge.client.settings.KeyModifier.NONE ? ":" + keyMapping.getKeyModifier() : "");
String newValue = access.process("key_" + keyMapping.getName(), currentValue);
if (!currentValue.equals(newValue)) {
keyMapping.setKey(InputConstants.getKey(newValue));
+ if (newValue.indexOf(':') != -1) {
+ String[] pts = newValue.split(":");
+ keyMapping.setKeyModifierAndCode(net.minecraftforge.client.settings.KeyModifier.valueFromString(pts[1]), InputConstants.getKey(pts[0]));
+ } else {
+ keymapping.setKeyModifierAndCode(net.minecraftforge.client.settings.KeyModifier.NONE, InputConstants.getKey(s1));
+ keyMapping.setKeyModifierAndCode(net.minecraftforge.client.settings.KeyModifier.NONE, InputConstants.getKey(newValue));
+ }
}
}
+ }
+ private void processOptionsEnd(Options.FieldAccess access) {
for (SoundSource soundsource : SoundSource.values()) {
access.process("soundCategory_" + soundsource.getName(), this.soundSourceVolumes.get(soundsource));
for (SoundSource source : SoundSource.values()) {
access.process("soundCategory_" + source.getName(), this.soundSourceVolumes.get(source));
}
@@ -1571,6 +_,10 @@
@@ -1579,6 +_,10 @@
}
public void load() {
@@ -57,64 +57,64 @@
try {
if (!this.optionsFile.exists()) {
return;
@@ -1590,7 +_,8 @@
@@ -1598,7 +_,8 @@
}
final CompoundTag compoundtag1 = this.dataFix(compoundtag);
final CompoundTag options = this.dataFix(rawOptions);
- this.processOptions(
+ java.util.function.Consumer<FieldAccess> processor = limited ? this::processOptionsKeysOnly : this::processOptions;
+ processor.accept(
new Options.FieldAccess() {
{
Objects.requireNonNull(Options.this);
@@ -1672,6 +_,17 @@
@@ -1680,6 +_,17 @@
);
compoundtag1.getString("fullscreenResolution").ifPresent(fullscreenResolution -> this.fullscreenVideoModeString = fullscreenResolution);
options.getString("fullscreenResolution").ifPresent(fullscreenResolution -> this.fullscreenVideoModeString = fullscreenResolution);
KeyMapping.resetMapping();
+
+ if (limited) {
+ this.unknownKeys.clear();
+ } else {
+ var knownKeys = Arrays.stream(this.keyMappings).map(k -> "key_" + k.getName()).collect(Collectors.toSet());
+ for (var entry : compoundtag1.entrySet()) {
+ for (var entry : options.entrySet()) {
+ if (entry.getKey().startsWith("key_") && entry.getValue().asString().isPresent() && !knownKeys.contains(entry.getKey()))
+ this.unknownKeys.put(entry.getKey(), entry.getValue().asString().get());
+ }
+ }
+
} catch (Exception exception) {
LOGGER.error("Failed to load options", (Throwable)exception);
} catch (Exception var7) {
LOGGER.error("Failed to load options", (Throwable)var7);
}
@@ -1699,6 +_,7 @@
@@ -1707,6 +_,7 @@
public void save() {
try (final PrintWriter printwriter = new PrintWriter(new OutputStreamWriter(new FileOutputStream(this.optionsFile), StandardCharsets.UTF_8))) {
printwriter.println("version:" + SharedConstants.getCurrentVersion().dataVersion().version());
try (final PrintWriter writer = new PrintWriter(new OutputStreamWriter(new FileOutputStream(this.optionsFile), StandardCharsets.UTF_8))) {
writer.println("version:" + SharedConstants.getCurrentVersion().dataVersion().version());
+ var seen = new java.util.HashSet<String>();
this.processOptions(
new Options.FieldAccess() {
{
@@ -1706,6 +_,7 @@
@@ -1714,6 +_,7 @@
}
public void writePrefix(final String name) {
+ seen.add(name);
printwriter.print(name);
printwriter.print(':');
writer.print(name);
writer.print(':');
}
@@ -1761,6 +_,12 @@
if (s != null) {
printwriter.println("fullscreenResolution:" + s);
@@ -1769,6 +_,12 @@
if (fullscreenVideoModeString != null) {
writer.println("fullscreenResolution:" + fullscreenVideoModeString);
}
+ // Forge add any unknown keys so that Mods can add keybindings between vanilla loading/saving its config.
+ for (var entry : this.unknownKeys.entrySet()) {
+ if (!seen.contains(entry.getKey()))
+ printwriter.println(entry.getKey() + ":" + entry.getValue());
+ writer.println(entry.getKey() + ":" + entry.getValue());
+ }
+
} catch (Exception exception) {
LOGGER.error("Failed to save options", (Throwable)exception);
} catch (Exception var6) {
LOGGER.error("Failed to save options", (Throwable)var6);
}
@@ -1798,6 +_,7 @@
@@ -1806,6 +_,7 @@
}
public void broadcastOptions() {
@@ -122,7 +122,7 @@
if (this.minecraft.player != null) {
this.minecraft.player.connection.broadcastClientInformation(this.buildPlayerInformation());
}
@@ -1915,6 +_,23 @@
@@ -1923,6 +_,23 @@
public static Component genericValueLabel(final Component caption, final int value) {
return genericValueLabel(caption, Component.literal(Integer.toString(value)));
@@ -1,10 +1,10 @@
--- a/net/minecraft/client/Screenshot.java
+++ b/net/minecraft/client/Screenshot.java
@@ -45,6 +_,13 @@
file2 = new File(file1, forceName);
@@ -44,6 +_,13 @@
file = new File(picDir, forceName);
}
+ var event = new net.minecraftforge.client.event.ScreenshotEvent(image, file2);
+ var event = new net.minecraftforge.client.event.ScreenshotEvent(image, file);
+ if (net.minecraftforge.client.event.ScreenshotEvent.BUS.post(event)) {
+ callback.accept(event.getCancelMessage());
+ return;
@@ -14,19 +14,19 @@
Util.ioPool()
.execute(
() -> {
@@ -52,10 +_,13 @@
NativeImage nativeimage = image;
@@ -51,10 +_,13 @@
NativeImage twrVar0$ = image;
try {
- image.writeToFile(file2);
- Component component = Component.literal(file2.getName())
- image.writeToFile(file);
- Component component = Component.literal(file.getName())
+ image.writeToFile(outputFile);
+ Component component = Component.literal(outputFile.getName())
.withStyle(ChatFormatting.UNDERLINE)
.withStyle(s -> s.withClickEvent(new ClickEvent.OpenFile(file2.getAbsoluteFile())));
.withStyle(s -> s.withClickEvent(new ClickEvent.OpenFile(file.getAbsoluteFile())));
+ if (event.getResultMessage() != null)
+ callback.accept(event.getResultMessage());
+ else
callback.accept(Component.translatable("screenshot.success", component));
} catch (Throwable throwable1) {
} catch (Throwable var7) {
if (image != null) {
@@ -1,15 +1,15 @@
--- a/net/minecraft/client/color/block/BlockColors.java
+++ b/net/minecraft/client/color/block/BlockColors.java
@@ -45,6 +_,7 @@
blockcolors.register(List.of(BlockTintSources.constant(-2046180)), Blocks.ATTACHED_MELON_STEM, Blocks.ATTACHED_PUMPKIN_STEM);
blockcolors.register(List.of(BlockTintSources.stem()), Blocks.MELON_STEM, Blocks.PUMPKIN_STEM);
blockcolors.register(List.of(BlockTintSources.constant(-9321636, -14647248)), Blocks.LILY_PAD);
+ net.minecraftforge.client.ForgeHooksClient.onBlockColorsInit(blockcolors);
return blockcolors;
colors.register(List.of(BlockTintSources.constant(-2046180)), Blocks.ATTACHED_MELON_STEM, Blocks.ATTACHED_PUMPKIN_STEM);
colors.register(List.of(BlockTintSources.stem()), Blocks.MELON_STEM, Blocks.PUMPKIN_STEM);
colors.register(List.of(BlockTintSources.constant(-9321636, -14647248)), Blocks.LILY_PAD);
+ net.minecraftforge.client.ForgeHooksClient.onBlockColorsInit(colors);
return colors;
}
@@ -57,6 +_,8 @@
return layer >= list.size() ? null : list.get(layer);
return layer >= layers.size() ? null : layers.get(layer);
}
+ /** @deprecated Register via {@link net.minecraftforge.client.event.RegisterColorHandlersEvent.Block} */
@@ -1,22 +1,22 @@
--- a/net/minecraft/client/data/Main.java
+++ b/net/minecraft/client/data/Main.java
@@ -28,13 +_,18 @@
OptionSpec<Void> optionspec1 = optionparser.accepts("client", "Include client generators");
OptionSpec<Void> optionspec2 = optionparser.accepts("all", "Include all generators");
OptionSpec<String> optionspec3 = optionparser.accepts("output", "Output folder").withRequiredArg().defaultsTo("generated");
+ OptionSpec<String> inputSpec = optionparser.accepts("input", "Input folder").withRequiredArg();
+ var loader = net.minecraftforge.data.loading.DatagenModLoader.setup(optionparser, true);
OptionSet optionset = optionparser.parse(args);
- if (!optionset.has(optionspec) && optionset.hasOptions()) {
+ if (!optionset.has(optionspec) && optionset.hasOptions() && loader.hasArgs(optionset)) {
+ var input = optionset.valuesOf(inputSpec).stream().map(Paths::get).toList();
Path path = Paths.get(optionspec3.value(optionset));
boolean flag = optionset.has(optionspec2);
boolean flag1 = flag || optionset.has(optionspec1);
OptionSpec<Void> clientOption = parser.accepts("client", "Include client generators");
OptionSpec<Void> allOption = parser.accepts("all", "Include all generators");
OptionSpec<String> outputOption = parser.accepts("output", "Output folder").withRequiredArg().defaultsTo("generated");
+ OptionSpec<String> inputSpec = parser.accepts("input", "Input folder").withRequiredArg();
+ var loader = net.minecraftforge.data.loading.DatagenModLoader.setup(parser, true);
OptionSet optionSet = parser.parse(args);
- if (!optionSet.has(helpOption) && optionSet.hasOptions()) {
+ if (!optionSet.has(helpOption) && optionSet.hasOptions() && loader.hasArgs(optionSet)) {
+ var input = optionSet.valuesOf(inputSpec).stream().map(Paths::get).toList();
Path output = Paths.get(outputOption.value(optionSet));
boolean allOptions = optionSet.has(allOption);
boolean client = allOptions || optionSet.has(clientOption);
Bootstrap.bootStrap();
ClientBootstrap.bootstrap();
+ if (!loader.run(optionset, path, input, flag, flag1, flag, flag))
+ if (!loader.run(optionSet, output, input, allOptions, client, allOptions, allOptions))
+ return;
DataGenerator datagenerator = new DataGenerator.Cached(path, SharedConstants.getCurrentVersion(), true);
addClientProviders(datagenerator, flag1);
datagenerator.run();
DataGenerator generator = new DataGenerator.Cached(output, SharedConstants.getCurrentVersion(), true);
addClientProviders(generator, client);
generator.run();
@@ -1,6 +1,6 @@
--- a/net/minecraft/client/data/models/ItemModelGenerators.java
+++ b/net/minecraft/client/data/models/ItemModelGenerators.java
@@ -922,6 +_,9 @@
@@ -909,6 +_,9 @@
this.declareCustomModelItem(Items.COD);
this.declareCustomModelItem(Items.FEATHER);
this.declareCustomModelItem(Items.LEAD);
@@ -1,22 +1,22 @@
--- a/net/minecraft/client/data/models/ModelProvider.java
+++ b/net/minecraft/client/data/models/ModelProvider.java
@@ -44,11 +_,11 @@
@@ -43,11 +_,11 @@
@Override
public CompletableFuture<?> run(final CachedOutput cache) {
- ModelProvider.ItemInfoCollector modelprovider$iteminfocollector = new ModelProvider.ItemInfoCollector();
- ModelProvider.BlockStateGeneratorCollector modelprovider$blockstategeneratorcollector = new ModelProvider.BlockStateGeneratorCollector();
+ ModelProvider.ItemInfoCollector modelprovider$iteminfocollector = new ModelProvider.ItemInfoCollector(this::getKnownItems);
+ ModelProvider.BlockStateGeneratorCollector modelprovider$blockstategeneratorcollector = new ModelProvider.BlockStateGeneratorCollector(this::getKnownBlocks);
ModelProvider.SimpleModelCollector modelprovider$simplemodelcollector = new ModelProvider.SimpleModelCollector();
- new BlockModelGenerators(modelprovider$blockstategeneratorcollector, modelprovider$iteminfocollector, modelprovider$simplemodelcollector).run();
- new ItemModelGenerators(modelprovider$iteminfocollector, modelprovider$simplemodelcollector).run();
+ getBlockModelGenerators(modelprovider$blockstategeneratorcollector, modelprovider$iteminfocollector, modelprovider$simplemodelcollector).run();
+ getItemModelGenerators(modelprovider$iteminfocollector, modelprovider$simplemodelcollector).run();
modelprovider$blockstategeneratorcollector.validate();
modelprovider$iteminfocollector.finalizeAndValidate();
- ModelProvider.ItemInfoCollector itemModels = new ModelProvider.ItemInfoCollector();
- ModelProvider.BlockStateGeneratorCollector blockStateGenerators = new ModelProvider.BlockStateGeneratorCollector();
+ ModelProvider.ItemInfoCollector itemModels = new ModelProvider.ItemInfoCollector(this::getKnownItems);
+ ModelProvider.BlockStateGeneratorCollector blockStateGenerators = new ModelProvider.BlockStateGeneratorCollector(this::getKnownBlocks);
ModelProvider.SimpleModelCollector simpleModels = new ModelProvider.SimpleModelCollector();
- new BlockModelGenerators(blockStateGenerators, itemModels, simpleModels).run();
- new ItemModelGenerators(itemModels, simpleModels).run();
+ getBlockModelGenerators(blockStateGenerators, itemModels, simpleModels).run();
+ getItemModelGenerators(itemModels, simpleModels).run();
blockStateGenerators.validate();
itemModels.finalizeAndValidate();
return CompletableFuture.allOf(
@@ -58,6 +_,22 @@
@@ -57,6 +_,22 @@
);
}
@@ -39,7 +39,7 @@
@Override
public final String getName() {
return "Model Definitions";
@@ -66,6 +_,15 @@
@@ -65,6 +_,15 @@
@OnlyIn(Dist.CLIENT)
public static class BlockStateGeneratorCollector implements Consumer<BlockModelDefinitionGenerator> {
private final Map<Block, BlockModelDefinitionGenerator> generators = new HashMap<>();
@@ -55,17 +55,17 @@
public void accept(final BlockModelDefinitionGenerator generator) {
Block block = generator.block();
@@ -76,8 +_,7 @@
@@ -75,8 +_,7 @@
}
public void validate() {
- List<Identifier> list = BuiltInRegistries.BLOCK
- List<Identifier> missingDefinitions = BuiltInRegistries.BLOCK
- .listElements()
+ List<Identifier> list = known.get().map(Block::builtInRegistryHolder)
+ List<Identifier> missingDefinitions = known.get().map(Block::builtInRegistryHolder)
.filter(e -> !this.generators.containsKey(e.value()))
.map(e -> e.key().identifier())
.toList();
@@ -97,6 +_,15 @@
@@ -96,6 +_,15 @@
public static class ItemInfoCollector implements ItemModelOutput {
private final Map<Item, ClientItem> itemInfos = new HashMap<>();
private final Map<Item, Item> copies = new HashMap<>();
@@ -81,7 +81,7 @@
@Override
public void accept(final Item item, final ItemModel.Unbaked model, final ClientItem.Properties properties) {
@@ -115,7 +_,7 @@
@@ -114,7 +_,7 @@
this.copies.put(acceptor, donor);
}
@@ -89,23 +89,23 @@
+ public void generateDefaultBlockModels() {
BuiltInRegistries.ITEM.forEach(item -> {
if (!this.copies.containsKey(item)) {
if (item instanceof BlockItem blockitem && !this.itemInfos.containsKey(blockitem)) {
@@ -124,6 +_,8 @@
if (item instanceof BlockItem blockItem && !this.itemInfos.containsKey(blockItem)) {
@@ -123,6 +_,8 @@
}
}
});
+ }
+ public void finalizeAndValidate() {
this.copies.forEach((acceptor, donor) -> {
ClientItem clientitem = this.itemInfos.get(donor);
if (clientitem == null) {
@@ -132,8 +_,8 @@
this.register(acceptor, clientitem);
ClientItem donorInfo = this.itemInfos.get(donor);
if (donorInfo == null) {
@@ -131,8 +_,8 @@
this.register(acceptor, donorInfo);
}
});
- List<Identifier> list = BuiltInRegistries.ITEM
- List<Identifier> missingDefinitions = BuiltInRegistries.ITEM
- .listElements()
+ List<Identifier> list = known.get()
+ List<Identifier> missingDefinitions = known.get()
+ .map(item -> item.builtInRegistryHolder())
.filter(e -> !this.itemInfos.containsKey(e.value()))
.map(e -> e.key().identifier())
@@ -29,74 +29,87 @@
this.extractCameraOverlays(graphics, deltaTracker);
this.extractCrosshair(graphics, deltaTracker);
@@ -385,7 +_,7 @@
int i = Mth.floor(this.minecraft.mouseHandler.getScaledXPos(window));
int j = Mth.floor(this.minecraft.mouseHandler.getScaledYPos(window));
int mouseX = Mth.floor(this.minecraft.mouseHandler.getScaledXPos(window));
int mouseY = Mth.floor(this.minecraft.mouseHandler.getScaledYPos(window));
graphics.nextStratum();
- this.chat.extractRenderState(graphics, this.getFont(), this.tickCount, i, j, ChatComponent.DisplayMode.BACKGROUND, false);
+ net.minecraftforge.client.ForgeHooksClient.onCustomizeChatEvent(graphics, this.chat, window, i, j, this.tickCount, this.getFont());
- this.chat.extractRenderState(graphics, this.getFont(), this.tickCount, mouseX, mouseY, ChatComponent.DisplayMode.BACKGROUND, false);
+ net.minecraftforge.client.ForgeHooksClient.onCustomizeChatEvent(graphics, this.chat, window, mouseX, mouseY, this.tickCount, this.getFont());
}
}
@@ -475,6 +_,8 @@
@@ -478,6 +_,8 @@
for (MobEffectInstance mobeffectinstance : Ordering.natural().reverse().sortedCopy(collection)) {
Holder<MobEffect> holder = mobeffectinstance.getEffect();
+ var renderer = net.minecraftforge.client.extensions.common.IClientMobEffectExtensions.of(mobeffectinstance);
+ if (!renderer.isVisibleInGui(mobeffectinstance)) continue;
if (mobeffectinstance.showIcon()) {
int k = graphics.guiWidth();
int l = 1;
@@ -505,6 +_,7 @@
for (MobEffectInstance instance : Ordering.natural().reverse().sortedCopy(activeEffects)) {
Holder<MobEffect> effect = instance.getEffect();
+ var renderer = net.minecraftforge.client.extensions.common.IClientMobEffectExtensions.of(instance);
+ if (!renderer.isVisibleInGui(instance)) continue;
if (instance.showIcon()) {
int x = graphics.guiWidth();
int y = 1;
@@ -508,6 +_,7 @@
}
}
+ if (renderer.extractGuiIcon(mobeffectinstance, this, graphics, k, l, 0, f)) continue;
graphics.blitSprite(RenderPipelines.GUI_TEXTURED, getMobEffectSprite(holder), k + 3, l + 3, 18, 18, ARGB.white(f));
+ if (renderer.extractGuiIcon(instance, this, graphics, x, y, 0, alpha)) continue;
graphics.blitSprite(RenderPipelines.GUI_TEXTURED, getMobEffectSprite(effect), x + 3, y + 3, 18, 18, ARGB.white(alpha));
}
}
@@ -605,6 +_,10 @@
@@ -610,15 +_,37 @@
}
public void extractSelectedItemName(final GuiGraphicsExtractor graphics) {
+ renderSelectedItemName(graphics, 0);
+ }
+
+ // Forge: Maintain vanilla parity during FLD#BACKGROUND
+ public final void updateContextualInfo(final GuiGraphicsExtractor 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().extractBackground(graphics, deltaTracker);
+ }
+ // Forge: Same as above but for FLD#CONTEXTUAL_INFO
+ public final void extractContextualInfoState(final GuiGraphicsExtractor graphics, DeltaTracker deltaTracker) {
+ this.contextualInfoBar.getValue().extractRenderState(graphics, deltaTracker);
+ }
+
+ public void renderSelectedItemName(final GuiGraphicsExtractor graphics, int yShift) {
if (this.toolHighlightTimer > 0 && !this.lastToolHighlight.isEmpty()) {
MutableComponent mutablecomponent = Component.empty()
.append(this.lastToolHighlight.getHoverName())
@@ -613,9 +_,13 @@
mutablecomponent.withStyle(ChatFormatting.ITALIC);
MutableComponent str = Component.empty().append(this.lastToolHighlight.getHoverName()).withStyle(this.lastToolHighlight.getRarity().color());
if (this.lastToolHighlight.has(DataComponents.CUSTOM_NAME)) {
str.withStyle(ChatFormatting.ITALIC);
}
- int i = this.getFont().width(mutablecomponent);
+ Component highlightTip = this.lastToolHighlight.getHighlightTip(mutablecomponent);
- int strWidth = this.getFont().width(str);
+ Component highlightTip = this.lastToolHighlight.getHighlightTip(str);
+ Font font = net.minecraftforge.client.extensions.common.IClientItemExtensions.of(lastToolHighlight).getFont(lastToolHighlight, net.minecraftforge.client.extensions.common.IClientItemExtensions.FontContext.SELECTED_ITEM_NAME);
+ if (font == null)
+ font = this.getFont();
+ int i = font.width(highlightTip);
int j = (graphics.guiWidth() - i) / 2;
- int k = graphics.guiHeight() - 59;
+ int k = graphics.guiHeight() - Math.max(yShift, 59);
+ int strWidth = font.width(highlightTip);
int x = (graphics.guiWidth() - strWidth) / 2;
- int y = graphics.guiHeight() - 59;
+ int y = graphics.guiHeight() - Math.max(yShift, 59);
if (!this.minecraft.gameMode.canHurtPlayer()) {
k += 14;
y += 14;
}
@@ -626,7 +_,7 @@
@@ -629,7 +_,7 @@
}
if (l > 0) {
- graphics.textWithBackdrop(this.getFont(), mutablecomponent, j, k, i, ARGB.white(l));
+ graphics.textWithBackdrop(font, highlightTip, j, k, i, ARGB.white(l));
if (alpha > 0) {
- graphics.textWithBackdrop(this.getFont(), str, x, y, strWidth, ARGB.white(alpha));
+ graphics.textWithBackdrop(font, highlightTip, x, y, strWidth, ARGB.white(alpha));
}
}
}
@@ -1165,7 +_,9 @@
@@ -1195,7 +_,9 @@
this.toolHighlightTimer = 0;
} else if (this.lastToolHighlight.isEmpty()
|| !itemstack.is(this.lastToolHighlight.getItem())
- || !itemstack.getHoverName().equals(this.lastToolHighlight.getHoverName())) {
+ || !itemstack.getHoverName().equals(this.lastToolHighlight.getHoverName())
+ || !itemstack.getHighlightTip(itemstack.getHoverName()).equals(lastToolHighlight.getHighlightTip(lastToolHighlight.getHoverName()))
|| !selected.is(this.lastToolHighlight.getItem())
- || !selected.getHoverName().equals(this.lastToolHighlight.getHoverName())) {
+ || !selected.getHoverName().equals(this.lastToolHighlight.getHoverName())
+ || !selected.getHighlightTip(selected.getHoverName()).equals(lastToolHighlight.getHighlightTip(lastToolHighlight.getHoverName()))
+ ) {
this.toolHighlightTimer = (int)(40.0 * this.minecraft.options.notificationDisplayTime().get());
} else if (this.toolHighlightTimer > 0) {
@@ -9,15 +9,15 @@
private static final int EXTRA_SPACE_AFTER_FIRST_TOOLTIP_LINE = 2;
private final Minecraft minecraft;
private final Matrix3x2fStack pose;
@@ -867,6 +_,7 @@
CrashReport crashreport = CrashReport.forThrowable(throwable, "Rendering item");
CrashReportCategory crashreportcategory = crashreport.addCategory("Item being rendered");
crashreportcategory.setDetail("Item Type", () -> String.valueOf(itemStack.getItem()));
+ crashreportcategory.setDetail("Registry Name", () -> String.valueOf(net.minecraftforge.registries.ForgeRegistries.ITEMS.getKey(itemStack.getItem())));
crashreportcategory.setDetail("Item Components", () -> String.valueOf(itemStack.getComponents()));
crashreportcategory.setDetail("Item Foil", () -> String.valueOf(itemStack.hasFoil()));
throw new ReportedException(crashreport);
@@ -893,6 +_,7 @@
@@ -914,6 +_,7 @@
CrashReport report = CrashReport.forThrowable(var11, "Rendering item");
CrashReportCategory category = report.addCategory("Item being rendered");
category.setDetail("Item Type", () -> String.valueOf(itemStack.getItem()));
+ category.setDetail("Registry Name", () -> String.valueOf(net.minecraftforge.registries.ForgeRegistries.ITEMS.getKey(itemStack.getItem())));
category.setDetail("Item Components", () -> String.valueOf(itemStack.getComponents()));
category.setDetail("Item Foil", () -> String.valueOf(itemStack.hasFoil()));
throw new ReportedException(report);
@@ -940,6 +_,7 @@
this.itemCooldown(itemStack, x, y);
this.itemCount(font, itemStack, x, y, countText);
this.pose.popMatrix();
@@ -25,7 +25,7 @@
}
}
@@ -1069,16 +_,25 @@
@@ -1112,16 +_,25 @@
this.setTooltipForNextFrame(this.minecraft.font, formattedCharSequences, DefaultTooltipPositioner.INSTANCE, x, y, false);
}
@@ -51,17 +51,20 @@
public void setTooltipForNextFrame(
final Font font,
final List<Component> texts,
@@ -1087,8 +_,7 @@
@@ -1130,11 +_,7 @@
final int yo,
final @Nullable Identifier style
) {
- List<ClientTooltipComponent> list = texts.stream().map(Component::getVisualOrderText).map(ClientTooltipComponent::create).collect(Util.toMutableList());
- optionalImage.ifPresent(image -> list.add(list.isEmpty() ? 0 : 1, ClientTooltipComponent.create(image)));
+ List<ClientTooltipComponent> list = net.minecraftforge.client.ForgeHooksClient.gatherTooltipComponents(this.tooltipStack, texts, optionalImage, xo, guiWidth(), guiHeight(), font);
this.setTooltipForNextFrameInternal(font, list, xo, yo, DefaultTooltipPositioner.INSTANCE, style, false);
- List<ClientTooltipComponent> components = texts.stream()
- .map(Component::getVisualOrderText)
- .map(ClientTooltipComponent::create)
- .collect(Util.toMutableList());
- optionalImage.ifPresent(image -> components.add(components.isEmpty() ? 0 : 1, ClientTooltipComponent.create(image)));
+ List<ClientTooltipComponent> components = net.minecraftforge.client.ForgeHooksClient.gatherTooltipComponents(this.tooltipStack, texts, optionalImage, xo, guiWidth(), guiHeight(), font);
this.setTooltipForNextFrameInternal(font, components, xo, yo, DefaultTooltipPositioner.INSTANCE, style, false);
}
@@ -1116,7 +_,22 @@
@@ -1162,7 +_,22 @@
}
public void setComponentTooltipForNextFrame(final Font font, final List<Component> lines, final int xo, final int yo) {
@@ -85,7 +88,7 @@
}
public void setComponentTooltipForNextFrame(final Font font, final List<Component> lines, final int xo, final int yo, final @Nullable Identifier style) {
@@ -1167,10 +_,16 @@
@@ -1213,10 +_,16 @@
) {
if (!lines.isEmpty()) {
if (this.deferredTooltip == null || replaceExisting) {
@@ -103,7 +106,7 @@
public void tooltip(
final Font font,
@@ -1178,13 +_,16 @@
@@ -1224,13 +_,16 @@
final int xo,
final int yo,
final ClientTooltipPositioner positioner,
@@ -113,48 +116,48 @@
) {
+ var preEvent = net.minecraftforge.client.ForgeHooksClient.onRenderTooltipPre(itemstack, this, xo, yo, guiWidth(), guiHeight(), lines, font, positioner, style);
+ if (preEvent == null) return;
int i = 0;
int j = lines.size() == 1 ? -2 : 0;
int textWidth = 0;
int tempHeight = lines.size() == 1 ? -2 : 0;
for (ClientTooltipComponent clienttooltipcomponent : lines) {
- int k = clienttooltipcomponent.getWidth(font);
+ int k = clienttooltipcomponent.getWidth(preEvent.getFont());
if (k > i) {
i = k;
for (ClientTooltipComponent line : lines) {
- int lineWidth = line.getWidth(font);
+ int lineWidth = line.getWidth(preEvent.getFont());
if (lineWidth > textWidth) {
textWidth = lineWidth;
}
@@ -1194,25 +_,25 @@
@@ -1240,25 +_,25 @@
int l1 = i;
int i2 = j;
- Vector2ic vector2ic = positioner.positionTooltip(this.guiWidth(), this.guiHeight(), xo, yo, i, j);
+ Vector2ic vector2ic = positioner.positionTooltip(this.guiWidth(), this.guiHeight(), preEvent.getX(), preEvent.getY(), i, j);
int l = vector2ic.x();
int i1 = vector2ic.y();
int w = textWidth;
int h = tempHeight;
- Vector2ic positionedTooltip = positioner.positionTooltip(this.guiWidth(), this.guiHeight(), xo, yo, textWidth, tempHeight);
+ Vector2ic positionedTooltip = positioner.positionTooltip(this.guiWidth(), this.guiHeight(), preEvent.getX(), preEvent.getY(), textWidth, tempHeight);
int x = positionedTooltip.x();
int y = positionedTooltip.y();
this.pose.pushMatrix();
- TooltipRenderUtil.extractTooltipBackground(this, l, i1, i, j, style);
+ TooltipRenderUtil.extractTooltipBackground(this, l, i1, i, j, preEvent.getBackground());
int j1 = i1;
- TooltipRenderUtil.extractTooltipBackground(this, x, y, textWidth, tempHeight, style);
+ TooltipRenderUtil.extractTooltipBackground(this, x, y, textWidth, tempHeight, preEvent.getBackground());
int localY = y;
for (int k1 = 0; k1 < lines.size(); k1++) {
ClientTooltipComponent clienttooltipcomponent1 = lines.get(k1);
- clienttooltipcomponent1.extractText(this, font, l, j1);
- j1 += clienttooltipcomponent1.getHeight(font) + (k1 == 0 ? 2 : 0);
+ clienttooltipcomponent1.extractText(this, preEvent.getFont(), l, j1);
+ j1 += clienttooltipcomponent1.getHeight(preEvent.getFont()) + (k1 == 0 ? 2 : 0);
for (int i = 0; i < lines.size(); i++) {
ClientTooltipComponent line = lines.get(i);
- line.extractText(this, font, x, localY);
- localY += line.getHeight(font) + (i == 0 ? 2 : 0);
+ line.extractText(this, preEvent.getFont(), x, localY);
+ localY += line.getHeight(preEvent.getFont()) + (i == 0 ? 2 : 0);
}
j1 = i1;
localY = y;
for (int j2 = 0; j2 < lines.size(); j2++) {
ClientTooltipComponent clienttooltipcomponent2 = lines.get(j2);
- clienttooltipcomponent2.extractImage(font, l, j1, l1, i2, this);
- j1 += clienttooltipcomponent2.getHeight(font) + (j2 == 0 ? 2 : 0);
+ clienttooltipcomponent2.extractImage(preEvent.getFont(), l, j1, l1, i2, this);
+ j1 += clienttooltipcomponent2.getHeight(preEvent.getFont()) + (j2 == 0 ? 2 : 0);
for (int i = 0; i < lines.size(); i++) {
ClientTooltipComponent line = lines.get(i);
- line.extractImage(font, x, localY, w, h, this);
- localY += line.getHeight(font) + (i == 0 ? 2 : 0);
+ line.extractImage(preEvent.getFont(), x, localY, w, h, this);
+ localY += line.getHeight(preEvent.getFont()) + (i == 0 ? 2 : 0);
}
this.pose.popMatrix();
@@ -1287,6 +_,14 @@
@@ -1333,6 +_,14 @@
private ActiveTextCollector.Parameters createDefaultTextParameters(final float opacity) {
return new ActiveTextCollector.Parameters(new Matrix3x2f(this.pose), opacity, this.scissorStack.peek());
@@ -2,19 +2,19 @@
+++ b/net/minecraft/client/gui/components/BossHealthOverlay.java
@@ -68,13 +_,16 @@
for (LerpingBossEvent lerpingbossevent : this.events.values()) {
int k = i / 2 - 91;
+ var event = net.minecraftforge.client.ForgeHooksClient.onCustomizeBossEventProgress(graphics, this.minecraft.getWindow(), lerpingbossevent, k, j, 10 + this.minecraft.font.lineHeight);
+ if (event != null) {
this.extractBar(graphics, k, j, lerpingbossevent);
Component component = lerpingbossevent.getName();
int l = this.minecraft.font.width(component);
int i1 = i / 2 - l / 2;
int j1 = j - 9;
graphics.text(this.minecraft.font, component, i1, j1, -1);
- j += 10 + 9;
for (LerpingBossEvent event : this.events.values()) {
int xLeft = screenWidth / 2 - 91;
+ var progressEvent = net.minecraftforge.client.ForgeHooksClient.onCustomizeBossEventProgress(graphics, this.minecraft.getWindow(), event, xLeft, yOffset, 10 + this.minecraft.font.lineHeight);
+ if (progressEvent != null) {
this.extractBar(graphics, xLeft, yOffset, event);
Component msg = event.getName();
int width = this.minecraft.font.width(msg);
int x = screenWidth / 2 - width / 2;
int y = yOffset - 9;
graphics.text(this.minecraft.font, msg, x, y, -1);
- yOffset += 10 + 9;
+ }
+ j += event.getIncrement();
if (j >= graphics.guiHeight() / 3) {
+ yOffset += progressEvent.getIncrement();
if (yOffset >= graphics.guiHeight() / 3) {
break;
}
@@ -2,9 +2,9 @@
+++ b/net/minecraft/client/gui/components/debug/DebugEntryLookingAtEntity.java
@@ -29,6 +_,7 @@
if (entity != null) {
list.add(ChatFormatting.UNDERLINE + "Targeted Entity");
list.add(entity.typeHolder().getRegisteredName());
+ entity.getType().builtInRegistryHolder().tags().forEach(t -> list.add("#" + t.location()));
result.add(ChatFormatting.UNDERLINE + "Targeted Entity");
result.add(entity.typeHolder().getRegisteredName());
+ entity.getType().builtInRegistryHolder().tags().forEach(t -> result.add("#" + t.location()));
}
displayer.addToGroup(GROUP, list);
displayer.addToGroup(GROUP, result);
@@ -1,7 +1,7 @@
--- a/net/minecraft/client/gui/render/GuiRenderer.java
+++ b/net/minecraft/client/gui/render/GuiRenderer.java
@@ -131,6 +_,7 @@
builder.put((Class<? extends PictureInPictureRenderState>)pictureinpicturerenderer.getRenderStateClass(), pictureinpicturerenderer);
@@ -130,6 +_,7 @@
builder.put((Class<? extends PictureInPictureRenderState>)pictureInPictureRenderer.getRenderStateClass(), pictureInPictureRenderer);
}
+ net.minecraftforge.client.ForgeHooksClient.onRegisterPictureInPictureRenderers(pictureInPictureRenderers, bufferSource, builder);
@@ -3,7 +3,7 @@
@@ -114,6 +_,8 @@
}
if (optional.isEmpty()) {
if (resolvedAddress.isEmpty()) {
+ ConnectScreen.LOGGER.error("Couldn't connect to server: Unknown host \"{}\"", hostAndPort.getHost());
+ net.minecraftforge.network.DualStackUtils.logInitialPreferences();
minecraft.execute(
@@ -8,24 +8,24 @@
+
@Override
public void extractRenderState(final GuiGraphicsExtractor graphics, final int mouseX, final int mouseY, final float a) {
int i = graphics.guiWidth();
int width = graphics.guiWidth();
@@ -103,6 +_,7 @@
f2 = 1.0F;
logoAlpha = 1.0F;
}
+ if (extractContentRenderState(graphics, f2)) {
int k2 = (int)(graphics.guiWidth() * 0.5);
int i1 = (int)(graphics.guiHeight() * 0.5);
double d0 = Math.min(graphics.guiWidth() * 0.75, (double)graphics.guiHeight()) * 0.25;
@@ -118,6 +_,7 @@
if (f < 1.0F) {
this.extractProgressBar(graphics, i / 2 - k1, i2 - 5, i / 2 + k1, i2 + 5, 1.0F - Mth.clamp(f, 0.0F, 1.0F));
+ if (extractContentRenderState(graphics, logoAlpha)) {
int contentX = (int)(graphics.guiWidth() * 0.5);
int logoY = (int)(graphics.guiHeight() * 0.5);
double logoHeight = Math.min(graphics.guiWidth() * 0.75, (double)graphics.guiHeight()) * 0.25;
@@ -148,6 +_,7 @@
graphics, width / 2 - logoWidthHalf, barY - 5, width / 2 + logoWidthHalf, barY + 5, 1.0F - Mth.clamp(fadeOutAnim, 0.0F, 1.0F)
);
}
+ }
if (f >= 2.0F) {
if (fadeOutAnim >= 2.0F) {
this.minecraft.setOverlay(null);
@@ -127,6 +_,7 @@
@@ -157,6 +_,7 @@
@Override
public void tick() {
if (this.fadeOutStart == -1L && this.reload.isDone() && this.isReadyToFadeOut()) {
@@ -33,8 +33,8 @@
try {
this.reload.checkExceptions();
this.onFinish.accept(Optional.empty());
@@ -134,7 +_,6 @@
this.onFinish.accept(Optional.of(throwable));
@@ -164,7 +_,6 @@
this.onFinish.accept(Optional.of(var2));
}
- this.fadeOutStart = Util.getMillis();
@@ -8,13 +8,13 @@
+ }
+
+ public static <T extends AbstractContainerMenu> java.util.Optional<ScreenConstructor<T, ?>> getScreenFactory(@Nullable MenuType<T> type, Minecraft minecraft, int containerId, Component title) {
MenuScreens.ScreenConstructor<T, ?> screenconstructor = getConstructor(type);
if (screenconstructor == null) {
MenuScreens.ScreenConstructor<T, ?> constructor = getConstructor(type);
if (constructor == null) {
LOGGER.warn("Failed to create screen for menu type: {}", BuiltInRegistries.MENU.getKey(type));
+ return java.util.Optional.empty();
} else {
- screenconstructor.fromPacket(title, type, minecraft, containerId);
+ return java.util.Optional.of(screenconstructor);
- constructor.fromPacket(title, type, minecraft, containerId);
+ return java.util.Optional.of(constructor);
}
}
@@ -1,10 +1,10 @@
--- a/net/minecraft/client/gui/screens/PauseScreen.java
+++ b/net/minecraft/client/gui/screens/PauseScreen.java
@@ -104,6 +_,7 @@
@@ -102,6 +_,7 @@
} else {
gridlayout$rowhelper.addChild(this.openScreenButton(PLAYER_REPORTING, () -> new SocialInteractionsScreen(this)));
helper.addChild(this.openScreenButton(PLAYER_REPORTING, () -> new SocialInteractionsScreen(this)));
}
+ gridlayout$rowhelper.addChild(Button.builder(Component.translatable("fml.menu.mods"), button -> this.minecraft.setScreen(new net.minecraftforge.client.gui.ModListScreen(this))).width(BUTTON_WIDTH_FULL).build(), 2);
+ helper.addChild(Button.builder(Component.translatable("fml.menu.mods"), button -> this.minecraft.setScreen(new net.minecraftforge.client.gui.ModListScreen(this))).width(BUTTON_WIDTH_FULL).build(), 2);
this.disconnectButton = gridlayout$rowhelper.addChild(
this.disconnectButton = helper.addChild(
Button.builder(
@@ -9,47 +9,47 @@
public TitleScreen() {
this(false);
@@ -105,11 +_,15 @@
int j = this.width - i - 2;
int k = 24;
int l = this.height / 4 + 48;
int copyrightX = this.width - copyrightWidth - 2;
int spacing = 24;
int topPos = this.height / 4 + 48;
+ Button modButton = null;
if (this.minecraft.isDemo()) {
l = this.createDemoMenuOptions(l, 24);
topPos = this.createDemoMenuOptions(topPos, 24);
} else {
l = this.createNormalMenuOptions(l, 24);
topPos = this.createNormalMenuOptions(topPos, 24);
+ modButton = this.addRenderableWidget(Button.builder(Component.translatable("fml.menu.mods"), button -> this.minecraft.setScreen(new net.minecraftforge.client.gui.ModListScreen(this)))
+ .pos(this.width / 2 - 100, l).size(98, 20).build());
+ .pos(this.width / 2 - 100, topPos).size(98, 20).build());
}
+ modUpdateNotification = net.minecraftforge.client.gui.TitleScreenModUpdateIndicator.init(this, modButton);
l = this.createTestWorldButton(l, 24);
SpriteIconButton spriteiconbutton = this.addRenderableWidget(
@@ -176,7 +_,7 @@
}).bounds(this.width / 2 - 100, i = topPos + spacing, 200, 20).tooltip(tooltip).build()).active = flag;
topPos = this.createTestWorldButton(topPos, 24);
SpriteIconButton language = this.addRenderableWidget(
@@ -182,7 +_,7 @@
}).bounds(this.width / 2 - 100, var6 = topPos + spacing, 200, 20).tooltip(tooltip).build()).active = multiplayerAllowed;
this.addRenderableWidget(
Button.builder(Component.translatable("menu.online"), button -> this.minecraft.setScreen(new RealmsMainScreen(this)))
- .bounds(this.width / 2 - 100, topPos = i + spacing, 200, 20)
+ .bounds(this.width / 2 + 2, topPos = i + spacing, 98, 20)
- .bounds(this.width / 2 - 100, topPos = var6 + spacing, 200, 20)
+ .bounds(this.width / 2 + 2, topPos = var6 + spacing, 98, 20)
.tooltip(tooltip)
.build()
)
@@ -304,9 +_,18 @@
s = s + I18n.get("menu.modded");
@@ -310,9 +_,18 @@
versionString = versionString + I18n.get("menu.modded");
}
- graphics.text(this.font, s, 2, this.height - 10, ARGB.white(f));
+ final float f_f = f;
- graphics.text(this.font, versionString, 2, this.height - 10, ARGB.white(widgetFade));
+ final float widgetFade_f = widgetFade;
+ net.minecraftforge.internal.BrandingControl.forEachLine(true, true, (brd, brdline) ->
+ graphics.text(this.font, brd, 2, this.height - ( 10 + brdline * (this.font.lineHeight + 1)), ARGB.color(f_f, -1))
+ graphics.text(this.font, brd, 2, this.height - ( 10 + brdline * (this.font.lineHeight + 1)), ARGB.color(widgetFade_f, -1))
+ );
+
+ net.minecraftforge.internal.BrandingControl.forEachAboveCopyrightLine((brd, brdline) ->
+ graphics.text(this.font, brd, this.width - font.width(brd), this.height - (10 + (brdline + 1) * ( this.font.lineHeight + 1)), ARGB.color(f_f, -1))
+ graphics.text(this.font, brd, this.width - font.width(brd), this.height - (10 + (brdline + 1) * ( this.font.lineHeight + 1)), ARGB.color(widgetFade_f, -1))
+ );
+
if (this.realmsNotificationsEnabled() && f >= 1.0F) {
if (this.realmsNotificationsEnabled() && widgetFade >= 1.0F) {
this.realmsNotificationsScreen.extractRenderState(graphics, mouseX, mouseY, a);
+ if (f >= 1.0f) this.modUpdateNotification.extractRenderState(graphics, mouseX, mouseY, a);
+ if (widgetFade >= 1.0f) this.modUpdateNotification.extractRenderState(graphics, mouseX, mouseY, a);
}
}
@@ -27,11 +27,11 @@
@@ -155,8 +_,8 @@
return null;
} else {
for (AdvancementTabType advancementtabtype : AdvancementTabType.values()) {
- if (index < advancementtabtype.getMax()) {
- return new AdvancementTab(minecraft, screen, advancementtabtype, index, root, optional.get());
+ if ((index % AdvancementTabType.MAX_TABS) < advancementtabtype.getMax()) {
+ return new AdvancementTab(minecraft, screen, advancementtabtype, index % AdvancementTabType.MAX_TABS, index / AdvancementTabType.MAX_TABS, root, optional.get());
for (AdvancementTabType type : AdvancementTabType.values()) {
- if (index < type.getMax()) {
- return new AdvancementTab(minecraft, screen, type, index, root, display.get());
+ if ((index % AdvancementTabType.MAX_TABS) < type.getMax()) {
+ return new AdvancementTab(minecraft, screen, type, index % AdvancementTabType.MAX_TABS, index / AdvancementTabType.MAX_TABS, root, display.get());
}
index -= advancementtabtype.getMax();
index -= type.getMax();
@@ -1,6 +1,6 @@
--- a/net/minecraft/client/gui/screens/advancements/AdvancementsScreen.java
+++ b/net/minecraft/client/gui/screens/advancements/AdvancementsScreen.java
@@ -51,6 +_,7 @@
@@ -50,6 +_,7 @@
private final Map<AdvancementHolder, AdvancementTab> tabs = Maps.newLinkedHashMap();
private @Nullable AdvancementTab selectedTab;
private boolean isScrolling;
@@ -8,7 +8,7 @@
public AdvancementsScreen(final ClientAdvancements advancements) {
this(advancements, null);
@@ -64,6 +_,19 @@
@@ -63,6 +_,19 @@
@Override
protected void init() {
@@ -28,33 +28,33 @@
this.layout.addTitleHeader(TITLE, this.font);
this.tabs.clear();
this.selectedTab = null;
@@ -106,7 +_,7 @@
int j = (this.height - 140) / 2;
@@ -105,7 +_,7 @@
int yo = (this.height - 140) / 2;
for (AdvancementTab advancementtab : this.tabs.values()) {
- if (advancementtab.isMouseOver(i, j, event.x(), event.y())) {
+ if (advancementtab.getPage() == tabPage && advancementtab.isMouseOver(i, j, event.x(), event.y())) {
this.advancements.setSelectedTab(advancementtab.getRootNode().holder(), true);
for (AdvancementTab tab : this.tabs.values()) {
- if (tab.isMouseOver(xo, yo, event.x(), event.y())) {
+ if (tab.getPage() == tabPage && tab.isMouseOver(xo, yo, event.x(), event.y())) {
this.advancements.setSelectedTab(tab.getRootNode().holder(), true);
break;
}
@@ -197,10 +_,12 @@
@@ -196,10 +_,12 @@
graphics.blit(RenderPipelines.GUI_TEXTURED, WINDOW_LOCATION, xo, yo, 0.0F, 0.0F, 252, 140, 256, 256);
if (this.tabs.size() > 1) {
for (AdvancementTab advancementtab : this.tabs.values()) {
+ if (advancementtab.getPage() == tabPage)
advancementtab.extractTab(graphics, xo, yo, mouseX, mouseY, advancementtab == this.selectedTab);
for (AdvancementTab tab : this.tabs.values()) {
+ if (tab.getPage() == tabPage)
tab.extractTab(graphics, xo, yo, mouseX, mouseY, tab == this.selectedTab);
}
for (AdvancementTab advancementtab1 : this.tabs.values()) {
+ if (advancementtab1.getPage() == tabPage)
advancementtab1.extractIcon(graphics, xo, yo);
for (AdvancementTab tab : this.tabs.values()) {
+ if (tab.getPage() == tabPage)
tab.extractIcon(graphics, xo, yo);
}
}
@@ -219,6 +_,7 @@
@@ -218,6 +_,7 @@
if (this.tabs.size() > 1) {
for (AdvancementTab advancementtab : this.tabs.values()) {
+ if (advancementtab.getPage() == tabPage)
if (advancementtab.isMouseOver(xo, yo, mouseX, mouseY)) {
graphics.setTooltipForNextFrame(this.font, advancementtab.getTitle(), mouseX, mouseY);
for (AdvancementTab tab : this.tabs.values()) {
+ if (tab.getPage() == tabPage)
if (tab.isMouseOver(xo, yo, mouseX, mouseY)) {
graphics.setTooltipForNextFrame(this.font, tab.getTitle(), mouseX, mouseY);
}
@@ -1,34 +1,31 @@
--- a/net/minecraft/client/gui/screens/inventory/AbstractContainerScreen.java
+++ b/net/minecraft/client/gui/screens/inventory/AbstractContainerScreen.java
@@ -111,6 +_,7 @@
int i = this.leftPos;
int j = this.topPos;
int xo = this.leftPos;
int yo = this.topPos;
super.extractRenderState(graphics, mouseX, mouseY, a);
+ net.minecraftforge.client.event.ForgeEventFactoryClient.onContainerRenderBackground(this, graphics, mouseX, mouseY);
graphics.pose().pushMatrix();
graphics.pose().translate(i, j);
graphics.pose().translate(xo, yo);
this.extractLabels(graphics, mouseX, mouseY);
@@ -122,6 +_,7 @@
if (slot != null && slot != this.hoveredSlot) {
this.onStopHovering(slot);
if (previouslyHoveredSlot != null && previouslyHoveredSlot != this.hoveredSlot) {
this.onStopHovering(previouslyHoveredSlot);
}
+ net.minecraftforge.client.event.ForgeEventFactoryClient.onContainerRenderForeground(this, graphics, mouseX, mouseY);
graphics.pose().popMatrix();
}
@@ -203,9 +_,9 @@
this.font,
this.getTooltipFromContainerItem(itemstack),
itemstack.getTooltipImage(),
+ itemstack,
mouseX,
- mouseY,
- itemstack.get(DataComponents.TOOLTIP_STYLE)
+ mouseY
@@ -200,7 +_,7 @@
ItemStack item = this.hoveredSlot.getItem();
if (this.menu.getCarried().isEmpty() || this.showTooltipWithItemInHand(item)) {
graphics.setTooltipForNextFrame(
- this.font, this.getTooltipFromContainerItem(item), item.getTooltipImage(), mouseX, mouseY, item.get(DataComponents.TOOLTIP_STYLE)
+ this.font, this.getTooltipFromContainerItem(item), item.getTooltipImage(), item, mouseX, mouseY
);
}
}
@@ -221,7 +_,8 @@
@@ -216,7 +_,8 @@
private void extractFloatingItem(final GuiGraphicsExtractor graphics, final ItemStack carried, final int x, final int y, final @Nullable String itemCount) {
graphics.item(carried, x, y);
@@ -38,75 +35,75 @@
}
protected void extractLabels(final GuiGraphicsExtractor graphics, final int xm, final int ym) {
@@ -319,7 +_,8 @@
@@ -316,7 +_,8 @@
if (super.mouseClicked(event, doubleClick)) {
return true;
} else {
- boolean flag = this.minecraft.options.keyPickItem.matchesMouse(event) && this.minecraft.player.hasInfiniteMaterials();
- boolean cloning = this.minecraft.options.keyPickItem.matchesMouse(event) && this.minecraft.player.hasInfiniteMaterials();
+ var mouseKey = com.mojang.blaze3d.platform.InputConstants.Type.MOUSE.getOrCreate(event.button());
+ boolean flag = this.minecraft.options.keyPickItem.isActiveAndMatches(mouseKey) && this.minecraft.player.hasInfiniteMaterials();
+ boolean cloning = this.minecraft.options.keyPickItem.isActiveAndMatches(mouseKey) && this.minecraft.player.hasInfiniteMaterials();
Slot slot = this.getHoveredSlot(event.x(), event.y());
this.doubleclick = this.lastClickSlot == slot && doubleClick;
this.skipNextRelease = false;
@@ -329,6 +_,7 @@
int i = this.leftPos;
int j = this.topPos;
boolean flag1 = this.hasClickedOutside(event.x(), event.y(), i, j);
+ if (slot != null) flag1 = false; // Forge, prevent dropping of items through slots outside of GUI boundaries
int k = -1;
@@ -326,6 +_,7 @@
int xo = this.leftPos;
int yo = this.topPos;
boolean clickedOutside = this.hasClickedOutside(event.x(), event.y(), xo, yo);
+ if (slot != null) clickedOutside = false; // Forge, prevent dropping of items through slots outside of GUI boundaries
int slotId = -1;
if (slot != null) {
k = slot.index;
@@ -354,7 +_,7 @@
slotId = slot.index;
@@ -351,7 +_,7 @@
}
} else if (!this.isQuickCrafting) {
if (this.menu.getCarried().isEmpty()) {
- if (flag) {
- if (cloning) {
+ if (this.minecraft.options.keyPickItem.isActiveAndMatches(mouseKey)) {
this.slotClicked(slot, k, event.button(), ContainerInput.CLONE);
this.slotClicked(slot, slotId, event.button(), ContainerInput.CLONE);
} else {
boolean flag2 = k != -999 && event.hasShiftDown();
@@ -378,7 +_,7 @@
boolean quickKey = slotId != -999 && event.hasShiftDown();
@@ -375,7 +_,7 @@
this.quickCraftingType = 0;
} else if (event.button() == 1) {
this.quickCraftingType = 1;
- } else if (flag) {
- } else if (cloning) {
+ } else if (this.minecraft.options.keyPickItem.isActiveAndMatches(mouseKey)) {
this.quickCraftingType = 2;
}
}
@@ -448,10 +_,13 @@
@@ -445,10 +_,13 @@
@Override
public boolean mouseReleased(final MouseButtonEvent event) {
+ super.mouseReleased(event); //Forge, Call parent to release buttons
Slot slot = this.getHoveredSlot(event.x(), event.y());
int i = this.leftPos;
int j = this.topPos;
boolean flag = this.hasClickedOutside(event.x(), event.y(), i, j);
+ if (slot != null) flag = false; // Forge, prevent dropping of items through slots outside of GUI boundaries
int xo = this.leftPos;
int yo = this.topPos;
boolean clickedOutside = this.hasClickedOutside(event.x(), event.y(), xo, yo);
+ if (slot != null) clickedOutside = false; // Forge, prevent dropping of items through slots outside of GUI boundaries
+ var mouseKey = com.mojang.blaze3d.platform.InputConstants.Type.MOUSE.getOrCreate(event.button());
int k = -1;
int slotId = -1;
if (slot != null) {
k = slot.index;
@@ -468,7 +_,7 @@
if (slot1 != null
&& slot1.mayPickup(this.minecraft.player)
&& slot1.hasItem()
- && slot1.container == slot.container
+ && slot1.isSameInventory(slot)
&& AbstractContainerMenu.canItemQuickReplace(slot1, this.lastQuickMoved, true)) {
this.slotClicked(slot1, slot1.index, event.button(), ContainerInput.QUICK_MOVE);
slotId = slot.index;
@@ -465,7 +_,7 @@
if (target != null
&& target.mayPickup(this.minecraft.player)
&& target.hasItem()
- && target.container == slot.container
+ && target.isSameInventory(slot)
&& AbstractContainerMenu.canItemQuickReplace(target, this.lastQuickMoved, true)) {
this.slotClicked(target, target.index, event.button(), ContainerInput.QUICK_MOVE);
}
@@ -527,7 +_,7 @@
@@ -524,7 +_,7 @@
} else if (this.isQuickCrafting && !this.quickCraftSlots.isEmpty()) {
this.quickCraftToSlots();
} else if (!this.menu.getCarried().isEmpty()) {
- if (this.minecraft.options.keyPickItem.matchesMouse(event)) {
+ if (this.minecraft.options.keyPickItem.isActiveAndMatches(mouseKey)) {
this.slotClicked(slot, k, event.button(), ContainerInput.CLONE);
this.slotClicked(slot, slotId, event.button(), ContainerInput.CLONE);
} else {
boolean flag1 = k != -999 && event.hasShiftDown();
@@ -598,7 +_,7 @@
boolean quickKey = slotId != -999 && event.hasShiftDown();
@@ -595,7 +_,7 @@
public boolean keyPressed(final KeyEvent event) {
if (super.keyPressed(event)) {
return true;
@@ -115,7 +112,7 @@
this.onClose();
return true;
} else {
@@ -696,6 +_,13 @@
@@ -693,6 +_,13 @@
super.onClose();
}
@@ -11,7 +11,7 @@
super(new CreativeModeInventoryScreen.ItemPickerMenu(player), player.getInventory(), CommonComponents.EMPTY, 195, 136);
@@ -164,7 +_,7 @@
private void refreshCurrentTabContents(final Collection<ItemStack> displayList) {
int i = this.menu.getRowIndexForScroll(this.scrollOffs);
int oldRowIndex = this.menu.getRowIndexForScroll(this.scrollOffs);
this.menu.items.clear();
- if (selectedTab.getType() == CreativeModeTab.Type.SEARCH) {
+ if (selectedTab.hasSearchBar()) {
@@ -63,7 +63,7 @@
+ } else if (!selectedTab.hasSearchBar()) {
return false;
} else {
String s = this.searchBox.getValue();
String oldContents = this.searchBox.getValue();
@@ -405,7 +_,7 @@
@Override
public boolean keyPressed(final KeyEvent event) {
@@ -80,20 +80,20 @@
+ if (!selectedTab.hasSearchBar()) return;
this.menu.items.clear();
this.visibleTags.clear();
String s = this.searchBox.getValue();
String searchTerm = this.searchBox.getValue();
@@ -453,10 +_,10 @@
SearchTree<ItemStack> searchtree;
if (s.startsWith("#")) {
s = s.substring(1);
- searchtree = sessionsearchtrees.creativeTagSearch();
+ searchtree = sessionsearchtrees.getSearchTree(net.minecraftforge.client.CreativeModeTabSearchRegistry.getTagSearchKey(selectedTab));
this.updateVisibleTags(s);
SearchTree<ItemStack> tree;
if (searchTerm.startsWith("#")) {
searchTerm = searchTerm.substring(1);
- tree = searchTrees.creativeTagSearch();
+ tree = searchTrees.getSearchTree(net.minecraftforge.client.CreativeModeTabSearchRegistry.getTagSearchKey(selectedTab));
this.updateVisibleTags(searchTerm);
} else {
- searchtree = sessionsearchtrees.creativeNameSearch();
+ searchtree = sessionsearchtrees.getSearchTree(net.minecraftforge.client.CreativeModeTabSearchRegistry.getNameSearchKey(selectedTab));
- tree = searchTrees.creativeNameSearch();
+ tree = searchTrees.getSearchTree(net.minecraftforge.client.CreativeModeTabSearchRegistry.getNameSearchKey(selectedTab));
}
this.menu.items.addAll(searchtree.search(s.toLowerCase(Locale.ROOT)));
this.menu.items.addAll(tree.search(searchTerm.toLowerCase(Locale.ROOT)));
@@ -484,7 +_,7 @@
@Override
protected void extractLabels(final GuiGraphicsExtractor graphics, final int xm, final int ym) {
@@ -104,22 +104,22 @@
}
@@ -494,7 +_,7 @@
double d0 = event.x() - this.leftPos;
double d1 = event.y() - this.topPos;
double xm = event.x() - this.leftPos;
double ym = event.y() - this.topPos;
- for (CreativeModeTab creativemodetab : CreativeModeTabs.tabs()) {
+ for (CreativeModeTab creativemodetab : currentPage.getVisibleTabs()) {
if (this.checkTabClicked(creativemodetab, d0, d1)) {
- for (CreativeModeTab tab : CreativeModeTabs.tabs()) {
+ for (CreativeModeTab tab : currentPage.getVisibleTabs()) {
if (this.checkTabClicked(tab, xm, ym)) {
return true;
}
@@ -516,7 +_,7 @@
double d1 = event.y() - this.topPos;
double ym = event.y() - this.topPos;
this.scrolling = false;
- for (CreativeModeTab creativemodetab : CreativeModeTabs.tabs()) {
+ for (CreativeModeTab creativemodetab : currentPage.getVisibleTabs()) {
if (this.checkTabClicked(creativemodetab, d0, d1)) {
this.selectTab(creativemodetab);
- for (CreativeModeTab tab : CreativeModeTabs.tabs()) {
+ for (CreativeModeTab tab : currentPage.getVisibleTabs()) {
if (this.checkTabClicked(tab, xm, ym)) {
this.selectTab(tab);
return true;
@@ -610,13 +_,15 @@
this.originalSlots = null;
@@ -130,7 +130,7 @@
this.searchBox.setVisible(true);
this.searchBox.setCanLoseFocus(false);
this.searchBox.setFocused(true);
if (creativemodetab != tab) {
if (oldTab != tab) {
this.searchBox.setValue("");
}
+ this.searchBox.setWidth(selectedTab.getSearchBarWidth());
@@ -142,7 +142,7 @@
this.effects.extractRenderState(graphics, mouseX, mouseY);
super.extractRenderState(graphics, mouseX, mouseY, a);
- for (CreativeModeTab creativemodetab : CreativeModeTabs.tabs()) {
- for (CreativeModeTab tab : CreativeModeTabs.tabs()) {
+ if (this.pages.size() != 1) {
+ Component page = Component.literal(String.format("%d / %d", this.pages.indexOf(this.currentPage) + 1, this.pages.size()));
+ graphics.pose().pushMatrix();
@@ -150,39 +150,39 @@
+ graphics.pose().popMatrix();
+ }
+
+ for (CreativeModeTab creativemodetab : currentPage.getVisibleTabs()) {
if (this.checkTabHovering(graphics, creativemodetab, mouseX, mouseY)) {
+ for (CreativeModeTab tab : currentPage.getVisibleTabs()) {
if (this.checkTabHovering(graphics, tab, mouseX, mouseY)) {
break;
}
@@ -701,7 +_,7 @@
public List<Component> getTooltipFromContainerItem(final ItemStack itemStack) {
boolean flag = this.hoveredSlot != null && this.hoveredSlot instanceof CreativeModeInventoryScreen.CustomCreativeSlot;
boolean flag1 = selectedTab.getType() == CreativeModeTab.Type.CATEGORY;
- boolean flag2 = selectedTab.getType() == CreativeModeTab.Type.SEARCH;
+ boolean flag2 = selectedTab.hasSearchBar();
TooltipFlag.Default tooltipflag$default = this.minecraft.options.advancedItemTooltips ? TooltipFlag.Default.ADVANCED : TooltipFlag.Default.NORMAL;
TooltipFlag tooltipflag = flag ? tooltipflag$default.asCreative() : tooltipflag$default;
List<Component> list = itemStack.getTooltipLines(Item.TooltipContext.of(this.minecraft.level), this.minecraft.player, tooltipflag);
boolean isCreativeSlot = this.hoveredSlot != null && this.hoveredSlot instanceof CreativeModeInventoryScreen.CustomCreativeSlot;
boolean isSingleCategoryTab = selectedTab.getType() == CreativeModeTab.Type.CATEGORY;
- boolean isSearchTab = selectedTab.getType() == CreativeModeTab.Type.SEARCH;
+ boolean isSearchTab = selectedTab.hasSearchBar();
TooltipFlag.Default originalTooltipStyle = this.minecraft.options.advancedItemTooltips ? TooltipFlag.Default.ADVANCED : TooltipFlag.Default.NORMAL;
TooltipFlag tooltipStyle = isCreativeSlot ? originalTooltipStyle.asCreative() : originalTooltipStyle;
List<Component> originalLines = itemStack.getTooltipLines(Item.TooltipContext.of(this.minecraft.level), this.minecraft.player, tooltipStyle);
@@ -722,7 +_,7 @@
int i = 1;
for (CreativeModeTab creativemodetab : CreativeModeTabs.tabs()) {
- if (creativemodetab.getType() != CreativeModeTab.Type.SEARCH && creativemodetab.contains(itemStack)) {
+ if (!creativemodetab.hasSearchBar() && creativemodetab.contains(itemStack)) {
list1.add(i++, creativemodetab.getDisplayName().copy().withStyle(ChatFormatting.BLUE));
for (CreativeModeTab tab : CreativeModeTabs.tabs()) {
- if (tab.getType() != CreativeModeTab.Type.SEARCH && tab.contains(itemStack)) {
+ if (!tab.hasSearchBar() && tab.contains(itemStack)) {
linesToDisplay.add(i++, tab.getDisplayName().copy().withStyle(ChatFormatting.BLUE));
}
}
@@ -735,7 +_,7 @@
public void extractBackground(final GuiGraphicsExtractor graphics, final int mouseX, final int mouseY, final float a) {
super.extractBackground(graphics, mouseX, mouseY, a);
- for (CreativeModeTab creativemodetab : CreativeModeTabs.tabs()) {
+ for (CreativeModeTab creativemodetab : currentPage.getVisibleTabs()) {
if (creativemodetab != selectedTab) {
this.extractTabButton(graphics, mouseX, mouseY, creativemodetab);
- for (CreativeModeTab tab : CreativeModeTabs.tabs()) {
+ for (CreativeModeTab tab : currentPage.getVisibleTabs()) {
if (tab != selectedTab) {
this.extractTabButton(graphics, mouseX, mouseY, tab);
}
@@ -770,6 +_,7 @@
graphics.blitSprite(RenderPipelines.GUI_TEXTURED, identifier, j, k + (int)((i - k - 17) * this.scrollOffs), 12, 15);
graphics.blitSprite(RenderPipelines.GUI_TEXTURED, sprite, xscr, yscr + (int)((yscr2 - yscr - 17) * this.scrollOffs), 12, 15);
}
+ if (currentPage.getVisibleTabs().contains(selectedTab)) //Forge: only display tab selection when the selected tab is on the current page
@@ -193,39 +193,39 @@
}
private int getTabX(final CreativeModeTab tab) {
- int i = tab.column();
+ int i = currentPage.getColumn(tab);
int j = 27;
int k = 27 * i;
- int pos = tab.column();
+ int pos = currentPage.getColumn(tab);
int spacing = 27;
int x = 27 * pos;
if (tab.isAlignedRight()) {
@@ -791,7 +_,7 @@
private int getTabY(final CreativeModeTab tab) {
int i = 0;
int y = 0;
- if (tab.row() == CreativeModeTab.Row.TOP) {
+ if (currentPage.isTop(tab)) {
i -= 32;
y -= 32;
} else {
i += this.imageHeight;
y += this.imageHeight;
@@ -819,8 +_,8 @@
protected void extractTabButton(final GuiGraphicsExtractor graphics, final int mouseX, final int mouseY, final CreativeModeTab tab) {
boolean flag = tab == selectedTab;
- boolean flag1 = tab.row() == CreativeModeTab.Row.TOP;
- int i = tab.column();
+ boolean flag1 = currentPage.isTop(tab);
+ int i = currentPage.getColumn(tab);
int j = this.leftPos + this.getTabX(tab);
int k = this.topPos - (flag1 ? 28 : -(this.imageHeight - 4));
Identifier[] aidentifier;
boolean selected = tab == selectedTab;
- boolean isTop = tab.row() == CreativeModeTab.Row.TOP;
- int pos = tab.column();
+ boolean isTop = currentPage.isTop(tab);
+ int pos = currentPage.getColumn(tab);
int x = this.leftPos + this.getTabX(tab);
int y = this.topPos - (isTop ? 28 : -(this.imageHeight - 4));
Identifier[] sprites;
@@ -834,6 +_,7 @@
graphics.requestCursor(CursorTypes.POINTING_HAND);
}
+ com.mojang.blaze3d.opengl.GlStateManager._enableBlend(); //Forge: Make sure blend is enabled else tabs show a white border.
graphics.blitSprite(RenderPipelines.GUI_TEXTURED, aidentifier[Mth.clamp(i, 0, aidentifier.length)], j, k, 26, 32);
int l = j + 13 - 8;
int i1 = k + 16 - 8 + (flag1 ? 1 : -1);
graphics.blitSprite(RenderPipelines.GUI_TEXTURED, sprites[Mth.clamp(pos, 0, sprites.length)], x, y, 26, 32);
int iconX = x + 13 - 8;
int iconY = y + 16 - 8 + (isTop ? 1 : -1);
@@ -870,6 +_,14 @@
}
}
@@ -1,39 +1,39 @@
--- a/net/minecraft/client/gui/screens/inventory/EffectsInInventory.java
+++ b/net/minecraft/client/gui/screens/inventory/EffectsInInventory.java
@@ -47,12 +_,16 @@
int j = this.screen.width - i;
Collection<MobEffectInstance> collection = this.minecraft.player.getActiveEffects();
if (!collection.isEmpty() && j >= 32) {
- int k = j >= 120 ? j - 7 : 32;
+ var event = net.minecraftforge.client.event.ForgeEventFactoryClient.onScreenEffectSize(this.screen, j, j < 120, i);
int availableWidth = this.screen.width - xo;
Collection<MobEffectInstance> activeEffects = this.minecraft.player.getActiveEffects();
if (!activeEffects.isEmpty() && availableWidth >= 32) {
- int maxWidth = availableWidth >= 120 ? availableWidth - 7 : 32;
+ var event = net.minecraftforge.client.event.ForgeEventFactoryClient.onScreenEffectSize(this.screen, availableWidth, availableWidth < 120, xo);
+ if (event == null) return;
+ int k = !event.isCompact() ? j - 7 : 32;
+ i = event.getHorizontalOffset();
int l = 33;
if (collection.size() > 5) {
l = 132 / (collection.size() - 1);
+ int maxWidth = !event.isCompact() ? availableWidth - 7 : 32;
+ xo = event.getHorizontalOffset();
int yStep = 33;
if (activeEffects.size() > 5) {
yStep = 132 / (activeEffects.size() - 1);
}
+ Iterable<MobEffectInstance> iterable = collection.stream().filter(net.minecraftforge.client.ForgeHooksClient::shouldRenderEffect).sorted().toList();
this.extractEffects(graphics, collection, i, l, mouseX, mouseY, k);
+ Iterable<MobEffectInstance> iterable = activeEffects.stream().filter(net.minecraftforge.client.ForgeHooksClient::shouldRenderEffect).sorted().toList();
this.extractEffects(graphics, activeEffects, xo, yStep, mouseX, mouseY, maxWidth);
}
}
@@ -72,6 +_,11 @@
for (MobEffectInstance mobeffectinstance : iterable) {
boolean flag = mobeffectinstance.isAmbient();
+ var renderer = net.minecraftforge.client.extensions.common.IClientMobEffectExtensions.of(mobeffectinstance);
+ if (renderer.extractInventory(mobeffectinstance, this, graphics, x0, i, 0)) {
+ i += yStep;
for (MobEffectInstance effect : sortedEffects) {
boolean isAmbient = effect.isAmbient();
+ var renderer = net.minecraftforge.client.extensions.common.IClientMobEffectExtensions.of(effect);
+ if (renderer.extractInventory(effect, this, graphics, x0, y0, 0)) {
+ y0 += yStep;
+ continue;
+ }
Component component = this.getEffectName(mobeffectinstance);
Component component1 = MobEffectUtil.formatDuration(mobeffectinstance, 1.0F, this.minecraft.level.tickRateManager().tickrate());
int j = this.extractBackground(graphics, font, component, component1, x0, i, flag, maxWidth);
Component effectText = this.getEffectName(effect);
Component duration = MobEffectUtil.formatDuration(effect, 1.0F, this.minecraft.level.tickRateManager().tickrate());
int textureWidth = this.extractBackground(graphics, font, effectText, duration, x0, y0, isAmbient, maxWidth);
@@ -136,5 +_,9 @@
}
return mutablecomponent;
return name;
+ }
+
+ public AbstractContainerScreen<?> getScreen() {
@@ -1,32 +1,33 @@
--- a/net/minecraft/client/gui/screens/inventory/EnchantmentScreen.java
+++ b/net/minecraft/client/gui/screens/inventory/EnchantmentScreen.java
@@ -111,7 +_,7 @@
int l1 = 86 - this.font.width(s);
FormattedText formattedtext = EnchantmentNames.getInstance().getRandomName(this.font, l1);
int i2 = -9937334;
- if ((k < l + 1 || this.minecraft.player.experienceLevel < k1) && !this.minecraft.player.hasInfiniteMaterials()) {
+ if (((k < l + 1 || this.minecraft.player.experienceLevel < k1) && !this.minecraft.player.hasInfiniteMaterials()) || this.menu.enchantClue[l] == -1) { // Forge: render buttons as disabled when enchantable but enchantability not met on lower levels{
graphics.blitSprite(RenderPipelines.GUI_TEXTURED, ENCHANTMENT_SLOT_DISABLED_SPRITE, i1, j + 14 + 19 * l, 108, 19);
graphics.blitSprite(RenderPipelines.GUI_TEXTURED, DISABLED_LEVEL_SPRITES[l], i1 + 1, j + 15 + 19 * l, 16, 16);
graphics.textWithWordWrap(this.font, formattedtext, j1, j + 16 + 19 * l, l1, ARGB.opaque((i2 & 16711422) >> 1), false);
@@ -162,13 +_,16 @@
int textWidth = 86 - this.font.width(costText);
FormattedText message = EnchantmentNames.getInstance().getRandomName(this.font, textWidth);
int col = -9937334;
- if ((goldCount < i + 1 || this.minecraft.player.experienceLevel < cost) && !this.minecraft.player.hasInfiniteMaterials()) {
+ if (((goldCount < i + 1 || this.minecraft.player.experienceLevel < cost) && !this.minecraft.player.hasInfiniteMaterials()) || this.menu.enchantClue[i] == -1) { // Forge: render buttons as disabled when enchantable but enchantability not met on lower levels{
graphics.blitSprite(RenderPipelines.GUI_TEXTURED, ENCHANTMENT_SLOT_DISABLED_SPRITE, leftPos, yo + 14 + 19 * i, 108, 19);
graphics.blitSprite(RenderPipelines.GUI_TEXTURED, DISABLED_LEVEL_SPRITES[i], leftPos + 1, yo + 15 + 19 * i, 16, 16);
graphics.textWithWordWrap(this.font, message, leftPosText, yo + 16 + 19 * i, textWidth, ARGB.opaque((col & 16711422) >> 1), false);
@@ -162,14 +_,18 @@
.registryAccess()
.lookupOrThrow(Registries.ENCHANTMENT)
.get(this.menu.enchantClue[j]);
- if (!optional.isEmpty()) {
.get(this.menu.enchantClue[i]);
- if (!enchant.isEmpty()) {
+ {
int l = this.menu.levelClue[j];
int i1 = j + 1;
- if (this.isHovering(60, 14 + 19 * j, 108, 17, mouseX, mouseY) && k > 0 && l >= 0) {
+ if (this.isHovering(60, 14 + 19 * j, 108, 17, (double)mouseX, (double)mouseY) && l >= 0) {
List<Component> list = Lists.newArrayList();
- list.add(Component.translatable("container.enchant.clue", Enchantment.getFullname(optional.get(), l)).withStyle(ChatFormatting.WHITE));
- if (!flag) {
+ list.add(Component.translatable("container.enchant.clue", optional.isEmpty() ? "" : Enchantment.getFullname(optional.get(), l)).withStyle(ChatFormatting.WHITE));
+ if (optional.isEmpty()) {
+ list.add(Component.literal(""));
+ list.add(Component.translatable("forge.container.enchant.limitedEnchantability").withStyle(ChatFormatting.RED));
+ } else if (!flag) {
list.add(CommonComponents.EMPTY);
if (this.minecraft.player.experienceLevel < k) {
list.add(Component.translatable("container.enchant.level.requirement", this.menu.costs[j]).withStyle(ChatFormatting.RED));
int enchantLevel = this.menu.levelClue[i];
int cost = i + 1;
- if (this.isHovering(60, 14 + 19 * i, 108, 17, mouseX, mouseY) && minLevel > 0 && enchantLevel >= 0) {
+ if (this.isHovering(60, 14 + 19 * i, 108, 17, (double)mouseX, (double)mouseY) && enchantLevel >= 0) {
List<Component> texts = Lists.newArrayList();
texts.add(
- Component.translatable("container.enchant.clue", Enchantment.getFullname(enchant.get(), enchantLevel)).withStyle(ChatFormatting.WHITE)
+ Component.translatable("container.enchant.clue", enchant.isEmpty() ? "" : Enchantment.getFullname(enchant.get(), enchantLevel)).withStyle(ChatFormatting.WHITE)
);
+ if (enchant.isEmpty()) {
+ texts.add(Component.literal(""));
+ texts.add(Component.translatable("forge.container.enchant.limitedEnchantability").withStyle(ChatFormatting.RED));
+ } else
if (!infiniteMaterials) {
texts.add(CommonComponents.EMPTY);
if (this.minecraft.player.experienceLevel < minLevel) {
@@ -1,9 +1,9 @@
--- a/net/minecraft/client/gui/screens/inventory/tooltip/ClientTooltipComponent.java
+++ b/net/minecraft/client/gui/screens/inventory/tooltip/ClientTooltipComponent.java
@@ -20,7 +_,7 @@
case ClientActivePlayersTooltip.ActivePlayersTooltip clientactiveplayerstooltip$activeplayerstooltip -> new ClientActivePlayersTooltip(
clientactiveplayerstooltip$activeplayerstooltip
);
@@ -18,7 +_,7 @@
return (ClientTooltipComponent)(switch (component) {
case BundleTooltip bundleTooltip -> new ClientBundleTooltip(bundleTooltip.contents());
case ClientActivePlayersTooltip.ActivePlayersTooltip activePlayersTooltip -> new ClientActivePlayersTooltip(activePlayersTooltip);
- default -> throw new IllegalArgumentException("Unknown TooltipComponent");
+ default -> net.minecraftforge.client.gui.ClientTooltipComponentManager.createClientTooltipComponent(component);
});
@@ -1,6 +1,6 @@
--- a/net/minecraft/client/gui/screens/multiplayer/ServerSelectionList.java
+++ b/net/minecraft/client/gui/screens/multiplayer/ServerSelectionList.java
@@ -371,6 +_,8 @@
@@ -372,6 +_,8 @@
graphics.setTooltipForNextFrame(Lists.transform(this.onlinePlayersTooltip, Component::getVisualOrderText), mouseX, mouseY);
}
@@ -8,4 +8,4 @@
+
if (this.minecraft.options.touchscreen().get() || hovered) {
graphics.fill(this.getContentX(), this.getContentY(), this.getContentX() + 32, this.getContentY() + 32, -1601138544);
int i1 = mouseX - this.getContentX();
int relX = mouseX - this.getContentX();
@@ -1,6 +1,6 @@
--- a/net/minecraft/client/gui/screens/options/controls/KeyBindsList.java
+++ b/net/minecraft/client/gui/screens/options/controls/KeyBindsList.java
@@ -123,9 +_,10 @@
@@ -122,9 +_,10 @@
this.name = name;
this.changeButton = Button.builder(name, button -> {
KeyBindsList.this.keyBindsScreen.selectedKey = key;
@@ -12,7 +12,7 @@
.createNarration(
defaultNarrationSupplier -> key.isUnbound()
? Component.translatable("narrator.controls.unbound", name)
@@ -133,7 +_,7 @@
@@ -132,7 +_,7 @@
)
.build();
this.resetButton = Button.builder(RESET_BUTTON_TITLE, button -> {
@@ -21,13 +21,13 @@
KeyBindsList.this.resetMappingAndUpdateButtons();
}).bounds(0, 0, 50, 20).createNarration(defaultNarrationSupplier -> Component.translatable("narrator.controls.reset", name)).build();
this.refreshEntry();
@@ -174,7 +_,8 @@
MutableComponent mutablecomponent = Component.empty();
@@ -173,7 +_,8 @@
MutableComponent tooltip = Component.empty();
if (!this.key.isUnbound()) {
for (KeyMapping keymapping : KeyBindsList.this.minecraft.options.keyMappings) {
- if (keymapping != this.key && this.key.same(keymapping) && (!keymapping.isDefault() || !this.key.isDefault())) {
+ var vanillConflict = keymapping != this.key && this.key.same(keymapping) && (!keymapping.isDefault() || !this.key.isDefault());
+ if (vanillConflict || keymapping.hasKeyModifierConflict(this.key)) { // FORGE: gracefully handle conflicts like SHIFT vs SHIFT+G
for (KeyMapping otherKey : KeyBindsList.this.minecraft.options.keyMappings) {
- if (otherKey != this.key && this.key.same(otherKey) && (!otherKey.isDefault() || !this.key.isDefault())) {
+ var vanillConflict = otherKey != this.key && this.key.same(otherKey) && (!otherKey.isDefault() || !this.key.isDefault());
+ if (vanillConflict || otherKey.hasKeyModifierConflict(this.key)) { // FORGE: gracefully handle conflicts like SHIFT vs SHIFT+G
if (this.hasCollision) {
mutablecomponent.append(", ");
tooltip.append(", ");
}
@@ -3,9 +3,9 @@
@@ -42,7 +_,7 @@
protected void addFooter() {
this.resetButton = Button.builder(Component.translatable("controls.resetAll"), button -> {
for (KeyMapping keymapping : this.options.keyMappings) {
- keymapping.setKey(keymapping.getDefaultKey());
+ keymapping.setToDefault();
for (KeyMapping key : this.options.keyMappings) {
- key.setKey(key.getDefaultKey());
+ key.setToDefault();
}
this.keyBindsList.resetMappingAndUpdateButtons();
@@ -27,7 +27,7 @@
@@ -101,5 +_,18 @@
}
this.resetButton.active = flag;
this.resetButton.active = canReset;
+ }
+
+ @Override
@@ -1,11 +1,11 @@
--- a/net/minecraft/client/gui/screens/packs/TransferableSelectionList.java
+++ b/net/minecraft/client/gui/screens/packs/TransferableSelectionList.java
@@ -66,7 +_,7 @@
Component component = Component.empty().append(this.title).withStyle(ChatFormatting.UNDERLINE, ChatFormatting.BOLD);
this.addEntry(new TransferableSelectionList.HeaderEntry(this.minecraft.font, component), (int)(9.0F * 1.5F));
Component header = Component.empty().append(this.title).withStyle(ChatFormatting.UNDERLINE, ChatFormatting.BOLD);
this.addEntry(new TransferableSelectionList.HeaderEntry(this.minecraft.font, header), (int)(9.0F * 1.5F));
this.setSelected(null);
- entries.forEach(e -> {
+ entries.filter(PackSelectionModel.Entry::notHidden).forEach(e -> {
TransferableSelectionList.PackEntry transferableselectionlist$packentry = new TransferableSelectionList.PackEntry(this.minecraft, this, e);
this.addEntry(transferableselectionlist$packentry);
TransferableSelectionList.PackEntry entry = new TransferableSelectionList.PackEntry(this.minecraft, this, e);
this.addEntry(entry);
if (transferredEntry != null && transferredEntry.getId().equals(e.getId())) {
@@ -1,14 +1,14 @@
--- a/net/minecraft/client/gui/screens/worldselection/CreateWorldScreen.java
+++ b/net/minecraft/client/gui/screens/worldselection/CreateWorldScreen.java
@@ -177,6 +_,7 @@
WorldDataConfiguration worlddataconfiguration = SharedConstants.IS_RUNNING_IN_IDE
@@ -174,6 +_,7 @@
WorldDataConfiguration dataConfig = SharedConstants.IS_RUNNING_IN_IDE
? new WorldDataConfiguration(new DataPackConfig(List.of("vanilla", "tests"), List.of()), FeatureFlags.DEFAULT_FLAGS)
: WorldDataConfiguration.DEFAULT;
+ net.minecraftforge.event.ForgeEventFactory.addPackFindersServer(packrepository::addPackFinder);
WorldLoader.InitConfig worldloader$initconfig = createDefaultLoadConfig(packrepository, worlddataconfiguration);
CompletableFuture<WorldCreationContext> completablefuture = WorldLoader.load(
worldloader$initconfig,
@@ -520,7 +_,7 @@
+ net.minecraftforge.event.ForgeEventFactory.addPackFindersServer(vanillaOnlyPackRepository::addPackFinder);
WorldLoader.InitConfig loadConfig = createDefaultLoadConfig(vanillaOnlyPackRepository, dataConfig);
CompletableFuture<WorldCreationContext> loadResult = WorldLoader.load(
loadConfig,
@@ -509,7 +_,7 @@
if (retry) {
onAbort.accept(this.uiState.getSettings().dataConfiguration());
} else {
@@ -17,10 +17,10 @@
}
},
Component.translatable("dataPack.validation.failed"),
@@ -634,6 +_,7 @@
if (path != null) {
@@ -623,6 +_,7 @@
if (dataPackDir != null) {
if (this.tempDataPackRepository == null) {
this.tempDataPackRepository = ServerPacksSource.createPackRepository(path, this.packValidator);
this.tempDataPackRepository = ServerPacksSource.createPackRepository(dataPackDir, this.packValidator);
+ net.minecraftforge.resource.ResourcePackLoader.loadResourcePacks(this.tempDataPackRepository, false);
this.tempDataPackRepository.reload();
}
@@ -1,6 +1,6 @@
--- a/net/minecraft/client/gui/screens/worldselection/PresetEditor.java
+++ b/net/minecraft/client/gui/screens/worldselection/PresetEditor.java
@@ -29,6 +_,10 @@
@@ -28,6 +_,10 @@
@OnlyIn(Dist.CLIENT)
public interface PresetEditor {
@@ -1,11 +1,11 @@
--- a/net/minecraft/client/gui/screens/worldselection/WorldCreationUiState.java
+++ b/net/minecraft/client/gui/screens/worldselection/WorldCreationUiState.java
@@ -234,7 +_,7 @@
@@ -229,7 +_,7 @@
public @Nullable PresetEditor getPresetEditor() {
Holder<WorldPreset> holder = this.getWorldType().preset();
- return holder != null ? PresetEditor.EDITORS.get(holder.unwrapKey()) : null;
+ return holder != null ? holder.unwrapKey().map(net.minecraftforge.client.PresetEditorManager::get).orElse(null) : null; // FORGE: redirect lookup to expanded map
Holder<WorldPreset> preset = this.getWorldType().preset();
- return preset != null ? PresetEditor.EDITORS.get(preset.unwrapKey()) : null;
+ return preset != null ? preset.unwrapKey().map(net.minecraftforge.client.PresetEditorManager::get).orElse(null) : null; // FORGE: redirect lookup to expanded map
}
public List<WorldCreationUiState.WorldTypeEntry> getNormalPresetList() {
Loaded 100 of 526 files, more files were not shown because too many files have changed in this diff. Show more