[1.21.1] ModLoading cleanup and optimisations (#10052)

This commit is contained in:
Paint_Ninja 2024-08-16 22:49:11 +01:00 committed by GitHub
parent f106ff0c21
commit 9714512c31
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
101 changed files with 837 additions and 841 deletions

View file

@ -57,7 +57,9 @@ public class DeferredWorkQueue
LOGGER.debug(LOADING, "Dispatching synchronous work for work queue {}: {} jobs", modLoadingStage, tasks.size());
RuntimeException aggregate = new RuntimeException();
Stopwatch timer = Stopwatch.createStarted();
tasks.forEach(t -> makeRunnable(t, Runnable::run, aggregate));
for (TaskInfo t : tasks) {
makeRunnable(t, Runnable::run, aggregate);
}
timer.stop();
if (aggregate.getSuppressed().length > 0) {
LOGGER.fatal(
@ -114,7 +116,7 @@ public class DeferredWorkQueue
return future;
}
private static class TaskInfo {
private static final class TaskInfo {
private final ModContainer owner;
private Runnable task;
private CompletableFuture<?> future;

View file

@ -61,7 +61,7 @@ public class InterModComms
}
}
private static ConcurrentMap<String, ConcurrentLinkedQueue<IMCMessage>> containerQueues = new ConcurrentHashMap<>();
private static final ConcurrentMap<String, ConcurrentLinkedQueue<IMCMessage>> containerQueues = new ConcurrentHashMap<>();
/**
* Send IMC to remote. Sender will default to the active modcontainer, or minecraft if not.
@ -115,16 +115,14 @@ public class InterModComms
return getMessages(modId, s->Boolean.TRUE);
}
private static class QueueFilteringSpliterator implements Spliterator<IMCMessage>
{
private final ConcurrentLinkedQueue<IMCMessage> queue;
private final Predicate<String> methodFilter;
private final Iterator<IMCMessage> iterator;
private record QueueFilteringSpliterator(
ConcurrentLinkedQueue<IMCMessage> queue,
Predicate<String> methodFilter,
Iterator<IMCMessage> iterator
) implements Spliterator<IMCMessage> {
public QueueFilteringSpliterator(final ConcurrentLinkedQueue<IMCMessage> queue, final Predicate<String> methodFilter) {
this.queue = queue;
this.iterator = queue.iterator();
this.methodFilter = methodFilter;
this(queue, methodFilter, queue.iterator());
}
@Override

View file

@ -45,7 +45,7 @@ public abstract class ModContainer
protected final IModInfo modInfo;
protected ModLoadingStage modLoadingStage;
protected Supplier<?> contextExtension;
protected final Map<ModLoadingStage, Runnable> activityMap = new HashMap<>();
protected final Map<ModLoadingStage, Runnable> activityMap = new EnumMap<>(ModLoadingStage.class);
protected final Map<Class<? extends IExtensionPoint<?>>, Supplier<?>> extensionPoints = new IdentityHashMap<>();
protected final EnumMap<ModConfig.Type, ModConfig> configs = new EnumMap<>(ModConfig.Type.class);
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
@ -168,7 +168,10 @@ public abstract class ModContainer
}
public void dispatchConfigEvent(IConfigEvent event) {
configHandler.ifPresent(configHandler->configHandler.accept(event));
var handler = configHandler.orElse(null);
if (handler != null) {
handler.accept(event);
}
}
/**

View file

@ -74,11 +74,12 @@ public class ModList
}
private String fileToLine(IModFile mf) {
var mainMod = mf.getModInfos().getFirst();
return String.format(Locale.ENGLISH, "%-50.50s|%-30.30s|%-30.30s|%-20.20s|%-10.10s|Manifest: %s", mf.getFileName(),
mf.getModInfos().get(0).getDisplayName(),
mf.getModInfos().get(0).getModId(),
mf.getModInfos().get(0).getVersion(),
getModContainerState(mf.getModInfos().get(0).getModId()),
mainMod.getDisplayName(),
mainMod.getModId(),
mainMod.getVersion(),
getModContainerState(mainMod.getModId()),
((ModFileInfo)mf.getModFileInfo()).getCodeSigningFingerprint().orElse("NOSIGNATURE"));
}
private String crashReport() {
@ -95,14 +96,6 @@ public class ModList
return INSTANCE;
}
private static ForkJoinWorkerThread newForkJoinWorkerThread(ForkJoinPool pool) {
ForkJoinWorkerThread thread = ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(pool);
thread.setName("modloading-worker-" + thread.getPoolIndex());
// The default sets it to the SystemClassloader, so copy the current one.
thread.setContextClassLoader(Thread.currentThread().getContextClassLoader());
return thread;
}
public List<IModFileInfo> getModFiles()
{
return modFiles;
@ -131,16 +124,18 @@ public class ModList
CompletableFuture<Void> cf = new CompletableFuture<>();
final RuntimeException accumulator = new RuntimeException();
cf.completeExceptionally(accumulator);
throwables.forEach(exception -> {
for (Throwable exception : throwables) {
if (exception instanceof CompletionException) {
exception = exception.getCause();
}
if (exception.getSuppressed().length!=0) {
Arrays.stream(exception.getSuppressed()).forEach(accumulator::addSuppressed);
if (exception.getSuppressed().length != 0) {
for (Throwable throwable : exception.getSuppressed()) {
accumulator.addSuppressed(throwable);
}
} else {
accumulator.addSuppressed(exception);
}
});
}
return cf;
}
}
@ -148,11 +143,11 @@ public class ModList
static <V> CompletableFuture<List<Map.Entry<V, Throwable>>> gather(List<? extends CompletableFuture<? extends V>> futures) {
List<Map.Entry<V, Throwable>> list = new ArrayList<>(futures.size());
CompletableFuture<?>[] results = new CompletableFuture[futures.size()];
futures.forEach(future -> {
for (var future : futures) {
int i = list.size();
list.add(null);
results[i] = future.whenComplete((result, exception) -> list.set(i, new AbstractMap.SimpleImmutableEntry<>(result, exception)));
});
}
return CompletableFuture.allOf(results).handle((r, th)->null).thenApply(res -> list);
}

View file

@ -77,13 +77,13 @@ import static net.minecraftforge.fml.Logging.LOADING;
public class ModLoader
{
private static final Logger LOGGER = LogManager.getLogger();
private static ModLoader INSTANCE;
private final LoadingModList loadingModList;
private final List<ModLoadingException> loadingExceptions;
private final Set<IModInfo> erroredModInfos;
private final List<ModLoadingWarning> loadingWarnings;
private final ModStateManager stateManager;
private boolean loadingStateValid;
private static boolean loadingStateValid;
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
private final Optional<Consumer<String>> statusConsumer = StartupNotificationManager.modLoaderConsumer();
private final Set<IModLoadingState> completedStates = new HashSet<>();
@ -91,7 +91,6 @@ public class ModLoader
private ModLoader()
{
INSTANCE = this;
this.loadingModList = FMLLoader.getLoadingModList();
this.loadingExceptions = this.loadingModList.getErrors().stream()
.flatMap(ModLoadingException::fromEarlyException)
@ -99,24 +98,32 @@ public class ModLoader
this.loadingWarnings = this.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()) {
this.erroredModInfos = Collections.emptySet();
} else {
this.erroredModInfos = Collections.newSetFromMap(new IdentityHashMap<>());
this.erroredModInfos.addAll(this.loadingExceptions.stream().map(ModLoadingException::getModInfo).toList());
}
this.loadingModList.getModFiles().stream()
.filter(ModFileInfo::missingLicense)
.filter(modFileInfo -> modFileInfo.getMods().stream().noneMatch(thisModInfo -> this.loadingExceptions.stream().map(ModLoadingException::getModInfo).anyMatch(otherInfo -> otherInfo == thisModInfo))) //Ignore files where any other mod already encountered an error
.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()))
.forEach(this.loadingExceptions::add);
this.stateManager = new ModStateManager();
CrashReportCallables.registerCrashCallable("ModLauncher", FMLLoader::getLauncherInfo);
CrashReportCallables.registerCrashCallable("ModLauncher launch target", FMLLoader::launcherHandlerName);
CrashReportCallables.registerCrashCallable("ModLauncher naming", FMLLoader::getNaming);
CrashReportCallables.registerCrashCallable("ModLauncher services", this::computeModLauncherServiceList);
CrashReportCallables.registerCrashCallable("FML Language Providers", this::computeLanguageList);
CrashReportCallables.registerCrashCallable("ModLauncher services", ModLoader::computeModLauncherServiceList);
CrashReportCallables.registerCrashCallable("FML Language Providers", ModLoader::computeLanguageList);
}
private String computeLanguageList() {
return "\n"+FMLLoader.getLanguageLoadingProvider().applyForEach(lp->lp.name() +"@"+ lp.getClass().getPackage().getImplementationVersion()).collect(Collectors.joining("\n\t\t", "\t\t", ""));
private static String computeLanguageList() {
return "\n" + FMLLoader.getLanguageLoadingProvider()
.applyForEach(lp -> lp.name() + "@" + lp.getClass().getPackage().getImplementationVersion())
.collect(Collectors.joining("\n\t\t", "\t\t", ""));
}
private String computeModLauncherServiceList() {
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")+
@ -125,9 +132,13 @@ public class ModLoader
collect(Collectors.joining("\n\t\t","\t\t",""));
}
public static ModLoader get()
{
return INSTANCE == null ? INSTANCE = new ModLoader() : INSTANCE;
public static ModLoader get() {
return LazyInit.INSTANCE;
}
private static final class LazyInit {
private static final ModLoader INSTANCE = new ModLoader();
private LazyInit() {}
}
/**
@ -144,7 +155,7 @@ public class ModLoader
final ModList modList = ModList.of(loadingModList.getModFiles().stream().map(ModFileInfo::getFile).toList(),
loadingModList.getMods());
if (!this.loadingExceptions.isEmpty()) {
LOGGER.fatal(CORE, "Error during pre-loading phase", loadingExceptions.get(0));
LOGGER.fatal(CORE, "Error during pre-loading phase", loadingExceptions.getFirst());
statusConsumer.ifPresent(c->c.accept("ERROR DURING MOD LOADING"));
modList.setLoadedMods(Collections.emptyList());
loadingStateValid = false;
@ -152,7 +163,7 @@ public class ModLoader
}
List<? extends ForgeFeature.Bound> failedBounds = loadingModList.getMods().stream()
.map(ModInfo::getForgeFeatures)
.flatMap(Collection::stream)
.flatMap(List::stream)
.filter(bound -> !ForgeFeature.testFeature(FMLEnvironment.dist, bound))
.toList();
@ -169,10 +180,10 @@ public class ModLoader
final List<ModContainer> modContainers = loadingModList.getModFiles().stream()
.map(ModFileInfo::getFile)
.map(this::buildMods)
.<ModContainer>mapMulti(Iterable::forEach)
.flatMap(List::stream)
.toList();
if (!loadingExceptions.isEmpty()) {
LOGGER.fatal(CORE, "Failed to initialize mod containers", loadingExceptions.get(0));
LOGGER.fatal(CORE, "Failed to initialize mod containers", loadingExceptions.getFirst());
statusConsumer.ifPresent(c->c.accept("ERROR DURING MOD LOADING"));
modList.setLoadedMods(Collections.emptyList());
loadingStateValid = false;
@ -181,22 +192,28 @@ public class ModLoader
modList.setLoadedMods(modContainers);
this.modList = modList;
var stateList = stateManager.getStates(ModLoadingPhase.GATHER);
var progress = StartupMessageManager.addProgressBar("Mod Gather", stateList.stream().mapToInt(mls -> mls.size().applyAsInt(modList)).sum());
stateList.forEach(mls->dispatchAndHandleError(mls, syncExecutor, parallelExecutor, periodicTask, progress));
var progress = StartupMessageManager.addProgressBar("Mod Gather", stateList.stream().mapToInt(mls -> mls.size().applyAsInt(this.modList)).sum());
for (IModLoadingState mls : stateList) {
dispatchAndHandleError(mls, syncExecutor, parallelExecutor, periodicTask, progress);
}
progress.complete();
}
public void loadMods(final ModWorkManager.DrivenExecutor syncExecutor, final Executor parallelExecutor, final Runnable periodicTask) {
var stateList = stateManager.getStates(ModLoadingPhase.LOAD);
var progress = StartupMessageManager.addProgressBar("Mod Loading", stateList.stream().mapToInt(mls -> mls.size().applyAsInt(modList)).sum());
stateList.forEach(mls->dispatchAndHandleError(mls, syncExecutor, parallelExecutor, periodicTask, progress));
for (IModLoadingState mls : stateList) {
dispatchAndHandleError(mls, syncExecutor, parallelExecutor, periodicTask, progress);
}
progress.complete();
}
public void finishMods(final ModWorkManager.DrivenExecutor syncExecutor, final Executor parallelExecutor, final Runnable periodicTask) {
var stateList = stateManager.getStates(ModLoadingPhase.COMPLETE);
var progress = StartupMessageManager.addProgressBar("Mod Complete", stateList.stream().mapToInt(mls -> mls.size().applyAsInt(modList)).sum());
stateList.forEach(mls->dispatchAndHandleError(mls, syncExecutor, parallelExecutor, periodicTask, progress));
for (IModLoadingState mls : stateList) {
dispatchAndHandleError(mls, syncExecutor, parallelExecutor, periodicTask, progress);
}
statusConsumer.ifPresent(c->c.accept(String.format("Mod loading complete - %d mods loaded", this.modList.size())));
progress.complete();
}
@ -206,10 +223,15 @@ public class ModLoader
LOGGER.error("Cowardly refusing to process mod state change request from {}", state);
return;
}
progressBar.label(progressBar.name()+ " working");
progressBar.label(progressBar.name() + " working");
syncExecutor.drive(ticker);
state.inlineRunnable().ifPresent(a->this.handleInlineTransition(a, state, syncExecutor, ticker));
state.buildTransition(syncExecutor, parallelExecutor, progressBar).ifPresent(t->waitForTransition(state, syncExecutor, ticker, t));
var inlineRunnable = state.inlineRunnable().orElse(null);
if (inlineRunnable != null) handleInlineTransition(inlineRunnable, state, syncExecutor, ticker);
var transition = state.buildTransition(syncExecutor, parallelExecutor, progressBar).orElse(null);
if (transition != null) waitForTransition(state, syncExecutor, ticker, transition);
completedStates.add(state);
}
@ -286,9 +308,12 @@ public class ModLoader
try {
final String modId = idToProviderEntry.getKey();
final IModLanguageProvider.IModLanguageLoader languageLoader = idToProviderEntry.getValue();
IModInfo info = Optional.ofNullable(modInfoMap.get(modId)).
// throw a missing metadata error if there is no matching modid in the modInfoMap from the mods.toml file
orElseThrow(()->new ModLoadingException(null, ModLoadingStage.CONSTRUCT, "fml.modloading.missingmetadata", null, modId));
IModInfo info = modInfoMap.get(modId);
// throw a missing metadata error if there is no matching modid in the modInfoMap from the mods.toml file
if (info == null)
throw new ModLoadingException(null, ModLoadingStage.CONSTRUCT, "fml.modloading.missingmetadata", null, modId);
return languageLoader.loadMod(info, modFile.getScanResult(), FMLLoader.getGameLayer());
} catch (ModLoadingException mle) {
// exceptions are caught and added to the error list for later handling
@ -303,7 +328,7 @@ public class ModLoader
* and don't want to cause extraneous crashes due to trying to do things that aren't possible in a "broken load"
*/
public static boolean isLoadingStateValid() {
return get().loadingStateValid;
return loadingStateValid;
}
public boolean hasCompletedState(final String stateName) {

View file

@ -39,7 +39,10 @@ public record ModLoadingState(String name, String previous,
final ProgressMeter progressBar,
final Function<Executor, CompletableFuture<Void>> preSyncTask,
final Function<Executor, CompletableFuture<Void>> postSyncTask) {
return transition.map(t -> t.build(name, syncExecutor, parallelExecutor, progressBar, preSyncTask, postSyncTask));
var transition = this.transition.orElse(null);
return transition == null
? Optional.empty()
: Optional.ofNullable(transition.build(name, syncExecutor, parallelExecutor, progressBar, preSyncTask, postSyncTask));
}
/**

View file

@ -16,15 +16,13 @@ import java.util.stream.Collectors;
@SuppressWarnings("UnstableApiUsage")
public class ModStateManager {
static ModStateManager INSTANCE;
private final EnumMap<ModLoadingPhase, List<IModLoadingState>> stateMap;
public ModStateManager() {
INSTANCE = this;
final var sp = ServiceLoader.load(FMLLoader.getGameLayer(), IModStateProvider.class);
this.stateMap = ServiceLoaderUtils.streamWithErrorHandling(sp, sce->{})
.map(IModStateProvider::getAllStates)
.<IModLoadingState>mapMulti(Iterable::forEach)
.flatMap(List::stream)
.collect(Collectors.groupingBy(IModLoadingState::phase, ()->new EnumMap<>(ModLoadingPhase.class), Collectors.toUnmodifiableList()));
}
@ -36,14 +34,16 @@ public class ModStateManager {
var dummy = ModLoadingState.empty("", "", phase);
nodes.forEach(graph::addNode);
graph.addNode(dummy);
nodes.forEach(n->graph.putEdge(lookup.getOrDefault(n.previous(), dummy), n));
for (IModLoadingState node : nodes) {
graph.putEdge(lookup.getOrDefault(node.previous(), dummy), node);
}
return TopologicalSort.topologicalSort(graph, Comparator.comparingInt(nodes::indexOf)).stream().filter(st->st!=dummy).toList();
}
public IModLoadingState findState(final String stateName) {
return stateMap.values()
.stream()
.flatMap(Collection::stream)
.flatMap(List::stream)
.filter(mls -> mls.name().equals(stateName))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("Unknown IModLoadingState: " + stateName));

View file

@ -17,6 +17,7 @@ import static net.minecraftforge.fml.Logging.LOADING;
public class ModWorkManager {
private static final Logger LOGGER = LogManager.getLogger();
private static final long PARK_TIME = TimeUnit.MILLISECONDS.toNanos(1);
public interface DrivenExecutor extends Executor {
boolean selfDriven();
boolean driveOne();
@ -33,8 +34,11 @@ public class ModWorkManager {
}
}
}
private static class SyncExecutor implements DrivenExecutor {
private ConcurrentLinkedDeque<Runnable> tasks = new ConcurrentLinkedDeque<>();
private record SyncExecutor(ConcurrentLinkedDeque<Runnable> tasks) implements DrivenExecutor {
public SyncExecutor() {
this(new ConcurrentLinkedDeque<>());
}
@Override
public boolean driveOne() {
@ -56,13 +60,7 @@ public class ModWorkManager {
}
}
private static class WrappingExecutor implements DrivenExecutor {
private final Executor wrapped;
public WrappingExecutor(final Executor executor) {
this.wrapped = executor;
}
private record WrappingExecutor(Executor wrapped) implements DrivenExecutor {
@Override
public boolean selfDriven() {
return true;
@ -79,11 +77,9 @@ public class ModWorkManager {
}
}
private static SyncExecutor syncExecutor;
private static final SyncExecutor syncExecutor = new SyncExecutor();
public static DrivenExecutor syncExecutor() {
if (syncExecutor == null)
syncExecutor = new SyncExecutor();
return syncExecutor;
}
@ -91,14 +87,8 @@ public class ModWorkManager {
return new WrappingExecutor(executor);
}
private static ForkJoinPool parallelThreadPool;
public static Executor parallelExecutor() {
if (parallelThreadPool == null) {
final int loadingThreadCount = FMLConfig.getIntConfigValue(FMLConfig.ConfigValue.MAX_THREADS);
LOGGER.debug(LOADING, "Using {} threads for parallel mod-loading", loadingThreadCount);
parallelThreadPool = new ForkJoinPool(loadingThreadCount, ModWorkManager::newForkJoinWorkerThread, null, false);
}
return parallelThreadPool;
return LazyInit.PARALLEL_EXECUTOR;
}
private static ForkJoinWorkerThread newForkJoinWorkerThread(ForkJoinPool pool) {
@ -109,4 +99,14 @@ public class ModWorkManager {
return thread;
}
private static final class LazyInit {
private LazyInit() {}
private static final ForkJoinPool PARALLEL_EXECUTOR;
static {
final int loadingThreadCount = FMLConfig.getIntConfigValue(FMLConfig.ConfigValue.MAX_THREADS);
LOGGER.debug(LOADING, "Using {} threads for parallel mod-loading", loadingThreadCount);
PARALLEL_EXECUTOR = new ForkJoinPool(loadingThreadCount, ModWorkManager::newForkJoinWorkerThread, null, false);
}
}
}

View file

@ -23,7 +23,7 @@ public final class OptionalMod<T>
return new OptionalMod<>(modId);
}
private static OptionalMod<?> EMPTY = new OptionalMod<>(true);
private static final OptionalMod<?> EMPTY = new OptionalMod<>(true);
private static <T> OptionalMod<T> empty() {
@SuppressWarnings("unchecked")
OptionalMod<T> t = (OptionalMod<T>) EMPTY;
@ -213,8 +213,8 @@ public final class OptionalMod<T>
public boolean equals(Object obj)
{
if (this == obj) return true;
if (obj instanceof OptionalMod) {
return Objects.equals(((OptionalMod)obj).modId, modId);
if (obj instanceof OptionalMod<?> optionalMod) {
return Objects.equals(optionalMod.modId, modId);
}
return false;
}

View file

@ -25,7 +25,7 @@ import static net.minecraftforge.fml.config.ConfigTracker.CONFIG;
public class ConfigFileTypeHandler {
private static final Logger LOGGER = LogUtils.getLogger();
static ConfigFileTypeHandler TOML = new ConfigFileTypeHandler();
static final ConfigFileTypeHandler TOML = new ConfigFileTypeHandler();
private static final Path defaultConfigPath = FMLPaths.GAMEDIR.get().resolve(FMLConfig.getConfigValue(FMLConfig.ConfigValue.DEFAULT_CONFIG_PATH));
public Function<ModConfig, CommentedFileConfig> reader(Path configBasePath) {
@ -62,7 +62,7 @@ public class ConfigFileTypeHandler {
}
}
private boolean setupConfigFile(final ModConfig modConfig, final Path file, final ConfigFormat<?> conf) throws IOException {
private static boolean setupConfigFile(final ModConfig modConfig, final Path file, final ConfigFormat<?> conf) throws IOException {
if (!Files.isDirectory(file.getParent())) {
Files.createDirectories(file.getParent());
}
@ -109,17 +109,11 @@ public class ConfigFileTypeHandler {
}
}
private static class ConfigWatcher implements Runnable {
private final ModConfig modConfig;
private final CommentedFileConfig commentedFileConfig;
private final ClassLoader realClassLoader;
ConfigWatcher(final ModConfig modConfig, final CommentedFileConfig commentedFileConfig, final ClassLoader classLoader) {
this.modConfig = modConfig;
this.commentedFileConfig = commentedFileConfig;
this.realClassLoader = classLoader;
}
private record ConfigWatcher(
ModConfig modConfig,
CommentedFileConfig commentedFileConfig,
ClassLoader realClassLoader
) implements Runnable {
@Override
public void run() {
// Force the regular classloader onto the special thread

View file

@ -47,15 +47,19 @@ public class ConfigTracker {
public void loadConfigs(ModConfig.Type type, Path configBasePath) {
LOGGER.debug(CONFIG, "Loading configs type {}", type);
this.configSets.get(type).forEach(config -> openConfig(config, configBasePath));
for (ModConfig config : this.configSets.get(type)) {
openConfig(config, configBasePath);
}
}
public void unloadConfigs(ModConfig.Type type, Path configBasePath) {
LOGGER.debug(CONFIG, "Unloading configs type {}", type);
this.configSets.get(type).forEach(config -> closeConfig(config, configBasePath));
for (ModConfig config : this.configSets.get(type)) {
closeConfig(config, configBasePath);
}
}
private void openConfig(final ModConfig config, final Path configBasePath) {
private static void openConfig(final ModConfig config, final Path configBasePath) {
LOGGER.trace(CONFIG, "Loading config file type {} at {} for {}", config.getType(), config.getFileName(), config.getModId());
final CommentedFileConfig configData = config.getHandler().reader(configBasePath).apply(config);
config.setConfigData(configData);
@ -63,7 +67,7 @@ public class ConfigTracker {
config.save();
}
private void closeConfig(final ModConfig config, final Path configBasePath) {
private static void closeConfig(final ModConfig config, final Path configBasePath) {
if (config.getConfigData() != null) {
LOGGER.trace(CONFIG, "Closing config file type {} at {} for {}", config.getType(), config.getFileName(), config.getModId());
// stop the filewatcher before we save the file and close it, so reload doesn't fire

View file

@ -24,7 +24,6 @@ public class ModConfig
private final ModContainer container;
private final ConfigFileTypeHandler configHandler;
private CommentedConfig configData;
private Callable<Void> saveHandler;
public ModConfig(final Type type, final IConfigSpec<?> spec, final ModContainer container, final String fileName) {
this.type = type;

View file

@ -25,7 +25,7 @@ public class SimpleFont {
private final int lineSpacing;
private final int descent;
private final int GLYPH_COUNT = 127-32;
private Glyph[] glyphs;
private final Glyph[] glyphs;
private record Glyph(char c, int charwidth, int[] pos, float[] uv) {
Pos loadQuad(Pos pos, int colour, SimpleBufferBuilder bb) {

View file

@ -36,9 +36,9 @@ import cpw.mods.modlauncher.serviceapi.ILaunchPluginService;
*/
public class CapabilityTokenSubclass implements ILaunchPluginService {
private final String FUNC_NAME = "getType";
private final String FUNC_DESC = "()Ljava/lang/String;";
private final String CAP_INJECT = "net/minecraftforge/common/capabilities/CapabilityToken"; //Don't directly reference this to prevent class loading.
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.
@Override
public String name() {
@ -67,7 +67,7 @@ public class CapabilityTokenSubclass implements ILaunchPluginService {
SignatureReader reader = new SignatureReader(classNode.signature); // Having a node version of this would probably be useful.
reader.accept(new SignatureVisitor(Opcodes.ASM9) {
Deque<String> stack = new ArrayDeque<>();
final Deque<String> stack = new ArrayDeque<>();
@Override
public void visitClassType(final String name) {
@ -100,7 +100,7 @@ public class CapabilityTokenSubclass implements ILaunchPluginService {
}
}
private static class Holder {
private static final class Holder {
String value;
}
}

View file

@ -34,8 +34,9 @@ public class ObjectHolderDefinalize implements ILaunchPluginService {
new VanillaObjectHolderData("net.minecraft.world.effect.MobEffects", "mob_effect", "net.minecraft.world.effect.MobEffect"),
new VanillaObjectHolderData("net.minecraft.core.particles.ParticleTypes", "particle_type", "net.minecraft.core.particles.ParticleType"),
new VanillaObjectHolderData("net.minecraft.sounds.SoundEvents", "sound_event", "net.minecraft.sounds.SoundEvent")
).collect(Collectors.toMap(VanillaObjectHolderData::holderClass, Function.identity()));
private final String OBJECT_HOLDER = "Lnet/minecraftforge/registries/ObjectHolder;"; //Don't directly reference this to prevent class loading.
).collect(Collectors.toUnmodifiableMap(VanillaObjectHolderData::holderClass, Function.identity()));
private static final String OBJECT_HOLDER = "Lnet/minecraftforge/registries/ObjectHolder;"; //Don't directly reference this to prevent class loading.
private static final int PUBLIC_STATIC_FINAL_FLAGS = Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL;
@Override
public String name() {
@ -51,8 +52,7 @@ public class ObjectHolderDefinalize implements ILaunchPluginService {
return isEmpty ? NAY : YAY;
}
private boolean hasHolder(List<AnnotationNode> lst)
{
private static boolean hasHolder(List<AnnotationNode> lst) {
return lst != null && lst.stream().anyMatch(n -> n.desc.equals(OBJECT_HOLDER));
}
@ -76,10 +76,9 @@ public class ObjectHolderDefinalize implements ILaunchPluginService {
{
final AtomicBoolean changes = new AtomicBoolean();
//Must be public static finals, and non-array objects
final int flags = Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL;
//Fix Annotated Fields before injecting from class level
classNode.fields.stream().filter(f -> ((f.access & flags) == flags) && f.desc.startsWith("L") && hasHolder(f.visibleAnnotations)).forEach(f ->
classNode.fields.stream().filter(f -> ((f.access & PUBLIC_STATIC_FINAL_FLAGS) == PUBLIC_STATIC_FINAL_FLAGS) && f.desc.startsWith("L") && hasHolder(f.visibleAnnotations)).forEach(f ->
{
int prev = f.access;
f.access &= ~Opcodes.ACC_FINAL; //Strip final
@ -89,7 +88,7 @@ public class ObjectHolderDefinalize implements ILaunchPluginService {
if (VANILLA_OBJECT_HOLDERS.containsKey(classType.getClassName())) //Class level, de-finalize all fields and add @ObjectHolder to them!
{
classNode.fields.stream().filter(f -> ((f.access & flags) == flags) && f.desc.startsWith("L")).forEach(f ->
classNode.fields.stream().filter(f -> ((f.access & PUBLIC_STATIC_FINAL_FLAGS) == PUBLIC_STATIC_FINAL_FLAGS) && f.desc.startsWith("L")).forEach(f ->
{
int prev = f.access;
f.access &= ~Opcodes.ACC_FINAL;

View file

@ -28,15 +28,16 @@ import org.slf4j.Logger;
public class RuntimeEnumExtender implements ILaunchPluginService {
private static final Logger LOGGER = LogUtils.getLogger();
private final Type STRING = Type.getType(String.class);
private final Type ENUM = Type.getType(Enum.class);
private final Type MARKER_IFACE = Type.getType("Lnet/minecraftforge/common/IExtensibleEnum;");
private final Type ARRAY_UTILS = Type.getType("Lorg/apache/commons/lang3/ArrayUtils;"); //Don't directly reference this to prevent class loading.
private final String ADD_DESC = Type.getMethodDescriptor(Type.getType(Object[].class), Type.getType(Object[].class), Type.getType(Object.class));
private final Type UNSAFE_HACKS = Type.getType("Lnet/minecraftforge/fml/unsafe/UnsafeHacks;"); //Again, not direct reference to prevent class loading.
private final String CLEAN_DESC = Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(Class.class));
private final String NAME_DESC = Type.getMethodDescriptor(STRING);
private final String EQUALS_DESC = Type.getMethodDescriptor(Type.BOOLEAN_TYPE, STRING);
private static final Type STRING = Type.getType(String.class);
private static final Type ENUM = Type.getType(Enum.class);
private static final Type MARKER_IFACE = Type.getType("Lnet/minecraftforge/common/IExtensibleEnum;");
private static final Type ARRAY_UTILS = Type.getType("Lorg/apache/commons/lang3/ArrayUtils;"); //Don't directly reference this to prevent class loading.
private static final String ADD_DESC = Type.getMethodDescriptor(Type.getType(Object[].class), Type.getType(Object[].class), Type.getType(Object.class));
private static final Type UNSAFE_HACKS = Type.getType("Lnet/minecraftforge/fml/unsafe/UnsafeHacks;"); //Again, not direct reference to prevent class loading.
private static final String CLEAN_DESC = Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(Class.class));
private static final String NAME_DESC = Type.getMethodDescriptor(STRING);
private static final String EQUALS_DESC = Type.getMethodDescriptor(Type.BOOLEAN_TYPE, STRING);
private static final int FLAGS = Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL | Opcodes.ACC_SYNTHETIC;
@Override
public String name() {
@ -59,9 +60,9 @@ public class RuntimeEnumExtender implements ILaunchPluginService {
return ComputeFlags.NO_REWRITE;
Type array = Type.getType("[" + classType.getDescriptor());
final int flags = Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL | Opcodes.ACC_SYNTHETIC;
String arrayDesc = array.getDescriptor();
FieldNode values = classNode.fields.stream().filter(f -> f.desc.contentEquals(array.getDescriptor()) && ((f.access & flags) == flags)).findFirst().orElse(null);
FieldNode values = classNode.fields.stream().filter(f -> f.desc.equals(arrayDesc) && ((f.access & FLAGS) == FLAGS)).findFirst().orElse(null);
if (!classNode.interfaces.contains(MARKER_IFACE.getInternalName())) {
return ComputeFlags.NO_REWRITE;
@ -70,13 +71,13 @@ public class RuntimeEnumExtender implements ILaunchPluginService {
//Static methods named "create" with first argument as a string
List<MethodNode> candidates = classNode.methods.stream()
.filter(m -> ((m.access & Opcodes.ACC_STATIC) != 0) && m.name.equals("create"))
.collect(Collectors.toList());
.toList();
if (candidates.isEmpty()) {
throw new IllegalStateException("IExtensibleEnum has no candidate factory methods: " + classType.getClassName());
}
candidates.forEach(mtd ->
for (var mtd : candidates)
{
Type[] args = Type.getArgumentTypes(mtd.desc);
if (args.length == 0 || !args[0].equals(STRING)) {
@ -108,8 +109,7 @@ public class RuntimeEnumExtender implements ILaunchPluginService {
Type[] ctrArgs = new Type[args.length + 1];
ctrArgs[0] = STRING;
ctrArgs[1] = Type.INT_TYPE;
for (int x = 1; x < args.length; x++)
ctrArgs[1 + x] = args[x];
System.arraycopy(args, 1, ctrArgs, 2, args.length - 1);
String desc = Type.getMethodDescriptor(Type.VOID_TYPE, ctrArgs);
@ -231,7 +231,7 @@ public class RuntimeEnumExtender implements ILaunchPluginService {
ins.load(vars, classType);
ins.areturn(classType);
}
});
}
return ComputeFlags.COMPUTE_FRAMES;
}

View file

@ -17,11 +17,9 @@ import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.fml.loading.targets.CommonLaunchHandler;
import net.minecraftforge.forgespi.Environment;
import net.minecraftforge.forgespi.coremod.ICoreModProvider;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@ -36,7 +34,6 @@ import static net.minecraftforge.fml.loading.LogMarkers.SCAN;
public class FMLLoader {
private static final Logger LOGGER = LogUtils.getLogger();
private static AccessTransformerService accessTransformer;
private static ModDiscoverer modDiscoverer;
private static ICoreModProvider coreModProvider;
private static LanguageLoadingProvider languageLoadingProvider;
private static Dist dist;
@ -62,7 +59,7 @@ public class FMLLoader {
/*eventBus =*/ getPlugin(env, "eventbus", "1.0", "EventBus");
runtimeDistCleaner = getPlugin(env, "runtimedistcleaner", "1.0", "RuntimeDistCleaner");
coreModProvider = getSingleService(ICoreModProvider.class, "CoreMod");
LOGGER.debug(CORE,"FML found CoreMod version : {}", JarVersionLookupHandler.getInfo(coreModProvider.getClass()).impl().version().orElse("MISSING"));
LOGGER.debug(CORE, "FML found CoreMod version : {}", JarVersionLookupHandler.getInfo(coreModProvider.getClass()).impl().version().orElse("MISSING"));
checkPackage(Environment.class, "2.0", "ForgeSPI");
try {
@ -74,14 +71,15 @@ public class FMLLoader {
}
}
@SuppressWarnings("unchecked")
private static <T> T getPlugin(IEnvironment env, String id, String version, String name) throws IncompatibleEnvironmentException {
@SuppressWarnings("unchecked")
var plugin = (T)env.findLaunchPlugin(id).orElseThrow(() -> {
var plugin = env.findLaunchPlugin(id).orElse(null);
if (plugin == null) {
LOGGER.error(CORE, "{} library is missing, we need this to run", name);
return new IncompatibleEnvironmentException("Missing " + name + ", cannot run");
});
throw new IncompatibleEnvironmentException("Missing " + name + ", cannot run");
}
checkPackage(plugin.getClass(), version, name);
return plugin;
return (T) plugin;
}
private static void checkPackage(Class<?> cls, String version, String name) throws IncompatibleEnvironmentException {
@ -137,20 +135,20 @@ public class FMLLoader {
}
commonLaunchHandler = (CommonLaunchHandler)launchHandler.get();
launchHandlerName = launchHandler.get().name();
gamePath = environment.getProperty(IEnvironment.Keys.GAMEDIR.get()).orElse(Paths.get(".").toAbsolutePath());
gamePath = environment.getProperty(IEnvironment.Keys.GAMEDIR.get()).orElse(Path.of(".").toAbsolutePath());
naming = commonLaunchHandler.getNaming();
dist = commonLaunchHandler.getDist();
production = commonLaunchHandler.isProduction();
accessTransformer.getExtension().accept(Pair.of(naming, "srg"));
accessTransformer.getExtension().accept(Map.entry(naming, "srg"));
runtimeDistCleaner.getExtension().accept(dist);
}
public static List<ITransformationService.Resource> beginModScan(final Map<String,?> arguments) {
LOGGER.debug(SCAN,"Scanning for Mod Locators");
modDiscoverer = new ModDiscoverer(arguments);
var modDiscoverer = new ModDiscoverer(arguments);
modValidator = modDiscoverer.discoverMods();
var pluginResources = modValidator.getPluginResources();
return List.of(pluginResources);
@ -172,10 +170,6 @@ public class FMLLoader {
return languageLoadingProvider;
}
static ModDiscoverer getModDiscoverer() {
return modDiscoverer;
}
public static CommonLaunchHandler getLaunchHandler() {
return commonLaunchHandler;
}

View file

@ -24,7 +24,7 @@ import static net.minecraftforge.fml.loading.LogMarkers.CORE;
public class FMLServiceProvider implements ITransformationService {
private static final Logger LOGGER = LogUtils.getLogger();
private Map<String, Object> arguments;
private final Map<String, Object> arguments = new HashMap<>();
public FMLServiceProvider() {
var markers = System.getProperty("forge.logging.markers", "").split(",");
@ -45,7 +45,6 @@ public class FMLServiceProvider implements ITransformationService {
FMLConfig.load();
LOGGER.debug(CORE, "Preparing ModFile");
environment.computePropertyIfAbsent(Environment.Keys.MODFILEFACTORY.get(), k->ModFile::new);
arguments = new HashMap<>();
LOGGER.debug(CORE, "Preparing launch handler");
FMLLoader.setupLaunchHandler(environment, arguments);
FMLEnvironment.setupInteropEnvironment(environment);
@ -68,14 +67,6 @@ public class FMLServiceProvider implements ITransformationService {
FMLLoader.onInitialLoad(environment, otherServices);
}
@Override
public void arguments(BiFunction<String, String, OptionSpecBuilder> argumentBuilder) {
}
@Override
public void argumentValues(OptionResult option) {
}
@SuppressWarnings("rawtypes")
@Override
public @NotNull List<ITransformer> transformers() {

View file

@ -78,9 +78,13 @@ public class LoadingModList
public void addAccessTransformers()
{
modFiles.stream()
.map(ModFileInfo::getFile)
.forEach(mod -> mod.getAccessTransformer().ifPresent(path -> FMLLoader.addAccessTransformer(path, mod)));
for (ModFileInfo modFile : modFiles) {
ModFile mod = modFile.getFile();
var at = mod.getAccessTransformer().orElse(null);
if (at != null) {
FMLLoader.addAccessTransformer(at, mod);
}
}
}
public void addForScanning(BackgroundScanHandler backgroundScanHandler)

View file

@ -36,7 +36,7 @@ public class MCPNamingService implements INameMappingService {
@Override
public Map.Entry<String, String> understanding() {
return Pair.of("srg", "mcp");
return Map.entry("srg", "mcp");
}
@Override

View file

@ -34,7 +34,7 @@ public class MavenCoordinateResolver {
(!extension.isEmpty() ? "." + extension : ".jar");
String[] groups = groupId.split("\\.");
Path result = Paths.get(groups[0]);
Path result = Path.of(groups[0]);
for (int i = 1; i < groups.length; i++) {
result = result.resolve(groups[i]);
}

View file

@ -57,13 +57,13 @@ public class ModSorter
// Note this will never actually throw an error because the duplicate checks are done in ModDiscovererer before we get to this phase
// So all this is really doing is wasting time.
// But i'm leaving it here until I rewrite all of cpw's mod loading code because its such a clusterfuck.
return LoadingModList.of(ms.systemMods, ms.systemMods.stream().map(mf->(ModInfo)mf.getModInfos().get(0)).collect(toList()), e);
return LoadingModList.of(ms.systemMods, ms.systemMods.stream().map(mf->(ModInfo)mf.getModInfos().get(0)).toList(), e);
}
// try and validate dependencies
final List<ExceptionData> failedList = Stream.concat(ms.verifyDependencyVersions().stream(), errors.stream()).toList();
// if we miss one or the other, we abort now
if (!failedList.isEmpty()) {
return LoadingModList.of(ms.systemMods, ms.systemMods.stream().map(mf->(ModInfo)mf.getModInfos().get(0)).collect(toList()), new EarlyLoadingException("failure to validate mod list", null, failedList));
return LoadingModList.of(ms.systemMods, ms.systemMods.stream().map(mf->(ModInfo)mf.getModInfos().get(0)).toList(), new EarlyLoadingException("failure to validate mod list", null, failedList));
} else {
// Otherwise, lets try and sort the modlist and proceed
EarlyLoadingException earlyLoadingException = null;
@ -89,9 +89,9 @@ public class ModSorter
infos.keySet().forEach(graph::addNode);
modFiles.stream()
.map(ModFile::getModInfos)
.<IModInfo>mapMulti(Iterable::forEach)
.flatMap(List::stream)
.map(IModInfo::getDependencies)
.<IModInfo.ModVersion>mapMulti(Iterable::forEach)
.<IModInfo.ModVersion>flatMap(List::stream)
.forEach(dep -> addDependency(graph, dep));
final List<ModFileInfo> sorted;
@ -107,7 +107,7 @@ public class ModSorter
LOGGER.error(LOADING, "Mod Sorting failed.\nDetected Cycles: {}\n", cycles);
}
var dataList = cycles.stream()
.<ModFileInfo>mapMulti(Iterable::forEach)
.flatMap(Set::stream)
.<IModInfo>mapMulti((mf,c)->mf.getMods().forEach(c))
.map(IModInfo::getModId)
.map(list -> new ExceptionData("fml.modloading.cycle", list))
@ -116,7 +116,7 @@ public class ModSorter
}
this.sortedList = sorted.stream()
.map(ModFileInfo::getMods)
.<IModInfo>mapMulti(Iterable::forEach)
.flatMap(List::stream)
.map(ModInfo.class::cast)
.collect(toList());
this.modFiles = sorted.stream()
@ -126,12 +126,14 @@ public class ModSorter
private void addDependency(MutableGraph<ModFileInfo> topoGraph, IModInfo.ModVersion dep)
{
final ModFileInfo self = (ModFileInfo)dep.getOwner().getOwningFile();
final IModInfo targetModInfo = modIdNameLookup.get(dep.getModId());
// soft dep that doesn't exist. Just return. No edge required.
if (targetModInfo == null || !(targetModInfo.getOwningFile() instanceof final ModFileInfo target)) return;
final ModFileInfo self = (ModFileInfo)dep.getOwner().getOwningFile();
if (self == target)
return; // in case a jar has two mods that have dependencies between
switch (dep.getOrdering()) {
case BEFORE -> topoGraph.putEdge(self, target);
case AFTER -> topoGraph.putEdge(target, self);
@ -165,7 +167,7 @@ public class ModSorter
var container = modFilesByFirstId.get(systemMod);
if (container != null && !container.isEmpty()) {
LOGGER.debug("Found system mod: {}", systemMod);
this.systemMods.add((ModFile) container.get(0));
this.systemMods.add(container.getFirst());
} else {
throw new IllegalStateException("Failed to find system mod: " + systemMod);
}
@ -176,23 +178,23 @@ public class ModSorter
{
final var modVersions = modFiles.stream()
.map(ModFile::getModInfos)
.<IModInfo>mapMulti(Iterable::forEach)
.flatMap(List::stream)
.collect(toMap(IModInfo::getModId, IModInfo::getVersion));
final var modVersionDependencies = modFiles.stream()
.map(ModFile::getModInfos)
.<IModInfo>mapMulti(Iterable::forEach)
.flatMap(List::stream)
.collect(groupingBy(Function.identity(), flatMapping(e -> e.getDependencies().stream(), toList())));
final var modRequirements = modVersionDependencies.values().stream()
.<IModInfo.ModVersion>mapMulti(Iterable::forEach)
.<IModInfo.ModVersion>flatMap(List::stream)
.filter(mv -> mv.getSide().isCorrectSide())
.collect(toSet());
final long mandatoryRequired = modRequirements.stream().filter(IModInfo.ModVersion::isMandatory).count();
LOGGER.debug(LOADING, "Found {} mod requirements ({} mandatory, {} optional)", modRequirements.size(), mandatoryRequired, modRequirements.size() - mandatoryRequired);
final var missingVersions = modRequirements.stream()
.filter(mv -> (mv.isMandatory() || modVersions.containsKey(mv.getModId())) && this.modVersionNotContained(mv, modVersions))
.filter(mv -> (mv.isMandatory() || modVersions.containsKey(mv.getModId())) && !modVersionContained(mv, modVersions))
.collect(toSet());
final long mandatoryMissing = missingVersions.stream().filter(IModInfo.ModVersion::isMandatory).count();
LOGGER.debug(LOADING, "Found {} mod requirements missing ({} mandatory, {} optional)", missingVersions.size(), mandatoryMissing, missingVersions.size() - mandatoryMissing);
@ -240,9 +242,13 @@ public class ModSorter
);
}
private boolean modVersionNotContained(final IModInfo.ModVersion mv, final Map<String, ArtifactVersion> modVersions)
{
return !(VersionSupportMatrix.testVersionSupportMatrix(mv.getVersionRange(), mv.getModId(), "mod", (modId, range) -> modVersions.containsKey(modId) &&
(range.containsVersion(modVersions.get(modId)) || modVersions.get(modId).toString().equals("0.0NONE"))));
private static boolean modVersionContained(IModInfo.ModVersion mv, Map<String, ArtifactVersion> modVersions) {
var modId = mv.getModId();
var range = mv.getVersionRange();
if (modVersions.containsKey(modId)
&& (range.containsVersion(modVersions.get(modId)) || modVersions.get(modId).toString().equals("0.0NONE")))
return true;
return VersionSupportMatrix.testVersionSupportMatrix(mv.getVersionRange(), mv.getModId(), "mod");
}
}

View file

@ -173,7 +173,7 @@ public class RuntimeDistCleaner implements ILaunchPluginService
return unpacked;
}
private boolean remove(final List<AnnotationNode> anns, final String side)
private static boolean remove(final List<AnnotationNode> anns, final String side)
{
var onlyIns = unpack(anns);
@ -193,7 +193,7 @@ public class RuntimeDistCleaner implements ILaunchPluginService
return false;
}
private boolean hasOnlyInWithModAnnotation(final List<AnnotationNode> anns)
private static boolean hasOnlyInWithModAnnotation(final List<AnnotationNode> anns)
{
if (anns == null)
{

View file

@ -14,16 +14,17 @@ import org.apache.commons.lang3.text.StrSubstitutor;
@SuppressWarnings("deprecation")
public class StringSubstitutor {
private static final Map<String, String> GLOBALS = Map.of(
"mcVersion", FMLLoader.versionInfo().mcVersion(),
"forgeVersion", FMLLoader.versionInfo().forgeVersion()
);
public static String replace(final String in, final ModFile file) {
return new StrSubstitutor(getStringLookup(file)).replace(in);
}
private static StrLookup<String> getStringLookup(final ModFile file) {
var globals = Map.of(
"mcVersion", FMLLoader.versionInfo().mcVersion(),
"forgeVersion", FMLLoader.versionInfo().forgeVersion()
);
return new StrLookup<String>() {
return new StrLookup<>() {
@Override
public String lookup(String key) {
var parts = key.split("\\.");
@ -32,7 +33,7 @@ public class StringSubstitutor {
var pfx = parts[0];
if ("global".equals(pfx))
return globals.get(parts[1]);
return GLOBALS.get(parts[1]);
else if ("file".equals(pfx) && file != null)
return String.valueOf(file.getSubstitutionMap().get().get(parts[1]));

View file

@ -47,12 +47,12 @@ public class UniqueModListBuilder
// Select the newest by artifact version sorting of non-unique files thus identified
uniqueModList = modFilesByFirstId.entrySet().stream()
.map(this::selectNewestModInfo)
.map(UniqueModListBuilder::selectNewestModInfo)
.toList();
// Select the newest by artifact version sorting of non-unique files thus identified
uniqueLibListWithVersion = libFilesWithVersionByModuleName.entrySet().stream()
.map(this::selectNewestModInfo)
.map(UniqueModListBuilder::selectNewestModInfo)
.toList();
// Transform to the full mod id list
@ -75,7 +75,7 @@ public class UniqueModListBuilder
final List<String> dupedModErrors = modIds.values().stream()
.filter(modInfos -> modInfos.size() > 1)
.map(mods -> String.format("\tMod ID: '%s' from mod files: %s",
mods.get(0).getModId(),
mods.getFirst().getModId(),
mods.stream()
.map(modInfo -> modInfo.getOwningFile().getFile().getFileName()).collect(joining(", "))
)).toList();
@ -91,9 +91,8 @@ public class UniqueModListBuilder
final List<String> dupedLibErrors = versionedLibIds.values().stream()
.filter(modFiles -> modFiles.size() > 1)
.map(mods -> String.format("\tLibrary: '%s' from files: %s",
getModId(mods.get(0)),
mods.stream()
.map(modFile -> modFile.getFileName()).collect(joining(", "))
getModId(mods.getFirst()),
mods.stream().map(ModFile::getFileName).collect(joining(", "))
)).toList();
if (!dupedLibErrors.isEmpty()) {
@ -107,25 +106,23 @@ public class UniqueModListBuilder
final Map<String, List<ModFile>> uniqueModFilesByFirstId = uniqueModList.stream()
.collect(groupingBy(UniqueModListBuilder::getModId));
final List<ModFile> loadedList = new ArrayList<>();
loadedList.addAll(uniqueModList);
final List<ModFile> loadedList = new ArrayList<>(uniqueModList);
loadedList.addAll(uniqueLibListWithVersion);
return new UniqueModListData(loadedList, uniqueModFilesByFirstId);
}
private ModFile selectNewestModInfo(Map.Entry<String, List<ModFile>> fullList) {
private static ModFile selectNewestModInfo(Map.Entry<String, List<ModFile>> fullList) {
List<ModFile> modInfoList = fullList.getValue();
if (modInfoList.size() > 1) {
LOGGER.debug("Found {} mods for first modid {}, selecting most recent based on version data", modInfoList.size(), fullList.getKey());
modInfoList.sort(Comparator.comparing(this::getVersion).reversed());
LOGGER.debug("Selected file {} for modid {} with version {}", modInfoList.get(0).getFileName(), fullList.getKey(), this.getVersion(modInfoList.get(0)));
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);
}
private ArtifactVersion getVersion(final ModFile mf)
{
private static ArtifactVersion getVersion(final ModFile mf) {
if (mf.getModFileInfo() == null || mf.getModInfos() == null || mf.getModInfos().isEmpty()) {
return mf.getJarVersion();
}

View file

@ -5,33 +5,53 @@
package net.minecraftforge.fml.loading;
import net.minecraftforge.forgespi.language.MavenVersionAdapter;
import org.apache.maven.artifact.versioning.ArtifactVersion;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
import org.apache.maven.artifact.versioning.VersionRange;
import org.jetbrains.annotations.ApiStatus;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.BiPredicate;
@ApiStatus.Internal // since 1.21.1, will be made non-public in a later MC version
public class VersionSupportMatrix {
private static final HashMap<String, List<ArtifactVersion>> overrideVersions = new HashMap<>();
private static final Map<String, List<ArtifactVersion>> OVERRIDE_VERSIONS;
static {
final ArtifactVersion version = new DefaultArtifactVersion(FMLLoader.versionInfo().mcVersion());
if (MavenVersionAdapter.createFromVersionSpec("[1.21.1]").containsVersion(version)) {
// 1.21.1 is Compatible with 1.21
add("languageloader.javafml", "51");
add("mod.minecraft", "1.21");
add("mod.forge", "51.0.33");
if ("1.21.1".equals(FMLLoader.versionInfo().mcVersion())) {
OVERRIDE_VERSIONS = Map.ofEntries(
// 1.21.1 is compatible with 1.21
entry("languageloader.javafml", "51"),
entry("mod.minecraft", "1.21"),
entry("mod.forge", "51.0.33")
);
} else {
OVERRIDE_VERSIONS = Collections.emptyMap();
}
}
private static void add(String key, String value) {
overrideVersions.computeIfAbsent(key, k -> new ArrayList<>()).add(new DefaultArtifactVersion(value));
}
public static <T> boolean testVersionSupportMatrix(VersionRange declaredRange, String lookupId, String type, BiPredicate<String, VersionRange> standardLookup) {
/**
* @deprecated Use {@link #testVersionSupportMatrix(VersionRange, String, String)} instead, unwrapping your BiPredicate.
*/
@Deprecated(forRemoval = true, since = "1.21.1")
public static boolean testVersionSupportMatrix(VersionRange declaredRange, String lookupId, String type, BiPredicate<String, VersionRange> standardLookup) {
if (standardLookup.test(lookupId, declaredRange)) return true;
List<ArtifactVersion> custom = overrideVersions.get(type + "." + lookupId);
return custom == null ? false : custom.stream().anyMatch(declaredRange::containsVersion);
return testVersionSupportMatrix(declaredRange, lookupId, type);
}
public static boolean testVersionSupportMatrix(VersionRange declaredRange, String lookupId, String type) {
if (OVERRIDE_VERSIONS.isEmpty()) return false;
List<ArtifactVersion> custom = OVERRIDE_VERSIONS.get(type + "." + lookupId);
return custom != null && custom.stream().anyMatch(declaredRange::containsVersion);
}
private static Map.Entry<String, List<ArtifactVersion>> entry(String typeAndLookupId, String declaredRange) {
return Map.entry(typeAndLookupId, List.of(new DefaultArtifactVersion(declaredRange)));
}
private static Map.Entry<String, List<ArtifactVersion>> entry(String typeAndLookupId, List<String> declaredRanges) {
return Map.entry(typeAndLookupId, declaredRanges.stream().map(DefaultArtifactVersion::new).map(it -> (ArtifactVersion) it).toList());
}
}

View file

@ -49,10 +49,7 @@ public class ForgeHighlight {
LOGGER.warn("Failed to invoke initializeTerminal on TCA", e);
}
if (!TerminalConsoleAppender.isAnsiSupported() && Arrays.stream(options).noneMatch(s -> s.equals("disableAnsi=true"))) {
List<String> optionList = new ArrayList<>();
optionList.add(options[0]);
optionList.add("disableAnsi=true");
options = optionList.toArray(new String[0]);
options = new String[] { options[0], "disableAnsi=true" };
}
return HighlightConverter.newInstance(config, options);
}

View file

@ -81,6 +81,11 @@ public abstract class AbstractModProvider implements IModProvider {
var mf = mod.getSecureJar().moduleDataProvider().getManifest().getMainAttributes();
var license = mf.getValue("LICENSE");
var dummy = new IConfigurable() {
@Override
public <T> Optional<T> getConfigElement(String key) {
return Optional.empty();
}
@Override
public <T> Optional<T> getConfigElement(String... key) {
return Optional.empty();
@ -140,10 +145,11 @@ public abstract class AbstractModProvider implements IModProvider {
}
private static final class Holder<T> {
T value;
private T value;
}
private record DefaultModFileInfo(IModFile mod, String license, IConfigurable configurable) implements IModFileInfo, IConfigurable {
@Override public <T> Optional<T> getConfigElement(final String string) { return Optional.empty(); }
@Override public <T> Optional<T> getConfigElement(final String... strings) { return Optional.empty(); }
@Override public List<? extends IConfigurable> getConfigList(final String... strings) { return null; }
@Override public List<IModInfo> getMods() { return Collections.emptyList(); }

View file

@ -29,6 +29,7 @@ public class BackgroundScanHandler
}
private static final Logger LOGGER = LogUtils.getLogger();
private static final boolean DEBUG = LOGGER.isErrorEnabled(LogMarkers.SCAN);
private final ExecutorService modContentScanner;
private final List<ModFile> modFiles;
private ScanStatus status;
@ -55,16 +56,16 @@ public class BackgroundScanHandler
}
status = ScanStatus.RUNNING;
ImmediateWindowHandler.updateProgress("Scanning mod candidates");
final CompletableFuture<ModFileScanData> future = CompletableFuture.supplyAsync(file::compileContent, modContentScanner)
.whenComplete(file::setScanResult)
.whenComplete((r,t)-> this.addCompletedFile(file,r,t));
CompletableFuture<ModFileScanData> future = CompletableFuture.supplyAsync(file::compileContent, modContentScanner)
.whenComplete(file::setScanResult);
if (DEBUG) future = future.whenComplete((r, t) -> addCompletedFile(file, t));
file.setFutureScanResult(future);
}
private void addCompletedFile(final ModFile file, final ModFileScanData modFileScanData, final Throwable throwable) {
private void addCompletedFile(final ModFile file, final Throwable throwable) {
if (throwable != null) {
status = ScanStatus.ERRORED;
LOGGER.error(LogMarkers.SCAN,"An error occurred scanning file {}", file, throwable);
LOGGER.error(LogMarkers.SCAN, "An error occurred scanning file {}", file, throwable);
}
}

View file

@ -18,9 +18,9 @@ import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.jar.Attributes;
@ -28,7 +28,7 @@ import java.util.jar.JarFile;
import java.util.jar.Manifest;
@ApiStatus.Internal
public class ClasspathLocator extends AbstractModProvider implements IModLocator {
public final class ClasspathLocator extends AbstractModProvider implements IModLocator {
private static final Logger LOGGER = LogUtils.getLogger();
private static final Attributes.Name MOD_TYPE = new Attributes.Name("FMLModType");
@ -80,11 +80,11 @@ public class ClasspathLocator extends AbstractModProvider implements IModLocator
return ret;
}
private List<URL> getUrls(ClassLoader cl, String resource) {
private static List<URL> getUrls(ClassLoader cl, String resource) {
try {
var lst = Collections.list(cl.getResources(resource));
if (LOGGER.isDebugEnabled(LogMarkers.SCAN)) {
Collections.sort(lst, (a, b) -> a.toString().compareTo(b.toString()));
lst.sort(Comparator.comparing(URL::toString));
LOGGER.debug(LogMarkers.SCAN, "Scanning Classloader: {} for {}", cl, resource);
for (var url : lst)
LOGGER.debug(LogMarkers.SCAN, "\t{}", url);
@ -104,8 +104,7 @@ public class ClasspathLocator extends AbstractModProvider implements IModLocator
len += 2;
}
str = str.substring(0, str.length() - len);
var path = Paths.get(URI.create(str));
return path;
return Path.of(URI.create(str));
}
private static Path getPathFromResource(ClassLoader cl, String resource) {

View file

@ -15,7 +15,7 @@ import java.nio.file.Path;
import org.jetbrains.annotations.ApiStatus;
@ApiStatus.Internal
public class CoreModFile implements ICoreModFile {
public final class CoreModFile implements ICoreModFile {
private final Path internalPath;
private final ModFile file;
private final String name;

View file

@ -6,7 +6,6 @@
package net.minecraftforge.fml.loading.moddiscovery;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.mojang.logging.LogUtils;
import net.minecraftforge.fml.loading.EarlyLoadingException;
import net.minecraftforge.forgespi.language.IModInfo;
@ -26,6 +25,7 @@ import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@ -51,7 +51,7 @@ public class JarInJarDependencyLocator extends AbstractModProvider implements ID
@Override
public List<IModFile> scanMods(Iterable<IModFile> loadedMods) {
final List<IModFile> sources = Lists.newArrayList();
final List<IModFile> sources = new ArrayList<>();
loadedMods.forEach(sources::add);
var dependenciesToLoad = JarSelector.detectAndSelect(
@ -102,34 +102,34 @@ public class JarInJarDependencyLocator extends AbstractModProvider implements ID
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(this::buildExceptionData)
.map(JarInJarDependencyLocator::buildExceptionData)
.toList();
return new EarlyLoadingException(failedDependencies.size() + " Dependency restrictions were not met.", null, errors);
}
@NotNull
private EarlyLoadingException.ExceptionData buildExceptionData(JarSelector.ResolutionFailureInformation<IModFile> entry) {
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(this::getModWithVersionRangeStream)
.map(this::formatError)
.flatMap(JarInJarDependencyLocator::getModWithVersionRangeStream)
.map(JarInJarDependencyLocator::formatError)
.collect(Collectors.joining(", "))
);
}
@NotNull
private String getErrorTranslationKey(JarSelector.ResolutionFailureInformation<IModFile> entry) {
private static String getErrorTranslationKey(JarSelector.ResolutionFailureInformation<IModFile> entry) {
return entry.failureReason() == JarSelector.FailureReason.VERSION_RESOLUTION_FAILED ?
"fml.dependencyloading.conflictingdependencies" :
"fml.dependencyloading.mismatchedcontaineddependencies";
}
@NotNull
private Stream<ModWithVersionRange> getModWithVersionRangeStream(JarSelector.SourceWithRequestedVersionRange<IModFile> file) {
private static Stream<ModWithVersionRange> getModWithVersionRangeStream(JarSelector.SourceWithRequestedVersionRange<IModFile> file) {
return file.sources()
.stream()
.map(IModFile::getModFileInfo)
@ -152,7 +152,7 @@ public class JarInJarDependencyLocator extends AbstractModProvider implements ID
}
@NotNull
private String formatError(ModWithVersionRange modWithVersionRange){
private static String formatError(ModWithVersionRange modWithVersionRange){
return YELLOW + modWithVersionRange.modInfo().getModId() + RESET + " - " +
RED + modWithVersionRange.versionRange().toString() + RESET + " - " +
GREEN + modWithVersionRange.artifactVersion().toString() + RESET;

View file

@ -18,7 +18,7 @@ import java.util.List;
import org.jetbrains.annotations.ApiStatus;
@ApiStatus.Internal
public class MinecraftLocator extends AbstractModProvider implements IModLocator {
public final class MinecraftLocator extends AbstractModProvider implements IModLocator {
@Override
public List<IModLocator.ModFileOrException> scanMods() {
var minecraft = FMLLoader.getLaunchHandler().getMinecraftPaths();
@ -29,13 +29,13 @@ public class MinecraftLocator extends AbstractModProvider implements IModLocator
jar -> meta,
minecraft.toArray(Path[]::new)
);
var mc = ModFileFactory.FACTORY.build(mcjar, this, this::buildMinecraftTOML);
var mc = ModFileFactory.FACTORY.build(mcjar, this, MinecraftLocator::buildMinecraftTOML);
meta.setModFile(mc);
return List.of(new ModFileOrException(mc, null));
}
private IModFileInfo buildMinecraftTOML(final IModFile iModFile) {
private static IModFileInfo buildMinecraftTOML(final IModFile iModFile) {
// We haven't changed this in years, and I can't be asked right now to special case this one file in the path.
final var conf = Config.inMemory();
conf.set("modLoader", "minecraft");
@ -52,7 +52,7 @@ public class MinecraftLocator extends AbstractModProvider implements IModLocator
conf.set("mods", List.of(mods));
var configWrapper = new NightConfigWrapper(conf);
return new ModFileInfo((ModFile)iModFile, configWrapper, configWrapper::setFile, List.of());
return new ModFileInfo((ModFile) iModFile, configWrapper, configWrapper::setFile);
}
@Override

View file

@ -6,7 +6,6 @@
package net.minecraftforge.fml.loading.moddiscovery;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps;
import com.mojang.logging.LogUtils;
import cpw.mods.modlauncher.Launcher;
import cpw.mods.modlauncher.api.IModuleLayerManager;
@ -28,6 +27,7 @@ import org.jetbrains.annotations.ApiStatus;
import org.slf4j.Logger;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@ -49,17 +49,19 @@ public class ModDiscoverer {
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());
modLocatorList.forEach(l->l.initArguments(arguments));
for (IModLocator iModLocator : modLocatorList) {
iModLocator.initArguments(arguments);
}
dependencyLocatorList = ServiceLoaderUtils.streamServiceLoader(()-> dependencyLocators, sce->LOGGER.error("Failed to load dependency locator list", sce)).collect(Collectors.toList());
dependencyLocatorList.forEach(l->l.initArguments(arguments));
for (IDependencyLocator l : dependencyLocatorList) {
l.initArguments(arguments);
}
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(",")));
}
if (LOGGER.isDebugEnabled(LogMarkers.CORE))
{
LOGGER.debug(LogMarkers.CORE, "Found Dependency Locators : {}", dependencyLocatorList.stream()
.map(dependencyLocator -> "(%s:%s)".formatted(dependencyLocator.name(),
dependencyLocator.getClass().getPackage().getImplementationVersion())).collect(Collectors.joining(",")));
@ -126,7 +128,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 = Maps.newHashMap();
Map<IModFile.Type, List<ModFile>> modFilesMap = new EnumMap<>(IModFile.Type.class);
try {
final UniqueModListBuilder modsUniqueListBuilder = new UniqueModListBuilder(loadedFiles);
final UniqueModListBuilder.UniqueModListData uniqueModsData = modsUniqueListBuilder.buildUniqueList();
@ -134,7 +136,7 @@ public class ModDiscoverer {
//Grab the temporary results.
//This allows loading to continue to a base state, in case dependency loading fails.
modFilesMap = uniqueModsData.modFiles().stream()
.collect(Collectors.groupingBy(IModFile::getType));
.collect(Collectors.groupingBy(IModFile::getType, () -> new EnumMap<>(IModFile.Type.class), Collectors.toList()));
loadedFiles = uniqueModsData.modFiles();
}
catch (EarlyLoadingException exception) {
@ -171,11 +173,11 @@ public class ModDiscoverer {
//We now only need the mod files map, not the list.
modFilesMap = uniqueModsAndDependenciesData.modFiles().stream()
.collect(Collectors.groupingBy(IModFile::getType));
.collect(Collectors.groupingBy(IModFile::getType, () -> new EnumMap<>(IModFile.Type.class), Collectors.toList()));
} catch (EarlyLoadingException exception) {
LOGGER.error(LogMarkers.SCAN, "Failed to build unique mod list after dependency discovery.", exception);
discoveryErrorData.addAll(exception.getAllData());
modFilesMap = loadedFiles.stream().collect(Collectors.groupingBy(IModFile::getType));
modFilesMap = loadedFiles.stream().collect(Collectors.groupingBy(IModFile::getType, () -> new EnumMap<>(IModFile.Type.class), Collectors.toList()));
}
}
else {
@ -190,8 +192,7 @@ public class ModDiscoverer {
return validator;
}
private void handleLocatedFiles(final List<ModFile> loadedFiles, final List<IModFile> locatedFiles)
{
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());

View file

@ -45,7 +45,6 @@ public class ModFile implements IModFile {
private Throwable scanError;
private final SecureJar jar;
private final Type modFileType;
private final Manifest manifest;
private final IModProvider provider;
private IModFileInfo modFileInfo;
private ModFileScanData fileModFileScanData;
@ -65,7 +64,7 @@ public class ModFile implements IModFile {
this.jar = jar;
this.parser = parser;
manifest = this.jar.moduleDataProvider().getManifest();
var manifest = this.jar.moduleDataProvider().getManifest();
modFileType = Type.valueOf(type);
jarVersion = Optional.ofNullable(manifest.getMainAttributes().getValue(Attributes.Name.IMPLEMENTATION_VERSION)).orElse("0.0NONE");
this.modFileInfo = ModFileParser.readModList(this, this.parser);

View file

@ -104,11 +104,11 @@ public class ModFileInfo implements IModFileInfo, IConfigurable {
this.modFile.setFileProperties(this.properties);
final List<? extends IConfigurable> modConfigs = config.getConfigList("mods");
if (modConfigs.isEmpty())
if (modConfigs == null || modConfigs.isEmpty())
throw new InvalidModFileException("Missing mods list", this);
this.mods = modConfigs.stream()
.map(mi-> (IModInfo)new ModInfo(this, mi))
.map(mi -> (IModInfo) ModInfo.of(this, mi))
.toList();
if (LOGGER.isDebugEnabled(LogMarkers.LOADING)) {
@ -158,6 +158,11 @@ public class ModFileInfo implements IModFileInfo, IConfigurable {
return this.showAsDataPack;
}
@Override
public <T> Optional<T> getConfigElement(final String key) {
return this.config.getConfigElement(key);
}
@Override
public <T> Optional<T> getConfigElement(final String... key) {
return this.config.getConfigElement(key);
@ -186,7 +191,7 @@ public class ModFileInfo implements IModFileInfo, IConfigurable {
return Strings.isNullOrEmpty(license);
}
private final char[] HEX = "0123456789ABCDEF".toCharArray();
private static final char[] HEX = "0123456789ABCDEF".toCharArray();
public Optional<String> getCodeSigningFingerprint() {
var signers = this.modFile.getSecureJar().getManifestSigners();
if (signers == null)

View file

@ -27,211 +27,181 @@ import java.util.Optional;
import java.util.regex.Pattern;
@ApiStatus.Internal
public class ModInfo implements IModInfo, IConfigurable {
public record ModInfo(
ModFileInfo getOwningFile,
IConfigurable getConfig,
String getModId,
String getNamespace,
ArtifactVersion getVersion,
String getDisplayName,
String getDescription,
Optional<String> getLogoFile,
boolean getLogoBlur,
Optional<URL> getUpdateURL,
Optional<URL> getModURL,
Holder<List<? extends ModVersion>> dependencies,
Holder<List<ForgeFeature.Bound>> forgeFeatures,
Map<String, Object> getModProperties
) implements IModInfo, IConfigurable {
private static final Logger LOGGER = LogUtils.getLogger();
private static final DefaultArtifactVersion DEFAULT_VERSION = new DefaultArtifactVersion("1");
private static final Pattern VALID_MODID = Pattern.compile("^[a-z][a-z0-9_]{1,63}$");
private static final Pattern VALID_NAMESPACE = Pattern.compile("^[a-z][a-z0-9_.-]{1,63}$");
private static final Pattern VALID_VERSION = Pattern.compile("^\\d+.*");
private final ModFileInfo owningFile;
private final String modId;
private final String namespace;
private final ArtifactVersion version;
private final String displayName;
private final String description;
private final Optional<String> logoFile;
private final boolean logoBlur;
private final Optional<URL> updateJSONURL;
private final List<? extends IModInfo.ModVersion> dependencies;
private final List<ForgeFeature.Bound> features;
private final Map<String,Object> properties;
private final IConfigurable config;
private final Optional<URL> modUrl;
public ModInfo(final ModFileInfo owningFile, final IConfigurable config) {
Optional<ModFileInfo> ownFile = Optional.ofNullable(owningFile);
this.owningFile = owningFile;
this.config = config;
public static ModInfo of(ModFileInfo owningFile, IConfigurable config) {
// These are sourced from the mod specific [[mod]] entry
this.modId = config.<String>getConfigElement("modId")
.orElseThrow(() -> new InvalidModFileException("Missing modId", owningFile));
String modId = config.<String>getConfigElement("modId").orElse(null);
if (modId == null)
throw new InvalidModFileException("Missing modId", owningFile);
// verify we have a valid modid
if (!VALID_MODID.matcher(this.modId).matches()) {
LOGGER.error(LogUtils.FATAL_MARKER, "Invalid modId found in file {} - {} does not match the standard: {}", this.owningFile.getFile().getFilePath(), this.modId, VALID_MODID.pattern());
throw new InvalidModFileException("Invalid modId found : " + this.modId, owningFile);
if (!VALID_MODID.matcher(modId).matches()) {
LOGGER.error(LogUtils.FATAL_MARKER, "Invalid modId found in file {} - {} does not match the standard: {}", owningFile.getFile().getFilePath(), modId, VALID_MODID.pattern());
throw new InvalidModFileException("Invalid modId found : " + modId, owningFile);
}
this.namespace = config.<String>getConfigElement("namespace")
.orElse(this.modId);
String namespace = config.<String>getConfigElement("namespace")
.orElse(modId);
// verify our namespace is valid
if (!VALID_NAMESPACE.matcher(this.namespace).matches()) {
LOGGER.error(LogUtils.FATAL_MARKER, "Invalid override namespace found in file {} - {} does not match the standard: {}", this.owningFile.getFile().getFilePath(), this.namespace, VALID_NAMESPACE.pattern());
throw new InvalidModFileException("Invalid override namespace found : " + this.namespace, owningFile);
if (!VALID_NAMESPACE.matcher(namespace).matches()) {
LOGGER.error(LogUtils.FATAL_MARKER, "Invalid override namespace found in file {} - {} does not match the standard: {}", owningFile.getFile().getFilePath(), namespace, VALID_NAMESPACE.pattern());
throw new InvalidModFileException("Invalid override namespace found : " + namespace, owningFile);
}
this.version = config.<String>getConfigElement("version")
.map(s -> StringSubstitutor.replace(s, ownFile.map(ModFileInfo::getFile).orElse(null)))
ArtifactVersion version = config.<String>getConfigElement("version")
.map(s -> StringSubstitutor.replace(s, owningFile.getFile()))
.map(DefaultArtifactVersion::new)
.orElse(DEFAULT_VERSION);
// verify we have a valid mod version
if (!VALID_VERSION.matcher(this.version.toString()).matches())
throw new InvalidModFileException("Illegal version number specified " + this.version, this.getOwningFile());
if (!VALID_VERSION.matcher(version.toString()).matches())
throw new InvalidModFileException("Illegal version number specified " + version, owningFile);
// The remaining properties all default to sensible values and are not essential
this.displayName = config.<String>getConfigElement("displayName")
.orElse(this.modId);
this.description = config.<String>getConfigElement("description")
String displayName = config.<String>getConfigElement("displayName")
.orElse(modId);
String description = config.<String>getConfigElement("description")
.orElse("MISSING DESCRIPTION")
.replace("\r\n", "\n").stripIndent();
this.logoFile = Optional.ofNullable(config.<String>getConfigElement("logoFile")
.orElseGet(() -> ownFile.flatMap(mf -> mf.<String>getConfigElement("logoFile"))
.orElse(null)));
this.logoBlur = config.<Boolean>getConfigElement("logoBlur")
.orElseGet(() -> ownFile.flatMap(mf -> mf.<Boolean>getConfigElement("logoBlur"))
.orElse(true));
this.updateJSONURL = config.<String>getConfigElement("updateJSONURL")
.map(StringUtils::toURL);
this.modUrl = config.<String>getConfigElement("modUrl")
Optional<String> logoFile = Optional.ofNullable(
config.<String>getConfigElement("logoFile")
.orElseGet(() -> owningFile.<String>getConfigElement("logoFile").orElse(null))
);
Boolean logoBlur = config.<Boolean>getConfigElement("logoBlur").orElse(null);
if (logoBlur == null)
logoBlur = owningFile.<Boolean>getConfigElement("logoBlur").orElse(true);
Optional<URL> updateJSONURL = config.<String>getConfigElement("updateJSONURL")
.map(StringUtils::toURL);
// These are sourced from the file rather than the mod-specific block, but with a modid tag
if (ownFile.isEmpty()) {
this.dependencies = Collections.emptyList();
this.features = Collections.emptyList();
this.properties = Collections.emptyMap();
} else {
var deps = this.owningFile.getConfigList("dependencies", this.modId);
if (deps == null || deps.isEmpty()) {
this.dependencies = Collections.emptyList();
} else {
var tmp = new ArrayList<ModVersion>();
for (var dep : deps)
tmp.add(new ModVersion(this, dep));
this.dependencies = Collections.unmodifiableList(tmp);
}
Optional<URL> modUrl = config.<String>getConfigElement("modUrl")
.map(StringUtils::toURL);
var feats = this.owningFile.<Map<String, Object>>getConfigElement("features", this.modId).orElse(null);
if (feats == null) {
this.features = Collections.emptyList();
} else {
var tmp = new ArrayList<ForgeFeature.Bound>();
for (var entry : feats.entrySet()) {
if (!(entry.getValue() instanceof String val))
throw new InvalidModFileException("Invalid feature bound {" + entry.getValue() + "} for key {" + entry.getKey() + "} only strings are accepted", this.owningFile);
tmp.add(new ForgeFeature.Bound(entry.getKey(), val, this));
}
this.features = Collections.unmodifiableList(tmp);
}
// dependencies and features are done after the constructor as they need to reference the ModInfo we are creating
List<? extends ModVersion> dependencies = Collections.emptyList();
List<ForgeFeature.Bound> forgeFeatures = Collections.emptyList();
var props = this.owningFile.<Map<String, Object>>getConfigElement("modproperties", this.modId).orElse(null);
if (props == null)
this.properties = Collections.emptyMap();
else
this.properties = Collections.unmodifiableMap(props);
Map<String, Object> modProperties = owningFile.<Map<String, Object>>getConfigElement("modproperties", modId)
.map(Collections::unmodifiableMap)
.orElse(Collections.emptyMap());
return new ModInfo(
owningFile, config,
modId, namespace, version,
displayName, description, logoFile, logoBlur, updateJSONURL, modUrl,
new Holder<>(dependencies), new Holder<>(forgeFeatures), modProperties
).setupDependencies().setupForgeFeatures();
}
private ModInfo setupDependencies() {
var deps = getOwningFile.getConfigList("dependencies", getModId);
if (deps == null || deps.isEmpty()) {
dependencies.value = Collections.emptyList();
return this;
}
var tmp = new ModVersion[deps.size()];
for (int i = 0; i < deps.size(); i++) {
tmp[i] = ModVersion.of(this, deps.get(i));
}
dependencies.value = List.of(tmp);
return this;
}
@Override
public ModFileInfo getOwningFile() {
return owningFile;
}
private ModInfo setupForgeFeatures() {
var feats = getOwningFile.<Map<String, Object>>getConfigElement("features", getModId).orElse(null);
if (feats == null) {
forgeFeatures.value = Collections.emptyList();
return this;
}
@Override
public String getModId() {
return modId;
}
@Override
public String getDisplayName() {
return this.displayName;
}
@Override
public String getDescription() {
return this.description;
}
@Override
public ArtifactVersion getVersion() {
return version;
}
@Override
public List<? extends IModInfo.ModVersion> getDependencies() {
return this.dependencies;
}
@Override
public String getNamespace() {
return this.namespace;
}
@Override
public Map<String, Object> getModProperties() {
return this.properties;
}
@Override
public Optional<URL> getUpdateURL() {
return this.updateJSONURL;
}
@Override
public Optional<String> getLogoFile() {
return this.logoFile;
}
@Override
public boolean getLogoBlur() {
return this.logoBlur;
}
@Override
public IConfigurable getConfig() {
var tmp = new ArrayList<ForgeFeature.Bound>();
for (var entry : feats.entrySet()) {
if (!(entry.getValue() instanceof String val))
throw new InvalidModFileException("Invalid feature bound {" + entry.getValue() + "} for key {" + entry.getKey() + "} only strings are accepted", getOwningFile);
tmp.add(new ForgeFeature.Bound(entry.getKey(), val, this));
}
forgeFeatures.value = List.copyOf(tmp);
return this;
}
@Override
public List<? extends ForgeFeature.Bound> getForgeFeatures() {
return this.features;
public <T> Optional<T> getConfigElement(String key) {
return getConfig.getConfigElement(key);
}
@Override
public <T> Optional<T> getConfigElement(final String... key) {
return this.config.getConfigElement(key);
public <T> Optional<T> getConfigElement(String... key) {
return getConfig.getConfigElement(key);
}
@Override
public List<? extends IConfigurable> getConfigList(final String... key) {
public List<? extends IConfigurable> getConfigList(String... key) {
return null;
}
@Override
public Optional<URL> getModURL() {
return modUrl;
public List<? extends IModInfo.ModVersion> getDependencies() {
return dependencies.value;
}
class ModVersion implements net.minecraftforge.forgespi.language.IModInfo.ModVersion {
private IModInfo owner;
private final String modId;
private final VersionRange versionRange;
private final boolean mandatory;
private final Ordering ordering;
private final DependencySide side;
private final Optional<URL> referralUrl;
@Override
public List<? extends ForgeFeature.Bound> getForgeFeatures() {
return forgeFeatures.value;
}
public ModVersion(final IModInfo owner, final IConfigurable config) {
this.owner = owner;
this.modId = config.<String>getConfigElement("modId")
.orElseThrow(()->new InvalidModFileException("Missing required field modid in dependency", getOwningFile()));
private static final class Holder<T> {
private T value;
if (this.modId.equals("forge")) {
public Holder(T value) {
this.value = value;
}
}
private record ModVersion(
Holder<IModInfo> owner,
String getModId,
VersionRange getVersionRange,
boolean isMandatory,
Ordering getOrdering,
DependencySide getSide,
Optional<URL> getReferralURL
) implements IModInfo.ModVersion {
public static ModVersion of(IModInfo owner, IConfigurable config) {
var modId = config.<String>getConfigElement("modId").orElse(null);
if (modId == null)
throw new InvalidModFileException("Missing required field modid in dependency", owner.getOwningFile());
if (modId.equals("forge")) {
var fileProps = owner.getOwningFile().getFileProperties();
// Checking containsKey to avoid a possible exception if the property is not present (due to Collections.emptyMap())
if (!fileProps.isEmpty() && fileProps.containsKey(ModFileInfo.NOT_A_FORGE_MOD_PROP)) {
@ -241,65 +211,41 @@ public class ModInfo implements IModInfo, IConfigurable {
}
}
var mandatory = config.<Boolean>getConfigElement("mandatory");
if (mandatory.isPresent())
this.mandatory = mandatory.get();
boolean mandatory;
var mandatoryValue = config.<Boolean>getConfigElement("mandatory");
if (mandatoryValue.isPresent())
mandatory = mandatoryValue.get();
else if (owner.getOwningFile().getFileProperties().containsKey(ModFileInfo.NOT_A_FORGE_MOD_PROP))
this.mandatory = true;
mandatory = true;
else
throw new InvalidModFileException("Missing required field mandatory in dependency", getOwningFile());
throw new InvalidModFileException("Missing required field mandatory in dependency", owner.getOwningFile());
this.versionRange = config.<String>getConfigElement("versionRange")
var versionRange = config.<String>getConfigElement("versionRange")
.map(MavenVersionAdapter::createFromVersionSpec)
.orElse(UNBOUNDED);
this.ordering = config.<String>getConfigElement("ordering")
var ordering = config.<String>getConfigElement("ordering")
.map(Ordering::valueOf)
.orElse(Ordering.NONE);
this.side = config.<String>getConfigElement("side")
var side = config.<String>getConfigElement("side")
.map(DependencySide::valueOf)
.orElse(DependencySide.BOTH);
this.referralUrl = config.<String>getConfigElement("referralUrl")
var referralUrl = config.<String>getConfigElement("referralUrl")
.map(StringUtils::toURL);
}
@Override
public String getModId() {
return modId;
}
@Override
public VersionRange getVersionRange() {
return versionRange;
}
@Override
public boolean isMandatory() {
return mandatory;
}
@Override
public Ordering getOrdering() {
return ordering;
}
@Override
public DependencySide getSide() {
return side;
}
@Override
public void setOwner(final IModInfo owner) {
this.owner = owner;
return new ModVersion(
new Holder<>(owner),
modId, versionRange, mandatory, ordering, side, referralUrl
);
}
@Override
public IModInfo getOwner() {
return owner;
return owner.value;
}
@Override
public Optional<URL> getReferralURL() {
return referralUrl;
public void setOwner(IModInfo newOwner) {
owner.value = newOwner;
}
}
}

View file

@ -64,7 +64,7 @@ public class ModValidator {
}
@NotNull
private List<ModFile> validateFiles(final List<ModFile> mods) {
private static List<ModFile> validateFiles(final List<ModFile> mods) {
final List<ModFile> brokenFiles = new ArrayList<>();
for (Iterator<ModFile> iterator = mods.iterator(); iterator.hasNext();) {
ModFile modFile = iterator.next();
@ -106,8 +106,8 @@ public class ModValidator {
private List<EarlyLoadingException.ExceptionData> validateLanguages() {
List<EarlyLoadingException.ExceptionData> errorData = new ArrayList<>();
for (Iterator<ModFile> iterator = this.candidateMods.iterator(); iterator.hasNext(); ) {
final ModFile modFile = iterator.next();
for (Iterator<ModFile> iterator = this.candidateMods.iterator(); iterator.hasNext();) {
var modFile = iterator.next();
try {
modFile.identifyLanguage();
} catch (EarlyLoadingException e) {

View file

@ -18,11 +18,12 @@ import java.util.Optional;
import java.util.stream.Collectors;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import static java.util.Arrays.asList;
@ApiStatus.Internal
class NightConfigWrapper implements IConfigurable {
final class NightConfigWrapper implements IConfigurable {
private final UnmodifiableConfig config;
private IModFileInfo file;
@ -30,39 +31,51 @@ class NightConfigWrapper implements IConfigurable {
this.config = config;
}
NightConfigWrapper setFile(IModFileInfo file) {
private NightConfigWrapper(UnmodifiableConfig config, IModFileInfo file) {
this.config = config;
this.file = file;
}
void setFile(IModFileInfo file) {
this.file = file;
return this;
}
@Override
@SuppressWarnings("unchecked")
public <T> Optional<T> getConfigElement(String key) {
var path = List.of(key);
return Optional.ofNullable(validate(this.config.get(path), path));
}
@Override
public <T> Optional<T> getConfigElement(final String... key) {
var path = asList(key);
return this.config.getOptional(path).map(value -> {
if (value instanceof UnmodifiableConfig cfg) {
// New Night config doesn't implement valueMap(), so do a copy.
var builder = ImmutableMap.builder();
for (var e: cfg.entrySet())
builder.put(e.getKey(), e.getValue());
return (T)builder.build();
} else if (value instanceof ArrayList<?> al && al.size() > 0 && al.get(0) instanceof UnmodifiableConfig) {
throw new InvalidModFileException("The configuration path " + path + " is invalid. I wasn't expecting a multi-object list - remove one of the [[ ]]", file);
}
return (T) value;
});
return Optional.ofNullable(validate(this.config.get(path), path));
}
@SuppressWarnings("unchecked")
private <T> T validate(@Nullable T value, List<String> path) {
if (value instanceof UnmodifiableConfig cfg) {
// New Night config doesn't implement valueMap(), so do a copy.
var entries = cfg.entrySet();
var builder = ImmutableMap.builderWithExpectedSize(entries.size());
for (var e : entries)
builder.put(e.getKey(), e.getValue());
return (T) builder.build();
} else if (value instanceof ArrayList<?> al && !al.isEmpty() && al.getFirst() instanceof UnmodifiableConfig) {
throw new InvalidModFileException("The configuration path " + path + " is invalid. I wasn't expecting a multi-object list - remove one of the [[ ]]", file);
}
return value;
}
@Override
public List<? extends IConfigurable> getConfigList(final String... key) {
final List<String> path = asList(key);
if (this.config.contains(path) && !(this.config.get(path) instanceof Collection)) {
throw new InvalidModFileException("The configuration path "+path+" is invalid. Expecting a collection!", file);
throw new InvalidModFileException("The configuration path " + path + " is invalid. Expecting a collection!", file);
}
final Collection<UnmodifiableConfig> nestedConfigs = this.config.getOrElse(path, ArrayList::new);
return nestedConfigs.stream()
.map(NightConfigWrapper::new)
.map(cw->cw.setFile(file))
.map(conf -> new NightConfigWrapper(conf, file))
.collect(Collectors.toList());
}
}

View file

@ -21,37 +21,36 @@ import java.nio.file.Path;
import java.util.List;
@ApiStatus.Internal
class Scanner {
record Scanner(ModFile fileToScan, ModFileScanData result) {
private static final Logger LOGGER = LogUtils.getLogger();
private final ModFile fileToScan;
private static final boolean DEBUG = LOGGER.isDebugEnabled(LogMarkers.SCAN);
public Scanner(final ModFile fileToScan) {
this.fileToScan = fileToScan;
public Scanner(ModFile fileToScan) {
this(fileToScan, new ModFileScanData());
}
public ModFileScanData scan() {
ModFileScanData result = new ModFileScanData();
result.addModFileInfo(fileToScan.getModFileInfo());
fileToScan.scanFile(p -> fileVisitor(p, result));
fileToScan.scanFile(this::fileVisitor);
final List<IModLanguageProvider> loaders = fileToScan.getLoaders();
if (loaders != null) {
loaders.forEach(loader -> {
LOGGER.debug(LogMarkers.SCAN, "Scanning {} with language loader {}", fileToScan.getFilePath(), loader.name());
for (IModLanguageProvider loader : loaders) {
if (DEBUG) LOGGER.debug("Scanning {} with language loader {}", fileToScan.getFilePath(), loader.name());
loader.getFileVisitor().accept(result);
});
}
}
return result;
}
private void fileVisitor(final Path path, final ModFileScanData result) {
LOGGER.debug(LogMarkers.SCAN,"Scanning {} path {}", fileToScan, path);
try (InputStream in = Files.newInputStream(path)){
private void fileVisitor(final Path path) {
try (InputStream in = Files.newInputStream(path)) {
ModClassVisitor mcv = new ModClassVisitor();
ClassReader cr = new ClassReader(in);
cr.accept(mcv, 0);
mcv.buildData(result.getClasses(), result.getAnnotations());
} catch (IOException | IllegalArgumentException e) {
// mark path bad
if (DEBUG) LOGGER.warn("Failed scanning {} path {}", fileToScan, path);
}
}
}

View file

@ -24,8 +24,8 @@ import java.util.function.Supplier;
@ApiStatus.Internal
class ArgumentList {
private static final Logger LOGGER = LogUtils.getLogger();
private List<Supplier<String[]>> entries = new ArrayList<>();
private Map<String, EntryValue> values = new HashMap<>();
private final List<Supplier<String[]>> entries = new ArrayList<>();
private final Map<String, EntryValue> values = new HashMap<>();
public static ArgumentList from(String... args) {
ArgumentList ret = new ArgumentList();
@ -77,8 +77,8 @@ class ArgumentList {
public String[] getArguments() {
return entries.stream()
.flatMap(e -> Arrays.asList(e.get()).stream())
.toArray(size -> new String[size]);
.flatMap(e -> Arrays.stream(e.get()))
.toArray(String[]::new);
}
public boolean hasValue(String key) {
@ -122,7 +122,7 @@ class ArgumentList {
return ent.getValue();
}
private class EntryValue implements Supplier<String[]> {
private static final class EntryValue implements Supplier<String[]> {
private final String prefix;
private final String key;
private final boolean split;

View file

@ -40,7 +40,7 @@ abstract class CommonDevLaunchHandler extends CommonLaunchHandler {
String username = args.get("username");
if (username != null) { // Replace '#' placeholders with random numbers
Matcher m = Pattern.compile("#+").matcher(username);
StringBuffer replaced = new StringBuffer();
StringBuilder replaced = new StringBuilder();
while (m.find()) {
m.appendReplacement(replaced, getRandomNumbers(m.group().length()));
}

View file

@ -7,7 +7,6 @@ package net.minecraftforge.fml.loading.targets;
import com.mojang.logging.LogUtils;
import cpw.mods.modlauncher.api.ILaunchHandlerService;
import cpw.mods.modlauncher.api.ITransformingClassLoaderBuilder;
import cpw.mods.modlauncher.api.ServiceRunner;
import net.minecraftforge.fml.loading.FMLLoader;
import net.minecraftforge.api.distmarker.Dist;
@ -19,7 +18,6 @@ import org.slf4j.Logger;
import java.io.IOException;
import java.net.URI;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
/**
@ -47,10 +45,6 @@ public abstract class CommonLaunchHandler implements ILaunchHandlerService {
public abstract List<Path> getMinecraftPaths();
@Override
public void configureTransformationClassLoader(final ITransformingClassLoaderBuilder builder) {
}
protected String[] preLaunch(String[] arguments, ModuleLayer layer) {
URI uri;
try (var reader = layer.configuration().findModule("net.minecraftforge.fmlloader").orElseThrow().reference().open()) {
@ -107,7 +101,7 @@ public abstract class CommonLaunchHandler implements ILaunchHandlerService {
len += 2;
}
str = str.substring(0, str.length() - len);
var path = Paths.get(URI.create(str));
var path = Path.of(URI.create(str));
return path;
}
}

View file

@ -9,7 +9,7 @@ import java.util.List;
import org.jetbrains.annotations.ApiStatus;
@ApiStatus.Internal
abstract class ForgeDevLaunchHandler extends CommonDevLaunchHandler {
sealed abstract class ForgeDevLaunchHandler extends CommonDevLaunchHandler {
private ForgeDevLaunchHandler(LaunchType type) {
super(type, "forge_dev_");
}
@ -33,25 +33,25 @@ abstract class ForgeDevLaunchHandler extends CommonDevLaunchHandler {
return List.of(filtered);
}
public static class Client extends ForgeDevLaunchHandler {
public static final class Client extends ForgeDevLaunchHandler {
public Client() {
super(CLIENT);
}
}
public static class Data extends ForgeDevLaunchHandler {
public static final class Data extends ForgeDevLaunchHandler {
public Data() {
super(DATA);
}
}
public static class Server extends ForgeDevLaunchHandler {
public static final class Server extends ForgeDevLaunchHandler {
public Server() {
super(SERVER);
}
}
public static class ServerGameTest extends ForgeDevLaunchHandler {
public static final class ServerGameTest extends ForgeDevLaunchHandler {
public ServerGameTest() {
super(SERVER_GAMETEST);
}

View file

@ -35,7 +35,7 @@ import net.minecraftforge.fml.loading.moddiscovery.AbstractModProvider;
import net.minecraftforge.forgespi.locating.IModLocator;
@ApiStatus.Internal
public class ForgeDevLocator extends AbstractModProvider implements IModLocator {
public final class ForgeDevLocator extends AbstractModProvider implements IModLocator {
private static final String PACK_META = "pack.mcmeta";
@Override
@ -60,7 +60,7 @@ public class ForgeDevLocator extends AbstractModProvider implements IModLocator
return ret;
}
private List<Path> getMods() {
private static List<Path> getMods() {
// Forge is an exploded directory as well
var minecraft = ForgeDevLaunchHandler.getPathFromResource("net/minecraft/client/Minecraft.class");
var forge = ForgeDevLaunchHandler.getPathFromResource("net/minecraftforge/common/MinecraftForge.class");
@ -84,7 +84,7 @@ public class ForgeDevLocator extends AbstractModProvider implements IModLocator
return ret;
}
private List<Path> explodeTestMods(Path path) {
private static List<Path> explodeTestMods(Path path) {
var mod = new ArrayList<Path>();
var memory = Jimfs.newFileSystem();
@ -137,7 +137,7 @@ public class ForgeDevLocator extends AbstractModProvider implements IModLocator
// Find all the @Mods so we can generate tomls, and so we can pick packages. Right now it doesn't allow mods in parent directories,
// I could make it merge all the way up, but I think this would be fine.
private Map<String, Set<String>> findTestModPackages(Path path) {
private static Map<String, Set<String>> findTestModPackages(Path path) {
var mods = new HashMap<String, Set<String>>();
try (var files = Files.walk(path)) {
var classes = files
@ -190,7 +190,7 @@ public class ForgeDevLocator extends AbstractModProvider implements IModLocator
}
// Builds or update the mods.toml file for all @Mods in this package
private void buildModsToml(Set<Path> resources, Set<String> modids, Path root) {
private static void buildModsToml(Set<Path> resources, Set<String> modids, Path root) {
var toml = resources.stream()
.map(p -> p.resolve(MODS_TOML))
.filter(Files::exists)
@ -226,7 +226,7 @@ public class ForgeDevLocator extends AbstractModProvider implements IModLocator
}
for (var modid : modids) {
if (!modlist.stream().anyMatch(c -> modid.equals(c.get("modId")))) {
if (modlist.stream().noneMatch(c -> modid.equals(c.get("modId")))) {
modified = true;
var tmp = Config.inMemory();
tmp.set("modId", modid);
@ -248,7 +248,7 @@ public class ForgeDevLocator extends AbstractModProvider implements IModLocator
}
// This is optional, it just hides a warning screen when starting up. I should probably remove this once I restructure how data gen for test mods work and make it generate there.
private void buildPackMeta(Set<Path> paths, Path root) {
private static void buildPackMeta(Set<Path> paths, Path root) {
var existing = paths.stream()
.map(p -> p.resolve(PACK_META))
.filter(Files::exists)

View file

@ -10,7 +10,7 @@ import java.util.List;
import org.jetbrains.annotations.ApiStatus;
@ApiStatus.Internal
abstract class ForgeProdLaunchHandler extends CommonLaunchHandler {
sealed abstract class ForgeProdLaunchHandler extends CommonLaunchHandler {
protected ForgeProdLaunchHandler(LaunchType type) {
super(type, "forge_");
}
@ -18,7 +18,7 @@ abstract class ForgeProdLaunchHandler extends CommonLaunchHandler {
@Override public String getNaming() { return "mcp"; }
@Override public boolean isProduction() { return true; }
public static class Client extends ForgeProdLaunchHandler {
public static final class Client extends ForgeProdLaunchHandler {
public Client() {
super(CLIENT);
}
@ -29,7 +29,7 @@ abstract class ForgeProdLaunchHandler extends CommonLaunchHandler {
}
}
public static class Server extends ForgeProdLaunchHandler {
public static final class Server extends ForgeProdLaunchHandler {
public Server() {
super(SERVER);
}

View file

@ -14,7 +14,7 @@ import org.jetbrains.annotations.ApiStatus;
import cpw.mods.jarhandling.SecureJar;
@ApiStatus.Internal
abstract class ForgeUserdevLaunchHandler extends CommonDevLaunchHandler {
sealed abstract class ForgeUserdevLaunchHandler extends CommonDevLaunchHandler {
private ForgeUserdevLaunchHandler(LaunchType type) {
super(type, "forge_userdev_");
}
@ -33,25 +33,25 @@ abstract class ForgeUserdevLaunchHandler extends CommonDevLaunchHandler {
return List.of(minecraft);
}
public static class Client extends ForgeUserdevLaunchHandler {
public static final class Client extends ForgeUserdevLaunchHandler {
public Client() {
super(CLIENT);
}
}
public static class Data extends ForgeUserdevLaunchHandler {
public static final class Data extends ForgeUserdevLaunchHandler {
public Data() {
super(DATA);
}
}
public static class Server extends ForgeUserdevLaunchHandler {
public static final class Server extends ForgeUserdevLaunchHandler {
public Server() {
super(SERVER);
}
}
public static class ServerGameTest extends ForgeUserdevLaunchHandler {
public static final class ServerGameTest extends ForgeUserdevLaunchHandler {
public ServerGameTest() {
super(SERVER_GAMETEST);
}

View file

@ -12,7 +12,7 @@ import net.minecraftforge.fml.loading.moddiscovery.AbstractModProvider;
import net.minecraftforge.forgespi.locating.IModLocator;
@ApiStatus.Internal
public class ForgeUserdevLocator extends AbstractModProvider implements IModLocator {
public final class ForgeUserdevLocator extends AbstractModProvider implements IModLocator {
@Override
public String name() {
return "forge_userdev_locator";

View file

@ -25,7 +25,7 @@ import java.util.Set;
*/
public class StronglyConnectedComponentDetector<T> {
private final Graph<T> graph;
private Map<T, Integer> ids;
private final Map<T, Integer> ids = new HashMap<>();
private T[] elements;
private int[] dfn;
private int[] low;
@ -49,7 +49,6 @@ public class StronglyConnectedComponentDetector<T> {
private void calculate() {
components = new HashSet<>();
int t = 0;
ids = new HashMap<>();
Set<T> nodes = graph.nodes();
elements = (T[]) new Object[nodes.size()];
for (T node : nodes) {

View file

@ -31,21 +31,8 @@ public class FMLJavaModLanguageProvider implements IModLanguageProvider
{
private static final Logger LOGGER = LogManager.getLogger();
private static class FMLModTarget implements IModLanguageProvider.IModLanguageLoader {
private record FMLModTarget(String className, String modId) implements IModLanguageProvider.IModLanguageLoader {
private static final Logger LOGGER = FMLJavaModLanguageProvider.LOGGER;
private final String className;
private final String modId;
private FMLModTarget(String className, String modId)
{
this.className = className;
this.modId = modId;
}
public String getModId()
{
return modId;
}
@SuppressWarnings("unchecked")
@Override
@ -94,7 +81,7 @@ public class FMLJavaModLanguageProvider implements IModLanguageProvider
.filter(ad -> ad.annotationType().equals(MODANNOTATION))
.peek(ad -> LOGGER.debug(SCAN, "Found @Mod class {} with id {}", ad.clazz().getClassName(), ad.annotationData().get("value")))
.map(ad -> new FMLModTarget(ad.clazz().getClassName(), (String)ad.annotationData().get("value")))
.collect(Collectors.toMap(FMLModTarget::getModId, Function.identity(), (a,b)->a));
.collect(Collectors.toMap(FMLModTarget::modId, Function.identity(), (a,b)->a));
scanResult.addLanguageLoader(modTargetMap);
};
}

View file

@ -38,7 +38,7 @@ public class FMLModContainer extends ModContainer {
LOGGER.debug(LOADING,"Creating FMLModContainer instance for {}", className);
this.scanResults = modFileScanResults;
activityMap.put(ModLoadingStage.CONSTRUCT, this::constructMod);
this.eventBus = BusBuilder.builder().setExceptionHandler(this::onEventFailed).setTrackPhases(false).markerType(IModBusEvent.class).useModLauncher().build();
this.eventBus = BusBuilder.builder().setExceptionHandler(FMLModContainer::onEventFailed).setTrackPhases(false).markerType(IModBusEvent.class).useModLauncher().build();
this.configHandler = Optional.of(ce->this.eventBus.post(ce.self()));
final FMLJavaModLoadingContext contextExtension = new FMLJavaModLoadingContext(this);
this.contextExtension = () -> contextExtension;
@ -56,7 +56,7 @@ public class FMLModContainer extends ModContainer {
}
}
private void onEventFailed(IEventBus iEventBus, Event event, IEventListener[] iEventListeners, int i, Throwable throwable) {
private static void onEventFailed(IEventBus iEventBus, Event event, IEventListener[] iEventListeners, int i, Throwable throwable) {
LOGGER.error(new EventBusErrorMessage(event, i, iEventListeners, throwable));
}

View file

@ -21,14 +21,12 @@ import static net.minecraftforge.fml.loading.LogMarkers.LOADING;
public class LowCodeModContainer extends ModContainer
{
private static final Logger LOGGER = LogUtils.getLogger();
private final ModFileScanData scanResults;
private Object modInstance;
private final Object modInstance;
public LowCodeModContainer(IModInfo info, ModFileScanData modFileScanResults, ModuleLayer gameLayer)
{
super(info);
LOGGER.debug(LOADING, "Creating LowCodeModContainer for {}", info.getModId());
this.scanResults = modFileScanResults;
this.modInstance = new Object();
this.contextExtension = () -> null;
this.extensionPoints.remove(IExtensionPoint.DisplayTest.class);
@ -45,9 +43,4 @@ public class LowCodeModContainer extends ModContainer
{
return modInstance;
}
@Override
protected <T extends Event & IModBusEvent> void acceptEvent(final T e)
{
}
}

View file

@ -5,7 +5,6 @@
package net.minecraftforge.fml.mclanguageprovider;
import net.minecraftforge.fml.ModContainer;
import net.minecraftforge.forgespi.language.ILifecycleEvent;
import net.minecraftforge.forgespi.language.IModInfo;
import net.minecraftforge.forgespi.language.IModLanguageProvider;
@ -15,14 +14,11 @@ import org.apache.logging.log4j.Logger;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Supplier;
import static net.minecraftforge.fml.Logging.LOADING;
import net.minecraftforge.forgespi.language.IModLanguageProvider.IModLanguageLoader;
public class MinecraftModLanguageProvider implements IModLanguageProvider {
private static final Logger LOGGER = LogManager.getLogger();
@Override

View file

@ -5,6 +5,6 @@
@DontObfuscate
public static String getClientModName() {
- return "vanilla";
+ return net.minecraftforge.internal.BrandingControl.getClientBranding();
+ return net.minecraftforge.internal.BrandingControl.getBranding();
}
}

View file

@ -46,11 +46,11 @@
}
- p_282860_.drawString(this.font, s, 2, this.height - 10, 16777215 | i);
+ net.minecraftforge.internal.BrandingControl.forEachLine(true, true, (brdline, brd) ->
+ net.minecraftforge.internal.BrandingControl.forEachLine(true, true, (brd, brdline) ->
+ p_282860_.drawString(this.font, brd, 2, this.height - ( 10 + brdline * (this.font.lineHeight + 1)), 16777215 | i)
+ );
+
+ net.minecraftforge.internal.BrandingControl.forEachAboveCopyrightLine((brdline, brd) ->
+ net.minecraftforge.internal.BrandingControl.forEachAboveCopyrightLine((brd, brdline) ->
+ p_282860_.drawString(this.font, brd, this.width - font.width(brd), this.height - (10 + (brdline + 1) * ( this.font.lineHeight + 1)), 16777215 | i)
+ );
+

View file

@ -162,7 +162,7 @@
@DontObfuscate
public String getServerModName() {
- return "vanilla";
+ return net.minecraftforge.internal.BrandingControl.getServerBranding();
+ return net.minecraftforge.internal.BrandingControl.getBranding();
}
public SystemReport fillSystemReport(SystemReport p_177936_) {

View file

@ -23,9 +23,9 @@ plugins {
dependencyResolutionManagement {
versionCatalogs {
libs {
library('forgespi', 'net.minecraftforge:forgespi:7.1.4') // Needs modlauncher
library('forgespi', 'net.minecraftforge:forgespi:7.1.5') // Needs modlauncher
library('modlauncher', 'net.minecraftforge:modlauncher:10.2.1') // Needs securemodules
library('securemodules', 'net.minecraftforge:securemodules:2.2.19') // Needs unsafe
library('securemodules', 'net.minecraftforge:securemodules:2.2.20') // Needs unsafe
library('unsafe', 'net.minecraftforge:unsafe:0.9.2')
library('accesstransformers', 'net.minecraftforge:accesstransformers:8.2.0')
library('coremods', 'net.minecraftforge:coremods:5.1.6')

View file

@ -141,7 +141,6 @@ import net.minecraftforge.common.ForgeConfig;
import net.minecraftforge.common.ForgeI18n;
import net.minecraftforge.common.ForgeMod;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.eventbus.api.Event;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.IExtensionPoint;
import net.minecraftforge.fml.ModList;
@ -202,7 +201,7 @@ public class ForgeHooksClient {
}
public static void clearGuiLayers(Minecraft minecraft) {
while(guiLayers.size() > 0)
while (!guiLayers.isEmpty())
popGuiLayerInternal(minecraft);
}
@ -221,7 +220,7 @@ public class ForgeHooksClient {
}
public static void popGuiLayer(Minecraft minecraft) {
if (guiLayers.size() == 0) {
if (guiLayers.isEmpty()) {
minecraft.setScreen(null);
return;
}
@ -310,7 +309,7 @@ public class ForgeHooksClient {
if (idx == -1)
return base + complex;
String name = complex.substring(idx + 1, complex.length());
String name = complex.substring(idx + 1);
if (idx > 1) {
String domain = complex.substring(0, idx);
return domain + ':' + base + name;
@ -320,9 +319,7 @@ public class ForgeHooksClient {
}
public static float getFieldOfViewModifier(Player entity, float fovModifier) {
ComputeFovModifierEvent fovModifierEvent = new ComputeFovModifierEvent(entity, fovModifier);
MinecraftForge.EVENT_BUS.post(fovModifierEvent);
return fovModifierEvent.getNewFovModifier();
return MinecraftForge.EVENT_BUS.fire(new ComputeFovModifierEvent(entity, fovModifier)).getNewFovModifier();
}
/**
@ -333,41 +330,52 @@ public class ForgeHooksClient {
//RenderingRegistry.registerBlockHandler(RenderBlockFluid.instance);
}
private static VersionChecker.Status getForgeVersionStatus() {
final class LazyInit {
private static final VersionChecker.Status INSTANCE = ForgeVersion.getStatus();
static {
forgeStatusLine = switch (INSTANCE) {
// case FAILED -> " Version check failed";
// case UP_TO_DATE -> "Forge up to date";
// case AHEAD -> "Using non-recommended Forge build, issues may arise.";
case OUTDATED, BETA_OUTDATED -> I18n.get("forge.update.newversion", ForgeVersion.getTarget());
default -> null;
};
}
private LazyInit() {}
}
return LazyInit.INSTANCE;
}
public static void renderMainMenu(TitleScreen gui, GuiGraphics graphics, Font font, int width, int height, int alpha) {
VersionChecker.Status status = ForgeVersion.getStatus();
VersionChecker.Status status = getForgeVersionStatus();
if (status == VersionChecker.Status.BETA || status == VersionChecker.Status.BETA_OUTDATED) {
// Render a warning at the top of the screen
Component line = Component.translatable("forge.update.beta.1", ChatFormatting.RED, ChatFormatting.RESET).withStyle(ChatFormatting.RED);
graphics.drawCenteredString(font, line, width / 2, 4 + (0 * (font.lineHeight + 1)), 0xFFFFFF | alpha);
graphics.drawCenteredString(font, line, width / 2, 4, 0xFFFFFF | alpha);
line = Component.translatable("forge.update.beta.2");
graphics.drawCenteredString(font, line, width / 2, 4 + (1 * (font.lineHeight + 1)), 0xFFFFFF | alpha);
graphics.drawCenteredString(font, line, width / 2, 4 + (font.lineHeight + 1), 0xFFFFFF | alpha);
}
forgeStatusLine = switch(status) {
// case FAILED -> " Version check failed";
// case UP_TO_DATE -> "Forge up to date";
// case AHEAD -> "Using non-recommended Forge build, issues may arise.";
case OUTDATED, BETA_OUTDATED -> I18n.get("forge.update.newversion", ForgeVersion.getTarget());
default -> null;
};
}
public static String forgeStatusLine;
@Nullable
public static SoundInstance playSound(SoundEngine manager, SoundInstance sound) {
PlaySoundEvent e = new PlaySoundEvent(manager, sound);
MinecraftForge.EVENT_BUS.post(e);
return e.getSound();
return MinecraftForge.EVENT_BUS.fire(new PlaySoundEvent(manager, sound)).getSound();
}
public static void drawScreen(Screen screen, GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) {
guiGraphics.pose().pushPose();
guiLayers.forEach(layer -> {
for (Screen layer : guiLayers) {
// Prevent the background layers from thinking the mouse is over their controls and showing them as highlighted.
drawScreenInternal(layer, guiGraphics, Integer.MAX_VALUE, Integer.MAX_VALUE, partialTick);
guiGraphics.pose().translate(0, 0, 10000);
});
}
drawScreenInternal(screen, guiGraphics, mouseX, mouseY, partialTick);
guiGraphics.pose().popPose();
}
@ -535,8 +543,7 @@ public class ForgeHooksClient {
public static void onClientChangeGameType(PlayerInfo info, GameType currentGameMode, GameType newGameMode) {
if (currentGameMode != newGameMode) {
ClientPlayerChangeGameTypeEvent evt = new ClientPlayerChangeGameTypeEvent(info, currentGameMode, newGameMode);
MinecraftForge.EVENT_BUS.post(evt);
MinecraftForge.EVENT_BUS.post(new ClientPlayerChangeGameTypeEvent(info, currentGameMode, newGameMode));
}
}
@ -563,8 +570,7 @@ public class ForgeHooksClient {
}
public static void onRecipesUpdated(RecipeManager mgr) {
Event event = new RecipesUpdatedEvent(mgr);
MinecraftForge.EVENT_BUS.post(event);
MinecraftForge.EVENT_BUS.post(new RecipesUpdatedEvent(mgr));
}
public static void onKeyInput(int key, int scanCode, int action, int modifiers) {
@ -589,7 +595,7 @@ public class ForgeHooksClient {
public static SpriteContents loadSpriteContents(ResourceLocation name, Resource resource, FrameSize frameSize, NativeImage image, ResourceMetadata animationMeta) {
try {
ForgeTextureMetadata forgeMeta = ForgeTextureMetadata.forResource(resource);
return forgeMeta.getLoader() == null ? null : forgeMeta.getLoader().loadContents(name, resource, frameSize, image, animationMeta, forgeMeta);
return forgeMeta.loader() == null ? null : forgeMeta.loader().loadContents(name, resource, frameSize, image, animationMeta, forgeMeta);
} catch (IOException e) {
LOGGER.error("Unable to get Forge metadata for {}, falling back to vanilla loading", name);
e.printStackTrace();
@ -599,10 +605,10 @@ public class ForgeHooksClient {
@Nullable
public static TextureAtlasSprite loadTextureAtlasSprite(ResourceLocation atlasName, SpriteContents contents, int atlasWidth, int atlasHeight, int spriteX, int spriteY, int mipmapLevel) {
if (contents.forgeMeta == null || contents.forgeMeta.getLoader() == null)
if (contents.forgeMeta == null || contents.forgeMeta.loader() == null)
return null;
return contents.forgeMeta.getLoader().makeSprite(atlasName, contents, atlasWidth, atlasHeight, spriteX, spriteY, mipmapLevel);
return contents.forgeMeta.loader().makeSprite(atlasName, contents, atlasWidth, atlasHeight, spriteX, spriteY, mipmapLevel);
}
private static final Map<ModelLayerLocation, Supplier<LayerDefinition>> layerDefinitions = new HashMap<>();
@ -612,7 +618,9 @@ public class ForgeHooksClient {
}
public static void loadLayerDefinitions(ImmutableMap.Builder<ModelLayerLocation, LayerDefinition> builder) {
layerDefinitions.forEach((k, v) -> builder.put(k, v.get()));
for (var entry : layerDefinitions.entrySet()) {
builder.put(entry.getKey(), entry.getValue().get());
}
}
public static void processForgeListPingData(ServerStatus packet, ServerData target) {
@ -716,7 +724,7 @@ public class ForgeHooksClient {
}
private static Connection getClientConnection() {
return Minecraft.getInstance().getConnection()!=null ? Minecraft.getInstance().getConnection().getConnection() : null;
return Minecraft.getInstance().getConnection() != null ? Minecraft.getInstance().getConnection().getConnection() : null;
}
public static void handleClientLevelClosing(ClientLevel level) {
@ -825,8 +833,7 @@ public class ForgeHooksClient {
Font font = getTooltipFont(stack, fallbackFont);
var event = new RenderTooltipEvent.GatherComponents(stack, screenWidth, screenHeight, elements, -1);
MinecraftForge.EVENT_BUS.post(event);
if (event.isCanceled()) return List.of();
if (MinecraftForge.EVENT_BUS.post(event)) return List.of();
// text wrapping
int tooltipTextWidth = event.getTooltipElements().stream()
@ -891,9 +898,7 @@ public class ForgeHooksClient {
}
public static ScreenEvent.RenderInventoryMobEffects onScreenPotionSize(Screen screen, int availableSpace, boolean compact, int horizontalOffset) {
final ScreenEvent.RenderInventoryMobEffects event = new ScreenEvent.RenderInventoryMobEffects(screen, availableSpace, compact, horizontalOffset);
MinecraftForge.EVENT_BUS.post(event);
return event;
return MinecraftForge.EVENT_BUS.fire(new ScreenEvent.RenderInventoryMobEffects(screen, availableSpace, compact, horizontalOffset));
}
public static boolean onToastAdd(Toast toast) {

View file

@ -45,7 +45,10 @@ public final class ItemDecoratorHandler
var event = new RegisterItemDecorationsEvent(decorators);
ModLoader.get().postEventWrapContainerInModOrder(event);
var builder = new ImmutableMap.Builder<Item, ItemDecoratorHandler>();
decorators.forEach((item, itemDecorators) -> builder.put(item, new ItemDecoratorHandler(itemDecorators)));
for (var entry : decorators.entrySet()) {
Item item = entry.getKey();
builder.put(item, new ItemDecoratorHandler(entry.getValue()));
}
DECORATOR_LOOKUP = builder.build();
}

View file

@ -7,6 +7,7 @@ package net.minecraftforge.client;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
@ -31,7 +32,12 @@ public final class PresetEditorManager
Map<ResourceKey<WorldPreset>, PresetEditor> gatheredEditors = new HashMap<>();
// Vanilla's map uses Optional<ResourceKey>s as its keys.
// As far as we can tell there's no good reason for this, so we'll just use regular keys.
PresetEditor.EDITORS.forEach((k, v) -> k.ifPresent(key -> gatheredEditors.put(key, v)));
for (var entry : PresetEditor.EDITORS.entrySet()) {
var key = entry.getKey().orElse(null);
if (key != null) {
gatheredEditors.put(key, entry.getValue());
}
}
// Gather mods' entries
RegisterPresetEditorsEvent event = new RegisterPresetEditorsEvent(gatheredEditors);

View file

@ -5,8 +5,6 @@
package net.minecraftforge.client;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import net.minecraft.client.RecipeBookCategories;
import net.minecraft.world.inventory.RecipeBookType;
import net.minecraft.world.item.crafting.Recipe;
@ -57,14 +55,14 @@ public final class RecipeBookManager {
@ApiStatus.Internal
public static void init() {
// The ImmutableMap is the patched out value of AGGREGATE_CATEGORIES
var aggregateCategories = new HashMap<>(ImmutableMap.of(
RecipeBookCategories.CRAFTING_SEARCH, ImmutableList.of(RecipeBookCategories.CRAFTING_EQUIPMENT, RecipeBookCategories.CRAFTING_BUILDING_BLOCKS, RecipeBookCategories.CRAFTING_MISC, RecipeBookCategories.CRAFTING_REDSTONE),
RecipeBookCategories.FURNACE_SEARCH, ImmutableList.of(RecipeBookCategories.FURNACE_FOOD, RecipeBookCategories.FURNACE_BLOCKS, RecipeBookCategories.FURNACE_MISC),
RecipeBookCategories.BLAST_FURNACE_SEARCH, ImmutableList.of(RecipeBookCategories.BLAST_FURNACE_BLOCKS, RecipeBookCategories.BLAST_FURNACE_MISC),
RecipeBookCategories.SMOKER_SEARCH, ImmutableList.of(RecipeBookCategories.SMOKER_FOOD)
var aggregateCategories = new HashMap<>(Map.of(
RecipeBookCategories.CRAFTING_SEARCH, List.of(RecipeBookCategories.CRAFTING_EQUIPMENT, RecipeBookCategories.CRAFTING_BUILDING_BLOCKS, RecipeBookCategories.CRAFTING_MISC, RecipeBookCategories.CRAFTING_REDSTONE),
RecipeBookCategories.FURNACE_SEARCH, List.of(RecipeBookCategories.FURNACE_FOOD, RecipeBookCategories.FURNACE_BLOCKS, RecipeBookCategories.FURNACE_MISC),
RecipeBookCategories.BLAST_FURNACE_SEARCH, List.of(RecipeBookCategories.BLAST_FURNACE_BLOCKS, RecipeBookCategories.BLAST_FURNACE_MISC),
RecipeBookCategories.SMOKER_SEARCH, List.of(RecipeBookCategories.SMOKER_FOOD)
));
var typeCategories = new HashMap<RecipeBookType, ImmutableList<RecipeBookCategories>>();
var typeCategories = new HashMap<RecipeBookType, List<RecipeBookCategories>>();
var recipeCategoryLookups = new HashMap<RecipeType<?>, Function<Recipe<?>, RecipeBookCategories>>();
var event = new RegisterRecipeBookCategoriesEvent(aggregateCategories, typeCategories, recipeCategoryLookups);
ModLoader.get().postEventWrapContainerInModOrder(event);

View file

@ -78,8 +78,7 @@ public final class ForgeEventFactoryClient {
* @return the event object passed in and possibly modified by listeners
*/
private static <E extends Event> E fire(E e) {
post(e);
return e;
return MinecraftForge.EVENT_BUS.fire(e);
}
/**

View file

@ -5,7 +5,6 @@
package net.minecraftforge.client.event;
import com.google.common.collect.ImmutableList;
import net.minecraft.client.RecipeBookCategories;
import net.minecraft.world.inventory.RecipeBookType;
import net.minecraft.world.item.crafting.Recipe;
@ -31,14 +30,14 @@ import java.util.function.Function;
*/
public class RegisterRecipeBookCategoriesEvent extends Event implements IModBusEvent
{
private final Map<RecipeBookCategories, ImmutableList<RecipeBookCategories>> aggregateCategories;
private final Map<RecipeBookType, ImmutableList<RecipeBookCategories>> typeCategories;
private final Map<RecipeBookCategories, List<RecipeBookCategories>> aggregateCategories;
private final Map<RecipeBookType, List<RecipeBookCategories>> typeCategories;
private final Map<RecipeType<?>, Function<Recipe<?>, RecipeBookCategories>> recipeCategoryLookups;
@ApiStatus.Internal
public RegisterRecipeBookCategoriesEvent(
Map<RecipeBookCategories, ImmutableList<RecipeBookCategories>> aggregateCategories,
Map<RecipeBookType, ImmutableList<RecipeBookCategories>> typeCategories,
Map<RecipeBookCategories, List<RecipeBookCategories>> aggregateCategories,
Map<RecipeBookType, List<RecipeBookCategories>> typeCategories,
Map<RecipeType<?>, Function<Recipe<?>, RecipeBookCategories>> recipeCategoryLookups)
{
this.aggregateCategories = aggregateCategories;
@ -51,7 +50,7 @@ public class RegisterRecipeBookCategoriesEvent extends Event implements IModBusE
*/
public void registerAggregateCategory(RecipeBookCategories category, List<RecipeBookCategories> others)
{
aggregateCategories.put(category, ImmutableList.copyOf(others));
aggregateCategories.put(category, List.copyOf(others));
}
/**
@ -59,7 +58,7 @@ public class RegisterRecipeBookCategoriesEvent extends Event implements IModBusE
*/
public void registerBookCategories(RecipeBookType type, List<RecipeBookCategories> categories)
{
typeCategories.put(type, ImmutableList.copyOf(categories));
typeCategories.put(type, List.copyOf(categories));
}
/**

View file

@ -21,60 +21,44 @@ import java.util.Optional;
/**
* The "forge" section of texture metadata files (.mcmeta). Currently used only to specify custom
* TextureAtlasSprite loaders.
*
* @see ITextureAtlasSpriteLoader
*/
public final class ForgeTextureMetadata
{
public record ForgeTextureMetadata(@Nullable ITextureAtlasSpriteLoader loader) {
public static final ForgeTextureMetadata EMPTY = new ForgeTextureMetadata(null);
public static final MetadataSectionSerializer<ForgeTextureMetadata> SERIALIZER = new Serializer();
public static ForgeTextureMetadata forResource(Resource resource) throws IOException {
Optional<ForgeTextureMetadata> metadata = resource.metadata().getSection(SERIALIZER);
return metadata.isEmpty() ? EMPTY : metadata.get();
return metadata.orElse(EMPTY);
}
@Nullable
private final ITextureAtlasSpriteLoader loader;
public ForgeTextureMetadata(@Nullable ITextureAtlasSpriteLoader loader)
{
this.loader = loader;
}
@Nullable
public ITextureAtlasSpriteLoader getLoader()
{
public ITextureAtlasSpriteLoader getLoader() {
return loader;
}
private static final class Serializer implements MetadataSectionSerializer<ForgeTextureMetadata>
{
private static final class Serializer implements MetadataSectionSerializer<ForgeTextureMetadata> {
@Override
@NotNull
public String getMetadataSectionName()
{
public String getMetadataSectionName() {
return "forge";
}
@Override
@NotNull
public ForgeTextureMetadata fromJson(JsonObject json)
{
public ForgeTextureMetadata fromJson(JsonObject json) {
@Nullable
ITextureAtlasSpriteLoader loader;
if (json.has("loader"))
{
if (json.has("loader")) {
ResourceLocation loaderName = ResourceLocation.parse(GsonHelper.getAsString(json, "loader"));
loader = TextureAtlasSpriteLoaderManager.get(loaderName);
if (loader == null)
{
if (loader == null) {
throw new JsonSyntaxException("Unknown TextureAtlasSpriteLoader " + loaderName);
}
}
else
{
} else {
loader = null;
}
return new ForgeTextureMetadata(loader);

View file

@ -13,35 +13,31 @@ import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.Map;
/**
* Manager for {@link ITextureAtlasSpriteLoader} instances.
* <p>
* Provides a lookup.
*/
public final class TextureAtlasSpriteLoaderManager
{
private static ImmutableMap<ResourceLocation, ITextureAtlasSpriteLoader> LOADERS;
public final class TextureAtlasSpriteLoaderManager {
private static Map<ResourceLocation, ITextureAtlasSpriteLoader> LOADERS;
/**
* Finds the loader with the given name, or null if none is registered.
*/
@Nullable
public static ITextureAtlasSpriteLoader get(ResourceLocation name)
{
public static ITextureAtlasSpriteLoader get(ResourceLocation name) {
return LOADERS.get(name);
}
@ApiStatus.Internal
public static void init()
{
public static void init() {
var loaders = new HashMap<ResourceLocation, ITextureAtlasSpriteLoader>();
var event = new RegisterTextureAtlasSpriteLoadersEvent(loaders);
ModLoader.get().postEventWrapContainerInModOrder(event);
LOADERS = ImmutableMap.copyOf(loaders);
}
private TextureAtlasSpriteLoaderManager()
{
}
private TextureAtlasSpriteLoaderManager() {}
}

View file

@ -21,7 +21,7 @@ import net.minecraft.world.level.biome.Biome;
public class BiomeManager
{
private static TrackedList<BiomeEntry>[] biomes = setupBiomes();
private static final TrackedList<BiomeEntry>[] biomes = setupBiomes();
private static final List<ResourceKey<Biome>> additionalOverworldBiomes = new ArrayList<>();
private static final List<ResourceKey<Biome>> additionalOverworldBiomesView = Collections.unmodifiableList(additionalOverworldBiomes);

View file

@ -109,7 +109,7 @@ public final class CreativeModeTabRegistry {
@Override
protected void apply(@NotNull JsonObject data, @NotNull ResourceManager resourceManager, ProfilerFiller p) {
try {
if (data.size() > 0) {
if (!data.isEmpty()) {
JsonArray order = GsonHelper.getAsJsonArray(data, "order");
List<CreativeModeTab> customOrder = new ArrayList<>();
for (JsonElement entry : order) {
@ -183,8 +183,9 @@ public final class CreativeModeTabRegistry {
DEFAULT_TABS.add(BuiltInRegistries.CREATIVE_MODE_TAB.get(CreativeModeTabs.OP_BLOCKS));
DEFAULT_TABS.add(BuiltInRegistries.CREATIVE_MODE_TAB.get(CreativeModeTabs.INVENTORY));
final List<Holder<CreativeModeTab>> indexed = new ArrayList<>();
BuiltInRegistries.CREATIVE_MODE_TAB.holders().filter(c -> !DEFAULT_TABS.contains(c.get())).forEach(indexed::add);
final List<Holder<CreativeModeTab>> indexed = BuiltInRegistries.CREATIVE_MODE_TAB.holders()
.filter(c -> !DEFAULT_TABS.contains(c.get()))
.collect(Collectors.toList());
int vanillaTabs = 10;
for (int i = 0; i < vanillaTabs; i++) { // Vanilla ordering

View file

@ -632,7 +632,7 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper<UnmodifiableConfi
if (count > currentPath.size())
throw new IllegalArgumentException("Attempted to pop " + count + " elements when we only had: " + currentPath);
for (int x = 0; x < count; x++)
currentPath.remove(currentPath.size() - 1);
currentPath.removeLast();
return this;
}
@ -644,10 +644,14 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper<UnmodifiableConfi
public ForgeConfigSpec build() {
context.ensureEmpty();
Config valueCfg = Config.of(Config.getDefaultMapCreator(true, true), InMemoryFormat.withSupport(ConfigValue.class::isAssignableFrom));
values.forEach(v -> valueCfg.set(v.getPath(), v));
for (ConfigValue<?> value : values) {
valueCfg.set(value.getPath(), value);
}
ForgeConfigSpec ret = new ForgeConfigSpec(storage, valueCfg, levelComments, levelTranslationKeys);
values.forEach(v -> v.spec = ret);
for (ConfigValue<?> v : values) {
v.spec = ret;
}
return ret;
}

View file

@ -30,8 +30,8 @@ public class ForgeI18n {
// From FontRenderer.renderCharAtPos
private static final String ALLOWED_CHARS = "\u00c0\u00c1\u00c2\u00c8\u00ca\u00cb\u00cd\u00d3\u00d4\u00d5\u00da\u00df\u00e3\u00f5\u011f\u0130\u0131\u0152\u0153\u015e\u015f\u0174\u0175\u017e\u0207\u0000\u0000\u0000\u0000\u0000\u0000\u0000 !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u0000\u00c7\u00fc\u00e9\u00e2\u00e4\u00e0\u00e5\u00e7\u00ea\u00eb\u00e8\u00ef\u00ee\u00ec\u00c4\u00c5\u00c9\u00e6\u00c6\u00f4\u00f6\u00f2\u00fb\u00f9\u00ff\u00d6\u00dc\u00f8\u00a3\u00d8\u00d7\u0192\u00e1\u00ed\u00f3\u00fa\u00f1\u00d1\u00aa\u00ba\u00bf\u00ae\u00ac\u00bd\u00bc\u00a1\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u03b1\u03b2\u0393\u03c0\u03a3\u03c3\u03bc\u03c4\u03a6\u0398\u03a9\u03b4\u221e\u2205\u2208\u2229\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u00b7\u221a\u207f\u00b2\u25a0\u0000";
private static final CharMatcher DISALLOWED_CHAR_MATCHER = CharMatcher.anyOf(ALLOWED_CHARS).negate();
private static Map<String,String> i18n;
private static Map<String,FormatFactory> customFactories;
private static Map<String, String> i18n;
private static final Map<String, FormatFactory> customFactories;
// From StringUtils
private static final Pattern PATTERN_CONTROL_CODE = Pattern.compile("(?i)\\u00A7[0-9A-FK-OR]");

View file

@ -60,11 +60,11 @@ public class ForgeInternalHandler {
WorldWorkerManager.tick(false);
}
@SubscribeEvent
public void checkSettings(ClientTickEvent event) {
//if (event.phase == Phase.END)
// CloudRenderer.updateCloudSettings();
}
// @SubscribeEvent
// public void checkSettings(ClientTickEvent event) {
// if (event.phase == Phase.END)
// CloudRenderer.updateCloudSettings();
// }
@SubscribeEvent
public void onChunkUnload(ChunkEvent.Unload event) {

View file

@ -399,7 +399,9 @@ public class ForgeMod {
modEventBus.addListener(this::registerFluids);
modEventBus.addListener(this::registerVanillaDisplayContexts);
modEventBus.register(this);
registries.forEach(r -> r.register(modEventBus));
for (DeferredRegister<?> r : registries) {
r.register(modEventBus);
}
MinecraftForge.EVENT_BUS.addListener(this::serverStopping);
ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, ForgeConfig.clientSpec);

View file

@ -92,14 +92,14 @@ public class ForgeSpawnEggItem extends SpawnEggItem {
private static class ColorRegisterHandler {
@SubscribeEvent(priority = EventPriority.HIGHEST)
public static void registerSpawnEggColors(RegisterColorHandlersEvent.Item event) {
MOD_EGGS.forEach(egg -> {
for (ForgeSpawnEggItem egg : MOD_EGGS) {
event.register((stack, layer) -> {
int color = egg.getColor(layer);
if (FastColor.ARGB32.alpha(color) == 0)
color = FastColor.ARGB32.opaque(color);
return color;
}, egg);
});
}
}
}
}

View file

@ -29,11 +29,12 @@ public class VillagerTradingManager
static
{
VillagerTrades.TRADES.entrySet().forEach(e ->
{
VillagerTrades.TRADES.forEach((key, value) -> {
Int2ObjectMap<ItemListing[]> copy = new Int2ObjectOpenHashMap<>();
e.getValue().int2ObjectEntrySet().forEach(ent -> copy.put(ent.getIntKey(), Arrays.copyOf(ent.getValue(), ent.getValue().length)));
VANILLA_TRADES.put(e.getKey(), copy);
for (var ent : value.int2ObjectEntrySet()) {
copy.put(ent.getIntKey(), Arrays.copyOf(ent.getValue(), ent.getValue().length));
}
VANILLA_TRADES.put(key, copy);
});
VillagerTrades.WANDERING_TRADER_TRADES.int2ObjectEntrySet().forEach(e -> WANDERER_TRADES.put(e.getIntKey(), Arrays.copyOf(e.getValue(), e.getValue().length)));
}
@ -71,13 +72,14 @@ public class VillagerTradingManager
{
mutableTrades.put(i, NonNullList.create());
}
trades.int2ObjectEntrySet().forEach(e ->
{
Arrays.stream(e.getValue()).forEach(mutableTrades.get(e.getIntKey())::add);
});
for (var entry : trades.int2ObjectEntrySet()) {
Arrays.stream(entry.getValue()).forEach(mutableTrades.get(entry.getIntKey())::add);
}
MinecraftForge.EVENT_BUS.post(new VillagerTradesEvent(mutableTrades, prof));
Int2ObjectMap<ItemListing[]> newTrades = new Int2ObjectOpenHashMap<>();
mutableTrades.int2ObjectEntrySet().forEach(e -> newTrades.put(e.getIntKey(), e.getValue().toArray(new ItemListing[0])));
for (var entry : mutableTrades.int2ObjectEntrySet()) {
newTrades.put(entry.getIntKey(), entry.getValue().toArray(new ItemListing[0]));
}
VillagerTrades.TRADES.put(prof, newTrades);
}
}

View file

@ -10,7 +10,7 @@ import java.util.List;
public class WorldWorkerManager
{
private static List<IWorker> workers = new ArrayList<IWorker>();
private static final List<IWorker> workers = new ArrayList<>();
private static long startTime = -1;
private static int index = 0;

View file

@ -13,6 +13,8 @@ import net.minecraft.commands.arguments.selector.EntitySelectorParser;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Allows modders to register custom entity selectors by assigning an {@link IEntitySelectorType} to a String token. <br>
@ -21,6 +23,7 @@ import java.util.HashMap;
public class EntitySelectorManager
{
private static final HashMap<String, IEntitySelectorType> REGISTRY = new HashMap<>();
private static final List<String> RESERVED_TOKENS = List.of("p", "a", "r", "s", "e");
/**
* Registers a new {@link IEntitySelectorType} for the given {@code token}.<br>
@ -34,7 +37,7 @@ public class EntitySelectorManager
throw new IllegalArgumentException("Token must not be empty");
}
if (Arrays.asList("p", "a", "r", "s", "e").contains(token))
if (RESERVED_TOKENS.contains(token))
{
throw new IllegalArgumentException("Token clashes with vanilla @" + token);
}
@ -78,6 +81,10 @@ public class EntitySelectorManager
*/
public static void fillSelectorSuggestions(SuggestionsBuilder suggestionBuilder)
{
REGISTRY.forEach((token, type) -> suggestionBuilder.suggest("@" + token, type.getSuggestionTooltip()));
for (var entry : REGISTRY.entrySet()) {
String token = entry.getKey();
IEntitySelectorType type = entry.getValue();
suggestionBuilder.suggest("@" + token, type.getSuggestionTooltip());
}
}
}

View file

@ -7,6 +7,7 @@ package net.minecraftforge.common.crafting;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.stream.Stream;
@ -61,10 +62,10 @@ public class ConditionalRecipe {
}
public static class Builder {
private List<InnerRecipe> recipes = new ArrayList<>();
private List<InnerAdvancement> advancements = new ArrayList<>();
private final List<InnerRecipe> recipes = new ArrayList<>();
private final List<InnerAdvancement> advancements = new ArrayList<>();
private RecipeOutput bouncer = new RecipeOutput() {
private final RecipeOutput bouncer = new RecipeOutput() {
@Override
public void accept(ResourceLocation id, Recipe<?> value, @Nullable AdvancementHolder advancement) {
recipe(id, value, advancement);
@ -221,7 +222,7 @@ public class ConditionalRecipe {
var count = new Holder<Integer>();
count.value = -1;
var ret = stream.map(entry -> accept(context, ops, count, entry))
.filter(entry -> entry != null)
.filter(Objects::nonNull)
.findFirst()
.orElse(null);
@ -273,7 +274,7 @@ public class ConditionalRecipe {
}
};
private static class Holder<T> {
private static final class Holder<T> {
private T value;
}
}

View file

@ -59,7 +59,7 @@ public class SimpleCraftingContainer {
int height = this.rows.size();
if (height == 0)
throw new IllegalStateException("Invalid builder, empty inventory");
int width = this.rows.get(0).length();
int width = this.rows.getFirst().length();
var items = NonNullList.withSize(width * height, ItemStack.EMPTY);
int idx = 0;

View file

@ -35,7 +35,7 @@ public class CompoundIngredient extends AbstractIngredient {
return new CompoundIngredient(Arrays.asList(children));
}
private List<Ingredient> children;
private final List<Ingredient> children;
private ItemStack[] stacks;
private IntList itemIds;
private final boolean isSimple;

View file

@ -109,7 +109,7 @@ public class PartialNBTIngredient extends AbstractIngredient {
};
public static class Builder {
private List<ItemLike> items = new ArrayList<>();
private final List<ItemLike> items = new ArrayList<>();
private CompoundTag nbt;
public Builder nbt(CompoundTag value) {

View file

@ -253,10 +253,10 @@ public abstract class SoundDefinitionsProvider implements DataProvider
private CompletableFuture<?> save(final CachedOutput cache, final Path targetFile)
{
return DataProvider.saveStable(cache, this.mapToJson(this.sounds), targetFile);
return DataProvider.saveStable(cache, mapToJson(this.sounds), targetFile);
}
private JsonObject mapToJson(final Map<String, SoundDefinition> map)
private static JsonObject mapToJson(final Map<String, SoundDefinition> map)
{
final JsonObject obj = new JsonObject();
// namespaces are ignored when serializing

View file

@ -49,7 +49,11 @@ public abstract class SpriteSourceProvider extends JsonCodecProvider<List<Sprite
protected final void gather(BiConsumer<ResourceLocation, List<SpriteSource>> consumer)
{
addSources();
atlases.forEach((atlas, srcList) -> consumer.accept(atlas, srcList.sources));
for (var entry : atlases.entrySet()) {
ResourceLocation atlas = entry.getKey();
SourceList srcList = entry.getValue();
consumer.accept(atlas, srcList.sources);
}
}
protected abstract void addSources();

View file

@ -11,8 +11,8 @@ import net.minecraft.world.level.ChunkPos;
// Sorter to load nearby chunks first
public class CenterChunkPosComparator implements java.util.Comparator<ChunkPos>
{
private int x;
private int z;
private final int x;
private final int z;
public CenterChunkPosComparator(ServerPlayer entityplayer)
{

View file

@ -30,7 +30,7 @@ public class ConcatenatedListView<T> implements List<T>
{
return switch (members.size()) {
case 0 -> List.of();
case 1 -> Collections.unmodifiableList(members.get(0));
case 1 -> Collections.unmodifiableList(members.getFirst());
default -> new ConcatenatedListView<>(members);
};
}

View file

@ -46,7 +46,7 @@ public class HexDumper {
private static final String HEX = "0123456789ABCDEF";
private final int marked;
private final StringBuilder buf;
private char[] ascii = new char[16];
private final char[] ascii = new char[16];
private int index = 0;
private Instance(int marked, int size) {

View file

@ -15,7 +15,7 @@ import org.jetbrains.annotations.Nullable;
public class LevelCapabilityData extends SavedData {
public static final String ID = "capabilities";
private INBTSerializable<CompoundTag> serializable;
private final INBTSerializable<CompoundTag> serializable;
private CompoundTag capNBT = null;
public LevelCapabilityData(@Nullable INBTSerializable<CompoundTag> serializable) {

View file

@ -325,7 +325,7 @@ public class MutableHashedLinkedMap<K, V> implements Iterable<Map.Entry<K, V>>
Value apply(Key key, Value left, Value right);
}
private class Entry implements Map.Entry<K, V>
private final class Entry implements Map.Entry<K, V>
{
private final K key;
private V value;
@ -374,7 +374,7 @@ public class MutableHashedLinkedMap<K, V> implements Iterable<Map.Entry<K, V>>
}
}
private static class BasicStrategy implements Strategy<Object> {
private static final class BasicStrategy implements Strategy<Object> {
@Override
public int hashCode(Object o) {
return Objects.hashCode(o);
@ -386,7 +386,7 @@ public class MutableHashedLinkedMap<K, V> implements Iterable<Map.Entry<K, V>>
}
}
private static class IdentityStrategy implements Strategy<Object> {
private static final class IdentityStrategy implements Strategy<Object> {
@Override
public int hashCode(Object o) {
return System.identityHashCode(o);

View file

@ -8,6 +8,7 @@ package net.minecraftforge.common.util;
import java.io.IOException;
import java.io.Writer;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.Map;
import java.util.Properties;
@ -25,7 +26,7 @@ public class SortedProperties extends Properties {
@Override
public Set<Map.Entry<Object, Object>> entrySet() {
Set<Map.Entry<Object, Object>> ret = new TreeSet<>((left, right) -> left.getKey().toString().compareTo(right.getKey().toString()));
Set<Map.Entry<Object, Object>> ret = new TreeSet<>(Comparator.comparing(entry -> entry.getKey().toString()));
ret.addAll(super.entrySet());
return ret;
}

View file

@ -125,9 +125,9 @@ public class TextTable
public static class Column
{
private String header;
private final String header;
private int width;
private Alignment alignment;
private final Alignment alignment;
public Column(String header)
{

View file

@ -302,19 +302,12 @@ public final class TransformationHelper
Quaternionf ret;
try
{
if (entry.getKey().equals("x"))
{
ret = Axis.XP.rotationDegrees(entry.getValue().getAsNumber().floatValue());
}
else if (entry.getKey().equals("y"))
{
ret = Axis.YP.rotationDegrees(entry.getValue().getAsNumber().floatValue());
}
else if (entry.getKey().equals("z"))
{
ret = Axis.ZP.rotationDegrees(entry.getValue().getAsNumber().floatValue());
}
else throw new JsonParseException("Axis rotation: expected single axis key, got: " + entry.getKey());
ret = switch (entry.getKey()) {
case "x" -> Axis.XP.rotationDegrees(entry.getValue().getAsNumber().floatValue());
case "y" -> Axis.YP.rotationDegrees(entry.getValue().getAsNumber().floatValue());
case "z" -> Axis.ZP.rotationDegrees(entry.getValue().getAsNumber().floatValue());
default -> throw new JsonParseException("Axis rotation: expected single axis key, got: " + entry.getKey());
};
}
catch(ClassCastException ex)
{

View file

@ -17,10 +17,12 @@ public class BiomeGenerationSettingsBuilder extends BiomeGenerationSettings.Plai
{
public BiomeGenerationSettingsBuilder(BiomeGenerationSettings orig)
{
orig.getCarvingStages().forEach(k -> {
for (GenerationStep.Carving k : orig.getCarvingStages()) {
carvers.put(k, new ArrayList<>());
orig.getCarvers(k).forEach(v -> carvers.get(k).add(v));
});
for (Holder<ConfiguredWorldCarver<?>> v : orig.getCarvers(k)) {
carvers.get(k).add(v);
}
}
orig.features().forEach(l -> {
final ArrayList<Holder<PlacedFeature>> featureList = new ArrayList<>();
l.forEach(featureList::add);

View file

@ -5,11 +5,9 @@
package net.minecraftforge.internal;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import java.util.Collections;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.stream.IntStream;
import java.util.function.ObjIntConsumer;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.server.packs.resources.ResourceManagerReloadListener;
@ -19,20 +17,20 @@ import net.minecraftforge.fml.ModList;
import net.minecraftforge.versions.forge.ForgeVersion;
import net.minecraftforge.versions.mcp.MCPVersion;
public class BrandingControl {
public final class BrandingControl {
private BrandingControl() {}
private static List<String> brandings;
private static List<String> brandingsNoMC;
private static List<String> overCopyrightBrandings;
private static void computeBranding() {
if (brandings == null) {
ImmutableList.Builder<String> brd = ImmutableList.builder();
brd.add("Forge " + ForgeVersion.getVersion());
brd.add("Minecraft " + MCPVersion.getMCVersion());
brd.add("MCP " + MCPVersion.getMCPVersion());
int tModCount = ModList.get().size();
brd.add(ForgeI18n.parseMessage("fml.menu.loadingmods", tModCount));
brandings = brd.build();
brandings = List.of(
"Forge " + ForgeVersion.getVersion(),
"Minecraft " + MCPVersion.getMCVersion(),
"MCP " + MCPVersion.getMCPVersion(),
ForgeI18n.parseMessage("fml.menu.loadingmods", ModList.get().size())
);
brandingsNoMC = brandings.subList(1, brandings.size());
}
}
@ -40,34 +38,36 @@ public class BrandingControl {
private static List<String> getBrandings(boolean includeMC, boolean reverse) {
computeBranding();
if (includeMC)
return reverse ? Lists.reverse(brandings) : brandings;
return reverse ? brandings.reversed() : brandings;
else
return reverse ? Lists.reverse(brandingsNoMC) : brandingsNoMC;
return reverse ? brandingsNoMC.reversed() : brandingsNoMC;
}
private static void computeOverCopyrightBrandings() {
if (overCopyrightBrandings == null) {
ImmutableList.Builder<String> brd = ImmutableList.builder();
if (ForgeHooksClient.forgeStatusLine != null) brd.add(ForgeHooksClient.forgeStatusLine);
overCopyrightBrandings = brd.build();
public static List<String> getOverCopyrightBrandings() {
final class LazyInit {
private static final List<String> INSTANCE = ForgeHooksClient.forgeStatusLine == null
? Collections.emptyList()
: List.of(ForgeHooksClient.forgeStatusLine);
private LazyInit() {}
}
return LazyInit.INSTANCE;
}
public static void forEachLine(boolean includeMC, boolean reverse, BiConsumer<Integer, String> lineConsumer) {
final List<String> brandings = getBrandings(includeMC, reverse);
IntStream.range(0, brandings.size()).boxed().forEachOrdered(idx -> lineConsumer.accept(idx, brandings.get(idx)));
public static void forEachLine(boolean includeMC, boolean reverse, ObjIntConsumer<String> lineConsumer) {
var brandings = getBrandings(includeMC, reverse);
for (int idx = 0; idx < brandings.size(); idx++)
lineConsumer.accept(brandings.get(idx), idx);
}
public static void forEachAboveCopyrightLine(BiConsumer<Integer, String> lineConsumer) {
computeOverCopyrightBrandings();
IntStream.range(0, overCopyrightBrandings.size()).boxed().forEachOrdered(idx->lineConsumer.accept(idx, overCopyrightBrandings.get(idx)));
public static void forEachAboveCopyrightLine(ObjIntConsumer<String> lineConsumer) {
var overCopyrightBrandings = getOverCopyrightBrandings();
for (int idx = 0; idx < overCopyrightBrandings.size(); idx++)
lineConsumer.accept(overCopyrightBrandings.get(idx), idx);
}
public static String getClientBranding() {
return "forge";
}
public static String getServerBranding() {
public static String getBranding() {
return "forge";
}

View file

@ -15,15 +15,9 @@ import net.minecraftforge.fml.event.config.ModConfigEvent;
import java.util.function.Supplier;
public class ForgeBindings implements IBindingsProvider {
@Override
public Supplier<IEventBus> getForgeBusSupplier() {
return ()-> MinecraftForge.EVENT_BUS;
}
@Override
public Supplier<I18NParser> getMessageParser() {
return ()->new I18NParser() {
public final class ForgeBindings implements IBindingsProvider {
private static final class LazyInit {
private static final Supplier<I18NParser> INSTANCE = () -> new I18NParser() {
@Override
public String parseMessage(final String i18nMessage, final Object... args) {
return ForgeI18n.parseMessage(i18nMessage, args);
@ -34,10 +28,22 @@ public class ForgeBindings implements IBindingsProvider {
return ForgeI18n.stripControlCodes(toStrip);
}
};
private LazyInit() {}
}
@Override
public Supplier<IEventBus> getForgeBusSupplier() {
return () -> MinecraftForge.EVENT_BUS;
}
@Override
public Supplier<I18NParser> getMessageParser() {
return LazyInit.INSTANCE;
}
@Override
public Supplier<IConfigEvent.ConfigConfig> getConfigConfiguration() {
return ()->new IConfigEvent.ConfigConfig(ModConfigEvent.Loading::new, ModConfigEvent.Reloading::new, ModConfigEvent.Unloading::new);
return () -> new IConfigEvent.ConfigConfig(ModConfigEvent.Loading::new, ModConfigEvent.Reloading::new, ModConfigEvent.Unloading::new);
}
}

View file

@ -22,7 +22,7 @@ import java.text.SimpleDateFormat;
import java.util.*;
public class CrashReportExtender {
private static final String LINE_SEPARATOR = System.getProperty( "line.separator" );
private static final String LINE_SEPARATOR = System.lineSeparator();
public static void extendSystemReport(final SystemReport systemReport) {
for (final ISystemReportExtender call : CrashReportCallables.allCrashCallables()) {
if (call.isActive())

View file

@ -44,8 +44,8 @@ public class ChannelListManager {
.build();
private record Register(List<String> channels) implements CustomPacketPayload {
private static Type<Register> TYPE = CustomPacketPayload.createType("register");
private static StreamCodec<FriendlyByteBuf, Register> CODEC = StreamCodec.of(
private static final Type<Register> TYPE = CustomPacketPayload.createType("register");
private static final StreamCodec<FriendlyByteBuf, Register> CODEC = StreamCodec.of(
(buf, v) -> encode(buf, v.channels),
buf -> new Register(decode(buf))
);
@ -57,8 +57,8 @@ public class ChannelListManager {
}
private record Unregister(List<String> channels) implements CustomPacketPayload {
private static Type<Unregister> TYPE = CustomPacketPayload.createType("unregister");
private static StreamCodec<FriendlyByteBuf, Unregister> CODEC = StreamCodec.of(
private static final Type<Unregister> TYPE = CustomPacketPayload.createType("unregister");
private static final StreamCodec<FriendlyByteBuf, Unregister> CODEC = StreamCodec.of(
(buf, v) -> encode(buf, v.channels),
buf -> new Unregister(decode(buf))
);

View file

@ -31,6 +31,7 @@ import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntRBTreeMap;
import net.minecraft.core.Holder;
import net.minecraft.core.HolderSet;
import net.minecraft.nbt.Tag;
import net.minecraft.tags.TagKey;
import net.minecraftforge.common.util.LogMessageAdapter;
import net.minecraftforge.fml.ModLoadingContext;
@ -794,7 +795,11 @@ public class ForgeRegistry<V> implements IForgeRegistryInternal<V>, IForgeRegist
//Public for tests
public Snapshot makeSnapshot() {
Snapshot ret = new Snapshot();
this.ids.forEach((id, value) -> ret.ids.put(getKey(value), id));
for (Entry<Integer, V> entry : this.ids.entrySet()) {
Integer id = entry.getKey();
V value = entry.getValue();
ret.ids.put(getKey(value), id.intValue());
}
ret.aliases.putAll(this.aliases);
ret.blocked.addAll(this.blocked);
ret.overrides.putAll(getOverrideOwners());
@ -862,19 +867,19 @@ public class ForgeRegistry<V> implements IForgeRegistryInternal<V>, IForgeRegist
data.put("ids", ids);
ListTag aliases = new ListTag();
this.aliases.entrySet().forEach(e -> {
this.aliases.forEach((k, v) -> {
CompoundTag tag = new CompoundTag();
tag.putString("K", e.getKey().toString());
tag.putString("V", e.getValue().toString());
tag.putString("K", k.toString());
tag.putString("V", v.toString());
aliases.add(tag);
});
data.put("aliases", aliases);
ListTag overrides = new ListTag();
this.overrides.entrySet().forEach(e -> {
this.overrides.forEach((k, v) -> {
CompoundTag tag = new CompoundTag();
tag.putString("K", e.getKey().toString());
tag.putString("V", e.getValue());
tag.putString("K", k.toString());
tag.putString("V", v);
overrides.add(tag);
});
data.put("overrides", overrides);
@ -891,22 +896,22 @@ public class ForgeRegistry<V> implements IForgeRegistryInternal<V>, IForgeRegist
return ret;
ListTag list = nbt.getList("ids", 10);
list.forEach(e -> {
CompoundTag comp = (CompoundTag)e;
for (Tag tag : list) {
CompoundTag comp = (CompoundTag) tag;
ret.ids.put(ResourceLocation.parse(comp.getString("K")), comp.getInt("V"));
});
}
list = nbt.getList("aliases", 10);
list.forEach(e -> {
CompoundTag comp = (CompoundTag)e;
for (Tag tag : list) {
CompoundTag comp = (CompoundTag) tag;
ret.aliases.put(ResourceLocation.parse(comp.getString("K")), ResourceLocation.parse(comp.getString("V")));
});
}
list = nbt.getList("overrides", 10);
list.forEach(e -> {
CompoundTag comp = (CompoundTag)e;
for (Tag tag : list) {
CompoundTag comp = (CompoundTag) tag;
ret.overrides.put(ResourceLocation.parse(comp.getString("K")), comp.getString("V"));
});
}
int[] blocked = nbt.getIntArray("blocked");
for (int i : blocked)
@ -976,20 +981,24 @@ public class ForgeRegistry<V> implements IForgeRegistryInternal<V>, IForgeRegist
}
} else {
// block item missing, warn as requested and block the id
if (action == MissingMappingsEvent.Action.DEFAULT) {
V m = this.missing == null ? null : this.missing.createMissing(remap.key, injectNetworkDummies);
if (m == null)
defaulted.add(remap.key);
else
this.add(remap.id, remap.key, m, remap.key.getNamespace());
} else if (action == MissingMappingsEvent.Action.IGNORE) {
LOGGER.debug(REGISTRIES,"Ignoring {}", remap.key);
ignored++;
} else if (action == MissingMappingsEvent.Action.FAIL) {
LOGGER.debug(REGISTRIES,"Failing {}!", remap.key);
failed.add(remap.key);
} else if (action == MissingMappingsEvent.Action.WARN) {
LOGGER.warn(REGISTRIES,"{} may cause world breakage!", remap.key);
switch (action) {
case DEFAULT -> {
V m = this.missing == null ? null : this.missing.createMissing(remap.key, injectNetworkDummies);
if (m == null)
defaulted.add(remap.key);
else
this.add(remap.id, remap.key, m, remap.key.getNamespace());
}
case IGNORE -> {
LOGGER.debug(REGISTRIES, "Ignoring {}", remap.key);
ignored++;
}
case FAIL -> {
LOGGER.debug(REGISTRIES, "Failing {}!", remap.key);
failed.add(remap.key);
}
case WARN -> LOGGER.warn(REGISTRIES, "{} may cause world breakage!", remap.key);
case null, default -> {}
}
this.block(remap.id);
}

View file

@ -514,12 +514,12 @@ public class GameData {
throw new IllegalStateException("Could not get holder for " + key + " " + obj);
}
obj.matchingStates().forEach((state) -> {
for (BlockState state : obj.matchingStates()) {
var oldType = map.put(state, holder);
if (oldType != null) {
throw new IllegalStateException(String.format(Locale.ENGLISH, "Point of interest types %s and %s both list %s in their blockstates, this is not allowed. Blockstates can only have one point of interest type each.", oldType, obj, state));
}
});
}
}
@Override

View file

@ -53,10 +53,10 @@ class NamespacedWrapper<T> extends MappedRegistry<T> implements ILockableRegistr
Lifecycle registryLifecycle = Lifecycle.stable();
private boolean frozen = false; // Frozen is vanilla's variant of locked, but it can be unfrozen
private List<Holder.Reference<T>> holdersSorted;
private ObjectList<Holder.Reference<T>> holdersById = new ObjectArrayList<>(256);
private Map<ResourceLocation, Holder.Reference<T>> holdersByName = new HashMap<>();
private Map<T, Holder.Reference<T>> holders = new IdentityHashMap<>();
private RegistryManager stage;
private final ObjectList<Holder.Reference<T>> holdersById = new ObjectArrayList<>(256);
private final Map<ResourceLocation, Holder.Reference<T>> holdersByName = new HashMap<>();
private final Map<T, Holder.Reference<T>> holders = new IdentityHashMap<>();
private final RegistryManager stage;
private volatile Map<TagKey<T>, HolderSet.Named<T>> tags = new IdentityHashMap<>();
private final Map<ResourceKey<T>, RegistrationInfo> registrationInfos = new IdentityHashMap<>();
@ -323,7 +323,9 @@ class NamespacedWrapper<T> extends MappedRegistry<T> implements ILockableRegistr
@Override
public void bindTags(Map<TagKey<T>, List<Holder<T>>> newTags) {
Map<Holder.Reference<T>, List<TagKey<T>>> holderToTag = new IdentityHashMap<>();
this.holdersByName.values().forEach(v -> holderToTag.put(v, new ArrayList<>()));
for (Holder.Reference<T> tReference : this.holdersByName.values()) {
holderToTag.put(tReference, new ArrayList<>());
}
newTags.forEach((name, values) -> values.forEach(holder -> addTagToHolder(holderToTag, name, holder)));
Set<TagKey<T>> set = new HashSet<>(Sets.difference(this.tags.keySet(), newTags.keySet()));
@ -336,15 +338,17 @@ class NamespacedWrapper<T> extends MappedRegistry<T> implements ILockableRegistr
newTags.forEach((k, v) -> tmpTags.computeIfAbsent(k, this::createTag).bind(v));
Set<TagKey<T>> defaultedTags = Sets.difference(this.optionalTags.keySet(), newTags.keySet());
defaultedTags.forEach(name -> {
for (TagKey<T> name : defaultedTags) {
List<Holder<T>> defaults = this.optionalTags.get(name).stream()
.map(valueSupplier -> getHolder(valueSupplier.get()).orElse(null))
.filter(Objects::nonNull)
.distinct()
.toList();
defaults.forEach(holder -> addTagToHolder(holderToTag, name, holder));
for (Holder<T> holder : defaults) {
addTagToHolder(holderToTag, name, holder);
}
tmpTags.computeIfAbsent(name, this::createTag).bind(defaults);
});
}
holderToTag.forEach(Holder.Reference::bindTags);
this.tags = tmpTags;

View file

@ -221,16 +221,13 @@ public class ObjectHolderRegistry
public static void applyObjectHolders(Predicate<ResourceLocation> filter)
{
RuntimeException aggregate = new RuntimeException("Failed to apply some object holders, see suppressed exceptions for details");
objectHolders.forEach(objectHolder -> {
try
{
for (Consumer<Predicate<ResourceLocation>> objectHolder : objectHolders) {
try {
objectHolder.accept(filter);
}
catch (Exception e)
{
} catch (Exception e) {
aggregate.addSuppressed(e);
}
});
}
if (aggregate.getSuppressed().length > 0)
{

View file

@ -5,7 +5,6 @@
package net.minecraftforge.registries;
import com.google.common.collect.Lists;
import net.minecraft.core.Holder;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.registries.IForgeRegistry.AddCallback;
@ -15,6 +14,8 @@ import net.minecraftforge.registries.IForgeRegistry.CreateCallback;
import net.minecraftforge.registries.IForgeRegistry.MissingFactory;
import net.minecraftforge.registries.IForgeRegistry.ValidateCallback;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@ -39,18 +40,18 @@ public class RegistryBuilder<T> {
private ResourceLocation optionalDefaultKey;
private int minId = 0;
private int maxId = MAX_ID;
private List<AddCallback<T>> addCallback = Lists.newArrayList();
private List<ClearCallback<T>> clearCallback = Lists.newArrayList();
private List<CreateCallback<T>> createCallback = Lists.newArrayList();
private List<ValidateCallback<T>> validateCallback = Lists.newArrayList();
private List<BakeCallback<T>> bakeCallback = Lists.newArrayList();
private final List<AddCallback<T>> addCallback = new ArrayList<>();
private final List<ClearCallback<T>> clearCallback = new ArrayList<>();
private final List<CreateCallback<T>> createCallback = new ArrayList<>();
private final List<ValidateCallback<T>> validateCallback = new ArrayList<>();
private final List<BakeCallback<T>> bakeCallback = new ArrayList<>();
private boolean saveToDisc = true;
private boolean sync = true;
private boolean allowOverrides = true;
private boolean allowModifications = false;
private boolean hasWrapper = false;
private MissingFactory<T> missingFactory;
private Set<ResourceLocation> legacyNames = new HashSet<>();
private final Set<ResourceLocation> legacyNames = new HashSet<>();
@Nullable
private Function<T, Holder.Reference<T>> intrusiveHolderCallback = null;
@ -206,8 +207,8 @@ public class RegistryBuilder<T> {
IForgeRegistry<T> create() {
if (hasWrapper) {
GameData.WrapperFactory<T> wrapper = GameData.createWrapperFactory(getDefault() != null);
this.addCallback.add(0, wrapper);
this.createCallback.add(0, wrapper);
this.addCallback.addFirst(wrapper);
this.createCallback.addFirst(wrapper);
}
return RegistryManager.ACTIVE.createRegistry(registryName, this);
}
@ -217,7 +218,7 @@ public class RegistryBuilder<T> {
if (addCallback.isEmpty())
return null;
if (addCallback.size() == 1)
return addCallback.get(0);
return addCallback.getFirst();
var tmp = this.addCallback;
return (owner, stage, id, key, obj, old) -> {
@ -231,7 +232,7 @@ public class RegistryBuilder<T> {
if (clearCallback.isEmpty())
return null;
if (clearCallback.size() == 1)
return clearCallback.get(0);
return clearCallback.getFirst();
var tmp = this.clearCallback;
return (owner, stage) -> {
@ -245,7 +246,7 @@ public class RegistryBuilder<T> {
if (createCallback.isEmpty())
return null;
if (createCallback.size() == 1)
return createCallback.get(0);
return createCallback.getFirst();
var tmp = this.createCallback;
return (owner, stage) -> {
@ -259,7 +260,7 @@ public class RegistryBuilder<T> {
if (validateCallback.isEmpty())
return null;
if (validateCallback.size() == 1)
return validateCallback.get(0);
return validateCallback.getFirst();
var tmp = this.validateCallback;
return (owner, stage, id, key, obj) -> {
@ -273,7 +274,7 @@ public class RegistryBuilder<T> {
if (bakeCallback.isEmpty())
return null;
if (bakeCallback.size() == 1)
return bakeCallback.get(0);
return bakeCallback.getFirst();
var tmp = this.bakeCallback;
return (owner, stage) -> {

View file

@ -7,14 +7,13 @@ package net.minecraftforge.registries;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.Sets;
import com.mojang.serialization.Lifecycle;
import net.minecraft.core.RegistrationInfo;
import net.minecraft.core.WritableRegistry;
import net.minecraft.core.registries.BuiltInRegistries;
@ -35,10 +34,10 @@ public class RegistryManager {
private static Set<ResourceLocation> vanillaRegistryKeys = Set.of();
BiMap<ResourceLocation, ForgeRegistry<?>> registries = HashBiMap.create();
private Map<ResourceLocation, ? extends IForgeRegistry<?>> registryView = Collections.unmodifiableMap(registries);
private Set<ResourceLocation> persisted = Sets.newHashSet();
private Set<ResourceLocation> synced = Sets.newHashSet();
private Map<ResourceLocation, ResourceLocation> legacyNames = new HashMap<>();
private final Map<ResourceLocation, ? extends IForgeRegistry<?>> registryView = Collections.unmodifiableMap(registries);
private final Set<ResourceLocation> persisted = new HashSet<>();
private final Set<ResourceLocation> synced = new HashSet<>();
private final Map<ResourceLocation, ResourceLocation> legacyNames = new HashMap<>();
private final String name;
RegistryManager() {
@ -152,7 +151,9 @@ public class RegistryManager {
public Map<ResourceLocation, Snapshot> takeSnapshot(boolean savingToDisc) {
Map<ResourceLocation, Snapshot> ret = new HashMap<>();
var keys = savingToDisc ? this.persisted : this.synced;
keys.forEach(name -> ret.put(name, getRegistry(name).makeSnapshot()));
for (ResourceLocation key : keys) {
ret.put(key, getRegistry(key).makeSnapshot());
}
return ret;
}

Some files were not shown because too many files have changed in this diff Show more