mirror of
https://github.com/MinecraftForge/MinecraftForge.git
synced 2026-11-14 09:05:17 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e00bddd6d8 | ||
|
|
f0ef4fe307 | ||
|
|
71f626be39 | ||
|
|
1554afd613 | ||
|
|
631b756d5b | ||
|
|
bc6408ee24 | ||
|
|
998999412f | ||
|
|
02bbab1f90 | ||
|
|
1cde9f70a9 | ||
|
|
2dd93eea1d | ||
|
|
592ca2897c | ||
|
|
27fb189b0d | ||
|
|
3cd49ed58d |
No files matched your search
@@ -0,0 +1,28 @@
|
||||
name: Publish
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ '1.20.4' ]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
uses: MinecraftForge/SharedActions/.github/workflows/gradle.yml@main
|
||||
with:
|
||||
java: 17
|
||||
gradle_setup: './gradlew setup'
|
||||
gradle_tasks: 'build publish'
|
||||
artifact_name: 'forge'
|
||||
secrets:
|
||||
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
|
||||
PROMOTE_ARTIFACT_WEBHOOK: ${{ secrets.PROMOTE_ARTIFACT_WEBHOOK }}
|
||||
PROMOTE_ARTIFACT_USERNAME: ${{ secrets.PROMOTE_ARTIFACT_USERNAME }}
|
||||
PROMOTE_ARTIFACT_PASSWORD: ${{ secrets.PROMOTE_ARTIFACT_PASSWORD }}
|
||||
MAVEN_USER: ${{ secrets.MAVEN_USER }}
|
||||
MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }}
|
||||
SIGN_KEY_ALIAS: ${{ secrets.SIGN_KEY_ALIAS }}
|
||||
SIGN_KEY_PASSWORD: ${{ secrets.SIGN_KEY_PASSWORD }}
|
||||
SIGN_KEYSTORE_PASSWORD: ${{ secrets.SIGN_KEYSTORE_PASSWORD }}
|
||||
SIGN_KEYSTORE_DATA: ${{ secrets.SIGN_KEYSTORE_DATA }}
|
||||
Vendored
-104
@@ -1,104 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<project>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<name>MinecraftForge_Forge Config DSL Script</name>
|
||||
<groupId>MinecraftForge_Forge</groupId>
|
||||
<artifactId>MinecraftForge_Forge_dsl</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<parent>
|
||||
<groupId>org.jetbrains.teamcity</groupId>
|
||||
<artifactId>configs-dsl-kotlin-parent</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>jetbrains-all</id>
|
||||
<url>https://download.jetbrains.com/teamcity-repository</url>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>teamcity-server</id>
|
||||
<url>https://teamcity.minecraftforge.net/app/dsl-plugins-repository</url>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>JetBrains</id>
|
||||
<url>https://download.jetbrains.com/teamcity-repository</url>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
|
||||
<build>
|
||||
<sourceDirectory>${basedir}</sourceDirectory>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>kotlin-maven-plugin</artifactId>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<version>${kotlin.version}</version>
|
||||
|
||||
<configuration/>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>compile</id>
|
||||
<phase>process-sources</phase>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>test-compile</id>
|
||||
<phase>process-test-sources</phase>
|
||||
<goals>
|
||||
<goal>test-compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.jetbrains.teamcity</groupId>
|
||||
<artifactId>teamcity-configs-maven-plugin</artifactId>
|
||||
<version>${teamcity.dsl.version}</version>
|
||||
<configuration>
|
||||
<format>kotlin</format>
|
||||
<dstDir>target/generated-configs</dstDir>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.teamcity</groupId>
|
||||
<artifactId>configs-dsl-kotlin</artifactId>
|
||||
<version>${teamcity.dsl.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.teamcity</groupId>
|
||||
<artifactId>configs-dsl-kotlin-plugins</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<type>pom</type>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-stdlib-jdk8</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-script-runtime</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
Vendored
-193
@@ -1,193 +0,0 @@
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.*
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.projectFeatures.githubIssues
|
||||
import jetbrains.buildServer.configs.kotlin.*
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.gradle
|
||||
|
||||
/*
|
||||
The settings script is an entry point for defining a TeamCity
|
||||
project hierarchy. The script should contain a single call to the
|
||||
project() function with a Project instance or an init function as
|
||||
an argument.
|
||||
|
||||
VcsRoots, BuildTypes, Templates, and subprojects can be
|
||||
registered inside the project using the vcsRoot(), buildType(),
|
||||
template(), and subProject() methods respectively.
|
||||
|
||||
To debug settings scripts in command-line, run the
|
||||
|
||||
mvnDebug org.jetbrains.teamcity:teamcity-configs-maven-plugin:generate
|
||||
|
||||
command and attach your debugger to the port 8000.
|
||||
|
||||
To debug in IntelliJ Idea, open the 'Maven Projects' tool window (View
|
||||
-> Tool Windows -> Maven Projects), find the generate task node
|
||||
(Plugins -> teamcity-configs -> teamcity-configs:generate), the
|
||||
'Debug' option is available in the context menu for the task.
|
||||
*/
|
||||
|
||||
version = "2021.2"
|
||||
|
||||
project {
|
||||
|
||||
buildType(Build)
|
||||
buildType(BuildSecondaryBranches)
|
||||
buildType(PullRequests)
|
||||
buildType(PullRequestChecks)
|
||||
buildType(PullRequestCompatibility)
|
||||
|
||||
params {
|
||||
text("docker_jdk_version", "17", label = "Gradle version", description = "The version of the JDK to use during execution of tasks in a JDK.", display = ParameterDisplay.HIDDEN, allowEmpty = false)
|
||||
text("docker_gradle_version", "7.5.1", label = "Gradle version", description = "The version of Gradle to use during execution of Gradle tasks.", display = ParameterDisplay.HIDDEN, allowEmpty = false)
|
||||
text("git_main_branch", "1.20.x", label = "Git Main Branch", description = "The git main or default branch to use in VCS operations.", display = ParameterDisplay.HIDDEN, allowEmpty = false)
|
||||
text("git_branch_spec", """
|
||||
+:refs/heads/(main*)
|
||||
+:refs/heads/(master*)
|
||||
+:refs/heads/(develop|release|staging|main|master)
|
||||
+:refs/heads/(1.*)
|
||||
""".trimIndent(), label = "The branch specification of the repository", description = "By default all main branches are build by the configuration. Modify this value to adapt the branches build.", display = ParameterDisplay.HIDDEN, allowEmpty = true)
|
||||
text("github_repository_name", "MinecraftForge", label = "The github repository name. Used to connect to it in VCS Roots.", description = "This is the repository slug on github. So for example `MinecraftForge` or `MinecraftForge`. It is interpolated into the global VCS Roots.", display = ParameterDisplay.HIDDEN, allowEmpty = false)
|
||||
text("env.PUBLISHED_JAVA_ARTIFACT_ID", "forge", label = "Published artifact id", description = "The maven coordinate artifact id that has been published by this build. Can not be empty.", allowEmpty = false)
|
||||
text("env.PUBLISHED_JAVA_GROUP", "net.minecraftforge", label = "Published group", description = "The maven coordinate group that has been published by this build. Can not be empty.", allowEmpty = false)
|
||||
//These are references and not actually keys
|
||||
password("env.CROWDIN_KEY", "credentialsJSON:a3102dbe-805d-4177-9f54-3d2c2eb08fd5", display = ParameterDisplay.HIDDEN)
|
||||
password("env.KEYSTORE_URL", "credentialsJSON:a7ae1c82-8058-4061-8d12-7f6bc2618d2e", display = ParameterDisplay.HIDDEN)
|
||||
password("env.KEYSTORE_PASSWORD", "credentialsJSON:d7b964e3-a1fd-47a8-b892-6f601fe47479", display = ParameterDisplay.HIDDEN)
|
||||
text("additional_publishing_gradle_parameters", "-PcrowdinKey=%env.CROWDIN_KEY% -PkeystoreKeyPass=%env.KEYSTORE_PASSWORD% -PkeystoreStorePass=%env.KEYSTORE_PASSWORD% -Pkeystore=%env.KEYSTORE_URL%", label = "Additional gradle parameters for publish", description = "Contains the additional gradle parameters used during publishing.", display = ParameterDisplay.HIDDEN, allowEmpty = true)
|
||||
|
||||
checkbox("should_execute_build", "false", label = "Should build", description = "Indicates if the build task should be executed.", display = ParameterDisplay.HIDDEN,
|
||||
checked = "true", unchecked = "false")
|
||||
checkbox("should_execute_test", "false", label = "Should build", description = "Indicates if the build task should be executed.", display = ParameterDisplay.HIDDEN,
|
||||
checked = "true", unchecked = "false")
|
||||
}
|
||||
|
||||
features {
|
||||
githubIssues {
|
||||
id = "MinecraftForge_MinecraftForge__IssueTracker"
|
||||
displayName = "MinecraftForge/MinecraftForge"
|
||||
repositoryURL = "https://github.com/MinecraftForge/MinecraftForge"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object Build : BuildType({
|
||||
templates(AbsoluteId("MinecraftForge_SetupGradleUtilsCiEnvironmen"), AbsoluteId("MinecraftForge_BuildWithDiscordNotifications"), AbsoluteId("MinecraftForge_BuildMainBranches"), AbsoluteId("MinecraftForge_SetupProjectUsingGradle"), AbsoluteId("MinecraftForge_BuildUsingGradle"), AbsoluteId("MinecraftForge_PublishProjectUsingGradle"), AbsoluteId("MinecraftForge_TriggersStaticFilesWebpageGenerator"))
|
||||
id("MinecraftForge_MinecraftForge__Build")
|
||||
name = "Build"
|
||||
description = "Builds and Publishes the main branches of the project."
|
||||
})
|
||||
|
||||
object BuildSecondaryBranches : BuildType({
|
||||
templates(AbsoluteId("MinecraftForge_ExcludesBuildingDefaultBranch"), AbsoluteId("MinecraftForge_SetupGradleUtilsCiEnvironmen"), AbsoluteId("MinecraftForge_BuildWithDiscordNotifications"), AbsoluteId("MinecraftForge_BuildMainBranches"), AbsoluteId("MinecraftForge_SetupProjectUsingGradle"), AbsoluteId("MinecraftForge_BuildUsingGradle"))
|
||||
id("MinecraftForge_MinecraftForge__BuildSecondaryBranches")
|
||||
name = "Build - Secondary Branches"
|
||||
description = "Builds and Publishes the secondary branches of the project."
|
||||
params {
|
||||
text("git_branch_spec", """
|
||||
+:refs/heads/(*)
|
||||
-:refs/heads/noci*
|
||||
""".trimIndent(), label = "The branch specification of the repository", description = "By default all main branches are build by the configuration. Modify this value to adapt the branches build.", display = ParameterDisplay.HIDDEN, allowEmpty = true)
|
||||
}
|
||||
vcs {
|
||||
branchFilter = """
|
||||
+:*
|
||||
-:1.*
|
||||
-:<default>
|
||||
""".trimIndent()
|
||||
}
|
||||
})
|
||||
|
||||
object PullRequests : BuildType({
|
||||
templates(AbsoluteId("MinecraftForge_BuildPullRequests"), AbsoluteId("MinecraftForge_SetupGradleUtilsCiEnvironmen"), AbsoluteId("MinecraftForge_BuildWithDiscordNotifications"), AbsoluteId("MinecraftForge_SetupProjectUsingGradle"), AbsoluteId("MinecraftForge_BuildUsingGradle"))
|
||||
id("MinecraftForge_MinecraftForge__PullRequests")
|
||||
name = "Pull Requests"
|
||||
description = "Builds pull requests for the project"
|
||||
|
||||
params {
|
||||
checkbox("should_execute_build", "true", label = "Should build", description = "Indicates if the build task should be executed.", display = ParameterDisplay.HIDDEN,
|
||||
checked = "true", unchecked = "false")
|
||||
text(
|
||||
"gradle_build_task",
|
||||
"assemble",
|
||||
label = "Gradle build task to execute during build",
|
||||
description = "Determines the build task that is executed to build the project.",
|
||||
display = ParameterDisplay.HIDDEN,
|
||||
allowEmpty = false
|
||||
)
|
||||
}
|
||||
|
||||
vcs {
|
||||
branchFilter = """
|
||||
+:*
|
||||
-:1.*
|
||||
-:<default>
|
||||
""".trimIndent()
|
||||
}
|
||||
})
|
||||
|
||||
object PullRequestChecks : BuildType({
|
||||
templates(AbsoluteId("MinecraftForge_BuildPullRequests"), AbsoluteId("MinecraftForge_SetupGradleUtilsCiEnvironmen"), AbsoluteId("MinecraftForge_BuildWithDiscordNotifications"), AbsoluteId("MinecraftForge_SetupProjectUsingGradle"))
|
||||
id("MinecraftForge_MinecraftForge__PullRequestChecks")
|
||||
name = "Pull Requests (Checks)"
|
||||
description = "Checks pull requests for the project"
|
||||
|
||||
steps {
|
||||
gradle {
|
||||
name = "Check"
|
||||
id = "RUNNER_10_Check"
|
||||
|
||||
tasks = "checkAll"
|
||||
gradleParams = "--continue %gradle_custom_args%"
|
||||
enableStacktrace = true
|
||||
dockerImage = "%docker_gradle_image%"
|
||||
dockerRunParameters = """
|
||||
-v "/opt/cache/agent/gradle:/home/gradle/.gradle"
|
||||
-v "/opt/cache/shared/gradle:/home/gradle/rocache:ro"
|
||||
--network=host
|
||||
-u 1000:1000
|
||||
%docker_additional_args%
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
vcs {
|
||||
branchFilter = """
|
||||
+:*
|
||||
-:1.*
|
||||
-:<default>
|
||||
""".trimIndent()
|
||||
}
|
||||
})
|
||||
|
||||
object PullRequestCompatibility : BuildType({
|
||||
templates(AbsoluteId("MinecraftForge_BuildPullRequests"), AbsoluteId("MinecraftForge_SetupGradleUtilsCiEnvironmen"), AbsoluteId("MinecraftForge_BuildWithDiscordNotifications"), AbsoluteId("MinecraftForge_SetupProjectUsingGradle"))
|
||||
id("MinecraftForge_MinecraftForge__PullRequestCompatibility")
|
||||
name = "Pull Requests (Compatibility)"
|
||||
description = "Validates binary compatibility for pull requests made to the project"
|
||||
|
||||
steps {
|
||||
gradle {
|
||||
name = "Validate"
|
||||
id = "RUNNER_10_Compatibility"
|
||||
|
||||
tasks = "checkJarCompatibility"
|
||||
gradleParams = "--continue %gradle_custom_args%"
|
||||
enableStacktrace = true
|
||||
dockerImage = "%docker_gradle_image%"
|
||||
dockerRunParameters = """
|
||||
-v "/opt/cache/agent/gradle:/home/gradle/.gradle"
|
||||
-v "/opt/cache/shared/gradle:/home/gradle/rocache:ro"
|
||||
--network=host
|
||||
-u 1000:1000
|
||||
%docker_additional_args%
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
vcs {
|
||||
branchFilter = """
|
||||
+:*
|
||||
-:1.*
|
||||
-:<default>
|
||||
""".trimIndent()
|
||||
}
|
||||
})
|
||||
@@ -4,9 +4,9 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'org.ow2.asm:asm:9.6'
|
||||
implementation 'org.ow2.asm:asm-tree:9.6'
|
||||
implementation 'net.minecraftforge:srgutils:0.5.+'
|
||||
implementation 'org.ow2.asm:asm:9.9.1'
|
||||
implementation 'org.ow2.asm:asm-tree:9.9.1'
|
||||
implementation 'net.minecraftforge:srgutils:0.6.2'
|
||||
implementation 'commons-io:commons-io:2.13.0'
|
||||
implementation 'com.google.code.gson:gson:2.10.1'
|
||||
implementation 'org.eclipse.jgit:org.eclipse.jgit:6.5.0.202303070854-r'
|
||||
|
||||
@@ -19,6 +19,8 @@ abstract class BundleList extends DefaultTask {
|
||||
configure {
|
||||
dependsOn(project.tasks.universalJar)
|
||||
inputs.file(project.tasks.universalJar.archiveFile)
|
||||
dependsOn(project.tasks.applyServerBinPatches)
|
||||
inputs.file(project.tasks.applyServerBinPatches.output)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -190,7 +190,7 @@ public class FMLLoader {
|
||||
}
|
||||
|
||||
public static void addAccessTransformer(Path atPath, ModFile modName) {
|
||||
LOGGER.debug(SCAN, "Adding Access Transformer in {}", modName.getFilePath());
|
||||
LOGGER.debug(SCAN, "Adding Access Transformer in {} in {}", atPath, modName.getFilePath());
|
||||
accessTransformer.offerResource(atPath, modName.getFileName());
|
||||
}
|
||||
|
||||
|
||||
@@ -5,12 +5,14 @@
|
||||
|
||||
package net.minecraftforge.fml.loading;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
import cpw.mods.modlauncher.api.LamdbaExceptionUtils;
|
||||
import net.minecraftforge.fml.loading.moddiscovery.BackgroundScanHandler;
|
||||
import net.minecraftforge.fml.loading.moddiscovery.ModFile;
|
||||
import net.minecraftforge.fml.loading.moddiscovery.ModFileInfo;
|
||||
import net.minecraftforge.fml.loading.moddiscovery.ModInfo;
|
||||
import net.minecraftforge.forgespi.locating.IModFile;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
@@ -30,6 +32,7 @@ import java.util.stream.Collectors;
|
||||
*/
|
||||
public class LoadingModList
|
||||
{
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
private static LoadingModList INSTANCE;
|
||||
private final List<ModFileInfo> modFiles;
|
||||
private final List<ModInfo> sortedList;
|
||||
@@ -78,9 +81,22 @@ public class LoadingModList
|
||||
|
||||
public void addAccessTransformers()
|
||||
{
|
||||
modFiles.stream()
|
||||
.map(ModFileInfo::getFile)
|
||||
.forEach(mod -> mod.getAccessTransformer().ifPresent(path -> FMLLoader.addAccessTransformer(path, mod)));
|
||||
var errors = new ArrayList<EarlyLoadingException.ExceptionData>();
|
||||
for (ModFileInfo modFile : modFiles) {
|
||||
ModFile mod = modFile.getFile();
|
||||
for (Path at : mod.getAccessTransformers()) {
|
||||
if (!Files.exists(at)) {
|
||||
var message = "Invalid mod file: " + modFile.getFile().getFileName() + ". Missing Access Transformer: " + at;
|
||||
errors.add(new EarlyLoadingException.ExceptionData(message));
|
||||
LOGGER.error(message);
|
||||
} else {
|
||||
FMLLoader.addAccessTransformer(at, mod);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!errors.isEmpty()) {
|
||||
preLoadErrors.add(new EarlyLoadingException("Invalid Access Transformers", null, errors));
|
||||
}
|
||||
}
|
||||
|
||||
public void addForScanning(BackgroundScanHandler backgroundScanHandler)
|
||||
|
||||
@@ -34,8 +34,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.Marker;
|
||||
import org.slf4j.MarkerFactory;
|
||||
|
||||
public class RuntimeDistCleaner implements ILaunchPluginService
|
||||
{
|
||||
public final class RuntimeDistCleaner implements ILaunchPluginService {
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
private static final Marker DISTXFORM = MarkerFactory.getMarker("DISTXFORM");
|
||||
private static String DIST;
|
||||
@@ -177,6 +176,10 @@ public class RuntimeDistCleaner implements ILaunchPluginService
|
||||
if (internalName.startsWith("net/minecraftforge/"))
|
||||
return NAY;
|
||||
|
||||
// No Vanilla classes use @OnlyIn(Dist.SERVER), so we can skip them entirely when we're running on a client
|
||||
if (FMLEnvironment.dist.isClient() && (internalName.startsWith("net/minecraft/") || internalName.startsWith("com/mojang/")))
|
||||
return NAY;
|
||||
|
||||
return YAY;
|
||||
}
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@ public class ModFile implements IModFile {
|
||||
private CompletableFuture<ModFileScanData> futureScanResult;
|
||||
private List<CoreModFile> coreMods;
|
||||
private Path accessTransformer;
|
||||
private List<Path> accessTransformers = Collections.emptyList();
|
||||
|
||||
static final Attributes.Name TYPE = new Attributes.Name("FMLModType");
|
||||
private SecureJar.Status securityStatus;
|
||||
@@ -96,7 +97,11 @@ public class ModFile implements IModFile {
|
||||
}
|
||||
|
||||
public Optional<Path> getAccessTransformer() {
|
||||
return Optional.ofNullable(Files.exists(accessTransformer) ? accessTransformer : null);
|
||||
return Optional.ofNullable(accessTransformer);
|
||||
}
|
||||
|
||||
public List<Path> getAccessTransformers() {
|
||||
return accessTransformers;
|
||||
}
|
||||
|
||||
public boolean identifyMods() {
|
||||
@@ -105,7 +110,28 @@ public class ModFile implements IModFile {
|
||||
LOGGER.debug(LogMarkers.LOADING,"Loading mod file {} with languages {}", this.getFilePath(), this.modFileInfo.requiredLanguageLoaders());
|
||||
this.coreMods = ModFileParser.getCoreMods(this);
|
||||
this.coreMods.forEach(mi-> LOGGER.debug(LogMarkers.LOADING,"Found coremod {}", mi.getPath()));
|
||||
this.accessTransformer = findResource("META-INF", "accesstransformer.cfg");
|
||||
List<String> cfg;
|
||||
// Note: Some mods may have invalid tomls copied from other projects.
|
||||
// Unfortunately we must protect against those landmines.
|
||||
try {
|
||||
cfg = this.modFileInfo.getConfig().<List<String>>getConfigElement("accessTransformers").orElse(null);
|
||||
} catch (Exception e) {
|
||||
cfg = null;
|
||||
LOGGER.warn("{} contains an invalid 'accessTransformers' TOML entry. Should be e.g. accessTransformers = [\"META-INF/accesstransformer.cfg\", \"META-INF/extra_at.cfg\"] or accessTransformers = [] for no ATs. Falling back to default.", this.getFileName());
|
||||
}
|
||||
if (cfg == null) {
|
||||
var path = findResource("META-INF", "accesstransformer.cfg");
|
||||
if (Files.exists(path)) {
|
||||
this.accessTransformer = path;
|
||||
this.accessTransformers = List.of(path);
|
||||
}
|
||||
} else if (!cfg.isEmpty()) {
|
||||
var paths = new ArrayList<Path>(cfg.size());
|
||||
for (var path : cfg) {
|
||||
paths.add(getSecureJar().getPath(path.replace("\\","/")));
|
||||
}
|
||||
this.accessTransformers = List.copyOf(paths);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+12
-4
@@ -6,6 +6,7 @@
|
||||
package net.minecraftforge.fml.loading.moddiscovery;
|
||||
|
||||
import com.electronwill.nightconfig.core.file.FileConfig;
|
||||
import com.electronwill.nightconfig.core.io.ParsingException;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.mojang.logging.LogUtils;
|
||||
@@ -13,6 +14,7 @@ import net.minecraftforge.fml.loading.LogMarkers;
|
||||
import net.minecraftforge.forgespi.language.IModFileInfo;
|
||||
import net.minecraftforge.forgespi.locating.IModFile;
|
||||
import net.minecraftforge.forgespi.locating.ModFileFactory;
|
||||
import net.minecraftforge.forgespi.locating.ModFileLoadingException;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
@@ -31,6 +33,7 @@ public class ModFileParser {
|
||||
return parser.build(modFile);
|
||||
}
|
||||
|
||||
// Note: Although @Nullable, several other places in FML assume ModFileInfo is not null. Keep this in mind
|
||||
public static @Nullable IModFileInfo modsTomlParser(final IModFile imodFile) {
|
||||
ModFile modFile = (ModFile) imodFile;
|
||||
LOGGER.debug(LogMarkers.LOADING,"Considering mod file candidate {}", modFile.getFilePath());
|
||||
@@ -41,10 +44,15 @@ public class ModFileParser {
|
||||
}
|
||||
|
||||
final FileConfig fileConfig = FileConfig.builder(modsjson).build();
|
||||
fileConfig.load();
|
||||
fileConfig.close();
|
||||
final NightConfigWrapper configWrapper = new NightConfigWrapper(fileConfig);
|
||||
return new ModFileInfo(modFile, configWrapper, configWrapper::setFile);
|
||||
try {
|
||||
fileConfig.load();
|
||||
fileConfig.close();
|
||||
final NightConfigWrapper configWrapper = new NightConfigWrapper(fileConfig);
|
||||
return new ModFileInfo(modFile, configWrapper, configWrapper::setFile);
|
||||
} catch (ParsingException e) {
|
||||
LOGGER.error("Mod candidate {} contains a corrupt or misconfigured toml.", modFile.getFileName());
|
||||
throw new ModFileLoadingException("Mod candidate " + modFile.getFileName() + " contains a corrupt or misconfigured toml.");
|
||||
}
|
||||
}
|
||||
|
||||
protected static List<CoreModFile> getCoreMods(final ModFile modFile) {
|
||||
|
||||
@@ -41,6 +41,13 @@ authors="${mod_authors}" #optional
|
||||
# IMPORTANT NOTE: this is NOT an instruction as to which environments (CLIENT or DEDICATED SERVER) your mod loads on. Your mod should load (and maybe do nothing!) whereever it finds itself.
|
||||
#displayTest="MATCH_VERSION" # if nothing is specified, MATCH_VERSION is the default when clientSideOnly=false, otherwise IGNORE_ALL_VERSION when clientSideOnly=true (#optional)
|
||||
|
||||
|
||||
# Access Transformers
|
||||
# Optional, if not specified Forge will attempt to find the default META-INF/accesstransformer.cfg and silently continue if not found. May default to an empty list in a future MC.
|
||||
# If you specify AT path strings in this list, Forge will attempt to load each of them and throw errors for any that aren't found.
|
||||
# Specifying an empty list when your mod has no ATs is recommended to opt-out of the default for slightly better loading performance.
|
||||
#accessTransformers = []
|
||||
|
||||
# The description text for the mod (multi line!) (#mandatory)
|
||||
description='''${mod_description}'''
|
||||
# A dependency - use the . to indicate dependency for a specific modid. Dependencies are optional.
|
||||
|
||||
@@ -88,12 +88,14 @@
|
||||
}
|
||||
|
||||
public void tick() {
|
||||
@@ -263,7 +_,7 @@
|
||||
@@ -263,7 +_,9 @@
|
||||
|
||||
private boolean sameDestroyTarget(BlockPos p_105282_) {
|
||||
ItemStack itemstack = this.minecraft.player.getMainHandItem();
|
||||
- return p_105282_.equals(this.destroyBlockPos) && ItemStack.isSameItemSameTags(itemstack, this.destroyingItem);
|
||||
+ return p_105282_.equals(this.destroyBlockPos) && ItemStack.isSameItemSameTags(itemstack, this.destroyingItem) && !destroyingItem.shouldCauseBlockBreakReset(itemstack);
|
||||
+ if (Boolean.FALSE)
|
||||
+ ItemStack.isSameItemSameTags(itemstack, this.destroyingItem);
|
||||
+ return p_105282_.equals(this.destroyBlockPos) && !destroyingItem.shouldCauseBlockBreakReset(itemstack);
|
||||
}
|
||||
|
||||
private void ensureHasSentCarriedItem() {
|
||||
|
||||
+2
-2
@@ -31,7 +31,7 @@ dependencyResolutionManagement {
|
||||
library('antlr-runtime', 'org.antlr:antlr4-runtime:4.9.1') // Needed by Access Transformer because parsing a simple text file was too complicated
|
||||
library('coremods', 'net.minecraftforge:coremods:5.2.6')
|
||||
library('nashorn', 'org.openjdk.nashorn:nashorn-core:15.4') // Needed by coremods, because the JRE no longer ships JS
|
||||
library('eventbus', 'net.minecraftforge:eventbus:6.2.27')
|
||||
library('eventbus', 'net.minecraftforge:eventbus:6.2.33')
|
||||
library('typetools', 'net.jodah:typetools:0.6.3') // Needed by EventBus because of lambdas
|
||||
library('mergetool-api', 'net.minecraftforge:mergetool-api:1.0')
|
||||
|
||||
@@ -51,7 +51,7 @@ dependencyResolutionManagement {
|
||||
library('bootstrap-shim', 'net.minecraftforge', 'bootstrap-shim').versionRef('bootstrap')
|
||||
|
||||
// ASM it's used for so many of our hacks
|
||||
version('asm', '9.7.1')
|
||||
version('asm', '9.9.1')
|
||||
library('asm', 'org.ow2.asm', 'asm' ).versionRef('asm')
|
||||
library('asm-tree', 'org.ow2.asm', 'asm-tree' ).versionRef('asm')
|
||||
library('asm-util', 'org.ow2.asm', 'asm-util' ).versionRef('asm')
|
||||
|
||||
@@ -93,11 +93,23 @@ public class ForgeConfig {
|
||||
* General configuration that doesn't need to be synchronized but needs to be available before server startup
|
||||
*/
|
||||
public static class Common {
|
||||
public final ForgeConfigSpec.LongValue channelListLimit;
|
||||
|
||||
Common(ForgeConfigSpec.Builder builder) {
|
||||
builder.comment("[DEPRECATED / NO EFFECT]: General configuration settings")
|
||||
builder.comment("General configuration settings")
|
||||
.push("general");
|
||||
|
||||
channelListLimit = builder
|
||||
.comment(
|
||||
"Maximum size to use to store the channel list for a network connection.",
|
||||
"The other end of the connection may send as many channels as it wishes.",
|
||||
"Some malicious actors may use this to use up all your memory causing OutOfMemoryException",
|
||||
"A sane default is 1KB (1024), but may need to be expanded for larger modpacks.",
|
||||
"Forge will auto-adjust this if mods register their channels using our channel manager."
|
||||
)
|
||||
.translation("forge.configui.channellistlimit")
|
||||
.defineInRange("channelListLimit", 1024, 1, Long.MAX_VALUE);
|
||||
|
||||
builder.pop();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,13 +114,13 @@ public class ConcatenatedListView<T> implements List<T>
|
||||
@Override
|
||||
public Iterator<T> iterator()
|
||||
{
|
||||
return Iterables.unmodifiableIterable(Iterables.concat(lists)).iterator();
|
||||
return Iterables.unmodifiableIterable(Iterables.<T>concat(lists)).iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Spliterator<T> spliterator()
|
||||
{
|
||||
return Iterables.unmodifiableIterable(Iterables.concat(lists)).spliterator();
|
||||
return Iterables.unmodifiableIterable(Iterables.<T>concat(lists)).spliterator();
|
||||
}
|
||||
|
||||
// Delegate to a concatenated collection
|
||||
|
||||
@@ -171,20 +171,11 @@ public class ChannelBuilder {
|
||||
* @return the {@link NetworkInstance}
|
||||
*/
|
||||
private NetworkInstance createNetworkInstance() {
|
||||
if (NetworkRegistry.lock) {
|
||||
NetworkRegistry.LOGGER.error(NetworkRegistry.NETREGISTRY, "Attempted to register channel {} even though registry phase is over", name);
|
||||
throw new IllegalArgumentException("Registration of impl channels is locked");
|
||||
}
|
||||
if (NetworkRegistry.instances.containsKey(name)) {
|
||||
NetworkRegistry.LOGGER.error(NetworkRegistry.NETREGISTRY, "NetworkDirection channel {} already registered.", name);
|
||||
throw new IllegalArgumentException("NetworkDirection Channel {"+ name +"} already registered");
|
||||
}
|
||||
|
||||
var networkInstance = new NetworkInstance(name, networkProtocolVersion,
|
||||
getClientAcceptedVersions(), getServerAcceptedVersions(),
|
||||
attributes, connectionHandler
|
||||
);
|
||||
NetworkRegistry.instances.put(name, networkInstance);
|
||||
NetworkRegistry.register(networkInstance);
|
||||
return networkInstance;
|
||||
|
||||
}
|
||||
|
||||
@@ -10,7 +10,9 @@ import io.netty.buffer.Unpooled;
|
||||
import net.minecraft.ResourceLocationException;
|
||||
import net.minecraft.network.Connection;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraftforge.common.ForgeConfig;
|
||||
import net.minecraftforge.event.ForgeEventFactory;
|
||||
import net.minecraftforge.event.network.ChannelRegistrationChangeEvent;
|
||||
import net.minecraftforge.event.network.CustomPayloadEvent;
|
||||
@@ -22,6 +24,7 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ApiStatus.Internal // TODO: Decide what I want to have public, Right now it should just be add/remove channels.
|
||||
public class ChannelListManager {
|
||||
@@ -39,7 +42,7 @@ public class ChannelListManager {
|
||||
|
||||
private static void registerListener(CustomPayloadEvent evt) {
|
||||
var ctx = evt.getSource();
|
||||
updateFrom(ctx, evt.getPayload(), ChannelRegistrationChangeEvent.Type.REGISTER);
|
||||
updateFrom(ctx, evt.getPayload(), ChannelRegistrationChangeEvent.Type.REGISTER, ctx.isClientSide());
|
||||
ctx.setPacketHandled(true);
|
||||
// Send the client's channels to the server whenever asked. If we're in the login state then our login wrapper will unwrap for us.
|
||||
if (ctx.isClientSide())
|
||||
@@ -48,7 +51,7 @@ public class ChannelListManager {
|
||||
|
||||
private static void unregisterListener(CustomPayloadEvent evt) {
|
||||
var ctx = evt.getSource();
|
||||
updateFrom(ctx, evt.getPayload(), ChannelRegistrationChangeEvent.Type.UNREGISTER);
|
||||
updateFrom(ctx, evt.getPayload(), ChannelRegistrationChangeEvent.Type.UNREGISTER, ctx.isClientSide());
|
||||
ctx.setPacketHandled(true);
|
||||
}
|
||||
|
||||
@@ -100,7 +103,7 @@ public class ChannelListManager {
|
||||
channel.send(buf, connection);
|
||||
}
|
||||
|
||||
private static void updateFrom(CustomPayloadEvent.Context source, FriendlyByteBuf buffer, final ChannelRegistrationChangeEvent.Type changeType) {
|
||||
private static void updateFrom(CustomPayloadEvent.Context source, FriendlyByteBuf buffer, final ChannelRegistrationChangeEvent.Type changeType, boolean isClient) {
|
||||
byte[] data = new byte[Math.max(buffer.readableBytes(), 0)];
|
||||
buffer.readBytes(data);
|
||||
var channels = new HashSet<String>();
|
||||
@@ -133,11 +136,20 @@ public class ChannelListManager {
|
||||
|
||||
ForgeEventFactory.onChannelRegistrationChange(source.getConnection(), changeType, changed);
|
||||
|
||||
// It is a known issue, that I don't really care about, that we don't get the true in-memory size of each channel due to character encoding.
|
||||
// A sane limit of 1KB will be enough to catch any malicious actors even with non-8-bit strings.
|
||||
var list = NetworkContext.get(source.getConnection());
|
||||
for (var channel : changed) {
|
||||
boolean fire = changeType == ChannelRegistrationChangeEvent.Type.UNREGISTER
|
||||
? list.remoteChannels.remove(channel)
|
||||
: list.remoteChannels.add(channel);
|
||||
boolean fire = false;
|
||||
if (changeType == ChannelRegistrationChangeEvent.Type.UNREGISTER) {
|
||||
fire = list.remoteChannels.remove(channel);
|
||||
if (fire)
|
||||
list.remoteChannelsSize -= channel.toString().length();
|
||||
} else {
|
||||
fire = list.remoteChannels.add(channel);
|
||||
if (fire)
|
||||
list.remoteChannelsSize += channel.toString().length();
|
||||
}
|
||||
|
||||
if (fire) {
|
||||
var target = NetworkRegistry.findTarget(channel);
|
||||
@@ -145,5 +157,21 @@ public class ChannelListManager {
|
||||
target.registrationChange(changeType == ChannelRegistrationChangeEvent.Type.REGISTER);
|
||||
}
|
||||
}
|
||||
|
||||
if (list.remoteChannelsSize >= NetworkRegistry.getChannelMemorySize() && list.remoteChannelsSize >= ForgeConfig.COMMON.channelListLimit.get()) {
|
||||
LOGGER.warn("Received too many network channels, {} channels using {} bytes, Limit is currently {}, to change edit the Forge {} in config\forge_common.toml",
|
||||
list.remoteChannels.size(), list.remoteChannelsSize, ForgeConfig.COMMON.channelListLimit.get(), ForgeConfig.COMMON.channelListLimit.getPath().stream().collect(Collectors.joining("."))
|
||||
);
|
||||
|
||||
if (isClient) {
|
||||
// We don't want to allow the server to screw with us, so just remove the added channels
|
||||
for (var channel : changed) {
|
||||
if (list.remoteChannels.remove(channel))
|
||||
list.remoteChannelsSize -= channel.toString().length();
|
||||
}
|
||||
} else {
|
||||
source.getSender().connection.disconnect(Component.translatable("forge.server.channellistlimit.kick"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -111,6 +111,8 @@ public class NetworkContext {
|
||||
|
||||
@ApiStatus.Internal
|
||||
Set<ResourceLocation> remoteChannels = new HashSet<>();
|
||||
@ApiStatus.Internal
|
||||
long remoteChannelsSize = 0;
|
||||
private Set<ResourceLocation> remoteChannelsView = Collections.unmodifiableSet(this.remoteChannels);
|
||||
|
||||
@ApiStatus.Internal
|
||||
|
||||
@@ -41,7 +41,8 @@ public class NetworkRegistry {
|
||||
static final Logger LOGGER = LogManager.getLogger();
|
||||
static final Marker NETREGISTRY = MarkerManager.getMarker("NETREGISTRY");
|
||||
|
||||
static Map<ResourceLocation, NetworkInstance> instances = Collections.synchronizedMap(new HashMap<>());
|
||||
private static Map<ResourceLocation, NetworkInstance> instances = Collections.synchronizedMap(new HashMap<>());
|
||||
private static long memorySize = 0;
|
||||
|
||||
public static boolean acceptsVanillaClientConnections() {
|
||||
return (instances.isEmpty() || listRejectedVanillaMods(n -> n.clientAcceptedVersions).isEmpty()) && DataPackRegistriesHooks.getSyncedCustomRegistries().isEmpty();
|
||||
@@ -179,4 +180,30 @@ public class NetworkRegistry {
|
||||
instances.forEach((k, v) -> ret.put(k, v.getNetworkProtocolVersion()));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static long getChannelMemorySize() {
|
||||
return memorySize;
|
||||
}
|
||||
|
||||
static void register(NetworkInstance instance) {
|
||||
checkLock(instance);
|
||||
|
||||
var name = instance.getChannelName();
|
||||
if (NetworkRegistry.instances.containsKey(name))
|
||||
error("Channel " + name + " already registered.");
|
||||
else
|
||||
memorySize += name.toString().length(); // This can fail if people don't use our api. So this is a best effort.
|
||||
|
||||
instances.put(name, instance);
|
||||
}
|
||||
|
||||
private static void checkLock(NetworkInstance instance) {
|
||||
if (NetworkRegistry.lock)
|
||||
error("Attempted to register channel " + instance.getChannelName() + " even though registry phase is over");
|
||||
}
|
||||
|
||||
private static void error(String message) {
|
||||
NetworkRegistry.LOGGER.error(NetworkRegistry.NETREGISTRY, message);
|
||||
throw new IllegalArgumentException(message);
|
||||
}
|
||||
}
|
||||
@@ -336,7 +336,7 @@ public class GameData
|
||||
if (forgeRegistry != null)
|
||||
forgeRegistry.freeze();
|
||||
LOGGER.debug(REGISTRIES, "Applying holder lookups: {}", registryKey.location());
|
||||
ObjectHolderRegistry.applyObjectHolders(registryKey.location()::equals);
|
||||
ObjectHolderRegistry.applyObjectHolders(registryKey.location());
|
||||
LOGGER.debug(REGISTRIES, "Holder lookups applied: {}", registryKey.location());
|
||||
} catch (Throwable t)
|
||||
{
|
||||
|
||||
@@ -19,7 +19,7 @@ import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
class ObjectHolderRef implements Consumer<Predicate<ResourceLocation>>
|
||||
class ObjectHolderRef implements Consumer<Predicate<ResourceLocation>>, Runnable
|
||||
{
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
private final Field field;
|
||||
@@ -87,7 +87,12 @@ class ObjectHolderRef implements Consumer<Predicate<ResourceLocation>>
|
||||
{
|
||||
if (registry == null || !filter.test(registry.getRegistryName()))
|
||||
return;
|
||||
run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
Object thing;
|
||||
if (registry.containsKey(injectedObject))
|
||||
{
|
||||
|
||||
@@ -7,10 +7,11 @@ package net.minecraftforge.registries;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
@@ -40,12 +41,24 @@ public class ObjectHolderRegistry
|
||||
* The internal list is backed by a HashSet so it is HIGHLY recommended you implement a proper equals
|
||||
* and hashCode function to de-duplicate callers here.
|
||||
* The default @ObjectHolder implementation uses the hashCode/equals for the field the annotation is on.
|
||||
*
|
||||
* @deprecated Use {{@link #addHandler(ResourceLocation, Runnable)} if you are only targeting a single registry
|
||||
*/
|
||||
public static synchronized void addHandler(Consumer<Predicate<ResourceLocation>> ref)
|
||||
{
|
||||
objectHolders.add(ref);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Registers a handler that is run after the named registry has been injected and finalized.
|
||||
* Handlers are kept in a HashSet per registry, so the same instance is only ever registered once.
|
||||
*/
|
||||
public static synchronized void addHandler(ResourceLocation registry, Runnable ref)
|
||||
{
|
||||
namedHandlers.computeIfAbsent(registry, id -> new HashSet<>()).add(ref);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removed the specified handler from the notification list.
|
||||
*
|
||||
@@ -60,12 +73,36 @@ public class ObjectHolderRegistry
|
||||
return objectHolders.remove(ref);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a handler registered with {@link #addHandler(ResourceLocation, Runnable)}.
|
||||
*
|
||||
* @return true if handler was matched and removed.
|
||||
*/
|
||||
public static synchronized boolean removeHandler(ResourceLocation registry, Runnable ref)
|
||||
{
|
||||
var handlers = namedHandlers.get(registry);
|
||||
return handlers != null && handlers.remove(ref);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a check that is run once, before the handlers of the next holder pass.
|
||||
* Used to report holders that point at a registry that does not exist.
|
||||
*/
|
||||
public static synchronized void addValidator(Runnable ref)
|
||||
{
|
||||
validators.add(ref);
|
||||
}
|
||||
|
||||
//==============================================================
|
||||
// Everything below is internal, do not use.
|
||||
//==============================================================
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
private static final Set<Consumer<Predicate<ResourceLocation>>> objectHolders = new HashSet<>();
|
||||
// Order is not guaranteed, the old implementation kept every handler in one HashSet.
|
||||
private static final Map<ResourceLocation, Set<Runnable>> namedHandlers = new HashMap<>();
|
||||
private static final List<Runnable> validators = new ArrayList<>();
|
||||
|
||||
private static final Type OBJECT_HOLDER = Type.getType(ObjectHolder.class);
|
||||
private static final Type MOD = Type.getType(Mod.class);
|
||||
// Hardcoded list of vanilla classes that should have object holders for each field of the given registry type.
|
||||
@@ -83,48 +120,59 @@ public class ObjectHolderRegistry
|
||||
public static void findObjectHolders()
|
||||
{
|
||||
LOGGER.debug(ForgeRegistry.REGISTRIES,"Processing ObjectHolder annotations");
|
||||
final List<ModFileScanData.AnnotationData> annotations = ModList.get().getAllScanData().stream()
|
||||
.map(ModFileScanData::getAnnotations)
|
||||
.flatMap(Collection::stream)
|
||||
.filter(a -> OBJECT_HOLDER.equals(a.annotationType()) || MOD.equals(a.annotationType()))
|
||||
.toList();
|
||||
final HashMap<Type, String> classModIds = new HashMap<>();
|
||||
final List<ModFileScanData.AnnotationData> annotations = new ArrayList<>();
|
||||
|
||||
if (annotations.stream().noneMatch(a -> OBJECT_HOLDER.equals(a.annotationType())))
|
||||
return; // No object holders found, skip the rest of the processing
|
||||
for (var data : ModList.get().getAllScanData()) {
|
||||
for (var annotation : data.getAnnotations()) {
|
||||
if (OBJECT_HOLDER.equals(annotation.annotationType())) {
|
||||
annotations.add(annotation);
|
||||
}
|
||||
// Gather all @Mod classes so that @ObjectHolder's in those classes don't need to specify the mod id; modder convenience
|
||||
else if (MOD.equals(annotation.annotationType())) {
|
||||
classModIds.put(annotation.clazz(), (String)annotation.annotationData().get("value"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// In theory when a mod is installed, we want to make sure we can update vanilla fields that are overriden by mods.
|
||||
// However, due to Mojang registry changes, and this code being old and having an oversight, this short circuit is expected behavior
|
||||
// So Lets just ignore the potential pitfalls for if they ever come up.
|
||||
if (annotations.isEmpty()) {
|
||||
return; // No object holders found, skip the rest of the processing,
|
||||
}
|
||||
|
||||
Map<Type, String> classModIds = Maps.newHashMap();
|
||||
Map<Type, Class<?>> classCache = Maps.newHashMap();
|
||||
|
||||
// Gather all @Mod classes so that @ObjectHolder's in those classes don't need to specify the mod id; modder convenience
|
||||
annotations.stream()
|
||||
.filter(a -> MOD.equals(a.annotationType()))
|
||||
.forEach(data -> classModIds.put(data.clazz(), (String)data.annotationData().get("value")));
|
||||
|
||||
// Validate all the vanilla class-level object holders then scan those first
|
||||
VANILLA_OBJECT_HOLDERS.forEach(data -> {
|
||||
try
|
||||
{
|
||||
for (var data : VANILLA_OBJECT_HOLDERS) {
|
||||
try {
|
||||
Class<?> holderClass = Class.forName(data.holderClass(), true, ObjectHolderRegistry.class.getClassLoader());
|
||||
Class<?> registryClass = Class.forName(data.registryType(), true, ObjectHolderRegistry.class.getClassLoader());
|
||||
|
||||
Type holderType = Type.getType(holderClass);
|
||||
classCache.put(holderType, holderClass);
|
||||
scanTarget(classModIds, classCache, holderType, null, registryClass, data.registryName(), "minecraft", true, true);
|
||||
}
|
||||
catch (ClassNotFoundException e)
|
||||
{
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new RuntimeException("Vanilla class not found, should not be possible", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Scan actual fields annotated with @ObjectHolder second
|
||||
annotations.stream()
|
||||
.filter(a -> OBJECT_HOLDER.equals(a.annotationType())).filter(a -> a.targetType() == ElementType.FIELD)
|
||||
.forEach(data -> scanTarget(classModIds, classCache, data.clazz(),
|
||||
data.memberName(), null, (String)data.annotationData().get("registryName"),
|
||||
(String)data.annotationData().get("value"), false, false));
|
||||
for (var data : annotations) {
|
||||
if (data.targetType() == ElementType.FIELD) {
|
||||
var registry = (String)data.annotationData().get("registryName");
|
||||
var name = (String)data.annotationData().get("value");
|
||||
scanTarget(classModIds, classCache, data.clazz(), data.memberName(), null, registry, name, false, false);
|
||||
}
|
||||
// This is where we WOULD scan for class targets, but Shrimp broke that in 1.19 and so I don't want to re-add a feature that apparently nobody used.
|
||||
}
|
||||
|
||||
LOGGER.debug(ForgeRegistry.REGISTRIES,"Found {} ObjectHolder annotations", objectHolders.size());
|
||||
int count = objectHolders.size();
|
||||
for (var set : namedHandlers.values())
|
||||
count += set.size();
|
||||
|
||||
LOGGER.debug(ForgeRegistry.REGISTRIES, "Found {} ObjectHolder annotations", count);
|
||||
}
|
||||
|
||||
private static void scanTarget(Map<Type, String> classModIds, Map<Type, Class<?>> classCache, Type type,
|
||||
@@ -149,9 +197,10 @@ public class ObjectHolderRegistry
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
ResourceLocation regName = ResourceLocation.parse(registryName);
|
||||
if (isClass)
|
||||
{
|
||||
scanClassForFields(classModIds, type, new ResourceLocation(registryName), registryClass, value, clazz, extractFromValue);
|
||||
scanClassForFields(classModIds, type, regName, registryClass, value, clazz, extractFromValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -168,9 +217,9 @@ public class ObjectHolderRegistry
|
||||
try
|
||||
{
|
||||
Field f = clazz.getDeclaredField(annotationTarget);
|
||||
ObjectHolderRef ref = ObjectHolderRef.create(new ResourceLocation(registryName), f, value, extractFromValue);
|
||||
ObjectHolderRef ref = ObjectHolderRef.create(regName, f, value, extractFromValue);
|
||||
if (ref != null)
|
||||
addHandler(ref);
|
||||
addHandler(regName, ref);
|
||||
}
|
||||
catch (NoSuchFieldException ex)
|
||||
{
|
||||
@@ -191,7 +240,7 @@ public class ObjectHolderRegistry
|
||||
continue;
|
||||
ObjectHolderRef ref = ObjectHolderRef.create(registryName, f, value + ':' + f.getName().toLowerCase(Locale.ENGLISH), extractFromExistingValues);
|
||||
if (ref != null)
|
||||
addHandler(ref);
|
||||
addHandler(registryName, ref);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,38 +256,115 @@ public class ObjectHolderRegistry
|
||||
throw new IllegalStateException("No registry name was declared for " + declaration);
|
||||
}
|
||||
|
||||
public static void applyObjectHolders()
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
private static final Predicate<ResourceLocation> ALWAYS_TRUE = name -> true;
|
||||
public static void applyObjectHolders() {
|
||||
try {
|
||||
LOGGER.debug(ForgeRegistry.REGISTRIES, "Applying holder lookups");
|
||||
applyObjectHolders(key -> true);
|
||||
var exceptions = new ArrayList<Exception>();
|
||||
validate(exceptions);
|
||||
// Call named registries
|
||||
for (var handlers : namedHandlers.values())
|
||||
apply(handlers, exceptions);
|
||||
|
||||
// Call legacy for backward compatibility
|
||||
if (!objectHolders.isEmpty())
|
||||
apply(ALWAYS_TRUE, objectHolders, exceptions);
|
||||
|
||||
throwIfNeeded(exceptions);
|
||||
LOGGER.debug(ForgeRegistry.REGISTRIES, "Holder lookups applied");
|
||||
} catch (RuntimeException e)
|
||||
{
|
||||
} catch (RuntimeException e) {
|
||||
// It is more important that the calling contexts continue without exception to prevent further cascading errors
|
||||
LOGGER.error("", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #applyObjectHolders(ResourceLocation)}
|
||||
*/
|
||||
public static void applyObjectHolders(Predicate<ResourceLocation> filter)
|
||||
{
|
||||
RuntimeException aggregate = new RuntimeException("Failed to apply some object holders, see suppressed exceptions for details");
|
||||
objectHolders.forEach(objectHolder -> {
|
||||
try
|
||||
{
|
||||
objectHolder.accept(filter);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
aggregate.addSuppressed(e);
|
||||
}
|
||||
});
|
||||
LOGGER.debug(ForgeRegistry.REGISTRIES, "Apply filtered holder lookups");
|
||||
var exceptions = new ArrayList<Exception>();
|
||||
validate(exceptions);
|
||||
|
||||
if (aggregate.getSuppressed().length > 0)
|
||||
{
|
||||
throw aggregate;
|
||||
// call new registries if needed
|
||||
for (var entry : namedHandlers.entrySet()) {
|
||||
if (filter.test(entry.getKey())) {
|
||||
apply(entry.getValue(), exceptions);
|
||||
}
|
||||
}
|
||||
|
||||
// Call legacy
|
||||
if (!objectHolders.isEmpty())
|
||||
apply(filter, objectHolders, exceptions);
|
||||
|
||||
throwIfNeeded(exceptions);
|
||||
LOGGER.debug(ForgeRegistry.REGISTRIES, "Holder lookups applied");
|
||||
}
|
||||
|
||||
/** Runs the handlers of one registry. */
|
||||
public static void applyObjectHolders(ResourceLocation registry) {
|
||||
var exceptions = new ArrayList<Exception>();
|
||||
validate(exceptions);
|
||||
|
||||
var handlers = namedHandlers.get(registry);
|
||||
if (handlers != null)
|
||||
apply(handlers, exceptions);
|
||||
|
||||
if (!objectHolders.isEmpty())
|
||||
apply(registry::equals, objectHolders, exceptions);
|
||||
|
||||
throwIfNeeded(exceptions);
|
||||
}
|
||||
|
||||
// Each validator runs once, at the first holder pass after it was registered.
|
||||
private static void validate(List<Exception> exceptions) {
|
||||
List<Runnable> pending;
|
||||
synchronized (ObjectHolderRegistry.class) {
|
||||
if (validators.isEmpty())
|
||||
return;
|
||||
pending = new ArrayList<>(validators);
|
||||
validators.clear();
|
||||
}
|
||||
|
||||
for (var validator : pending) {
|
||||
try {
|
||||
validator.run();
|
||||
} catch (Exception e) {
|
||||
exceptions.add(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void apply(Set<Runnable> handlers, List<Exception> exceptions) {
|
||||
for (var handler : handlers) {
|
||||
try {
|
||||
handler.run();
|
||||
} catch (Exception e) {
|
||||
exceptions.add(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void apply(Predicate<ResourceLocation> filter, Set<Consumer<Predicate<ResourceLocation>>> handlers, List<Exception> exceptions) {
|
||||
for (var handler : handlers) {
|
||||
try {
|
||||
handler.accept(filter);
|
||||
} catch (Exception e) {
|
||||
exceptions.add(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void throwIfNeeded(List<Exception> exceptions) {
|
||||
if (exceptions.isEmpty())
|
||||
return;
|
||||
|
||||
var aggregate = new RuntimeException("Failed to apply some object holders, see suppressed exceptions for details");
|
||||
for (var e : exceptions)
|
||||
aggregate.addSuppressed(e);
|
||||
throw aggregate;
|
||||
}
|
||||
|
||||
private record VanillaObjectHolderData(String holderClass, String registryName, String registryType) {}
|
||||
|
||||
@@ -26,8 +26,7 @@ public final class RegistryObject<T> implements Supplier<T>
|
||||
@Nullable
|
||||
private final ResourceLocation name;
|
||||
@Nullable
|
||||
private ResourceKey<T> key;
|
||||
private final boolean optionalRegistry;
|
||||
private final ResourceKey<T> key;
|
||||
@Nullable
|
||||
private T value;
|
||||
@Nullable
|
||||
@@ -135,7 +134,6 @@ public final class RegistryObject<T> implements Supplier<T>
|
||||
private RegistryObject() {
|
||||
this.name = null;
|
||||
this.key = null;
|
||||
this.optionalRegistry = false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -145,12 +143,7 @@ public final class RegistryObject<T> implements Supplier<T>
|
||||
throw new IllegalArgumentException("Invalid registry argument, must not be null");
|
||||
this.name = name;
|
||||
this.key = (ResourceKey<T>) ResourceKey.create(registry.getRegistryKey(), name);
|
||||
this.optionalRegistry = false;
|
||||
ObjectHolderRegistry.addHandler(pred ->
|
||||
{
|
||||
if (pred.test(registry.getRegistryName()))
|
||||
this.updateReference((IForgeRegistry<? extends T>) registry);
|
||||
});
|
||||
ObjectHolderRegistry.addHandler(registry.getRegistryName(), () -> this.updateReference((IForgeRegistry<? extends T>) registry));
|
||||
this.updateReference((IForgeRegistry<? extends T>) registry);
|
||||
}
|
||||
|
||||
@@ -158,31 +151,14 @@ public final class RegistryObject<T> implements Supplier<T>
|
||||
{
|
||||
this.name = name;
|
||||
this.key = ResourceKey.create(ResourceKey.createRegistryKey(registryName), name);
|
||||
this.optionalRegistry = optionalRegistry;
|
||||
final Throwable callerStack = new Throwable("Calling Site from mod: " + modid);
|
||||
ObjectHolderRegistry.addHandler(new Consumer<>()
|
||||
{
|
||||
private boolean registryExists = false;
|
||||
private boolean invalidRegistry = false;
|
||||
|
||||
@Override
|
||||
public void accept(Predicate<ResourceLocation> pred)
|
||||
{
|
||||
if (invalidRegistry)
|
||||
return;
|
||||
if (!RegistryObject.this.optionalRegistry && !registryExists)
|
||||
{
|
||||
if (!registryExists(registryName))
|
||||
{
|
||||
invalidRegistry = true;
|
||||
throw new IllegalStateException("Unable to find registry with key " + registryName + " for mod \"" + modid + "\". Check the 'caused by' to see further stack.", callerStack);
|
||||
}
|
||||
registryExists = true;
|
||||
}
|
||||
if (pred.test(registryName))
|
||||
RegistryObject.this.updateReference(registryName);
|
||||
}
|
||||
});
|
||||
if (!optionalRegistry) {
|
||||
final Throwable callerStack = new Throwable("Calling Site from mod: " + modid);
|
||||
ObjectHolderRegistry.addValidator(() -> {
|
||||
if (!registryExists(registryName))
|
||||
throw new IllegalStateException("Unable to find registry with key " + registryName + " for mod \"" + modid + "\". Check the 'caused by' to see further stack.", callerStack);
|
||||
});
|
||||
}
|
||||
ObjectHolderRegistry.addHandler(registryName, () -> this.updateReference(registryName));
|
||||
this.updateReference(registryName);
|
||||
}
|
||||
|
||||
@@ -485,8 +461,8 @@ public final class RegistryObject<T> implements Supplier<T>
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (this == obj) return true;
|
||||
if (obj instanceof RegistryObject) {
|
||||
return Objects.equals(((RegistryObject<?>)obj).name, name);
|
||||
if (obj instanceof RegistryObject<?> o) {
|
||||
return o.key == key && Objects.equals(o.name, name);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -204,6 +204,10 @@
|
||||
"forge.configgui.stabilizeDirectionGetNearest": "Stabilize Direction Get Nearest",
|
||||
"forge.configgui.stabilizeDirectionGetNearest.tooltip": "When enabled, a slightly biased Direction#getNearest calculation will be used to prevent normal fighting on 45 degree angle faces.",
|
||||
|
||||
"forge.configgui.channellistlimit": "Channel List Size Limit",
|
||||
"forge.configgui.channellistlimit.tooltip": "Maximum size to use to store the channel list for a network connection. Some malicious actors may use this to use up all your memory. A sane default is 1KB (1024), but may need to be expanded for larger modpacks.",
|
||||
"forge.server.channellistlimit.kick": "Received too many networking channels",
|
||||
|
||||
"forge.controlsgui.shift": "SHIFT + %s",
|
||||
"forge.controlsgui.control": "CTRL + %s",
|
||||
"forge.controlsgui.control.mac": "CMD + %s",
|
||||
|
||||
Reference in new issue
Block a user