mirror of
https://github.com/MinecraftForge/MinecraftForge.git
synced 2026-11-14 01:04:24 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b0a68b8c79 | ||
|
|
f225d1f3fb | ||
|
|
15214a6e9a | ||
|
|
dfce6b62d4 | ||
|
|
0232ad7424 | ||
|
|
e6ff2e0c24 | ||
|
|
72980d2b03 | ||
|
|
6e3b51d81f | ||
|
|
6b1c628326 | ||
|
|
85f9b1012c | ||
|
|
7569de7004 | ||
|
|
5ccde7422c | ||
|
|
ad6bf9428e | ||
|
|
d176bab28e | ||
|
|
58d6f072e4 | ||
|
|
cc393818e4 | ||
|
|
e84098a011 | ||
|
|
f2db3941ea | ||
|
|
c2bfe12068 | ||
|
|
d9e3a0081e | ||
|
|
84d7a0facb | ||
|
|
9c66928854 | ||
|
|
4b494451db | ||
|
|
e9ea4ca169 | ||
|
|
c5ca054936 | ||
|
|
7d2c7c0f91 | ||
|
|
54b872af69 | ||
|
|
ae954bea17 | ||
|
|
ad59985a9b | ||
|
|
f8fdd43552 | ||
|
|
b269f7dbfd | ||
|
|
e688f136dd | ||
|
|
81276b341a | ||
|
|
f050617b47 | ||
|
|
e6c7958456 | ||
|
|
4802ceeccc | ||
|
|
2ea16d89ca | ||
|
|
e541201129 | ||
|
|
09aac3a4d3 | ||
|
|
dccb1ab3a4 | ||
|
|
8bf96d8738 | ||
|
|
069a94f205 | ||
|
|
332ed4ab76 | ||
|
|
030f2dbd38 | ||
|
|
ec3abdea14 | ||
|
|
4cf5508e04 | ||
|
|
d861f647d7 | ||
|
|
992f793695 | ||
|
|
69eb940764 | ||
|
|
c1f26d544a |
No files matched your search
@@ -0,0 +1,16 @@
|
||||
name: 'Comment actions'
|
||||
|
||||
on: issue_comment
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
pull-requests: write
|
||||
discussions: write
|
||||
|
||||
jobs:
|
||||
labels:
|
||||
uses: MinecraftForge/SharedActions/.github/workflows/comment_actions.yml@main
|
||||
secrets:
|
||||
GH_APP_KEY: ${{ secrets.GH_APP_KEY }}
|
||||
GH_APP_NAME: ${{ secrets.GH_APP_NAME }}
|
||||
@@ -0,0 +1,16 @@
|
||||
name: 'Issue actions'
|
||||
|
||||
on: issues
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
pull-requests: write
|
||||
discussions: write
|
||||
|
||||
jobs:
|
||||
labels:
|
||||
uses: MinecraftForge/SharedActions/.github/workflows/issue_actions.yml@main
|
||||
secrets:
|
||||
GH_APP_KEY: ${{ secrets.GH_APP_KEY }}
|
||||
GH_APP_NAME: ${{ secrets.GH_APP_NAME }}
|
||||
@@ -0,0 +1,16 @@
|
||||
name: 'Pull Request Review actions'
|
||||
|
||||
on: pull_request_review
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
pull-requests: write
|
||||
discussions: write
|
||||
|
||||
jobs:
|
||||
labels:
|
||||
uses: MinecraftForge/SharedActions/.github/workflows/pr_review_actions.yml@main
|
||||
secrets:
|
||||
GH_APP_KEY: ${{ secrets.GH_APP_KEY }}
|
||||
GH_APP_NAME: ${{ secrets.GH_APP_NAME }}
|
||||
@@ -0,0 +1,16 @@
|
||||
name: 'Pull Request actions'
|
||||
|
||||
on: pull_request
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
pull-requests: write
|
||||
discussions: write
|
||||
|
||||
jobs:
|
||||
labels:
|
||||
uses: MinecraftForge/SharedActions/.github/workflows/pr_actions.yml@main
|
||||
secrets:
|
||||
GH_APP_KEY: ${{ secrets.GH_APP_KEY }}
|
||||
GH_APP_NAME: ${{ secrets.GH_APP_NAME }}
|
||||
@@ -0,0 +1,16 @@
|
||||
name: 'Push actions'
|
||||
|
||||
on: push
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
pull-requests: write
|
||||
discussions: write
|
||||
|
||||
jobs:
|
||||
labels:
|
||||
uses: MinecraftForge/SharedActions/.github/workflows/push_actions.yml@main
|
||||
secrets:
|
||||
GH_APP_KEY: ${{ secrets.GH_APP_KEY }}
|
||||
GH_APP_NAME: ${{ secrets.GH_APP_NAME }}
|
||||
@@ -1,42 +0,0 @@
|
||||
name: 'Spam label'
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [labeled]
|
||||
pull_request:
|
||||
types: [labeled]
|
||||
discussion:
|
||||
types: [labeled]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
pull-requests: write
|
||||
discussions: write
|
||||
|
||||
jobs:
|
||||
lock:
|
||||
if: ${{ (github.event.label.name == 'spam') && (github.event.action == 'labeled') }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: peter-evans/close-issue@v2
|
||||
name: Close Issue
|
||||
with:
|
||||
close-reason: 'not_planned'
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
- run: gh pr close ${{ github.event.pull_request.html_url }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
name: Close PR
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
- uses: maxkomarychev/[email protected]
|
||||
name: Lock PR
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
with:
|
||||
token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
issue_number: ${{ github.event.number }}
|
||||
- uses: OSDKDev/[email protected]
|
||||
with:
|
||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
name: Lock issue
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
@@ -1,27 +0,0 @@
|
||||
name: Support Requests
|
||||
|
||||
on:
|
||||
issues:
|
||||
types:
|
||||
- labeled
|
||||
- unlabeled
|
||||
- reopened
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
|
||||
jobs:
|
||||
support:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: dessant/support-requests@v2
|
||||
with:
|
||||
support-label: Forum
|
||||
issue-comment: >
|
||||
:wave: We use the issue tracker exclusively for final bug reports and feature requests.
|
||||
However, this issue appears to be better suited for the
|
||||
[Forge Support Forums](https://forums.minecraftforge.net/) or
|
||||
[Forge Discord](https://discord.gg/UvedJ9m). Please create a new topic on the support forum
|
||||
with this issue or ask in the `#tech-support` channel in the Discord server, and the
|
||||
conversation can continue there.
|
||||
lock-issue: true
|
||||
+39
-16
@@ -1,4 +1,10 @@
|
||||
import groovy.json.JsonBuilder
|
||||
import net.minecraftforge.forge.tasks.checks.CheckATs
|
||||
import net.minecraftforge.forge.tasks.checks.CheckExcs
|
||||
import net.minecraftforge.forge.tasks.checks.CheckPatches
|
||||
import net.minecraftforge.forge.tasks.checks.CheckSAS
|
||||
import net.minecraftforge.forge.tasks.checks.CheckTask
|
||||
import net.minecraftforge.forge.tasks.checks.CheckMode
|
||||
|
||||
import java.nio.file.Files
|
||||
import java.util.Date
|
||||
@@ -56,7 +62,7 @@ ext {
|
||||
JETBRAINS_ANNOTATIONS_VERSION = '23.0.0'
|
||||
SLF4J_API_VERSION = '1.8.0-beta4'
|
||||
APACHE_MAVEN_ARTIFACT_VERSION = '3.8.5'
|
||||
JARJAR_VERSION = '0.3.18'
|
||||
JARJAR_VERSION = '0.3.19'
|
||||
|
||||
// These versions should be kept in sync with the version manifest JSON
|
||||
// To use a version newer than the version manifest JSON, the dependency must be added to the installer configuration
|
||||
@@ -1168,34 +1174,45 @@ project(':forge') {
|
||||
}
|
||||
}
|
||||
|
||||
task checkATs(type: CheckATs, dependsOn: [extractInheritance, createSrg2Mcp]) {
|
||||
inheritance = extractInheritance.output
|
||||
tasks.register('checkAll') {
|
||||
dependsOn 'checkLicenses'
|
||||
group = 'checks'
|
||||
}
|
||||
tasks.register('checkAllAndFix') {
|
||||
dependsOn 'findFieldInstanceChecks', 'checkLicenses'
|
||||
group = 'checks'
|
||||
}
|
||||
|
||||
CheckTask.registerTask(tasks, 'ATs', CheckATs) {
|
||||
dependsOn extractInheritance, createSrg2Mcp
|
||||
ats.from patcher.accessTransformers
|
||||
inheritance = extractInheritance.output
|
||||
mappings = createSrg2Mcp.output
|
||||
}
|
||||
|
||||
task checkSAS(type: CheckSAS, dependsOn: extractInheritance) {
|
||||
inheritance = extractInheritance.output
|
||||
CheckTask.registerTask(tasks, 'SAS', CheckSAS) {
|
||||
dependsOn extractInheritance
|
||||
sass.from patcher.sideAnnotationStrippers
|
||||
inheritance = extractInheritance.output
|
||||
}
|
||||
|
||||
task checkExcs(type: CheckExcs, dependsOn: jar) {
|
||||
CheckTask.registerTask(tasks, 'Excs', CheckExcs) {
|
||||
dependsOn jar
|
||||
binary = jar.archiveFile.get().asFile
|
||||
excs.from patcher.excs
|
||||
}
|
||||
|
||||
task checkPatchesAndFix(type: CheckPatches, dependsOn: genPatches) {
|
||||
CheckTask.registerTask(tasks, 'Patches', CheckPatches) {
|
||||
dependsOn genPatches
|
||||
patchDir = file("$rootDir/patches")
|
||||
autoFix = true
|
||||
}
|
||||
|
||||
task checkPatches(type: CheckPatches, dependsOn: genPatches) {
|
||||
patchDir = file("$rootDir/patches")
|
||||
autoFix = false
|
||||
patchesWithS2SArtifact = [
|
||||
'minecraft/net/minecraft/client/renderer/ViewArea.java.patch',
|
||||
'minecraft/net/minecraft/data/models/blockstates/Variant.java.patch',
|
||||
]
|
||||
}
|
||||
|
||||
genPatches {
|
||||
finalizedBy checkPatches
|
||||
finalizedBy checkAndFixPatches
|
||||
autoHeader true
|
||||
lineEnding = '\n'
|
||||
}
|
||||
@@ -1241,9 +1258,15 @@ project(':forge') {
|
||||
dependsOn 'setupCheckJarCompatibility'
|
||||
|
||||
baseJar = project.tasks.named('mergeBaseForgeJar').flatMap { it.output }
|
||||
}
|
||||
baseLibraries.from(project.tasks.named('createJoinedSRG').flatMap { it.output })
|
||||
|
||||
task checkAll(dependsOn: [checkATs, checkSAS, checkExcs, findFieldInstanceChecks, checkPatches, checkLicenses]){}
|
||||
inputJar = project.tasks.named('reobfJar').flatMap { it.output }
|
||||
concreteLibraries.from(project.PACKED_DEPS.collect { project.rootProject.tasks.getByPath(it).archiveFile })
|
||||
|
||||
commonLibraries.from(project.configurations.minecraftImplementation)
|
||||
commonLibraries.from(project.configurations.installer)
|
||||
commonLibraries.from(project.configurations.moduleonly)
|
||||
}
|
||||
|
||||
task launcherJson(type: LauncherJson) {
|
||||
packedDependencies = [':fmlloader:jar']
|
||||
|
||||
@@ -8,4 +8,5 @@ dependencies {
|
||||
implementation 'org.ow2.asm:asm-tree:9.3'
|
||||
implementation 'net.minecraftforge:srgutils:0.4.+'
|
||||
implementation 'commons-io:commons-io:2.8.0'
|
||||
implementation 'com.google.code.gson:gson:2.10'
|
||||
}
|
||||
@@ -1,267 +0,0 @@
|
||||
package net.minecraftforge.forge.tasks
|
||||
|
||||
import net.minecraftforge.srgutils.IMappingFile
|
||||
import org.gradle.api.file.ConfigurableFileCollection
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.tasks.InputFile
|
||||
import org.gradle.api.tasks.InputFiles
|
||||
import org.gradle.api.tasks.Optional
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.objectweb.asm.Opcodes
|
||||
|
||||
abstract class CheckATs extends DefaultTask {
|
||||
@InputFile abstract RegularFileProperty getInheritance()
|
||||
@InputFiles abstract ConfigurableFileCollection getAts()
|
||||
@InputFile @Optional abstract RegularFileProperty getMappings()
|
||||
|
||||
@TaskAction
|
||||
protected void exec() {
|
||||
def mappings = mappings.map { IMappingFile.load(it.asFile) }.getOrNull()
|
||||
Util.init()
|
||||
def parse = { line ->
|
||||
def idx = line.indexOf('#')
|
||||
def comment = idx == -1 ? null : line.substring(idx)
|
||||
if (idx != -1) line = line.substring(0, idx - 1)
|
||||
def (modifier, cls, desc) = (line.trim() + ' ').split(' ', -1)
|
||||
def key = cls + (desc.isEmpty() ? '' : ' ' + desc)
|
||||
return [modifier, cls, desc, comment, key]
|
||||
}
|
||||
def accessLevel = { access ->
|
||||
if ((access & Opcodes.ACC_PUBLIC) != 0) return 3
|
||||
if ((access & Opcodes.ACC_PROTECTED) != 0) return 2
|
||||
if ((access & Opcodes.ACC_PRIVATE) != 0) return 0
|
||||
return 1
|
||||
}
|
||||
def accessStr = { access ->
|
||||
if (access.endsWith('-f') || access.endsWith('+f'))
|
||||
return 4
|
||||
switch (access.toLowerCase()) {
|
||||
case 'public': return 3
|
||||
case 'protected': return 2
|
||||
case 'default': return 1
|
||||
case 'private': return 0
|
||||
default: return -1
|
||||
}
|
||||
}
|
||||
def json = inheritance.get().asFile.json()
|
||||
|
||||
ats.each { f ->
|
||||
TreeMap lines = [:]
|
||||
def group = null
|
||||
for (def line : f.readLines()) {
|
||||
if (line.isEmpty()) continue
|
||||
if (line.startsWith('#group ')) {
|
||||
def (modifier, cls, desc, comment, key) = parse.call(line.substring(7))
|
||||
|
||||
if (desc != '*' && desc != '*()' && desc != '<init>')
|
||||
throw new IllegalStateException('Invalid group: ' + line)
|
||||
|
||||
group = [modifier: modifier, cls: cls, desc: desc, comment: comment,
|
||||
existing: [] as Set,
|
||||
children: [] as TreeSet,
|
||||
group: true
|
||||
]
|
||||
if (lines.containsKey(key))
|
||||
throw new IllegalStateException('Duplicate group: ' + line)
|
||||
|
||||
lines[key] = group
|
||||
} else if (group != null) {
|
||||
if (line.startsWith('#endgroup')) {
|
||||
group = null
|
||||
} else {
|
||||
def (modifier, cls, desc, comment, key) = parse.call(line)
|
||||
group['existing'].add(key)
|
||||
}
|
||||
} else if (line.startsWith('#endgroup')) {
|
||||
throw new IllegalStateException('Invalid group ending: ' + line)
|
||||
} else if (line.startsWith('#')) {
|
||||
//Nom
|
||||
} else {
|
||||
def (modifier, cls, desc, comment, key) = parse.call(line)
|
||||
if (lines.containsKey(key)) {
|
||||
println('Duplicate: ' + line)
|
||||
continue
|
||||
}
|
||||
lines[key] = [modifier: modifier, cls: cls, desc: desc, comment: comment, group: false]
|
||||
}
|
||||
}
|
||||
|
||||
// Process Groups, this will remove any entries outside the group that is covered by the group
|
||||
for (def key : new ArrayList<>(lines.keySet())) {
|
||||
def entry = lines.get(key)
|
||||
if (entry != null && entry['group']) {
|
||||
def cls = entry['cls']
|
||||
def jcls = json.get(cls.replaceAll('\\.', '/'))
|
||||
if (jcls == null) {
|
||||
lines.remove(key)
|
||||
println('Invalid Group: ' + key)
|
||||
} else if ('*' == entry['desc']) {
|
||||
if (!jcls.containsKey('fields')) {
|
||||
lines.remove(key)
|
||||
println('Invalid Group, Class has no fields: ' + key)
|
||||
} else {
|
||||
jcls['fields'].each { field, value ->
|
||||
def fkey = cls + ' ' + field
|
||||
if (accessLevel.call(value['access']) < accessStr.call(entry['modifier'])) {
|
||||
if (lines.containsKey(fkey)) {
|
||||
lines.remove(fkey)
|
||||
} else if (!entry['existing'].contains(fkey)) {
|
||||
println('Added: ' + fkey)
|
||||
}
|
||||
entry['children'].add(fkey)
|
||||
} else if (lines.containsKey(fkey)) {
|
||||
lines.remove(fkey)
|
||||
println('Removed: ' + fkey)
|
||||
}
|
||||
}
|
||||
entry['existing'].stream().findAll{ !entry['children'].contains(it) }.each{ println('Removed: ' + it) }
|
||||
}
|
||||
} else if ('*()' == entry['desc']) {
|
||||
if (!jcls.containsKey('methods')) {
|
||||
lines.remove(key)
|
||||
println('Invalid Group, Class has no methods: ' + key)
|
||||
} else {
|
||||
jcls['methods'].each{ mtd, value ->
|
||||
if (mtd.startsWith('<clinit>') || mtd.startsWith('lambda$'))
|
||||
return
|
||||
key = cls + ' ' + mtd.replace(' ', '')
|
||||
if (accessLevel.call(value['access']) < accessStr.call(entry['modifier'])) {
|
||||
if (lines.containsKey(key)) {
|
||||
lines.remove(key)
|
||||
} else if (!entry['existing'].contains(key)) {
|
||||
println('Added: ' + key)
|
||||
}
|
||||
entry['children'].add(key)
|
||||
} else if (lines.containsKey(key)) {
|
||||
lines.remove(key)
|
||||
println('Removed: ' + key)
|
||||
}
|
||||
}
|
||||
entry['existing'].stream().findAll{ !entry['children'].contains(it) }.each{ println('Removed: ' + it) }
|
||||
}
|
||||
} else if ('<init>' == entry['desc']) { //Make all public non-abstract subclasses
|
||||
json.each{ tcls,value ->
|
||||
if (!value.containsKey('methods') || ((value['access'] & Opcodes.ACC_ABSTRACT) != 0))
|
||||
return
|
||||
def parents = [] as Set
|
||||
def parent = tcls
|
||||
while (parent != null && json.containsKey(parent)) {
|
||||
parents.add(parent)
|
||||
def p = json[parent]
|
||||
parent = p == null ? null : p['superName']
|
||||
}
|
||||
if (parents.contains(cls.replaceAll('\\.', '/'))) {
|
||||
value['methods'].each{ mtd, v ->
|
||||
if (mtd.startsWith('<init>')) {
|
||||
def child = tcls.replaceAll('/', '\\.') + ' ' + mtd.replace(' ', '')
|
||||
if (accessLevel.call(v['access']) < 3) {
|
||||
if (lines.containsKey(child)) {
|
||||
lines.remove(child)
|
||||
} else if (!entry['existing'].contains(child)) {
|
||||
println('Added: ' + child)
|
||||
}
|
||||
entry['children'].add(child)
|
||||
} else if (lines.containsKey(child)) {
|
||||
lines.remove(child)
|
||||
println('Removed: ' + child)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
entry['existing'].stream().findAll{ !entry['children'].contains(it) }.each{ println('Removed: ' + it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process normal lines, remove invalid and remove narrowing
|
||||
for (def key : new ArrayList<>(lines.keySet())) {
|
||||
def entry = lines.get(key)
|
||||
if (entry != null && !entry['group']) {
|
||||
def cls = entry['cls']
|
||||
def jcls = json.get(cls.replaceAll('\\.', '/'))
|
||||
if (jcls == null) {
|
||||
lines.remove(key)
|
||||
println('Invalid: ' + key)
|
||||
} else if (entry['desc'] == '') {
|
||||
if (accessLevel.call(jcls['access']) > accessStr.call(entry['modifier']) && (entry.comment == null || !entry.comment.startsWith('#force '))) {
|
||||
lines.remove(key)
|
||||
println('Invalid Narrowing: ' + key)
|
||||
}
|
||||
} else if (!entry['desc'].contains('(')) {
|
||||
if (!jcls.containsKey('fields') || !jcls['fields'].containsKey(entry['desc'])) {
|
||||
lines.remove(key)
|
||||
println('Invalid: ' + key)
|
||||
} else {
|
||||
def value = jcls['fields'][entry['desc']]
|
||||
if (accessLevel.call(value['access']) > accessStr.call(entry['modifier']) && (entry.comment == null || !entry.comment.startsWith('#force '))) {
|
||||
lines.remove(key)
|
||||
println('Invalid Narrowing: ' + key)
|
||||
println(entry.comment)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
def jdesc = entry['desc'].replace('(', ' (')
|
||||
if (!jcls.containsKey('methods') || !jcls['methods'].containsKey(jdesc)) {
|
||||
lines.remove(key)
|
||||
println('Invalid: ' + key)
|
||||
} else {
|
||||
def value = jcls['methods'][jdesc]
|
||||
if (accessLevel.call(value['access']) > accessStr.call(entry['modifier']) && (entry.comment == null || !entry.comment.startsWith('#force '))) {
|
||||
lines.remove(key)
|
||||
println('Invalid Narrowing: ' + key)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def data = []
|
||||
def remapComment = { entry ->
|
||||
if (!mappings || !entry || !entry.desc) return null
|
||||
def comment = entry.comment?.substring(1)?.trim()
|
||||
def jsonCls = json.get(entry.cls.replaceAll('\\.', '/'))
|
||||
def mappingsClass = mappings?.getClass(jsonCls.name)
|
||||
if (!mappingsClass) return entry.comment
|
||||
def idx = entry.desc.indexOf('(')
|
||||
def mappedName = idx == -1
|
||||
? mappingsClass.remapField(entry.desc)
|
||||
: mappingsClass.remapMethod(entry.desc.substring(0, idx), entry.desc.substring(idx))
|
||||
if (!mappedName) return entry.comment
|
||||
if (mappedName == '<init>')
|
||||
mappedName = 'constructor'
|
||||
|
||||
if (comment?.startsWith(mappedName))
|
||||
return '# ' + comment
|
||||
if (comment && comment.indexOf(' ') != -1) {
|
||||
def split = comment.split(" - ").toList()
|
||||
if (split[0].indexOf(' ') != -1)
|
||||
// The first string is more than one word, so append before it
|
||||
return "# ${mappedName} - ${comment}"
|
||||
split.remove(0)
|
||||
return "# ${mappedName} - ${String.join(' - ', split)}"
|
||||
}
|
||||
return '# ' + mappedName
|
||||
}
|
||||
lines.each { key,value ->
|
||||
if (!value.group) {
|
||||
def comment = remapComment.call(value)
|
||||
data.add(value.modifier + ' ' + key + (comment ? ' ' + comment : ''))
|
||||
} else {
|
||||
data.add('#group ' + value.modifier + ' ' + key + (value.comment == null ? '' : ' ' + value.comment))
|
||||
value.children.each {
|
||||
def line = value.modifier + ' ' + it
|
||||
def (modifier, cls, desc, comment) = parse.call(line)
|
||||
comment = remapComment.call([modifier: modifier, cls: cls, desc: desc, comment: comment])
|
||||
data.add(line + (comment ? ' ' + comment : ''))
|
||||
}
|
||||
data.add('#endgroup')
|
||||
}
|
||||
}
|
||||
f.text = data.join('\n')
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
package net.minecraftforge.forge.tasks
|
||||
|
||||
import org.gradle.api.file.ConfigurableFileCollection
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.tasks.InputFile
|
||||
import org.gradle.api.tasks.InputFiles
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
|
||||
import org.objectweb.asm.ClassReader
|
||||
import org.objectweb.asm.ClassVisitor
|
||||
import org.objectweb.asm.MethodVisitor
|
||||
import org.objectweb.asm.Opcodes
|
||||
import org.objectweb.asm.Type
|
||||
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipInputStream
|
||||
|
||||
abstract class CheckExcs extends DefaultTask {
|
||||
@InputFile abstract RegularFileProperty getBinary()
|
||||
@InputFiles abstract ConfigurableFileCollection getExcs()
|
||||
|
||||
@TaskAction
|
||||
protected void exec() {
|
||||
Util.init()
|
||||
def known = []
|
||||
binary.get().asFile.withInputStream { i ->
|
||||
new ZipInputStream(i).withCloseable { zin ->
|
||||
def visitor = new ClassVisitor(Opcodes.ASM9) {
|
||||
private String cls
|
||||
@Override
|
||||
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
|
||||
this.cls = name
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
|
||||
known.add(this.cls + '.' + name + descriptor)
|
||||
super.visitMethod(access, name, descriptor, signature, exceptions)
|
||||
}
|
||||
}
|
||||
ZipEntry zein
|
||||
while ((zein = zin.nextEntry) != null) {
|
||||
if (zein.name.endsWith('.class')) {
|
||||
ClassReader reader = new ClassReader(zin)
|
||||
reader.accept(visitor, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
excs.each { f ->
|
||||
def lines = []
|
||||
f.eachLine { line ->
|
||||
def idx = line.indexOf('#')
|
||||
if (idx == 0 || line.isEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
def comment = idx == -1 ? null : line.substring(idx)
|
||||
if (idx != -1) line = line.substring(0, idx - 1)
|
||||
|
||||
if (!line.contains('=')) {
|
||||
println('Invalid: ' + line)
|
||||
return
|
||||
}
|
||||
|
||||
def (key, value) = line.split('=', 2)
|
||||
if (!known.contains(key)) {
|
||||
println('Unknown: ' + line)
|
||||
return
|
||||
}
|
||||
|
||||
def (cls, desc) = key.split('\\.', 2)
|
||||
if (!desc.contains('(')) {
|
||||
println('Invalid: ' + line)
|
||||
return
|
||||
}
|
||||
def name = desc.split('\\(', 2)[0]
|
||||
desc = '(' + desc.split('\\(', 2)[1]
|
||||
|
||||
def (exceptions, args) = value.contains('|') ? value.split('|', 2) : [value, '']
|
||||
|
||||
if (args.split(',').length != Type.getArgumentTypes(desc).length) {
|
||||
println('Invalid: ' + line)
|
||||
return
|
||||
}
|
||||
lines.add(line)
|
||||
}
|
||||
f.text = lines.sort().join('\n')
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,219 +0,0 @@
|
||||
package net.minecraftforge.forge.tasks
|
||||
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.InputDirectory
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Paths
|
||||
import java.util.regex.Pattern
|
||||
|
||||
abstract class CheckPatches extends DefaultTask {
|
||||
@InputDirectory abstract DirectoryProperty getPatchDir()
|
||||
@Input boolean autoFix = false
|
||||
|
||||
@TaskAction
|
||||
protected void exec() {
|
||||
def hasS2SArtifact = [
|
||||
Paths.get("patches/minecraft/net/minecraft/client/renderer/ViewArea.java.patch"),
|
||||
Paths.get("patches/minecraft/net/minecraft/data/models/blockstates/Variant.java.patch")
|
||||
]
|
||||
|
||||
def verified = true;
|
||||
patchDir.get().asFileTree.each { patch ->
|
||||
def patchPath = project.rootDir.toPath().relativize(patch.toPath())
|
||||
verified &= verifyPatch(patch, autoFix, patchPath.toString(), hasS2SArtifact.contains(patchPath))
|
||||
}
|
||||
|
||||
if (!verified)
|
||||
throw new RuntimeException('One or more patches failed verification. Check the log for errors.')
|
||||
}
|
||||
|
||||
def verifyPatch(patch, fix, patchPath, hasS2SArtifact) {
|
||||
def hunk_start_pattern = Pattern.compile('^@@ -[0-9,]* \\+[0-9,_]* @@$')
|
||||
def white_space_pattern = Pattern.compile('^[+\\-]\\s*$')
|
||||
def import_pattern = Pattern.compile('^[+\\-]\\s*import.*')
|
||||
def field_pattern = Pattern.compile('^[+\\-][\\s]*((public|protected|private)[\\s]*)?(static[\\s]*)?(final)?([^=;]*)(=.*)?;\\s*$')
|
||||
def method_pattern = Pattern.compile('^[+\\-][\\s]*((public|protected|private)[\\s]*)?(static[\\s]*)?(final)?([^(]*)[(]([^)]*)?[)]\\s*[{]\\s*$')
|
||||
def class_pattern = Pattern.compile('^[+\\-][\\s]*((public|protected|private)[\\s]*)?(static[\\s]*)?(final[\\s]*)?(class|interface)([^{]*)[{]\\s*$')
|
||||
|
||||
def accessMap = [("private"):0, (null):1, ("protected"):2, ("public"):3]
|
||||
|
||||
def hasProblem = false;
|
||||
def lines = patch.readLines()
|
||||
|
||||
def hunksStart = 0
|
||||
def onlyWhiteSpace = false
|
||||
|
||||
def didFix = false
|
||||
def newLines = []
|
||||
def whiteSpaceErrors = []
|
||||
|
||||
// First two lines are file name ++/-- and we do not care
|
||||
newLines.add(lines[0] + '\n')
|
||||
newLines.add(lines[1] + '\n')
|
||||
|
||||
int i = 2
|
||||
for (; i < lines.size(); ++i) {
|
||||
def line = lines[i]
|
||||
newLines.add(line + '\n')
|
||||
|
||||
if (hunk_start_pattern.matcher(line).find()) {
|
||||
if (onlyWhiteSpace) {
|
||||
if (fix || hasS2SArtifact) {
|
||||
logger.lifecycle("Removing white space hunk starting at line {}, file: {}", hunksStart + 1, patchPath)
|
||||
def toRemove = i - hunksStart;
|
||||
while (toRemove-- > 0)
|
||||
newLines.remove(newLines.size() - 1)
|
||||
didFix = true
|
||||
}
|
||||
else {
|
||||
logger.lifecycle("Patch contains only white space hunk starting at line {}, file: {}", hunksStart + 1, patchPath)
|
||||
hasProblem = true
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!whiteSpaceErrors.empty)
|
||||
hasProblem = true
|
||||
whiteSpaceErrors.each { error -> logger.lifecycle(error) }
|
||||
whiteSpaceErrors.clear()
|
||||
}
|
||||
hunksStart = i
|
||||
onlyWhiteSpace = true
|
||||
continue
|
||||
}
|
||||
|
||||
if (line.startsWithAny('+','-')) {
|
||||
def prefixChange = false
|
||||
def prevLine = lines[i - 1]
|
||||
|
||||
if (line.charAt(0) == (char)'+' && prevLine.charAt(0) == (char)'-') {
|
||||
def prevTrim = prevLine.substring(1).replaceAll("\\s", "")
|
||||
def currTrim = line.substring(1).replaceAll("\\s", "")
|
||||
|
||||
if (prevTrim.equals(currTrim)) {
|
||||
prefixChange = true
|
||||
}
|
||||
|
||||
def pMatcher = field_pattern.matcher(prevLine)
|
||||
def cMatcher = field_pattern.matcher(line)
|
||||
|
||||
if (pMatcher.find() && cMatcher.find() &&
|
||||
pMatcher.group(6) == cMatcher.group(6) && // = ...
|
||||
pMatcher.group(5) == cMatcher.group(5) && // field name
|
||||
pMatcher.group(3) == cMatcher.group(3) && // static
|
||||
(accessMap[pMatcher.group(2)] < accessMap[cMatcher.group(2)] || pMatcher.group(4) != cMatcher.group(4))) {
|
||||
logger.lifecycle("Patch contains access changes or final removal at line {}, file: {}", i + 1, patchPath)
|
||||
hasProblem = true
|
||||
}
|
||||
|
||||
pMatcher = method_pattern.matcher(prevLine)
|
||||
cMatcher = method_pattern.matcher(line)
|
||||
|
||||
if (pMatcher.find() && cMatcher.find() &&
|
||||
pMatcher.group(6) == cMatcher.group(6) && // params
|
||||
pMatcher.group(5) == cMatcher.group(5) && // <T> void name
|
||||
pMatcher.group(3) == cMatcher.group(3) && // static
|
||||
(accessMap[pMatcher.group(2)] < accessMap[cMatcher.group(2)] || pMatcher.group(4) != cMatcher.group(4))) {
|
||||
logger.lifecycle("Patch contains access changes or final removal at line {}, file: {}", i + 1, patchPath)
|
||||
hasProblem = true
|
||||
}
|
||||
|
||||
pMatcher = class_pattern.matcher(prevLine)
|
||||
cMatcher = class_pattern.matcher(line)
|
||||
|
||||
if (pMatcher.find() && cMatcher.find() &&
|
||||
pMatcher.group(6) == cMatcher.group(6) && // ClassName<> extends ...
|
||||
pMatcher.group(5) == cMatcher.group(5) && // class | interface
|
||||
pMatcher.group(3) == cMatcher.group(3) && // static
|
||||
(accessMap[pMatcher.group(2)] < accessMap[cMatcher.group(2)] || pMatcher.group(4) != cMatcher.group(4))) {
|
||||
logger.lifecycle("Patch contains access changes or final removal at line {}, file: {}", i + 1, patchPath)
|
||||
hasProblem = true
|
||||
}
|
||||
}
|
||||
|
||||
if (line.charAt(0) == (char)'-' && i + 1 < lines.size()) {
|
||||
def nextLine = lines[i + 1]
|
||||
if (nextLine.charAt(0) == (char)'+') {
|
||||
def nextTrim = nextLine.substring(1).replaceAll("\\s", "")
|
||||
def currTrim = line.substring(1).replaceAll("\\s", "")
|
||||
|
||||
if (nextTrim.equals(currTrim)) {
|
||||
prefixChange = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def isWhiteSpaceChange = white_space_pattern.matcher(line).find()
|
||||
|
||||
if (!prefixChange && !isWhiteSpaceChange) {
|
||||
onlyWhiteSpace = hasS2SArtifact && import_pattern.matcher(line).find()
|
||||
}
|
||||
else if (isWhiteSpaceChange) {
|
||||
def prevLineChange = prevLine.startsWithAny('+','-')
|
||||
def nextLineChange = i + 1 < lines.size() && lines[i + 1].startsWithAny('+','-')
|
||||
|
||||
if (!prevLineChange && !nextLineChange) {
|
||||
whiteSpaceErrors.add(String.format("Patch contains white space change in valid hunk at line %d (cannot auto fix), file: %s", i + 1, patchPath))
|
||||
}
|
||||
}
|
||||
|
||||
if (line.contains("\t")) {
|
||||
if (!fix) {
|
||||
logger.lifecycle("Patch contains tabs on line {}, file: {}", i + 1, patchPath)
|
||||
hasProblem = true
|
||||
}
|
||||
else {
|
||||
logger.lifecycle("Fixing tabs on line {}, file: {}", i + 1, patchPath)
|
||||
line = line.replaceAll('\t', ' ')
|
||||
newLines.remove(newLines.size() - 1)
|
||||
newLines.add(line + '\n')
|
||||
didFix = true
|
||||
}
|
||||
}
|
||||
|
||||
if (import_pattern.matcher(line).find() && !hasS2SArtifact) {
|
||||
logger.lifecycle("Patch contains import change on line {}, file: {}", i + 1, patchPath)
|
||||
hasProblem = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (onlyWhiteSpace) {
|
||||
if (fix || hasS2SArtifact) {
|
||||
logger.lifecycle("Removing white space hunk starting at line {}, file: {}", hunksStart + 1, patchPath)
|
||||
def toRemove = i - hunksStart;
|
||||
while (toRemove-- > 0)
|
||||
newLines.remove(newLines.size() - 1)
|
||||
didFix = true
|
||||
}
|
||||
else {
|
||||
logger.lifecycle("Patch contains only white space hunk starting at line {}, file: {}", hunksStart + 1, patchPath)
|
||||
hasProblem = true
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!whiteSpaceErrors.empty)
|
||||
hasProblem = true
|
||||
whiteSpaceErrors.each { error -> logger.lifecycle(error) }
|
||||
}
|
||||
|
||||
if (didFix) {
|
||||
if (newLines.size() <= 2) {
|
||||
logger.lifecycle("Patch is now empty removing, file: {}", patchPath)
|
||||
Files.delete(patch.toPath())
|
||||
}
|
||||
else {
|
||||
if (!hasS2SArtifact)
|
||||
logger.lifecycle("*** Updating patch file. Please run setup then genPatches again. ***")
|
||||
patch.withWriter('UTF-8') { writer ->
|
||||
newLines.each { l -> writer.write(l) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return !hasProblem
|
||||
}
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
package net.minecraftforge.forge.tasks
|
||||
|
||||
import org.gradle.api.file.ConfigurableFileCollection
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.tasks.InputFile
|
||||
import org.gradle.api.tasks.InputFiles
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
|
||||
abstract class CheckSAS extends DefaultTask {
|
||||
@InputFile abstract RegularFileProperty getInheritance()
|
||||
@InputFiles abstract ConfigurableFileCollection getSass()
|
||||
|
||||
@TaskAction
|
||||
protected void exec() {
|
||||
Util.init()
|
||||
def json = inheritance.get().asFile.json()
|
||||
|
||||
sass.each { f ->
|
||||
def lines = []
|
||||
f.eachLine { line ->
|
||||
if (line[0] == '\t') return //Skip any tabbed lines, those are ones we add
|
||||
def idx = line.indexOf('#')
|
||||
if (idx == 0 || line.isEmpty()) {
|
||||
lines.add(line)
|
||||
return
|
||||
}
|
||||
def comment = idx == -1 ? null : line.substring(idx)
|
||||
if (idx != -1) line = line.substring(0, idx - 1)
|
||||
def (cls, name, desc) = (line.trim().replace('(', ' (') + ' ').split(' ', -1)
|
||||
cls = cls.replaceAll('\\.', '/')
|
||||
|
||||
if (json[cls] == null) {
|
||||
println('Invalid: ' + line)
|
||||
} else if (name.isEmpty()) { //Class SAS
|
||||
def toAdd = []
|
||||
def clsSided = isSided(json[cls])
|
||||
def 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
if (json[cls]['methods'] != null) {
|
||||
for (entry in json[cls]['methods']) {
|
||||
if (isSided(entry.value)) {
|
||||
sided = true
|
||||
toAdd.add('\t' + cls + ' ' + entry.key.replaceAll(' ', ''))
|
||||
findChildMethods(json, cls, entry.key).each { lines.add('\t' + it) }
|
||||
findChildMethods(json, cls, entry.key).each { println(line + ' -- ' + it) }
|
||||
} else if (clsSided) {
|
||||
findChildMethods(json, cls, entry.key).each { lines.add('\t' + it) }
|
||||
findChildMethods(json, cls, entry.key).each { println(line + ' -- ' + it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sided) {
|
||||
lines.add(cls + (comment == null ? '' : ' ' + comment))
|
||||
lines.addAll(toAdd.sort())
|
||||
} else
|
||||
println('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
|
||||
*/
|
||||
println('Invalid: ' + line)
|
||||
} else { // Methods
|
||||
if (json[cls]['methods'] == null || !isSided(json[cls]['methods'][name + ' ' + desc]))
|
||||
println('Invalid: ' + line)
|
||||
else {
|
||||
lines.add(cls + ' ' + name + desc + (comment == null ? '' : ' ' + comment))
|
||||
findChildMethods(json, cls, name + ' ' + desc).each { println(line + ' -- ' + it) }
|
||||
findChildMethods(json, cls, name + ' ' + desc).each { lines.add('\t' + it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
f.text = lines.join('\n')
|
||||
}
|
||||
}
|
||||
|
||||
protected static isSided(json) {
|
||||
if (json == null || json['annotations'] == null)
|
||||
return false
|
||||
for (ann in json['annotations']) {
|
||||
if ('Lnet/minecraftforge/api/distmarker/OnlyIn;'.equals(ann.desc))
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
protected static findChildMethods(json, cls, 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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package net.minecraftforge.forge.tasks
|
||||
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import groovy.transform.CompileStatic
|
||||
import groovy.transform.TupleConstructor
|
||||
|
||||
@CompileStatic
|
||||
@TupleConstructor
|
||||
final class InheritanceData implements Annotatable {
|
||||
String name
|
||||
int access
|
||||
String superName
|
||||
List<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
-8
@@ -18,6 +18,7 @@ abstract class SetupCheckJarCompatibility extends DefaultTask {
|
||||
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') })
|
||||
}
|
||||
@@ -31,17 +32,9 @@ abstract class SetupCheckJarCompatibility extends DefaultTask {
|
||||
}.toArray(Dependency[]::new))
|
||||
|
||||
project.tasks.named('checkJarCompatibility') {
|
||||
baseLibraries.from(project.tasks.createJoinedSRG.output)
|
||||
baseLibraries.from(project.provider {
|
||||
fmlLibs.resolvedConfiguration.lenientConfiguration.files
|
||||
})
|
||||
|
||||
inputJar = project.tasks.reobfJar.output
|
||||
concreteLibraries.from(project.PACKED_DEPS.collect { project.rootProject.tasks.getByPath(it).archiveFile })
|
||||
|
||||
commonLibraries.from(project.configurations.minecraftImplementation)
|
||||
commonLibraries.from(project.configurations.installer)
|
||||
commonLibraries.from(project.configurations.moduleonly)
|
||||
}
|
||||
|
||||
def baseForgeUserdev = project.layout.buildDirectory.dir(name).map { it.file("forge-${inputVersion}-userdev.jar") }.get().asFile
|
||||
|
||||
@@ -0,0 +1,310 @@
|
||||
package net.minecraftforge.forge.tasks.checks
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
import groovy.transform.TupleConstructor
|
||||
import net.minecraftforge.forge.tasks.InheritanceData
|
||||
import net.minecraftforge.srgutils.IMappingFile
|
||||
import org.gradle.api.file.ConfigurableFileCollection
|
||||
import org.gradle.api.file.RegularFile
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.tasks.InputFile
|
||||
import org.gradle.api.tasks.InputFiles
|
||||
import org.gradle.api.tasks.Optional
|
||||
import org.objectweb.asm.Opcodes
|
||||
|
||||
@CompileStatic
|
||||
abstract class CheckATs extends CheckTask {
|
||||
|
||||
@InputFile abstract RegularFileProperty getInheritance()
|
||||
@InputFiles abstract ConfigurableFileCollection getAts()
|
||||
@InputFile @Optional abstract RegularFileProperty getMappings()
|
||||
|
||||
@Override
|
||||
void check(Reporter reporter, boolean fix) {
|
||||
final IMappingFile mappings = mappings.map { RegularFile it -> IMappingFile.load(it.asFile) }.getOrNull()
|
||||
final inheritance = InheritanceData.parse(this.inheritance.get().asFile)
|
||||
|
||||
ats.each {
|
||||
final lines = process(it, reporter, inheritance)
|
||||
if (fix) {
|
||||
it.text = joinBack(lines, inheritance, mappings).join('\n')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static TreeMap<String, ATParser.Entry> process(File file, Reporter reporter, Map<String, InheritanceData> inheritance) {
|
||||
final TreeMap<String, ATParser.Entry> lines = ATParser.parse(file.readLines(), reporter)
|
||||
final Map<String, ATParser.Entry> constructorGroups = [:]
|
||||
|
||||
final itr = lines.entrySet().iterator()
|
||||
final toRemove = []
|
||||
while (itr.hasNext()) {
|
||||
final next = itr.next()
|
||||
String key = next.key
|
||||
final entry = next.value
|
||||
if (entry === null) continue
|
||||
|
||||
final binaryName = entry.cls.replaceAll('\\.', '/')
|
||||
|
||||
// Process Groups, this will remove any entries outside the group that is covered by the group
|
||||
if (entry.group) {
|
||||
final jcls = inheritance[binaryName]
|
||||
if (jcls === null) {
|
||||
itr.remove()
|
||||
reporter.report("Invalid group: $key")
|
||||
} else if ('*' == entry.desc) {
|
||||
if (!jcls.fields) {
|
||||
itr.remove()
|
||||
reporter.report("Invalid group, class has no fields: $key")
|
||||
} else {
|
||||
jcls.fields.each { field, value ->
|
||||
final fkey = entry.cls + ' ' + field
|
||||
if (accessLevel(value.access) < accessStr(entry.modifier)) {
|
||||
if (lines.containsKey(fkey)) {
|
||||
toRemove.add(fkey)
|
||||
} else if (!entry.existing.contains(fkey)) {
|
||||
reporter.report("Missing group entry: $fkey")
|
||||
}
|
||||
entry.children.add(fkey)
|
||||
} else if (lines.containsKey(fkey)) {
|
||||
toRemove.add(fkey)
|
||||
reporter.report("Found invalid group entry: $fkey")
|
||||
}
|
||||
}
|
||||
entry.existing.findAll { it !in entry.children }.each { println('Removed: ' + it) }
|
||||
}
|
||||
} else if ('*()' == entry.desc) {
|
||||
if (!jcls.methods) {
|
||||
itr.remove()
|
||||
reporter.report("Invalid group, class has no methods: $key")
|
||||
} else {
|
||||
jcls.methods.each { mtd, value ->
|
||||
if (mtd.startsWith('<clinit>') || mtd.startsWith('lambda$')) return
|
||||
key = entry.cls + ' ' + mtd.replace(' ', '')
|
||||
if (accessLevel(value.access) < accessStr(entry.modifier)) {
|
||||
if (lines.containsKey(key)) {
|
||||
toRemove.add(key)
|
||||
} else if (!entry.existing.contains(key)) {
|
||||
reporter.report("Missing group entry: $key")
|
||||
}
|
||||
entry.children.add(key)
|
||||
} else if (lines.containsKey(key)) {
|
||||
toRemove.add(key)
|
||||
reporter.report("Found invalid group entry: $key")
|
||||
}
|
||||
}
|
||||
entry.existing.findAll { it !in entry.children }.each { println('Removed: ' + it) }
|
||||
}
|
||||
} else if ('<init>' == entry.desc) { //Make all public non-abstract subclasses
|
||||
constructorGroups.put(binaryName, entry)
|
||||
}
|
||||
}
|
||||
|
||||
// Process normal lines, remove invalid and remove narrowing
|
||||
else {
|
||||
def jcls = inheritance.get(binaryName)
|
||||
if (jcls === null) {
|
||||
itr.remove()
|
||||
reporter.report("Invalid: $key")
|
||||
} else if (entry.desc == '') {
|
||||
if (accessLevel(jcls.access) > accessStr(entry.modifier) && (entry.comment === null || !entry.comment.startsWith('#force '))) {
|
||||
itr.remove()
|
||||
reporter.report("Invalid Narrowing: $key")
|
||||
}
|
||||
} else if (!entry.desc.contains('(')) {
|
||||
if (!jcls.fields || !jcls.fields.containsKey(entry.desc)) {
|
||||
itr.remove()
|
||||
reporter.report("Invalid: $key")
|
||||
} else {
|
||||
final value = jcls.fields[entry.desc]
|
||||
if (accessLevel(value.access) > accessStr(entry.modifier) && (entry.comment === null || !entry.comment.startsWith('#force '))) {
|
||||
itr.remove()
|
||||
reporter.report("Invalid Narrowing: $key - ${entry.comment}")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
final jdesc = entry.desc.replace('(', ' (')
|
||||
if (!jcls.methods || !jcls.methods.containsKey(jdesc)) {
|
||||
itr.remove()
|
||||
reporter.report("Invalid: $key")
|
||||
} else {
|
||||
final value = jcls.methods[jdesc]
|
||||
if (accessLevel(value.access) > accessStr(entry.modifier) && (entry.comment === null || !entry.comment.startsWith('#force '))) {
|
||||
itr.remove()
|
||||
reporter.report("Invalid Narrowing: $key")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inheritance.each { tcls, value ->
|
||||
if (!value.methods || ((value.access & Opcodes.ACC_ABSTRACT) !== 0)) return
|
||||
String parent = tcls
|
||||
while (parent !== null) {
|
||||
constructorGroups[parent]?.tap { entry ->
|
||||
value.methods.each { mtd, v ->
|
||||
if (mtd.startsWith('<init>')) {
|
||||
final child = tcls.replaceAll('/', '\\.') + ' ' + mtd.replace(' ', '')
|
||||
if (accessLevel(v.access) < 3) {
|
||||
if (lines.containsKey(child)) {
|
||||
toRemove.add(child)
|
||||
} else if (child !in entry.existing) {
|
||||
reporter.report("Missing group entry: $child")
|
||||
}
|
||||
entry.children.add(child)
|
||||
} else if (lines.containsKey(child)) {
|
||||
toRemove.add(child)
|
||||
reporter.report("Found invalid group entry: $child")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
parent = inheritance[parent]?.superName
|
||||
}
|
||||
}
|
||||
constructorGroups.values().each { entry -> entry.existing.findAll { it !in entry.children }.each{ reporter.report("Found invalid group entry: $it") } }
|
||||
|
||||
toRemove.each(lines.&remove)
|
||||
|
||||
return lines
|
||||
}
|
||||
|
||||
private static List<String> joinBack(TreeMap<String, ATParser.Entry> lines, Map<String, InheritanceData> inheritance, IMappingFile mappings) {
|
||||
final data = []
|
||||
final remapComment = { ATParser.Entry entry ->
|
||||
if (!mappings || !entry || !entry.desc) return null
|
||||
final comment = entry.comment?.substring(1)?.trim()
|
||||
final jsonCls = inheritance.get(entry.cls.replaceAll('\\.', '/'))
|
||||
final mappingsClass = mappings?.getClass(jsonCls.name)
|
||||
if (mappingsClass === null) return entry.comment
|
||||
final idx = entry.desc.indexOf('(')
|
||||
|
||||
String mappedName = idx == -1
|
||||
? mappingsClass.remapField(entry.desc)
|
||||
: mappingsClass.remapMethod(entry.desc.substring(0, idx), entry.desc.substring(idx))
|
||||
if (!mappedName) return entry.comment
|
||||
if (mappedName == '<init>')
|
||||
mappedName = 'constructor'
|
||||
|
||||
if (comment?.startsWith(mappedName))
|
||||
return '# ' + comment
|
||||
if (comment && comment.indexOf(' ') !== -1) {
|
||||
def split = comment.split(' - ').toList()
|
||||
if (split[0].indexOf(' ') !== -1)
|
||||
// The first string is more than one word, so append before it
|
||||
return "# ${mappedName} - ${comment}"
|
||||
split.remove(0)
|
||||
return "# ${mappedName} - ${String.join(' - ', split)}"
|
||||
}
|
||||
return '# ' + mappedName
|
||||
}
|
||||
lines.each { key, value ->
|
||||
if (!value.group) {
|
||||
def comment = remapComment.call(value)
|
||||
data.add(value.modifier + ' ' + key + (comment ? ' ' + comment : ''))
|
||||
} else {
|
||||
data.add(('#group ' + value.modifier + ' ' + key + ' ' + (value.comment ?: '')).trim())
|
||||
value.children.each {
|
||||
final line = value.modifier + ' ' + it
|
||||
final entry = ATParser.parseEntry(line)
|
||||
final comment = remapComment(entry)
|
||||
data.add(line + (comment ? ' ' + comment : ''))
|
||||
}
|
||||
data.add('#endgroup')
|
||||
}
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
static int accessStr(String access) {
|
||||
if (access.endsWith('-f') || access.endsWith('+f')) return 4
|
||||
switch (access.toLowerCase()) {
|
||||
case 'public': return 3
|
||||
case 'protected': return 2
|
||||
case 'default': return 1
|
||||
case 'private': return 0
|
||||
default: return -1
|
||||
}
|
||||
}
|
||||
|
||||
static int accessLevel(int access) {
|
||||
if ((access & Opcodes.ACC_PUBLIC) !== 0) return 3
|
||||
if ((access & Opcodes.ACC_PROTECTED) !== 0) return 2
|
||||
if ((access & Opcodes.ACC_PRIVATE) !== 0) return 0
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
@CompileStatic
|
||||
class ATParser {
|
||||
static TreeMap<String, Entry> parse(List<String> lines, CheckTask.Reporter reporter) {
|
||||
TreeMap<String, Entry> outLines = new TreeMap<>()
|
||||
Entry group = null
|
||||
for (final line : lines) {
|
||||
if (line.isEmpty()) continue
|
||||
if (line.startsWith('#group ')) {
|
||||
final entry = parseEntry(line.substring(7))
|
||||
|
||||
if (entry.desc != '*' && entry.desc != '*()' && entry.desc != '<init>') {
|
||||
reporter.report("Invalid group: $line", false)
|
||||
}
|
||||
|
||||
entry.group = true
|
||||
entry.children = []
|
||||
entry.existing = []
|
||||
|
||||
group = entry
|
||||
|
||||
if (outLines.containsKey(entry.key)) {
|
||||
reporter.report("Duplicate group: $line", false)
|
||||
}
|
||||
|
||||
outLines[entry.key] = group
|
||||
} else if (group !== null) {
|
||||
if (line.startsWith('#endgroup')) {
|
||||
group = null
|
||||
} else {
|
||||
final key = parseEntry(line).key
|
||||
group.existing.add(key)
|
||||
}
|
||||
} else if (line.startsWith('#endgroup')) {
|
||||
reporter.report("Invalid group ending: $line", false)
|
||||
} else if (line.startsWith('#')) {
|
||||
//Nom
|
||||
} else {
|
||||
final entry = parseEntry(line)
|
||||
if (outLines.containsKey(entry.key)) {
|
||||
reporter.report("Found duplicate: $line")
|
||||
continue
|
||||
}
|
||||
outLines[entry.key] = entry
|
||||
}
|
||||
}
|
||||
return outLines
|
||||
}
|
||||
|
||||
static Entry parseEntry(String line) {
|
||||
final idx = line.indexOf('#')
|
||||
final String comment = idx === -1 ? null : line.substring(idx)
|
||||
if (idx !== -1) line = line.substring(0, idx - 1)
|
||||
final data = (line.trim() + ' ').split(' ', -1)
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package net.minecraftforge.forge.tasks.checks
|
||||
|
||||
import org.gradle.api.file.ConfigurableFileCollection
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.tasks.InputFile
|
||||
import org.gradle.api.tasks.InputFiles
|
||||
import org.objectweb.asm.ClassReader
|
||||
import org.objectweb.asm.ClassVisitor
|
||||
import org.objectweb.asm.MethodVisitor
|
||||
import org.objectweb.asm.Opcodes
|
||||
import org.objectweb.asm.Type
|
||||
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipInputStream
|
||||
|
||||
abstract class CheckExcs extends CheckTask {
|
||||
|
||||
@InputFile abstract RegularFileProperty getBinary()
|
||||
@InputFiles abstract ConfigurableFileCollection getExcs()
|
||||
|
||||
@Override
|
||||
void check(Reporter reporter, boolean fix) {
|
||||
final Set<String> known = []
|
||||
collectKnown(known)
|
||||
|
||||
excs.each { f ->
|
||||
final lines = []
|
||||
f.eachLine { line ->
|
||||
def idx = line.indexOf('#')
|
||||
if (idx == 0 || line.isEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
if (idx != -1) line = line.substring(0, idx - 1)
|
||||
|
||||
if (!line.contains('=')) {
|
||||
reporter.report("Invalid: $line")
|
||||
return
|
||||
}
|
||||
|
||||
def (String key, String value) = line.split('=', 2)
|
||||
if (!known.contains(key)) {
|
||||
reporter.report("Unknown: $line")
|
||||
return
|
||||
}
|
||||
|
||||
String desc = key.split('\\.', 2)[1]
|
||||
if (!desc.contains('(')) {
|
||||
reporter.report("Invalid: $line")
|
||||
return
|
||||
}
|
||||
desc = '(' + desc.split('\\(', 2)[1]
|
||||
|
||||
def (exceptions, String args) = value.contains('|') ? value.split('|', 2) : [value, '']
|
||||
|
||||
if (args.split(',').length !== Type.getArgumentTypes(desc).length) {
|
||||
reporter.report("Invalid: $line")
|
||||
return
|
||||
}
|
||||
lines.add(line)
|
||||
}
|
||||
|
||||
if (fix) f.text = lines.sort().join('\n')
|
||||
}
|
||||
}
|
||||
|
||||
private void collectKnown(Collection<String> known) {
|
||||
binary.get().asFile.withInputStream { i ->
|
||||
new ZipInputStream(i).withCloseable { zin ->
|
||||
final visitor = new ClassVisitor(Opcodes.ASM9) {
|
||||
private String cls
|
||||
@Override
|
||||
void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
|
||||
this.cls = name
|
||||
}
|
||||
|
||||
@Override
|
||||
MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
|
||||
known.add(this.cls + '.' + name + descriptor)
|
||||
super.visitMethod(access, name, descriptor, signature, exceptions)
|
||||
}
|
||||
}
|
||||
ZipEntry zein
|
||||
while ((zein = zin.nextEntry) !== null) {
|
||||
if (zein.name.endsWith('.class')) {
|
||||
ClassReader reader = new ClassReader(zin)
|
||||
reader.accept(visitor, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package net.minecraftforge.forge.tasks.checks
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
|
||||
@CompileStatic
|
||||
enum CheckMode {
|
||||
CHECK,
|
||||
FIX
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
package net.minecraftforge.forge.tasks.checks
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.provider.ListProperty
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.InputDirectory
|
||||
import org.gradle.api.tasks.Optional
|
||||
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.util.regex.Pattern
|
||||
|
||||
@CompileStatic
|
||||
abstract class CheckPatches extends CheckTask {
|
||||
|
||||
private static final Pattern HUNK_START_PATTERN = Pattern.compile('^@@ -[0-9,]* \\+[0-9,_]* @@$')
|
||||
private static final Pattern WHITESPACE_PATTERN = Pattern.compile('^[+\\-]\\s*$')
|
||||
private static final Pattern IMPORT_PATTERN = Pattern.compile('^[+\\-]\\s*import.*')
|
||||
private static final Pattern FIELD_PATTERN = Pattern.compile('^[+\\-][\\s]*((public|protected|private)[\\s]*)?(static[\\s]*)?(final)?([^=;]*)(=.*)?;\\s*$')
|
||||
private static final Pattern METHOD_PATTERN = Pattern.compile('^[+\\-][\\s]*((public|protected|private)[\\s]*)?(static[\\s]*)?(final)?([^(]*)[(]([^)]*)?[)]\\s*[{]\\s*$')
|
||||
private static final Pattern CLASS_PATTERN = Pattern.compile('^[+\\-][\\s]*((public|protected|private)[\\s]*)?(static[\\s]*)?(final[\\s]*)?(class|interface)([^{]*)[{]\\s*$')
|
||||
private static final Map<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('\\', '/')))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void verifyPatch(Path patch, Reporter reporter, boolean fix, String patchPath, boolean hasS2SArtifact) {
|
||||
final oldFixedErrors = reporter.fixed.size()
|
||||
|
||||
final lines = Files.readAllLines(patch)
|
||||
|
||||
int hunksStart = 0
|
||||
boolean onlyWhiteSpace = false
|
||||
|
||||
final List<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
|
||||
(ACCESS_MAP[pMatcher.group(2)] < ACCESS_MAP[cMatcher.group(2)] || pMatcher.group(4) != cMatcher.group(4))) {
|
||||
reporter.report("Patch contains access changes or final removal at line ${i + 1}, file: $patchPath", false)
|
||||
}
|
||||
|
||||
pMatcher = METHOD_PATTERN.matcher(prevLine)
|
||||
cMatcher = METHOD_PATTERN.matcher(line)
|
||||
|
||||
if (pMatcher.find() && cMatcher.find() &&
|
||||
pMatcher.group(6) == cMatcher.group(6) && // params
|
||||
pMatcher.group(5) == cMatcher.group(5) && // <T> void name
|
||||
pMatcher.group(3) == cMatcher.group(3) && // static
|
||||
(ACCESS_MAP[pMatcher.group(2)] < ACCESS_MAP[cMatcher.group(2)] || pMatcher.group(4) != cMatcher.group(4))) {
|
||||
reporter.report("Patch contains access changes or final removal at line ${i + 1}, file: $patchPath", false)
|
||||
}
|
||||
|
||||
pMatcher = CLASS_PATTERN.matcher(prevLine)
|
||||
cMatcher = CLASS_PATTERN.matcher(line)
|
||||
|
||||
if (pMatcher.find() && cMatcher.find() &&
|
||||
pMatcher.group(6) == cMatcher.group(6) && // ClassName<> extends ...
|
||||
pMatcher.group(5) == cMatcher.group(5) && // class | interface
|
||||
pMatcher.group(3) == cMatcher.group(3) && // static
|
||||
(ACCESS_MAP[pMatcher.group(2)] < ACCESS_MAP[cMatcher.group(2)] || pMatcher.group(4) != cMatcher.group(4))) {
|
||||
reporter.report("Patch contains access changes or final removal at line ${i + 1}, file: $patchPath", false)
|
||||
}
|
||||
}
|
||||
|
||||
if (line.charAt(0) == (char)'-' && i + 1 < lines.size()) {
|
||||
final nextLine = lines[i + 1]
|
||||
if (nextLine.charAt(0) == (char)'+') {
|
||||
final nextTrim = nextLine.substring(1).replaceAll("\\s", "")
|
||||
final currTrim = line.substring(1).replaceAll("\\s", "")
|
||||
|
||||
if (nextTrim == currTrim) {
|
||||
prefixChange = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final isWhiteSpaceChange = WHITESPACE_PATTERN.matcher(line).find()
|
||||
|
||||
if (!prefixChange && !isWhiteSpaceChange) {
|
||||
onlyWhiteSpace = hasS2SArtifact && IMPORT_PATTERN.matcher(line).find()
|
||||
} else if (isWhiteSpaceChange) {
|
||||
final prevLineChange = prevLine.startsWithAny('+','-')
|
||||
final nextLineChange = i + 1 < lines.size() && lines[i + 1].startsWithAny('+','-')
|
||||
|
||||
if (!prevLineChange && !nextLineChange) {
|
||||
reporter.report("Patch contains white space change in valid hunk at line ${i + 1}, file: $patchPath", false)
|
||||
}
|
||||
}
|
||||
|
||||
if (line.contains('\t')) {
|
||||
reporter.report("Patch contains tabs on line ${i + 1}, file: $patchPath")
|
||||
line = line.replaceAll('\t', ' ')
|
||||
newLines.remove(newLines.size() - 1)
|
||||
newLines.add(line + '\n')
|
||||
}
|
||||
|
||||
if (IMPORT_PATTERN.matcher(line).find() && !hasS2SArtifact) {
|
||||
reporter.report("Patch contains import change on line ${i + 1}, file: $patchPath", false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (onlyWhiteSpace) {
|
||||
if (!hasS2SArtifact)
|
||||
reporter.report("Patch contains only white space hunk starting at line ${hunksStart + 1}, file: $patchPath")
|
||||
def toRemove = i - hunksStart;
|
||||
while (toRemove-- > 0)
|
||||
newLines.remove(newLines.size() - 1)
|
||||
}
|
||||
|
||||
if ((reporter.fixed.size() > oldFixedErrors && fix) || hasS2SArtifact) {
|
||||
if (newLines.size() <= 2) {
|
||||
logger.lifecycle("Patch is now empty removing, file: {}", patchPath)
|
||||
Files.delete(patch)
|
||||
}
|
||||
else {
|
||||
if (!hasS2SArtifact)
|
||||
logger.lifecycle("*** Updating patch file. Please run setup then genPatches again. ***")
|
||||
Files.newBufferedWriter(patch).withCloseable {
|
||||
newLines.each { l -> it.write(l) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package net.minecraftforge.forge.tasks.checks
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
import net.minecraftforge.forge.tasks.Annotatable
|
||||
import net.minecraftforge.forge.tasks.InheritanceData
|
||||
import org.gradle.api.file.ConfigurableFileCollection
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.tasks.InputFile
|
||||
import org.gradle.api.tasks.InputFiles
|
||||
|
||||
@CompileStatic
|
||||
abstract class CheckSAS extends CheckTask {
|
||||
|
||||
@InputFile abstract RegularFileProperty getInheritance()
|
||||
@InputFiles abstract ConfigurableFileCollection getSass()
|
||||
|
||||
@Override
|
||||
void check(Reporter reporter, boolean fix) {
|
||||
final inheritance = InheritanceData.parse(this.inheritance.get().asFile)
|
||||
|
||||
sass.each { f ->
|
||||
final lines = []
|
||||
f.eachLine { line ->
|
||||
if (line[0] == '\t') return // Skip any tabbed lines, those are ones we add
|
||||
final idx = line.indexOf('#')
|
||||
if (idx == 0 || line.isEmpty()) {
|
||||
lines.add(line)
|
||||
return
|
||||
}
|
||||
def comment = idx == -1 ? null : line.substring(idx)
|
||||
if (idx != -1) line = line.substring(0, idx - 1)
|
||||
final spl = (line.trim().replace('(', ' (') + ' ').split(' ', -1)
|
||||
def (String cls, String name, String desc) = [spl[0], spl[1], spl[2]]
|
||||
cls = cls.replaceAll('\\.', '/')
|
||||
|
||||
if (inheritance[cls] === null) {
|
||||
reporter.report("Invalid: $line")
|
||||
} else if (name.isEmpty()) { //Class SAS
|
||||
final toAdd = []
|
||||
final clsSided = isSided(inheritance[cls])
|
||||
boolean sided = clsSided
|
||||
|
||||
/* TODO: MergeTool doesn't do fields
|
||||
if (json[cls]['fields'] != null) {
|
||||
for (entry in json[cls]['fields']) {
|
||||
if (isSided(entry.value)) {
|
||||
sided = true
|
||||
toAdd.add('\t' + cls + ' ' + entry.key)
|
||||
}
|
||||
}
|
||||
} */
|
||||
|
||||
final clsInh = inheritance[cls]
|
||||
if (clsInh.methods) {
|
||||
for (entry in clsInh.methods) {
|
||||
if (isSided(entry.value)) {
|
||||
sided = true
|
||||
toAdd.add('\t' + cls + ' ' + entry.key.replaceAll(' ', ''))
|
||||
findChildMethods(inheritance, cls, entry.key).each { lines.add('\t' + it) }
|
||||
findChildMethods(inheritance, cls, entry.key).each { println(line + ' -- ' + it) }
|
||||
} else if (clsSided) {
|
||||
findChildMethods(inheritance, cls, entry.key).each { lines.add('\t' + it) }
|
||||
findChildMethods(inheritance, cls, entry.key).each { println(line + ' -- ' + it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sided) {
|
||||
lines.add(cls + (comment == null ? '' : ' ' + comment))
|
||||
lines.addAll(toAdd.sort())
|
||||
} else {
|
||||
reporter.report("Invalid: $line")
|
||||
}
|
||||
|
||||
} else if (desc.isEmpty()) { // Fields
|
||||
/* TODO: MergeTool doesn't do fields
|
||||
if (json[cls]['fields'] != null && isSided(json[cls]['fields'][name]))
|
||||
lines.add(cls + ' ' + name + (comment == null ? '' : ' ' + comment))
|
||||
else */
|
||||
reporter.report("Invalid: $line")
|
||||
} else { // Methods
|
||||
final clsInh = inheritance[cls]
|
||||
if (clsInh.methods === null || !isSided(clsInh.methods[name + ' ' + desc]))
|
||||
reporter.report("Invalid: $line")
|
||||
else {
|
||||
lines.add(cls + ' ' + name + desc + (comment == null ? '' : ' ' + comment))
|
||||
findChildMethods(inheritance, cls, name + ' ' + desc).each { println(line + ' -- ' + it) }
|
||||
findChildMethods(inheritance, cls, name + ' ' + desc).each { lines.add('\t' + it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fix) f.text = lines.join('\n')
|
||||
}
|
||||
}
|
||||
|
||||
protected static isSided(Annotatable annotatable) {
|
||||
if (annotatable === null) return false
|
||||
for (ann in annotatable.annotations) {
|
||||
if ('Lnet/minecraftforge/api/distmarker/OnlyIn;' == ann.desc)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
protected static findChildMethods(Map<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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package net.minecraftforge.forge.tasks.checks
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
import net.minecraftforge.forge.tasks.Util
|
||||
import org.gradle.api.Action
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.logging.LogLevel
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.gradle.api.tasks.TaskContainer
|
||||
import org.gradle.api.tasks.VerificationTask
|
||||
|
||||
@CompileStatic
|
||||
abstract class CheckTask extends DefaultTask implements VerificationTask {
|
||||
@Input
|
||||
abstract Property<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()
|
||||
|
||||
final doFix = getMode().get() === CheckMode.FIX
|
||||
final Reporter reporter = new Reporter(doFix)
|
||||
check(reporter, doFix)
|
||||
|
||||
if (reporter.messages) {
|
||||
if (getMode().get() === CheckMode.CHECK) {
|
||||
logger.error("Check task '{}' found errors:\n{}", name, reporter.messages.join('\n'))
|
||||
if (!ignoreFailures) {
|
||||
throw new IllegalArgumentException("${reporter.messages.size()} errors were found!")
|
||||
}
|
||||
} else {
|
||||
if (logger.isEnabled(LogLevel.DEBUG)) {
|
||||
logger.warn("Check task '{}' found {} errors and fixed {}:\n{}", name, reporter.messages.size(), reporter.fixed.size(), reporter.fixed.join('\n'))
|
||||
} else {
|
||||
logger.warn("Check task '{}' found {} errors and fixed {}.", name, reporter.messages.size(), reporter.fixed.size())
|
||||
}
|
||||
|
||||
if (reporter.notFixed) {
|
||||
logger.error('{} errors could not be fixed:\n{}', reporter.notFixed.size(), reporter.notFixed.join('\n'))
|
||||
if (!ignoreFailures) {
|
||||
throw new IllegalArgumentException("${reporter.notFixed.size()} errors which cannot be fixed were found!")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract void check(Reporter reporter, boolean fix)
|
||||
|
||||
@CompileStatic
|
||||
static final class Reporter {
|
||||
final boolean trackFixed
|
||||
Reporter(boolean trackFixed) {
|
||||
this.trackFixed = trackFixed
|
||||
}
|
||||
|
||||
public final List<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') Closure configuration) {
|
||||
taskName = taskName.capitalize()
|
||||
tasks.register("check$taskName", clazz) { CheckTask task ->
|
||||
configuration.setDelegate((T) task)
|
||||
configuration.call(task)
|
||||
task.mode.set(CheckMode.CHECK)
|
||||
task.group = 'checks'
|
||||
}
|
||||
tasks.named('checkAll').configure { it.dependsOn("check$taskName") }
|
||||
|
||||
tasks.register("checkAndFix$taskName", clazz) { CheckTask task ->
|
||||
configuration.setDelegate((T) task)
|
||||
configuration.call(task)
|
||||
task.mode.set(CheckMode.FIX)
|
||||
task.group = 'checks'
|
||||
}
|
||||
tasks.named('checkAllAndFix').configure { it.dependsOn("checkAndFix$taskName") }
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -2,8 +2,8 @@
|
||||
|
||||
MinecraftForge
|
||||
=============
|
||||
[](https://files.minecraftforge.net)
|
||||
[](https://files.minecraftforge.net)
|
||||
[](https://files.minecraftforge.net) [](https://discord.gg/UvedJ9m) [](https://www.patreon.com/LexManos)
|
||||
|
||||
Forge is a free, open-source modding API all of your favourite mods use!
|
||||
|
||||
+1
-1
@@ -31,7 +31,6 @@ public abstract class CommonDevLaunchHandler extends CommonLaunchHandler {
|
||||
// The extra jar is on the classpath, so try and pull it out of the legacy classpath
|
||||
var legacyCP = Objects.requireNonNull(System.getProperty("legacyClassPath"), "Missing legacyClassPath, cannot find client-extra").split(File.pathSeparator);
|
||||
var extra = findJarOnClasspath(legacyCP, "client-extra");
|
||||
mcstream.add(extra);
|
||||
|
||||
// The MC code/Patcher edits are in exploded directories
|
||||
final var modstream = Stream.<List<Path>>builder();
|
||||
@@ -42,6 +41,7 @@ public abstract class CommonDevLaunchHandler extends CommonLaunchHandler {
|
||||
minecraft.stream().distinct().forEach(mcstream::add);
|
||||
mods.values().forEach(modstream::add);
|
||||
|
||||
mcstream.add(extra);
|
||||
var mcFilter = getMcFilter(extra, minecraft, modstream);
|
||||
return new LocatedPaths(mcstream.build().toList(), mcFilter, modstream.build().toList(), getFmlStuff(legacyCP));
|
||||
}
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ minecraft {
|
||||
// See more information here: https://github.com/MinecraftForge/MCPConfig/blob/master/Mojang.md
|
||||
//
|
||||
// Parchment is an unofficial project maintained by ParchmentMC, separate from MinecraftForge
|
||||
// Additional setup is needed to use their mappings: https://github.com/ParchmentMC/Parchment/wiki/Getting-Started
|
||||
// Additional setup is needed to use their mappings: https://parchmentmc.org/docs/getting-started
|
||||
//
|
||||
// Use non-default mappings at your own risk. They may not always work.
|
||||
// Simply re-run your setup task after changing the mappings to update your workspace.
|
||||
|
||||
@@ -58,8 +58,7 @@ public class ExampleMod
|
||||
MinecraftForge.EVENT_BUS.register(this);
|
||||
|
||||
// Register the item to a creative tab
|
||||
modEventBus.addListener((CreativeModeTabEvent.BuildContents event) -> event.registerSimple(CreativeModeTabs.BUILDING_BLOCKS,
|
||||
EXAMPLE_BLOCK_ITEM.get()));
|
||||
modEventBus.addListener(this::addCreative);
|
||||
}
|
||||
|
||||
private void commonSetup(final FMLCommonSetupEvent event)
|
||||
@@ -69,6 +68,12 @@ public class ExampleMod
|
||||
LOGGER.info("DIRT BLOCK >> {}", ForgeRegistries.BLOCKS.getKey(Blocks.DIRT));
|
||||
}
|
||||
|
||||
private void addCreative(CreativeModeTabEvent.BuildContents event)
|
||||
{
|
||||
if (event.getTab() == CreativeModeTabs.BUILDING_BLOCKS)
|
||||
event.accept(EXAMPLE_BLOCK_ITEM);
|
||||
}
|
||||
|
||||
// You can use SubscribeEvent and let the Event Bus discover methods to call
|
||||
@SubscribeEvent
|
||||
public void onServerStarting(ServerStartingEvent event)
|
||||
|
||||
@@ -22,7 +22,7 @@ modId="examplemod" #mandatory
|
||||
version="${file.jarVersion}" #mandatory
|
||||
# A display name for the mod
|
||||
displayName="Example Mod" #mandatory
|
||||
# A URL to query for updates for this mod. See the JSON update specification https://mcforge.readthedocs.io/en/latest/gettingstarted/autoupdate/
|
||||
# A URL to query for updates for this mod. See the JSON update specification https://docs.minecraftforge.net/en/latest/misc/updatechecker/
|
||||
#updateJSONURL="https://change.me.example.invalid/updates.json" #optional
|
||||
# A URL for the "homepage" for this mod, displayed in the mod UI
|
||||
#displayURL="https://change.me.to.your.mods.homepage.example.invalid/" #optional
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
--- a/net/minecraft/client/gui/components/Button.java
|
||||
+++ b/net/minecraft/client/gui/components/Button.java
|
||||
@@ -29,6 +_,11 @@
|
||||
this.f_252416_ = p_259552_;
|
||||
}
|
||||
|
||||
+ protected Button(Builder builder) {
|
||||
+ this(builder.f_252538_, builder.f_252462_, builder.f_252510_, builder.f_252447_, builder.f_252401_, builder.f_252468_, builder.f_252431_);
|
||||
+ m_257544_(builder.f_256855_); // Forge: Make use of the Builder tooltip
|
||||
+ }
|
||||
+
|
||||
public void m_5691_() {
|
||||
this.f_93717_.m_93750_(this);
|
||||
}
|
||||
@@ -92,9 +_,11 @@
|
||||
}
|
||||
|
||||
public Button m_253136_() {
|
||||
- Button button = new Button(this.f_252538_, this.f_252462_, this.f_252510_, this.f_252447_, this.f_252401_, this.f_252468_, this.f_252431_);
|
||||
- button.m_257544_(this.f_256855_);
|
||||
- return button;
|
||||
+ return build(Button::new);
|
||||
+ }
|
||||
+
|
||||
+ public Button build(java.util.function.Function<Builder, Button> builder) {
|
||||
+ return builder.apply(this);
|
||||
}
|
||||
}
|
||||
|
||||
+21
-8
@@ -18,7 +18,7 @@
|
||||
this.m_98630_();
|
||||
} else {
|
||||
(this.f_97732_).f_98639_.addAll(p_261591_);
|
||||
@@ -273,6 +_,30 @@
|
||||
@@ -273,6 +_,34 @@
|
||||
protected void m_7856_() {
|
||||
if (this.f_96541_.f_91072_.m_105290_()) {
|
||||
super.m_7856_();
|
||||
@@ -45,6 +45,10 @@
|
||||
+ if (this.pages.size() > 1) {
|
||||
+ m_142416_(net.minecraft.client.gui.components.Button.m_253074_(Component.m_237113_("<"), b -> setCurrentPage(this.pages.get(Math.max(this.pages.indexOf(this.currentPage) - 1, 0)))).m_252794_(f_97735_, f_97736_ - 50).m_253046_(20, 20).m_253136_());
|
||||
+ m_142416_(net.minecraft.client.gui.components.Button.m_253074_(Component.m_237113_(">"), b -> setCurrentPage(this.pages.get(Math.min(this.pages.indexOf(this.currentPage) + 1, this.pages.size() - 1)))).m_252794_(f_97735_ + f_97726_ - 20, f_97736_ - 50).m_253046_(20, 20).m_253136_());
|
||||
+ }
|
||||
+ this.currentPage = this.pages.stream().filter(page -> page.getVisibleTabs().contains(f_98507_)).findFirst().orElse(this.currentPage);
|
||||
+ if (!this.currentPage.getVisibleTabs().contains(f_98507_)) {
|
||||
+ f_98507_ = this.currentPage.getVisibleTabs().get(0);
|
||||
+ }
|
||||
this.f_98510_ = new EditBox(this.f_96547_, this.f_97735_ + 82, this.f_97736_ + 6, 80, 9, Component.m_237115_("itemGroup.search"));
|
||||
this.f_98510_.m_94199_(50);
|
||||
@@ -80,11 +84,11 @@
|
||||
if (s.startsWith("#")) {
|
||||
s = s.substring(1);
|
||||
- searchtree = this.f_96541_.m_231372_(SearchRegistry.f_119942_);
|
||||
+ searchtree = this.f_96541_.m_231372_(net.minecraftforge.client.CreativeModeTabSearchRegistry.getNameSearchKey(f_98507_));
|
||||
+ searchtree = this.f_96541_.m_231372_(net.minecraftforge.client.CreativeModeTabSearchRegistry.getTagSearchKey(f_98507_));
|
||||
this.m_98619_(s);
|
||||
} else {
|
||||
- searchtree = this.f_96541_.m_231372_(SearchRegistry.f_119941_);
|
||||
+ searchtree = this.f_96541_.m_231372_(net.minecraftforge.client.CreativeModeTabSearchRegistry.getTagSearchKey(f_98507_));
|
||||
+ searchtree = this.f_96541_.m_231372_(net.minecraftforge.client.CreativeModeTabSearchRegistry.getNameSearchKey(f_98507_));
|
||||
}
|
||||
|
||||
(this.f_97732_).f_98639_.addAll(searchtree.m_6293_(s.toLowerCase(Locale.ROOT)));
|
||||
@@ -174,7 +178,7 @@
|
||||
TooltipFlag.Default tooltipflag$default = this.f_96541_.f_91066_.f_92125_ ? TooltipFlag.Default.f_256730_ : TooltipFlag.Default.f_256752_;
|
||||
TooltipFlag tooltipflag = flag ? tooltipflag$default.m_257777_() : tooltipflag$default;
|
||||
List<Component> list = p_98591_.m_41651_(this.f_96541_.f_91074_, tooltipflag);
|
||||
@@ -641,8 +_,8 @@
|
||||
@@ -641,20 +_,20 @@
|
||||
|
||||
int i = 1;
|
||||
|
||||
@@ -185,7 +189,12 @@
|
||||
list1.add(i++, creativemodetab.m_40786_().m_6881_().m_130940_(ChatFormatting.BLUE));
|
||||
}
|
||||
}
|
||||
@@ -654,7 +_,7 @@
|
||||
}
|
||||
|
||||
- this.m_169388_(p_98590_, list1, p_98591_.m_150921_(), p_98592_, p_98593_);
|
||||
+ this.renderTooltip(p_98590_, list1, p_98591_.m_150921_(), p_98592_, p_98593_, null, p_98591_);
|
||||
}
|
||||
|
||||
protected void m_7286_(PoseStack p_98572_, float p_98573_, int p_98574_, int p_98575_) {
|
||||
RenderSystem.m_157429_(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
|
||||
@@ -203,7 +212,7 @@
|
||||
this.m_93228_(p_98572_, this.f_97735_, this.f_97736_, 0, 0, this.f_97726_, this.f_97727_);
|
||||
this.f_98510_.m_86412_(p_98572_, p_98574_, p_98575_, p_98573_);
|
||||
RenderSystem.m_157429_(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
@@ -671,7 +_,7 @@
|
||||
@@ -671,11 +_,12 @@
|
||||
int k = this.f_97736_ + 18;
|
||||
int i = k + 112;
|
||||
RenderSystem.m_157427_(GameRenderer::m_172817_);
|
||||
@@ -212,6 +221,11 @@
|
||||
if (f_98507_.m_40791_()) {
|
||||
this.m_93228_(p_98572_, j, k + (int)((float)(i - k - 17) * this.f_98508_), 232 + (this.m_98631_() ? 0 : 12), 0, 12, 15);
|
||||
}
|
||||
|
||||
+ if (currentPage.getVisibleTabs().contains(f_98507_)) //Forge: only display tab selection when the selected tab is on the current page
|
||||
this.m_98581_(p_98572_, f_98507_);
|
||||
if (f_98507_.m_257962_() == CreativeModeTab.Type.INVENTORY) {
|
||||
InventoryScreen.m_98850_(this.f_97735_ + 88, this.f_97736_ + 45, 20, (float)(this.f_97735_ + 88 - p_98574_), (float)(this.f_97736_ + 45 - 30 - p_98575_), this.f_96541_.f_91074_);
|
||||
@@ -684,7 +_,7 @@
|
||||
}
|
||||
|
||||
@@ -249,7 +263,7 @@
|
||||
this.m_93228_(p_98582_, l, i1, j, k, 26, 32);
|
||||
this.f_96542_.f_115093_ = 100.0F;
|
||||
l += 5;
|
||||
@@ -784,6 +_,15 @@
|
||||
@@ -784,6 +_,14 @@
|
||||
|
||||
}
|
||||
|
||||
@@ -259,7 +273,6 @@
|
||||
+
|
||||
+ public void setCurrentPage(net.minecraftforge.client.gui.CreativeTabsScreenPage currentPage) {
|
||||
+ this.currentPage = currentPage;
|
||||
+ this.m_98560_(currentPage.getDefaultTab());
|
||||
+ }
|
||||
+
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
--- a/net/minecraft/client/renderer/block/model/BlockElement.java
|
||||
+++ b/net/minecraft/client/renderer/block/model/BlockElement.java
|
||||
@@ -28,14 +_,21 @@
|
||||
public final Map<Direction, BlockElementFace> f_111310_;
|
||||
public final BlockElementRotation f_111311_;
|
||||
public final boolean f_111312_;
|
||||
+ public net.minecraftforge.client.model.ForgeFaceData faceData;
|
||||
|
||||
public BlockElement(Vector3f p_253626_, Vector3f p_254426_, Map<Direction, BlockElementFace> p_254454_, @Nullable BlockElementRotation p_254229_, boolean p_253661_) {
|
||||
+ this(p_253626_, p_254426_, p_254454_, p_254229_, p_253661_, net.minecraftforge.client.model.ForgeFaceData.DEFAULT);
|
||||
+ }
|
||||
+
|
||||
+ public BlockElement(Vector3f p_253626_, Vector3f p_254426_, Map<Direction, BlockElementFace> p_254454_, @Nullable BlockElementRotation p_254229_, boolean p_253661_, net.minecraftforge.client.model.ForgeFaceData faceData) {
|
||||
this.f_111308_ = p_253626_;
|
||||
this.f_111309_ = p_254426_;
|
||||
this.f_111310_ = p_254454_;
|
||||
this.f_111311_ = p_254229_;
|
||||
this.f_111312_ = p_253661_;
|
||||
this.m_111319_();
|
||||
+ this.faceData = faceData;
|
||||
+ this.f_111310_.values().forEach(face -> face.parent = this);
|
||||
}
|
||||
|
||||
private void m_111319_() {
|
||||
@@ -78,7 +_,8 @@
|
||||
throw new JsonParseException("Expected shade to be a Boolean");
|
||||
} else {
|
||||
boolean flag = GsonHelper.m_13855_(jsonobject, "shade", true);
|
||||
- return new BlockElement(vector3f, vector3f1, map, blockelementrotation, flag);
|
||||
+ var faceData = net.minecraftforge.client.model.ForgeFaceData.read(jsonobject.get("forge_data"), net.minecraftforge.client.model.ForgeFaceData.DEFAULT);
|
||||
+ return new BlockElement(vector3f, vector3f1, map, blockelementrotation, flag, faceData);
|
||||
}
|
||||
}
|
||||
|
||||
+20
-17
@@ -1,40 +1,43 @@
|
||||
--- a/net/minecraft/client/renderer/block/model/BlockElementFace.java
|
||||
+++ b/net/minecraft/client/renderer/block/model/BlockElementFace.java
|
||||
@@ -19,12 +_,24 @@
|
||||
@@ -19,12 +_,30 @@
|
||||
public final int f_111355_;
|
||||
public final String f_111356_;
|
||||
public final BlockFaceUV f_111357_;
|
||||
+ public final int emissivity;
|
||||
+ public final boolean hasAmbientOcclusion;
|
||||
+ @Nullable
|
||||
+ private final net.minecraftforge.client.model.ForgeFaceData faceData; // If null, we defer to the parent BlockElement's ForgeFaceData, which is not nullable.
|
||||
+ @Nullable
|
||||
+ BlockElement parent; // Parent canot be set by the constructor due to instantiation ordering. This shouldn't really ever be null, but it could theoretically be.
|
||||
|
||||
public BlockElementFace(@Nullable Direction p_111359_, int p_111360_, String p_111361_, BlockFaceUV p_111362_) {
|
||||
+ this(p_111359_, p_111360_, p_111361_, p_111362_, 0, true);
|
||||
+ this(p_111359_, p_111360_, p_111361_, p_111362_, null);
|
||||
+ }
|
||||
+
|
||||
+ public BlockElementFace(@Nullable Direction p_111359_, int p_111360_, String p_111361_, BlockFaceUV p_111362_, int emissivity) {
|
||||
+ this(p_111359_, p_111360_, p_111361_, p_111362_, emissivity, true);
|
||||
+ }
|
||||
+
|
||||
+ public BlockElementFace(@Nullable Direction p_111359_, int p_111360_, String p_111361_, BlockFaceUV p_111362_, int emissivity, boolean hasAmbientOcclusion) {
|
||||
+ public BlockElementFace(@Nullable Direction p_111359_, int p_111360_, String p_111361_, BlockFaceUV p_111362_, @Nullable net.minecraftforge.client.model.ForgeFaceData faceData) {
|
||||
this.f_111354_ = p_111359_;
|
||||
this.f_111355_ = p_111360_;
|
||||
this.f_111356_ = p_111361_;
|
||||
this.f_111357_ = p_111362_;
|
||||
+ this.emissivity = emissivity;
|
||||
+ this.hasAmbientOcclusion = hasAmbientOcclusion;
|
||||
+ this.faceData = faceData;
|
||||
+ }
|
||||
+
|
||||
+ public net.minecraftforge.client.model.ForgeFaceData getFaceData() {
|
||||
+ if(this.faceData != null) {
|
||||
+ return this.faceData;
|
||||
+ } else if(this.parent != null) {
|
||||
+ return this.parent.faceData;
|
||||
+ }
|
||||
+ return net.minecraftforge.client.model.ForgeFaceData.DEFAULT;
|
||||
}
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
@@ -37,7 +_,11 @@
|
||||
@@ -37,7 +_,8 @@
|
||||
int i = this.m_111368_(jsonobject);
|
||||
String s = this.m_111370_(jsonobject);
|
||||
BlockFaceUV blockfaceuv = p_111367_.deserialize(jsonobject, BlockFaceUV.class);
|
||||
- return new BlockElementFace(direction, i, s, blockfaceuv);
|
||||
+ int emissivity = GsonHelper.m_13824_(jsonobject, "emissivity", 0);
|
||||
+ if (emissivity != net.minecraft.util.Mth.m_14045_(emissivity, 0, 15))
|
||||
+ throw new JsonParseException("The emissivity value must be between 0 and 15. Found: " + emissivity);
|
||||
+ boolean hasAmbientOcclusion = GsonHelper.m_13855_(jsonobject, "ambientocclusion", true);
|
||||
+ return new BlockElementFace(direction, i, s, blockfaceuv, emissivity, hasAmbientOcclusion);
|
||||
+ var faceData = net.minecraftforge.client.model.ForgeFaceData.read(jsonobject.get("forge_data"), null);
|
||||
+ return new BlockElementFace(direction, i, s, blockfaceuv, faceData);
|
||||
}
|
||||
|
||||
protected int m_111368_(JsonObject p_111369_) {
|
||||
@@ -39,6 +39,17 @@
|
||||
public Collection<ResourceLocation> m_7970_() {
|
||||
Set<ResourceLocation> set = Sets.newHashSet();
|
||||
|
||||
@@ -127,6 +_,10 @@
|
||||
}
|
||||
|
||||
public void m_5500_(Function<ResourceLocation, UnbakedModel> p_249059_) {
|
||||
+ if (customData.hasCustomGeometry()) {
|
||||
+ customData.getCustomGeometry().resolveParents(p_249059_, customData);
|
||||
+ }
|
||||
+
|
||||
Set<UnbakedModel> set = Sets.newLinkedHashSet();
|
||||
|
||||
for(BlockModel blockmodel = this; blockmodel.f_111419_ != null && blockmodel.f_111418_ == null; blockmodel = blockmodel.f_111418_) {
|
||||
@@ -161,11 +_,24 @@
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
--- a/net/minecraft/client/renderer/block/model/FaceBakery.java
|
||||
+++ b/net/minecraft/client/renderer/block/model/FaceBakery.java
|
||||
@@ -50,7 +_,10 @@
|
||||
@@ -50,7 +_,14 @@
|
||||
this.m_111630_(aint, direction);
|
||||
}
|
||||
|
||||
- return new BakedQuad(aint, p_111603_.f_111355_, direction, p_111604_, p_111608_);
|
||||
+ net.minecraftforge.client.ForgeHooksClient.fillNormal(aint, direction);
|
||||
+ var quad = new BakedQuad(aint, p_111603_.f_111355_, direction, p_111604_, p_111608_, p_111603_.hasAmbientOcclusion);
|
||||
+ net.minecraftforge.client.model.QuadTransformers.settingEmissivity(p_111603_.emissivity).processInPlace(quad);
|
||||
+ var data = p_111603_.getFaceData();
|
||||
+ var quad = new BakedQuad(aint, p_111603_.f_111355_, direction, p_111604_, p_111608_, data.ambientOcclusion());
|
||||
+ if (!net.minecraftforge.client.model.ForgeFaceData.DEFAULT.equals(data)) {
|
||||
+ net.minecraftforge.client.model.QuadTransformers.applyingLightmap(data.blockLight(), data.skyLight()).processInPlace(quad);
|
||||
+ net.minecraftforge.client.model.QuadTransformers.applyingColor(data.color()).processInPlace(quad);
|
||||
+ }
|
||||
+ return quad;
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -1,10 +1,11 @@
|
||||
--- a/net/minecraft/client/renderer/block/model/ItemModelGenerator.java
|
||||
+++ b/net/minecraft/client/renderer/block/model/ItemModelGenerator.java
|
||||
@@ -40,6 +_,7 @@
|
||||
@@ -40,6 +_,8 @@
|
||||
map.put("particle", p_111672_.m_111477_("particle") ? Either.left(p_111672_.m_111480_("particle")) : map.get("layer0"));
|
||||
BlockModel blockmodel = new BlockModel((ResourceLocation)null, list, map, false, p_111672_.m_111479_(), p_111672_.m_111491_(), p_111672_.m_111484_());
|
||||
blockmodel.f_111416_ = p_111672_.f_111416_;
|
||||
+ blockmodel.customData.copyFrom(p_111672_.customData);
|
||||
+ blockmodel.customData.setGui3d(false);
|
||||
return blockmodel;
|
||||
}
|
||||
|
||||
+12
-11
@@ -1,15 +1,6 @@
|
||||
--- a/net/minecraft/client/renderer/entity/ItemFrameRenderer.java
|
||||
+++ b/net/minecraft/client/renderer/entity/ItemFrameRenderer.java
|
||||
@@ -62,7 +_,7 @@
|
||||
ItemStack itemstack = p_115076_.m_31822_();
|
||||
if (!flag) {
|
||||
ModelManager modelmanager = this.f_234645_.m_110907_().m_110881_();
|
||||
- ModelResourceLocation modelresourcelocation = this.m_174212_(p_115076_, itemstack);
|
||||
+ ModelResourceLocation modelresourcelocation = p_115076_.m_31822_().m_41720_() instanceof MapItem ? f_115045_ : f_115044_;
|
||||
p_115079_.m_85836_();
|
||||
p_115079_.m_252880_(-0.5F, -0.5F, -0.5F);
|
||||
this.f_234645_.m_110937_().m_111067_(p_115079_.m_85850_(), p_115080_.m_6299_(Sheets.m_110789_()), (BlockState)null, modelmanager.m_119422_(modelresourcelocation), 1.0F, 1.0F, 1.0F, p_115081_, OverlayTexture.f_118083_);
|
||||
@@ -70,30 +_,31 @@
|
||||
@@ -70,31 +_,32 @@
|
||||
}
|
||||
|
||||
if (!itemstack.m_41619_()) {
|
||||
@@ -42,7 +33,17 @@
|
||||
int k = this.m_174208_(p_115076_, 15728880, p_115081_);
|
||||
p_115079_.m_85841_(0.5F, 0.5F, 0.5F);
|
||||
this.f_115047_.m_174269_(itemstack, ItemTransforms.TransformType.FIXED, k, OverlayTexture.f_118083_, p_115079_, p_115080_, p_115076_.m_19879_());
|
||||
+ }
|
||||
}
|
||||
+ }
|
||||
}
|
||||
|
||||
p_115079_.m_85849_();
|
||||
@@ -106,7 +_,7 @@
|
||||
|
||||
private ModelResourceLocation m_174212_(T p_174213_, ItemStack p_174214_) {
|
||||
boolean flag = p_174213_.m_6095_() == EntityType.f_147033_;
|
||||
- if (p_174214_.m_150930_(Items.f_42573_)) {
|
||||
+ if (p_174214_.m_41720_() instanceof MapItem) {
|
||||
return flag ? f_174202_ : f_115045_;
|
||||
} else {
|
||||
return flag ? f_174201_ : f_115044_;
|
||||
@@ -9,3 +9,16 @@
|
||||
}
|
||||
|
||||
private void m_246472_(int p_252243_, int p_251110_, int p_249203_) {
|
||||
@@ -145,5 +_,12 @@
|
||||
|
||||
public void m_247255_(SpriteLoader.Preparations p_251993_) {
|
||||
this.m_117960_(false, p_251993_.f_244353_() > 0);
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * {@return the set of sprites in this atlas.}
|
||||
+ */
|
||||
+ public java.util.Set<ResourceLocation> getTextureLocations() {
|
||||
+ return java.util.Collections.unmodifiableSet(f_118264_.keySet());
|
||||
}
|
||||
}
|
||||
@@ -35,6 +35,15 @@
|
||||
CompletableFuture<Map<ResourceLocation, BlockModel>> completablefuture = m_246704_(p_251134_, p_250550_);
|
||||
CompletableFuture<Map<ResourceLocation, List<ModelBakery.LoadedJson>>> completablefuture1 = m_246899_(p_251134_, p_250550_);
|
||||
CompletableFuture<ModelBakery> completablefuture2 = completablefuture.thenCombineAsync(completablefuture1, (p_251201_, p_251281_) -> {
|
||||
@@ -172,6 +_,8 @@
|
||||
return " " + p_248692_.m_119193_() + ":" + p_248692_.m_119203_();
|
||||
}).collect(Collectors.joining("\n")));
|
||||
});
|
||||
+ p_252136_.m_6182_("forge_modify_baking_result");
|
||||
+ net.minecraftforge.client.ForgeHooksClient.onModifyBakingResult(p_248945_.m_119251_(), p_248945_);
|
||||
p_252136_.m_6182_("dispatch");
|
||||
Map<ResourceLocation, BakedModel> map = p_248945_.m_119251_();
|
||||
BakedModel bakedmodel = map.get(ModelBakery.f_119230_);
|
||||
@@ -201,6 +_,8 @@
|
||||
this.f_119397_ = modelbakery.m_119251_();
|
||||
this.f_119404_ = modelbakery.m_119355_();
|
||||
|
||||
@@ -17,7 +17,16 @@
|
||||
this.m_205921_(p_256594_);
|
||||
Validate.notNull(p_256594_);
|
||||
Validate.notNull(p_256374_);
|
||||
@@ -300,6 +_,12 @@
|
||||
@@ -145,6 +_,8 @@
|
||||
reference = this.f_205842_.computeIfAbsent(p_256594_, (p_258168_) -> {
|
||||
return Holder.Reference.m_254896_(this.m_255331_(), p_258168_);
|
||||
});
|
||||
+ // Forge: Bind the value immediately so it can be queried while the registry is not frozen
|
||||
+ reference.m_247654_(p_256374_);
|
||||
}
|
||||
|
||||
this.f_205842_.put(p_256594_, reference);
|
||||
@@ -300,14 +_,17 @@
|
||||
return this.f_205842_.containsKey(p_175392_);
|
||||
}
|
||||
|
||||
@@ -30,6 +39,14 @@
|
||||
public Registry<T> m_203521_() {
|
||||
if (this.f_205845_) {
|
||||
return this;
|
||||
} else {
|
||||
this.f_205845_ = true;
|
||||
- this.f_205843_.forEach((p_247989_, p_247990_) -> {
|
||||
- p_247990_.m_247654_(p_247989_);
|
||||
- });
|
||||
List<ResourceLocation> list = this.f_205842_.entrySet().stream().filter((p_211055_) -> {
|
||||
return !p_211055_.getValue().m_203633_();
|
||||
}).map((p_211794_) -> {
|
||||
@@ -321,7 +_,8 @@
|
||||
throw new IllegalStateException("Some intrusive holders were not registered: " + this.f_244282_.values());
|
||||
}
|
||||
|
||||
@@ -11,3 +11,24 @@
|
||||
private RegistrySetBuilder.BuildState m_254900_(RegistryAccess p_256400_) {
|
||||
RegistrySetBuilder.BuildState registrysetbuilder$buildstate = RegistrySetBuilder.BuildState.m_255369_(p_256400_, this.f_254732_.stream().map(RegistrySetBuilder.RegistryStub::f_254738_));
|
||||
this.f_254732_.forEach((p_255629_) -> {
|
||||
@@ -81,7 +_,7 @@
|
||||
RegistrySetBuilder.UniversalLookup registrysetbuilder$universallookup = new RegistrySetBuilder.UniversalLookup(registrysetbuilder$compositeowner);
|
||||
ImmutableMap.Builder<ResourceLocation, HolderGetter<?>> builder = ImmutableMap.builder();
|
||||
p_255995_.m_206193_().forEach((p_258197_) -> {
|
||||
- builder.put(p_258197_.f_206233_().m_135782_(), RegistrySetBuilder.m_254882_(p_258197_.f_206234_().m_255303_()));
|
||||
+ builder.put(p_258197_.f_206233_().m_135782_(), net.minecraftforge.common.ForgeHooks.wrapRegistryLookup(p_258197_.f_206234_().m_255303_()));
|
||||
});
|
||||
p_256495_.forEach((p_256603_) -> {
|
||||
builder.put(p_256603_.m_135782_(), registrysetbuilder$universallookup);
|
||||
@@ -102,6 +_,11 @@
|
||||
|
||||
public <S> HolderGetter<S> m_255420_(ResourceKey<? extends Registry<? extends S>> p_255961_) {
|
||||
return (HolderGetter<S>) BuildState.this.f_254690_.getOrDefault(p_255961_.m_135782_(), BuildState.this.f_254749_);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public <S> Optional<HolderLookup.RegistryLookup<S>> registryLookup(ResourceKey<? extends Registry<? extends S>> registry) {
|
||||
+ return Optional.ofNullable((HolderLookup.RegistryLookup<S>) BuildState.this.f_254690_.get(registry.m_135782_()));
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
--- a/net/minecraft/core/RegistrySynchronization.java
|
||||
+++ b/net/minecraft/core/RegistrySynchronization.java
|
||||
@@ -23,7 +_,7 @@
|
||||
m_245912_(builder, Registries.f_256952_, Biome.f_47430_);
|
||||
m_245912_(builder, Registries.f_256873_, ChatType.f_237005_);
|
||||
m_245912_(builder, Registries.f_256787_, DimensionType.f_63843_);
|
||||
- return builder.build();
|
||||
+ return net.minecraftforge.registries.DataPackRegistriesHooks.grabNetworkableRegistries(builder); // FORGE: Keep the map so custom registries can be added later
|
||||
});
|
||||
public static final Codec<RegistryAccess> f_244380_ = m_247146_();
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
stopwatch1.start();
|
||||
hashcache.m_253116_(hashcache.m_253234_(p_254418_, p_253750_::m_213708_).join());
|
||||
stopwatch1.stop();
|
||||
@@ -56,6 +_,30 @@
|
||||
@@ -56,6 +_,34 @@
|
||||
public DataGenerator.PackGenerator m_253030_(boolean p_253826_, String p_254134_) {
|
||||
Path path = this.f_244116_.m_247566_(PackOutput.Target.DATA_PACK).resolve("minecraft").resolve("datapacks").resolve(p_254134_);
|
||||
return new DataGenerator.PackGenerator(p_253826_, p_254134_, new PackOutput(path));
|
||||
@@ -30,6 +30,10 @@
|
||||
+ return this.f_244116_;
|
||||
+ }
|
||||
+
|
||||
+ public PackOutput getPackOutput(String path) {
|
||||
+ return new PackOutput(f_243753_.resolve(path));
|
||||
+ }
|
||||
+
|
||||
+ public <T extends DataProvider> T addProvider(boolean run, DataProvider.Factory<T> factory) {
|
||||
+ return addProvider(run, factory.m_253034_(this.f_244116_));
|
||||
+ }
|
||||
|
||||
@@ -1,53 +1,14 @@
|
||||
--- a/net/minecraft/data/advancements/AdvancementProvider.java
|
||||
+++ b/net/minecraft/data/advancements/AdvancementProvider.java
|
||||
@@ -18,8 +_,19 @@
|
||||
@@ -14,6 +_,11 @@
|
||||
import net.minecraft.data.PackOutput;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
+/**
|
||||
+ * @deprecated Forge: Use {@link net.minecraftforge.common.data.ForgeAdvancementProvider} instead,
|
||||
+ * provides ease of access for the {@link net.minecraftforge.common.data.ExistingFileHelper} in the generator
|
||||
+ */
|
||||
+@Deprecated
|
||||
public class AdvancementProvider implements DataProvider {
|
||||
private final PackOutput.PathProvider f_236156_;
|
||||
private final List<AdvancementSubProvider> f_244266_;
|
||||
private final CompletableFuture<HolderLookup.Provider> f_254664_;
|
||||
+ @org.jetbrains.annotations.Nullable
|
||||
+ protected final net.minecraftforge.common.data.ExistingFileHelper existingFileHelper;
|
||||
|
||||
+ /**
|
||||
+ * @deprecated Forge: Use the {@linkplain #AdvancementProvider(PackOutput, CompletableFuture, List, net.minecraftforge.common.data.ExistingFileHelper) existing file helper variant}
|
||||
+ */
|
||||
+ @Deprecated
|
||||
public AdvancementProvider(PackOutput p_256529_, CompletableFuture<HolderLookup.Provider> p_255722_, List<AdvancementSubProvider> p_255883_) {
|
||||
+ this(p_256529_, p_255722_, p_255883_, null);
|
||||
+ }
|
||||
+
|
||||
+ public AdvancementProvider(PackOutput p_256529_, CompletableFuture<HolderLookup.Provider> p_255722_, List<AdvancementSubProvider> p_255883_, @org.jetbrains.annotations.Nullable net.minecraftforge.common.data.ExistingFileHelper existingFileHelper) {
|
||||
+ this.existingFileHelper = existingFileHelper;
|
||||
this.f_236156_ = p_256529_.m_245269_(PackOutput.Target.DATA_PACK, "advancements");
|
||||
this.f_244266_ = p_255883_;
|
||||
this.f_254664_ = p_255722_;
|
||||
@@ -38,14 +_,27 @@
|
||||
}
|
||||
};
|
||||
|
||||
- for(AdvancementSubProvider advancementsubprovider : this.f_244266_) {
|
||||
- advancementsubprovider.m_245571_(p_255484_, consumer);
|
||||
- }
|
||||
+ // Forge: Allow mods to take control of advancement registration
|
||||
+ this.registerAdvancements(p_255484_, consumer, this.existingFileHelper);
|
||||
|
||||
return CompletableFuture.allOf(list.toArray((p_253393_) -> {
|
||||
return new CompletableFuture[p_253393_];
|
||||
}));
|
||||
});
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * Registers all {@linkplain Advancement advancements}.
|
||||
+ *
|
||||
+ * @param registries the registries available for lookup including all registries in {@link net.minecraft.core.registries.Registries}
|
||||
+ * @param saver a {@link Consumer} which saves any advancements provided
|
||||
+ * @param existingFileHelper the existing file helper to check for the existence of files like parent advancements
|
||||
+ * @see Advancement.Builder#save(Consumer, ResourceLocation, net.minecraftforge.common.data.ExistingFileHelper)
|
||||
+ */
|
||||
+ protected void registerAdvancements(HolderLookup.Provider registries, Consumer<Advancement> saver, net.minecraftforge.common.data.ExistingFileHelper existingFileHelper) {
|
||||
+ for (AdvancementSubProvider subProvider : this.f_244266_) {
|
||||
+ subProvider.m_245571_(registries, saver);
|
||||
+ }
|
||||
}
|
||||
|
||||
public final String m_6055_() {
|
||||
+36
-1
@@ -1,11 +1,46 @@
|
||||
--- a/net/minecraft/data/registries/RegistriesDatapackGenerator.java
|
||||
+++ b/net/minecraft/data/registries/RegistriesDatapackGenerator.java
|
||||
@@ -18,12 +_,23 @@
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
+/**
|
||||
+ * @deprecated Forge: Use {@link net.minecraftforge.common.data.DatapackBuiltinEntriesProvider} instead
|
||||
+ */
|
||||
+@Deprecated
|
||||
public class RegistriesDatapackGenerator implements DataProvider {
|
||||
private static final Logger f_254654_ = LogUtils.getLogger();
|
||||
private final PackOutput f_254743_;
|
||||
private final CompletableFuture<HolderLookup.Provider> f_254747_;
|
||||
+ private final java.util.function.Predicate<String> namespacePredicate;
|
||||
|
||||
+ @Deprecated
|
||||
public RegistriesDatapackGenerator(PackOutput p_256643_, CompletableFuture<HolderLookup.Provider> p_255780_) {
|
||||
+ this(p_256643_, p_255780_, null);
|
||||
+ }
|
||||
+
|
||||
+ public RegistriesDatapackGenerator(PackOutput p_256643_, CompletableFuture<HolderLookup.Provider> p_255780_, @org.jetbrains.annotations.Nullable java.util.Set<String> modIds) {
|
||||
+ this.namespacePredicate = modIds == null ? namespace -> true : modIds::contains;
|
||||
this.f_254747_ = p_255780_;
|
||||
this.f_254743_ = p_256643_;
|
||||
}
|
||||
@@ -31,7 +_,7 @@
|
||||
public CompletableFuture<?> m_213708_(CachedOutput p_255785_) {
|
||||
return this.f_254747_.thenCompose((p_256533_) -> {
|
||||
DynamicOps<JsonElement> dynamicops = RegistryOps.m_255058_(JsonOps.INSTANCE, p_256533_);
|
||||
- return CompletableFuture.allOf(RegistryDataLoader.f_243803_.stream().flatMap((p_256552_) -> {
|
||||
+ return CompletableFuture.allOf(net.minecraftforge.registries.DataPackRegistriesHooks.getDataPackRegistries().stream().flatMap((p_256552_) -> {
|
||||
+ return CompletableFuture.allOf(net.minecraftforge.registries.DataPackRegistriesHooks.getDataPackRegistriesWithDimensions().flatMap((p_256552_) -> {
|
||||
return this.m_254918_(p_255785_, p_256533_, dynamicops, p_256552_).stream();
|
||||
}).toArray((p_255809_) -> {
|
||||
return new CompletableFuture[p_255809_];
|
||||
@@ -42,8 +_,8 @@
|
||||
private <T> Optional<CompletableFuture<?>> m_254918_(CachedOutput p_256502_, HolderLookup.Provider p_256492_, DynamicOps<JsonElement> p_256000_, RegistryDataLoader.RegistryData<T> p_256449_) {
|
||||
ResourceKey<? extends Registry<T>> resourcekey = p_256449_.f_243794_();
|
||||
return p_256492_.m_254861_(resourcekey).map((p_255847_) -> {
|
||||
- PackOutput.PathProvider packoutput$pathprovider = this.f_254743_.m_245269_(PackOutput.Target.DATA_PACK, resourcekey.m_135782_().m_135815_());
|
||||
- return CompletableFuture.allOf(p_255847_.m_214062_().map((p_256105_) -> {
|
||||
+ PackOutput.PathProvider packoutput$pathprovider = this.f_254743_.m_245269_(PackOutput.Target.DATA_PACK, net.minecraftforge.common.ForgeHooks.prefixNamespace(resourcekey.m_135782_()));
|
||||
+ return CompletableFuture.allOf(p_255847_.m_214062_().filter(holder -> this.namespacePredicate.test(holder.m_205785_().m_135782_().m_135827_())).map((p_256105_) -> {
|
||||
return m_255283_(packoutput$pathprovider.m_245731_(p_256105_.m_205785_().m_135782_()), p_256502_, p_256000_, p_256449_.f_244580_(), p_256105_.m_203334_());
|
||||
}).toArray((p_256279_) -> {
|
||||
return new CompletableFuture[p_256279_];
|
||||
@@ -0,0 +1,9 @@
|
||||
--- a/net/minecraft/data/worldgen/BootstapContext.java
|
||||
+++ b/net/minecraft/data/worldgen/BootstapContext.java
|
||||
@@ -14,4 +_,6 @@
|
||||
}
|
||||
|
||||
<S> HolderGetter<S> m_255420_(ResourceKey<? extends Registry<? extends S>> p_256410_);
|
||||
+
|
||||
+ default <S> java.util.Optional<net.minecraft.core.HolderLookup.RegistryLookup<S>> registryLookup(ResourceKey<? extends Registry<? extends S>> registry) { return java.util.Optional.empty(); }
|
||||
}
|
||||
@@ -9,11 +9,11 @@
|
||||
}
|
||||
|
||||
static <E> void m_255048_(RegistryOps.RegistryInfoLookup p_256369_, ResourceManager p_256349_, ResourceKey<? extends Registry<E>> p_255792_, WritableRegistry<E> p_256211_, Decoder<E> p_256232_, Map<ResourceKey<?>, Exception> p_255884_) {
|
||||
@@ -138,6 +_,7 @@
|
||||
@@ -136,6 +_,7 @@
|
||||
try (Reader reader = resource.m_215508_()) {
|
||||
JsonElement jsonelement = JsonParser.parseReader(reader);
|
||||
DataResult<E> dataresult = p_256232_.parse(registryops, jsonelement);
|
||||
+ if (!net.minecraftforge.common.crafting.conditions.ICondition.shouldRegisterEntry(jsonelement)) continue;
|
||||
E e = dataresult.getOrThrow(false, (p_248715_) -> {
|
||||
});
|
||||
+ if (net.minecraftforge.common.crafting.conditions.ICondition.shouldRegisterEntry(jsonelement))
|
||||
p_256211_.m_255290_(resourcekey, e, resource.m_247137_() ? Lifecycle.stable() : dataresult.lifecycle());
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
+ return DataResult.error("Not a registry ops");
|
||||
+
|
||||
+ return registryOps.f_254668_.m_254838_(resourceKey).map(registryInfo -> {
|
||||
+ if (!(registryInfo.f_254724_() instanceof HolderLookup.RegistryLookup<E> registryLookup))
|
||||
+ if (!(registryInfo.f_254675_() instanceof HolderLookup.RegistryLookup<E> registryLookup))
|
||||
+ return DataResult.<HolderLookup.RegistryLookup<E>>error("Found holder getter but was not a registry lookup for " + resourceKey);
|
||||
+
|
||||
+ return DataResult.success(registryLookup, registryInfo.f_254751_());
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
--- a/net/minecraft/server/packs/VanillaPackResources.java
|
||||
+++ b/net/minecraft/server/packs/VanillaPackResources.java
|
||||
@@ -32,6 +_,19 @@
|
||||
this.f_10314_ = p_250468_;
|
||||
this.f_244169_ = p_248798_;
|
||||
this.f_244459_ = p_251106_;
|
||||
+ this.cacheManager = new net.minecraftforge.resource.ResourceCacheManager(false, "indexVanillaPackCachesOnThread",
|
||||
+ (packType, namespace) -> {
|
||||
+ List<Path> validPaths = new java.util.ArrayList<>();
|
||||
+
|
||||
+ for (Path basePath : this.f_244459_.getOrDefault(packType, List.of())) {
|
||||
+ Path path = basePath.resolve(namespace);
|
||||
+ if (Files.exists(path) && PathPackResources.m_246877_(path)) {
|
||||
+ validPaths.add(path);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return validPaths;
|
||||
+ });
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -66,6 +_,14 @@
|
||||
public void m_8031_(PackType p_248974_, String p_248703_, String p_250848_, PackResources.ResourceOutput p_249668_) {
|
||||
FileUtil.m_245538_(p_250848_).get().ifLeft((p_248228_) -> {
|
||||
List<Path> list = this.f_244459_.get(p_248974_);
|
||||
+ // TODO 1.19.3: Fix resource caching to actually work and produce valid results -SS
|
||||
+ // if (net.minecraftforge.resource.ResourceCacheManager.shouldUseCache() && this.cacheManager.hasCached(p_248974_, p_248703_)) {
|
||||
+ // for (Path rootPath : list) {
|
||||
+ // this.cacheManager.listResources(p_248974_, p_248703_, FileUtil.resolvePath(rootPath, p_248228_), this::getResource, p_249668_);
|
||||
+ // }
|
||||
+ // return;
|
||||
+ // }
|
||||
+
|
||||
int i = list.size();
|
||||
if (i == 1) {
|
||||
m_246310_(p_249668_, p_248703_, list.get(0), p_248228_);
|
||||
@@ -161,5 +_,20 @@
|
||||
return new Resource(this, p_248221_);
|
||||
});
|
||||
};
|
||||
+ }
|
||||
+
|
||||
+ private final net.minecraftforge.resource.ResourceCacheManager cacheManager;
|
||||
+
|
||||
+ @Override
|
||||
+ public void initForNamespace(final String namespace) {
|
||||
+ if (net.minecraftforge.resource.ResourceCacheManager.shouldUseCache())
|
||||
+ this.cacheManager.index(namespace);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void init(final PackType packType) {
|
||||
+ // Forge: Init vanilla namespaces
|
||||
+ initForNamespace("minecraft");
|
||||
+ initForNamespace("realms");
|
||||
}
|
||||
}
|
||||
+8
@@ -9,3 +9,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +_,7 @@
|
||||
JsonObject jsonobject = new JsonObject();
|
||||
jsonobject.add("description", Component.Serializer.m_130716_(p_250206_.m_10373_()));
|
||||
jsonobject.addProperty("pack_format", p_250206_.m_10374_());
|
||||
+ net.minecraftforge.common.ForgeHooks.writeTypedPackFormats(jsonobject, p_250206_);
|
||||
return jsonobject;
|
||||
}
|
||||
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
--- a/net/minecraft/server/packs/resources/FallbackResourceManager.java
|
||||
+++ b/net/minecraft/server/packs/resources/FallbackResourceManager.java
|
||||
@@ -98,8 +_,11 @@
|
||||
|
||||
for(int i = this.f_10599_.size() - 1; i >= 0; --i) {
|
||||
FallbackResourceManager.PackEntry fallbackresourcemanager$packentry = this.f_10599_.get(i);
|
||||
- PackResources packresources = fallbackresourcemanager$packentry.f_215433_;
|
||||
- if (packresources != null) {
|
||||
+ PackResources pack = fallbackresourcemanager$packentry.f_215433_;
|
||||
+ if (pack != null) {
|
||||
+ var children = pack.getChildren();
|
||||
+ var packs = children == null ? List.of(pack) : children;
|
||||
+ for (final PackResources packresources : packs) {
|
||||
IoSupplier<InputStream> iosupplier = packresources.m_214146_(this.f_10601_, p_215367_);
|
||||
if (iosupplier != null) {
|
||||
IoSupplier<ResourceMetadata> iosupplier1;
|
||||
@@ -113,6 +_,7 @@
|
||||
}
|
||||
|
||||
list.add(new Resource(packresources, iosupplier, iosupplier1));
|
||||
+ }
|
||||
}
|
||||
}
|
||||
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
--- a/net/minecraft/server/packs/resources/MultiPackResourceManager.java
|
||||
+++ b/net/minecraft/server/packs/resources/MultiPackResourceManager.java
|
||||
@@ -24,6 +_,7 @@
|
||||
public MultiPackResourceManager(PackType p_203797_, List<PackResources> p_203798_) {
|
||||
this.f_203795_ = List.copyOf(p_203798_);
|
||||
Map<String, FallbackResourceManager> map = new HashMap<>();
|
||||
+ p_203798_.forEach(resourcePack -> resourcePack.init(p_203797_));
|
||||
List<String> list = p_203798_.stream().flatMap((p_215471_) -> {
|
||||
return p_215471_.m_5698_(p_203797_).stream();
|
||||
}).distinct().toList();
|
||||
@@ -1,5 +1,13 @@
|
||||
--- a/net/minecraft/world/entity/npc/AbstractVillager.java
|
||||
+++ b/net/minecraft/world/entity/npc/AbstractVillager.java
|
||||
@@ -116,6 +_,7 @@
|
||||
CriteriaTriggers.f_10585_.m_70959_((ServerPlayer)this.f_35263_, this, p_35274_.m_45368_());
|
||||
}
|
||||
|
||||
+ net.minecraftforge.common.MinecraftForge.EVENT_BUS.post(new net.minecraftforge.event.entity.player.TradeWithVillagerEvent(this.f_35263_, p_35274_, this));
|
||||
}
|
||||
|
||||
protected abstract void m_8058_(MerchantOffer p_35299_);
|
||||
@@ -164,9 +_,9 @@
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
--- a/net/minecraft/world/item/CreativeModeTab.java
|
||||
+++ b/net/minecraft/world/item/CreativeModeTab.java
|
||||
@@ -28,14 +_,26 @@
|
||||
@@ -28,14 +_,30 @@
|
||||
private Consumer<List<ItemStack>> f_256965_;
|
||||
private final Supplier<ItemStack> f_256912_;
|
||||
private final CreativeModeTab.DisplayItemsGenerator f_256824_;
|
||||
@@ -25,6 +25,10 @@
|
||||
+ this.tabsImage = tabsImage;
|
||||
+ this.labelColor = labelColor;
|
||||
+ this.slotColor = slotColor;
|
||||
+ }
|
||||
+
|
||||
+ protected CreativeModeTab(CreativeModeTab.Builder builder) {
|
||||
+ this(builder.f_256796_, builder.f_256977_, builder.f_256847_, builder.f_256856_, builder.f_256981_, builder.f_256953_, builder.backgroundLocation, builder.hasSearchBar, builder.searchBarWidth, builder.tabsImage, builder.labelColor, builder.slotColor);
|
||||
}
|
||||
|
||||
public static CreativeModeTab.Builder m_257815_(CreativeModeTab.Row p_259342_, int p_260312_) {
|
||||
@@ -44,7 +48,7 @@
|
||||
public void m_258021_(FeatureFlagSet p_260125_, boolean p_259855_) {
|
||||
CreativeModeTab.ItemDisplayBuilder creativemodetab$itemdisplaybuilder = new CreativeModeTab.ItemDisplayBuilder(this, p_260125_);
|
||||
- this.f_256824_.m_257865_(p_260125_, creativemodetab$itemdisplaybuilder, p_259855_);
|
||||
+ net.minecraftforge.client.ForgeHooksClient.onCreativeModeTabBuildContents(this, this.f_256824_).m_257865_(p_260125_, creativemodetab$itemdisplaybuilder, p_259855_);
|
||||
+ net.minecraftforge.client.ForgeHooksClient.onCreativeModeTabBuildContents(this, this.f_256824_, p_260125_, creativemodetab$itemdisplaybuilder, p_259855_);
|
||||
this.f_243839_ = creativemodetab$itemdisplaybuilder.f_244363_;
|
||||
this.f_243841_ = creativemodetab$itemdisplaybuilder.f_244585_;
|
||||
this.m_257466_();
|
||||
@@ -83,7 +87,7 @@
|
||||
private final CreativeModeTab.Row f_256796_;
|
||||
private final int f_256977_;
|
||||
private Component f_256856_ = Component.m_237119_();
|
||||
@@ -136,6 +_,13 @@
|
||||
@@ -136,6 +_,14 @@
|
||||
private boolean f_256854_ = false;
|
||||
private CreativeModeTab.Type f_256847_ = CreativeModeTab.Type.CATEGORY;
|
||||
private String f_257036_ = "items.png";
|
||||
@@ -94,10 +98,11 @@
|
||||
+ private net.minecraft.resources.ResourceLocation tabsImage = CREATIVE_INVENTORY_TABS_IMAGE;
|
||||
+ private int labelColor = 4210752;
|
||||
+ private int slotColor = -2130706433;
|
||||
+ private java.util.function.Function<CreativeModeTab.Builder, CreativeModeTab> tabFactory = CreativeModeTab::new;
|
||||
|
||||
public Builder(CreativeModeTab.Row p_259171_, int p_259661_) {
|
||||
this.f_256796_ = p_259171_;
|
||||
@@ -174,23 +_,57 @@
|
||||
@@ -174,11 +_,49 @@
|
||||
|
||||
protected CreativeModeTab.Builder m_257623_(CreativeModeTab.Type p_259283_) {
|
||||
this.f_256847_ = p_259283_;
|
||||
@@ -108,7 +113,6 @@
|
||||
|
||||
public CreativeModeTab.Builder m_257609_(String p_259981_) {
|
||||
- this.f_257036_ = p_259981_;
|
||||
- return this;
|
||||
+ return withBackgroundLocation(new net.minecraft.resources.ResourceLocation("textures/gui/container/creative_inventory/tab_" + p_259981_));
|
||||
+ }
|
||||
+
|
||||
@@ -142,14 +146,19 @@
|
||||
+ public CreativeModeTab.Builder withSlotColor(int slotColor) {
|
||||
+ this.slotColor = slotColor;
|
||||
+ return this;
|
||||
+ }
|
||||
+
|
||||
+ public CreativeModeTab.Builder withTabFactory(java.util.function.Function<CreativeModeTab.Builder, CreativeModeTab> tabFactory) {
|
||||
+ this.tabFactory = tabFactory;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CreativeModeTab m_257652_() {
|
||||
@@ -186,11 +_,12 @@
|
||||
if ((this.f_256847_ == CreativeModeTab.Type.HOTBAR || this.f_256847_ == CreativeModeTab.Type.INVENTORY) && this.f_256953_ != f_256756_) {
|
||||
throw new IllegalStateException("Special tabs can't have display items");
|
||||
} else {
|
||||
- CreativeModeTab creativemodetab = new CreativeModeTab(this.f_256796_, this.f_256977_, this.f_256847_, this.f_256856_, this.f_256981_, this.f_256953_);
|
||||
+ CreativeModeTab creativemodetab = new CreativeModeTab(this.f_256796_, this.f_256977_, this.f_256847_, this.f_256856_, this.f_256981_, this.f_256953_, this.backgroundLocation, this.hasSearchBar, this.searchBarWidth, this.tabsImage, this.labelColor, this.slotColor);
|
||||
+ CreativeModeTab creativemodetab = tabFactory.apply(this);
|
||||
creativemodetab.f_257018_ = this.f_256854_;
|
||||
creativemodetab.f_40768_ = this.f_256851_;
|
||||
creativemodetab.f_40767_ = this.f_256992_;
|
||||
|
||||
@@ -207,14 +207,6 @@
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -640,6 +_,7 @@
|
||||
this.m_46745_(p_151544_).m_8092_(true);
|
||||
}
|
||||
|
||||
+ this.m_46717_(p_151544_, m_8055_(p_151544_).m_60734_()); //Notify neighbors of changes
|
||||
}
|
||||
|
||||
public int m_5736_() {
|
||||
@@ -684,7 +_,7 @@
|
||||
public int m_46681_(BlockPos p_46682_, Direction p_46683_) {
|
||||
BlockState blockstate = this.m_8055_(p_46682_);
|
||||
|
||||
@@ -5,16 +5,21 @@
|
||||
|
||||
public void m_48792_(Level p_52068_, BlockPos p_52069_, BlockState p_52070_, BlockState p_52071_, FallingBlockEntity p_52072_) {
|
||||
- if (m_52080_(p_52068_, p_52069_, p_52071_)) {
|
||||
+ if (m_52080_(p_52068_, p_52069_, p_52070_)) { // Forge: Uses block of falling entity instead of block at current position, checked within shouldSolidify
|
||||
+ if (shouldSolidify(p_52068_, p_52069_, p_52070_, p_52071_.m_60819_())) { // Forge: Use block of falling entity instead of block at replaced position, and check if shouldSolidify with the FluidState of the replaced block
|
||||
p_52068_.m_7731_(p_52069_, this.f_52058_, 3);
|
||||
}
|
||||
|
||||
@@ -34,19 +_,19 @@
|
||||
@@ -33,20 +_,24 @@
|
||||
return m_52080_(blockgetter, blockpos, blockstate) ? this.f_52058_ : super.m_5573_(p_52063_);
|
||||
}
|
||||
|
||||
+ private static boolean shouldSolidify(BlockGetter p_52081_, BlockPos p_52082_, BlockState p_52083_, net.minecraft.world.level.material.FluidState fluidState) {
|
||||
+ return p_52083_.canBeHydrated(p_52081_, p_52082_, fluidState, p_52082_) || touchesLiquid(p_52081_, p_52082_, p_52083_);
|
||||
+ }
|
||||
+
|
||||
private static boolean m_52080_(BlockGetter p_52081_, BlockPos p_52082_, BlockState p_52083_) {
|
||||
- return m_52088_(p_52083_) || m_52064_(p_52081_, p_52082_);
|
||||
+ return p_52083_.canBeHydrated(p_52081_, p_52082_, p_52081_.m_6425_(p_52082_), p_52082_) || touchesLiquid(p_52081_, p_52082_, p_52083_);
|
||||
+ return shouldSolidify(p_52081_, p_52082_, p_52083_, p_52081_.m_6425_(p_52082_));
|
||||
}
|
||||
|
||||
- private static boolean m_52064_(BlockGetter p_52065_, BlockPos p_52066_) {
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
BlockState blockstate = p_222539_.m_8055_(blockpos.m_7495_());
|
||||
- if (p_222539_.m_8055_(blockpos).m_60795_() && (blockstate.m_60713_(Blocks.f_50093_) || blockstate.m_204336_(BlockTags.f_144274_))) {
|
||||
+ Block block = blockstate.m_60734_();
|
||||
+ if (p_222539_.m_46859_(blockpos) && (blockstate.canSustainPlant(p_222539_, blockpos.m_7495_(), Direction.UP, this) || block == Blocks.f_50093_ || block == Blocks.f_50493_ || block == Blocks.f_50546_ || block == Blocks.f_50599_ || block == Blocks.f_50440_)) {
|
||||
+ if (p_222539_.m_46859_(blockpos) && (blockstate.canSustainPlant(p_222539_, blockpos.m_7495_(), Direction.UP, this.f_57015_) || block == Blocks.f_50093_ || block == Blocks.f_50493_ || block == Blocks.f_50546_ || block == Blocks.f_50599_ || block == Blocks.f_50440_)) {
|
||||
p_222539_.m_46597_(blockpos, this.f_57015_.m_49966_());
|
||||
p_222539_.m_46597_(p_222540_, this.f_57015_.m_7810_().m_49966_().m_61124_(HorizontalDirectionalBlock.f_54117_, direction));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
--- a/net/minecraft/world/level/block/StemGrownBlock.java
|
||||
+++ b/net/minecraft/world/level/block/StemGrownBlock.java
|
||||
@@ -2,7 +_,7 @@
|
||||
|
||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||
|
||||
-public abstract class StemGrownBlock extends Block {
|
||||
+public abstract class StemGrownBlock extends Block implements net.minecraftforge.common.IPlantable {
|
||||
public StemGrownBlock(BlockBehaviour.Properties p_57058_) {
|
||||
super(p_57058_);
|
||||
}
|
||||
@@ -10,4 +_,12 @@
|
||||
public abstract StemBlock m_7161_();
|
||||
|
||||
public abstract AttachedStemBlock m_7810_();
|
||||
+
|
||||
+ //FORGE START
|
||||
+ @Override
|
||||
+ public net.minecraft.world.level.block.state.BlockState getPlant(net.minecraft.world.level.BlockGetter world, net.minecraft.core.BlockPos pos) {
|
||||
+ net.minecraft.world.level.block.state.BlockState state = world.m_8055_(pos);
|
||||
+ if (state.m_60734_() != this) return m_49966_();
|
||||
+ return state;
|
||||
+ }
|
||||
}
|
||||
+6
-27
@@ -1,33 +1,12 @@
|
||||
--- a/net/minecraft/world/level/block/grower/AbstractMegaTreeGrower.java
|
||||
+++ b/net/minecraft/world/level/block/grower/AbstractMegaTreeGrower.java
|
||||
@@ -27,6 +_,14 @@
|
||||
return super.m_213817_(p_222891_, p_222892_, p_222893_, p_222894_, p_222895_);
|
||||
}
|
||||
|
||||
+ /**
|
||||
+ * Forge: context-sensitive version to be able to use data pack provided features
|
||||
+ */
|
||||
+ @Nullable
|
||||
+ protected Holder<? extends ConfiguredFeature<?, ?>> getConfiguredMegaFeature(ServerLevel level, ChunkGenerator chunkGenerator, BlockPos pos, BlockState state, RandomSource random, ResourceKey<ConfiguredFeature<?, ?>> featureKey) {
|
||||
+ return level.m_8891_().m_175515_(Registries.f_256911_).m_203636_(featureKey).orElse(null);
|
||||
+ }
|
||||
+
|
||||
@Nullable
|
||||
protected abstract ResourceKey<ConfiguredFeature<?, ?>> m_213566_(RandomSource p_222904_);
|
||||
|
||||
@@ -35,11 +_,12 @@
|
||||
if (resourcekey == null) {
|
||||
@@ -36,6 +_,9 @@
|
||||
return false;
|
||||
} else {
|
||||
- Holder<ConfiguredFeature<?, ?>> holder = p_222897_.m_8891_().m_175515_(Registries.f_256911_).m_203636_(resourcekey).orElse((Holder.Reference<ConfiguredFeature<?, ?>>)null);
|
||||
- if (holder == null) {
|
||||
+ Holder<? extends ConfiguredFeature<?, ?>> holder = this.getConfiguredMegaFeature(p_222897_, p_222898_, p_222899_, p_222900_, p_222901_, resourcekey);
|
||||
+ net.minecraftforge.event.level.SaplingGrowTreeEvent event = net.minecraftforge.event.ForgeEventFactory.blockGrowFeature(p_222897_, p_222901_, p_222899_, holder);
|
||||
+ if (event.getResult().equals(net.minecraftforge.eventbus.api.Event.Result.DENY) || event.getFeature() == null) {
|
||||
Holder<ConfiguredFeature<?, ?>> holder = p_222897_.m_8891_().m_175515_(Registries.f_256911_).m_203636_(resourcekey).orElse((Holder.Reference<ConfiguredFeature<?, ?>>)null);
|
||||
+ var event = net.minecraftforge.event.ForgeEventFactory.blockGrowFeature(p_222897_, p_222901_, p_222899_, holder);
|
||||
+ holder = event.getFeature();
|
||||
+ if (event.getResult() == net.minecraftforge.eventbus.api.Event.Result.DENY) return false;
|
||||
if (holder == null) {
|
||||
return false;
|
||||
} else {
|
||||
- ConfiguredFeature<?, ?> configuredfeature = holder.m_203334_();
|
||||
+ ConfiguredFeature<?, ?> configuredfeature = event.getFeature().m_203334_();
|
||||
BlockState blockstate = Blocks.f_50016_.m_49966_();
|
||||
p_222897_.m_7731_(p_222899_.m_7918_(p_222902_, 0, p_222903_), blockstate, 4);
|
||||
p_222897_.m_7731_(p_222899_.m_7918_(p_222902_ + 1, 0, p_222903_), blockstate, 4);
|
||||
+6
-27
@@ -1,33 +1,12 @@
|
||||
--- a/net/minecraft/world/level/block/grower/AbstractTreeGrower.java
|
||||
+++ b/net/minecraft/world/level/block/grower/AbstractTreeGrower.java
|
||||
@@ -14,6 +_,14 @@
|
||||
import net.minecraft.world.level.levelgen.feature.ConfiguredFeature;
|
||||
|
||||
public abstract class AbstractTreeGrower {
|
||||
+ /**
|
||||
+ * Forge: context-sensitive version to be able to use data pack provided features
|
||||
+ */
|
||||
+ @Nullable
|
||||
+ protected Holder<? extends ConfiguredFeature<?, ?>> getConfiguredFeature(ServerLevel level, ChunkGenerator chunkGenerator, BlockPos pos, BlockState state, RandomSource random, boolean hasFlowers, ResourceKey<ConfiguredFeature<?, ?>> featureKey) {
|
||||
+ return level.m_8891_().m_175515_(Registries.f_256911_).m_203636_(featureKey).orElse(null);
|
||||
+ }
|
||||
+
|
||||
@Nullable
|
||||
protected abstract ResourceKey<ConfiguredFeature<?, ?>> m_213888_(RandomSource p_222910_, boolean p_222911_);
|
||||
|
||||
@@ -22,11 +_,12 @@
|
||||
if (resourcekey == null) {
|
||||
@@ -23,6 +_,9 @@
|
||||
return false;
|
||||
} else {
|
||||
- Holder<ConfiguredFeature<?, ?>> holder = p_222905_.m_8891_().m_175515_(Registries.f_256911_).m_203636_(resourcekey).orElse((Holder.Reference<ConfiguredFeature<?, ?>>)null);
|
||||
- if (holder == null) {
|
||||
+ Holder<? extends ConfiguredFeature<?, ?>> holder = this.getConfiguredFeature(p_222905_, p_222906_, p_222907_, p_222908_, p_222909_, this.m_60011_(p_222905_, p_222907_), resourcekey);
|
||||
+ net.minecraftforge.event.level.SaplingGrowTreeEvent event = net.minecraftforge.event.ForgeEventFactory.blockGrowFeature(p_222905_, p_222909_, p_222907_, holder);
|
||||
+ if (event.getResult().equals(net.minecraftforge.eventbus.api.Event.Result.DENY) || event.getFeature() == null) {
|
||||
Holder<ConfiguredFeature<?, ?>> holder = p_222905_.m_8891_().m_175515_(Registries.f_256911_).m_203636_(resourcekey).orElse((Holder.Reference<ConfiguredFeature<?, ?>>)null);
|
||||
+ var event = net.minecraftforge.event.ForgeEventFactory.blockGrowFeature(p_222905_, p_222909_, p_222907_, holder);
|
||||
+ holder = event.getFeature();
|
||||
+ if (event.getResult() == net.minecraftforge.eventbus.api.Event.Result.DENY) return false;
|
||||
if (holder == null) {
|
||||
return false;
|
||||
} else {
|
||||
- ConfiguredFeature<?, ?> configuredfeature = holder.m_203334_();
|
||||
+ ConfiguredFeature<?, ?> configuredfeature = event.getFeature().m_203334_();
|
||||
BlockState blockstate = p_222905_.m_6425_(p_222907_).m_76188_();
|
||||
p_222905_.m_7731_(p_222907_, blockstate, 4);
|
||||
if (configuredfeature.m_224953_(p_222905_, p_222906_, p_222909_, p_222907_)) {
|
||||
@@ -48,8 +48,15 @@
|
||||
public LootContext.Builder m_230911_(RandomSource p_230912_) {
|
||||
this.f_78958_ = p_230912_;
|
||||
return this;
|
||||
@@ -191,7 +_,7 @@
|
||||
@@ -189,9 +_,14 @@
|
||||
return (T)this.f_78956_.get(p_78983_);
|
||||
}
|
||||
|
||||
+ public LootContext.Builder withQueriedLootTableId(ResourceLocation queriedLootTableId) {
|
||||
+ this.queriedLootTableId = queriedLootTableId;
|
||||
+ return this;
|
||||
+ }
|
||||
+
|
||||
public LootContext m_78975_(LootContextParamSet p_78976_) {
|
||||
Set<LootContextParam<?>> set = Sets.difference(this.f_78956_.keySet(), p_78976_.m_81398_());
|
||||
- if (!set.isEmpty()) {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
try {
|
||||
- LootTable loottable = f_79189_.fromJson(p_79199_, LootTable.class);
|
||||
+ net.minecraft.server.packs.resources.Resource res = p_79215_.m_213713_(getPreparedPath(p_79198_)).orElse(null);
|
||||
+ LootTable loottable = net.minecraftforge.common.ForgeHooks.loadLootTable(f_79189_, p_79198_, p_79199_, res == null || !res.m_215506_().equals("Default"), this);
|
||||
+ LootTable loottable = net.minecraftforge.common.ForgeHooks.loadLootTable(f_79189_, p_79198_, p_79199_, res == null || !res.m_215506_().equals(net.minecraft.server.packs.repository.BuiltInPackSource.f_243761_), this);
|
||||
builder.put(p_79198_, loottable);
|
||||
} catch (Exception exception) {
|
||||
f_79188_.error("Couldn't parse loot table {}", p_79198_, exception);
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"sources": [
|
||||
{
|
||||
"type": "minecraft:single",
|
||||
"resource": "forge:white"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"type": "minecraft:the_end",
|
||||
"generator": {
|
||||
"type": "minecraft:noise",
|
||||
"biome_source": {
|
||||
"type": "minecraft:the_end"
|
||||
},
|
||||
"settings": "minecraft:end"
|
||||
}
|
||||
}
|
||||
+2236
File diff suppressed because it is too large.
Load diff
@@ -36,7 +36,6 @@ public class ClientForgeMod
|
||||
|
||||
// TODO: Deprecated. To be removed in 1.20
|
||||
event.register("bucket", DynamicFluidContainerModel.Loader.INSTANCE_DEPRECATED);
|
||||
event.register("item-layers", ItemLayerModel.Loader.INSTANCE_DEPRECATED);
|
||||
event.register("separate-perspective", SeparateTransformsModel.Loader.INSTANCE_DEPRECATED);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.client;
|
||||
|
||||
import java.util.IdentityHashMap;
|
||||
|
||||
@@ -6,10 +6,6 @@
|
||||
package net.minecraftforge.client;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.graph.ElementOrder;
|
||||
import com.google.common.graph.GraphBuilder;
|
||||
import com.google.common.graph.MutableGraph;
|
||||
import com.mojang.blaze3d.platform.NativeImage;
|
||||
import com.mojang.blaze3d.platform.Window;
|
||||
import com.mojang.blaze3d.shaders.FogShape;
|
||||
@@ -74,7 +70,6 @@ import net.minecraft.client.sounds.SoundEngine;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.locale.Language;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.Connection;
|
||||
import net.minecraft.network.chat.ChatType;
|
||||
import net.minecraft.network.chat.CommonComponents;
|
||||
@@ -94,9 +89,11 @@ import net.minecraft.world.entity.HumanoidArm;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.ai.attributes.AttributeInstance;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.flag.FeatureFlagSet;
|
||||
import net.minecraft.world.inventory.tooltip.TooltipComponent;
|
||||
import net.minecraft.world.item.CreativeModeTab;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.ItemStackLinkedSet;
|
||||
import net.minecraft.world.item.crafting.RecipeManager;
|
||||
import net.minecraft.world.level.BlockAndTintGetter;
|
||||
import net.minecraft.world.level.GameType;
|
||||
@@ -144,7 +141,7 @@ import net.minecraftforge.client.textures.ForgeTextureMetadata;
|
||||
import net.minecraftforge.common.ForgeI18n;
|
||||
import net.minecraftforge.common.ForgeMod;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.common.util.ItemStackMap;
|
||||
import net.minecraftforge.common.util.MutableHashedLinkedMap;
|
||||
import net.minecraftforge.event.CreativeModeTabEvent;
|
||||
import net.minecraftforge.eventbus.api.Event;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
@@ -153,8 +150,6 @@ import net.minecraftforge.fml.ModList;
|
||||
import net.minecraftforge.fml.ModLoader;
|
||||
import net.minecraftforge.fml.VersionChecker;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.loading.toposort.CyclePresentException;
|
||||
import net.minecraftforge.fml.loading.toposort.TopologicalSort;
|
||||
import net.minecraftforge.network.NetworkConstants;
|
||||
import net.minecraftforge.network.NetworkRegistry;
|
||||
import net.minecraftforge.registries.GameData;
|
||||
@@ -174,9 +169,9 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
@@ -435,7 +430,7 @@ public class ForgeHooksClient
|
||||
private static void drawScreenInternal(Screen screen, PoseStack poseStack, int mouseX, int mouseY, float partialTick)
|
||||
{
|
||||
if (!MinecraftForge.EVENT_BUS.post(new ScreenEvent.Render.Pre(screen, poseStack, mouseX, mouseY, partialTick)))
|
||||
screen.render(poseStack, mouseX, mouseY, partialTick);
|
||||
screen.renderWithTooltip(poseStack, mouseX, mouseY, partialTick);
|
||||
MinecraftForge.EVENT_BUS.post(new ScreenEvent.Render.Post(screen, poseStack, mouseX, mouseY, partialTick));
|
||||
}
|
||||
|
||||
@@ -477,9 +472,14 @@ public class ForgeHooksClient
|
||||
return event;
|
||||
}
|
||||
|
||||
public static void onModifyBakingResult(Map<ResourceLocation, BakedModel> models, ModelBakery modelBakery)
|
||||
{
|
||||
ModLoader.get().postEvent(new ModelEvent.ModifyBakingResult(models, modelBakery));
|
||||
}
|
||||
|
||||
public static void onModelBake(ModelManager modelManager, Map<ResourceLocation, BakedModel> models, ModelBakery modelBakery)
|
||||
{
|
||||
ModLoader.get().postEvent(new ModelEvent.BakingCompleted(modelManager, models, modelBakery));
|
||||
ModLoader.get().postEvent(new ModelEvent.BakingCompleted(modelManager, Collections.unmodifiableMap(models), modelBakery));
|
||||
}
|
||||
|
||||
public static BakedModel handleCameraTransforms(PoseStack poseStack, BakedModel model, ItemTransforms.TransformType cameraTransformType, boolean applyLeftHandTransform)
|
||||
@@ -1189,106 +1189,26 @@ public class ForgeHooksClient
|
||||
return new ResourceLocation(loc.getNamespace(), normalised);
|
||||
}
|
||||
|
||||
public static CreativeModeTab.DisplayItemsGenerator onCreativeModeTabBuildContents(CreativeModeTab tab, CreativeModeTab.DisplayItemsGenerator originalGenerator) {
|
||||
Set<CreativeModeTabEvent.DisplayItemsAdapter> customGenerators = new HashSet<>();
|
||||
CreativeModeTabEvent.BuildContents event = ModLoader.get().postEventWithReturn(new net.minecraftforge.event.CreativeModeTabEvent.BuildContents(tab, customGenerators));
|
||||
|
||||
record ModificationEntry(CreativeModeTab.TabVisibility tabVisibility, ItemStack before, ItemStack after) {}
|
||||
|
||||
return (featureFlagSet, output, hasPermissions) -> {
|
||||
final Map<ItemStack, CreativeModeTab.TabVisibility> originalEntries = ItemStackMap.createTypeAndTagLinkedMap();
|
||||
final Map<ItemStack, ModificationEntry> entries = ItemStackMap.createTypeAndTagMap();
|
||||
|
||||
originalGenerator.accept(featureFlagSet, originalEntries::put, hasPermissions);
|
||||
customGenerators.forEach(generator -> generator.accept(featureFlagSet, (stack, visibility, before, after) -> {
|
||||
if (stack.getCount() != 1)
|
||||
throw new IllegalArgumentException("The stack count must be 1");
|
||||
|
||||
entries.put(stack, new ModificationEntry(visibility, before, after));
|
||||
}, hasPermissions));
|
||||
|
||||
// We switch to the stacks tag here, as we need to be able to compare and hash them for the graph!
|
||||
// We could not handle this with an open hashset since the builder does not support this internally.
|
||||
final MutableGraph<CompoundTag> graph = GraphBuilder.directed().nodeOrder(ElementOrder.insertion()).build();
|
||||
final Map<CompoundTag, ItemStack> tagToStack = Maps.newHashMap();
|
||||
|
||||
CompoundTag lastStackTag = null;
|
||||
for (ItemStack stack : originalEntries.keySet())
|
||||
{
|
||||
final CompoundTag tag = stack.serializeNBT();
|
||||
tagToStack.put(tag, stack);
|
||||
if (!stack.isEmpty())
|
||||
{
|
||||
graph.addNode(tag);
|
||||
}
|
||||
if (lastStackTag != null)
|
||||
{
|
||||
graph.putEdge(lastStackTag, tag);
|
||||
}
|
||||
lastStackTag = tag;
|
||||
public static void onCreativeModeTabBuildContents(CreativeModeTab tab, CreativeModeTab.DisplayItemsGenerator originalGenerator, FeatureFlagSet flags, CreativeModeTab.Output output, boolean hasPermisions)
|
||||
{
|
||||
final var entries = new MutableHashedLinkedMap<ItemStack, CreativeModeTab.TabVisibility>(ItemStackLinkedSet.TYPE_AND_TAG,
|
||||
(key, left, right) -> {
|
||||
//throw new IllegalStateException("Accidentally adding the same item stack twice " + key.getDisplayName().getString() + " to a Creative Mode Tab: " + tab.getDisplayName().getString());
|
||||
// Vanilla adds enchanting books twice in both visibilities.
|
||||
// This is just code cleanliness for them. For us lets just increase the visibility and merge the entries.
|
||||
return CreativeModeTab.TabVisibility.PARENT_AND_SEARCH_TABS;
|
||||
}
|
||||
);
|
||||
|
||||
CompoundTag finalLastStackTag = lastStackTag;
|
||||
entries.forEach((stack, modificationEntry) -> {
|
||||
final CompoundTag tag = stack.serializeNBT();
|
||||
if (!stack.isEmpty())
|
||||
{
|
||||
graph.addNode(tag);
|
||||
tagToStack.put(tag, stack);
|
||||
}
|
||||
if (!modificationEntry.after.isEmpty())
|
||||
{
|
||||
final CompoundTag afterTag = modificationEntry.after.serializeNBT();
|
||||
graph.putEdge(afterTag, tag);
|
||||
tagToStack.put(afterTag, modificationEntry.after);
|
||||
}
|
||||
else if (finalLastStackTag != null)
|
||||
{
|
||||
// If the user does not specify an after, we assume it is the last stack and put it after that.
|
||||
graph.putEdge(finalLastStackTag, tag);
|
||||
}
|
||||
if (!modificationEntry.before.isEmpty())
|
||||
{
|
||||
final CompoundTag beforeTag = modificationEntry.before.serializeNBT();
|
||||
graph.putEdge(tag, beforeTag);
|
||||
tagToStack.put(beforeTag, modificationEntry.before);
|
||||
}
|
||||
});
|
||||
originalGenerator.accept(flags, (stack, vis) -> {
|
||||
if (stack.getCount() != 1)
|
||||
throw new IllegalArgumentException("The stack count must be 1");
|
||||
entries.put(stack, vis);
|
||||
}, hasPermisions);
|
||||
|
||||
try
|
||||
{
|
||||
List<CompoundTag> tierList = TopologicalSort.topologicalSort(graph, null);
|
||||
ModLoader.get().postEvent(new CreativeModeTabEvent.BuildContents(tab, flags, hasPermisions, entries));
|
||||
|
||||
// Now we unwind the tags again so that they are sorted neatly.
|
||||
for (CompoundTag tag : tierList)
|
||||
{
|
||||
final ItemStack stack = tagToStack.get(tag);
|
||||
if (originalEntries.containsKey(stack))
|
||||
{
|
||||
output.accept(stack, originalEntries.get(stack));
|
||||
}
|
||||
else if (entries.containsKey(stack))
|
||||
{
|
||||
output.accept(stack, entries.get(stack).tabVisibility);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (CyclePresentException cyclePresentException)
|
||||
{
|
||||
LOGGER.error("Failed to inject custom items into creative mode tab {} due to a cycle in the dependency graph", tab, cyclePresentException);
|
||||
LOGGER.warn("Detected cycles:");
|
||||
for (Set<Object> cycle : cyclePresentException.getCycles())
|
||||
{
|
||||
graph.edges().forEach(edge -> {
|
||||
if (cycle.contains(edge.nodeU()) && cycle.contains(edge.nodeV()))
|
||||
{
|
||||
LOGGER.warn(" - {} -> {}", tagToStack.get(edge.nodeU()), tagToStack.get(edge.nodeV()));
|
||||
}
|
||||
});
|
||||
}
|
||||
LOGGER.error("Falling back to default!");
|
||||
originalGenerator.accept(featureFlagSet, output, hasPermissions);
|
||||
}
|
||||
};
|
||||
for (var entry : entries)
|
||||
output.accept(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
@@ -32,9 +32,56 @@ public abstract class ModelEvent extends Event
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Fired while the {@link ModelManager} is reloading models, after the model registry is set up, but before it's
|
||||
* passed to the {@link net.minecraft.client.renderer.block.BlockModelShaper} for caching.
|
||||
*
|
||||
* <p>
|
||||
* This event is fired from a worker thread and it is therefore not safe to access anything outside the
|
||||
* model registry and {@link ModelBakery} provided in this event.<br>
|
||||
* The {@link ModelManager} firing this event is not fully set up with the latest data when this event fires and
|
||||
* must therefore not be accessed in this event.
|
||||
* </p>
|
||||
*
|
||||
* <p>This event is not {@linkplain Cancelable cancellable}, and does not {@linkplain HasResult have a result}.</p>
|
||||
*
|
||||
* <p>This event is fired on the {@linkplain FMLJavaModLoadingContext#getModEventBus() mod-specific event bus},
|
||||
* only on the {@linkplain LogicalSide#CLIENT logical client}.</p>
|
||||
*/
|
||||
public static class ModifyBakingResult extends ModelEvent implements IModBusEvent
|
||||
{
|
||||
private final Map<ResourceLocation, BakedModel> models;
|
||||
private final ModelBakery modelBakery;
|
||||
|
||||
@ApiStatus.Internal
|
||||
public ModifyBakingResult(Map<ResourceLocation, BakedModel> models, ModelBakery modelBakery)
|
||||
{
|
||||
this.models = models;
|
||||
this.modelBakery = modelBakery;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the modifiable registry map of models and their model names
|
||||
*/
|
||||
public Map<ResourceLocation, BakedModel> getModels()
|
||||
{
|
||||
return models;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the model loader
|
||||
*/
|
||||
public ModelBakery getModelBakery()
|
||||
{
|
||||
return modelBakery;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fired when the {@link ModelManager} is notified of the resource manager reloading.
|
||||
* Called after model registry is set up, but before it's passed to {@link net.minecraft.client.renderer.block.BlockModelShaper}.
|
||||
* Called after the model registry is set up and cached in the {@link net.minecraft.client.renderer.block.BlockModelShaper}.<br>
|
||||
* The model registry given by this event is unmodifiable. To modify the model registry, use
|
||||
* {@link ModelEvent.ModifyBakingResult} instead.
|
||||
*
|
||||
* <p>This event is not {@linkplain Cancelable cancellable}, and does not {@linkplain HasResult have a result}.</p>
|
||||
*
|
||||
@@ -64,7 +111,7 @@ public abstract class ModelEvent extends Event
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the modifiable registry map of models and their model names
|
||||
* @return an unmodifiable view of the registry map of models and their model names
|
||||
*/
|
||||
public Map<ResourceLocation, BakedModel> getModels()
|
||||
{
|
||||
|
||||
@@ -575,7 +575,7 @@ public class ForgeGui extends Gui
|
||||
minecraft.getProfiler().push("chat");
|
||||
|
||||
Window window = minecraft.getWindow();
|
||||
var event = new CustomizeGuiOverlayEvent.Chat(window, pStack, minecraft.getFrameTime(), 0, height - 48);
|
||||
var event = new CustomizeGuiOverlayEvent.Chat(window, pStack, minecraft.getFrameTime(), 0, height - 40);
|
||||
MinecraftForge.EVENT_BUS.post(event);
|
||||
|
||||
pStack.pushPose();
|
||||
|
||||
@@ -33,6 +33,11 @@ public class ExtendedButton extends Button
|
||||
super(xPos, yPos, width, height, displayString, handler, createNarration);
|
||||
}
|
||||
|
||||
public ExtendedButton(Button.Builder builder)
|
||||
{
|
||||
super(builder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws this button to the screen.
|
||||
*/
|
||||
@@ -40,7 +45,7 @@ public class ExtendedButton extends Button
|
||||
public void renderButton(PoseStack poseStack, int mouseX, int mouseY, float partialTick)
|
||||
{
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
int k = this.getYImage(this.isHovered);
|
||||
int k = this.getYImage(this.isHoveredOrFocused());
|
||||
ScreenUtils.blitWithBorder(poseStack, WIDGETS_LOCATION, this.getX(), this.getY(), 0, 46 + k * 20, this.width, this.height, 200, 20, 2, 3, 2, 2, this.getBlitOffset());
|
||||
this.renderBg(poseStack, mc, mouseX, mouseY);
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ public class UnicodeGlyphButton extends ExtendedButton
|
||||
{
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
this.isHovered = mouseX >= this.getX() && mouseY >= this.getY() && mouseX < this.getX() + this.width && mouseY < this.getY() + this.height;
|
||||
int k = this.getYImage(this.isHovered);
|
||||
int k = this.getYImage(this.isHoveredOrFocused());
|
||||
ScreenUtils.blitWithBorder(poseStack, WIDGETS_LOCATION, this.getX(), this.getY(), 0, 46 + k * 20, this.width, this.height, 200, 20, 2, 3, 2, 2, this.getBlitOffset());
|
||||
this.renderBg(poseStack, mc, mouseX, mouseY);
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.client.resources.model.Material;
|
||||
import net.minecraft.client.resources.model.ModelBaker;
|
||||
import net.minecraft.client.resources.model.ModelState;
|
||||
import net.minecraft.client.resources.model.UnbakedModel;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
@@ -112,6 +113,12 @@ public class CompositeModel implements IUnbakedGeometry<CompositeModel>
|
||||
return new Baked(context.isGui3d(), context.useBlockLight(), context.useAmbientOcclusion(), particle, context.getTransforms(), overrides, bakedParts, itemPassesBuilder.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolveParents(Function<ResourceLocation, UnbakedModel> modelGetter, IGeometryBakingContext context)
|
||||
{
|
||||
children.values().forEach(child -> child.resolveParents(modelGetter));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getConfigurableComponentNames()
|
||||
{
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.client.model;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.JsonOps;
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||
|
||||
import net.minecraft.client.renderer.block.model.BlockElement;
|
||||
import net.minecraft.client.renderer.block.model.BlockElementFace;
|
||||
import net.minecraft.util.ExtraCodecs;
|
||||
|
||||
/**
|
||||
* Holds extra data that may be injected into a face.<p>
|
||||
* Used by {@link ItemLayerModel}, {@link BlockElement} and {@link BlockElementFace}
|
||||
*
|
||||
* @param color Color in ARGB format
|
||||
* @param blockLight Block Light for this face from 0-15 (inclusive)
|
||||
* @param skyLight Sky Light for this face from 0-15 (inclusive)
|
||||
* @param ambientOcclusion If this face has AO
|
||||
*/
|
||||
public record ForgeFaceData(int color, int blockLight, int skyLight, boolean ambientOcclusion)
|
||||
{
|
||||
|
||||
public static final ForgeFaceData DEFAULT = new ForgeFaceData(0xFFFFFFFF, 0, 0, true);
|
||||
|
||||
public static final Codec<Integer> COLOR = new ExtraCodecs.EitherCodec<>(Codec.INT, Codec.STRING).xmap(
|
||||
either -> either.map(Function.identity(), str -> (int) Long.parseLong(str, 16)),
|
||||
color -> Either.right(Integer.toHexString(color)));
|
||||
|
||||
public static final Codec<ForgeFaceData> CODEC = RecordCodecBuilder.create(builder -> builder.group(
|
||||
COLOR.optionalFieldOf("color", 0xFFFFFFFF).forGetter(ForgeFaceData::color),
|
||||
Codec.intRange(0, 15).optionalFieldOf("block_light", 0).forGetter(ForgeFaceData::blockLight),
|
||||
Codec.intRange(0, 15).optionalFieldOf("sky_light", 0).forGetter(ForgeFaceData::skyLight),
|
||||
Codec.BOOL.optionalFieldOf("ambient_occlusion", true).forGetter(ForgeFaceData::ambientOcclusion))
|
||||
.apply(builder, ForgeFaceData::new));
|
||||
|
||||
/**
|
||||
* Parses a ForgeFaceData from JSON
|
||||
* @param obj The JsonObject to parse from, weakly-typed to JsonElement to reduce logic complexity.
|
||||
* @param fallback What to return if the first parameter is null.
|
||||
* @return The parsed ForgeFaceData, or the fallback parameter if the first parmeter is null.
|
||||
* @throws JsonParseException
|
||||
*/
|
||||
@Nullable
|
||||
public static ForgeFaceData read(@Nullable JsonElement obj, @Nullable ForgeFaceData fallback) throws JsonParseException
|
||||
{
|
||||
if(obj == null)
|
||||
{
|
||||
return fallback;
|
||||
}
|
||||
return CODEC.parse(JsonOps.INSTANCE, obj).getOrThrow(false, JsonParseException::new);
|
||||
}
|
||||
}
|
||||
@@ -10,11 +10,9 @@ import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.renderer.block.model.ItemModelGenerator;
|
||||
import net.minecraft.client.renderer.block.model.ItemOverrides;
|
||||
@@ -23,7 +21,6 @@ import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.client.resources.model.Material;
|
||||
import net.minecraft.client.resources.model.ModelBaker;
|
||||
import net.minecraft.client.resources.model.ModelState;
|
||||
import net.minecraft.client.resources.model.UnbakedModel;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraftforge.client.ForgeRenderTypes;
|
||||
import net.minecraftforge.client.RenderTypeGroup;
|
||||
@@ -31,13 +28,9 @@ import net.minecraftforge.client.model.geometry.IGeometryBakingContext;
|
||||
import net.minecraftforge.client.model.geometry.IGeometryLoader;
|
||||
import net.minecraftforge.client.model.geometry.IUnbakedGeometry;
|
||||
import net.minecraftforge.client.model.geometry.UnbakedGeometryHelper;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
@@ -48,26 +41,16 @@ import java.util.function.Function;
|
||||
*/
|
||||
public class ItemLayerModel implements IUnbakedGeometry<ItemLayerModel>
|
||||
{
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
|
||||
@Nullable
|
||||
private ImmutableList<Material> textures;
|
||||
private final IntSet emissiveLayers;
|
||||
private final Int2ObjectMap<ForgeFaceData> layerData;
|
||||
private final Int2ObjectMap<ResourceLocation> renderTypeNames;
|
||||
private final boolean deprecatedLoader, logWarning;
|
||||
|
||||
public ItemLayerModel(@Nullable ImmutableList<Material> textures, IntSet emissiveLayers, Int2ObjectMap<ResourceLocation> renderTypeNames)
|
||||
{
|
||||
this(textures, emissiveLayers, renderTypeNames, false, false);
|
||||
}
|
||||
|
||||
private ItemLayerModel(@Nullable ImmutableList<Material> textures, IntSet emissiveLayers, Int2ObjectMap<ResourceLocation> renderTypeNames, boolean deprecatedLoader, boolean logWarning)
|
||||
private ItemLayerModel(@Nullable ImmutableList<Material> textures, Int2ObjectMap<ForgeFaceData> layerData, Int2ObjectMap<ResourceLocation> renderTypeNames)
|
||||
{
|
||||
this.textures = textures;
|
||||
this.emissiveLayers = emissiveLayers;
|
||||
this.layerData = layerData;
|
||||
this.renderTypeNames = renderTypeNames;
|
||||
this.deprecatedLoader = deprecatedLoader;
|
||||
this.logWarning = logWarning;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -84,11 +67,6 @@ public class ItemLayerModel implements IUnbakedGeometry<ItemLayerModel>
|
||||
textures = builder.build();
|
||||
}
|
||||
|
||||
if (deprecatedLoader)
|
||||
LOGGER.warn("Model \"" + modelLocation + "\" is using the deprecated loader \"forge:item-layers\" instead of \"forge:item_layers\". This loader will be removed in 1.20.");
|
||||
if (logWarning)
|
||||
LOGGER.warn("Model \"" + modelLocation + "\" is using the deprecated \"fullbright_layers\" field in its item layer model instead of \"emissive_layers\". This field will be removed in 1.20.");
|
||||
|
||||
TextureAtlasSprite particle = spriteGetter.apply(
|
||||
context.hasMaterial("particle") ? context.getMaterial("particle") : textures.get(0)
|
||||
);
|
||||
@@ -101,9 +79,8 @@ public class ItemLayerModel implements IUnbakedGeometry<ItemLayerModel>
|
||||
for (int i = 0; i < textures.size(); i++)
|
||||
{
|
||||
TextureAtlasSprite sprite = spriteGetter.apply(textures.get(i));
|
||||
var unbaked = UnbakedGeometryHelper.createUnbakedItemElements(i, sprite.contents());
|
||||
var unbaked = UnbakedGeometryHelper.createUnbakedItemElements(i, sprite.contents(), this.layerData.get(i));
|
||||
var quads = UnbakedGeometryHelper.bakeElements(unbaked, $ -> sprite, modelState, modelLocation);
|
||||
if (emissiveLayers.contains(i)) QuadTransformers.settingMaxEmissivity().processInPlace(quads);
|
||||
var renderTypeName = renderTypeNames.get(i);
|
||||
var renderTypes = renderTypeName != null ? context.getRenderType(renderTypeName) : null;
|
||||
builder.addQuads(renderTypes != null ? renderTypes : normalRenderTypes, quads);
|
||||
@@ -114,16 +91,7 @@ public class ItemLayerModel implements IUnbakedGeometry<ItemLayerModel>
|
||||
|
||||
public static final class Loader implements IGeometryLoader<ItemLayerModel>
|
||||
{
|
||||
public static final Loader INSTANCE = new Loader(false);
|
||||
@Deprecated(forRemoval = true, since = "1.19")
|
||||
public static final Loader INSTANCE_DEPRECATED = new Loader(true);
|
||||
|
||||
private final boolean deprecated;
|
||||
|
||||
private Loader(boolean deprecated)
|
||||
{
|
||||
this.deprecated = deprecated;
|
||||
}
|
||||
public static final Loader INSTANCE = new Loader();
|
||||
|
||||
@Override
|
||||
public ItemLayerModel read(JsonObject jsonObject, JsonDeserializationContext deserializationContext)
|
||||
@@ -141,25 +109,28 @@ public class ItemLayerModel implements IUnbakedGeometry<ItemLayerModel>
|
||||
}
|
||||
}
|
||||
|
||||
var emissiveLayers = new IntOpenHashSet();
|
||||
readUnlit(jsonObject, "emissive_layers", renderTypeNames, emissiveLayers, false);
|
||||
boolean logWarning = readUnlit(jsonObject, "fullbright_layers", renderTypeNames, emissiveLayers, true); // TODO: Deprecated name. To be removed in 1.20
|
||||
|
||||
return new ItemLayerModel(null, emissiveLayers, renderTypeNames, deprecated, logWarning);
|
||||
var emissiveLayers = new Int2ObjectArrayMap<ForgeFaceData>();
|
||||
if(jsonObject.has("forge_data"))
|
||||
{
|
||||
JsonObject forgeData = jsonObject.get("forge_data").getAsJsonObject();
|
||||
readLayerData(forgeData, "layers", renderTypeNames, emissiveLayers, false);
|
||||
}
|
||||
return new ItemLayerModel(null, emissiveLayers, renderTypeNames);
|
||||
}
|
||||
|
||||
private boolean readUnlit(JsonObject jsonObject, String name, Int2ObjectOpenHashMap<ResourceLocation> renderTypeNames, IntOpenHashSet litLayers, boolean logWarning)
|
||||
protected void readLayerData(JsonObject jsonObject, String name, Int2ObjectOpenHashMap<ResourceLocation> renderTypeNames, Int2ObjectMap<ForgeFaceData> layerData, boolean logWarning)
|
||||
{
|
||||
if (!jsonObject.has(name))
|
||||
return false;
|
||||
var fullbrightLayers = jsonObject.getAsJsonArray(name);
|
||||
var renderType = new ResourceLocation("forge", "item_unlit");
|
||||
for (var layer : fullbrightLayers)
|
||||
{
|
||||
litLayers.add(layer.getAsInt());
|
||||
renderTypeNames.putIfAbsent(layer.getAsInt(), renderType);
|
||||
return;
|
||||
}
|
||||
var fullbrightLayers = jsonObject.getAsJsonObject(name);
|
||||
for (var entry : fullbrightLayers.entrySet())
|
||||
{
|
||||
int layer = Integer.parseInt(entry.getKey());
|
||||
var data = ForgeFaceData.read(entry.getValue(), ForgeFaceData.DEFAULT);
|
||||
layerData.put(layer, data);
|
||||
}
|
||||
return logWarning && !fullbrightLayers.isEmpty();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -83,19 +83,27 @@ public final class QuadTransformers {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return a new {@link BakedQuad} transformer that applies the specified lightmap}
|
||||
* @return A new {@link BakedQuad} transformer that applies the specified packed light value.
|
||||
*/
|
||||
public static IQuadTransformer applyingLightmap(int lightmap)
|
||||
public static IQuadTransformer applyingLightmap(int packedLight)
|
||||
{
|
||||
return quad -> {
|
||||
var vertices = quad.getVertices();
|
||||
for (int i = 0; i < 4; i++)
|
||||
vertices[i * IQuadTransformer.STRIDE + IQuadTransformer.UV2] = lightmap;
|
||||
vertices[i * IQuadTransformer.STRIDE + IQuadTransformer.UV2] = packedLight;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return a {@link BakedQuad} transformer that sets the lightmap to the given emissivity (0-15)}
|
||||
* @return A new {@link BakedQuad} transformer that applies the specified block and sky light values.
|
||||
*/
|
||||
public static IQuadTransformer applyingLightmap(int blockLight, int skyLight)
|
||||
{
|
||||
return applyingLightmap(LightTexture.pack(blockLight, skyLight));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A {@link BakedQuad} transformer that sets the lightmap to the given emissivity (0-15)
|
||||
*/
|
||||
public static IQuadTransformer settingEmissivity(int emissivity)
|
||||
{
|
||||
@@ -104,13 +112,64 @@ public final class QuadTransformers {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return a {@link BakedQuad} transformer that sets the lightmap to its max value}
|
||||
* @return A {@link BakedQuad} transformer that sets the lightmap to its max value
|
||||
*/
|
||||
public static IQuadTransformer settingMaxEmissivity()
|
||||
{
|
||||
return EMISSIVE_TRANSFORMERS[15];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param color The color in ARGB format.
|
||||
* @return A {@link BakedQuad} transformer that sets the color to the specified value.
|
||||
*/
|
||||
public static IQuadTransformer applyingColor(int color)
|
||||
{
|
||||
final int fixedColor = toABGR(color);
|
||||
return quad -> {
|
||||
var vertices = quad.getVertices();
|
||||
for (int i = 0; i < 4; i++)
|
||||
vertices[i * IQuadTransformer.STRIDE + IQuadTransformer.COLOR] = fixedColor;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* This method supplies a default alpha value of 255 (no transparency)
|
||||
* @param red The red value (0-255)
|
||||
* @param green The green value (0-255)
|
||||
* @param blue The blue value (0-255)
|
||||
* @return A {@link BakedQuad} transformer that sets the color to the specified value.
|
||||
*/
|
||||
public static IQuadTransformer applyingColor(int red, int green, int blue)
|
||||
{
|
||||
return applyingColor(255, red, green, blue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param alpha The alpha value (0-255)
|
||||
* @param red The red value (0-255)
|
||||
* @param green The green value (0-255)
|
||||
* @param blue The blue value (0-255)
|
||||
* @return A {@link BakedQuad} transformer that sets the color to the specified value.
|
||||
*/
|
||||
public static IQuadTransformer applyingColor(int alpha, int red, int green, int blue)
|
||||
{
|
||||
return applyingColor(alpha << 24 | red << 16 | green << 8 | blue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an ARGB color to an ABGR color, as the commonly used color format is not the format colors end up packed into.
|
||||
* This function doubles as its own inverse.
|
||||
* @param color ARGB color
|
||||
* @return ABGR color
|
||||
*/
|
||||
public static int toABGR(int argb)
|
||||
{
|
||||
return (argb & 0xFF00FF00) // alpha and green same spot
|
||||
| ((argb >> 16) & 0x000000FF) // red moves to blue
|
||||
| ((argb << 16) & 0x00FF0000); // blue moves to red
|
||||
}
|
||||
|
||||
private QuadTransformers()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.client.resources.model.Material;
|
||||
import net.minecraft.client.resources.model.ModelBaker;
|
||||
import net.minecraft.client.resources.model.ModelState;
|
||||
import net.minecraft.client.resources.model.UnbakedModel;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.util.GsonHelper;
|
||||
@@ -78,6 +79,13 @@ public class SeparateTransformsModel implements IUnbakedGeometry<SeparateTransfo
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolveParents(Function<ResourceLocation, UnbakedModel> modelGetter, IGeometryBakingContext context)
|
||||
{
|
||||
baseModel.resolveParents(modelGetter);
|
||||
perspectives.values().forEach(model -> model.resolveParents(modelGetter));
|
||||
}
|
||||
|
||||
public static class Baked implements IDynamicBakedModel
|
||||
{
|
||||
private final boolean isAmbientOcclusion;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
package net.minecraftforge.client.model.generators;
|
||||
|
||||
import net.minecraft.data.DataGenerator;
|
||||
import net.minecraft.data.PackOutput;
|
||||
import net.minecraftforge.common.data.ExistingFileHelper;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@@ -15,8 +15,8 @@ import org.jetbrains.annotations.NotNull;
|
||||
*/
|
||||
public abstract class BlockModelProvider extends ModelProvider<BlockModelBuilder> {
|
||||
|
||||
public BlockModelProvider(DataGenerator generator, String modid, ExistingFileHelper existingFileHelper) {
|
||||
super(generator, modid, BLOCK_FOLDER, BlockModelBuilder::new, existingFileHelper);
|
||||
public BlockModelProvider(PackOutput output, String modid, ExistingFileHelper existingFileHelper) {
|
||||
super(output, modid, BLOCK_FOLDER, BlockModelBuilder::new, existingFileHelper);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -14,7 +14,7 @@ import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import java.io.IOException;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
@@ -25,7 +25,6 @@ import java.util.function.Function;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.Direction.Axis;
|
||||
import net.minecraft.data.CachedOutput;
|
||||
import net.minecraft.data.DataGenerator;
|
||||
import net.minecraft.data.DataProvider;
|
||||
import net.minecraft.data.PackOutput;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
@@ -73,20 +72,20 @@ public abstract class BlockStateProvider implements DataProvider {
|
||||
@VisibleForTesting
|
||||
protected final Map<Block, IGeneratedBlockState> registeredBlocks = new LinkedHashMap<>();
|
||||
|
||||
private final DataGenerator generator;
|
||||
private final PackOutput output;
|
||||
private final String modid;
|
||||
private final BlockModelProvider blockModels;
|
||||
private final ItemModelProvider itemModels;
|
||||
|
||||
public BlockStateProvider(DataGenerator gen, String modid, ExistingFileHelper exFileHelper) {
|
||||
this.generator = gen;
|
||||
public BlockStateProvider(PackOutput output, String modid, ExistingFileHelper exFileHelper) {
|
||||
this.output = output;
|
||||
this.modid = modid;
|
||||
this.blockModels = new BlockModelProvider(gen, modid, exFileHelper) {
|
||||
this.blockModels = new BlockModelProvider(output, modid, exFileHelper) {
|
||||
@Override public CompletableFuture<?> run(CachedOutput cache) { return CompletableFuture.allOf(); }
|
||||
|
||||
@Override protected void registerModels() {}
|
||||
};
|
||||
this.itemModels = new ItemModelProvider(gen, modid, this.blockModels.existingFileHelper) {
|
||||
this.itemModels = new ItemModelProvider(output, modid, this.blockModels.existingFileHelper) {
|
||||
@Override protected void registerModels() {}
|
||||
|
||||
@Override public CompletableFuture<?> run(CachedOutput cache) { return CompletableFuture.allOf(); }
|
||||
@@ -99,10 +98,10 @@ public abstract class BlockStateProvider implements DataProvider {
|
||||
itemModels().clear();
|
||||
registeredBlocks.clear();
|
||||
registerStatesAndModels();
|
||||
models().generateAll(cache);
|
||||
itemModels().generateAll(cache);
|
||||
CompletableFuture<?>[] futures = new CompletableFuture<?>[this.registeredBlocks.size()];
|
||||
CompletableFuture<?>[] futures = new CompletableFuture<?>[2 + this.registeredBlocks.size()];
|
||||
int i = 0;
|
||||
futures[i++] = models().generateAll(cache);
|
||||
futures[i++] = itemModels().generateAll(cache);
|
||||
for (Map.Entry<Block, IGeneratedBlockState> entry : registeredBlocks.entrySet()) {
|
||||
futures[i++] = saveBlockState(cache, entry.getValue().toJson(), entry.getKey());
|
||||
}
|
||||
@@ -188,6 +187,11 @@ public abstract class BlockStateProvider implements DataProvider {
|
||||
itemModels().getBuilder(key(block).getPath()).parent(model);
|
||||
}
|
||||
|
||||
public void simpleBlockWithItem(Block block, ModelFile model) {
|
||||
simpleBlock(block, model);
|
||||
simpleBlockItem(block, model);
|
||||
}
|
||||
|
||||
public void simpleBlock(Block block, ConfiguredModel... models) {
|
||||
getVariantBuilder(block)
|
||||
.partialState().setModels(models);
|
||||
@@ -857,7 +861,7 @@ public abstract class BlockStateProvider implements DataProvider {
|
||||
|
||||
private CompletableFuture<?> saveBlockState(CachedOutput cache, JsonObject stateJson, Block owner) {
|
||||
ResourceLocation blockName = Preconditions.checkNotNull(key(owner));
|
||||
Path outputPath = this.generator.getPackOutput().getOutputFolder(PackOutput.Target.RESOURCE_PACK)
|
||||
Path outputPath = this.output.getOutputFolder(PackOutput.Target.RESOURCE_PACK)
|
||||
.resolve(blockName.getNamespace()).resolve("blockstates").resolve(blockName.getPath() + ".json");
|
||||
return DataProvider.saveStable(cache, stateJson, outputPath);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
package net.minecraftforge.client.model.generators;
|
||||
|
||||
import net.minecraft.data.DataGenerator;
|
||||
import net.minecraft.data.PackOutput;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraftforge.common.data.ExistingFileHelper;
|
||||
@@ -20,8 +20,8 @@ import java.util.Objects;
|
||||
*/
|
||||
public abstract class ItemModelProvider extends ModelProvider<ItemModelBuilder> {
|
||||
|
||||
public ItemModelProvider(DataGenerator generator, String modid, ExistingFileHelper existingFileHelper) {
|
||||
super(generator, modid, ITEM_FOLDER, ItemModelBuilder::new, existingFileHelper);
|
||||
public ItemModelProvider(PackOutput output, String modid, ExistingFileHelper existingFileHelper) {
|
||||
super(output, modid, ITEM_FOLDER, ItemModelBuilder::new, existingFileHelper);
|
||||
}
|
||||
|
||||
public ItemModelBuilder basicItem(Item item)
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.google.common.base.Preconditions;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.mojang.serialization.JsonOps;
|
||||
|
||||
import net.minecraft.client.renderer.block.model.BlockFaceUV;
|
||||
import net.minecraft.client.renderer.block.model.BlockModel.GuiLight;
|
||||
@@ -32,6 +33,7 @@ import net.minecraft.client.renderer.texture.MissingTextureAtlasSprite;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraftforge.client.model.ForgeFaceData;
|
||||
import net.minecraftforge.common.data.ExistingFileHelper;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.joml.Vector3f;
|
||||
@@ -60,7 +62,7 @@ public class ModelBuilder<T extends ModelBuilder<T>> extends ModelFile {
|
||||
|
||||
protected final List<ElementBuilder> elements = new ArrayList<>();
|
||||
|
||||
protected CustomLoaderBuilder customLoader = null;
|
||||
protected CustomLoaderBuilder<T> customLoader = null;
|
||||
|
||||
protected ModelBuilder(ResourceLocation outputLocation, ExistingFileHelper existingFileHelper) {
|
||||
super(outputLocation);
|
||||
@@ -317,11 +319,8 @@ public class ModelBuilder<T extends ModelBuilder<T>> extends ModelFile {
|
||||
if (face.tintIndex != -1) {
|
||||
faceObj.addProperty("tintindex", face.tintIndex);
|
||||
}
|
||||
if (face.emissivity > 0) {
|
||||
faceObj.addProperty("emissivity", face.emissivity);
|
||||
}
|
||||
if (!face.hasAmbientOcclusion) {
|
||||
faceObj.addProperty("ambientocclusion", face.hasAmbientOcclusion);
|
||||
if (!face.getFaceData().equals(ForgeFaceData.DEFAULT)) {
|
||||
faceObj.add("forge_data", ForgeFaceData.CODEC.encodeStart(JsonOps.INSTANCE, face.getFaceData()).result().get());
|
||||
}
|
||||
faces.add(dir.getSerializedName(), faceObj);
|
||||
}
|
||||
@@ -368,6 +367,9 @@ public class ModelBuilder<T extends ModelBuilder<T>> extends ModelFile {
|
||||
private final Map<Direction, FaceBuilder> faces = new LinkedHashMap<>();
|
||||
private RotationBuilder rotation;
|
||||
private boolean shade = true;
|
||||
private int color = 0xFFFFFFFF;
|
||||
private int blockLight = 0, skyLight = 0;
|
||||
private boolean hasAmbientOcclusion = true;
|
||||
|
||||
private void validateCoordinate(float coord, char name) {
|
||||
Preconditions.checkArgument(!(coord < -16.0F) && !(coord > 32.0F), "Position " + name + " out of range, must be within [-16, 32]. Found: %d", coord);
|
||||
@@ -499,6 +501,42 @@ public class ModelBuilder<T extends ModelBuilder<T>> extends ModelFile {
|
||||
public ElementBuilder cube(String texture) {
|
||||
return allFaces(addTexture(texture).andThen((dir, f) -> f.cullface(dir)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the block and sky light of the element (0-15).
|
||||
* Traditional "emissivity" values were set both of these to the same value.
|
||||
*
|
||||
* @param blockLight the block light
|
||||
* @param skyLight the sky light
|
||||
* @return this builder
|
||||
*/
|
||||
public ElementBuilder emissivity(int blockLight, int skyLight) {
|
||||
this.blockLight = blockLight;
|
||||
this.skyLight = skyLight;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the color of the element.
|
||||
*
|
||||
* @param color the color in ARGB format.
|
||||
* @return this builder
|
||||
*/
|
||||
public ElementBuilder color(int color) {
|
||||
this.color = color;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ambient occlusion of the element.
|
||||
*
|
||||
* @param ao the ambient occlusion
|
||||
* @return this builder
|
||||
*/
|
||||
public ElementBuilder ao(boolean ao) {
|
||||
this.hasAmbientOcclusion = ao;
|
||||
return this;
|
||||
}
|
||||
|
||||
private BiConsumer<Direction, FaceBuilder> addTexture(String texture) {
|
||||
return ($, f) -> f.texture(texture);
|
||||
@@ -507,7 +545,7 @@ public class ModelBuilder<T extends ModelBuilder<T>> extends ModelFile {
|
||||
BlockElement build() {
|
||||
Map<Direction, BlockElementFace> faces = this.faces.entrySet().stream()
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().build(), (k1, k2) -> { throw new IllegalArgumentException(); }, LinkedHashMap::new));
|
||||
return new BlockElement(from, to, faces, rotation == null ? null : rotation.build(), shade);
|
||||
return new BlockElement(from, to, faces, rotation == null ? null : rotation.build(), shade, new ForgeFaceData(this.color, this.blockLight, this.skyLight, this.hasAmbientOcclusion));
|
||||
}
|
||||
|
||||
public T end() { return self(); }
|
||||
@@ -519,7 +557,8 @@ public class ModelBuilder<T extends ModelBuilder<T>> extends ModelFile {
|
||||
private String texture = MissingTextureAtlasSprite.getLocation().toString();
|
||||
private float[] uvs;
|
||||
private FaceRotation rotation = FaceRotation.ZERO;
|
||||
private int emissivity = 0;
|
||||
private int color = 0xFFFFFFFF;
|
||||
private int blockLight = 0, skyLight = 0;
|
||||
private boolean hasAmbientOcclusion = true;
|
||||
|
||||
FaceBuilder(Direction dir) {
|
||||
@@ -568,23 +607,28 @@ public class ModelBuilder<T extends ModelBuilder<T>> extends ModelFile {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the emissivity of the face (0-15).
|
||||
* Set the block and sky light of the face (0-15).
|
||||
* Traditional "emissivity" values set both of these to the same value.
|
||||
*
|
||||
* @param emissivity the emissivity
|
||||
* @param blockLight the block light
|
||||
* @param skyLight the sky light
|
||||
* @return this builder
|
||||
*/
|
||||
public FaceBuilder emissivity(int emissivity) {
|
||||
this.emissivity = emissivity;
|
||||
public FaceBuilder emissivity(int blockLight, int skyLight) {
|
||||
this.blockLight = blockLight;
|
||||
this.skyLight = skyLight;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the face emissive (emissivity = 15).
|
||||
* Sets the color of the face.
|
||||
*
|
||||
* @param color the color in ARGB format.
|
||||
* @return this builder
|
||||
*/
|
||||
public FaceBuilder emissive() {
|
||||
return emissivity(15);
|
||||
public FaceBuilder color(int color) {
|
||||
this.color = color;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -602,7 +646,7 @@ public class ModelBuilder<T extends ModelBuilder<T>> extends ModelFile {
|
||||
if (this.texture == null) {
|
||||
throw new IllegalStateException("A model face must have a texture");
|
||||
}
|
||||
return new BlockElementFace(cullface, tintindex, texture, new BlockFaceUV(uvs, rotation.rotation), emissivity, hasAmbientOcclusion);
|
||||
return new BlockElementFace(cullface, tintindex, texture, new BlockFaceUV(uvs, rotation.rotation), new ForgeFaceData(this.color, this.blockLight, this.skyLight, this.hasAmbientOcclusion));
|
||||
}
|
||||
|
||||
public ElementBuilder end() { return ElementBuilder.this; }
|
||||
|
||||
@@ -16,7 +16,6 @@ import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import net.minecraft.data.CachedOutput;
|
||||
import net.minecraft.data.DataGenerator;
|
||||
import net.minecraft.data.DataProvider;
|
||||
import net.minecraft.data.PackOutput;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
@@ -34,7 +33,7 @@ public abstract class ModelProvider<T extends ModelBuilder<T>> implements DataPr
|
||||
protected static final ResourceType MODEL_WITH_EXTENSION = new ResourceType(PackType.CLIENT_RESOURCES, "", "models");
|
||||
|
||||
private static final Gson GSON = (new GsonBuilder()).setPrettyPrinting().create();
|
||||
protected final DataGenerator generator;
|
||||
protected final PackOutput output;
|
||||
protected final String modid;
|
||||
protected final String folder;
|
||||
protected final Function<ResourceLocation, T> factory;
|
||||
@@ -45,9 +44,9 @@ public abstract class ModelProvider<T extends ModelBuilder<T>> implements DataPr
|
||||
|
||||
protected abstract void registerModels();
|
||||
|
||||
public ModelProvider(DataGenerator generator, String modid, String folder, Function<ResourceLocation, T> factory, ExistingFileHelper existingFileHelper) {
|
||||
Preconditions.checkNotNull(generator);
|
||||
this.generator = generator;
|
||||
public ModelProvider(PackOutput output, String modid, String folder, Function<ResourceLocation, T> factory, ExistingFileHelper existingFileHelper) {
|
||||
Preconditions.checkNotNull(output);
|
||||
this.output = output;
|
||||
Preconditions.checkNotNull(modid);
|
||||
this.modid = modid;
|
||||
Preconditions.checkNotNull(folder);
|
||||
@@ -58,8 +57,8 @@ public abstract class ModelProvider<T extends ModelBuilder<T>> implements DataPr
|
||||
this.existingFileHelper = existingFileHelper;
|
||||
}
|
||||
|
||||
public ModelProvider(DataGenerator generator, String modid, String folder, BiFunction<ResourceLocation, ExistingFileHelper, T> builderFromModId, ExistingFileHelper existingFileHelper) {
|
||||
this(generator, modid, folder, loc->builderFromModId.apply(loc, existingFileHelper), existingFileHelper);
|
||||
public ModelProvider(PackOutput output, String modid, String folder, BiFunction<ResourceLocation, ExistingFileHelper, T> builderFromModId, ExistingFileHelper existingFileHelper) {
|
||||
this(output, modid, folder, loc->builderFromModId.apply(loc, existingFileHelper), existingFileHelper);
|
||||
}
|
||||
|
||||
public T getBuilder(String path) {
|
||||
@@ -408,6 +407,6 @@ public abstract class ModelProvider<T extends ModelBuilder<T>> implements DataPr
|
||||
|
||||
protected Path getPath(T model) {
|
||||
ResourceLocation loc = model.getLocation();
|
||||
return this.generator.getPackOutput().getOutputFolder(PackOutput.Target.RESOURCE_PACK).resolve(loc.getNamespace()).resolve("models").resolve(loc.getPath() + ".json");
|
||||
return this.output.getOutputFolder(PackOutput.Target.RESOURCE_PACK).resolve(loc.getNamespace()).resolve("models").resolve(loc.getPath() + ".json");
|
||||
}
|
||||
}
|
||||
+61
-14
@@ -5,30 +5,37 @@
|
||||
|
||||
package net.minecraftforge.client.model.generators.loaders;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.mojang.serialization.JsonOps;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraftforge.client.model.ForgeFaceData;
|
||||
import net.minecraftforge.client.model.generators.CustomLoaderBuilder;
|
||||
import net.minecraftforge.client.model.generators.ModelBuilder;
|
||||
import net.minecraftforge.common.data.ExistingFileHelper;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class ItemLayersModelBuilder<T extends ModelBuilder<T>> extends CustomLoaderBuilder<T>
|
||||
public class ItemLayerModelBuilder<T extends ModelBuilder<T>> extends CustomLoaderBuilder<T>
|
||||
{
|
||||
public static <T extends ModelBuilder<T>> ItemLayersModelBuilder<T> begin(T parent, ExistingFileHelper existingFileHelper)
|
||||
public static <T extends ModelBuilder<T>> ItemLayerModelBuilder<T> begin(T parent, ExistingFileHelper existingFileHelper)
|
||||
{
|
||||
return new ItemLayersModelBuilder<>(parent, existingFileHelper);
|
||||
return new ItemLayerModelBuilder<>(parent, existingFileHelper);
|
||||
}
|
||||
|
||||
private final IntSet emissiveLayers = new IntOpenHashSet();
|
||||
private final Int2ObjectMap<ForgeFaceData> faceData = new Int2ObjectOpenHashMap<>();
|
||||
private final Map<ResourceLocation, IntSet> renderTypes = new LinkedHashMap<>();
|
||||
private final IntSet layersWithRenderTypes = new IntOpenHashSet();
|
||||
|
||||
protected ItemLayersModelBuilder(T parent, ExistingFileHelper existingFileHelper)
|
||||
protected ItemLayerModelBuilder(T parent, ExistingFileHelper existingFileHelper)
|
||||
{
|
||||
super(new ResourceLocation("forge:item_layers"), parent, existingFileHelper);
|
||||
}
|
||||
@@ -36,18 +43,51 @@ public class ItemLayersModelBuilder<T extends ModelBuilder<T>> extends CustomLoa
|
||||
/**
|
||||
* Marks a set of layers to be rendered emissively.
|
||||
*
|
||||
* @param blockLight The block light (0-15)
|
||||
* @param skyLight The sky light (0-15)
|
||||
* @param layers the layers that will render unlit
|
||||
* @return this builder
|
||||
* @throws NullPointerException if {@code layers} is {@code null}
|
||||
* @throws IllegalArgumentException if {@code layers} is empty
|
||||
* @throws IllegalArgumentException if any entry in {@code layers} is smaller than 0
|
||||
*/
|
||||
public ItemLayersModelBuilder<T> emissive(int... layers)
|
||||
public ItemLayerModelBuilder<T> emissive(int blockLight, int skyLight, int... layers)
|
||||
{
|
||||
Preconditions.checkNotNull(layers, "Layers must not be null");
|
||||
Preconditions.checkArgument(layers.length > 0, "At least one layer must be specified");
|
||||
Preconditions.checkArgument(Arrays.stream(layers).allMatch(i -> i >= 0), "All layers must be >= 0");
|
||||
Arrays.stream(layers).forEach(emissiveLayers::add);
|
||||
for(int i : layers)
|
||||
{
|
||||
faceData.compute(i, (key, value) -> {
|
||||
ForgeFaceData fallback = value == null ? ForgeFaceData.DEFAULT : value;
|
||||
return new ForgeFaceData(fallback.color(), blockLight, skyLight, fallback.ambientOcclusion());
|
||||
});
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks a set of layers to be rendered with a specific color.
|
||||
*
|
||||
* @param color The color, in ARGB.
|
||||
* @param layers the layers that will render with color
|
||||
* @return this builder
|
||||
* @throws NullPointerException if {@code layers} is {@code null}
|
||||
* @throws IllegalArgumentException if {@code layers} is empty
|
||||
* @throws IllegalArgumentException if any entry in {@code layers} is smaller than 0
|
||||
*/
|
||||
public ItemLayerModelBuilder<T> color(int color, int... layers)
|
||||
{
|
||||
Preconditions.checkNotNull(layers, "Layers must not be null");
|
||||
Preconditions.checkArgument(layers.length > 0, "At least one layer must be specified");
|
||||
Preconditions.checkArgument(Arrays.stream(layers).allMatch(i -> i >= 0), "All layers must be >= 0");
|
||||
for(int i : layers)
|
||||
{
|
||||
faceData.compute(i, (key, value) -> {
|
||||
ForgeFaceData fallback = value == null ? ForgeFaceData.DEFAULT : value;
|
||||
return new ForgeFaceData(color, fallback.blockLight(), fallback.skyLight(), fallback.ambientOcclusion());
|
||||
});
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -64,7 +104,7 @@ public class ItemLayersModelBuilder<T extends ModelBuilder<T>> extends CustomLoa
|
||||
* @throws IllegalArgumentException if any entry in {@code layers} is smaller than 0
|
||||
* @throws IllegalArgumentException if any entry in {@code layers} already has a render type
|
||||
*/
|
||||
public ItemLayersModelBuilder<T> renderType(String renderType, int... layers)
|
||||
public ItemLayerModelBuilder<T> renderType(String renderType, int... layers)
|
||||
{
|
||||
Preconditions.checkNotNull(renderType, "Render type must not be null");
|
||||
ResourceLocation asLoc;
|
||||
@@ -88,7 +128,7 @@ public class ItemLayersModelBuilder<T extends ModelBuilder<T>> extends CustomLoa
|
||||
* @throws IllegalArgumentException if any entry in {@code layers} is smaller than 0
|
||||
* @throws IllegalArgumentException if any entry in {@code layers} already has a render type
|
||||
*/
|
||||
public ItemLayersModelBuilder<T> renderType(ResourceLocation renderType, int... layers)
|
||||
public ItemLayerModelBuilder<T> renderType(ResourceLocation renderType, int... layers)
|
||||
{
|
||||
Preconditions.checkNotNull(renderType, "Render type must not be null");
|
||||
Preconditions.checkNotNull(layers, "Layers must not be null");
|
||||
@@ -109,9 +149,16 @@ public class ItemLayersModelBuilder<T extends ModelBuilder<T>> extends CustomLoa
|
||||
{
|
||||
json = super.toJson(json);
|
||||
|
||||
JsonArray unlitLayers = new JsonArray();
|
||||
this.emissiveLayers.intStream().sorted().forEach(unlitLayers::add);
|
||||
json.add("emissive_layers", unlitLayers);
|
||||
JsonObject forgeData = new JsonObject();
|
||||
JsonObject layerObj = new JsonObject();
|
||||
|
||||
for(Int2ObjectMap.Entry<ForgeFaceData> entry : this.faceData.int2ObjectEntrySet())
|
||||
{
|
||||
layerObj.add(String.valueOf(entry.getIntKey()), ForgeFaceData.CODEC.encodeStart(JsonOps.INSTANCE, entry.getValue()).getOrThrow(false, s -> {}));
|
||||
}
|
||||
|
||||
forgeData.add("layers", layerObj);
|
||||
json.add("forge_data", forgeData);
|
||||
|
||||
JsonObject renderTypes = new JsonObject();
|
||||
this.renderTypes.forEach((renderType, layers) -> {
|
||||
+8
-1
@@ -41,6 +41,7 @@ public class BlockGeometryBakingContext implements IGeometryBakingContext
|
||||
private Transformation rootTransform;
|
||||
@Nullable
|
||||
private ResourceLocation renderTypeHint;
|
||||
private boolean gui3d = true;
|
||||
|
||||
@ApiStatus.Internal
|
||||
public BlockGeometryBakingContext(BlockModel owner)
|
||||
@@ -93,7 +94,7 @@ public class BlockGeometryBakingContext implements IGeometryBakingContext
|
||||
@Override
|
||||
public boolean isGui3d()
|
||||
{
|
||||
return true;
|
||||
return gui3d;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -141,12 +142,18 @@ public class BlockGeometryBakingContext implements IGeometryBakingContext
|
||||
this.renderTypeHint = renderTypeHint;
|
||||
}
|
||||
|
||||
public void setGui3d(boolean gui3d)
|
||||
{
|
||||
this.gui3d = gui3d;
|
||||
}
|
||||
|
||||
public void copyFrom(BlockGeometryBakingContext other)
|
||||
{
|
||||
this.customGeometry = other.customGeometry;
|
||||
this.rootTransform = other.rootTransform;
|
||||
this.visibilityData.copyFrom(other.visibilityData);
|
||||
this.renderTypeHint = other.renderTypeHint;
|
||||
this.gui3d = other.gui3d;
|
||||
}
|
||||
|
||||
public BakedModel bake(ModelBaker baker, Function<Material, TextureAtlasSprite> bakedTextureGetter, ModelState modelTransform, ItemOverrides overrides, ResourceLocation modelLocation)
|
||||
|
||||
@@ -7,6 +7,8 @@ package net.minecraftforge.client.model.geometry;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
import net.minecraft.client.renderer.block.model.BlockModel;
|
||||
import net.minecraft.client.renderer.block.model.ItemOverrides;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
@@ -28,6 +30,16 @@ public interface IUnbakedGeometry<T extends IUnbakedGeometry<T>>
|
||||
{
|
||||
BakedModel bake(IGeometryBakingContext context, ModelBaker baker, Function<Material, TextureAtlasSprite> spriteGetter, ModelState modelState, ItemOverrides overrides, ResourceLocation modelLocation);
|
||||
|
||||
/**
|
||||
* Resolve parents of nested {@link BlockModel}s which are later used in
|
||||
* {@link IUnbakedGeometry#bake(IGeometryBakingContext, ModelBaker, Function, ModelState, ItemOverrides, ResourceLocation)}
|
||||
* via {@link BlockModel#resolveParents(Function)}
|
||||
*/
|
||||
default void resolveParents(Function<ResourceLocation, UnbakedModel> modelGetter, IGeometryBakingContext context)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return a set of all the components whose visibility may be configured via {@link IGeometryBakingContext}}
|
||||
*/
|
||||
|
||||
@@ -27,6 +27,7 @@ import net.minecraft.client.resources.model.ModelState;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraftforge.client.model.ElementsModel;
|
||||
import net.minecraftforge.client.model.ForgeFaceData;
|
||||
import net.minecraftforge.client.model.IModelBuilder;
|
||||
import net.minecraftforge.client.model.SimpleModelState;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
@@ -127,15 +128,33 @@ public class UnbakedGeometryHelper
|
||||
return elementsModel.bake(blockModel.customData, modelBaker, spriteGetter, modelState, blockModel.getOverrides(modelBaker, owner, spriteGetter), modelLocation);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #createUnbakedItemElements(int, SpriteContents, ForgeFaceData)
|
||||
*/
|
||||
public static List<BlockElement> createUnbakedItemElements(int layerIndex, SpriteContents spriteContents)
|
||||
{
|
||||
return createUnbakedItemElements(layerIndex, spriteContents, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a list of {@linkplain BlockElement block elements} in the shape of the specified sprite contents.
|
||||
* These can later be baked using the same, or another texture.
|
||||
* <p>
|
||||
* The {@link Direction#NORTH} and {@link Direction#SOUTH} faces take up the whole surface.
|
||||
*/
|
||||
public static List<BlockElement> createUnbakedItemElements(int layerIndex, SpriteContents spriteContents)
|
||||
public static List<BlockElement> createUnbakedItemElements(int layerIndex, SpriteContents spriteContents, @Nullable ForgeFaceData faceData)
|
||||
{
|
||||
return ITEM_MODEL_GENERATOR.processFrames(layerIndex, "layer" + layerIndex, spriteContents);
|
||||
var elements = ITEM_MODEL_GENERATOR.processFrames(layerIndex, "layer" + layerIndex, spriteContents);
|
||||
elements.forEach(element -> element.faceData = faceData);
|
||||
return elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #createUnbakedItemMaskElements(int, SpriteContents, ForgeFaceData)
|
||||
*/
|
||||
public static List<BlockElement> createUnbakedItemMaskElements(int layerIndex, SpriteContents spriteContents)
|
||||
{
|
||||
return createUnbakedItemMaskElements(layerIndex, spriteContents, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -144,9 +163,9 @@ public class UnbakedGeometryHelper
|
||||
* <p>
|
||||
* The {@link Direction#NORTH} and {@link Direction#SOUTH} faces take up only the pixels the texture uses.
|
||||
*/
|
||||
public static List<BlockElement> createUnbakedItemMaskElements(int layerIndex, SpriteContents spriteContents)
|
||||
public static List<BlockElement> createUnbakedItemMaskElements(int layerIndex, SpriteContents spriteContents, @Nullable ForgeFaceData faceData)
|
||||
{
|
||||
var elements = createUnbakedItemElements(layerIndex, spriteContents);
|
||||
var elements = createUnbakedItemElements(layerIndex, spriteContents, faceData);
|
||||
elements.remove(0); // Remove north and south faces
|
||||
|
||||
int width = spriteContents.width(), height = spriteContents.height();
|
||||
|
||||
@@ -31,8 +31,8 @@ public class ObjLoader implements IGeometryLoader<ObjModel>, ResourceManagerRelo
|
||||
{
|
||||
public static ObjLoader INSTANCE = new ObjLoader();
|
||||
|
||||
private final Map<ObjModel.ModelSettings, ObjModel> modelCache = Maps.newHashMap();
|
||||
private final Map<ResourceLocation, ObjMaterialLibrary> materialCache = Maps.newHashMap();
|
||||
private final Map<ObjModel.ModelSettings, ObjModel> modelCache = Maps.newConcurrentMap();
|
||||
private final Map<ResourceLocation, ObjMaterialLibrary> materialCache = Maps.newConcurrentMap();
|
||||
|
||||
private ResourceManager manager = Minecraft.getInstance().getResourceManager();
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Function;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
@@ -46,6 +47,8 @@ import net.minecraft.commands.SharedSuggestionProvider;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.HolderLookup;
|
||||
import net.minecraft.core.HolderSet;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.network.chat.ChatDecorator;
|
||||
import net.minecraft.network.chat.ClickEvent;
|
||||
@@ -55,6 +58,7 @@ import net.minecraft.network.chat.TextColor;
|
||||
import net.minecraft.network.chat.contents.LiteralContents;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.packs.PackType;
|
||||
import net.minecraft.server.packs.metadata.pack.PackMetadataSection;
|
||||
import net.minecraft.tags.BlockTags;
|
||||
import net.minecraft.tags.TagEntry;
|
||||
import net.minecraft.tags.TagKey;
|
||||
@@ -1520,7 +1524,7 @@ public class ForgeHooks
|
||||
|
||||
for (PackType packType : PackType.values())
|
||||
{
|
||||
String key = "forge:" + packType.bridgeType.name().toLowerCase(Locale.ROOT) + "_pack_format";
|
||||
String key = makePackFormatKey(packType);
|
||||
if (json.has(key))
|
||||
{
|
||||
map.put(packType, GsonHelper.getAsInt(json, key));
|
||||
@@ -1530,6 +1534,24 @@ public class ForgeHooks
|
||||
return map.buildOrThrow();
|
||||
}
|
||||
|
||||
public static void writeTypedPackFormats(JsonObject json, PackMetadataSection section)
|
||||
{
|
||||
int packFormat = section.getPackFormat();
|
||||
for (PackType packType : PackType.values())
|
||||
{
|
||||
int format = section.getPackFormat(packType);
|
||||
if (format != packFormat)
|
||||
{
|
||||
json.addProperty(makePackFormatKey(packType), format);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String makePackFormatKey(PackType packType)
|
||||
{
|
||||
return "forge:" + packType.bridgeType.name().toLowerCase(Locale.ROOT) + "_pack_format";
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* This method is used to prefix the path, where elements of the associated registry are stored, with their namespace, if it is not minecraft
|
||||
@@ -1567,4 +1589,15 @@ public class ForgeHooks
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public static <T> HolderLookup.RegistryLookup<T> wrapRegistryLookup(final HolderLookup.RegistryLookup<T> lookup)
|
||||
{
|
||||
return new HolderLookup.RegistryLookup.Delegate<>()
|
||||
{
|
||||
@Override protected RegistryLookup<T> parent() { return lookup; }
|
||||
@Override public Stream<HolderSet.Named<T>> listTags() { return Stream.empty(); }
|
||||
@Override public Optional<HolderSet.Named<T>> get(TagKey<T> key) { return Optional.of(HolderSet.emptyNamed(lookup, key)); }
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,7 @@ import net.minecraftforge.common.crafting.IntersectionIngredient;
|
||||
import net.minecraftforge.common.data.ExistingFileHelper;
|
||||
import net.minecraftforge.common.data.ForgeBiomeTagsProvider;
|
||||
import net.minecraftforge.common.data.ForgeFluidTagsProvider;
|
||||
import net.minecraftforge.common.data.ForgeSpriteSourceProvider;
|
||||
import net.minecraftforge.common.extensions.IForgeEntity;
|
||||
import net.minecraftforge.common.extensions.IForgePlayer;
|
||||
import net.minecraftforge.common.loot.CanToolPerformAction;
|
||||
@@ -488,6 +489,8 @@ public class ForgeMod
|
||||
gen.addProvider(event.includeServer(), new ForgeRecipeProvider(packOutput));
|
||||
gen.addProvider(event.includeServer(), new ForgeLootTableProvider(packOutput));
|
||||
gen.addProvider(event.includeServer(), new ForgeBiomeTagsProvider(packOutput, lookupProvider, existingFileHelper));
|
||||
|
||||
gen.addProvider(event.includeClient(), new ForgeSpriteSourceProvider(packOutput, existingFileHelper));
|
||||
}
|
||||
|
||||
public void missingSoundMapping(MissingMappingsEvent event)
|
||||
|
||||
@@ -1,28 +1,70 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.common.data;
|
||||
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.core.HolderLookup;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
import net.minecraft.core.RegistrySetBuilder;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.data.PackOutput;
|
||||
import net.minecraft.data.registries.RegistriesDatapackGenerator;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Supplier;
|
||||
import net.minecraftforge.registries.DataPackRegistriesHooks;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* An extension of the {@link RegistriesDatapackGenerator} which properly handles
|
||||
* referencing existing dynamic registry objects within another dynamic registry
|
||||
* object.
|
||||
*/
|
||||
public class DatapackBuiltinEntriesProvider extends RegistriesDatapackGenerator
|
||||
{
|
||||
public DatapackBuiltinEntriesProvider(PackOutput output, Supplier<HolderLookup.Provider> registriesSupplier)
|
||||
|
||||
/**
|
||||
* Constructs a new datapack provider which generates all registry objects
|
||||
* from the provided mods using the holder.
|
||||
*
|
||||
* @param output the target directory of the data generator
|
||||
* @param registries a future of a lookup for registries and their objects
|
||||
* @param modIds a set of mod ids to generate the dynamic registry objects of
|
||||
*/
|
||||
public DatapackBuiltinEntriesProvider(PackOutput output, CompletableFuture<HolderLookup.Provider> registries, Set<String> modIds)
|
||||
{
|
||||
super(output, CompletableFuture.supplyAsync(registriesSupplier, Util.backgroundExecutor()));
|
||||
super(output, registries, modIds);
|
||||
}
|
||||
|
||||
public DatapackBuiltinEntriesProvider(PackOutput output, RegistrySetBuilder datapackEntriesBuilder)
|
||||
/**
|
||||
* Constructs a new datapack provider which generates all registry objects
|
||||
* from the provided mods using the holder. All entries that need to be
|
||||
* bootstrapped are provided within the {@link RegistrySetBuilder}.
|
||||
*
|
||||
* @param output the target directory of the data generator
|
||||
* @param registries a future of a lookup for registries and their objects
|
||||
* @param datapackEntriesBuilder a builder containing the dynamic registry objects added by this provider
|
||||
* @param modIds a set of mod ids to generate the dynamic registry objects of
|
||||
*/
|
||||
public DatapackBuiltinEntriesProvider(PackOutput output, CompletableFuture<HolderLookup.Provider> registries, RegistrySetBuilder datapackEntriesBuilder, Set<String> modIds)
|
||||
{
|
||||
this(output, () -> createLookup(datapackEntriesBuilder));
|
||||
this(output, registries.thenApply(r -> constructRegistries(r, datapackEntriesBuilder)), modIds);
|
||||
}
|
||||
|
||||
public static HolderLookup.Provider createLookup(RegistrySetBuilder datapackEntriesBuilder) {
|
||||
return datapackEntriesBuilder.build(RegistryAccess.fromRegistryOfRegistries(BuiltInRegistries.REGISTRY));
|
||||
/**
|
||||
* A method used to construct empty bootstraps for all registries this provider
|
||||
* did not touch such that existing dynamic registry objects do not get inlined.
|
||||
*
|
||||
* @param original a future of a lookup for registries and their objects
|
||||
* @param datapackEntriesBuilder a builder containing the dynamic registry objects added by this provider
|
||||
* @return a new lookup containing the existing and to be generated registries and their objects
|
||||
*/
|
||||
private static HolderLookup.Provider constructRegistries(HolderLookup.Provider original, RegistrySetBuilder datapackEntriesBuilder)
|
||||
{
|
||||
var builderKeys = new HashSet<>(datapackEntriesBuilder.getEntryKeys());
|
||||
DataPackRegistriesHooks.getDataPackRegistriesWithDimensions().filter(data -> !builderKeys.contains(data.key())).forEach(data -> datapackEntriesBuilder.add(data.key(), context -> {}));
|
||||
return datapackEntriesBuilder.buildPatch(RegistryAccess.fromRegistryOfRegistries(BuiltInRegistries.REGISTRY), original);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.common.data;
|
||||
|
||||
import net.minecraft.advancements.Advancement;
|
||||
import net.minecraft.core.HolderLookup;
|
||||
import net.minecraft.data.PackOutput;
|
||||
import net.minecraft.data.advancements.AdvancementProvider;
|
||||
import net.minecraft.data.advancements.AdvancementSubProvider;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* An extension of the {@link AdvancementProvider} to provide a feature-complete
|
||||
* experience to generate modded advancements.
|
||||
*/
|
||||
public class ForgeAdvancementProvider extends AdvancementProvider
|
||||
{
|
||||
/**
|
||||
* Constructs an advancement provider using the generators to write the
|
||||
* advancements to a file.
|
||||
*
|
||||
* @param output the target directory of the data generator
|
||||
* @param registries a future of a lookup for registries and their objects
|
||||
* @param existingFileHelper a helper used to find whether a file exists
|
||||
* @param subProviders the generators used to create the advancements
|
||||
*/
|
||||
public ForgeAdvancementProvider(PackOutput output, CompletableFuture<HolderLookup.Provider> registries, ExistingFileHelper existingFileHelper, List<AdvancementGenerator> subProviders)
|
||||
{
|
||||
super(output, registries, subProviders.stream().map(generator -> generator.toSubProvider(existingFileHelper)).toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* An interface used to generated modded advancements. This is parallel to
|
||||
* vanilla's {@link AdvancementSubProvider} with access to the {@link ExistingFileHelper}.
|
||||
*
|
||||
* @see AdvancementSubProvider
|
||||
*/
|
||||
public interface AdvancementGenerator
|
||||
{
|
||||
/**
|
||||
* A method used to generate advancements for a mod. Advancements should be
|
||||
* built via {@link net.minecraftforge.common.extensions.IForgeAdvancementBuilder#save(Consumer, ResourceLocation, ExistingFileHelper)}.
|
||||
*
|
||||
* @param registries a lookup for registries and their objects
|
||||
* @param saver a consumer used to write advancements to a file
|
||||
* @param existingFileHelper a helper used to find whether a file exists
|
||||
*/
|
||||
void generate(HolderLookup.Provider registries, Consumer<Advancement> saver, ExistingFileHelper existingFileHelper);
|
||||
|
||||
/**
|
||||
* Creates an {@link AdvancementSubProvider} from this generator.
|
||||
*
|
||||
* @param existingFileHelper a helper used to find whether a file exists
|
||||
* @return a sub provider wrapping this generator
|
||||
*/
|
||||
default AdvancementSubProvider toSubProvider(ExistingFileHelper existingFileHelper)
|
||||
{
|
||||
return (registries, saver) -> this.generate(registries, saver, existingFileHelper);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.common.data;
|
||||
|
||||
import net.minecraft.client.renderer.texture.atlas.sources.SingleFile;
|
||||
import net.minecraft.data.PackOutput;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class ForgeSpriteSourceProvider extends SpriteSourceProvider
|
||||
{
|
||||
public ForgeSpriteSourceProvider(PackOutput output, ExistingFileHelper fileHelper)
|
||||
{
|
||||
super(output, fileHelper, "forge");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addSources()
|
||||
{
|
||||
atlas(SpriteSourceProvider.BLOCKS_ATLAS).addSource(new SingleFile(new ResourceLocation("forge:white"), Optional.empty()));
|
||||
}
|
||||
}
|
||||
@@ -13,14 +13,12 @@ import com.google.gson.JsonObject;
|
||||
import com.mojang.serialization.JsonOps;
|
||||
import cpw.mods.modlauncher.api.LamdbaExceptionUtils;
|
||||
import net.minecraft.data.CachedOutput;
|
||||
import net.minecraft.data.DataGenerator;
|
||||
import net.minecraft.data.DataProvider;
|
||||
import net.minecraft.data.PackOutput;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraftforge.common.loot.IGlobalLootModifier;
|
||||
import net.minecraftforge.common.loot.LootModifier;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
@@ -37,14 +35,14 @@ import java.util.stream.Collectors;
|
||||
public abstract class GlobalLootModifierProvider implements DataProvider
|
||||
{
|
||||
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
|
||||
private final DataGenerator gen;
|
||||
private final PackOutput output;
|
||||
private final String modid;
|
||||
private final Map<String, JsonElement> toSerialize = new HashMap<>();
|
||||
private boolean replace = false;
|
||||
|
||||
public GlobalLootModifierProvider(DataGenerator gen, String modid)
|
||||
public GlobalLootModifierProvider(PackOutput output, String modid)
|
||||
{
|
||||
this.gen = gen;
|
||||
this.output = output;
|
||||
this.modid = modid;
|
||||
}
|
||||
|
||||
@@ -66,8 +64,8 @@ public abstract class GlobalLootModifierProvider implements DataProvider
|
||||
{
|
||||
start();
|
||||
|
||||
Path forgePath = this.gen.getPackOutput().getOutputFolder(PackOutput.Target.DATA_PACK).resolve("forge").resolve("loot_modifiers").resolve("global_loot_modifiers.json");
|
||||
Path modifierFolderPath = this.gen.getPackOutput().getOutputFolder(PackOutput.Target.DATA_PACK).resolve(this.modid).resolve("loot_modifiers");
|
||||
Path forgePath = this.output.getOutputFolder(PackOutput.Target.DATA_PACK).resolve("forge").resolve("loot_modifiers").resolve("global_loot_modifiers.json");
|
||||
Path modifierFolderPath = this.output.getOutputFolder(PackOutput.Target.DATA_PACK).resolve(this.modid).resolve("loot_modifiers");
|
||||
List<ResourceLocation> entries = new ArrayList<>();
|
||||
|
||||
ImmutableList.Builder<CompletableFuture<?>> futuresBuilder = new ImmutableList.Builder<>();
|
||||
|
||||
@@ -32,7 +32,6 @@ import net.minecraft.server.packs.PackType;
|
||||
import net.minecraftforge.common.crafting.CraftingHelper;
|
||||
import net.minecraftforge.common.crafting.conditions.ICondition;
|
||||
import net.minecraftforge.common.data.ExistingFileHelper.ResourceType;
|
||||
import net.minecraftforge.data.event.GatherDataEvent;
|
||||
import net.minecraftforge.registries.DataPackRegistriesHooks;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
@@ -48,7 +47,7 @@ import org.slf4j.Logger;
|
||||
public class JsonCodecProvider<T> implements DataProvider
|
||||
{
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
protected final DataGenerator dataGenerator;
|
||||
protected final PackOutput output;
|
||||
protected final ExistingFileHelper existingFileHelper;
|
||||
protected final String modid;
|
||||
protected final DynamicOps<JsonElement> dynamicOps;
|
||||
@@ -59,14 +58,14 @@ public class JsonCodecProvider<T> implements DataProvider
|
||||
protected Map<ResourceLocation, ICondition[]> conditions = Collections.emptyMap();
|
||||
|
||||
/**
|
||||
* @param dataGenerator DataGenerator provided by {@link GatherDataEvent}.
|
||||
* @param output {@linkplain PackOutput} provided by the {@link DataGenerator}.
|
||||
* @param dynamicOps DynamicOps to encode values to jsons with using the provided Codec, e.g. {@link JsonOps#INSTANCE}.
|
||||
* @param packType PackType specifying whether to generate entries in assets or data.
|
||||
* @param directory String representing the directory to generate jsons in, e.g. "dimension" or "cheesemod/cheese".
|
||||
* @param codec Codec to encode values to jsons with using the provided DynamicOps.
|
||||
* @param entries Map of named entries to serialize to jsons. Paths for values are derived from the ResourceLocation's entryid:entrypath as specified above.
|
||||
*/
|
||||
public JsonCodecProvider(DataGenerator dataGenerator, ExistingFileHelper existingFileHelper, String modid, DynamicOps<JsonElement> dynamicOps, PackType packType,
|
||||
public JsonCodecProvider(PackOutput output, ExistingFileHelper existingFileHelper, String modid, DynamicOps<JsonElement> dynamicOps, PackType packType,
|
||||
String directory, Codec<T> codec, Map<ResourceLocation, T> entries)
|
||||
{
|
||||
// Track generated data so other dataproviders can validate if needed.
|
||||
@@ -75,7 +74,7 @@ public class JsonCodecProvider<T> implements DataProvider
|
||||
{
|
||||
existingFileHelper.trackGenerated(id, resourceType);
|
||||
}
|
||||
this.dataGenerator = dataGenerator;
|
||||
this.output = output;
|
||||
this.existingFileHelper = existingFileHelper;
|
||||
this.modid = modid;
|
||||
this.dynamicOps = dynamicOps;
|
||||
@@ -85,38 +84,10 @@ public class JsonCodecProvider<T> implements DataProvider
|
||||
this.entries = entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return DatapackRegistryProvider that encodes using the registered loading codec for the provided registry key}
|
||||
* Ensures the correct directory and codec are used.
|
||||
* Only vanilla datapack registries enumerated in {@link RegistryAccess} and custom forge datapack registries can
|
||||
* be generated this way.
|
||||
*
|
||||
* @param <T> Registry element type, e.g. Biome
|
||||
* @param dataGenerator DataGenerator provided by {@link GatherDataEvent}.
|
||||
* @param modid namespace of the mod adding this DataProvider, for logging purposes.
|
||||
* @param registryOps RegistryOps to encode values to json with.
|
||||
* @param registryKey ResourceKey identifying the registry and its directory.
|
||||
* @param entries Map of entries to encode and their ResourceLocations. Paths for values are derived from the ResourceLocation's entryid:entrypath.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> JsonCodecProvider<T> forDatapackRegistry(DataGenerator dataGenerator, ExistingFileHelper existingFileHelper, String modid,
|
||||
RegistryOps<JsonElement> registryOps, ResourceKey<Registry<T>> registryKey, Map<ResourceLocation, T> entries)
|
||||
{
|
||||
final ResourceLocation registryId = registryKey.location();
|
||||
// Minecraft datapack registry folders are in data/json-namespace/registry-name/
|
||||
// Non-vanilla registry folders are data/json-namespace/registry-namespace/registry-name/
|
||||
final String registryFolder = registryId.getNamespace().equals("minecraft")
|
||||
? registryId.getPath()
|
||||
: registryId.getNamespace() + "/" + registryId.getPath();
|
||||
RegistryDataLoader.RegistryData<?> registryData = DataPackRegistriesHooks.getDataPackRegistries().stream().filter(data -> data.key() == registryKey).findAny().orElseThrow();
|
||||
final Codec<T> codec = (Codec<T>) registryData.elementCodec();
|
||||
return new JsonCodecProvider<>(dataGenerator, existingFileHelper, modid, registryOps, PackType.SERVER_DATA, registryFolder, codec, entries);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<?> run(final CachedOutput cache)
|
||||
{
|
||||
final Path outputFolder = this.dataGenerator.getPackOutput().getOutputFolder(this.packType == PackType.CLIENT_RESOURCES
|
||||
final Path outputFolder = this.output.getOutputFolder(this.packType == PackType.CLIENT_RESOURCES
|
||||
? PackOutput.Target.RESOURCE_PACK
|
||||
: PackOutput.Target.DATA_PACK);
|
||||
ImmutableList.Builder<CompletableFuture<?>> futuresBuilder = new ImmutableList.Builder<>();
|
||||
@@ -165,4 +136,4 @@ public class JsonCodecProvider<T> implements DataProvider
|
||||
this.conditions = conditions;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,6 @@ import java.util.TreeMap;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Supplier;
|
||||
import net.minecraft.data.CachedOutput;
|
||||
import net.minecraft.data.DataGenerator;
|
||||
import net.minecraft.data.DataProvider;
|
||||
import net.minecraft.data.PackOutput;
|
||||
import net.minecraft.world.effect.MobEffect;
|
||||
@@ -24,12 +23,12 @@ import net.minecraft.world.level.block.Block;
|
||||
|
||||
public abstract class LanguageProvider implements DataProvider {
|
||||
private final Map<String, String> data = new TreeMap<>();
|
||||
private final DataGenerator gen;
|
||||
private final PackOutput output;
|
||||
private final String modid;
|
||||
private final String locale;
|
||||
|
||||
public LanguageProvider(DataGenerator gen, String modid, String locale) {
|
||||
this.gen = gen;
|
||||
public LanguageProvider(PackOutput output, String modid, String locale) {
|
||||
this.output = output;
|
||||
this.modid = modid;
|
||||
this.locale = locale;
|
||||
}
|
||||
@@ -41,7 +40,7 @@ public abstract class LanguageProvider implements DataProvider {
|
||||
addTranslations();
|
||||
|
||||
if (!data.isEmpty())
|
||||
return save(cache, this.gen.getPackOutput().getOutputFolder(PackOutput.Target.RESOURCE_PACK).resolve(this.modid).resolve("lang").resolve(this.locale + ".json"));
|
||||
return save(cache, this.output.getOutputFolder(PackOutput.Target.RESOURCE_PACK).resolve(this.modid).resolve("lang").resolve(this.locale + ".json"));
|
||||
|
||||
return CompletableFuture.allOf();
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Supplier;
|
||||
import net.minecraft.data.CachedOutput;
|
||||
import net.minecraft.data.DataGenerator;
|
||||
import net.minecraft.data.DataProvider;
|
||||
import net.minecraft.data.PackOutput;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
@@ -30,7 +29,7 @@ import org.apache.logging.log4j.Logger;
|
||||
public abstract class SoundDefinitionsProvider implements DataProvider
|
||||
{
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
private final DataGenerator generator;
|
||||
private final PackOutput output;
|
||||
private final String modId;
|
||||
private final ExistingFileHelper helper;
|
||||
|
||||
@@ -39,13 +38,13 @@ public abstract class SoundDefinitionsProvider implements DataProvider
|
||||
/**
|
||||
* Creates a new instance of this data provider.
|
||||
*
|
||||
* @param generator The data generator instance provided by the event you are initializing this provider in.
|
||||
* @param output The {@linkplain PackOutput} instance provided by the data generator.
|
||||
* @param modId The mod ID of the current mod.
|
||||
* @param helper The existing file helper provided by the event you are initializing this provider in.
|
||||
*/
|
||||
protected SoundDefinitionsProvider(final DataGenerator generator, final String modId, final ExistingFileHelper helper)
|
||||
protected SoundDefinitionsProvider(final PackOutput output, final String modId, final ExistingFileHelper helper)
|
||||
{
|
||||
this.generator = generator;
|
||||
this.output = output;
|
||||
this.modId = modId;
|
||||
this.helper = helper;
|
||||
}
|
||||
@@ -63,7 +62,7 @@ public abstract class SoundDefinitionsProvider implements DataProvider
|
||||
this.validate();
|
||||
if (!this.sounds.isEmpty())
|
||||
{
|
||||
return this.save(cache, this.generator.getPackOutput().getOutputFolder(PackOutput.Target.RESOURCE_PACK).resolve(this.modId).resolve("sounds.json"));
|
||||
return this.save(cache, this.output.getOutputFolder(PackOutput.Target.RESOURCE_PACK).resolve(this.modId).resolve("sounds.json"));
|
||||
}
|
||||
|
||||
return CompletableFuture.allOf();
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.common.data;
|
||||
|
||||
import com.mojang.serialization.JsonOps;
|
||||
import net.minecraft.client.renderer.texture.atlas.SpriteSource;
|
||||
import net.minecraft.client.renderer.texture.atlas.SpriteSources;
|
||||
import net.minecraft.data.PackOutput;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.packs.PackType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
/**
|
||||
* <p>Data provider for atlas configuration files.<br>
|
||||
* An atlas configuration is bound to a specific texture atlas such as the {@code minecraft:blocks} atlas and
|
||||
* allows adding additional textures to the atlas by adding {@link SpriteSource}s to the configuration.</p>
|
||||
* <p>See {@link SpriteSources} for the available sources and the constants in this class for the
|
||||
* atlases used in vanilla Minecraft</p>
|
||||
*/
|
||||
public abstract class SpriteSourceProvider extends JsonCodecProvider<List<SpriteSource>>
|
||||
{
|
||||
protected static final ResourceLocation BLOCKS_ATLAS = new ResourceLocation("blocks");
|
||||
protected static final ResourceLocation BANNER_PATTERNS_ATLAS = new ResourceLocation("banner_patterns");
|
||||
protected static final ResourceLocation BEDS_ATLAS = new ResourceLocation("beds");
|
||||
protected static final ResourceLocation CHESTS_ATLAS = new ResourceLocation("chests");
|
||||
protected static final ResourceLocation SHIELD_PATTERNS_ATLAS = new ResourceLocation("shield_patterns");
|
||||
protected static final ResourceLocation SHULKER_BOXES_ATLAS = new ResourceLocation("shulker_boxes");
|
||||
protected static final ResourceLocation SIGNS_ATLAS = new ResourceLocation("signs");
|
||||
protected static final ResourceLocation MOB_EFFECTS_ATLAS = new ResourceLocation("mob_effects");
|
||||
protected static final ResourceLocation PAINTINGS_ATLAS = new ResourceLocation("paintings");
|
||||
protected static final ResourceLocation PARTICLES_ATLAS = new ResourceLocation("particles");
|
||||
|
||||
private final Map<ResourceLocation, SourceList> atlases = new HashMap<>();
|
||||
|
||||
public SpriteSourceProvider(PackOutput output, ExistingFileHelper fileHelper, String modid)
|
||||
{
|
||||
super(output, fileHelper, modid, JsonOps.INSTANCE, PackType.CLIENT_RESOURCES, "atlases", SpriteSources.FILE_CODEC, Map.of());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void gather(BiConsumer<ResourceLocation, List<SpriteSource>> consumer)
|
||||
{
|
||||
addSources();
|
||||
atlases.forEach((atlas, srcList) -> consumer.accept(atlas, srcList.sources));
|
||||
}
|
||||
|
||||
protected abstract void addSources();
|
||||
|
||||
/**
|
||||
* Get or create a {@link SourceList} for the given atlas
|
||||
* @param atlas The texture atlas the sources should be added to, see constants at the top for the format
|
||||
* and the vanilla atlases
|
||||
* @return an existing {@code SourceList} for the given atlas or a new one if not present yet
|
||||
*/
|
||||
protected final SourceList atlas(ResourceLocation atlas)
|
||||
{
|
||||
return atlases.computeIfAbsent(atlas, $ -> new SourceList());
|
||||
}
|
||||
|
||||
protected static final class SourceList
|
||||
{
|
||||
private final List<SpriteSource> sources = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Add the given {@link SpriteSource} to this atlas configuration
|
||||
* @param source The {@code SpriteSource} to be added
|
||||
*/
|
||||
public SourceList addSource(SpriteSource source)
|
||||
{
|
||||
sources.add(source);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,12 @@
|
||||
|
||||
package net.minecraftforge.common.extensions;
|
||||
|
||||
import java.util.Collection;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import net.minecraft.server.packs.PackResources;
|
||||
import net.minecraft.server.packs.PackType;
|
||||
import net.minecraft.server.packs.resources.ResourceManager;
|
||||
import net.minecraftforge.resource.DelegatingPackResources;
|
||||
|
||||
public interface IForgePackResources
|
||||
{
|
||||
@@ -17,7 +22,13 @@ public interface IForgePackResources
|
||||
return false;
|
||||
}
|
||||
|
||||
default void initForNamespace(final String nameSpace) {}
|
||||
|
||||
default void init(final PackType packType) {}
|
||||
/**
|
||||
* Gets a collection of {@code PackResource} instances nested inside this pack.
|
||||
* Used to merge several packs into one entry in the resource pack selection UI without
|
||||
* losing the ability for each pack to return a resource in
|
||||
* {@link ResourceManager#getResourceStack(ResourceLocation)}
|
||||
* @return Collection of nested {@code PackResource}, or null if this pack has no children
|
||||
* @see DelegatingPackResources
|
||||
*/
|
||||
@Nullable default Collection<PackResources> getChildren() { return null; }
|
||||
}
|
||||
@@ -0,0 +1,317 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.common.util;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import it.unimi.dsi.fastutil.Hash.Strategy;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap;
|
||||
|
||||
/**
|
||||
* A mutable linked map with a hashing strategy and a merge function.
|
||||
*
|
||||
* @param <K> the type of keys
|
||||
* @param <V> the type of mapped values
|
||||
*/
|
||||
public class MutableHashedLinkedMap<K, V> implements Iterable<Map.Entry<K, V>>
|
||||
{
|
||||
private final Strategy<? super K> strategy;
|
||||
private final Map<K, Entry> entries;
|
||||
private final MergeFunction<K, V> merge;
|
||||
private Entry head = null;
|
||||
private Entry last = null;
|
||||
|
||||
/**
|
||||
* Creates a mutable linked map with a default new-value-selecting merge function.
|
||||
*
|
||||
* @param strategy the hashing strategy
|
||||
*/
|
||||
public MutableHashedLinkedMap(Strategy<? super K> strategy)
|
||||
{
|
||||
this(strategy, (k, v1, v2) -> v2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a mutable linked map with a custom merge function.
|
||||
*
|
||||
* @param strategy the hashing strategy
|
||||
* @param merge the function used when merging an existing value and a new value
|
||||
*/
|
||||
public MutableHashedLinkedMap(Strategy<? super K> strategy, MergeFunction<K, V> merge)
|
||||
{
|
||||
this.strategy = strategy;
|
||||
this.entries = new Object2ObjectOpenCustomHashMap<>(strategy);
|
||||
this.merge = merge;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts the mapping with the specified key and value pair.
|
||||
*
|
||||
* <p>If there is a mapping already associated with this key, then the previous value and the specified (new) value
|
||||
* are merged according to this collection's merge function, and the position of the entry is not modified. If there
|
||||
* is no such mapping, then the key-value mapping is inserted at the end of this collection.</p>
|
||||
*
|
||||
* @param key key to be inserted
|
||||
* @param value (new) value to be associated with the key
|
||||
* @return the previous value associated with the specified key, or {@code null} if there was no mapping for the key
|
||||
*/
|
||||
@Nullable
|
||||
public V put(K key, V value)
|
||||
{
|
||||
var old = entries.get(key);
|
||||
if (old != null)
|
||||
{
|
||||
V ret = old.value;
|
||||
old.value = merge.apply(key, ret, value);
|
||||
return ret;
|
||||
}
|
||||
|
||||
var self = new Entry(key, value);
|
||||
|
||||
var l = last;
|
||||
self.previous = l;
|
||||
if (l == null)
|
||||
head = self;
|
||||
else
|
||||
l.next = self;
|
||||
last = self;
|
||||
|
||||
entries.put(key, self);
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean contains(K key) { return this.entries.containsKey(key); }
|
||||
public boolean isEmpty() { return this.entries.isEmpty(); }
|
||||
|
||||
@Nullable
|
||||
public V remove(K key)
|
||||
{
|
||||
var ret = this.entries.remove(key);
|
||||
if (ret == null)
|
||||
return null;
|
||||
|
||||
remove(ret);
|
||||
return ret.getValue();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public V get(K key)
|
||||
{
|
||||
var entry = entries.get(key);
|
||||
return entry == null ? null : entry.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Map.Entry<K, V>> iterator()
|
||||
{
|
||||
return new Iterator<>()
|
||||
{
|
||||
private Entry current = head;
|
||||
@Override public boolean hasNext() { return current != null; }
|
||||
@Override
|
||||
public Map.Entry<K, V> next()
|
||||
{
|
||||
if (!hasNext())
|
||||
throw new NoSuchElementException();
|
||||
var ret = current;
|
||||
current = current.next;
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts the mapping with the specified key and value pair at the beginning of this map.
|
||||
*
|
||||
* <p>If there is a mapping already associated with this key, then the previous value and the specified (new) value
|
||||
* are first merged according to this map's merge function, then the entry is moved to the beginning of the map.</p>
|
||||
*
|
||||
* @param key key to be inserted at the beginning
|
||||
* @param value (new) value to be associated with the key
|
||||
* @return the previous value associated with the specified key, or {@code null} if there was no mapping for the key
|
||||
*
|
||||
* @see #putBefore(Object, Object, Object)
|
||||
*/
|
||||
@Nullable
|
||||
public V putFirst(K key, V value)
|
||||
{
|
||||
if (head != null)
|
||||
return putBefore(head.getKey(), key, value);
|
||||
return put(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts the mapping with this key and value pair immediately after the entry with the specified positioning key.
|
||||
*
|
||||
* <p>If the specified positioning key is not present within this map, then this method behaves like
|
||||
* {@link #put(Object, Object)}. If there is a mapping already associated with this key, then the previous value
|
||||
* and the specified (new) value are first merged according to this map's merge function, then the entry is
|
||||
* moved to directly after the entry with the specified positioning key.</p>
|
||||
*
|
||||
* @param after the key to position this new entry afterwards
|
||||
* @param key key to be inserted at the beginning
|
||||
* @param value (new) value to be associated with the key
|
||||
* @return the previous value associated with the specified key, or {@code null} if there was no mapping for the key
|
||||
*
|
||||
* @see #putBefore(Object, Object, Object)
|
||||
*/
|
||||
@Nullable
|
||||
public V putAfter(K after, K key, V value)
|
||||
{
|
||||
var target = entries.get(after);
|
||||
if (target == null)
|
||||
return put(key, value);
|
||||
|
||||
V ret = null;
|
||||
var entry = entries.get(key);
|
||||
if (entry != null)
|
||||
{
|
||||
ret = entry.value;
|
||||
entry.value = merge.apply(key, ret, value);
|
||||
remove(entry);
|
||||
}
|
||||
else
|
||||
{
|
||||
entry = new Entry(key, value);
|
||||
entries.put(key, entry);
|
||||
}
|
||||
|
||||
entry.previous = target;
|
||||
if (target.next == null)
|
||||
last = target;
|
||||
else
|
||||
target.next.previous = entry;
|
||||
entry.next = target.next;
|
||||
|
||||
target.next = entry;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts the mapping with this key and value pair immediately before the entry with the specified positioning key.
|
||||
*
|
||||
* <p>If the specified positioning key is not present within this map, then this method behaves like
|
||||
* {@link #put(Object, Object)}. If there is a mapping already associated with this key, then the previous value
|
||||
* and the specified (new) value are first merged according to this map's merge function, then the entry is
|
||||
* moved to directly before the entry with the specified positioning key.</p>
|
||||
*
|
||||
* @param before the key to position this new entry afterwards
|
||||
* @param key key to be inserted at the beginning
|
||||
* @param value (new) value to be associated with the key
|
||||
* @return the previous value associated with the specified key, or {@code null} if there was no mapping for the key
|
||||
*
|
||||
* @see #putAfter(Object, Object, Object)
|
||||
*/
|
||||
@Nullable
|
||||
public V putBefore(K before, K key, V value)
|
||||
{
|
||||
var target = entries.get(before);
|
||||
if (target == null)
|
||||
return put(key, value);
|
||||
|
||||
V ret = null;
|
||||
var entry = entries.get(key);
|
||||
if (entry != null)
|
||||
{
|
||||
ret = entry.value;
|
||||
entry.value = merge.apply(key, ret, value);
|
||||
remove(entry);
|
||||
}
|
||||
else
|
||||
{
|
||||
entry = new Entry(key, value);
|
||||
entries.put(key, entry);
|
||||
}
|
||||
|
||||
entry.previous = target.previous;
|
||||
if (target.previous == null)
|
||||
head = entry;
|
||||
else
|
||||
target.previous.next = entry;
|
||||
entry.next = target;
|
||||
|
||||
target.previous = entry;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private void remove(Entry e)
|
||||
{
|
||||
if (head == null)
|
||||
head = e.next;
|
||||
else if (e.previous != null) // Should never be null, but just in case.
|
||||
{
|
||||
e.previous.next = e.next;
|
||||
e.previous = null;
|
||||
}
|
||||
|
||||
if (last == e)
|
||||
last = e.previous;
|
||||
else if (e.next != null) // Should never be null, but just in case.
|
||||
{
|
||||
e.next.previous = e.previous;
|
||||
e.next = null;
|
||||
}
|
||||
}
|
||||
|
||||
public static interface MergeFunction<Key, Value>
|
||||
{
|
||||
Value apply(Key key, Value left, Value right);
|
||||
}
|
||||
|
||||
private class Entry implements Map.Entry<K, V>
|
||||
{
|
||||
private final K key;
|
||||
private V value;
|
||||
|
||||
private Entry previous;
|
||||
private Entry next;
|
||||
|
||||
private Entry(K key, V value)
|
||||
{
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override public K getKey() { return this.key; }
|
||||
@Override public V getValue() { return this.value; }
|
||||
@Override
|
||||
public V setValue(V value)
|
||||
{
|
||||
var old = this.value;
|
||||
this.value = value;
|
||||
return old;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "Entry[" + this.key + ", " + this.value + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o)
|
||||
{
|
||||
if (!(o instanceof Map.Entry))
|
||||
return false;
|
||||
|
||||
Map.Entry<?,?> e = (Map.Entry<?, ?>)o;
|
||||
return (key == null ? e.getKey() == null : key.equals(e.getKey())) &&
|
||||
(value == null ? e.getValue() == null : value.equals(e.getValue()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return (key == null ? 0 : strategy.hashCode(key)) ^
|
||||
(value == null ? 0 : value.hashCode());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,15 +7,18 @@ package net.minecraftforge.event;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.flag.FeatureFlagSet;
|
||||
import net.minecraft.world.item.CreativeModeTab;
|
||||
import net.minecraft.world.item.CreativeModeTab.Output;
|
||||
import net.minecraft.world.item.CreativeModeTab.TabVisibility;
|
||||
import net.minecraft.world.item.CreativeModeTabs;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.ItemLike;
|
||||
import net.minecraftforge.common.CreativeModeTabRegistry;
|
||||
import net.minecraftforge.common.util.MutableHashedLinkedMap;
|
||||
import net.minecraftforge.eventbus.api.Cancelable;
|
||||
import net.minecraftforge.eventbus.api.Event;
|
||||
import net.minecraftforge.fml.LogicalSide;
|
||||
@@ -106,58 +109,20 @@ public class CreativeModeTabEvent extends Event implements IModBusEvent
|
||||
* This event is fired on the {@linkplain FMLJavaModLoadingContext#getModEventBus() mod-specific event bus},
|
||||
* only on the {@linkplain LogicalSide#CLIENT logical client}.
|
||||
*/
|
||||
public static final class BuildContents extends CreativeModeTabEvent
|
||||
public static final class BuildContents extends CreativeModeTabEvent implements Output
|
||||
{
|
||||
private final CreativeModeTab tab;
|
||||
private final Set<DisplayItemsAdapter> customGenerators;
|
||||
private final FeatureFlagSet flags;
|
||||
private final boolean hasPermissions;
|
||||
private final MutableHashedLinkedMap<ItemStack, TabVisibility> entries;
|
||||
|
||||
@ApiStatus.Internal
|
||||
public BuildContents(CreativeModeTab tab, Set<DisplayItemsAdapter> customGenerators)
|
||||
public BuildContents(CreativeModeTab tab, FeatureFlagSet flags, boolean hasPermissions, MutableHashedLinkedMap<ItemStack, TabVisibility> entries)
|
||||
{
|
||||
this.tab = tab;
|
||||
this.customGenerators = customGenerators;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the given generator.
|
||||
*/
|
||||
public void register(DisplayItemsAdapter generator)
|
||||
{
|
||||
this.customGenerators.add(generator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the given generator if the provided creative mode tab matches this event's currently populating tab.
|
||||
*/
|
||||
public void register(CreativeModeTab tab, DisplayItemsAdapter generator)
|
||||
{
|
||||
if (this.tab == tab)
|
||||
this.customGenerators.add(generator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the given generator if the provided tab name matches this event's currently populating tab's name.
|
||||
*/
|
||||
public void register(ResourceLocation tabName, DisplayItemsAdapter generator)
|
||||
{
|
||||
if (this.tab == CreativeModeTabRegistry.getTab(tabName))
|
||||
this.customGenerators.add(generator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given item if the provided creative mode tab matches this event's currently populating tab.
|
||||
*/
|
||||
public void registerSimple(CreativeModeTab tab, ItemLike item)
|
||||
{
|
||||
this.register(tab, (enabledFlags, output, permissions) -> output.accept(item));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given items if the provided creative mode tab matches this event's currently populating tab.
|
||||
*/
|
||||
public void registerSimple(CreativeModeTab tab, ItemLike... items)
|
||||
{
|
||||
this.register(tab, (enabledFlags, output, permissions) -> output.acceptAll(items));
|
||||
this.flags = flags;
|
||||
this.hasPermissions = hasPermissions;
|
||||
this.entries = entries;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -167,70 +132,36 @@ public class CreativeModeTabEvent extends Event implements IModBusEvent
|
||||
{
|
||||
return this.tab;
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface DisplayItemsAdapter
|
||||
{
|
||||
void accept(FeatureFlagSet enabledFlags, CreativeModeTabPopulator populator, boolean hasPermissions);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface CreativeModeTabPopulator
|
||||
{
|
||||
void accept(ItemStack stack, CreativeModeTab.TabVisibility visibility, ItemStack before, ItemStack after);
|
||||
|
||||
default void accept(ItemStack stack, ItemStack before, ItemStack after)
|
||||
public FeatureFlagSet getFlags()
|
||||
{
|
||||
accept(stack, CreativeModeTab.TabVisibility.PARENT_AND_SEARCH_TABS, before, after);
|
||||
return this.flags;
|
||||
}
|
||||
|
||||
default void accept(ItemLike stack, CreativeModeTab.TabVisibility visibility, ItemStack before, ItemStack after)
|
||||
public boolean hasPermissions()
|
||||
{
|
||||
accept(new ItemStack(stack), visibility, before, after);
|
||||
return this.hasPermissions;
|
||||
}
|
||||
|
||||
default void accept(ItemLike stack, ItemStack before, ItemStack after)
|
||||
public MutableHashedLinkedMap<ItemStack, TabVisibility> getEntries()
|
||||
{
|
||||
accept(stack, CreativeModeTab.TabVisibility.PARENT_AND_SEARCH_TABS, before, after);
|
||||
return this.entries;
|
||||
}
|
||||
|
||||
default void accept(ItemStack stack, CreativeModeTab.TabVisibility visibility)
|
||||
@Override
|
||||
public void accept(ItemStack stack, TabVisibility visibility)
|
||||
{
|
||||
accept(stack, visibility, ItemStack.EMPTY, ItemStack.EMPTY);
|
||||
getEntries().put(stack, visibility);
|
||||
}
|
||||
|
||||
default void accept(ItemStack stack)
|
||||
public void accept(Supplier<? extends ItemLike> item, CreativeModeTab.TabVisibility visibility)
|
||||
{
|
||||
this.accept(stack, CreativeModeTab.TabVisibility.PARENT_AND_SEARCH_TABS);
|
||||
this.accept(item.get(), visibility);
|
||||
}
|
||||
|
||||
default void accept(ItemLike itemLike, CreativeModeTab.TabVisibility visibility)
|
||||
public void accept(Supplier<? extends ItemLike> item)
|
||||
{
|
||||
this.accept(new ItemStack(itemLike), visibility);
|
||||
}
|
||||
|
||||
default void accept(ItemLike itemLike)
|
||||
{
|
||||
this.accept(new ItemStack(itemLike), CreativeModeTab.TabVisibility.PARENT_AND_SEARCH_TABS);
|
||||
}
|
||||
|
||||
default void acceptAll(Collection<ItemStack> stacks, CreativeModeTab.TabVisibility visibility)
|
||||
{
|
||||
stacks.forEach(stack -> this.accept(stack, visibility));
|
||||
}
|
||||
|
||||
default void acceptAll(Collection<ItemStack> stacks)
|
||||
{
|
||||
this.acceptAll(stacks, CreativeModeTab.TabVisibility.PARENT_AND_SEARCH_TABS);
|
||||
}
|
||||
|
||||
default void acceptAll(ItemLike... items)
|
||||
{
|
||||
for (ItemLike item : items)
|
||||
{
|
||||
this.accept(item);
|
||||
}
|
||||
this.accept(item.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -624,7 +624,7 @@ public class ForgeEventFactory
|
||||
return !blockGrowFeature(level, randomSource, pos, null).getResult().equals(Result.DENY);
|
||||
}
|
||||
|
||||
public static SaplingGrowTreeEvent blockGrowFeature(LevelAccessor level, RandomSource randomSource, BlockPos pos, @Nullable Holder<? extends ConfiguredFeature<?, ?>> holder)
|
||||
public static SaplingGrowTreeEvent blockGrowFeature(LevelAccessor level, RandomSource randomSource, BlockPos pos, @Nullable Holder<ConfiguredFeature<?, ?>> holder)
|
||||
{
|
||||
SaplingGrowTreeEvent event = new SaplingGrowTreeEvent(level, randomSource, pos, holder);
|
||||
MinecraftForge.EVENT_BUS.post(event);
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.event.entity.player;
|
||||
|
||||
import net.minecraft.world.entity.npc.AbstractVillager;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.trading.MerchantOffer;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.eventbus.api.Cancelable;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
/**
|
||||
* Fired when a player trades with an {@link AbstractVillager}.
|
||||
*
|
||||
* <p>This event is not {@linkplain Cancelable cancellable}, and does not {@linkplain Event.HasResult have a result}.</p>
|
||||
*
|
||||
* <p>This event is fired on the {@linkplain MinecraftForge#EVENT_BUS main Forge event bus},
|
||||
* only on the {@linkplain LogicalSide#SERVER logical server}.</p>
|
||||
*/
|
||||
public class TradeWithVillagerEvent extends PlayerEvent
|
||||
{
|
||||
private final MerchantOffer offer;
|
||||
private final AbstractVillager abstractVillager;
|
||||
|
||||
@ApiStatus.Internal
|
||||
public TradeWithVillagerEvent(Player player, MerchantOffer offer, AbstractVillager abstractVillager)
|
||||
{
|
||||
super(player);
|
||||
this.offer = offer;
|
||||
this.abstractVillager = abstractVillager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the {@link MerchantOffer} selected by the player to trade with}
|
||||
*/
|
||||
public MerchantOffer getMerchantOffer()
|
||||
{
|
||||
return offer;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the villager the player traded with}
|
||||
*/
|
||||
public AbstractVillager getAbstractVillager()
|
||||
{
|
||||
return abstractVillager;
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,7 @@ public class SaplingGrowTreeEvent extends LevelEvent
|
||||
private final RandomSource randomSource;
|
||||
private final BlockPos pos;
|
||||
@Nullable
|
||||
private Holder<? extends ConfiguredFeature<?, ?>> feature;
|
||||
private Holder<ConfiguredFeature<?, ?>> feature;
|
||||
|
||||
@Deprecated(forRemoval = true, since = "1.19.2")
|
||||
public SaplingGrowTreeEvent(LevelAccessor level, RandomSource randomSource, BlockPos pos)
|
||||
@@ -48,7 +48,7 @@ public class SaplingGrowTreeEvent extends LevelEvent
|
||||
this(level, randomSource, pos, null);
|
||||
}
|
||||
|
||||
public SaplingGrowTreeEvent(LevelAccessor level, RandomSource randomSource, BlockPos pos, @Nullable Holder<? extends ConfiguredFeature<?, ?>> feature)
|
||||
public SaplingGrowTreeEvent(LevelAccessor level, RandomSource randomSource, BlockPos pos, @Nullable Holder<ConfiguredFeature<?, ?>> feature)
|
||||
{
|
||||
super(level);
|
||||
this.randomSource = randomSource;
|
||||
@@ -76,15 +76,24 @@ public class SaplingGrowTreeEvent extends LevelEvent
|
||||
* {@return the holder of the feature which will be placed, possibly null}
|
||||
*/
|
||||
@Nullable
|
||||
public Holder<? extends ConfiguredFeature<?, ?>> getFeature() {
|
||||
public Holder<ConfiguredFeature<?, ?>> getFeature()
|
||||
{
|
||||
return feature;
|
||||
}
|
||||
|
||||
public void setFeature(@Nullable Holder<? extends ConfiguredFeature<?, ?>> feature) {
|
||||
/**
|
||||
* @param feature a {@linkplain Holder} referencing a tree feature to be placed instead of the current feature.
|
||||
*/
|
||||
public void setFeature(@Nullable Holder<ConfiguredFeature<?, ?>> feature)
|
||||
{
|
||||
this.feature = feature;
|
||||
}
|
||||
|
||||
public void setFeature(ResourceKey<ConfiguredFeature<?, ?>> featureKey) {
|
||||
/**
|
||||
* @param featureKey a {@linkplain ResourceKey} referencing a tree feature to be placed instead of the current feature.
|
||||
*/
|
||||
public void setFeature(ResourceKey<ConfiguredFeature<?, ?>> featureKey)
|
||||
{
|
||||
this.feature = this.getLevel().registryAccess().registryOrThrow(Registries.CONFIGURED_FEATURE).getHolder(featureKey).orElse(null);
|
||||
}
|
||||
}
|
||||
Loaded 100 of 156 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user