mirror of
https://github.com/MinecraftForge/MinecraftForge.git
synced 2026-11-14 01:04:24 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b518c804fb | ||
|
|
e50a0ffc6c | ||
|
|
7ea0a41fe2 | ||
|
|
f3d16621c0 | ||
|
|
aa55a6a831 | ||
|
|
3449217c65 | ||
|
|
41609a03d2 | ||
|
|
9b8e5c7109 | ||
|
|
f05ad3afd0 | ||
|
|
6a615f1a4b | ||
|
|
4376cd6b85 | ||
|
|
d17a111dc0 | ||
|
|
1b99b2cea4 | ||
|
|
4ef6695a30 | ||
|
|
0e6341a23e | ||
|
|
17ce723edf | ||
|
|
fc80e30289 | ||
|
|
ecd1491f96 | ||
|
|
8f7fbc76e1 | ||
|
|
ec405958c9 | ||
|
|
e2ff990601 | ||
|
|
8443262e39 |
No files matched your search
+2
-2
@@ -7,8 +7,8 @@ plugins {
|
||||
id 'net.minecraftforge.gradleutils' version '[2.6.4,2.7)'
|
||||
id 'eclipse'
|
||||
id 'de.undercouch.download' version '5.4.0'
|
||||
id 'net.minecraftforge.gradle.patcher' version '[6.0.22,6.2)' apply false
|
||||
id 'net.minecraftforge.gradle.mcp' version '[6.0.22,6.2)' apply false
|
||||
id 'net.minecraftforge.gradle.patcher' version '[6.0.46,6.2)' apply false
|
||||
id 'net.minecraftforge.gradle.mcp' version '[6.0.46,6.2)' apply false
|
||||
id 'net.minecraftforge.gradlejarsigner' version '1.0.4'
|
||||
id 'org.barfuin.gradle.taskinfo' version '2.1.0'
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ dependencies {
|
||||
implementation 'org.ow2.asm:asm:9.8'
|
||||
implementation 'org.ow2.asm:asm-tree:9.8'
|
||||
implementation 'net.minecraftforge:srgutils:0.5.10'
|
||||
implementation 'net.minecraftforge:JarJarMetadata:0.3.27'
|
||||
implementation 'commons-io:commons-io:2.13.0'
|
||||
implementation 'com.google.code.gson:gson:2.10.1'
|
||||
implementation 'org.eclipse.jgit:org.eclipse.jgit:6.7.0.202309050840-r'
|
||||
|
||||
@@ -0,0 +1,415 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.forge.tasks;
|
||||
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import net.minecraftforge.jarjar.metadata.ContainedJarIdentifier;
|
||||
import net.minecraftforge.jarjar.metadata.ContainedJarMetadata;
|
||||
import net.minecraftforge.jarjar.metadata.ContainedVersion;
|
||||
import net.minecraftforge.jarjar.metadata.Metadata;
|
||||
import net.minecraftforge.jarjar.metadata.MetadataIOHandler;
|
||||
import net.minecraftforge.jarjar.metadata.json.ArtifactVersionSerializer;
|
||||
import net.minecraftforge.jarjar.metadata.json.ContainedJarIdentifierSerializer;
|
||||
import net.minecraftforge.jarjar.metadata.json.ContainedJarMetadataSerializer;
|
||||
import net.minecraftforge.jarjar.metadata.json.ContainedVersionSerializer;
|
||||
import net.minecraftforge.jarjar.metadata.json.MetadataSerializer;
|
||||
import net.minecraftforge.jarjar.metadata.json.VersionRangeSerializer;
|
||||
import org.apache.maven.artifact.versioning.ArtifactVersion;
|
||||
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
|
||||
import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
|
||||
import org.apache.maven.artifact.versioning.VersionRange;
|
||||
import org.codehaus.groovy.runtime.InvokerHelper;
|
||||
import org.gradle.api.Action;
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.artifacts.ConfigurationContainer;
|
||||
import org.gradle.api.artifacts.Dependency;
|
||||
import org.gradle.api.artifacts.ExternalModuleDependency;
|
||||
import org.gradle.api.artifacts.FileCollectionDependency;
|
||||
import org.gradle.api.artifacts.MinimalExternalModuleDependency;
|
||||
import org.gradle.api.artifacts.ModuleDependency;
|
||||
import org.gradle.api.artifacts.ModuleIdentifier;
|
||||
import org.gradle.api.artifacts.ModuleVersionIdentifier;
|
||||
import org.gradle.api.artifacts.ResolvedArtifact;
|
||||
import org.gradle.api.file.ProjectLayout;
|
||||
import org.gradle.api.file.RegularFileProperty;
|
||||
import org.gradle.api.provider.Provider;
|
||||
import org.gradle.api.provider.SetProperty;
|
||||
import org.gradle.api.tasks.Input;
|
||||
import org.gradle.api.tasks.OutputFile;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
// TODO SUPER SUPER SUPER UGLY, CLEAN UP IN FORGEDEV 7
|
||||
@Deprecated(forRemoval = true) // Will be moved to JarJar plugin in ForgeDev 7
|
||||
public abstract class JarJarMetadataOptions extends DefaultTask {
|
||||
private static final Gson GSON = new GsonBuilder()
|
||||
.registerTypeAdapter(VersionRange.class, new VersionRangeSerializer())
|
||||
.registerTypeAdapter(ArtifactVersion.class, new ArtifactVersionSerializer())
|
||||
.registerTypeAdapter(DefaultArtifactVersion.class, new ArtifactVersionSerializer())
|
||||
.registerTypeAdapter(ContainedJarIdentifier.class, new ContainedJarIdentifierSerializer())
|
||||
.registerTypeAdapter(ContainedJarMetadata.class, new ContainedJarMetadataSerializer())
|
||||
.registerTypeAdapter(ContainedVersion.class, new ContainedVersionSerializer())
|
||||
.registerTypeAdapter(Metadata.class, new MetadataSerializer())
|
||||
.setPrettyPrinting()
|
||||
.create();
|
||||
|
||||
protected abstract @Input SetProperty<ResolvedDependencyInfoImpl> getResolvedDependencies();
|
||||
|
||||
protected abstract @OutputFile RegularFileProperty getMetadataFile();
|
||||
|
||||
protected abstract @Inject ProjectLayout getLayout();
|
||||
|
||||
// NOTE: I'm not adding a non-provider version. please just use the version catalog entries for now.
|
||||
public void add(Provider<MinimalExternalModuleDependency> dependency, Action<? super ResolvedDependencyInfo> action) {
|
||||
this.getResolvedDependencies().add(dependency.map(d -> {
|
||||
var ret = ResolvedDependencyInfoImpl.from(this.getProject().getConfigurations(), d);
|
||||
action.execute(ret);
|
||||
return ret;
|
||||
}));
|
||||
}
|
||||
|
||||
@Inject
|
||||
public JarJarMetadataOptions() {
|
||||
this.getMetadataFile().convention(this.getLayout().getBuildDirectory().file(this.getName() + "/options.json"));
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
protected void exec() {
|
||||
record ForgeLocaterOptions(String resource, String layer, String id, List<ContainedJarMetadata> deps, ContainedJarMetadata meta, boolean nested) { }
|
||||
|
||||
var resolved = this.getResolvedDependencies().get();
|
||||
var jars = new ArrayList<ForgeLocaterOptions>(resolved.size());
|
||||
for (var dependency : resolved) {
|
||||
var deps = new ArrayList<ContainedJarMetadata>();
|
||||
try (var zip = new ZipFile(dependency.artifact)) {
|
||||
var entry = zip.getEntry("META-INF/jarjar/metadata.json");
|
||||
if (entry != null) {
|
||||
try (var stream = zip.getInputStream(entry)) {
|
||||
var meta = MetadataIOHandler.fromStream(stream).orElse(null);
|
||||
if (meta == null)
|
||||
throw new IllegalStateException("Corrupt metadata.json in " + dependency.artifact.getAbsolutePath());
|
||||
for (var dep : meta.jars())
|
||||
deps.add(new ContainedJarMetadata(dep.identifier(), dep.version(), "", dep.isObfuscated()));
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
jars.add(new ForgeLocaterOptions(
|
||||
dependency.resource,
|
||||
dependency.layer,
|
||||
dependency.identifier,
|
||||
deps,
|
||||
new ContainedJarMetadata(
|
||||
new ContainedJarIdentifier(validateGroup(dependency), dependency.module.getName()),
|
||||
new ContainedVersion(null, parseVersion(dependency)),
|
||||
Objects.requireNonNull(dependency.path, "Dependency path is unspecified: " + dependency.asString),
|
||||
false
|
||||
),
|
||||
dependency.nested
|
||||
));
|
||||
}
|
||||
|
||||
try {
|
||||
record Meta(List<ForgeLocaterOptions> options){}
|
||||
|
||||
Files.writeString(
|
||||
this.getMetadataFile().getAsFile().get().toPath(),
|
||||
GSON.toJson(new Meta(jars), Meta.class)
|
||||
);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private String validateGroup(ResolvedDependencyInfoImpl dependency) {
|
||||
try {
|
||||
return Objects.requireNonNull(dependency.module.getGroup());
|
||||
} catch (NullPointerException e) {
|
||||
throw new IllegalArgumentException("Module dependency has no group: " + dependency.asString, e);
|
||||
}
|
||||
}
|
||||
|
||||
private ArtifactVersion parseVersion(ResolvedDependencyInfoImpl resolved) {
|
||||
try {
|
||||
return VersionRange.createFromVersionSpec(Objects.requireNonNull(resolved.version)).getRecommendedVersion();
|
||||
} catch (InvalidVersionSpecificationException e) {
|
||||
throw new IllegalArgumentException("Version is invalid for: " + resolved.asString, e);
|
||||
} catch (NullPointerException e) {
|
||||
throw new IllegalArgumentException("Version is unspecified for: " + resolved.asString, e);
|
||||
}
|
||||
}
|
||||
|
||||
private VersionRange parseVersionRange(ResolvedDependencyInfoImpl dependency) {
|
||||
if (dependency.hasManuallySpecifiedRange) {
|
||||
try {
|
||||
return VersionRange.createFromVersionSpec(dependency.versionRange);
|
||||
} catch (InvalidVersionSpecificationException e) {
|
||||
throw new IllegalArgumentException("Version is invalid for: " + dependency.asString, e);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
return VersionRange.createFromVersionSpec("[%s,)".formatted(Objects.requireNonNull(dependency.versionRange)));
|
||||
} catch (InvalidVersionSpecificationException e) {
|
||||
throw new IllegalArgumentException("Version range is invalid for: " + dependency.asString, e);
|
||||
} catch (NullPointerException e) {
|
||||
throw new IllegalArgumentException("Version is unspecified for: " + dependency.asString, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface ResolvedDependencyInfo {
|
||||
void containedJarMetadata(Action<? super ContainedJarMetadataInfo> action);
|
||||
|
||||
void setResource(String resource);
|
||||
|
||||
void setLayer(String layer);
|
||||
|
||||
void setId(String identifier);
|
||||
|
||||
void setNested(boolean nested);
|
||||
|
||||
interface ContainedJarMetadataInfo {
|
||||
void setGroup(String group);
|
||||
|
||||
void setName(String name);
|
||||
|
||||
void setPath(String version);
|
||||
}
|
||||
}
|
||||
|
||||
static final class ResolvedDependencyInfoImpl implements ResolvedDependencyInfo, ResolvedDependencyInfo.ContainedJarMetadataInfo, Serializable {
|
||||
private static final @Serial long serialVersionUID = -7577318115877822993L;
|
||||
|
||||
final MinimalModuleVersionIdentifier module;
|
||||
final String version;
|
||||
String versionRange;
|
||||
boolean hasManuallySpecifiedRange;
|
||||
boolean nested;
|
||||
final File artifact;
|
||||
String path;
|
||||
String resource;
|
||||
String layer;
|
||||
String identifier;
|
||||
final String asString;
|
||||
|
||||
public ResolvedDependencyInfoImpl(MinimalModuleVersionIdentifier module, String version, File artifact, String asString) {
|
||||
this.module = module;
|
||||
this.version = version;
|
||||
this.artifact = artifact;
|
||||
this.asString = asString;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void containedJarMetadata(Action<? super ContainedJarMetadataInfo> action) {
|
||||
action.execute(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGroup(String group) {
|
||||
this.module.group = group;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setName(String name) {
|
||||
this.module.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPath(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setResource(String resource) {
|
||||
this.resource = resource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLayer(String layer) {
|
||||
this.layer = layer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setId(String identifier) {
|
||||
this.identifier = identifier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNested(boolean nested) {
|
||||
this.nested = nested;
|
||||
}
|
||||
|
||||
static Set<File> getFiles(Set<ResolvedDependencyInfoImpl> resolvedDependencies) {
|
||||
var ret = new HashSet<File>(resolvedDependencies.size());
|
||||
for (var dependency : resolvedDependencies) {
|
||||
ret.add(dependency.artifact);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ResolvedDependencyInfoImpl from(ConfigurationContainer configurations, Dependency dependency) {
|
||||
var group = dependency.getGroup();
|
||||
var name = dependency.getName();
|
||||
var version = dependency.getVersion();
|
||||
|
||||
if (dependency instanceof FileCollectionDependency filesDependency) {
|
||||
File artifact;
|
||||
try {
|
||||
artifact = filesDependency.getFiles().getSingleFile();
|
||||
} catch (IllegalStateException e) {
|
||||
// TODO fileCollectionDependencyIsNotSingleFile
|
||||
throw e;
|
||||
}
|
||||
|
||||
return new ResolvedDependencyInfoImpl(
|
||||
new MinimalModuleVersionIdentifier(group, name, version),
|
||||
version,
|
||||
artifact,
|
||||
filesDependency.toString()
|
||||
);
|
||||
} else if (dependency instanceof ModuleDependency moduleDependency) {
|
||||
moduleDependency = moduleDependency.copy();
|
||||
if (moduleDependency instanceof ExternalModuleDependency externalModuleDependency) {
|
||||
externalModuleDependency.version(v -> v.strictly(version.toString()));
|
||||
}
|
||||
|
||||
var detachedConfiguration = configurations.detachedConfiguration(moduleDependency);
|
||||
detachedConfiguration.setTransitive(false);
|
||||
|
||||
ResolvedDependencyInfoImpl ret = null;
|
||||
for (var artifact : detachedConfiguration.getResolvedConfiguration().getFirstLevelModuleDependencies().iterator().next().getModuleArtifacts()) {
|
||||
var fileName = getFileName(artifact);
|
||||
if (!fileName.endsWith(".jar"))
|
||||
continue;
|
||||
|
||||
if (ret != null)
|
||||
throw new IllegalArgumentException("Module dependency has too many Jar artifacts: " + moduleDependency);
|
||||
|
||||
ret = new ResolvedDependencyInfoImpl(
|
||||
new MinimalModuleVersionIdentifier(group, name, artifact.getModuleVersion().getId().getVersion()),
|
||||
version,
|
||||
artifact.getFile(),
|
||||
moduleDependency.toString()
|
||||
);
|
||||
}
|
||||
if (ret == null)
|
||||
throw new IllegalArgumentException("Module dependency has no Jar artifacts: " + moduleDependency);
|
||||
|
||||
return ret;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unsupported dependency type: " + dependency.getClass().getName() + " -- " + dependency);
|
||||
}
|
||||
}
|
||||
|
||||
private static String getFileName(ResolvedArtifact artifact) {
|
||||
try {
|
||||
return InvokerHelper.getProperty(artifact.getId(), "fileName").toString();
|
||||
} catch (Throwable e) {
|
||||
// NOTE: Why not just use this to begin with?
|
||||
// ComponentArtifactIdentifier can have a getFileName() method, which doesn't necessarily resolve the file itself.
|
||||
// This allows us to get the name of the file to be used without asking Gradle to download the file.
|
||||
// So, if a file is not a JAR file, we can check the name without actually downloading it.
|
||||
return artifact.getFile().getName();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) return true;
|
||||
if (obj == null || obj.getClass() != this.getClass()) return false;
|
||||
var that = (ResolvedDependencyInfoImpl) obj;
|
||||
return Objects.equals(this.module, that.module) &&
|
||||
Objects.equals(this.version, that.version) &&
|
||||
Objects.equals(this.versionRange, that.versionRange) &&
|
||||
Objects.equals(this.artifact, that.artifact);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(module, version, versionRange, artifact);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ResolvedDependencyInfo[" +
|
||||
"module=" + module + ", " +
|
||||
"fixedVersion=" + version + ", " +
|
||||
"versionRange=" + versionRange + ", " +
|
||||
"artifact=" + artifact + ']';
|
||||
}
|
||||
|
||||
static final class MinimalModuleVersionIdentifier implements ModuleIdentifier, ModuleVersionIdentifier {
|
||||
private static final @Serial long serialVersionUID = -955346236759069739L;
|
||||
|
||||
private String group;
|
||||
private String name;
|
||||
private final String version;
|
||||
|
||||
@Inject
|
||||
public MinimalModuleVersionIdentifier(String group, String name, String version) {
|
||||
this.group = group;
|
||||
this.name = name;
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModuleIdentifier getModule() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getGroup() {
|
||||
return this.group;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return this.version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return this == obj || obj instanceof MinimalModuleVersionIdentifier o
|
||||
&& Objects.equals(this.group, o.group)
|
||||
&& Objects.equals(this.name, o.name)
|
||||
&& Objects.equals(this.version, o.version);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(group, name, version);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MinimalModuleVersionIdentifier[" +
|
||||
"group=" + group + ", " +
|
||||
"name=" + name + ", " +
|
||||
"version=" + version + ']';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -113,6 +113,7 @@ dependencies {
|
||||
// And this is only needed because custom file systems are REQUIRED to be on the boot classloader.
|
||||
// This has ASM/BootStrap/Unsafe all because I haven't gotten around to moving UnionFileSystem out to its own project.
|
||||
bootstrap(libs.jarjar.fs) // JarInJar file system
|
||||
bootstrap(libs.roimfs) // JarInJar File System - FileSystems need to be on boot loader.
|
||||
bootstrap(libs.bundles.jimfs) // In memory file system used for ForgeDev launches
|
||||
bootstrap(libs.securemodules) // Has Union file system in it
|
||||
bootstrap(libs.unsafe) // Needed by securemodules
|
||||
@@ -134,7 +135,9 @@ dependencies {
|
||||
installer(libs.maven.artifact)
|
||||
installer(libs.bundles.terminalconsoleappender)
|
||||
installer(libs.mixin)
|
||||
installer(libs.mixinextras.forge)
|
||||
installer(libs.bundles.jarjar)
|
||||
installer(libs.roimfs)
|
||||
|
||||
installer(project(':fmlcore'))
|
||||
installer(project(':fmlloader'))
|
||||
@@ -299,6 +302,11 @@ tasks.userdevConfig.configure {
|
||||
libraries.add(dep)
|
||||
}
|
||||
|
||||
// TODO [ForgeDev][UserDev] Split API/Runtime elements in userdev config
|
||||
// so we don't gotta keep making exceptions for MixinExtras
|
||||
addCompileDependency(libs.mixinextras.common)
|
||||
addAnnotationProcessorDependency(libs.mixinextras.common)
|
||||
|
||||
inject = '' // We don't have a userdev sourceset anymore. Empty as a gradle workaround...
|
||||
runs {
|
||||
client {
|
||||
|
||||
@@ -38,6 +38,10 @@ tasks.register('generateResources') {
|
||||
dependsOn('writeManifest')
|
||||
}
|
||||
|
||||
tasks.named('processResources') {
|
||||
dependsOn(generateResources)
|
||||
}
|
||||
|
||||
// Make sure out manifests get written before compiling the code, IDEA calls this task if you tell it to use the gradle build.
|
||||
tasks.withType(JavaCompile).configureEach {
|
||||
dependsOn 'generateResources'
|
||||
|
||||
+2
-2
@@ -2,8 +2,8 @@
|
||||
|
||||
MinecraftForge
|
||||
=============
|
||||
[][Download]
|
||||
[][Download]
|
||||
[][Download] [][Discord] [][Patreon]
|
||||
|
||||
Forge is a free, open-source modding API all of your favourite mods use!
|
||||
|
||||
@@ -41,14 +41,14 @@ public class ModList {
|
||||
private List<ModContainer> sortedContainers;
|
||||
|
||||
private ModList(final List<ModFile> modFiles, final List<ModInfo> sortedList) {
|
||||
this.modFiles = modFiles.stream().map(ModFile::getModFileInfo).toList();
|
||||
var modFileInfos = this.modFiles = modFiles.stream().map(ModFile::getModFileInfo).toList();
|
||||
this.sortedList = sortedList.stream().map(IModInfo.class::cast).toList();
|
||||
var byId = new HashMap<String, IModFileInfo>();
|
||||
for (var file : this.modFiles) {
|
||||
for (var file : modFileInfos) {
|
||||
for (var mod : file.getMods())
|
||||
byId.put(mod.getModId(), mod.getOwningFile());
|
||||
}
|
||||
this.fileById = Collections.unmodifiableMap(byId);
|
||||
this.fileById = Map.copyOf(byId);
|
||||
CrashReportCallables.registerCrashCallable("Mod List", this::crashReport);
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ public class ModList {
|
||||
}
|
||||
|
||||
private String crashReport() {
|
||||
return "\n"+applyForEachModFile(this::fileToLine).collect(Collectors.joining("\n\t\t", "\t\t", ""));
|
||||
return "\n" + applyForEachModFile(this::fileToLine).collect(Collectors.joining("\n\t\t", "\t\t", ""));
|
||||
}
|
||||
|
||||
public static ModList of(List<ModFile> modFiles, List<ModInfo> sortedList) {
|
||||
@@ -126,10 +126,9 @@ public class ModList {
|
||||
map(IModFileInfo::getFile).
|
||||
distinct().
|
||||
map(IModFile::getScanResult).
|
||||
collect(Collectors.toList());
|
||||
toList();
|
||||
}
|
||||
return modFileScanData;
|
||||
|
||||
}
|
||||
|
||||
public void forEachModFile(Consumer<IModFile> fileConsumer) {
|
||||
|
||||
@@ -84,20 +84,20 @@ public class ModLoader {
|
||||
private ModList modList;
|
||||
|
||||
private ModLoader() {
|
||||
this.loadingModList = FMLLoader.getLoadingModList();
|
||||
this.loadingExceptions = this.loadingModList.getErrors().stream()
|
||||
var loadingModList = this.loadingModList = FMLLoader.getLoadingModList();
|
||||
var loadingExceptions = this.loadingExceptions = loadingModList.getErrors().stream()
|
||||
.flatMap(ModLoadingException::fromEarlyException)
|
||||
.collect(Collectors.toList());
|
||||
this.loadingWarnings = this.loadingModList.getBrokenFiles().stream()
|
||||
this.loadingWarnings = loadingModList.getBrokenFiles().stream()
|
||||
.map(file -> new ModLoadingWarning(null, ModLoadingStage.VALIDATE, InvalidModIdentifier.identifyJarProblem(file.getFilePath()).orElse("fml.modloading.brokenfile"), file.getFileName()))
|
||||
.collect(Collectors.toList());
|
||||
if (this.loadingExceptions.isEmpty()) {
|
||||
if (loadingExceptions.isEmpty()) {
|
||||
this.erroredModInfos = Collections.emptySet();
|
||||
} else {
|
||||
this.erroredModInfos = Collections.newSetFromMap(new IdentityHashMap<>());
|
||||
this.erroredModInfos.addAll(this.loadingExceptions.stream().map(ModLoadingException::getModInfo).toList());
|
||||
this.erroredModInfos.addAll(loadingExceptions.stream().map(ModLoadingException::getModInfo).toList());
|
||||
}
|
||||
this.loadingModList.getModFiles().stream()
|
||||
loadingModList.getModFiles().stream()
|
||||
.filter(ModFileInfo::missingLicense)
|
||||
.filter(modFileInfo -> modFileInfo.getMods().stream().noneMatch(this.erroredModInfos::contains)) //Ignore files where any other mod already encountered an error
|
||||
.map(modFileInfo -> new ModLoadingException(null, ModLoadingStage.VALIDATE, "fml.modloading.missinglicense", null, modFileInfo.getFile()))
|
||||
@@ -112,16 +112,16 @@ public class ModLoader {
|
||||
|
||||
private static String computeLanguageList() {
|
||||
return "\n" + FMLLoader.getLanguageLoadingProvider()
|
||||
.applyForEach(lp -> lp.name() + "@" + lp.getClass().getPackage().getImplementationVersion())
|
||||
.applyForEach(lp -> lp.name() + '@' + lp.getClass().getPackage().getImplementationVersion())
|
||||
.collect(Collectors.joining("\n\t\t", "\t\t", ""));
|
||||
}
|
||||
|
||||
private static String computeModLauncherServiceList() {
|
||||
final List<Map<String, String>> mods = FMLLoader.modLauncherModList();
|
||||
return "\n"+mods.stream().map(mod->mod.getOrDefault("file","nofile")+
|
||||
" "+mod.getOrDefault("name", "missing")+
|
||||
" "+mod.getOrDefault("type","NOTYPE")+
|
||||
" "+mod.getOrDefault("description", "")).
|
||||
return "\n" + mods.stream().map(mod -> mod.getOrDefault("file","nofile") +
|
||||
' ' + mod.getOrDefault("name", "missing") +
|
||||
' ' + mod.getOrDefault("type","NOTYPE") +
|
||||
' ' + mod.getOrDefault("description", "")).
|
||||
collect(Collectors.joining("\n\t\t","\t\t",""));
|
||||
}
|
||||
|
||||
@@ -254,7 +254,7 @@ public class ModLoader {
|
||||
}
|
||||
|
||||
private void handleInlineTransition(final Consumer<ModList> transition, final IModLoadingState state, final ModWorkManager.DrivenExecutor syncExecutor, final Runnable ticker) {
|
||||
var pb = StartupMessageManager.addProgressBar("State transition " +state.name()+" running", 0);
|
||||
var pb = StartupMessageManager.addProgressBar("State transition " + state.name() + " running", 0);
|
||||
syncExecutor.drive(ticker);
|
||||
transition.accept(this.modList);
|
||||
syncExecutor.drive(ticker);
|
||||
@@ -353,11 +353,17 @@ public class ModLoader {
|
||||
return loadingStateValid;
|
||||
}
|
||||
|
||||
/** @deprecated Use {@link #hasCompletedState(IModLoadingState)} instead */
|
||||
@Deprecated(forRemoval = true)
|
||||
public boolean hasCompletedState(final String stateName) {
|
||||
IModLoadingState state = stateManager.findState(stateName);
|
||||
return completedStates.contains(state);
|
||||
}
|
||||
|
||||
public boolean hasCompletedState(IModLoadingState state) {
|
||||
return completedStates.contains(state);
|
||||
}
|
||||
|
||||
public static <T extends IModBusEvent> void runEventGenerator(Function<ModContainer, T> generator) {
|
||||
if (!loadingStateValid) {
|
||||
LOGGER.error("Cowardly refusing to send event generator to a broken mod state");
|
||||
|
||||
@@ -19,7 +19,13 @@ public class ModLoadingContext {
|
||||
private Object languageExtension;
|
||||
|
||||
/**
|
||||
* @deprecated Use the context provided by your language loader in your mod's constructor
|
||||
* @deprecated Use the context provided by your language loader in your mod's constructor. Example for Java mods: {@snippet :
|
||||
* @Mod("examplemod")
|
||||
* public final class ExampleMod {
|
||||
* public ExampleMod(FMLJavaModLoadingContext context) {
|
||||
* // Use context from the param instead of FMLJavaModLoadingContext.get() or ModLoadingContext.get()
|
||||
* }
|
||||
* }}
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since="1.21.1")
|
||||
public static ModLoadingContext get() {
|
||||
|
||||
@@ -7,6 +7,7 @@ package net.minecraftforge.fml;
|
||||
|
||||
import net.minecraftforge.fml.loading.progress.ProgressMeter;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executor;
|
||||
@@ -59,7 +60,7 @@ public record ModLoadingState(
|
||||
*/
|
||||
@Deprecated(since = "1.21.3", forRemoval = true)
|
||||
public static ModLoadingState empty(final String name, final String previous, final ModLoadingPhase phase) {
|
||||
return of(name, phase).after(previous).empty();
|
||||
return new ModLoadingState(name, previous, ml -> "", ml -> 0, phase, Optional.empty(), Optional.empty());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -120,6 +121,22 @@ public record ModLoadingState(
|
||||
return new Builder(name, phase);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return o instanceof ModLoadingState that
|
||||
&& this.phase == that.phase
|
||||
&& this.name.equals(that.name)
|
||||
&& Objects.equals(this.previous, that.previous);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = phase.hashCode();
|
||||
result = 31 * result + name.hashCode();
|
||||
result = 31 * result + Objects.hashCode(previous);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private final String name;
|
||||
private final ModLoadingPhase phase;
|
||||
|
||||
@@ -41,11 +41,13 @@ public class ModStateManager {
|
||||
}
|
||||
|
||||
public IModLoadingState findState(final String stateName) {
|
||||
return stateMap.values()
|
||||
var result = stateMap.values()
|
||||
.stream()
|
||||
.flatMap(List::stream)
|
||||
.filter(mls -> mls.name().equals(stateName))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new IllegalArgumentException("Unknown IModLoadingState: " + stateName));
|
||||
.orElse(null);
|
||||
if (result != null) return result;
|
||||
else throw new IllegalArgumentException("Unknown IModLoadingState: " + stateName);
|
||||
}
|
||||
}
|
||||
@@ -8,16 +8,22 @@ package net.minecraftforge.fml.event;
|
||||
import net.minecraftforge.eventbus.api.bus.BusGroup;
|
||||
import net.minecraftforge.eventbus.api.bus.EventBus;
|
||||
import net.minecraftforge.eventbus.api.event.InheritableEvent;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
|
||||
/**
|
||||
* Marker interface for events dispatched on the ModLifecycle event bus instead of the primary event bus
|
||||
* Marker interface for mod lifecycle events dispatched on the
|
||||
* {@linkplain net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext#getModBusGroup() mod BusGroup} instead
|
||||
* of the {@linkplain BusGroup#DEFAULT default BusGroup}.
|
||||
*
|
||||
* @apiNote Each mod gets its own unique mod BusGroup instance, obtained from the mod loading context. Most events
|
||||
* should go on the default BusGroup.
|
||||
*/
|
||||
public interface IModBusEvent extends InheritableEvent {
|
||||
/**
|
||||
* @param modEventBusGroup Obtained from {@link net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext#getModBusGroup()} or your language provider's equivalent.
|
||||
* @param eventClass The event type you want to get the associated bus for.
|
||||
*/
|
||||
static <T extends IModBusEvent> EventBus<T> getBus(BusGroup modEventBusGroup, Class<T> eventClass) {
|
||||
static <T extends IModBusEvent> @NonNull EventBus<@NonNull T> getBus(@NonNull BusGroup modEventBusGroup, @NonNull Class<T> eventClass) {
|
||||
// Do not copy! Temporary solution until the FML rewrite is complete. May throw in future
|
||||
// without prior notice.
|
||||
//
|
||||
|
||||
@@ -36,6 +36,7 @@ dependencies {
|
||||
implementation(libs.accesstransformers)
|
||||
implementation(libs.terminalconsoleappender)
|
||||
implementation(libs.jimfs)
|
||||
implementation(libs.roimfs)
|
||||
|
||||
// Needed because we have a custom log4j plugin, and they removed package scanning and require a data file to be generated
|
||||
implementation(libs.log4j.core)
|
||||
@@ -103,11 +104,31 @@ tasks.register('writeForgeVersionJson') {
|
||||
}
|
||||
}
|
||||
|
||||
tasks.register('jarJarOptionsJson', net.minecraftforge.forge.tasks.JarJarMetadataOptions) {
|
||||
metadataFile = project.file('src/main/resources/jarjar_options.json')
|
||||
// This is resolved too early by cpw.mods.modlauncher.TransformationServicesHandler.discoverServices(DiscoveryData) But eventually...
|
||||
//add(libs.mixin, 'org/spongepowered/asm/mixin/Mixin.class')
|
||||
add(libs.mixinextras.forge) {
|
||||
containedJarMetadata {
|
||||
path = ''
|
||||
}
|
||||
resource = 'com/llamalad7/mixinextras/platform/forge/MixinExtrasConfigPlugin.class'
|
||||
layer = 'GAMELIBRARY'
|
||||
id = 'mixinextras'
|
||||
nested = true
|
||||
}
|
||||
}
|
||||
|
||||
tasks.named('generateResources').configure {
|
||||
dependsOn('eclipseJdt')
|
||||
dependsOn('eclipseJdtApt')
|
||||
dependsOn('eclipseFactorypath')
|
||||
dependsOn('writeForgeVersionJson')
|
||||
dependsOn('jarJarOptionsJson')
|
||||
}
|
||||
|
||||
tasks.named('sourcesJar') {
|
||||
dependsOn('jarJarOptionsJson')
|
||||
}
|
||||
|
||||
eclipse {
|
||||
|
||||
+7
-9
@@ -34,11 +34,10 @@ import cpw.mods.modlauncher.serviceapi.ILaunchPluginService;
|
||||
* }</code>
|
||||
* </pre>
|
||||
*/
|
||||
public class CapabilityTokenSubclass implements ILaunchPluginService {
|
||||
|
||||
public final class CapabilityTokenSubclass implements ILaunchPluginService {
|
||||
private static final String FUNC_NAME = "getType";
|
||||
private static final String FUNC_DESC = "()Ljava/lang/String;";
|
||||
private static final String CAP_INJECT = "net/minecraftforge/common/capabilities/CapabilityToken"; //Don't directly reference this to prevent class loading.
|
||||
private static final String CAP_INJECT = "net/minecraftforge/common/capabilities/CapabilityToken"; // Don't directly reference this to prevent class loading.
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
@@ -56,8 +55,11 @@ public class CapabilityTokenSubclass implements ILaunchPluginService {
|
||||
String internalName = classType.getInternalName();
|
||||
if (internalName.startsWith("net/minecraft/") || internalName.startsWith("com/mojang/"))
|
||||
return NAY;
|
||||
|
||||
if (internalName.startsWith("net/minecraftforge/common/capabilities/"))
|
||||
return YAY;
|
||||
|
||||
return YAY;
|
||||
return internalName.contains("$") ? YAY : NAY; // Anonymous subclasses only
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -70,7 +72,7 @@ public class CapabilityTokenSubclass implements ILaunchPluginService {
|
||||
}
|
||||
return ComputeFlags.SIMPLE_REWRITE;
|
||||
} else if (CAP_INJECT.equals(classNode.superName)) {
|
||||
Holder cls = new Holder();
|
||||
var cls = new Object() { String value = null; };
|
||||
|
||||
SignatureReader reader = new SignatureReader(classNode.signature); // Having a node version of this would probably be useful.
|
||||
reader.accept(new SignatureVisitor(Opcodes.ASM9) {
|
||||
@@ -106,8 +108,4 @@ public class CapabilityTokenSubclass implements ILaunchPluginService {
|
||||
return ComputeFlags.NO_REWRITE;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class Holder {
|
||||
String value;
|
||||
}
|
||||
}
|
||||
@@ -146,9 +146,8 @@ public class FMLLoader {
|
||||
}
|
||||
|
||||
public static List<ITransformationService.Resource> beginModScan(final Map<String,?> arguments) {
|
||||
LOGGER.debug(SCAN,"Scanning for Mod Locators");
|
||||
var modDiscoverer = new ModDiscoverer(arguments);
|
||||
modValidator = modDiscoverer.discoverMods();
|
||||
LOGGER.debug(SCAN, "Scanning for Mod Locators");
|
||||
modValidator = ModDiscoverer.discoverMods(arguments);
|
||||
var pluginResources = modValidator.getPluginResources();
|
||||
return List.of(pluginResources);
|
||||
}
|
||||
|
||||
@@ -36,9 +36,7 @@ public class LoadingModList {
|
||||
.map(ModFile::getModFileInfo)
|
||||
.map(ModFileInfo.class::cast)
|
||||
.toList();
|
||||
this.sortedList = sortedList.stream()
|
||||
.map(ModInfo.class::cast)
|
||||
.toList();
|
||||
this.sortedList = sortedList.stream().toList();
|
||||
this.fileById = this.modFiles.stream()
|
||||
.map(ModFileInfo::getMods)
|
||||
.flatMap(Collection::stream)
|
||||
|
||||
+13
@@ -7,8 +7,10 @@ package net.minecraftforge.fml.loading;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
import cpw.mods.jarhandling.SecureJar;
|
||||
import cpw.mods.modlauncher.Launcher;
|
||||
import cpw.mods.modlauncher.api.LamdbaExceptionUtils;
|
||||
import cpw.mods.modlauncher.api.NamedPath;
|
||||
import cpw.mods.modlauncher.api.TypesafeMap;
|
||||
import cpw.mods.modlauncher.serviceapi.ITransformerDiscoveryService;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
@@ -40,6 +42,17 @@ public class ModDirTransformerDiscoverer implements ITransformerDiscoveryService
|
||||
|
||||
@Override
|
||||
public void earlyInitialization(final String launchTarget, final String[] arguments) {
|
||||
boolean isMixinEnabledInDev = false;
|
||||
for (String arg : arguments) {
|
||||
if (arg.startsWith("-mixin") || arg.startsWith("--mixin")) {
|
||||
isMixinEnabledInDev = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Launcher.INSTANCE.blackboard().putIfAbsent(
|
||||
TypesafeMap.Key.getOrCreate(Launcher.INSTANCE.blackboard(), "isMixinEnabledInDev", Boolean.class),
|
||||
isMixinEnabledInDev
|
||||
);
|
||||
ImmediateWindowHandler.load(launchTarget, arguments);
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ public class ModSorter {
|
||||
List<ModFile> modFiles;
|
||||
|
||||
try {
|
||||
modFiles = new UniqueModListBuilder(mods).buildUniqueList().modFiles();
|
||||
modFiles = UniqueModListBuilder.buildUniqueList(mods).modFiles();
|
||||
} catch (EarlyLoadingException e) {
|
||||
// We cannot build any list with duped mods. We have to abort immediately and report it
|
||||
// Note this will never actually throw an error because the duplicate checks are done in ModDiscovererer before we get to this phase
|
||||
|
||||
@@ -27,7 +27,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;
|
||||
@@ -52,6 +52,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,8 +17,6 @@ import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static java.util.stream.Collectors.groupingBy;
|
||||
import static java.util.stream.Collectors.joining;
|
||||
@@ -26,19 +24,12 @@ import static net.minecraftforge.fml.loading.LogMarkers.LOADING;
|
||||
|
||||
@ApiStatus.Internal
|
||||
@Deprecated(since = "1.21.3", forRemoval = true) // TODO: [FML][Loading] Convert to package private in 1.22
|
||||
public class UniqueModListBuilder {
|
||||
private final static Logger LOGGER = LogUtils.getLogger();
|
||||
public final class UniqueModListBuilder {
|
||||
private UniqueModListBuilder() {}
|
||||
|
||||
private final List<ModFile> modFiles;
|
||||
|
||||
public UniqueModListBuilder(final List<ModFile> modFiles) {
|
||||
this.modFiles = modFiles;
|
||||
}
|
||||
|
||||
public UniqueModListData buildUniqueList() {
|
||||
List<ModFile> uniqueModList;
|
||||
List<ModFile> uniqueLibListWithVersion;
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
|
||||
public static UniqueModListData buildUniqueList(List<ModFile> modFiles) {
|
||||
// Collect mod files by module name. This will be used for deduping purposes
|
||||
final Map<String, List<ModFile>> modFilesByFirstId = modFiles.stream()
|
||||
.filter(mf -> mf.getModFileInfo() != null)
|
||||
@@ -49,12 +40,12 @@ public class UniqueModListBuilder {
|
||||
.collect(groupingBy(UniqueModListBuilder::getModId));
|
||||
|
||||
// Select the newest by artifact version sorting of non-unique files thus identified
|
||||
uniqueModList = modFilesByFirstId.entrySet().stream()
|
||||
final List<ModFile> uniqueModList = modFilesByFirstId.entrySet().stream()
|
||||
.map(UniqueModListBuilder::selectNewestModInfo)
|
||||
.toList();
|
||||
|
||||
// Select the newest by artifact version sorting of non-unique files thus identified
|
||||
uniqueLibListWithVersion = libFilesWithVersionByModuleName.entrySet().stream()
|
||||
final List<ModFile> uniqueLibListWithVersion = libFilesWithVersionByModuleName.entrySet().stream()
|
||||
.map(UniqueModListBuilder::selectNewestModInfo)
|
||||
.toList();
|
||||
|
||||
@@ -66,12 +57,10 @@ public class UniqueModListBuilder {
|
||||
.collect(groupingBy(IModInfo::getModId));
|
||||
|
||||
// Transform to the full lib id list
|
||||
final Map<String, List<ModFile>> versionedLibIds = uniqueLibListWithVersion.stream()
|
||||
final List<List<ModFile>> versionedLibs = uniqueLibListWithVersion.stream()
|
||||
.map(UniqueModListBuilder::getModId)
|
||||
.collect(Collectors.toMap(
|
||||
Function.identity(),
|
||||
libFilesWithVersionByModuleName::get
|
||||
));
|
||||
.map(libFilesWithVersionByModuleName::get)
|
||||
.toList();
|
||||
|
||||
// Its theoretically possible that some mod has somehow moved an id to a secondary place, thus causing a dupe.
|
||||
// We can't handle this
|
||||
@@ -84,24 +73,24 @@ public class UniqueModListBuilder {
|
||||
)).toList();
|
||||
|
||||
if (!dupedModErrors.isEmpty()) {
|
||||
LOGGER.error(LOADING, "Found duplicate mods:\n{}", dupedModErrors.stream().collect(joining("\n")));
|
||||
LOGGER.error(LOADING, "Found duplicate mods:\n{}", String.join("\n", dupedModErrors));
|
||||
throw new EarlyLoadingException("Duplicate mods found", null, dupedModErrors.stream()
|
||||
.map(s -> new EarlyLoadingException.ExceptionData(s))
|
||||
.map(EarlyLoadingException.ExceptionData::new)
|
||||
.toList());
|
||||
}
|
||||
|
||||
|
||||
final List<String> dupedLibErrors = versionedLibIds.values().stream()
|
||||
.filter(modFiles -> modFiles.size() > 1)
|
||||
final List<String> dupedLibErrors = versionedLibs.stream()
|
||||
.filter(libModFiles -> libModFiles.size() > 1)
|
||||
.map(mods -> String.format("\tLibrary: '%s' from files: %s",
|
||||
getModId(mods.getFirst()),
|
||||
mods.stream().map(ModFile::getFileName).collect(joining(", "))
|
||||
)).toList();
|
||||
|
||||
if (!dupedLibErrors.isEmpty()) {
|
||||
LOGGER.error(LOADING, "Found duplicate plugins or libraries:\n{}", dupedLibErrors.stream().collect(joining("\n")));
|
||||
LOGGER.error(LOADING, "Found duplicate plugins or libraries:\n{}", String.join("\n", dupedLibErrors));
|
||||
throw new EarlyLoadingException("Duplicate plugins or libraries found", null, dupedLibErrors.stream()
|
||||
.map(s -> new EarlyLoadingException.ExceptionData(s))
|
||||
.map(EarlyLoadingException.ExceptionData::new)
|
||||
.toList());
|
||||
}
|
||||
|
||||
@@ -122,7 +111,7 @@ public class UniqueModListBuilder {
|
||||
modInfoList.sort(Comparator.comparing(UniqueModListBuilder::getVersion).reversed());
|
||||
LOGGER.debug("Selected file {} for modid {} with version {}", modInfoList.getFirst().getFileName(), fullList.getKey(), getVersion(modInfoList.getFirst()));
|
||||
}
|
||||
return modInfoList.get(0);
|
||||
return modInfoList.getFirst();
|
||||
}
|
||||
|
||||
private static ArtifactVersion getVersion(final ModFile mf) {
|
||||
@@ -130,7 +119,7 @@ public class UniqueModListBuilder {
|
||||
return mf.getJarVersion();
|
||||
}
|
||||
|
||||
return mf.getModInfos().get(0).getVersion();
|
||||
return mf.getModInfos().getFirst().getVersion();
|
||||
}
|
||||
|
||||
private static String getModId(ModFile modFile) {
|
||||
@@ -138,7 +127,7 @@ public class UniqueModListBuilder {
|
||||
return modFile.getSecureJar().name();
|
||||
}
|
||||
|
||||
return modFile.getModFileInfo().getMods().get(0).getModId();
|
||||
return modFile.getModFileInfo().getMods().getFirst().getModId();
|
||||
}
|
||||
|
||||
public record UniqueModListData(List<ModFile> modFiles, Map<String, List<ModFile>> modFilesByFirstId) {}
|
||||
|
||||
+15
-5
@@ -54,12 +54,16 @@ public abstract class AbstractModProvider implements IModProvider {
|
||||
|
||||
@Nullable
|
||||
protected IModLocator.ModFileOrException createMod(Path path, boolean ignoreUnknown, String defaultType) {
|
||||
var debug = path.toString();
|
||||
if (debug.equals("/"))
|
||||
debug = path.toUri().toString();
|
||||
|
||||
var mjm = new ModJarMetadata();
|
||||
SecureJar sj = null;
|
||||
try {
|
||||
sj = SecureJar.from(jar -> loadMetaFromJar(jar, mjm), path);
|
||||
} catch (Throwable t) {
|
||||
return new IModLocator.ModFileOrException(null, new ModFileLoadingException("Failed to create secure jar for \"" + path + "\" - " + t.getMessage()));
|
||||
return new IModLocator.ModFileOrException(null, new ModFileLoadingException("Failed to create secure jar for \"" + debug + "\" - " + t.getMessage()));
|
||||
}
|
||||
|
||||
IModFile mod;
|
||||
@@ -68,7 +72,7 @@ public abstract class AbstractModProvider implements IModProvider {
|
||||
type = defaultType;
|
||||
|
||||
if (sj.moduleDataProvider().findFile(MODS_TOML).isPresent()) {
|
||||
LOGGER.debug(LogMarkers.SCAN, "Found {} mod of type {}: {}", MODS_TOML, type, path);
|
||||
LOGGER.debug(LogMarkers.SCAN, "Found {} mod of type {}: {}", MODS_TOML, type, debug);
|
||||
mod = new ModFile(sj, this, ModFileParser::modsTomlParser);
|
||||
// ModJarMetadata is only used when loading mods.toml mods as ModJarMetadata
|
||||
mjm.setModFile(mod);
|
||||
@@ -77,17 +81,17 @@ public abstract class AbstractModProvider implements IModProvider {
|
||||
return new IModLocator.ModFileOrException(null, new ModFileLoadingException("File \"%s\" is not a Forge mod and cannot be loaded. Look for a Forge version of this mod or consider alternative mods.".formatted(mod.getFileName())));
|
||||
}
|
||||
} else if (type != null) {
|
||||
LOGGER.debug(LogMarkers.SCAN, "Found {} mod of type {}: {}", JarFile.MANIFEST_NAME, type, path);
|
||||
LOGGER.debug(LogMarkers.SCAN, "Found {} mod of type {}: {}", JarFile.MANIFEST_NAME, type, debug);
|
||||
mod = new ModFile(sj, this, this::manifestParser, type);
|
||||
} else if (ignoreUnknown) {
|
||||
return null;
|
||||
} else
|
||||
return new IModLocator.ModFileOrException(null, new ModFileLoadingException("Invalid mod file found " + path));
|
||||
return new IModLocator.ModFileOrException(null, new ModFileLoadingException("Invalid mod file found " + debug));
|
||||
|
||||
return new IModLocator.ModFileOrException(mod, null);
|
||||
}
|
||||
|
||||
private static JarMetadata loadMetaFromJar(SecureJar jar, ModJarMetadata mjm) {
|
||||
protected static JarMetadata loadMetaFromJar(SecureJar jar, ModJarMetadata mjm) {
|
||||
var info = jar.moduleDataProvider().open(MODULE_INFO).orElse(null);
|
||||
if (info != null) {
|
||||
try {
|
||||
@@ -145,6 +149,12 @@ public abstract class AbstractModProvider implements IModProvider {
|
||||
public <T> Optional<T> getConfigElement(String... key) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<? extends IConfigurable> getConfigList(String key) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<? extends IConfigurable> getConfigList(String... key) {
|
||||
return Collections.emptyList();
|
||||
|
||||
+3
-2
@@ -74,7 +74,8 @@ public final class ClasspathLocator extends AbstractModProvider implements IModL
|
||||
var ret = new ArrayList<ModFileOrException>();
|
||||
for (var path : claimed) {
|
||||
// Filter out anything found by the ServiceLoader
|
||||
if (!ModDirTransformerDiscoverer.isServiceProvider(path))
|
||||
if (!ModDirTransformerDiscoverer.isServiceProvider(path)
|
||||
&& !JarInJarDependencyLocator.isOption(path))
|
||||
ret.add(createMod(path));
|
||||
}
|
||||
return ret;
|
||||
@@ -107,7 +108,7 @@ public final class ClasspathLocator extends AbstractModProvider implements IModL
|
||||
return Path.of(URI.create(str));
|
||||
}
|
||||
|
||||
private static Path getPathFromResource(ClassLoader cl, String resource) {
|
||||
static Path getPathFromResource(ClassLoader cl, String resource) {
|
||||
var url = cl.getResource(resource);
|
||||
if (url == null)
|
||||
return null;
|
||||
|
||||
+621
-102
@@ -5,22 +5,39 @@
|
||||
|
||||
package net.minecraftforge.fml.loading.moddiscovery;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.electronwill.nightconfig.core.UnmodifiableConfig;
|
||||
import com.electronwill.nightconfig.core.file.FileConfig;
|
||||
import com.mojang.logging.LogUtils;
|
||||
|
||||
import cpw.mods.modlauncher.Launcher;
|
||||
import cpw.mods.modlauncher.api.TypesafeMap;
|
||||
import net.minecraftforge.fml.loading.EarlyLoadingException;
|
||||
import net.minecraftforge.fml.loading.EarlyLoadingException.ExceptionData;
|
||||
import net.minecraftforge.fml.loading.FMLEnvironment;
|
||||
import net.minecraftforge.forgespi.language.IModInfo;
|
||||
import net.minecraftforge.forgespi.locating.IDependencyLocator;
|
||||
import net.minecraftforge.forgespi.locating.IModFile;
|
||||
import net.minecraftforge.forgespi.locating.ModFileLoadingException;
|
||||
import net.minecraftforge.forgespi.locating.IModFile.Type;
|
||||
import net.minecraftforge.jarjar.metadata.ContainedJarIdentifier;
|
||||
import net.minecraftforge.jarjar.metadata.ContainedJarMetadata;
|
||||
import net.minecraftforge.jarjar.metadata.ContainedVersion;
|
||||
import net.minecraftforge.jarjar.metadata.MetadataIOHandler;
|
||||
import net.minecraftforge.jarjar.selection.JarSelector;
|
||||
import org.apache.maven.artifact.versioning.ArtifactVersion;
|
||||
|
||||
import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
|
||||
import org.apache.maven.artifact.versioning.VersionRange;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.Marker;
|
||||
import org.slf4j.MarkerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
@@ -28,14 +45,20 @@ import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.TreeMap;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.jar.Manifest;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@ApiStatus.Internal
|
||||
public class JarInJarDependencyLocator extends AbstractModProvider implements IDependencyLocator {
|
||||
private static final String ROIMFS = "roimfs";
|
||||
private static final String COLOR_CODE = "\u00a7";
|
||||
private static final String RESET = COLOR_CODE + "r";
|
||||
private static final String YELLOW = COLOR_CODE + "e";
|
||||
@@ -43,32 +66,238 @@ public class JarInJarDependencyLocator extends AbstractModProvider implements ID
|
||||
private static final String GREEN = COLOR_CODE + "2";
|
||||
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
private static final Marker MARKER = MarkerFactory.getMarker("JAR-JAR"); // LogMarkers.SCAN;
|
||||
|
||||
private static final Map<Path, Option> OPTIONS = new HashMap<>();
|
||||
private static volatile boolean optionsLoaded = false;
|
||||
|
||||
private static final Attributes.Name MIXIN_CONFIGS_ATTR = new Attributes.Name("MixinConfigs");
|
||||
private static final Attributes.Name MIXIN_CONNECTOR_ATTR = new Attributes.Name("MixinConnector");
|
||||
private static final ContainedJarMetadata MIXIN_EXTRAS_DEPENDENCY = makeMixinExtraDependency();
|
||||
|
||||
private static ContainedJarMetadata makeMixinExtraDependency() {
|
||||
VersionRange range = null;
|
||||
try {
|
||||
range = VersionRange.createFromVersionSpec("[0,)");
|
||||
} catch (InvalidVersionSpecificationException e) {
|
||||
throw new RuntimeException("Failed to create version range for mixinextras-forge dependency", e);
|
||||
}
|
||||
|
||||
return new ContainedJarMetadata(
|
||||
new ContainedJarIdentifier("io.github.llamalad7", "mixinextras-forge"),
|
||||
new ContainedVersion(range, null),
|
||||
null, false
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "JarInJar";
|
||||
}
|
||||
|
||||
private int fsID = 0;
|
||||
private synchronized int nextId() {
|
||||
return fsID++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<IModFile> scanMods(Iterable<IModFile> loadedMods) {
|
||||
final List<IModFile> sources = new ArrayList<>();
|
||||
loadedMods.forEach(sources::add);
|
||||
var mods = new ArrayList<IModFile>();
|
||||
loadedMods.forEach(mods::add);
|
||||
|
||||
var dependenciesToLoad = JarSelector.detectAndSelect(
|
||||
sources,
|
||||
this::loadResourceFromModFile,
|
||||
this::loadModFileFrom,
|
||||
this::identifyMod,
|
||||
this::exception
|
||||
);
|
||||
var selector = new Selector(mods);
|
||||
|
||||
if (dependenciesToLoad.isEmpty()) {
|
||||
// Add a synthetic Mixin-Extras dependency if we detect anyone using Mixin
|
||||
if (anyMixinsLoaded(mods)) {
|
||||
if (!selector.isRequired(MIXIN_EXTRAS_DEPENDENCY.identifier()))
|
||||
selector.addRequirement(MIXIN_EXTRAS_DEPENDENCY);
|
||||
} else if (selector.entries.size() == mods.size()) {
|
||||
LOGGER.info("No dependencies to load found. Skipping!");
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
LOGGER.info("Found {} dependencies adding them to mods collection", dependenciesToLoad.size());
|
||||
return dependenciesToLoad;
|
||||
loadOptions();
|
||||
|
||||
// Ideally, the json would be in order so we only have to loop once. But its written this way just in case.
|
||||
var seen = new HashSet<Option>();
|
||||
var added = false;
|
||||
do {
|
||||
added = false;
|
||||
for (var entry : OPTIONS.values()) {
|
||||
if (seen.contains(entry) || !selector.isRequired(entry.meta.identifier()))
|
||||
continue;
|
||||
|
||||
seen.add(entry);
|
||||
|
||||
entry.addTo(selector);
|
||||
|
||||
if (entry.deps != null && !entry.deps.isEmpty()) {
|
||||
added = true;
|
||||
for (var dep : entry.deps)
|
||||
selector.addRequirement(dep);
|
||||
}
|
||||
}
|
||||
} while (added);
|
||||
|
||||
var selected = selector.select();
|
||||
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
var ids = new TreeMap<String, List<Entry>>();
|
||||
for (var entry : selector.entries.values()) {
|
||||
if (entry != FAILED && entry.coord != null)
|
||||
ids.computeIfAbsent(entry.coord, id -> new ArrayList<>()).add(entry);
|
||||
}
|
||||
|
||||
for (var entry : ids.entrySet()) {
|
||||
LOGGER.info(MARKER, "JarJar Candidated for {}", entry.getKey());
|
||||
Collections.sort(entry.getValue());
|
||||
for (var option : entry.getValue())
|
||||
LOGGER.info(MARKER, "\t{}{}", selected.contains(option) ? '*' : ' ', option);
|
||||
}
|
||||
}
|
||||
|
||||
if (selected.isEmpty()) {
|
||||
LOGGER.info("No dependencies to load found. Skipping!");
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/* Since Java 11, the JRE decompresses all zip files to memory when getting a ByteChannel for them.
|
||||
* JarJar only targets Minecraft 1.18+ which is Java17+ Which means we can exploit the fact that
|
||||
* these are Disjointed file systems, and close all unselected libraries.
|
||||
*
|
||||
* https://github.com/openjdk/jdk11u/blob/25a9b9e5594e88c9e1e66f95c7ca45d5a9da8854/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystem.java#L703
|
||||
*
|
||||
* Combined with the fact that the JarJarFileSystem has always simply called into FileSystems.newFileSystem for its wrapped targets.
|
||||
* https://github.com/MinecraftForge/JarJar/blame/b39b2051439c81519d826d38898778bf3081356f/src/main/java/net/minecraftforge/jarjar/nio/pathfs/PathFileSystem.java#L38
|
||||
*
|
||||
* This effectively means that JarJar has ALWAYS been a wrapper around in-memory copies of every jar it scans.
|
||||
*
|
||||
* It also never closes these FileSystems, which potentially means they are leaked if the GC can't inherently GC them.
|
||||
*
|
||||
* Ideally we would extract these contained jars to a temp folder, so that we can use the nice optimizations Java has for ZipFile/JarFile.
|
||||
* But in order to do that we would need to write the file to a place that could potentially have conflicts.
|
||||
* To resolve those conflicts we should take a hint out of maven/gradle/minecraft's book and key the filepath based on a hash of the file being extracted.
|
||||
* However, because that hash is no available in existing metadata we would need to re-calculate it.
|
||||
*
|
||||
* To do it in an efficient way we I needed to be able to share the single opened FileSystem with both the SecureJar implementation, and with our recursive search.
|
||||
* So I use my new ReadOnlyInMemoryFileSystem which is specifically designed to use the same backing array for the JarInJar search as it does with the resulting dependency
|
||||
* selected SecureJars.
|
||||
*
|
||||
* This means that if we wanted to we could rather efficiently calculate a hash for extracting the file. However, it also means that we don't have to extract to file to
|
||||
* support ZipFileSystem caching properly. So as long as we are okay with not using the JRE's optimized ZipFile implementation, or we want to support legacy JarInJar metadata,
|
||||
* using this efficient in memory file system is the best option performance wise. Extracting the files during launch without an efficient caching/unique identifier system
|
||||
* would incur a lot of Disc IO which can be super slow depending on your setup.
|
||||
*
|
||||
* So, it is a stated goal of our future re-write to extract files into a cache directory to gain the benefits of JRE optimizations, using a FileSystem much simpler then
|
||||
* the existing JarInJarFileSystem is good enough.
|
||||
*/
|
||||
|
||||
// Lets close all unselected FileSystems
|
||||
var wasSelected = new HashSet<>(selected);
|
||||
for (var entry : selector.entries.values())
|
||||
entry.cleanup(wasSelected.contains(entry));
|
||||
|
||||
LOGGER.info(MARKER, "Found {} dependencies adding them to mods collection", selected.size());
|
||||
var ret = new ArrayList<IModFile>(selected.size());
|
||||
|
||||
// Now lets build the actual mod files
|
||||
for (var entry : selected) {
|
||||
try {
|
||||
var root = entry.getFinalPath();
|
||||
var mod = createMod(root, false, entry.type.name());
|
||||
|
||||
if (mod.ex() != null)
|
||||
throw fail("Failed to load JarInJar file " + entry, mod.ex());
|
||||
|
||||
ret.add(mod.file());
|
||||
} catch (EarlyLoadingException e) {
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
throw fail("Failed to load JarInJar file " + entry, e);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static Boolean anyMixinsLoaded = null;
|
||||
private static boolean anyMixinsLoaded(List<IModFile> mods) {
|
||||
var ret = anyMixinsLoaded;
|
||||
if (ret != null) return ret;
|
||||
|
||||
if (!FMLEnvironment.production
|
||||
&& Launcher.INSTANCE.blackboard().get(TypesafeMap.Key.getOrCreate(Launcher.INSTANCE.blackboard(), "isMixinEnabledInDev", Boolean.class)).orElse(false)) {
|
||||
anyMixinsLoaded = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
for (var mod : mods) {
|
||||
var attributes = mod.getSecureJar().moduleDataProvider().getManifest().getMainAttributes();
|
||||
if (attributes.getValue(MIXIN_CONFIGS_ATTR) != null || attributes.getValue(MIXIN_CONNECTOR_ATTR) != null) {
|
||||
anyMixinsLoaded = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
anyMixinsLoaded = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
private record Options(List<OptionMetadata> options) {}
|
||||
private record OptionMetadata(String resource, Type layer, String id, List<ContainedJarMetadata> deps, ContainedJarMetadata meta, boolean nested) {}
|
||||
|
||||
private static void loadOptions() {
|
||||
if (optionsLoaded)
|
||||
return;
|
||||
|
||||
synchronized (OPTIONS) {
|
||||
if (optionsLoaded)
|
||||
return;
|
||||
|
||||
optionsLoaded = true;
|
||||
|
||||
var self = JarInJarDependencyLocator.class;
|
||||
|
||||
try (var stream = self.getModule().getResourceAsStream("/jarjar_options.json")) {
|
||||
if (stream == null) {
|
||||
LOGGER.error(MARKER, "Failed to find jarjar_options.json");
|
||||
return;
|
||||
}
|
||||
|
||||
var meta = MetadataIOHandler.getGson().fromJson(new InputStreamReader(stream), Options.class);
|
||||
//var meta = MetadataIOHandler.fromStream(stream).orElse(null);
|
||||
if (meta == null) {
|
||||
LOGGER.error(MARKER, "Corrupted jarjar_options.json");
|
||||
return;
|
||||
}
|
||||
|
||||
for (var jar : meta.options()) {
|
||||
Path path = ClasspathLocator.getPathFromResource(self.getClassLoader(), jar.resource());
|
||||
if (path == null) {
|
||||
LOGGER.error(MARKER, "Failed to find JarJar option for {}", jar.resource());
|
||||
continue;
|
||||
}
|
||||
|
||||
LOGGER.debug(MARKER, "Found JarJar Option: {}", path.toAbsolutePath());
|
||||
if (jar.nested())
|
||||
OPTIONS.put(path, new NestedOption(jar.id(), path, jar.layer(), jar.meta(), jar.deps()));
|
||||
else
|
||||
OPTIONS.put(path, new Option(jar.id(), path, jar.layer(), jar.meta(), jar.deps()));
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
LOGGER.error(MARKER, "Failed to read JarJar Options file", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isOption(Path path) {
|
||||
loadOptions();
|
||||
return OPTIONS.containsKey(path);
|
||||
}
|
||||
|
||||
private static EarlyLoadingException fail(String message, @Nullable Throwable cause) {
|
||||
LOGGER.error(MARKER, message);
|
||||
return new EarlyLoadingException(message, cause, Collections.emptyList());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -76,89 +305,7 @@ public class JarInJarDependencyLocator extends AbstractModProvider implements ID
|
||||
return IModFile.Type.GAMELIBRARY.name();
|
||||
}
|
||||
|
||||
protected Optional<IModFile> loadModFileFrom(IModFile file, Path path) {
|
||||
try {
|
||||
final Path pathInModFile = file.findResource(path.toString());
|
||||
final URI filePathUri = new URI("jij:" + (pathInModFile.toAbsolutePath().toUri().getRawSchemeSpecificPart())).normalize();
|
||||
final Map<String, ?> outerFsArgs = ImmutableMap.of("packagePath", pathInModFile);
|
||||
final FileSystem zipFS = FileSystems.newFileSystem(filePathUri, outerFsArgs);
|
||||
final Path pathInFS = zipFS.getPath("/");
|
||||
final IModFile.Type parentType = file.getType();
|
||||
final String modType;
|
||||
if (parentType == IModFile.Type.LIBRARY || parentType == IModFile.Type.LANGPROVIDER) {
|
||||
modType = IModFile.Type.LIBRARY.name();
|
||||
} else {
|
||||
modType = IModFile.Type.GAMELIBRARY.name();
|
||||
}
|
||||
return Optional.of(createMod(pathInFS, false, modType).file());
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("Failed to load mod file {} from {}", path, file.getFileName());
|
||||
final RuntimeException exception = new ModFileLoadingException("Failed to load mod file " + file.getFileName());
|
||||
exception.initCause(e);
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
|
||||
protected EarlyLoadingException exception(Collection<JarSelector.ResolutionFailureInformation<IModFile>> failedDependencies) {
|
||||
final List<EarlyLoadingException.ExceptionData> errors = failedDependencies.stream()
|
||||
.filter(entry -> !entry.sources().isEmpty()) //Should never be the case, but just to be sure
|
||||
.map(JarInJarDependencyLocator::buildExceptionData)
|
||||
.toList();
|
||||
|
||||
return new EarlyLoadingException(failedDependencies.size() + " Dependency restrictions were not met.", null, errors);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static EarlyLoadingException.ExceptionData buildExceptionData(JarSelector.ResolutionFailureInformation<IModFile> entry) {
|
||||
return new EarlyLoadingException.ExceptionData(
|
||||
getErrorTranslationKey(entry),
|
||||
entry.identifier().group() + ":" + entry.identifier().artifact(),
|
||||
entry.sources()
|
||||
.stream()
|
||||
.flatMap(JarInJarDependencyLocator::getModWithVersionRangeStream)
|
||||
.map(JarInJarDependencyLocator::formatError)
|
||||
.collect(Collectors.joining(", "))
|
||||
);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String getErrorTranslationKey(JarSelector.ResolutionFailureInformation<IModFile> entry) {
|
||||
return entry.failureReason() == JarSelector.FailureReason.VERSION_RESOLUTION_FAILED ?
|
||||
"fml.dependencyloading.conflictingdependencies" :
|
||||
"fml.dependencyloading.mismatchedcontaineddependencies";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Stream<ModWithVersionRange> getModWithVersionRangeStream(JarSelector.SourceWithRequestedVersionRange<IModFile> file) {
|
||||
return file.sources()
|
||||
.stream()
|
||||
.map(IModFile::getModFileInfo)
|
||||
.flatMap(modFileInfo -> modFileInfo.getMods().stream())
|
||||
.map(modInfo -> new ModWithVersionRange(modInfo, file.requestedVersionRange(), file.includedVersion()));
|
||||
}
|
||||
|
||||
protected Optional<InputStream> loadResourceFromModFile(IModFile modFile, Path path) {
|
||||
try {
|
||||
var pathInModFile = modFile.findResource(path.toString());
|
||||
if (!Files.exists(pathInModFile)) {
|
||||
LOGGER.debug("Failed to load resource {} from {}, it does not contain dependency information.", path, modFile.getFileName());
|
||||
return Optional.empty();
|
||||
}
|
||||
return Optional.of(Files.newInputStream(pathInModFile));
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("Failed to load resource {} from mod {}, cause {}", path, modFile.getFileName(), e);
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String formatError(ModWithVersionRange modWithVersionRange){
|
||||
return YELLOW + modWithVersionRange.modInfo().getModId() + RESET + " - " +
|
||||
RED + modWithVersionRange.versionRange().toString() + RESET + " - " +
|
||||
GREEN + modWithVersionRange.artifactVersion().toString() + RESET;
|
||||
}
|
||||
|
||||
protected String identifyMod(IModFile modFile) {
|
||||
protected static String identifyMod(IModFile modFile) {
|
||||
if (modFile.getModFileInfo() != null && !modFile.getModInfos().isEmpty())
|
||||
return modFile.getModInfos().stream().map(IModInfo::getModId).collect(Collectors.joining());
|
||||
|
||||
@@ -169,5 +316,377 @@ public class JarInJarDependencyLocator extends AbstractModProvider implements ID
|
||||
return modFile.getFileName();
|
||||
}
|
||||
|
||||
private record ModWithVersionRange(IModInfo modInfo, VersionRange versionRange, ArtifactVersion artifactVersion) {}
|
||||
private static final Entry FAILED = new Entry("FAILED ENTRY", Path.of(""), Type.LIBRARY, null);
|
||||
private static sealed class Entry implements Comparable<Entry> {
|
||||
final String id;
|
||||
final Path path;
|
||||
final Type type;
|
||||
final @Nullable String coord;
|
||||
|
||||
private Entry(String id, Path path, Type type, @Nullable String coord) {
|
||||
this.id = id;
|
||||
this.path = path;
|
||||
this.type = type;
|
||||
this.coord = coord;
|
||||
}
|
||||
|
||||
Path getFinalPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
void cleanup(boolean selected) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (path.getFileName() == null)
|
||||
return path.toUri().toString();
|
||||
return path.getFileName().toString();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
Path getResource(String path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Entry o) {
|
||||
return o.toString().compareTo(toString());
|
||||
}
|
||||
}
|
||||
|
||||
private static final class ModEntry extends Entry {
|
||||
private final IModFile mod;
|
||||
|
||||
ModEntry(IModFile mod) {
|
||||
super(identifyMod(mod), mod.getFilePath(), mod.getType(), null);
|
||||
this.mod = mod;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
Path getResource(String path) {
|
||||
return mod.findResource(path);
|
||||
}
|
||||
}
|
||||
|
||||
private static sealed class Option extends Entry {
|
||||
final ContainedJarMetadata meta;
|
||||
final List<ContainedJarMetadata> deps;
|
||||
|
||||
private Option(String id, Path path, Type type, ContainedJarMetadata meta, List<ContainedJarMetadata> deps) {
|
||||
super(id, path, type, meta.identifier().group() + ':' + meta.identifier().artifact());
|
||||
this.meta = meta;
|
||||
this.deps = deps;
|
||||
}
|
||||
|
||||
public void addTo(Selector selector) {
|
||||
selector.option(this, meta);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class NestedOption extends Option {
|
||||
private FileSystem zip;
|
||||
private Path root;
|
||||
|
||||
private NestedOption(String id, Path path, Type type, ContainedJarMetadata meta, List<ContainedJarMetadata> deps) {
|
||||
super(id, path, type, meta, deps);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTo(Selector selector) {
|
||||
super.addTo(selector);
|
||||
selector.add(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
void cleanup(boolean selected) {
|
||||
if (this.zip != null) {
|
||||
try {
|
||||
zip.close();
|
||||
} catch (IOException e) {
|
||||
LOGGER.error(MARKER, "Failed to close unselected FileSystem {}", this, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
Path getResource(String path) {
|
||||
if (zip == null) {
|
||||
try {
|
||||
this.zip = FileSystems.newFileSystem(this.path);
|
||||
this.root = zip.getRootDirectories().iterator().next();
|
||||
} catch (IOException e) {
|
||||
LOGGER.error(MARKER, "Failed to open FileSystem for option root {}", this, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
var target = root.resolve(path);
|
||||
if (Files.exists(target))
|
||||
return target;
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static final class Nested extends Entry {
|
||||
private final Entry parent;
|
||||
private final String nestedPath;
|
||||
private final Path zipPath;
|
||||
|
||||
Nested(String id, Path path, Type type, String coords, Entry parent, String nestedPath, Path zipPath) {
|
||||
super(id, path, type, coords);
|
||||
this.parent = parent;
|
||||
this.nestedPath = nestedPath;
|
||||
this.zipPath = zipPath;
|
||||
}
|
||||
|
||||
/*
|
||||
* Due to a bug in Mixin, anything that is not on the DefaultFileSystem MUST be a directory.
|
||||
* Or else it can't resolve resources correctly. So for anything we put on the roimfs we need
|
||||
* to return the nested zip file system.
|
||||
*
|
||||
* This will be an annoying to backport, but we can cross that bridge when we come to it.
|
||||
*
|
||||
* See: https://github.com/SpongePowered/Mixin/blob/4053421aa10aaac6127d969028a29c94fe3054f6/src/main/java/org/spongepowered/asm/launch/platform/MainAttributes.java#L102
|
||||
*/
|
||||
@Override
|
||||
public Path getFinalPath() {
|
||||
return zipPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
void cleanup(boolean selected) {
|
||||
if (selected)
|
||||
return;
|
||||
|
||||
LOGGER.info(MARKER, "Closeing unselected FileSystem {}", this);
|
||||
try {
|
||||
zipPath.getFileSystem().close();
|
||||
path.getFileSystem().close();
|
||||
} catch (IOException e) {
|
||||
LOGGER.error(MARKER, "Failed to close unselected FileSystem {}", this, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
Path getResource(String path) {
|
||||
return zipPath.resolve(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return parent + "!/" + nestedPath;
|
||||
}
|
||||
}
|
||||
|
||||
private record Key(Entry parent, String path) {}
|
||||
|
||||
private final class Selector extends JarSelector<Entry> {
|
||||
private final HashMap<Key, Entry> entries = new HashMap<>();
|
||||
private final HashMap<Entry, HashMap<String, String>> children = new HashMap<>();
|
||||
|
||||
private Selector(Iterable<IModFile> mods) {
|
||||
for (var mod : mods)
|
||||
this.force(new ModEntry(mod));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void force(Entry entry) {
|
||||
super.force(entry);
|
||||
entries.put(new Key(entry, ""), entry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void option(Entry entry, ContainedJarMetadata meta) {
|
||||
super.option(entry, meta);
|
||||
entries.put(new Key(entry, ""), entry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(Entry entry) {
|
||||
super.add(entry);
|
||||
entries.put(new Key(entry, ""), entry);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
protected InputStream getResource(Entry source, String path) {
|
||||
try {
|
||||
Path target = source.getResource(path);
|
||||
if (target == null || !Files.exists(target)) {
|
||||
LOGGER.debug(MARKER, "Failed to load resource {} from {}, it does not contain dependency information.", path, source);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Read the metadata file and store identifiers so we can group them later.
|
||||
if (JarSelector.CONTAINED_JARS_METADATA_PATH.equals(path)) {
|
||||
try (var is = Files.newInputStream(target)) {
|
||||
var meta = MetadataIOHandler.fromStream(is).orElse(null);
|
||||
if (meta == null)
|
||||
return Files.newInputStream(target);
|
||||
var children = new HashMap<String, String>();
|
||||
for (var child : meta.jars())
|
||||
children.put(child.path().replace('\\', '/'), child.identifier().group() + ':' + child.identifier().artifact());
|
||||
this.children.put(source, children);
|
||||
}
|
||||
}
|
||||
|
||||
return Files.newInputStream(target);
|
||||
} catch (Exception e) {
|
||||
LOGGER.error(MARKER, "Failed to load resource {} from {}, cause {}", path, source, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Entry getNested(Entry source, String path) {
|
||||
var cleaned = path.replace('\\', '/');
|
||||
var key = new Key(source, cleaned);
|
||||
var entry = entries.get(key);
|
||||
if (entry == null) {
|
||||
entry = createNested(source, path, cleaned);
|
||||
if (entry == null)
|
||||
entry = FAILED;
|
||||
entries.put(key, entry);
|
||||
}
|
||||
return entry == FAILED ? null : entry;
|
||||
}
|
||||
|
||||
private Entry createNested(Entry source, String path, String cleaned) {
|
||||
var targetPath = source.getResource(path);
|
||||
|
||||
if (targetPath == null || !Files.isRegularFile(targetPath))
|
||||
return null;
|
||||
|
||||
Path roimfsPath = null;
|
||||
Path zipPath = null;
|
||||
try {
|
||||
// Lets copy the uncompressed file to memory, and then create a ReadOnlyInMemorryFileSystem for it, this allows us to reference it by URI.
|
||||
// Which is needed because a lot of things require navigation via URI.
|
||||
// And ZipFileSystem only caches the FileSystem instance when accessed via URI.
|
||||
byte[] data = Files.readAllBytes(targetPath);
|
||||
String filename = targetPath.getFileName().toString();
|
||||
URI uri = new URI(ROIMFS, "jar-jar-" + nextId() + "/" + filename, null);
|
||||
var roimfs = FileSystems.newFileSystem(uri, Collections.singletonMap("data", data));
|
||||
roimfsPath = roimfs.getPath(filename);
|
||||
var zip = FileSystems.newFileSystem(new URI("jar:" + roimfsPath.toUri()), Collections.emptyMap());
|
||||
zipPath = zip.getRootDirectories().iterator().next(); // Get first root, which on ZipFileSystem is the only root
|
||||
} catch (IOException | URISyntaxException e) {
|
||||
LOGGER.error(MARKER, "Failed to create FileSystem for nested jar {}", source, e);
|
||||
return null;
|
||||
}
|
||||
|
||||
var mf = new Manifest();
|
||||
var mfPath = zipPath.resolve(JarFile.MANIFEST_NAME);
|
||||
if (Files.exists(mfPath)) {
|
||||
try (var is = Files.newInputStream(mfPath)) {
|
||||
mf = new Manifest(is);
|
||||
} catch (IOException e) {
|
||||
LOGGER.error(MARKER, "Failed to read manifest from {}", source, e);
|
||||
}
|
||||
}
|
||||
|
||||
IModFile.Type type = null;
|
||||
String id = null;
|
||||
var tomlPath = zipPath.resolve(MODS_TOML);
|
||||
if (Files.exists(tomlPath)) {
|
||||
var cfg = FileConfig.builder(tomlPath).build();
|
||||
cfg.load();
|
||||
cfg.close();
|
||||
if (cfg.getOrElse("mods", null) instanceof Collection collection) {
|
||||
@SuppressWarnings("unchecked")
|
||||
var mods = (Collection<UnmodifiableConfig>)collection;
|
||||
if (!mods.isEmpty()) {
|
||||
var first = mods.iterator().next();
|
||||
var modId = first.getOrElse("modId", null);
|
||||
if (modId instanceof String str)
|
||||
id = str;
|
||||
}
|
||||
}
|
||||
var value = mf.getMainAttributes().getValue(ModFile.TYPE);
|
||||
if (value != null)
|
||||
type = IModFile.Type.valueOf(value);
|
||||
else
|
||||
type = IModFile.Type.MOD;
|
||||
} else {
|
||||
var value = mf.getMainAttributes().getValue(ModFile.TYPE);
|
||||
if (value != null)
|
||||
type = IModFile.Type.valueOf(value);
|
||||
else {
|
||||
// No mods.toml, no entry value, so decide based on the container's type
|
||||
if (source.type == IModFile.Type.LIBRARY || source.type == IModFile.Type.LANGPROVIDER)
|
||||
type = IModFile.Type.LIBRARY;
|
||||
else
|
||||
type = IModFile.Type.GAMELIBRARY;
|
||||
}
|
||||
}
|
||||
|
||||
if (id == null) {
|
||||
var modulePath = zipPath.resolve(MODULE_INFO);
|
||||
if (Files.exists(modulePath)) {
|
||||
try (var is = Files.newInputStream(modulePath)) {
|
||||
var module = ModuleDescriptor.read(is);
|
||||
id = module.name();
|
||||
} catch (IOException e) {
|
||||
LOGGER.error(MARKER, "Failed to read module-info.class from {}", source, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (id == null)
|
||||
id = mf.getMainAttributes().getValue("Automatic-Module-Name");
|
||||
|
||||
if (id == null)
|
||||
id = Path.of(path).getFileName().toString();
|
||||
|
||||
String coords = null;
|
||||
var child = this.children.get(source);
|
||||
if (child != null)
|
||||
coords = child.get(cleaned);
|
||||
|
||||
return new Nested(id, roimfsPath, type, coords, source, cleaned, zipPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getIdentifier(Entry entry) {
|
||||
return entry.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Throwable getFailureException(Collection<ResolutionFailureInformation<Entry>> failures) {
|
||||
var errors = new ArrayList<ExceptionData>();
|
||||
for (var failure : failures) {
|
||||
var message = failure.failureReason() == JarSelector.FailureReason.VERSION_RESOLUTION_FAILED ? "fml.dependencyloading.conflictingdependencies" : "fml.dependencyloading.mismatchedcontaineddependencies";
|
||||
var joiner = new StringJoiner(", ");
|
||||
for (var source : failure.sources()) {
|
||||
String paths = null;
|
||||
if (source.sources().size() == 0) {
|
||||
paths = "[No Sources]";
|
||||
} else if (source.sources().size() == 1) {
|
||||
var entry = source.sources().iterator().next();
|
||||
paths = entry.toString();
|
||||
} else {
|
||||
var j = new StringJoiner(", ", "[", "]");
|
||||
for (var entry : source.sources())
|
||||
j.add(entry.toString());
|
||||
paths = j.toString();
|
||||
}
|
||||
|
||||
var error = YELLOW + paths + RESET + " - " +
|
||||
RED + source.requestedVersionRange() + RESET + " - " +
|
||||
GREEN + source.includedVersion() + RESET;
|
||||
joiner.add(error);
|
||||
}
|
||||
|
||||
errors.add(new ExceptionData(message, failure.identifier().group() + ":" + failure.identifier().artifact(), joiner.toString()));
|
||||
}
|
||||
|
||||
return new EarlyLoadingException(failures.size() + " Dependency restrictions were not met.", null, errors);
|
||||
}
|
||||
}
|
||||
}
|
||||
+28
-37
@@ -5,7 +5,6 @@
|
||||
|
||||
package net.minecraftforge.fml.loading.moddiscovery;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.mojang.logging.LogUtils;
|
||||
import cpw.mods.modlauncher.Launcher;
|
||||
import cpw.mods.modlauncher.api.IModuleLayerManager;
|
||||
@@ -35,41 +34,36 @@ import java.util.ServiceLoader;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ApiStatus.Internal
|
||||
public class ModDiscoverer {
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
private final ServiceLoader<IModLocator> modLocators;
|
||||
private final ServiceLoader<IDependencyLocator> dependencyLocators;
|
||||
private final List<IModLocator> modLocatorList;
|
||||
private final List<IDependencyLocator> dependencyLocatorList;
|
||||
public final class ModDiscoverer {
|
||||
private ModDiscoverer() {}
|
||||
|
||||
public ModDiscoverer(Map<String, ?> arguments) {
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
public static ModValidator discoverMods(Map<String, ?> arguments) {
|
||||
Launcher.INSTANCE.environment().computePropertyIfAbsent(Environment.Keys.MODDIRECTORYFACTORY.get(), v->ModsFolderLocator::new);
|
||||
Launcher.INSTANCE.environment().computePropertyIfAbsent(Environment.Keys.PROGRESSMESSAGE.get(), v-> StartupNotificationManager.locatorConsumer().orElseGet(()-> s->{}));
|
||||
final var moduleLayerManager = Launcher.INSTANCE.environment().findModuleLayerManager().orElseThrow();
|
||||
modLocators = ServiceLoader.load(moduleLayerManager.getLayer(IModuleLayerManager.Layer.SERVICE).orElseThrow(), IModLocator.class);
|
||||
dependencyLocators = ServiceLoader.load(moduleLayerManager.getLayer(IModuleLayerManager.Layer.SERVICE).orElseThrow(), IDependencyLocator.class);
|
||||
modLocatorList = ServiceLoaderUtils.streamServiceLoader(()-> modLocators, sce->LOGGER.error("Failed to load mod locator list", sce)).collect(Collectors.toList());
|
||||
ServiceLoader<IModLocator> modLocators = ServiceLoader.load(moduleLayerManager.getLayer(IModuleLayerManager.Layer.SERVICE).orElseThrow(), IModLocator.class);
|
||||
ServiceLoader<IDependencyLocator> dependencyLocators = ServiceLoader.load(moduleLayerManager.getLayer(IModuleLayerManager.Layer.SERVICE).orElseThrow(), IDependencyLocator.class);
|
||||
List<IModLocator> modLocatorList = ServiceLoaderUtils.streamWithErrorHandling(modLocators, sce -> LOGGER.error("Failed to load mod locator list", sce)).toList();
|
||||
for (IModLocator iModLocator : modLocatorList) {
|
||||
iModLocator.initArguments(arguments);
|
||||
}
|
||||
dependencyLocatorList = ServiceLoaderUtils.streamServiceLoader(()-> dependencyLocators, sce->LOGGER.error("Failed to load dependency locator list", sce)).collect(Collectors.toList());
|
||||
List<IDependencyLocator> dependencyLocatorList = ServiceLoaderUtils.streamWithErrorHandling(dependencyLocators, sce -> LOGGER.error("Failed to load dependency locator list", sce)).toList();
|
||||
for (IDependencyLocator l : dependencyLocatorList) {
|
||||
l.initArguments(arguments);
|
||||
}
|
||||
if (LOGGER.isDebugEnabled(LogMarkers.CORE))
|
||||
{
|
||||
if (LOGGER.isDebugEnabled(LogMarkers.CORE)) {
|
||||
LOGGER.debug(LogMarkers.CORE, "Found Mod Locators : {}", modLocatorList.stream()
|
||||
.map(modLocator -> "(%s:%s)".formatted(modLocator.name(),
|
||||
modLocator.getClass().getPackage().getImplementationVersion())).collect(Collectors.joining(",")));
|
||||
.map(modLocator -> "(%s:%s)".formatted(modLocator.name(),
|
||||
modLocator.getClass().getPackage().getImplementationVersion())).collect(Collectors.joining(",")));
|
||||
|
||||
LOGGER.debug(LogMarkers.CORE, "Found Dependency Locators : {}", dependencyLocatorList.stream()
|
||||
.map(dependencyLocator -> "(%s:%s)".formatted(dependencyLocator.name(),
|
||||
dependencyLocator.getClass().getPackage().getImplementationVersion())).collect(Collectors.joining(",")));
|
||||
.map(dependencyLocator -> "(%s:%s)".formatted(dependencyLocator.name(),
|
||||
dependencyLocator.getClass().getPackage().getImplementationVersion())).collect(Collectors.joining(",")));
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
public ModValidator discoverMods() {
|
||||
LOGGER.debug(LogMarkers.SCAN,"Scanning for mods and other resources to load. We know {} ways to find mods", modLocatorList.size());
|
||||
List<ModFile> loadedFiles = new ArrayList<>();
|
||||
List<EarlyLoadingException.ExceptionData> discoveryErrorData = new ArrayList<>();
|
||||
@@ -117,7 +111,7 @@ public class ModDiscoverer {
|
||||
}
|
||||
|
||||
LOGGER.debug(LogMarkers.SCAN, "Locator {} found {} valid mod files", locator, locatedFiles.size());
|
||||
handleLocatedFiles(loadedFiles, locatedFiles);
|
||||
handleLocatedFiles(loadedFiles, locatedFiles, locator);
|
||||
} catch (InvalidModFileException imfe) {
|
||||
// We don't generally expect this exception, since it should come from the candidates stream above and be handled in the Locator, but just in case.
|
||||
LOGGER.error(LogMarkers.SCAN, "Locator {} found an invalid mod file {}", locator, imfe.getBrokenFile(), imfe);
|
||||
@@ -131,8 +125,7 @@ public class ModDiscoverer {
|
||||
//First processing run of the mod list. Any duplicates will cause resolution failure and dependency loading will be skipped.
|
||||
Map<IModFile.Type, List<ModFile>> modFilesMap = new EnumMap<>(IModFile.Type.class);
|
||||
try {
|
||||
final UniqueModListBuilder modsUniqueListBuilder = new UniqueModListBuilder(loadedFiles);
|
||||
final UniqueModListBuilder.UniqueModListData uniqueModsData = modsUniqueListBuilder.buildUniqueList();
|
||||
final UniqueModListBuilder.UniqueModListData uniqueModsData = UniqueModListBuilder.buildUniqueList(loadedFiles);
|
||||
|
||||
//Grab the temporary results.
|
||||
//This allows loading to continue to a base state, in case dependency loading fails.
|
||||
@@ -152,14 +145,10 @@ public class ModDiscoverer {
|
||||
for (IDependencyLocator locator : dependencyLocatorList) {
|
||||
try {
|
||||
LOGGER.debug(LogMarkers.SCAN,"Trying locator {}", locator);
|
||||
final List<IModFile> locatedMods = ImmutableList.copyOf(loadedFiles);
|
||||
final List<IModFile> locatedMods = List.copyOf(loadedFiles);
|
||||
|
||||
var locatedFiles = locator.scanMods(locatedMods);
|
||||
if (locatedFiles.stream().anyMatch(file -> !(file instanceof ModFile))) {
|
||||
LOGGER.error(LogMarkers.SCAN, "A dependency locator returned a file which is not a ModFile instance!. They will be skipped!");
|
||||
}
|
||||
|
||||
handleLocatedFiles(loadedFiles, locatedFiles);
|
||||
handleLocatedFiles(loadedFiles, locatedFiles, locator);
|
||||
}
|
||||
catch (EarlyLoadingException exception) {
|
||||
LOGGER.error(LogMarkers.SCAN, "Failed to load dependencies with locator {}", locator, exception);
|
||||
@@ -169,8 +158,7 @@ public class ModDiscoverer {
|
||||
|
||||
//Second processing run of the mod list. Any duplicates will cause resolution failure and only the mods list will be loaded.
|
||||
try {
|
||||
final UniqueModListBuilder modsAndDependenciesUniqueListBuilder = new UniqueModListBuilder(loadedFiles);
|
||||
final UniqueModListBuilder.UniqueModListData uniqueModsAndDependenciesData = modsAndDependenciesUniqueListBuilder.buildUniqueList();
|
||||
final UniqueModListBuilder.UniqueModListData uniqueModsAndDependenciesData = UniqueModListBuilder.buildUniqueList(loadedFiles);
|
||||
|
||||
//We now only need the mod files map, not the list.
|
||||
modFilesMap = uniqueModsAndDependenciesData.modFiles().stream()
|
||||
@@ -193,11 +181,14 @@ public class ModDiscoverer {
|
||||
return validator;
|
||||
}
|
||||
|
||||
private static void handleLocatedFiles(final List<ModFile> loadedFiles, final List<IModFile> locatedFiles) {
|
||||
var locatedModFiles = locatedFiles.stream().filter(ModFile.class::isInstance).map(ModFile.class::cast).toList();
|
||||
for (IModFile mf : locatedModFiles) {
|
||||
LOGGER.info(LogMarkers.SCAN, "Found mod file {} of type {} with provider {}", mf.getFileName(), mf.getType(), mf.getProvider());
|
||||
private static void handleLocatedFiles(final List<ModFile> loadedFiles, final List<IModFile> locatedFiles, final Object locator) {
|
||||
for (IModFile mf : locatedFiles) {
|
||||
if (mf instanceof ModFile modFile) {
|
||||
LOGGER.info(LogMarkers.SCAN, "Found mod file {} of type {} with provider {}", mf.getFileName(), mf.getType(), mf.getProvider());
|
||||
loadedFiles.add(modFile);
|
||||
} else {
|
||||
LOGGER.error(LogMarkers.SCAN, "Skipping mod file {} found by {}, as it was not a ModFile instance!", mf.getFileName(), locator);
|
||||
}
|
||||
}
|
||||
loadedFiles.addAll(locatedModFiles);
|
||||
}
|
||||
}
|
||||
@@ -21,13 +21,13 @@ import org.apache.maven.artifact.versioning.ArtifactVersion;
|
||||
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
@@ -98,7 +98,7 @@ public class ModFile implements IModFile {
|
||||
|
||||
public boolean identifyMods() {
|
||||
if (this.modFileInfo == null) return this.getType() != Type.MOD;
|
||||
LOGGER.debug(LogMarkers.LOADING,"Loading mod file {} with languages {}", this.getFilePath(), this.modFileInfo.requiredLanguageLoaders());
|
||||
LOGGER.debug(LogMarkers.LOADING,"Loading mod file {} with languages {}", this.getFileName(), this.modFileInfo.requiredLanguageLoaders());
|
||||
this.coreMods = ModFileParser.getCoreMods(this);
|
||||
if (!this.coreMods.isEmpty())
|
||||
LOGGER.warn("Mod file {} contains javascript coremods! JS CoreMods are deprecated and will be removed in a future release. Consider using Mixin or ModLauncher transformers.", this.getFileName());
|
||||
@@ -179,12 +179,28 @@ public class ModFile implements IModFile {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Mod File: " + Objects.toString(this.jar.getPrimaryPath());
|
||||
var path = getFilePath();
|
||||
return "Mod File: " + path == null ? "null" : path.toUri().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFileName() {
|
||||
return getFilePath().getFileName().toString();
|
||||
// This is only used for debugging, so lets try and make it a useful debug
|
||||
var path = getFilePath();
|
||||
var filename = path.getFileName();
|
||||
|
||||
// ZipFileSystem toString will return the outer file path
|
||||
var ret = filename == null ? path.getFileSystem().toString() : filename.toString();
|
||||
|
||||
// If it's a directory on the default file system, return the full path
|
||||
if (Files.isDirectory(path) && path.getFileSystem() == FileSystems.getDefault())
|
||||
ret = path.toAbsolutePath().toString();
|
||||
|
||||
// If we are in a FileSystem 'root' like UnionFS, return the full uri
|
||||
if (ret.isEmpty() || ret.equals("/"))
|
||||
ret = path.toUri().toString();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+1
-1
@@ -264,7 +264,7 @@ public class ModFileInfo implements IModFileInfo, IConfigurable {
|
||||
|
||||
@Override
|
||||
public String versionString() {
|
||||
return getMods().get(0).getVersion().toString();
|
||||
return getMods().getFirst().getVersion().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+4
-4
@@ -34,7 +34,6 @@ public class ModValidator {
|
||||
private LoadingModList loadingModList;
|
||||
private final List<IModFile> brokenFiles = new ArrayList<>();
|
||||
private final List<EarlyLoadingException.ExceptionData> discoveryErrorData;
|
||||
private final List<ModFileLoadingException> modFileLoadingExceptions;
|
||||
|
||||
public ModValidator(Map<IModFile.Type, List<ModFile>> modFiles, List<IModFileInfo> brokenFiles, List<EarlyLoadingException.ExceptionData> discoveryErrorData, List<ModFileLoadingException> modFileLoadingExceptions) {
|
||||
this.candidateMods = lst(modFiles.get(IModFile.Type.MOD));
|
||||
@@ -44,7 +43,6 @@ public class ModValidator {
|
||||
this.candidatePlugins.addAll(lst(modFiles.get(IModFile.Type.LIBRARY)));
|
||||
this.discoveryErrorData = discoveryErrorData;
|
||||
this.brokenFiles.addAll(brokenFiles.stream().map(IModFileInfo::getFile).toList());
|
||||
this.modFileLoadingExceptions = modFileLoadingExceptions;
|
||||
}
|
||||
|
||||
public ModValidator(Map<IModFile.Type, List<ModFile>> modFiles, List<IModFileInfo> brokenFiles, List<EarlyLoadingException.ExceptionData> discoveryErrorData) {
|
||||
@@ -52,7 +50,9 @@ public class ModValidator {
|
||||
}
|
||||
|
||||
private static List<ModFile> lst(List<ModFile> files) {
|
||||
return files == null ? new ArrayList<>() : new ArrayList<>(files);
|
||||
if (files == null) return new ArrayList<>();
|
||||
if (files instanceof ArrayList) return files;
|
||||
return new ArrayList<>(files);
|
||||
}
|
||||
|
||||
public void stage1Validation() {
|
||||
@@ -60,7 +60,7 @@ public class ModValidator {
|
||||
if (LOGGER.isDebugEnabled(LogMarkers.SCAN)) {
|
||||
LOGGER.debug(LogMarkers.SCAN, "Found {} mod files with {} mods", candidateMods.size(), candidateMods.stream().mapToInt(mf -> mf.getModInfos().size()).sum());
|
||||
}
|
||||
ImmediateWindowHandler.updateProgress("Found "+candidateMods.size()+" mod candidates");
|
||||
ImmediateWindowHandler.updateProgress("Found " + candidateMods.size() + " mod candidates");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"options": [
|
||||
{
|
||||
"resource": "com/llamalad7/mixinextras/platform/forge/MixinExtrasConfigPlugin.class",
|
||||
"layer": "GAMELIBRARY",
|
||||
"id": "mixinextras",
|
||||
"deps": [
|
||||
{
|
||||
"identifier": {
|
||||
"group": "com.github.LlamaLad7",
|
||||
"artifact": "MixinExtras"
|
||||
},
|
||||
"version": {
|
||||
"range": "[0.5.0,)",
|
||||
"artifactVersion": "0.5.0"
|
||||
},
|
||||
"path": "",
|
||||
"isObfuscated": false
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"identifier": {
|
||||
"group": "io.github.llamalad7",
|
||||
"artifact": "mixinextras-forge"
|
||||
},
|
||||
"version": {
|
||||
"artifactVersion": "0.5.0"
|
||||
},
|
||||
"path": "",
|
||||
"isObfuscated": false
|
||||
},
|
||||
"nested": true
|
||||
}
|
||||
]
|
||||
}
|
||||
-1
@@ -10,7 +10,6 @@ import cpw.mods.modlauncher.api.ITransformationService;
|
||||
import cpw.mods.modlauncher.api.ITransformer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ plugins {
|
||||
id 'eclipse'
|
||||
id 'idea'
|
||||
id 'maven-publish'
|
||||
id 'net.minecraftforge.gradle' version '[6.0.36,6.2)'
|
||||
id 'net.minecraftforge.gradle' version '[6.0.46,6.2)'
|
||||
}
|
||||
|
||||
version = mod_version
|
||||
|
||||
@@ -82,11 +82,31 @@
|
||||
}
|
||||
|
||||
public void setComponentTooltipForNextFrame(Font p_408650_, List<Component> p_407512_, int p_406019_, int p_408123_, @Nullable ResourceLocation p_409993_) {
|
||||
@@ -1069,11 +_,13 @@
|
||||
ClientTooltipPositioner p_409451_,
|
||||
@Nullable ResourceLocation p_409298_
|
||||
@@ -1056,9 +_,14 @@
|
||||
) {
|
||||
+ var preEvent = net.minecraftforge.client.ForgeHooksClient.onRenderTooltipPre(this.tooltipStack, this, p_283678_, p_281696_, guiWidth(), guiHeight(), p_282716_, p_283128_, p_409451_);
|
||||
if (!p_409928_.isEmpty()) {
|
||||
if (this.deferredTooltip == null || p_410618_) {
|
||||
- this.deferredTooltip = () -> this.renderTooltip(p_406605_, p_409928_, p_407794_, p_408784_, p_408798_, p_407676_);
|
||||
+ final var stackContext = this.tooltipStack;
|
||||
+ this.deferredTooltip = () -> this.renderTooltip(p_406605_, p_409928_, p_407794_, p_408784_, p_408798_, p_407676_, stackContext);
|
||||
}
|
||||
}
|
||||
+ }
|
||||
+
|
||||
+ public void renderTooltip(Font p_283128_, List<ClientTooltipComponent> p_282716_, int p_283678_, int p_281696_, ClientTooltipPositioner p_409451_, @Nullable ResourceLocation p_409298_) {
|
||||
+ renderTooltip(p_283128_, p_282716_, p_283678_, p_281696_, p_409451_, p_409298_, ItemStack.EMPTY);
|
||||
}
|
||||
|
||||
public void renderTooltip(
|
||||
@@ -1067,13 +_,16 @@
|
||||
int p_283678_,
|
||||
int p_281696_,
|
||||
ClientTooltipPositioner p_409451_,
|
||||
- @Nullable ResourceLocation p_409298_
|
||||
+ @Nullable ResourceLocation p_409298_,
|
||||
+ ItemStack itemstack
|
||||
) {
|
||||
+ var preEvent = net.minecraftforge.client.ForgeHooksClient.onRenderTooltipPre(itemstack, this, p_283678_, p_281696_, guiWidth(), guiHeight(), p_282716_, p_283128_, p_409451_, p_409298_);
|
||||
+ if (preEvent == null) return;
|
||||
int i = 0;
|
||||
int j = p_282716_.size() == 1 ? -2 : 0;
|
||||
@@ -97,7 +117,7 @@
|
||||
if (k > i) {
|
||||
i = k;
|
||||
}
|
||||
@@ -1083,16 +_,17 @@
|
||||
@@ -1083,16 +_,16 @@
|
||||
|
||||
int l1 = i;
|
||||
int i2 = j;
|
||||
@@ -107,8 +127,7 @@
|
||||
int i1 = vector2ic.y();
|
||||
this.pose.pushMatrix();
|
||||
- TooltipRenderUtil.renderTooltipBackground(this, l, i1, i, j, p_409298_);
|
||||
+ var background_event = net.minecraftforge.client.event.ForgeEventFactoryClient.onRenderTooltipBackground(this.tooltipStack, this, l, i1, preEvent.getFont(), p_282716_, p_409298_);
|
||||
+ TooltipRenderUtil.renderTooltipBackground(this, l, i1, i, j, background_event.getBackground());
|
||||
+ TooltipRenderUtil.renderTooltipBackground(this, l, i1, i, j, preEvent.getBackground());
|
||||
int j1 = i1;
|
||||
|
||||
for (int k1 = 0; k1 < p_282716_.size(); k1++) {
|
||||
|
||||
@@ -1,14 +1,5 @@
|
||||
--- a/net/minecraft/client/particle/ParticleEngine.java
|
||||
+++ b/net/minecraft/client/particle/ParticleEngine.java
|
||||
@@ -26,7 +_,7 @@
|
||||
public class ParticleEngine {
|
||||
private static final List<ParticleRenderType> RENDER_ORDER = List.of(ParticleRenderType.SINGLE_QUADS, ParticleRenderType.ITEM_PICKUP, ParticleRenderType.ELDER_GUARDIANS);
|
||||
protected ClientLevel level;
|
||||
- private final Map<ParticleRenderType, ParticleGroup<?>> particles = Maps.newIdentityHashMap();
|
||||
+ private final Map<ParticleRenderType, ParticleGroup<?>> particles = Maps.newTreeMap(net.minecraftforge.client.ForgeHooksClient.makeParticleRenderTypeComparator(RENDER_ORDER));
|
||||
private final Queue<TrackingEmitter> trackingEmitters = Queues.newArrayDeque();
|
||||
private final Queue<Particle> particlesToAdd = Queues.newArrayDeque();
|
||||
private final Object2IntOpenHashMap<ParticleLimit> trackedParticleCounts = new Object2IntOpenHashMap<>();
|
||||
@@ -63,7 +_,7 @@
|
||||
private <T extends ParticleOptions> Particle makeParticle(
|
||||
T p_107396_, double p_107397_, double p_107398_, double p_107399_, double p_107400_, double p_107401_, double p_107402_
|
||||
@@ -18,12 +9,34 @@
|
||||
return particleprovider == null
|
||||
? null
|
||||
: particleprovider.createParticle(p_107396_, this.level, p_107397_, p_107398_, p_107399_, p_107400_, p_107401_, p_107402_, this.random);
|
||||
@@ -123,7 +_,7 @@
|
||||
@@ -113,6 +_,8 @@
|
||||
return new ItemPickupParticleGroup(this);
|
||||
} else if (p_428647_ == ParticleRenderType.ELDER_GUARDIANS) {
|
||||
return new ElderGuardianParticleGroup(this);
|
||||
+ } else if (factories.containsKey(p_428647_)) {
|
||||
+ return factories.get(p_428647_).apply(this);
|
||||
} else {
|
||||
return (ParticleGroup<?>)(p_428647_ == ParticleRenderType.NO_RENDER ? new NoRenderParticleGroup(this) : new QuadParticleGroup(this, p_428647_));
|
||||
}
|
||||
@@ -122,8 +_,20 @@
|
||||
this.trackedParticleCounts.addTo(p_423291_, p_172283_);
|
||||
}
|
||||
|
||||
+
|
||||
+ private static final Map<ParticleRenderType, java.util.function.Function<ParticleEngine, ParticleGroup<?>>> factories = new java.util.HashMap<>();
|
||||
+ private static final List<ParticleRenderType> particleRenderOrder = new java.util.ArrayList<>(RENDER_ORDER);
|
||||
+
|
||||
+ // Forge: allow custom particle groups
|
||||
+ public static void registerParticleGroup(ParticleRenderType type, java.util.function.Function<ParticleEngine, ParticleGroup<?>> factory) {
|
||||
+ if (factories.putIfAbsent(type, factory) != null) {
|
||||
+ throw new IllegalArgumentException(type.name() + " already has a factory registered. Previous factory was " + factories.get(type) + ". This factory was " + factory);
|
||||
+ }
|
||||
+ particleRenderOrder.add(type);
|
||||
+ }
|
||||
+
|
||||
public void extract(ParticlesRenderState p_423938_, Frustum p_424803_, Camera p_430521_, float p_426823_) {
|
||||
- for (ParticleRenderType particlerendertype : RENDER_ORDER) {
|
||||
+ for (ParticleRenderType particlerendertype : this.particles.keySet()) { // Forge: allow custom ParticleRenderType's
|
||||
+ for (ParticleRenderType particlerendertype : particleRenderOrder) { // Forge: allow custom ParticleRenderType's
|
||||
ParticleGroup<?> particlegroup = this.particles.get(particlerendertype);
|
||||
if (particlegroup != null && !particlegroup.isEmpty()) {
|
||||
p_423938_.add(particlegroup.extractRenderState(p_424803_, p_430521_, p_426823_));
|
||||
@@ -25,7 +25,7 @@
|
||||
}
|
||||
|
||||
this.addLateDebugPass(framegraphbuilder, this.levelRenderState.cameraRenderState.pos, p_407881_, frustum);
|
||||
+ net.minecraftforge.client.FramePassManager.insertForgePasses(framegraphbuilder, targets); // Forge: Modded passes are inserted here.
|
||||
+ net.minecraftforge.client.FramePassManager.insertForgePasses(framegraphbuilder, targets, this.levelRenderState); // Forge: Modded passes are inserted here.
|
||||
profilerfiller.popPush("executeFrameGraph");
|
||||
framegraphbuilder.execute(p_367325_, new FrameGraphBuilder.Inspector() {
|
||||
@Override
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
+ }
|
||||
+
|
||||
+ static {
|
||||
+ if (net.minecraftforge.fml.ModLoader.isLoadingStateValid() && !net.minecraftforge.fml.ModLoader.get().hasCompletedState("LOAD_REGISTRIES")) {
|
||||
+ if (net.minecraftforge.fml.ModLoader.isLoadingStateValid() && !net.minecraftforge.fml.ModLoader.get().hasCompletedState(net.minecraftforge.common.ForgeStatesProvider.LOAD_REGISTRIES)) {
|
||||
+ com.mojang.logging.LogUtils.getLogger().error(
|
||||
+ "net.minecraft.client.renderer.Sheets loaded too early, modded registry-based materials may not work correctly",
|
||||
+ new IllegalStateException("net.minecraft.client.renderer.Sheets loaded too early")
|
||||
|
||||
@@ -45,9 +45,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
@@ -510,6 +_,13 @@
|
||||
if (this.gui == null) {
|
||||
this.gui = MinecraftServerGui.showFrameFor(this);
|
||||
@@ -439,6 +_,13 @@
|
||||
LOGGER.error("Interrupted while stopping the management server", (Throwable)interruptedexception);
|
||||
}
|
||||
}
|
||||
+
|
||||
+ if (this.dediLanPinger != null) {
|
||||
|
||||
@@ -238,7 +238,12 @@
|
||||
p_409579_.addFreshEntity(itementity);
|
||||
return itementity;
|
||||
}
|
||||
@@ -2177,7 +_,7 @@
|
||||
@@ -2173,11 +_,11 @@
|
||||
}
|
||||
|
||||
ItemStack itemstack = p_19978_.getItemInHand(p_19979_);
|
||||
- if (itemstack.is(Items.SHEARS) && this.shearOffAllLeashConnections(p_19978_)) {
|
||||
+ if (itemstack.canPerformAction(net.minecraftforge.common.ToolActions.SHEARS_HARVEST) && this.shearOffAllLeashConnections(p_19978_)) {
|
||||
itemstack.hurtAndBreak(1, p_19978_, p_19979_);
|
||||
return InteractionResult.SUCCESS;
|
||||
} else if (this instanceof Mob mob
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
--- a/net/minecraft/world/entity/animal/coppergolem/CopperGolem.java
|
||||
+++ b/net/minecraft/world/entity/animal/coppergolem/CopperGolem.java
|
||||
@@ -216,7 +_,7 @@
|
||||
}
|
||||
|
||||
Level level = this.level();
|
||||
- if (itemstack.is(Items.SHEARS) && this.readyForShearing()) {
|
||||
+ if (false && itemstack.is(Items.SHEARS) && this.readyForShearing()) { // Forge: Moved to onSheared
|
||||
if (level instanceof ServerLevel serverlevel) {
|
||||
this.shear(serverlevel, SoundSource.PLAYERS, itemstack);
|
||||
this.gameEvent(GameEvent.SHEAR, p_430585_);
|
||||
@@ -429,6 +_,18 @@
|
||||
ItemStack itemstack = this.getItemBySlot(EQUIPMENT_SLOT_ANTENNA);
|
||||
this.setItemSlot(EQUIPMENT_SLOT_ANTENNA, ItemStack.EMPTY);
|
||||
this.spawnAtLocation(p_428037_, itemstack, 1.5F);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public java.util.List<ItemStack> onSheared(@Nullable Player player, ItemStack item, Level world, net.minecraft.core.BlockPos pos, int fortune) {
|
||||
+ if (world instanceof ServerLevel server) {
|
||||
+ server.playSound(null, this, SoundEvents.COPPER_GOLEM_SHEAR, player == null ? SoundSource.BLOCKS : SoundSource.PLAYERS, 1.0F, 1.0F);
|
||||
+ var ret = new java.util.ArrayList<ItemStack>();
|
||||
+ ret.add(this.getItemBySlot(EQUIPMENT_SLOT_ANTENNA));
|
||||
+ this.setItemSlot(EQUIPMENT_SLOT_ANTENNA, ItemStack.EMPTY);
|
||||
+ return ret;
|
||||
+ }
|
||||
+ return java.util.Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
--- a/net/minecraft/world/entity/decoration/LeashFenceKnotEntity.java
|
||||
+++ b/net/minecraft/world/entity/decoration/LeashFenceKnotEntity.java
|
||||
@@ -72,7 +_,7 @@
|
||||
if (this.level().isClientSide()) {
|
||||
return InteractionResult.SUCCESS;
|
||||
} else {
|
||||
- if (p_31842_.getItemInHand(p_31843_).is(Items.SHEARS)) {
|
||||
+ if (p_31842_.getItemInHand(p_31843_).canPerformAction(net.minecraftforge.common.ToolActions.SHEARS_HARVEST)) {
|
||||
InteractionResult interactionresult = super.interact(p_31842_, p_31843_);
|
||||
if (interactionresult instanceof InteractionResult.Success interactionresult$success && interactionresult$success.wasItemInteraction()) {
|
||||
return interactionresult;
|
||||
+6
-2
@@ -27,7 +27,7 @@ dependencyResolutionManagement {
|
||||
|
||||
library('forgespi', 'net.minecraftforge:forgespi:7.1.5') // Needs modlauncher
|
||||
library('modlauncher', 'net.minecraftforge:modlauncher:10.2.4') // Needs securemodules
|
||||
library('securemodules', 'net.minecraftforge:securemodules:2.2.21') // Needs unsafe
|
||||
library('securemodules', 'net.minecraftforge:securemodules:2.2.23') // Needs unsafe
|
||||
library('unsafe', 'net.minecraftforge:unsafe:0.9.2')
|
||||
library('accesstransformers', 'net.minecraftforge:accesstransformers:8.2.2')
|
||||
library('coremods', 'net.minecraftforge:coremods:5.2.6')
|
||||
@@ -36,7 +36,11 @@ dependencyResolutionManagement {
|
||||
library('eventbus-validator', 'net.minecraftforge:eventbus-validator:7.0-beta.12')
|
||||
library('mergetool-api', 'net.minecraftforge:mergetool-api:1.0')
|
||||
|
||||
library('roimfs', 'net.minecraftforge:roimfs:1.0.0') // ReadOnlyInMemoryFileSystem - Used for JarInJar extraction at runtime
|
||||
library('mixin', 'org.spongepowered:mixin:0.8.7')
|
||||
version('mixinextras', '0.5.0')
|
||||
library('mixinextras-forge', 'io.github.llamalad7', 'mixinextras-forge').versionRef('mixinextras')
|
||||
library('mixinextras-common', 'io.github.llamalad7', 'mixinextras-common').versionRef('mixinextras')
|
||||
library('jetbrains-annotations', 'org.jetbrains:annotations:24.1.0') // for ApiStatus annotations
|
||||
library('jspecify', 'org.jspecify:jspecify:1.0.0') // for nullability annotations
|
||||
library('slf4j-api', 'org.slf4j:slf4j-api:2.0.7')
|
||||
@@ -76,7 +80,7 @@ dependencyResolutionManagement {
|
||||
bundle('night-config', ['night-config-toml', 'night-config-core'])
|
||||
|
||||
// Jar in Jar FileSystem
|
||||
version('jarjar', '0.3.26')
|
||||
version('jarjar', '0.4.2')
|
||||
library('jarjar-fs', 'net.minecraftforge', 'JarJarFileSystems').versionRef('jarjar')
|
||||
library('jarjar-meta', 'net.minecraftforge', 'JarJarMetadata' ).versionRef('jarjar')
|
||||
library('jarjar-selector', 'net.minecraftforge', 'JarJarSelector' ).versionRef('jarjar')
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
"values": [
|
||||
"#c:nuggets/gold",
|
||||
"#c:nuggets/iron",
|
||||
"#c:nuggets/copper",
|
||||
{
|
||||
"id": "#forge:nuggets",
|
||||
"required": false
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"values": [
|
||||
"minecraft:copper_nugget"
|
||||
]
|
||||
}
|
||||
@@ -658,8 +658,8 @@ public class ForgeHooksClient {
|
||||
return stackFont == null ? fallbackFont : stackFont;
|
||||
}
|
||||
|
||||
public static @Nullable RenderTooltipEvent.Pre onRenderTooltipPre(@NotNull ItemStack stack, GuiGraphics graphics, int x, int y, int screenWidth, int screenHeight, @NotNull List<ClientTooltipComponent> components, @NotNull Font fallbackFont, @NotNull ClientTooltipPositioner positioner) {
|
||||
var preEvent = new RenderTooltipEvent.Pre(stack, graphics, x, y, screenWidth, screenHeight, getTooltipFont(stack, fallbackFont), components, positioner);
|
||||
public static @Nullable RenderTooltipEvent.Pre onRenderTooltipPre(@NotNull ItemStack stack, GuiGraphics graphics, int x, int y, int screenWidth, int screenHeight, @NotNull List<ClientTooltipComponent> components, @NotNull Font fallbackFont, @NotNull ClientTooltipPositioner positioner, @Nullable ResourceLocation background) {
|
||||
var preEvent = new RenderTooltipEvent.Pre(stack, graphics, x, y, screenWidth, screenHeight, getTooltipFont(stack, fallbackFont), components, positioner, background);
|
||||
return RenderTooltipEvent.Pre.BUS.post(preEvent) ? null : preEvent;
|
||||
}
|
||||
|
||||
@@ -729,20 +729,6 @@ public class ForgeHooksClient {
|
||||
return font.split(text, maxWidth).stream().map(ClientTooltipComponent::create);
|
||||
}
|
||||
|
||||
public static Comparator<ParticleRenderType> makeParticleRenderTypeComparator(List<ParticleRenderType> renderOrder) {
|
||||
Comparator<ParticleRenderType> vanillaComparator = Comparator.comparingInt(renderOrder::indexOf);
|
||||
return (typeOne, typeTwo) -> {
|
||||
boolean vanillaOne = renderOrder.contains(typeOne);
|
||||
boolean vanillaTwo = renderOrder.contains(typeTwo);
|
||||
|
||||
if (vanillaOne && vanillaTwo)
|
||||
return vanillaComparator.compare(typeOne, typeTwo);
|
||||
if (!vanillaOne && !vanillaTwo)
|
||||
return Integer.compare(System.identityHashCode(typeOne), System.identityHashCode(typeTwo));
|
||||
return vanillaOne ? -1 : 1;
|
||||
};
|
||||
}
|
||||
|
||||
public static boolean isBlockInSolidLayer(BlockState state) {
|
||||
var model = Minecraft.getInstance().getBlockRenderer().getBlockModel(state);
|
||||
return model.getRenderTypes(state, RandomSource.create(), ModelData.EMPTY).contains(ChunkSectionLayer.SOLID);
|
||||
|
||||
@@ -7,9 +7,12 @@ package net.minecraftforge.client;
|
||||
|
||||
import com.mojang.blaze3d.framegraph.FrameGraphBuilder;
|
||||
import com.mojang.blaze3d.framegraph.FramePass;
|
||||
import com.mojang.blaze3d.resource.ResourceHandle;
|
||||
import net.minecraft.client.renderer.LevelTargetBundle;
|
||||
import net.minecraft.client.renderer.state.LevelRenderState;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -27,18 +30,43 @@ public class FramePassManager {
|
||||
|
||||
// Note: Pass order is determined automatically within FrameGraphBuilder. It's unclear what must be done to guarantee ordering.
|
||||
@ApiStatus.Internal
|
||||
public static void insertForgePasses(FrameGraphBuilder graphBuilder, LevelTargetBundle bundle) {
|
||||
public static void insertForgePasses(FrameGraphBuilder graphBuilder, LevelTargetBundle bundle, LevelRenderState state) {
|
||||
for (PassInfo info : addedPasses) {
|
||||
FramePass pass = graphBuilder.addPass(info.name);
|
||||
PassDefinition forgePass = info.pass;
|
||||
forgePass.targets(bundle, pass);
|
||||
pass.executes(forgePass::executes);
|
||||
forgePass.extracts(bundle, pass);
|
||||
pass.executes(() -> forgePass.executes(state));
|
||||
}
|
||||
}
|
||||
|
||||
public interface PassDefinition {
|
||||
void targets(LevelTargetBundle bundle, FramePass pass);
|
||||
void executes();
|
||||
/**
|
||||
* Use to define which targets your pass will bind against, see {@link FramePass#reads} and {@link FramePass#readsAndWrites}
|
||||
* A FramePass must bind to at least ONE target. Otherwise, you get freaky issues with >1 modded passes.
|
||||
* Additionally, this method should be used for extracting render states into instance variables if desired.
|
||||
*/
|
||||
default void extracts(LevelTargetBundle bundle, FramePass pass) {
|
||||
targets(bundle, pass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer {@link PassDefinition#extracts}
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "1.21.10")
|
||||
default void targets(LevelTargetBundle bundle, FramePass pass) {}
|
||||
|
||||
/**
|
||||
* Use to define what your pass does during the render stage.
|
||||
*/
|
||||
default void executes(LevelRenderState state) {
|
||||
executes();
|
||||
};
|
||||
|
||||
/**
|
||||
* Use to define what your pass does during the render stage, prefer {@link PassDefinition#executes(LevelRenderState)}.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "1.21.10")
|
||||
default void executes(){};
|
||||
}
|
||||
|
||||
private record PassInfo(String name, PassDefinition pass){}
|
||||
|
||||
@@ -29,9 +29,7 @@ public record AddGuiOverlayLayersEvent(ForgeLayeredDraw getLayeredDraw) implemen
|
||||
@ApiStatus.Internal
|
||||
public AddGuiOverlayLayersEvent {}
|
||||
|
||||
/**
|
||||
* @deprecated {@link AddGuiOverlayLayersEvent} is no longer an {@link IModBusEvent}, so use {@link #BUS} directly.
|
||||
*/
|
||||
/** @deprecated {@link AddGuiOverlayLayersEvent} is no longer an {@link IModBusEvent}, so use {@link #BUS} directly. */
|
||||
@Deprecated(forRemoval = true, since = "1.21.9")
|
||||
public static EventBus<AddGuiOverlayLayersEvent> getBus(BusGroup modBusGroup) {
|
||||
return BUS;
|
||||
|
||||
@@ -12,16 +12,19 @@ import net.minecraftforge.eventbus.api.event.MutableEvent;
|
||||
import net.minecraftforge.eventbus.api.event.characteristic.Cancellable;
|
||||
import net.minecraftforge.fml.LogicalSide;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Fired when the client is about to send a chat message to the server.
|
||||
*
|
||||
* <p>This event is {@linkplain Cancelable cancellable}, and does not {@linkplain HasResult have a result}.
|
||||
* <p>This event is {@linkplain Cancellable cancellable}.
|
||||
* If the event is cancelled, the chat message will not be sent to the server.</p>
|
||||
*
|
||||
* <p>This event is fired on the {@linkplain MinecraftForge#EVENT_BUS main Forge event bus},
|
||||
* only on the {@linkplain LogicalSide#CLIENT logical client}.</p>
|
||||
**/
|
||||
@NullMarked
|
||||
public final class ClientChatEvent extends MutableEvent implements Cancellable {
|
||||
public static final CancellableEventBus<ClientChatEvent> BUS = CancellableEventBus.create(ClientChatEvent.class);
|
||||
|
||||
@@ -47,7 +50,7 @@ public final class ClientChatEvent extends MutableEvent implements Cancellable {
|
||||
*
|
||||
* @param message the new message to be sent
|
||||
*/
|
||||
public void setMessage(String message) {
|
||||
public void setMessage(@Nullable String message) {
|
||||
this.message = Strings.nullToEmpty(message);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ import java.util.UUID;
|
||||
* Fired when a chat message is received on the client.
|
||||
* This can be used for filtering and detecting messages with specific words or phrases, and suppressing them.
|
||||
*
|
||||
* <p>This event is {@linkplain Cancelable cancellable}, and does not {@linkplain HasResult have a result}.
|
||||
* <p>This event is {@linkplain Cancellable cancellable}.
|
||||
* If the event is cancelled, the message is not displayed in the chat message window.</p>
|
||||
*
|
||||
* <p>This event is fired on the {@linkplain MinecraftForge#EVENT_BUS main Forge event bus},
|
||||
@@ -89,7 +89,7 @@ public sealed class ClientChatReceivedEvent extends MutableEvent implements Canc
|
||||
/**
|
||||
* Fired when a player chat message is received on the client.
|
||||
*
|
||||
* <p>This event is {@linkplain Cancelable cancellable}, and does not {@linkplain HasResult have a result}.
|
||||
* <p>This event is {@linkplain Cancellable cancellable}.
|
||||
* If the event is cancelled, the message is not displayed in the chat message window.</p>
|
||||
*
|
||||
* <p>This event is fired on the {@linkplain MinecraftForge#EVENT_BUS main Forge event bus},
|
||||
@@ -120,8 +120,8 @@ public sealed class ClientChatReceivedEvent extends MutableEvent implements Canc
|
||||
/**
|
||||
* Fired when a system message is received on the client.
|
||||
*
|
||||
* @Deprecated Mojang made ChatType a registry, which isn't always accessible when the System messages are sent.
|
||||
* So moved to it's own event. {@link SystemMessageReceivedEvent}
|
||||
* @deprecated Mojang made ChatType a registry, which isn't always accessible when the System messages are sent.
|
||||
* So moved to its own event. {@link SystemMessageReceivedEvent}
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "1.21.1")
|
||||
public static final class System extends ClientChatReceivedEvent {
|
||||
|
||||
@@ -12,6 +12,7 @@ import net.minecraftforge.eventbus.api.event.InheritableEvent;
|
||||
import net.minecraftforge.eventbus.api.event.RecordEvent;
|
||||
import net.minecraftforge.eventbus.api.event.characteristic.Cancellable;
|
||||
import net.minecraftforge.fml.LogicalSide;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
/**
|
||||
* Fired when pause is about to change
|
||||
@@ -21,6 +22,7 @@ import net.minecraftforge.fml.LogicalSide;
|
||||
* @see ClientPauseChangeEvent.Pre
|
||||
* @see ClientPauseChangeEvent.Post
|
||||
*/
|
||||
@NullMarked
|
||||
public sealed interface ClientPauseChangeEvent extends InheritableEvent {
|
||||
EventBus<ClientPauseChangeEvent> BUS = EventBus.create(ClientPauseChangeEvent.class);
|
||||
|
||||
|
||||
@@ -7,10 +7,8 @@ package net.minecraftforge.client.event;
|
||||
|
||||
import net.minecraft.client.multiplayer.PlayerInfo;
|
||||
import net.minecraft.world.level.GameType;
|
||||
import net.minecraftforge.common.util.HasResult;
|
||||
import net.minecraftforge.eventbus.api.bus.EventBus;
|
||||
import net.minecraftforge.eventbus.api.event.RecordEvent;
|
||||
import net.minecraftforge.eventbus.api.event.characteristic.Cancellable;
|
||||
import net.minecraftforge.fml.LogicalSide;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
@@ -18,8 +16,6 @@ import org.jspecify.annotations.NullMarked;
|
||||
/**
|
||||
* Fired when the client player is notified of a change of {@link GameType} from the server.
|
||||
*
|
||||
* <p>This event is not {@linkplain Cancellable cancellable}, and does not {@linkplain HasResult have a result}.</p>
|
||||
*
|
||||
* <p>This event is fired only on the {@linkplain LogicalSide#CLIENT logical client}.</p>
|
||||
*
|
||||
* @param getInfo the client player information
|
||||
|
||||
@@ -12,17 +12,17 @@ import net.minecraftforge.eventbus.api.bus.EventBus;
|
||||
import net.minecraftforge.eventbus.api.event.MutableEvent;
|
||||
import net.minecraftforge.fml.LogicalSide;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
/**
|
||||
* Fired after the field of vision (FOV) modifier for the player is calculated to allow developers to adjust it further.
|
||||
*
|
||||
* <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 MinecraftForge#EVENT_BUS main Forge event bus},
|
||||
* only on the {@linkplain LogicalSide#CLIENT logical client}.</p>
|
||||
*
|
||||
* @see ViewportEvent.ComputeFov
|
||||
*/
|
||||
@NullMarked
|
||||
public final class ComputeFovModifierEvent extends MutableEvent {
|
||||
public static final EventBus<ComputeFovModifierEvent> BUS = EventBus.create(ComputeFovModifierEvent.class);
|
||||
|
||||
|
||||
@@ -62,8 +62,6 @@ public sealed interface ContainerScreenEvent {
|
||||
* <p>This can be used for rendering elements that must be above other screen elements, but
|
||||
* below tooltips and the dragged stack, such as slot or item stack specific overlays.</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 MinecraftForge#EVENT_BUS main Forge event bus},
|
||||
* only on the {@linkplain LogicalSide#CLIENT logical client}.</p>
|
||||
*/
|
||||
@@ -83,8 +81,6 @@ public sealed interface ContainerScreenEvent {
|
||||
* Fired after the container screen's background layer and elements are drawn.
|
||||
* This can be used for rendering new background elements.
|
||||
*
|
||||
* <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 MinecraftForge#EVENT_BUS main Forge event bus},
|
||||
* only on the {@linkplain LogicalSide#CLIENT logical client}.</p>
|
||||
*/
|
||||
|
||||
@@ -39,6 +39,7 @@ import net.minecraftforge.eventbus.api.event.MutableEvent;
|
||||
import net.minecraftforge.eventbus.api.event.RecordEvent;
|
||||
import net.minecraftforge.eventbus.api.event.characteristic.SelfDestructing;
|
||||
import net.minecraftforge.fml.LogicalSide;
|
||||
import net.minecraftforge.fml.event.IModBusEvent;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
@@ -69,6 +70,7 @@ public sealed interface EntityRenderersEvent {
|
||||
record RegisterLayerDefinitions() implements SelfDestructing, RecordEvent, EntityRenderersEvent {
|
||||
public static final EventBus<RegisterLayerDefinitions> BUS = EventBus.create(RegisterLayerDefinitions.class);
|
||||
|
||||
/** @deprecated {@link EntityRenderersEvent.RegisterLayerDefinitions} is no longer an {@link IModBusEvent}, so use {@link #BUS} directly. */
|
||||
@Deprecated(forRemoval = true, since = "1.21.9")
|
||||
public static EventBus<RegisterLayerDefinitions> getBus(BusGroup modBusGroup) {
|
||||
return BUS;
|
||||
@@ -102,6 +104,7 @@ public sealed interface EntityRenderersEvent {
|
||||
record RegisterRenderers() implements SelfDestructing, RecordEvent, EntityRenderersEvent {
|
||||
public static final EventBus<RegisterRenderers> BUS = EventBus.create(RegisterRenderers.class);
|
||||
|
||||
/** @deprecated {@link RegisterRenderers} is no longer an {@link IModBusEvent}, so use {@link #BUS} directly. */
|
||||
@Deprecated(forRemoval = true, since = "1.21.9")
|
||||
public static EventBus<RegisterRenderers> getBus(BusGroup modBusGroup) {
|
||||
return BUS;
|
||||
@@ -140,6 +143,7 @@ public sealed interface EntityRenderersEvent {
|
||||
final class AddLayers extends MutableEvent implements EntityRenderersEvent {
|
||||
public static final EventBus<AddLayers> BUS = EventBus.create(AddLayers.class);
|
||||
|
||||
/** @deprecated {@link EntityRenderersEvent.AddLayers} is no longer an {@link IModBusEvent}, so use {@link #BUS} directly. */
|
||||
@Deprecated(forRemoval = true, since = "1.21.9")
|
||||
public static EventBus<AddLayers> getBus(BusGroup modBusGroup) {
|
||||
return BUS;
|
||||
@@ -231,6 +235,7 @@ public sealed interface EntityRenderersEvent {
|
||||
final class CreateSkullModels extends MutableEvent implements EntityRenderersEvent {
|
||||
public static final EventBus<CreateSkullModels> BUS = EventBus.create(CreateSkullModels.class);
|
||||
|
||||
/** @deprecated {@link EntityRenderersEvent.CreateSkullModels} is no longer an {@link IModBusEvent}, so use {@link #BUS} directly. */
|
||||
@Deprecated(forRemoval = true, since = "1.21.9")
|
||||
public static EventBus<CreateSkullModels> getBus(BusGroup modBusGroup) {
|
||||
return BUS;
|
||||
|
||||
@@ -244,10 +244,6 @@ public final class ForgeEventFactoryClient {
|
||||
TickEvent.RenderTickEvent.Post.BUS.post(new TickEvent.RenderTickEvent.Post(timer));
|
||||
}
|
||||
|
||||
public static RenderTooltipEvent.Background onRenderTooltipBackground(@NotNull ItemStack stack, GuiGraphics graphics, int x, int y, @NotNull Font font, @NotNull List<ClientTooltipComponent> components, @Nullable ResourceLocation backgroundPrefix) {
|
||||
return RenderTooltipEvent.Background.BUS.fire(new RenderTooltipEvent.Background(stack, graphics, x, y, font, components, backgroundPrefix));
|
||||
}
|
||||
|
||||
public static boolean onToastAdd(Toast toast) {
|
||||
return ToastAddEvent.BUS.post(new ToastAddEvent(toast));
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import net.minecraftforge.eventbus.api.event.RecordEvent;
|
||||
import net.minecraftforge.eventbus.api.event.characteristic.Cancellable;
|
||||
import net.minecraftforge.fml.LogicalSide;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
/**
|
||||
@@ -127,6 +128,7 @@ public sealed interface InputEvent {
|
||||
* @param getMouseX the X position of the mouse cursor
|
||||
* @param getMouseY the Y position of the mouse cursor
|
||||
*/
|
||||
@NullMarked
|
||||
record MouseScrollingEvent(
|
||||
double getDeltaX,
|
||||
double getDeltaY,
|
||||
|
||||
@@ -18,7 +18,6 @@ import net.minecraftforge.eventbus.api.bus.EventBus;
|
||||
import net.minecraftforge.eventbus.api.event.MutableEvent;
|
||||
import net.minecraftforge.eventbus.api.event.RecordEvent;
|
||||
import net.minecraftforge.fml.LogicalSide;
|
||||
import net.minecraftforge.fml.ModLoadingContext;
|
||||
import net.minecraftforge.fml.event.IModBusEvent;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
@@ -51,6 +50,7 @@ public sealed interface ModelEvent {
|
||||
implements RecordEvent, ModelEvent {
|
||||
public static final EventBus<ModifyBakingResult> BUS = EventBus.create(ModifyBakingResult.class);
|
||||
|
||||
/** @deprecated {@link ModelEvent.ModifyBakingResult} is no longer an {@link IModBusEvent}, so use {@link #BUS} directly. */
|
||||
@Deprecated(forRemoval = true, since = "1.21.9")
|
||||
public static EventBus<ModifyBakingResult> getBus(BusGroup modBusGroup) {
|
||||
return BUS;
|
||||
@@ -75,6 +75,7 @@ public sealed interface ModelEvent {
|
||||
implements RecordEvent, ModelEvent {
|
||||
public static final EventBus<BakingCompleted> BUS = EventBus.create(BakingCompleted.class);
|
||||
|
||||
/** @deprecated {@link ModelEvent.BakingCompleted} is no longer an {@link IModBusEvent}, so use {@link #BUS} directly. */
|
||||
@Deprecated(forRemoval = true, since = "1.21.9")
|
||||
public static EventBus<BakingCompleted> getBus(BusGroup modBusGroup) {
|
||||
return BUS;
|
||||
@@ -95,6 +96,7 @@ public sealed interface ModelEvent {
|
||||
final class RegisterModelStateDefinitions extends MutableEvent implements ModelEvent {
|
||||
public static final EventBus<RegisterModelStateDefinitions> BUS = EventBus.create(RegisterModelStateDefinitions.class);
|
||||
|
||||
/** @deprecated {@link ModelEvent.RegisterModelStateDefinitions} is no longer an {@link IModBusEvent}, so use {@link #BUS} directly. */
|
||||
@Deprecated(forRemoval = true, since = "1.21.9")
|
||||
public static EventBus<RegisterModelStateDefinitions> getBus(BusGroup modBusGroup) {
|
||||
return BUS;
|
||||
@@ -130,6 +132,7 @@ public sealed interface ModelEvent {
|
||||
final class RegisterGeometryLoaders extends MutableEvent implements ModelEvent {
|
||||
public static final EventBus<RegisterGeometryLoaders> BUS = EventBus.create(RegisterGeometryLoaders.class);
|
||||
|
||||
/** @deprecated {@link ModelEvent.RegisterGeometryLoaders} is no longer an {@link IModBusEvent}, so use {@link #BUS} directly. */
|
||||
@Deprecated(forRemoval = true, since = "1.21.9")
|
||||
public static EventBus<RegisterGeometryLoaders> getBus(BusGroup modBusGroup) {
|
||||
return BUS;
|
||||
|
||||
@@ -15,8 +15,6 @@ import org.jetbrains.annotations.ApiStatus;
|
||||
/**
|
||||
* Fired when the {@link ClientRecipeBook} has updated information about recipes from the server to the client.
|
||||
*
|
||||
* <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 MinecraftForge#EVENT_BUS main Forge event bus},
|
||||
* only on the {@linkplain LogicalSide#CLIENT logical client}.</p>
|
||||
*
|
||||
|
||||
@@ -6,14 +6,13 @@
|
||||
package net.minecraftforge.client.event;
|
||||
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import net.minecraft.commands.CommandBuildContext;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.commands.arguments.ObjectiveArgument;
|
||||
import net.minecraft.commands.arguments.ResourceLocationArgument;
|
||||
import net.minecraftforge.common.util.HasResult;
|
||||
import net.minecraftforge.eventbus.api.bus.EventBus;
|
||||
import net.minecraftforge.eventbus.api.event.RecordEvent;
|
||||
import net.minecraftforge.eventbus.api.event.characteristic.Cancellable;
|
||||
import net.minecraftforge.fml.LogicalSide;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
@@ -23,14 +22,12 @@ import org.jspecify.annotations.NullMarked;
|
||||
*
|
||||
* <p>Some command arguments behave differently for the client commands dispatcher:</p>
|
||||
* <ul>
|
||||
* <li>{@link ResourceLocationArgument#getAdvancement(com.mojang.brigadier.context.CommandContext, String)} only returns
|
||||
* advancements that are shown on the advancements screen.
|
||||
* <li>{@link ResourceLocationArgument#getId(CommandContext, String)} only returns advancements that are shown on
|
||||
* the advancements screen.
|
||||
* <li>{@link ObjectiveArgument#getObjective(com.mojang.brigadier.context.CommandContext, String)} only returns
|
||||
* objectives that are displayed to the player.
|
||||
* </ul>
|
||||
*
|
||||
* <p>This event is not {@linkplain Cancellable cancellable}, and does not {@linkplain HasResult have a result}.</p>
|
||||
*
|
||||
* <p>This event is fired only on the {@linkplain LogicalSide#CLIENT logical client}.</p>
|
||||
*
|
||||
* @param getDispatcher the command dispatcher for registering commands to be executed on the client
|
||||
|
||||
@@ -14,6 +14,7 @@ import net.minecraftforge.eventbus.api.bus.EventBus;
|
||||
import net.minecraftforge.eventbus.api.event.MutableEvent;
|
||||
import net.minecraftforge.eventbus.api.event.characteristic.SelfDestructing;
|
||||
import net.minecraftforge.fml.LogicalSide;
|
||||
import net.minecraftforge.fml.event.IModBusEvent;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@@ -29,6 +30,7 @@ import org.jspecify.annotations.NullMarked;
|
||||
public final class RegisterClientReloadListenersEvent extends MutableEvent implements SelfDestructing {
|
||||
public static final EventBus<RegisterClientReloadListenersEvent> BUS = EventBus.create(RegisterClientReloadListenersEvent.class);
|
||||
|
||||
/** @deprecated {@link RegisterClientReloadListenersEvent} is no longer an {@link IModBusEvent}, so use {@link #BUS} directly. */
|
||||
@Deprecated(forRemoval = true, since = "1.21.9")
|
||||
public static EventBus<RegisterClientReloadListenersEvent> getBus(BusGroup modBusGroup) {
|
||||
return BUS;
|
||||
|
||||
+2
@@ -12,6 +12,7 @@ import net.minecraftforge.eventbus.api.bus.EventBus;
|
||||
import net.minecraftforge.eventbus.api.event.MutableEvent;
|
||||
import net.minecraftforge.eventbus.api.event.characteristic.SelfDestructing;
|
||||
import net.minecraftforge.fml.LogicalSide;
|
||||
import net.minecraftforge.fml.event.IModBusEvent;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@@ -28,6 +29,7 @@ import java.util.function.Function;
|
||||
public final class RegisterClientTooltipComponentFactoriesEvent extends MutableEvent implements SelfDestructing {
|
||||
public static final EventBus<RegisterClientTooltipComponentFactoriesEvent> BUS = EventBus.create(RegisterClientTooltipComponentFactoriesEvent.class);
|
||||
|
||||
/** @deprecated {@link RegisterClientTooltipComponentFactoriesEvent} is no longer an {@link IModBusEvent}, so use {@link #BUS} directly. */
|
||||
@Deprecated(forRemoval = true, since = "1.21.9")
|
||||
public static EventBus<RegisterClientTooltipComponentFactoriesEvent> getBus(BusGroup modBusGroup) {
|
||||
return BUS;
|
||||
|
||||
@@ -15,6 +15,7 @@ import net.minecraftforge.eventbus.api.event.MutableEvent;
|
||||
import net.minecraftforge.eventbus.api.event.RecordEvent;
|
||||
import net.minecraftforge.eventbus.api.event.characteristic.SelfDestructing;
|
||||
import net.minecraftforge.fml.LogicalSide;
|
||||
import net.minecraftforge.fml.event.IModBusEvent;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@@ -38,6 +39,7 @@ public sealed interface RegisterColorHandlersEvent {
|
||||
record Block(BlockColors getBlockColors) implements RecordEvent, RegisterColorHandlersEvent {
|
||||
public static final EventBus<Block> BUS = EventBus.create(Block.class);
|
||||
|
||||
/** @deprecated {@link RegisterColorHandlersEvent.Block} is no longer an {@link IModBusEvent}, so use {@link #BUS} directly. */
|
||||
@Deprecated(forRemoval = true, since = "1.21.9")
|
||||
public static EventBus<Block> getBus(BusGroup modBusGroup) {
|
||||
return BUS;
|
||||
@@ -75,6 +77,7 @@ public sealed interface RegisterColorHandlersEvent {
|
||||
final class ColorResolvers extends MutableEvent implements SelfDestructing, RegisterColorHandlersEvent {
|
||||
public static final EventBus<ColorResolvers> BUS = EventBus.create(ColorResolvers.class);
|
||||
|
||||
/** @deprecated {@link RegisterColorHandlersEvent.ColorResolvers} is no longer an {@link IModBusEvent}, so use {@link #BUS} directly. */
|
||||
@Deprecated(forRemoval = true, since = "1.21.9")
|
||||
public static EventBus<ColorResolvers> getBus(BusGroup modBusGroup) {
|
||||
return BUS;
|
||||
|
||||
+2
@@ -12,6 +12,7 @@ import net.minecraftforge.eventbus.api.bus.EventBus;
|
||||
import net.minecraftforge.eventbus.api.event.MutableEvent;
|
||||
import net.minecraftforge.eventbus.api.event.characteristic.SelfDestructing;
|
||||
import net.minecraftforge.fml.LogicalSide;
|
||||
import net.minecraftforge.fml.event.IModBusEvent;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@@ -26,6 +27,7 @@ import java.util.Map;
|
||||
public final class RegisterDimensionSpecialEffectsEvent extends MutableEvent implements SelfDestructing {
|
||||
public static final EventBus<RegisterDimensionSpecialEffectsEvent> BUS = EventBus.create(RegisterDimensionSpecialEffectsEvent.class);
|
||||
|
||||
/** @deprecated {@link RegisterDimensionSpecialEffectsEvent} is no longer an {@link IModBusEvent}, so use {@link #BUS} directly. */
|
||||
@Deprecated(forRemoval = true, since = "1.21.9")
|
||||
public static EventBus<RegisterDimensionSpecialEffectsEvent> getBus(BusGroup modBusGroup) {
|
||||
return BUS;
|
||||
|
||||
@@ -12,6 +12,7 @@ import net.minecraftforge.eventbus.api.bus.EventBus;
|
||||
import net.minecraftforge.eventbus.api.event.MutableEvent;
|
||||
import net.minecraftforge.eventbus.api.event.characteristic.SelfDestructing;
|
||||
import net.minecraftforge.fml.LogicalSide;
|
||||
import net.minecraftforge.fml.event.IModBusEvent;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@@ -27,6 +28,7 @@ import java.util.Map;
|
||||
public final class RegisterEntitySpectatorShadersEvent extends MutableEvent implements SelfDestructing {
|
||||
public static final EventBus<RegisterEntitySpectatorShadersEvent> BUS = EventBus.create(RegisterEntitySpectatorShadersEvent.class);
|
||||
|
||||
/** @deprecated {@link RegisterEntitySpectatorShadersEvent} is no longer an {@link IModBusEvent}, so use {@link #BUS} directly. */
|
||||
@Deprecated(forRemoval = true, since = "1.21.9")
|
||||
public static EventBus<RegisterEntitySpectatorShadersEvent> getBus(BusGroup modBusGroup) {
|
||||
return BUS;
|
||||
|
||||
@@ -14,6 +14,7 @@ import net.minecraftforge.eventbus.api.bus.EventBus;
|
||||
import net.minecraftforge.eventbus.api.event.MutableEvent;
|
||||
import net.minecraftforge.eventbus.api.event.characteristic.SelfDestructing;
|
||||
import net.minecraftforge.fml.LogicalSide;
|
||||
import net.minecraftforge.fml.event.IModBusEvent;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@@ -30,6 +31,7 @@ import java.util.Map;
|
||||
public final class RegisterItemDecorationsEvent extends MutableEvent implements SelfDestructing {
|
||||
public static final EventBus<RegisterItemDecorationsEvent> BUS = EventBus.create(RegisterItemDecorationsEvent.class);
|
||||
|
||||
/** @deprecated {@link RegisterItemDecorationsEvent} is no longer an {@link IModBusEvent}, so use {@link #BUS} directly. */
|
||||
@Deprecated(forRemoval = true, since = "1.21.9")
|
||||
public static EventBus<RegisterItemDecorationsEvent> getBus(BusGroup modBusGroup) {
|
||||
return BUS;
|
||||
|
||||
@@ -12,6 +12,7 @@ import net.minecraftforge.eventbus.api.bus.EventBus;
|
||||
import net.minecraftforge.eventbus.api.event.MutableEvent;
|
||||
import net.minecraftforge.eventbus.api.event.characteristic.SelfDestructing;
|
||||
import net.minecraftforge.fml.LogicalSide;
|
||||
import net.minecraftforge.fml.event.IModBusEvent;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
@@ -25,6 +26,7 @@ import org.jspecify.annotations.NullMarked;
|
||||
public final class RegisterKeyMappingsEvent extends MutableEvent implements SelfDestructing {
|
||||
public static final EventBus<RegisterKeyMappingsEvent> BUS = EventBus.create(RegisterKeyMappingsEvent.class);
|
||||
|
||||
/** @deprecated {@link RegisterKeyMappingsEvent} is no longer an {@link IModBusEvent}, so use {@link #BUS} directly. */
|
||||
@Deprecated(forRemoval = true, since = "1.21.9")
|
||||
public static EventBus<RegisterKeyMappingsEvent> getBus(BusGroup modBusGroup) {
|
||||
return BUS;
|
||||
|
||||
@@ -16,6 +16,7 @@ import net.minecraftforge.eventbus.api.bus.EventBus;
|
||||
import net.minecraftforge.eventbus.api.event.MutableEvent;
|
||||
import net.minecraftforge.eventbus.api.event.characteristic.SelfDestructing;
|
||||
import net.minecraftforge.fml.LogicalSide;
|
||||
import net.minecraftforge.fml.event.IModBusEvent;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import java.util.Map;
|
||||
@@ -28,6 +29,7 @@ import java.util.Map;
|
||||
public final class RegisterNamedRenderTypesEvent extends MutableEvent implements SelfDestructing {
|
||||
public static final EventBus<RegisterNamedRenderTypesEvent> BUS = EventBus.create(RegisterNamedRenderTypesEvent.class);
|
||||
|
||||
/** @deprecated {@link RegisterNamedRenderTypesEvent} is no longer an {@link IModBusEvent}, so use {@link #BUS} directly. */
|
||||
@Deprecated(forRemoval = true, since = "1.21.9")
|
||||
public static EventBus<RegisterNamedRenderTypesEvent> getBus(BusGroup modBusGroup) {
|
||||
return BUS;
|
||||
|
||||
@@ -13,6 +13,7 @@ import net.minecraftforge.eventbus.api.bus.BusGroup;
|
||||
import net.minecraftforge.eventbus.api.bus.EventBus;
|
||||
import net.minecraftforge.eventbus.api.event.MutableEvent;
|
||||
import net.minecraftforge.fml.LogicalSide;
|
||||
import net.minecraftforge.fml.event.IModBusEvent;
|
||||
import net.minecraftforge.registries.RegisterEvent;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
@@ -29,6 +30,7 @@ import org.jspecify.annotations.NullMarked;
|
||||
public final class RegisterParticleProvidersEvent extends MutableEvent {
|
||||
public static EventBus<RegisterParticleProvidersEvent> BUS = EventBus.create(RegisterParticleProvidersEvent.class);
|
||||
|
||||
/** @deprecated {@link RegisterParticleProvidersEvent} is no longer an {@link IModBusEvent}, so use {@link #BUS} directly. */
|
||||
@Deprecated(forRemoval = true, since = "1.21.9")
|
||||
public static EventBus<RegisterParticleProvidersEvent> getBus(BusGroup modBusGroup) {
|
||||
return BUS;
|
||||
|
||||
@@ -25,7 +25,7 @@ import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
|
||||
/**
|
||||
* <p>Event for registering {@link PresetEditor} screen factories for world presets.</p>
|
||||
*
|
||||
* <p>This event is fired on the {@linkplain FMLJavaModLoadingContext#getModEventBus() mod-specific event bus},
|
||||
* <p>This event is fired on the {@linkplain FMLJavaModLoadingContext#getModBusGroup() mod BusGroup},
|
||||
* only on the {@linkplain LogicalSide#CLIENT logical client}.</p>
|
||||
*/
|
||||
public final class RegisterPresetEditorsEvent implements SelfDestructing, IModBusEvent {
|
||||
|
||||
+2
@@ -15,6 +15,7 @@ import net.minecraftforge.eventbus.api.bus.EventBus;
|
||||
import net.minecraftforge.eventbus.api.event.MutableEvent;
|
||||
import net.minecraftforge.eventbus.api.event.characteristic.SelfDestructing;
|
||||
import net.minecraftforge.fml.LogicalSide;
|
||||
import net.minecraftforge.fml.event.IModBusEvent;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
/**
|
||||
@@ -25,6 +26,7 @@ import org.jetbrains.annotations.ApiStatus;
|
||||
public final class RegisterTextureAtlasSpriteLoadersEvent extends MutableEvent implements SelfDestructing {
|
||||
public static final EventBus<RegisterTextureAtlasSpriteLoadersEvent> BUS = EventBus.create(RegisterTextureAtlasSpriteLoadersEvent.class);
|
||||
|
||||
/** {@link RegisterTextureAtlasSpriteLoadersEvent} is no longer an {@link IModBusEvent}, so use {@link #BUS} directly. */
|
||||
@Deprecated(forRemoval = true, since = "1.21.9")
|
||||
public static EventBus<RegisterTextureAtlasSpriteLoadersEvent> getBus(BusGroup modBusGroup) {
|
||||
return BUS;
|
||||
|
||||
@@ -22,9 +22,9 @@ import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Fired before a selection highlight is rendered.
|
||||
* Fired before a selection highlight is rendered.<br>
|
||||
* You should extract your custom data here so that you can use it in the render callback without effecting world state.
|
||||
*
|
||||
* <br><br>
|
||||
* See the two subclasses to listen for blocks or entities.
|
||||
*
|
||||
* @see Block
|
||||
|
||||
@@ -21,7 +21,7 @@ import org.jetbrains.annotations.ApiStatus;
|
||||
/**
|
||||
* Fired before an entity renderer renders the nameplate of an entity.
|
||||
*
|
||||
* <p>This event is not {@linkplain Cancelable cancellable}, and {@linkplain HasResult has a result}.</p>
|
||||
* <p>This event {@linkplain HasResult has a result}:</p>
|
||||
* <ul>
|
||||
* <li>{@link Result#ALLOW} - the nameplate will be forcibly rendered.</li>
|
||||
* <li>{@link Result#DEFAULT} - the vanilla logic will be used.</li>
|
||||
|
||||
@@ -35,7 +35,6 @@ import java.util.List;
|
||||
*
|
||||
* @see RenderTooltipEvent.GatherComponents
|
||||
* @see RenderTooltipEvent.Pre
|
||||
* @see RenderTooltipEvent.Background
|
||||
*/
|
||||
@NullMarked
|
||||
public abstract sealed class RenderTooltipEvent extends MutableEvent implements InheritableEvent {
|
||||
@@ -117,7 +116,7 @@ public abstract sealed class RenderTooltipEvent extends MutableEvent implements
|
||||
*
|
||||
* <p>This event is {@linkplain Cancellable cancellable}.
|
||||
* If this event is cancelled, then the list of components will be empty, causing the tooltip to not be rendered and
|
||||
* the corresponding {@link RenderTooltipEvent.Pre} and {@link RenderTooltipEvent.Background} to not be fired.</p>
|
||||
* the corresponding {@link RenderTooltipEvent.Pre} to not be fired.</p>
|
||||
*
|
||||
* <p>This event is fired on the {@linkplain MinecraftForge#EVENT_BUS main Forge event bus},
|
||||
* only on the {@linkplain LogicalSide#CLIENT logical client}.</p>
|
||||
@@ -195,11 +194,10 @@ public abstract sealed class RenderTooltipEvent extends MutableEvent implements
|
||||
|
||||
/**
|
||||
* Fired <b>before</b> the tooltip is rendered.
|
||||
* This can be used to modify the positioning and font of the tooltip.
|
||||
* This can be used to modify the positioning, font, and background of the tooltip.
|
||||
*
|
||||
* <p>This event is {@linkplain Cancellable cancellable}.
|
||||
* If this event is cancelled, then the tooltip will not be rendered and the corresponding
|
||||
* {@link RenderTooltipEvent.Background} will not be fired.</p>
|
||||
* If this event is cancelled, then the tooltip will not be rendered
|
||||
*
|
||||
* <p>This event is fired on the {@linkplain MinecraftForge#EVENT_BUS main Forge event bus},
|
||||
* only on the {@linkplain LogicalSide#CLIENT logical client}.</p>
|
||||
@@ -210,13 +208,19 @@ public abstract sealed class RenderTooltipEvent extends MutableEvent implements
|
||||
private final int screenWidth;
|
||||
private final int screenHeight;
|
||||
private final ClientTooltipPositioner positioner;
|
||||
@Nullable
|
||||
private ResourceLocation background;
|
||||
@Nullable
|
||||
private ResourceLocation originalBackground;
|
||||
|
||||
@ApiStatus.Internal
|
||||
public Pre(ItemStack stack, GuiGraphics graphics, int x, int y, int screenWidth, int screenHeight, Font font, List<ClientTooltipComponent> components, ClientTooltipPositioner positioner) {
|
||||
public Pre(ItemStack stack, GuiGraphics graphics, int x, int y, int screenWidth, int screenHeight, Font font, List<ClientTooltipComponent> components, ClientTooltipPositioner positioner, @Nullable ResourceLocation background) {
|
||||
super(stack, graphics, x, y, font, components);
|
||||
this.screenWidth = screenWidth;
|
||||
this.screenHeight = screenHeight;
|
||||
this.positioner = positioner;
|
||||
this.background = background;
|
||||
this.originalBackground = background;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -266,27 +270,6 @@ public abstract sealed class RenderTooltipEvent extends MutableEvent implements
|
||||
public void setY(int y) {
|
||||
this.y = y;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fired when the tooltip background prefix is determined.
|
||||
* This can be used to modify the textures to be used for the tooltip background.
|
||||
*
|
||||
* <p>This event is fired on the {@linkplain MinecraftForge#EVENT_BUS main Forge event bus},
|
||||
* only on the {@linkplain LogicalSide#CLIENT logical client}.</p>
|
||||
*/
|
||||
public static final class Background extends RenderTooltipEvent {
|
||||
public static final EventBus<Background> BUS = EventBus.create(Background.class);
|
||||
|
||||
private final @Nullable ResourceLocation originalBackground;
|
||||
private @Nullable ResourceLocation background;
|
||||
|
||||
@ApiStatus.Internal
|
||||
public Background(ItemStack stack, GuiGraphics graphics, int x, int y, Font fr, List<ClientTooltipComponent> components, @Nullable ResourceLocation background) {
|
||||
super(stack, graphics, x, y, fr, components);
|
||||
this.originalBackground = background;
|
||||
this.background = background;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the new prefix for the background texture
|
||||
|
||||
@@ -24,6 +24,7 @@ import net.minecraftforge.fml.LogicalSide;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
import java.util.Collections;
|
||||
@@ -110,7 +111,7 @@ public sealed interface ScreenEvent {
|
||||
/**
|
||||
* Fired <b>before</b> the screen's overridable initialization method is fired.
|
||||
*
|
||||
* <p>This event is {@linkplain Cancellable cancellable}, and does not {@linkplain HasResult have a result}.
|
||||
* <p>This event is {@linkplain Cancellable cancellable}.
|
||||
* If the event is cancelled, the initialization method will not be called, and the widgets and children lists
|
||||
* will not be cleared.</p>
|
||||
*
|
||||
@@ -129,8 +130,6 @@ public sealed interface ScreenEvent {
|
||||
/**
|
||||
* Fired <b>after</b> the screen's overridable initialization method is called.
|
||||
*
|
||||
* <p>This event is not {@linkplain Cancellable cancellable}, and does not {@linkplain 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#CLIENT logical client}.</p>
|
||||
*/
|
||||
@@ -175,7 +174,7 @@ public sealed interface ScreenEvent {
|
||||
/**
|
||||
* Fired <b>before</b> the screen is drawn.
|
||||
*
|
||||
* <p>This event is {@linkplain Cancellable cancellable}, and does not {@linkplain HasResult have a result}.
|
||||
* <p>This event is {@linkplain Cancellable cancellable}.
|
||||
* If the event is cancelled, the screen will not be drawn.</p>
|
||||
*
|
||||
* <p>This event is fired on the {@linkplain MinecraftForge#EVENT_BUS main Forge event bus},
|
||||
@@ -192,8 +191,6 @@ public sealed interface ScreenEvent {
|
||||
/**
|
||||
* Fired <b>after</b> the screen is drawn.
|
||||
*
|
||||
* <p>This event is not {@linkplain Cancellable cancellable}, and does not {@linkplain 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#CLIENT logical client}.</p>
|
||||
*/
|
||||
@@ -343,7 +340,7 @@ public sealed interface ScreenEvent {
|
||||
/**
|
||||
* Fired <b>before</b> the mouse click is handled by the screen.
|
||||
*
|
||||
* <p>This event is {@linkplain Cancellable cancellable}, and does not {@linkplain HasResult have a result}.
|
||||
* <p>This event is {@linkplain Cancellable cancellable}.
|
||||
* If the event is cancelled, the screen's mouse click handler will be bypassed
|
||||
* and the corresponding {@link MouseButtonPressed.Post} will not be fired.</p>
|
||||
*
|
||||
@@ -362,7 +359,7 @@ public sealed interface ScreenEvent {
|
||||
* Fired <b>after</b> the mouse click is handled, if the corresponding {@link MouseButtonPressed.Pre} was not
|
||||
* cancelled.
|
||||
*
|
||||
* <p>This event is not {@linkplain Cancellable cancellable}, {@linkplain HasResult has a result}.</p>
|
||||
* <p>This event {@linkplain HasResult has a result}:</p>
|
||||
* <ul>
|
||||
* <li>{@link Result#ALLOW} - forcibly sets the mouse click as handled</li>
|
||||
* <li>{@link Result#DEFAULT} - defaults to the return value of
|
||||
@@ -415,7 +412,7 @@ public sealed interface ScreenEvent {
|
||||
/**
|
||||
* Fired <b>before</b> the mouse release is handled by the screen.
|
||||
*
|
||||
* <p>This event is {@linkplain Cancellable cancellable}, and does not {@linkplain HasResult have a result}.
|
||||
* <p>This event is {@linkplain Cancellable cancellable}.
|
||||
* If the event is cancelled, the screen's mouse release handler will be bypassed
|
||||
* and the corresponding {@link MouseButtonReleased.Post} will not be fired.</p>
|
||||
*
|
||||
@@ -434,11 +431,11 @@ public sealed interface ScreenEvent {
|
||||
* Fired <b>after</b> the mouse release is handled, if the corresponding {@link MouseButtonReleased.Pre} was
|
||||
* not cancelled.
|
||||
*
|
||||
* <p>This event is not {@linkplain Cancellable cancellable}, {@linkplain HasResult has a result}.</p>
|
||||
* <p>This event {@linkplain HasResult has a result}:</p>
|
||||
* <ul>
|
||||
* <li>{@link Result#ALLOW} - forcibly sets the mouse release as handled</li>
|
||||
* <li>{@link Result#DEFAULT} - defaults to the return value of
|
||||
* {@link Screen#mouseReleased(double, double, int)} from the screen (see {@link #wasHandled()}.</li>
|
||||
* {@link Screen#mouseReleased(MouseButtonEvent)} from the screen (see {@link #wasHandled()}.</li>
|
||||
* <li>{@link Result#DENY} - forcibly sets the mouse release as not handled.</li>
|
||||
* </ul>
|
||||
*
|
||||
@@ -476,6 +473,7 @@ public sealed interface ScreenEvent {
|
||||
* @see MouseDragged.Pre
|
||||
* @see MouseDragged.Post
|
||||
*/
|
||||
@NullMarked
|
||||
sealed interface MouseDragged extends MouseInput {
|
||||
/**
|
||||
* {@return the mouse button's input code}
|
||||
@@ -498,7 +496,7 @@ public sealed interface ScreenEvent {
|
||||
/**
|
||||
* Fired <b>before</b> the mouse drag is handled by the screen.
|
||||
*
|
||||
* <p>This event is {@linkplain Cancellable cancellable}, and does not {@linkplain HasResult have a result}.
|
||||
* <p>This event is {@linkplain Cancellable cancellable}.
|
||||
* If the event is cancelled, the screen's mouse drag handler will be bypassed
|
||||
* and the corresponding {@link MouseDragged.Post} will not be fired.</p>
|
||||
*
|
||||
@@ -551,6 +549,7 @@ public sealed interface ScreenEvent {
|
||||
* @see MouseScrolled.Pre
|
||||
* @see MouseScrolled.Post
|
||||
*/
|
||||
@NullMarked
|
||||
sealed interface MouseScrolled extends MouseInput {
|
||||
/**
|
||||
* {@return the amount of change / delta of the mouse scroll in the vertical direction}
|
||||
@@ -565,7 +564,7 @@ public sealed interface ScreenEvent {
|
||||
/**
|
||||
* Fired <b>before</b> the mouse scroll is handled by the screen.
|
||||
*
|
||||
* <p>This event is {@linkplain Cancellable cancellable}, and does not {@linkplain HasResult have a result}.
|
||||
* <p>This event is {@linkplain Cancellable cancellable}.
|
||||
* If the event is cancelled, the screen's mouse scroll handler will be bypassed
|
||||
* and the corresponding {@link MouseScrolled.Post} will not be fired.</p>
|
||||
*
|
||||
@@ -666,7 +665,7 @@ public sealed interface ScreenEvent {
|
||||
/**
|
||||
* Fired <b>before</b> the key press is handled by the screen.
|
||||
*
|
||||
* <p>This event is {@linkplain Cancellable cancellable} and does not {@linkplain HasResult have a result}.
|
||||
* <p>This event is {@linkplain Cancellable cancellable}.
|
||||
* If the event is cancelled, the screen's key press handler will be bypassed
|
||||
* and the corresponding {@link KeyPressed.Post} will not be fired.</p>
|
||||
*
|
||||
@@ -684,7 +683,7 @@ public sealed interface ScreenEvent {
|
||||
* Fired <b>after</b> the key press is handled, if not handled by the screen
|
||||
* and the corresponding {@link KeyPressed.Pre} is not cancelled.
|
||||
*
|
||||
* <p>This event is {@linkplain Cancellable cancellable}, and does not {@linkplain HasResult have a result}.
|
||||
* <p>This event is {@linkplain Cancellable cancellable}.
|
||||
* If the event is cancelled, the key press will be set as handled.</p>
|
||||
*
|
||||
* <p>This event is fired on the {@linkplain MinecraftForge#EVENT_BUS main Forge event bus},
|
||||
@@ -709,7 +708,7 @@ public sealed interface ScreenEvent {
|
||||
/**
|
||||
* Fired <b>before</b> the key release is handled by the screen.
|
||||
*
|
||||
* <p>This event is {@linkplain Cancellable cancellable}, and does not {@linkplain HasResult have a result}.
|
||||
* <p>This event is {@linkplain Cancellable cancellable}.
|
||||
* If the event is cancelled, the screen's key release handler will be bypassed
|
||||
* and the corresponding {@link KeyReleased.Post} will not be fired.</p>
|
||||
*
|
||||
@@ -727,7 +726,7 @@ public sealed interface ScreenEvent {
|
||||
* Fired <b>after</b> the key release is handled, if not handled by the screen
|
||||
* and the corresponding {@link KeyReleased.Pre} is not cancelled.
|
||||
*
|
||||
* <p>This event is {@linkplain Cancellable cancellable}, and does not {@linkplain HasResult have a result}.
|
||||
* <p>This event is {@linkplain Cancellable cancellable}.
|
||||
* If the event is cancelled, the key release will be set as handled.</p>
|
||||
*
|
||||
* <p>This event is fired on the {@linkplain MinecraftForge#EVENT_BUS main Forge event bus},
|
||||
@@ -781,7 +780,7 @@ public sealed interface ScreenEvent {
|
||||
/**
|
||||
* Fired <b>before</b> the character input is handled by the screen.
|
||||
*
|
||||
* <p>This event is {@linkplain Cancellable cancellable}, and does not {@linkplain HasResult have a result}.
|
||||
* <p>This event is {@linkplain Cancellable cancellable}.
|
||||
* If the event is cancelled, the screen's character input handler will be bypassed
|
||||
* and the corresponding {@link CharacterTyped.Post} will not be fired.</p>
|
||||
*
|
||||
@@ -799,7 +798,7 @@ public sealed interface ScreenEvent {
|
||||
* Fired <b>after</b> the character input is handled, if not handled by the screen
|
||||
* and the corresponding {@link CharacterTyped.Pre} is not cancelled.
|
||||
*
|
||||
* <p>This event is {@linkplain Cancellable cancellable}, and does not {@linkplain HasResult have a result}.
|
||||
* <p>This event is {@linkplain Cancellable cancellable}.
|
||||
* If the event is cancelled, the character input will be set as handled.</p>
|
||||
*
|
||||
* <p>This event is fired on the {@linkplain MinecraftForge#EVENT_BUS main Forge event bus},
|
||||
@@ -817,7 +816,7 @@ public sealed interface ScreenEvent {
|
||||
* Fired before any {@link Screen} is opened, to allow changing it or preventing it from being opened.
|
||||
* All screen layers on the screen are closed before this event is fired.
|
||||
*
|
||||
* <p>This event is {@linkplain Cancellable cancellable}, and does not {@linkplain HasResult have a result}.
|
||||
* <p>This event is {@linkplain Cancellable cancellable}.
|
||||
* If this event is cancelled, then the {@code Screen} shall be prevented from opening and any previous screen
|
||||
* will remain open. However, cancelling this event will not prevent the closing of screen layers which happened before
|
||||
* this event fired.</p>
|
||||
@@ -874,8 +873,6 @@ public sealed interface ScreenEvent {
|
||||
* Fired before a {@link Screen} is closed.
|
||||
* All screen layers on the screen are closed before this event is fired.
|
||||
*
|
||||
* <p>This event is not {@linkplain Cancellable cancellable}, and does not {@linkplain 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#CLIENT logical client}.</p>
|
||||
*/
|
||||
|
||||
@@ -14,7 +14,8 @@ import net.minecraftforge.eventbus.api.event.MutableEvent;
|
||||
import net.minecraftforge.eventbus.api.event.characteristic.Cancellable;
|
||||
import net.minecraftforge.fml.LogicalSide;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -22,7 +23,7 @@ import java.io.IOException;
|
||||
/**
|
||||
* Fired when a screenshot is taken, but before it is written to disk.
|
||||
*
|
||||
* <p>This event is {@linkplain Cancelable cancellable}, and does not {@linkplain HasResult have a result}.
|
||||
* <p>This event is {@linkplain Cancellable cancellable}.
|
||||
* If this event is cancelled, then the screenshot is not written to disk, and the message in the event will be posted
|
||||
* to the player's chat.</p>
|
||||
*
|
||||
@@ -31,6 +32,7 @@ import java.io.IOException;
|
||||
*
|
||||
* @see Screenshot
|
||||
*/
|
||||
@NullMarked
|
||||
public final class ScreenshotEvent extends MutableEvent implements Cancellable {
|
||||
public static final CancellableEventBus<ScreenshotEvent> BUS = CancellableEventBus.create(ScreenshotEvent.class);
|
||||
public static final Component DEFAULT_CANCEL_REASON = Component.literal("Screenshot canceled");
|
||||
@@ -80,7 +82,7 @@ public final class ScreenshotEvent extends MutableEvent implements Cancellable {
|
||||
*
|
||||
* @param resultMessage the new result message
|
||||
*/
|
||||
public void setResultMessage(Component resultMessage) {
|
||||
public void setResultMessage(@Nullable Component resultMessage) {
|
||||
this.resultMessage = resultMessage;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import net.minecraftforge.eventbus.api.bus.BusGroup;
|
||||
import net.minecraftforge.eventbus.api.bus.EventBus;
|
||||
import net.minecraftforge.eventbus.api.event.RecordEvent;
|
||||
import net.minecraftforge.fml.LogicalSide;
|
||||
import net.minecraftforge.fml.event.IModBusEvent;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@@ -63,6 +64,7 @@ public sealed interface TextureStitchEvent {
|
||||
record Post(TextureAtlas getAtlas) implements RecordEvent, TextureStitchEvent {
|
||||
public static final EventBus<Post> BUS = EventBus.create(Post.class);
|
||||
|
||||
/** @deprecated {@link TextureStitchEvent.Post} is no longer an {@link IModBusEvent}, so use {@link #BUS} directly. */
|
||||
@Deprecated(forRemoval = true, since = "1.21.9")
|
||||
public static EventBus<Post> getBus(BusGroup modBusGroup) {
|
||||
return BUS;
|
||||
|
||||
@@ -16,7 +16,7 @@ import net.minecraftforge.fml.LogicalSide;
|
||||
* Fired when the client queues a {@link Toast} message to be shown onscreen.
|
||||
* Toasts are small popups that appear on the top right of the screen, for certain actions such as unlocking Advancements and Recipes.
|
||||
*
|
||||
* <p>This event is {@linkplain Cancelable cancellable}, and does not {@linkplain HasResult have a result}.
|
||||
* <p>This event is {@linkplain Cancellable cancellable}.
|
||||
* Cancelling the event stops the toast from being queued, which means it never renders.</p>
|
||||
*
|
||||
* <p>This event is fired on the {@linkplain MinecraftForge#EVENT_BUS main Forge event bus},
|
||||
|
||||
@@ -19,6 +19,7 @@ import net.minecraftforge.eventbus.api.event.characteristic.Cancellable;
|
||||
import net.minecraftforge.fml.LogicalSide;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.joml.Vector4f;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
/**
|
||||
* Fired for hooking into the entity view rendering in {@link GameRenderer}.
|
||||
@@ -52,7 +53,7 @@ public sealed interface ViewportEvent {
|
||||
/**
|
||||
* Fired for <b>rendering</b> custom fog. The plane distances are based on the player's render distance.
|
||||
*
|
||||
* <p>This event is {@linkplain Cancelable cancellable}, and {@linkplain HasResult has a result}. <br/>
|
||||
* <p>This event is {@linkplain Cancellable cancellable}.<br>
|
||||
* The event must be cancelled for any changes to the plane distances to take effect.</p>
|
||||
*
|
||||
* <p>This event is fired on the {@linkplain MinecraftForge#EVENT_BUS main Forge event bus},
|
||||
@@ -140,8 +141,6 @@ public sealed interface ViewportEvent {
|
||||
/**
|
||||
* Fired for customizing the <b>color</b> of the fog visible to the player.
|
||||
*
|
||||
* <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 MinecraftForge#EVENT_BUS main Forge event bus},
|
||||
* only on the {@linkplain LogicalSide#CLIENT logical client}.</p>
|
||||
*/
|
||||
@@ -234,11 +233,10 @@ public sealed interface ViewportEvent {
|
||||
* Fired to allow altering the angles of the player's camera.
|
||||
* This can be used to alter the player's view for different effects, such as applying roll.
|
||||
*
|
||||
* <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 MinecraftForge#EVENT_BUS main Forge event bus},
|
||||
* only on the {@linkplain LogicalSide#CLIENT logical client}.</p>
|
||||
*/
|
||||
@NullMarked
|
||||
final class ComputeCameraAngles extends MutableEvent implements ViewportEvent {
|
||||
public static final EventBus<ComputeCameraAngles> BUS = EventBus.create(ComputeCameraAngles.class);
|
||||
|
||||
@@ -327,13 +325,12 @@ public sealed interface ViewportEvent {
|
||||
* Fired for altering the raw field of view (FOV).
|
||||
* This is after the FOV settings are applied, and before modifiers such as the Nausea effect.
|
||||
*
|
||||
* <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 MinecraftForge#EVENT_BUS main Forge event bus},
|
||||
* only on the {@linkplain LogicalSide#CLIENT logical client}.</p>
|
||||
*
|
||||
* @see ComputeFovModifierEvent
|
||||
*/
|
||||
@NullMarked
|
||||
final class ComputeFov extends MutableEvent implements ViewportEvent {
|
||||
public static final EventBus<ComputeFov> BUS = EventBus.create(ComputeFov.class);
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import net.minecraftforge.eventbus.api.bus.BusGroup;
|
||||
import net.minecraftforge.eventbus.api.bus.EventBus;
|
||||
import net.minecraftforge.eventbus.api.event.RecordEvent;
|
||||
import net.minecraftforge.fml.LogicalSide;
|
||||
import net.minecraftforge.fml.event.IModBusEvent;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@@ -23,6 +24,7 @@ import org.jspecify.annotations.NullMarked;
|
||||
public record SoundEngineLoadEvent(SoundEngine getEngine) implements RecordEvent, SoundEvent {
|
||||
public static final EventBus<SoundEngineLoadEvent> BUS = EventBus.create(SoundEngineLoadEvent.class);
|
||||
|
||||
/** @deprecated {@link SoundEngineLoadEvent} is no longer an {@link IModBusEvent}, so use {@link #BUS} directly. */
|
||||
@Deprecated(forRemoval = true, since = "1.21.9")
|
||||
public static EventBus<SoundEngineLoadEvent> getBus(BusGroup modBusGroup) {
|
||||
return BUS;
|
||||
|
||||
@@ -16,9 +16,16 @@ import java.util.Collection;
|
||||
|
||||
/**
|
||||
* A small helper class to aid Forge modders in migrating from EventBus 6 to the new EventBus 7.
|
||||
* <br>
|
||||
* Refer to the migration guide mentioned in the Forge 1.21.6 announcement or ask in the Forge Discord for help with
|
||||
* migrating your mod to the new system.
|
||||
* <p>
|
||||
* Refer to the <a href="https://gist.github.com/PaintNinja/ad82c224aecee25efac1ea3e2cf19b91">migration guide</a>
|
||||
* mentioned in the <a href="https://files.minecraftforge.net">Forge 1.21.6 changelog</a> or
|
||||
* <a href="https://forums.minecraftforge.net/topic/159525-forge-581-minecraft-1218/">1.21.8 announcement</a> on the
|
||||
* forums, or ask in <a href="https://discord.minecraftforge.net">the Forge Discord</a> for help with migrating your mod
|
||||
* to the new system.
|
||||
* </p>
|
||||
* <p>
|
||||
* You can also check the updated example code in <a href="https://files.minecraftforge.net">the MDK</a> for the MC
|
||||
* version you're using.
|
||||
*/
|
||||
public final class EventBusMigrationHelper {
|
||||
public static final EventBusMigrationHelper INSTANCE = new EventBusMigrationHelper(BusGroup.DEFAULT);
|
||||
|
||||
@@ -10,9 +10,12 @@ import net.minecraft.client.gui.screens.Screen;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.client.ClientCommandHandler;
|
||||
import net.minecraftforge.client.ConfigScreenHandler;
|
||||
import net.minecraftforge.eventbus.api.bus.BusGroup;
|
||||
import net.minecraftforge.fml.ModLoadingContext;
|
||||
import net.minecraftforge.fml.config.IConfigSpec;
|
||||
import net.minecraftforge.fml.config.ModConfig;
|
||||
import net.minecraftforge.fml.event.IModBusEvent;
|
||||
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
|
||||
import net.minecraftforge.fml.loading.FMLEnvironment;
|
||||
import net.minecraftforge.network.DualStackUtils;
|
||||
import net.minecraftforge.versions.forge.ForgeVersion;
|
||||
@@ -27,10 +30,15 @@ import java.util.function.Supplier;
|
||||
|
||||
public class MinecraftForge {
|
||||
/**
|
||||
* The EventBus for all the Forge Events.
|
||||
* A helper for partial source-level back-compat with the old event system used in 1.21.5 and older, that acts as
|
||||
* a wrapper around {@link BusGroup#DEFAULT}.
|
||||
*
|
||||
* Events marked with {@link net.minecraftforge.fml.event.IModBusEvent}
|
||||
* belong on the ModBus and not this bus
|
||||
* <p>Please refer to the javadocs for {@link EventBusMigrationHelper} for further information and resources on how
|
||||
* to properly migrate your mod to the new event system.</p>
|
||||
*
|
||||
* <p>Events marked with {@link IModBusEvent} belong on the
|
||||
* {@linkplain FMLJavaModLoadingContext#getModBusGroup() mod BusGroup} and not on the
|
||||
* {@linkplain BusGroup#DEFAULT default BusGroup}.</p>
|
||||
*/
|
||||
public static final EventBusMigrationHelper EVENT_BUS = EventBusMigrationHelper.INSTANCE;
|
||||
|
||||
@@ -54,7 +62,7 @@ public class MinecraftForge {
|
||||
* @param screenFunction A function that takes the mods screen as an argument and returns your config screen to
|
||||
* show when the player clicks the config button for your mod on the mods screen.
|
||||
* <p>You should call {@link Minecraft#setScreen(Screen)} with the provided mods screen for the
|
||||
* action of your close button, using {@link Screen#minecraft} to get the client instance.</p>
|
||||
* action of your close button, using {@link Screen#getMinecraft()} to get the client instance.</p>
|
||||
* @see ModLoadingContext#registerExtensionPoint(Class, Supplier)
|
||||
* @see ModLoadingContext#registerConfig(ModConfig.Type, IConfigSpec)
|
||||
*/
|
||||
|
||||
@@ -748,6 +748,7 @@ public class Tags {
|
||||
*/
|
||||
public static final TagKey<Item> MUSIC_DISCS = cTag("music_discs");
|
||||
public static final TagKey<Item> NUGGETS = cTag("nuggets");
|
||||
public static final TagKey<Item> NUGGETS_COPPER = cTag("nuggets/copper");
|
||||
public static final TagKey<Item> NUGGETS_GOLD = cTag("nuggets/gold");
|
||||
public static final TagKey<Item> NUGGETS_IRON = cTag("nuggets/iron");
|
||||
public static final TagKey<Item> ORES = cTag("ores");
|
||||
|
||||
@@ -50,7 +50,7 @@ public abstract class CapabilityProvider<B extends ICapabilityProviderImpl<B>> i
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AttachCapabilitiesEvent<Entity> fireAttachCapabilitiesEvent(Entity provider) {
|
||||
protected AttachCapabilitiesEvent.Entities fireAttachCapabilitiesEvent(Entity provider) {
|
||||
return AttachCapabilitiesEvent.Entities.BUS.fire(new AttachCapabilitiesEvent.Entities(provider));
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ public abstract class CapabilityProvider<B extends ICapabilityProviderImpl<B>> i
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AttachCapabilitiesEvent<BlockEntity> fireAttachCapabilitiesEvent(BlockEntity provider) {
|
||||
protected AttachCapabilitiesEvent.BlockEntities fireAttachCapabilitiesEvent(BlockEntity provider) {
|
||||
return AttachCapabilitiesEvent.BlockEntities.BUS.fire(new AttachCapabilitiesEvent.BlockEntities(provider));
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ public abstract class CapabilityProvider<B extends ICapabilityProviderImpl<B>> i
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AttachCapabilitiesEvent<ItemStack> fireAttachCapabilitiesEvent(ItemStack provider) {
|
||||
protected AttachCapabilitiesEvent.ItemStacks fireAttachCapabilitiesEvent(ItemStack provider) {
|
||||
return AttachCapabilitiesEvent.ItemStacks.BUS.fire(new AttachCapabilitiesEvent.ItemStacks(provider));
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ public abstract class CapabilityProvider<B extends ICapabilityProviderImpl<B>> i
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AttachCapabilitiesEvent<Level> fireAttachCapabilitiesEvent(Level provider) {
|
||||
protected AttachCapabilitiesEvent.Levels fireAttachCapabilitiesEvent(Level provider) {
|
||||
return AttachCapabilitiesEvent.Levels.BUS.fire(new AttachCapabilitiesEvent.Levels(provider));
|
||||
}
|
||||
|
||||
@@ -155,7 +155,7 @@ public abstract class CapabilityProvider<B extends ICapabilityProviderImpl<B>> i
|
||||
doGatherCapabilities(parent == null ? null : parent.get());
|
||||
}
|
||||
|
||||
protected abstract AttachCapabilitiesEvent<?> fireAttachCapabilitiesEvent(B provider);
|
||||
protected abstract AttachCapabilitiesEvent fireAttachCapabilitiesEvent(B provider);
|
||||
protected abstract boolean shouldFireAttachCapabilitiesEvent();
|
||||
|
||||
private void doGatherCapabilities(@Nullable ICapabilityProvider parent) {
|
||||
@@ -282,7 +282,7 @@ public abstract class CapabilityProvider<B extends ICapabilityProviderImpl<B>> i
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AttachCapabilitiesEvent<LevelChunk> fireAttachCapabilitiesEvent(LevelChunk provider) {
|
||||
protected AttachCapabilitiesEvent.LevelChunks fireAttachCapabilitiesEvent(LevelChunk provider) {
|
||||
return AttachCapabilitiesEvent.LevelChunks.BUS.fire(new AttachCapabilitiesEvent.LevelChunks(provider));
|
||||
}
|
||||
|
||||
|
||||
@@ -10,18 +10,20 @@ import net.minecraftforge.fml.common.asm.CapabilityTokenSubclass;
|
||||
/**
|
||||
* Inspired by {@link com.google.common.reflect.TypeToken TypeToken}, use a subclass to capture
|
||||
* generic types. Then uses {@link CapabilityTokenSubclass a transformer}
|
||||
* to convert that generic into a string returned by {@link #getType}
|
||||
* to convert that generic into a string returned by {@link #getType}.
|
||||
* This allows us to know the generic type, without having a hard reference to the
|
||||
* class.
|
||||
*
|
||||
* Example usage:
|
||||
* <p>Example usage:</p>
|
||||
* <pre>{@code
|
||||
* public static Capability<IDataHolder> DATA_HOLDER_CAPABILITY
|
||||
* public static final Capability<IDataHolder> DATA_HOLDER_CAPABILITY
|
||||
* = CapabilityManager.get(new CapabilityToken<>(){});
|
||||
* }</pre>
|
||||
*
|
||||
*/
|
||||
public abstract class CapabilityToken<T> {
|
||||
protected CapabilityToken() {}
|
||||
|
||||
protected final String getType() {
|
||||
throw new RuntimeException("This will be implemented by a transformer");
|
||||
}
|
||||
|
||||
@@ -11,12 +11,11 @@ import net.minecraftforge.fluids.capability.IFluidHandlerItem;
|
||||
import net.minecraftforge.items.IItemHandler;
|
||||
|
||||
/*
|
||||
* References to Forge's built in capabilities.
|
||||
* References to Forge's built in capabilities.
|
||||
* Modders are recommended to use their own CapabilityTokens for 3rd party caps to maintain soft dependencies.
|
||||
* However, since nobody has a soft dependency on Forge, we expose this as API.
|
||||
*/
|
||||
public class ForgeCapabilities
|
||||
{
|
||||
public class ForgeCapabilities {
|
||||
public static final Capability<IEnergyStorage> ENERGY = CapabilityManager.get(new CapabilityToken<>(){});
|
||||
public static final Capability<IFluidHandler> FLUID_HANDLER = CapabilityManager.get(new CapabilityToken<>(){});
|
||||
public static final Capability<IFluidHandlerItem> FLUID_HANDLER_ITEM = CapabilityManager.get(new CapabilityToken<>(){});
|
||||
|
||||
@@ -10,6 +10,7 @@ import java.util.Objects;
|
||||
import net.minecraftforge.eventbus.api.bus.BusGroup;
|
||||
import net.minecraftforge.eventbus.api.bus.EventBus;
|
||||
import net.minecraftforge.eventbus.api.event.RecordEvent;
|
||||
import net.minecraftforge.fml.event.IModBusEvent;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.objectweb.asm.Type;
|
||||
|
||||
@@ -24,17 +25,18 @@ import org.objectweb.asm.Type;
|
||||
public record RegisterCapabilitiesEvent() implements RecordEvent {
|
||||
public static final EventBus<RegisterCapabilitiesEvent> BUS = EventBus.create(RegisterCapabilitiesEvent.class);
|
||||
|
||||
/** @deprecated {@link RegisterCapabilitiesEvent} is no longer an {@link IModBusEvent}, so use {@link #BUS} directly. */
|
||||
@Deprecated(forRemoval = true, since = "1.21.9")
|
||||
public static EventBus<RegisterCapabilitiesEvent> getBus(BusGroup modBusGroup) {
|
||||
return BUS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a capability to be consumed by others.
|
||||
* APIs who define the capability should call this.
|
||||
* This is meant to allow Capability consumers to have soft dependencies on the Capability type.
|
||||
* But be automatically notified when the Class actually exists. Meaning it's safe to create their implementations.
|
||||
*
|
||||
* Registers a capability to be consumed by others.<br>
|
||||
* APIs who define the capability should call this.<br>
|
||||
* This is meant to allow Capability consumers to have soft dependencies on the Capability type.<br>
|
||||
* But be automatically notified when the Class actually exists. Meaning it's safe to create their implementations.<br>
|
||||
* <br>
|
||||
* To retrieve the Capability instance, use the {@link CapabilityManager} gets functions.
|
||||
*/
|
||||
public <T> void register(Class<T> type) {
|
||||
|
||||
@@ -195,8 +195,10 @@ public final class ForgeItemTagsProvider extends VanillaItemTagsProvider {
|
||||
.add(Items.NETHER_STAR)
|
||||
.addOptionalTag(forgeItemTagKey("nether_stars"));
|
||||
tag(Tags.Items.NUGGETS)
|
||||
.addTags(Tags.Items.NUGGETS_GOLD, Tags.Items.NUGGETS_IRON)
|
||||
.addTags(Tags.Items.NUGGETS_GOLD, Tags.Items.NUGGETS_IRON, Tags.Items.NUGGETS_COPPER)
|
||||
.addOptionalTag(forgeItemTagKey("nuggets"));
|
||||
tag(Tags.Items.NUGGETS_COPPER)
|
||||
.add(Items.COPPER_NUGGET);
|
||||
tag(Tags.Items.NUGGETS_IRON)
|
||||
.add(Items.IRON_NUGGET)
|
||||
.addOptionalTag(forgeItemTagKey("nuggets/iron"));
|
||||
|
||||
@@ -11,6 +11,7 @@ import net.minecraft.server.packs.repository.RepositorySource;
|
||||
import net.minecraftforge.eventbus.api.bus.BusGroup;
|
||||
import net.minecraftforge.eventbus.api.bus.EventBus;
|
||||
import net.minecraftforge.eventbus.api.event.RecordEvent;
|
||||
import net.minecraftforge.fml.event.IModBusEvent;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@@ -22,6 +23,7 @@ import java.util.function.Consumer;
|
||||
public record AddPackFindersEvent(PackType getPackType, Consumer<RepositorySource> sourceAdder) implements RecordEvent {
|
||||
public static final EventBus<AddPackFindersEvent> BUS = EventBus.create(AddPackFindersEvent.class);
|
||||
|
||||
/** @deprecated {@link AddPackFindersEvent} is no longer an {@link IModBusEvent}, so use {@link #BUS} directly. */
|
||||
@Deprecated(forRemoval = true, since = "1.21.9")
|
||||
public static EventBus<AddPackFindersEvent> getBus(BusGroup modBusGroup) {
|
||||
return BUS;
|
||||
|
||||
@@ -37,61 +37,86 @@ import org.jspecify.annotations.NullMarked;
|
||||
* @see LevelChunks
|
||||
*/
|
||||
@NullMarked
|
||||
public abstract class AttachCapabilitiesEvent<T> extends MutableEvent implements InheritableEvent {
|
||||
private final T obj;
|
||||
public abstract class AttachCapabilitiesEvent extends MutableEvent implements InheritableEvent {
|
||||
private final Object obj;
|
||||
private final Map<ResourceLocation, ICapabilityProvider> caps = new LinkedHashMap<>();
|
||||
private final Map<ResourceLocation, ICapabilityProvider> view = Collections.unmodifiableMap(caps);
|
||||
private final List<Runnable> listeners = new ArrayList<>();
|
||||
private final List<Runnable> listenersView = Collections.unmodifiableList(listeners);
|
||||
|
||||
public static class Entities extends AttachCapabilitiesEvent<Entity> {
|
||||
public static class Entities extends AttachCapabilitiesEvent {
|
||||
public static final EventBus<Entities> BUS = EventBus.create(Entities.class);
|
||||
|
||||
public Entities(Entity obj) {
|
||||
super(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entity getObject() {
|
||||
return (Entity)super.getObject();
|
||||
}
|
||||
}
|
||||
|
||||
public static class BlockEntities extends AttachCapabilitiesEvent<BlockEntity> {
|
||||
public static class BlockEntities extends AttachCapabilitiesEvent {
|
||||
public static final EventBus<BlockEntities> BUS = EventBus.create(BlockEntities.class);
|
||||
|
||||
public BlockEntities(BlockEntity obj) {
|
||||
super(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockEntity getObject() {
|
||||
return (BlockEntity)super.getObject();
|
||||
}
|
||||
}
|
||||
|
||||
public static class ItemStacks extends AttachCapabilitiesEvent<ItemStack> {
|
||||
public static class ItemStacks extends AttachCapabilitiesEvent {
|
||||
public static final EventBus<ItemStacks> BUS = EventBus.create(ItemStacks.class);
|
||||
|
||||
public ItemStacks(ItemStack obj) {
|
||||
super(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getObject() {
|
||||
return (ItemStack)super.getObject();
|
||||
}
|
||||
}
|
||||
|
||||
public static class Levels extends AttachCapabilitiesEvent<Level> {
|
||||
public static class Levels extends AttachCapabilitiesEvent {
|
||||
public static final EventBus<Levels> BUS = EventBus.create(Levels.class);
|
||||
|
||||
public Levels(Level obj) {
|
||||
super(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Level getObject() {
|
||||
return (Level)super.getObject();
|
||||
}
|
||||
}
|
||||
|
||||
public static class LevelChunks extends AttachCapabilitiesEvent<LevelChunk> {
|
||||
public static class LevelChunks extends AttachCapabilitiesEvent {
|
||||
public static final EventBus<LevelChunks> BUS = EventBus.create(LevelChunks.class);
|
||||
|
||||
public LevelChunks(LevelChunk obj) {
|
||||
super(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LevelChunk getObject() {
|
||||
return (LevelChunk)super.getObject();
|
||||
}
|
||||
}
|
||||
|
||||
protected AttachCapabilitiesEvent(T obj) {
|
||||
protected AttachCapabilitiesEvent(Object obj) {
|
||||
this.obj = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the object that is being created, Not much state is set.
|
||||
*/
|
||||
public T getObject() {
|
||||
public Object getObject() {
|
||||
return this.obj;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import net.minecraftforge.eventbus.api.bus.BusGroup;
|
||||
import net.minecraftforge.eventbus.api.bus.EventBus;
|
||||
import net.minecraftforge.eventbus.api.event.RecordEvent;
|
||||
import net.minecraftforge.fml.LogicalSide;
|
||||
import net.minecraftforge.fml.event.IModBusEvent;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@@ -38,6 +39,7 @@ public record BuildCreativeModeTabContentsEvent(
|
||||
) implements RecordEvent, CreativeModeTab.Output {
|
||||
public static final EventBus<BuildCreativeModeTabContentsEvent> BUS = EventBus.create(BuildCreativeModeTabContentsEvent.class);
|
||||
|
||||
/** @deprecated {@link BuildCreativeModeTabContentsEvent} is no longer an {@link IModBusEvent}, so use {@link #BUS} directly. */
|
||||
@Deprecated(forRemoval = true, since = "1.21.9")
|
||||
public static EventBus<BuildCreativeModeTabContentsEvent> getBus(BusGroup modBusGroup) {
|
||||
return BUS;
|
||||
|
||||
@@ -6,15 +6,13 @@
|
||||
package net.minecraftforge.event;
|
||||
|
||||
import net.minecraft.world.Difficulty;
|
||||
import net.minecraftforge.common.ForgeHooks;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.eventbus.api.bus.EventBus;
|
||||
import net.minecraftforge.eventbus.api.event.RecordEvent;
|
||||
|
||||
/**
|
||||
* DifficultyChangeEvent is fired when difficulty is changing. <br>
|
||||
* <br>
|
||||
* This event is fired via the {@link ForgeHooks#onDifficultyChange(Difficulty, Difficulty)}.<br>
|
||||
* This event is fired via the {@link ForgeEventFactory#onDifficultyChange(Difficulty, Difficulty)}.<br>
|
||||
*/
|
||||
public record DifficultyChangeEvent(Difficulty getDifficulty, Difficulty getOldDifficulty) implements RecordEvent {
|
||||
public static final EventBus<DifficultyChangeEvent> BUS = EventBus.create(DifficultyChangeEvent.class);
|
||||
|
||||
@@ -571,7 +571,7 @@ public final class ForgeEventFactory {
|
||||
// }
|
||||
|
||||
@Nullable
|
||||
public static CapabilityDispatcher gatherCapabilities(AttachCapabilitiesEvent<?> event, @Nullable ICapabilityProvider parent) {
|
||||
public static CapabilityDispatcher gatherCapabilities(AttachCapabilitiesEvent event, @Nullable ICapabilityProvider parent) {
|
||||
return !event.getCapabilities().isEmpty() || parent != null ? new CapabilityDispatcher(event.getCapabilities(), event.getListeners(), parent) : null;
|
||||
}
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ public abstract sealed class GrindstoneEvent extends MutableEvent implements Inh
|
||||
}
|
||||
|
||||
/**
|
||||
* This event is {@link Cancelable} <br>
|
||||
* This event is {@link Cancellable} <br>
|
||||
* {@link OnPlaceItem} is fired when the inputs to a grindstone are changed. <br>
|
||||
*
|
||||
* The following rules apply:
|
||||
@@ -72,7 +72,7 @@ public abstract sealed class GrindstoneEvent extends MutableEvent implements Inh
|
||||
* <li>Vanilla XP calculation logic will be used unless all of the following criterias are met:</li>
|
||||
* <ul>
|
||||
* <li>the amount of experience is greater than or equal to {@code 0};</li>
|
||||
* <li>the event is not {@linkplain #isCanceled() canceled};</li>
|
||||
* <li>the event is not {@linkplain Cancellable cancelled};</li>
|
||||
* <li>the {@linkplain #getOutput() output} is not empty.</li>
|
||||
* </ul>
|
||||
* </ul>
|
||||
@@ -108,7 +108,7 @@ public abstract sealed class GrindstoneEvent extends MutableEvent implements Inh
|
||||
}
|
||||
|
||||
/**
|
||||
* This event is {@link Cancelable} <br>
|
||||
* This event is {@link Cancellable} <br>
|
||||
* {@link OnTakeItem} is fired when the output in a grindstone are is taken. <br>
|
||||
* It is called from {@link GrindstoneMenu#GrindstoneMenu(int, Inventory)}. <br>
|
||||
* If the event is canceled, vanilla behavior will not run, and no inputs will be consumed. <br>
|
||||
|
||||
@@ -35,8 +35,7 @@ import java.util.stream.Stream;
|
||||
* unsafe to access server or level information during handling of 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},
|
||||
* <p>This event is fired on the {@linkplain FMLJavaModLoadingContext#getModBusGroup() mod BusGroup},
|
||||
* on both {@linkplain LogicalSide logical sides}.</p>
|
||||
*/
|
||||
public final class ModMismatchEvent implements IModBusEvent {
|
||||
|
||||
@@ -141,8 +141,8 @@ public sealed class PlayLevelSoundEvent extends MutableEvent implements Cancella
|
||||
* PlayLevelSoundEvent.AtEntity is fired when a sound is played on the {@link Level} at an {@link Entity Entity}'s position.
|
||||
* This event is fired from {@link Level#playSound}, {@link Level#playSeededSound}, and {@link LocalPlayer#playSound}.
|
||||
* <p>
|
||||
* This event is {@link Cancelable cancelable}.
|
||||
* If this event is canceled, the sound is not played.
|
||||
* This event is {@link Cancellable cancellable}.
|
||||
* If this event is cancelled, the sound is not played.
|
||||
* <p>
|
||||
* This event does not have a result.
|
||||
* <p>
|
||||
@@ -170,8 +170,8 @@ public sealed class PlayLevelSoundEvent extends MutableEvent implements Cancella
|
||||
* PlayLevelSoundEvent.AtPosition is fired when a sound is played on the {@link Level} at a specific position.
|
||||
* This event is fired from {@link Level#playSound} and {@link Level#playSeededSound}.
|
||||
* <p>
|
||||
* This event is {@link Cancelable cancelable}.
|
||||
* If this event is canceled, the sound is not played.
|
||||
* This event is {@link Cancellable cancellable}.
|
||||
* If this event is cancelled, the sound is not played.
|
||||
* <p>
|
||||
* This event does not have a result.
|
||||
* <p>
|
||||
|
||||
@@ -48,10 +48,8 @@ public sealed abstract class PotionBrewEvent extends MutableEvent implements Inh
|
||||
* <br>
|
||||
* {@link #stacks} contains the itemstack array from the TileEntityBrewer holding all items in Brewer.<br>
|
||||
* <br>
|
||||
* This event is {@link net.minecraftforge.eventbus.api.Cancelable}.<br>
|
||||
* If the event is not canceled, the vanilla brewing will take place instead of modded brewing.
|
||||
* <br>
|
||||
* This event does not have a result. {@link HasResult}<br>
|
||||
* This event is {@link Cancellable}.<br>
|
||||
* If the event is not cancelled, the vanilla brewing will take place instead of modded brewing.
|
||||
* <br>
|
||||
* This event is fired on the {@link MinecraftForge#EVENT_BUS}.<br>
|
||||
* <br>
|
||||
@@ -72,10 +70,6 @@ public sealed abstract class PotionBrewEvent extends MutableEvent implements Inh
|
||||
* <br>
|
||||
* {@link #stacks} contains the itemstack array from the TileEntityBrewer holding all items in Brewer.<br>
|
||||
* <br>
|
||||
* This event is not {@link net.minecraftforge.eventbus.api.Cancelable}.<br>
|
||||
* <br>
|
||||
* This event does not have a result. {@link HasResult}<br>
|
||||
* <br>
|
||||
* This event is fired on the {@link MinecraftForge#EVENT_BUS}.<br>
|
||||
**/
|
||||
public static final class Post extends PotionBrewEvent {
|
||||
|
||||
@@ -14,6 +14,7 @@ import net.minecraft.world.entity.ai.attributes.DefaultAttributes;
|
||||
import net.minecraftforge.eventbus.api.bus.BusGroup;
|
||||
import net.minecraftforge.eventbus.api.bus.EventBus;
|
||||
import net.minecraftforge.eventbus.api.event.MutableEvent;
|
||||
import net.minecraftforge.fml.event.IModBusEvent;
|
||||
|
||||
/**
|
||||
* EntityAttributeCreationEvent.<br>
|
||||
@@ -23,6 +24,7 @@ import net.minecraftforge.eventbus.api.event.MutableEvent;
|
||||
public final class EntityAttributeCreationEvent extends MutableEvent {
|
||||
public static final EventBus<EntityAttributeCreationEvent> BUS = EventBus.create(EntityAttributeCreationEvent.class);
|
||||
|
||||
/** @deprecated {@link EntityAttributeCreationEvent} is no longer an {@link IModBusEvent}, so use {@link #BUS} directly. */
|
||||
@Deprecated(forRemoval = true, since = "1.21.9")
|
||||
public static EventBus<EntityAttributeCreationEvent> getBus(BusGroup modBusGroup) {
|
||||
return BUS;
|
||||
|
||||
@@ -14,6 +14,7 @@ import net.minecraft.world.entity.ai.attributes.DefaultAttributes;
|
||||
import net.minecraftforge.eventbus.api.bus.BusGroup;
|
||||
import net.minecraftforge.eventbus.api.bus.EventBus;
|
||||
import net.minecraftforge.eventbus.api.event.MutableEvent;
|
||||
import net.minecraftforge.fml.event.IModBusEvent;
|
||||
import net.minecraftforge.registries.ForgeRegistries;
|
||||
|
||||
import java.util.List;
|
||||
@@ -27,6 +28,7 @@ import java.util.Map;
|
||||
public final class EntityAttributeModificationEvent extends MutableEvent {
|
||||
public static final EventBus<EntityAttributeModificationEvent> BUS = EventBus.create(EntityAttributeModificationEvent.class);
|
||||
|
||||
/** @deprecated {@link EntityAttributeModificationEvent} is no longer an {@link IModBusEvent}, so use {@link #BUS} directly. */
|
||||
@Deprecated(forRemoval = true, since = "1.21.9")
|
||||
public static EventBus<EntityAttributeModificationEvent> getBus(BusGroup modBusGroup) {
|
||||
return BUS;
|
||||
|
||||
@@ -11,7 +11,7 @@ import net.minecraftforge.eventbus.api.bus.EventBus;
|
||||
import net.minecraftforge.eventbus.api.event.RecordEvent;
|
||||
|
||||
/**
|
||||
* EntityEvent is fired when an event involving any Entity occurs.
|
||||
* EntityEvent is a marker interface for when an event involving any Entity occurs.
|
||||
*/
|
||||
public interface EntityEvent {
|
||||
/**
|
||||
|
||||
@@ -23,7 +23,7 @@ import net.minecraftforge.fml.LogicalSide;
|
||||
* You will cause chunk loading deadlocks if you do not delay your world interactions.
|
||||
* <p>
|
||||
* This event is {@linkplain Cancellable cancellable}.
|
||||
* If the event is canceled, the entity will not be added to the level.
|
||||
* If the event is cancelled, the entity will not be added to the level.
|
||||
* <p>
|
||||
* This event is fired on the {@linkplain MinecraftForge#EVENT_BUS main Forge event bus}
|
||||
* on both logical sides.
|
||||
|
||||
@@ -20,6 +20,7 @@ import net.minecraftforge.eventbus.api.bus.BusGroup;
|
||||
import net.minecraftforge.eventbus.api.bus.EventBus;
|
||||
import net.minecraftforge.eventbus.api.event.MutableEvent;
|
||||
import net.minecraftforge.eventbus.api.listener.Priority;
|
||||
import net.minecraftforge.fml.event.IModBusEvent;
|
||||
import net.minecraftforge.registries.ForgeRegistries;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
@@ -41,6 +42,7 @@ import org.jetbrains.annotations.ApiStatus;
|
||||
public final class SpawnPlacementRegisterEvent extends MutableEvent {
|
||||
public static final EventBus<SpawnPlacementRegisterEvent> BUS = EventBus.create(SpawnPlacementRegisterEvent.class);
|
||||
|
||||
/** @deprecated {@link SpawnPlacementRegisterEvent} is no longer an {@link IModBusEvent}, so use {@link #BUS} directly. */
|
||||
@Deprecated(forRemoval = true, since = "1.21.9")
|
||||
public static EventBus<SpawnPlacementRegisterEvent> getBus(BusGroup modBusGroup) {
|
||||
return BUS;
|
||||
|
||||
@@ -30,12 +30,10 @@ import org.jetbrains.annotations.Nullable;
|
||||
* {@link #causedByPlayer} contains the player responsible for the breading (if applicable).<br>
|
||||
* {@link #child} contains the child that will be spawned.<br>
|
||||
* <br>
|
||||
* This event is {@link Cancelable}.<br>
|
||||
* If this event is canceled, the child Entity is not added to the world, and the parents <br>
|
||||
* This event is {@link Cancellable}.<br>
|
||||
* If this event is cancelled, the child Entity is not added to the world, and the parents <br>
|
||||
* will no longer attempt to mate.
|
||||
* <br>
|
||||
* This event does not have a result. {@link HasResult}<br>
|
||||
* <br>
|
||||
* This event is fired on the {@link MinecraftForge#EVENT_BUS}.
|
||||
**/
|
||||
public final class BabyEntitySpawnEvent extends MutableEvent implements Cancellable {
|
||||
|
||||
@@ -22,7 +22,7 @@ import net.minecraftforge.eventbus.api.event.characteristic.Cancellable;
|
||||
* This event is fired via the {@link ForgeHooks#onLivingAttack(LivingEntity, DamageSource, float)}.<br>
|
||||
* <br>
|
||||
* This event is {@linkplain Cancellable cancellable}.<br>
|
||||
* If this event is canceled, the Entity does not take attack damage.
|
||||
* If this event is cancelled, the Entity does not take attack damage.
|
||||
*
|
||||
* @param getSource the source of the attack
|
||||
* @param getAmount the amount of damage dealt to the entity
|
||||
|
||||
Loaded 100 of 136 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user