diff --git a/gradle.properties b/gradle.properties index b8c9d1db22..efadb17d5f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -12,7 +12,7 @@ java_version=17 minecraft_version=1.20.4 neoform_version=20231207.154220 -spi_version=8.0.0 +spi_version=9.0.1 mergetool_version=2.0.0 accesstransformers_version=10.0.1 coremods_version=6.0.2 @@ -30,7 +30,7 @@ jetbrains_annotations_version=24.0.1 slf4j_api_version=2.0.7 apache_maven_artifact_version=3.8.5 jarjar_version=0.4.0 -fancy_mod_loader_version=2.0.0 +fancy_mod_loader_version=2.0.1 mojang_logging_version=1.1.1 log4j_version=2.19.0 guava_version=31.1.2-jre diff --git a/patches/net/minecraft/client/Options.java.patch b/patches/net/minecraft/client/Options.java.patch index 778f6a6b2c..63c153e988 100644 --- a/patches/net/minecraft/client/Options.java.patch +++ b/patches/net/minecraft/client/Options.java.patch @@ -12,6 +12,15 @@ && !Minecraft.getInstance().getSoundManager().getAvailableSoundDevices().contains(p_232011_) ? Optional.empty() : Optional.of(p_232011_), +@@ -787,7 +_,7 @@ + this.incompatibleResourcePacks.clear(); + + for(Pack pack : p_275268_.getSelectedPacks()) { +- if (!pack.isFixedPosition()) { ++ if (!pack.isFixedPosition() && !pack.isHidden()) { + this.resourcePacks.add(pack.getId()); + if (!pack.getCompatibility().isCompatible()) { + this.incompatibleResourcePacks.add(pack.getId()); @@ -1058,6 +_,7 @@ } diff --git a/patches/net/minecraft/client/gui/screens/packs/PackSelectionModel.java.patch b/patches/net/minecraft/client/gui/screens/packs/PackSelectionModel.java.patch index 7693d01088..98bea19a02 100644 --- a/patches/net/minecraft/client/gui/screens/packs/PackSelectionModel.java.patch +++ b/patches/net/minecraft/client/gui/screens/packs/PackSelectionModel.java.patch @@ -1,23 +1,11 @@ --- a/net/minecraft/client/gui/screens/packs/PackSelectionModel.java +++ b/net/minecraft/client/gui/screens/packs/PackSelectionModel.java -@@ -106,6 +_,8 @@ - boolean canMoveUp(); - - boolean canMoveDown(); -+ -+ default boolean notHidden() { return true; } - } - - @OnlyIn(Dist.CLIENT) -@@ -205,6 +_,11 @@ - @Override - public void moveDown() { - this.move(1); -+ } -+ -+ @Override -+ public boolean notHidden() { -+ return !pack.isHidden(); - } - } - +@@ -31,7 +_,7 @@ + this.onListChanged = p_99909_; + this.iconGetter = p_99910_; + this.repository = p_99911_; +- this.selected = Lists.newArrayList(p_99911_.getSelectedPacks()); ++ this.selected = Lists.newArrayList(p_99911_.getSelectedPacks().stream().filter(p -> !p.isHidden()).toList()); + Collections.reverse(this.selected); + this.unselected = Lists.newArrayList(p_99911_.getAvailablePacks()); + this.unselected.removeAll(this.selected); diff --git a/patches/net/minecraft/client/gui/screens/packs/PackSelectionScreen.java.patch b/patches/net/minecraft/client/gui/screens/packs/PackSelectionScreen.java.patch deleted file mode 100644 index cf2aa31c07..0000000000 --- a/patches/net/minecraft/client/gui/screens/packs/PackSelectionScreen.java.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- a/net/minecraft/client/gui/screens/packs/PackSelectionScreen.java -+++ b/net/minecraft/client/gui/screens/packs/PackSelectionScreen.java -@@ -144,7 +_,7 @@ - TransferableSelectionList.PackEntry transferableselectionlist$packentry = p_100014_.getSelected(); - String s = transferableselectionlist$packentry == null ? "" : transferableselectionlist$packentry.getPackId(); - p_100014_.setSelected(null); -- p_100015_.forEach( -+ p_100015_.filter(PackSelectionModel.Entry::notHidden).forEach( - p_313437_ -> { - TransferableSelectionList.PackEntry transferableselectionlist$packentry1 = new TransferableSelectionList.PackEntry( - this.minecraft, p_100014_, p_313437_ diff --git a/patches/net/minecraft/client/gui/screens/worldselection/CreateWorldScreen.java.patch b/patches/net/minecraft/client/gui/screens/worldselection/CreateWorldScreen.java.patch index 890774ac5d..8fe4918093 100644 --- a/patches/net/minecraft/client/gui/screens/worldselection/CreateWorldScreen.java.patch +++ b/patches/net/minecraft/client/gui/screens/worldselection/CreateWorldScreen.java.patch @@ -32,7 +32,7 @@ if (path != null) { if (this.tempDataPackRepository == null) { this.tempDataPackRepository = ServerPacksSource.createPackRepository(path, this.packValidator); -+ net.neoforged.neoforge.resource.ResourcePackLoader.loadResourcePacks(this.tempDataPackRepository, net.neoforged.neoforge.server.ServerLifecycleHooks::buildPackFinder); ++ net.neoforged.neoforge.resource.ResourcePackLoader.loadResourcePacks(this.tempDataPackRepository, map -> net.neoforged.neoforge.resource.ResourcePackLoader.buildPackFinder(map, net.minecraft.server.packs.PackType.SERVER_DATA)); this.tempDataPackRepository.reload(); } diff --git a/patches/net/minecraft/server/MinecraftServer.java.patch b/patches/net/minecraft/server/MinecraftServer.java.patch index b405f24ddb..35b946b918 100644 --- a/patches/net/minecraft/server/MinecraftServer.java.patch +++ b/patches/net/minecraft/server/MinecraftServer.java.patch @@ -188,15 +188,15 @@ public static WorldDataConfiguration configurePackRepository( PackRepository p_248681_, DataPackConfig p_248920_, boolean p_249869_, FeatureFlagSet p_251243_ ) { -+ net.neoforged.neoforge.resource.ResourcePackLoader.loadResourcePacks(p_248681_, net.neoforged.neoforge.server.ServerLifecycleHooks::buildPackFinder); ++ net.neoforged.neoforge.resource.ResourcePackLoader.loadResourcePacks(p_248681_, map -> net.neoforged.neoforge.resource.ResourcePackLoader.buildPackFinder(map, PackType.SERVER_DATA)); p_248681_.reload(); -+ DataPackConfig.DEFAULT.addModPacks(net.neoforged.neoforge.common.CommonHooks.getModPacks()); -+ p_248920_.addModPacks(net.neoforged.neoforge.common.CommonHooks.getModPacks()); ++ DataPackConfig.DEFAULT.addModPacks(net.neoforged.neoforge.common.CommonHooks.getModDataPacks()); ++ p_248920_.addModPacks(net.neoforged.neoforge.common.CommonHooks.getModDataPacks()); if (p_249869_) { - p_248681_.setSelected(Collections.singleton("vanilla")); - return WorldDataConfiguration.DEFAULT; -+ p_248681_.setSelected(net.neoforged.neoforge.common.CommonHooks.getModPacksWithVanilla()); -+ return new WorldDataConfiguration(new DataPackConfig(net.neoforged.neoforge.common.CommonHooks.getModPacksWithVanilla(), ImmutableList.of()), FeatureFlags.DEFAULT_FLAGS); ++ p_248681_.setSelected(net.neoforged.neoforge.common.CommonHooks.getModDataPacksWithVanilla()); ++ return new WorldDataConfiguration(new DataPackConfig(net.neoforged.neoforge.common.CommonHooks.getModDataPacksWithVanilla(), ImmutableList.of()), FeatureFlags.DEFAULT_FLAGS); } else { Set set = Sets.newLinkedHashSet(); diff --git a/patches/net/minecraft/server/commands/DataPackCommand.java.patch b/patches/net/minecraft/server/commands/DataPackCommand.java.patch new file mode 100644 index 0000000000..ec062a79da --- /dev/null +++ b/patches/net/minecraft/server/commands/DataPackCommand.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/server/commands/DataPackCommand.java ++++ b/net/minecraft/server/commands/DataPackCommand.java +@@ -183,7 +_,7 @@ + private static int listEnabledPacks(CommandSourceStack p_136866_) { + PackRepository packrepository = p_136866_.getServer().getPackRepository(); + packrepository.reload(); +- Collection collection = packrepository.getSelectedPacks(); ++ Collection collection = packrepository.getSelectedPacks().stream().filter(p -> !p.isHidden()).toList(); + if (collection.isEmpty()) { + p_136866_.sendSuccess(() -> Component.translatable("commands.datapack.list.enabled.none"), false); + } else { diff --git a/patches/net/minecraft/server/packs/repository/Pack.java.patch b/patches/net/minecraft/server/packs/repository/Pack.java.patch index 952fc1423b..ee72049669 100644 --- a/patches/net/minecraft/server/packs/repository/Pack.java.patch +++ b/patches/net/minecraft/server/packs/repository/Pack.java.patch @@ -1,21 +1,48 @@ --- a/net/minecraft/server/packs/repository/Pack.java +++ b/net/minecraft/server/packs/repository/Pack.java -@@ -29,6 +_,7 @@ +@@ -29,6 +_,8 @@ private final Pack.Position defaultPosition; private final boolean required; private final boolean fixedPosition; -+ private final boolean hidden; // NeoForge: Allow packs to be hidden from the UI entirely ++ private final boolean hidden; // Neo: Allow packs to be hidden from the UI entirely ++ private final List children; // Neo: Allows packs to specify packs which will always be placed beneath them; must be hidden private final PackSource packSource; @Nullable -@@ -77,6 +_,7 @@ - this.defaultPosition = p_251298_; - this.fixedPosition = p_249753_; - this.packSource = p_251608_; +@@ -69,6 +_,33 @@ + boolean p_249753_, + PackSource p_251608_ + ) { ++ this(p_252218_, p_248829_, p_249377_, p_251718_, p_250162_, p_251298_, p_249753_, p_251608_, List.of()); ++ } ++ ++ private Pack( ++ String p_252218_, ++ boolean p_248829_, ++ Pack.ResourcesSupplier p_249377_, ++ Component p_251718_, ++ Pack.Info p_250162_, ++ Pack.Position p_251298_, ++ boolean p_249753_, ++ PackSource p_251608_, ++ List children ++ ) { ++ List flattenedChildren = new java.util.ArrayList<>(); ++ List remainingChildren = children; ++ // recursively flatten children ++ while (!remainingChildren.isEmpty()) { ++ List oldChildren = remainingChildren; ++ remainingChildren = new java.util.ArrayList<>(); ++ for (Pack child : oldChildren) { ++ flattenedChildren.add(child.withChildren(List.of()).hidden()); ++ remainingChildren.addAll(child.getChildren()); ++ } ++ } ++ this.children = List.copyOf(flattenedChildren); + this.hidden = p_250162_.isHidden(); - } - - @Nullable + this.id = p_252218_; + this.resources = p_249377_; + this.title = p_251718_; @@ -96,7 +_,7 @@ PackCompatibility packcompatibility = PackCompatibility.forVersion(inclusiverange, p_294759_); OverlayMetadataSection overlaymetadatasection = packresources.getMetadataSection(OverlayMetadataSection.TYPE); @@ -25,11 +52,36 @@ } return pack$info; -@@ -172,6 +_,8 @@ +@@ -172,6 +_,33 @@ return this.packSource; } + public boolean isHidden() { return hidden; } ++ ++ public List getChildren() { return children; } ++ ++ /** ++ * {@return a copy of the pack with the provided children in place of any children this pack currently has} ++ */ ++ public Pack withChildren(List children) { ++ return new Pack(this.id, this.required, this.resources, this.title, this.info, this.defaultPosition, this.fixedPosition, this.packSource, children); ++ } ++ ++ /** ++ * {@return a copy of the pack that is hidden} ++ */ ++ public Pack hidden() { ++ return new Pack( ++ this.id, this.required, this.resources, this.title, ++ new Info( ++ this.info.description(), ++ info.compatibility(), ++ info.requestedFeatures(), ++ info.overlays(), ++ true ++ ), ++ this.defaultPosition, this.fixedPosition, this.packSource, this.children); ++ } + @Override public boolean equals(Object p_10448_) { diff --git a/patches/net/minecraft/server/packs/repository/PackRepository.java.patch b/patches/net/minecraft/server/packs/repository/PackRepository.java.patch index 593d452b44..c16e20b391 100644 --- a/patches/net/minecraft/server/packs/repository/PackRepository.java.patch +++ b/patches/net/minecraft/server/packs/repository/PackRepository.java.patch @@ -9,6 +9,30 @@ } public void reload() { +@@ -71,11 +_,12 @@ + } + + private List rebuildSelected(Collection p_10518_) { +- List list = this.getAvailablePacks(p_10518_).collect(Collectors.toList()); ++ List list = this.getAvailablePacks(p_10518_).flatMap(p -> Stream.concat(Stream.of(p), p.getChildren().stream())).collect(Collectors.toList()); + + for(Pack pack : this.available.values()) { + if (pack.isRequired() && !list.contains(pack)) { +- pack.getDefaultPosition().insert(list, pack, Functions.identity(), false); ++ int i = pack.getDefaultPosition().insert(list, pack, Functions.identity(), false); ++ list.addAll(i + 1, pack.getChildren()); + } + } + +@@ -95,7 +_,7 @@ + } + + public Collection getSelectedIds() { +- return this.selected.stream().map(Pack::getId).collect(ImmutableSet.toImmutableSet()); ++ return this.selected.stream().filter(p -> !p.isHidden()).map(Pack::getId).collect(ImmutableSet.toImmutableSet()); + } + + public FeatureFlagSet getRequestedFeatureFlags() { @@ -109,6 +_,10 @@ @Nullable public Pack getPack(String p_10508_) { diff --git a/patches/net/minecraft/server/packs/resources/FallbackResourceManager.java.patch b/patches/net/minecraft/server/packs/resources/FallbackResourceManager.java.patch deleted file mode 100644 index 02503d9915..0000000000 --- a/patches/net/minecraft/server/packs/resources/FallbackResourceManager.java.patch +++ /dev/null @@ -1,24 +0,0 @@ ---- a/net/minecraft/server/packs/resources/FallbackResourceManager.java -+++ b/net/minecraft/server/packs/resources/FallbackResourceManager.java -@@ -103,8 +_,11 @@ - - for(int i = this.fallbacks.size() - 1; i >= 0; --i) { - FallbackResourceManager.PackEntry fallbackresourcemanager$packentry = this.fallbacks.get(i); -- PackResources packresources = fallbackresourcemanager$packentry.resources; -- if (packresources != null) { -+ PackResources pack = fallbackresourcemanager$packentry.resources; -+ if (pack != null) { -+ var children = pack.getChildren(); -+ var packs = children == null ? List.of(pack) : children; -+ for (final PackResources packresources : packs) { - IoSupplier iosupplier = packresources.getResource(this.type, p_215367_); - if (iosupplier != null) { - IoSupplier iosupplier1; -@@ -118,6 +_,7 @@ - } - - list.add(new Resource(packresources, iosupplier, iosupplier1)); -+ } - } - } - diff --git a/src/main/java/net/neoforged/neoforge/client/gui/ModListScreen.java b/src/main/java/net/neoforged/neoforge/client/gui/ModListScreen.java index 2f0c62efe7..a5bca764b9 100644 --- a/src/main/java/net/neoforged/neoforge/client/gui/ModListScreen.java +++ b/src/main/java/net/neoforged/neoforge/client/gui/ModListScreen.java @@ -34,6 +34,7 @@ import net.minecraft.network.chat.Style; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.packs.PackResources; +import net.minecraft.server.packs.repository.Pack; import net.minecraft.server.packs.resources.IoSupplier; import net.minecraft.util.FormattedCharSequence; import net.neoforged.fml.ModContainer; @@ -379,11 +380,11 @@ private void updateCache() { @SuppressWarnings("resource") Pair logoData = selectedMod.getLogoFile().map(logoFile -> { TextureManager tm = this.minecraft.getTextureManager(); - final PackResources resourcePack = ResourcePackLoader.getPackFor(selectedMod.getModId()) + final Pack.ResourcesSupplier resourcePack = ResourcePackLoader.getPackFor(selectedMod.getModId()) .orElse(ResourcePackLoader.getPackFor("neoforge").orElseThrow(() -> new RuntimeException("Can't find neoforge, WHAT!"))); - try { + try (PackResources packResources = resourcePack.openPrimary("mod:" + selectedMod.getModId())) { NativeImage logo = null; - IoSupplier logoResource = resourcePack.getRootResource(logoFile.split("[/\\\\]")); + IoSupplier logoResource = packResources.getRootResource(logoFile.split("[/\\\\]")); if (logoResource != null) logo = NativeImage.read(logoResource.get()); if (logo != null) { diff --git a/src/main/java/net/neoforged/neoforge/client/loading/ClientModLoader.java b/src/main/java/net/neoforged/neoforge/client/loading/ClientModLoader.java index 1bc1472704..b9247de512 100644 --- a/src/main/java/net/neoforged/neoforge/client/loading/ClientModLoader.java +++ b/src/main/java/net/neoforged/neoforge/client/loading/ClientModLoader.java @@ -6,24 +6,14 @@ package net.neoforged.neoforge.client.loading; import java.io.File; -import java.util.ArrayList; import java.util.Collections; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Optional; import java.util.concurrent.*; -import java.util.function.Consumer; -import net.minecraft.SharedConstants; import net.minecraft.client.Minecraft; -import net.minecraft.network.chat.Component; -import net.minecraft.server.packs.PackResources; import net.minecraft.server.packs.PackType; -import net.minecraft.server.packs.metadata.pack.PackMetadataSection; import net.minecraft.server.packs.repository.*; import net.minecraft.server.packs.resources.PreparableReloadListener; import net.minecraft.server.packs.resources.ReloadableResourceManager; import net.minecraft.server.packs.resources.ResourceManager; -import net.minecraft.util.InclusiveRange; import net.minecraft.util.profiling.ProfilerFiller; import net.minecraft.world.level.DataPackConfig; import net.neoforged.api.distmarker.Dist; @@ -37,11 +27,8 @@ import net.neoforged.neoforge.event.AddPackFindersEvent; import net.neoforged.neoforge.internal.BrandingControl; import net.neoforged.neoforge.logging.CrashReportExtender; -import net.neoforged.neoforge.resource.DelegatingPackResources; import net.neoforged.neoforge.resource.ResourcePackLoader; import net.neoforged.neoforge.server.LanguageHook; -import net.neoforged.neoforgespi.language.IModInfo; -import net.neoforged.neoforgespi.locating.IModFile; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -63,9 +50,9 @@ public static void begin(final Minecraft minecraft, final PackRepository default LanguageHook.loadForgeAndMCLangs(); createRunnableWithCatch(() -> ModLoader.get().gatherAndInitializeMods(ModWorkManager.syncExecutor(), ModWorkManager.parallelExecutor(), ImmediateWindowHandler::renderTick)).run(); if (error == null) { - ResourcePackLoader.loadResourcePacks(defaultResourcePacks, ClientModLoader::buildPackFinder); + ResourcePackLoader.loadResourcePacks(defaultResourcePacks, map -> ResourcePackLoader.buildPackFinder(map, PackType.CLIENT_RESOURCES)); ModLoader.get().postEvent(new AddPackFindersEvent(PackType.CLIENT_RESOURCES, defaultResourcePacks::addPackFinder)); - DataPackConfig.DEFAULT.addModPacks(ResourcePackLoader.getPackNames()); + DataPackConfig.DEFAULT.addModPacks(ResourcePackLoader.getDataPackNames()); mcResourceManager.registerReloadListener(ClientModLoader::onResourceReload); mcResourceManager.registerReloadListener(BrandingControl.resourceManagerReloadListener()); } @@ -144,40 +131,4 @@ public static boolean completeModLoading() { public static boolean isLoading() { return loading; } - - private static RepositorySource buildPackFinder(Map modResourcePacks) { - return packAcceptor -> clientPackFinder(modResourcePacks, packAcceptor); - } - - private static void clientPackFinder(Map modResourcePacks, Consumer packAcceptor) { - var hiddenPacks = new ArrayList(); - for (Entry e : modResourcePacks.entrySet()) { - IModInfo mod = e.getKey().getModInfos().get(0); - final String name = "mod:" + mod.getModId(); - final Pack modPack = Pack.readMetaAndCreate(name, Component.literal(e.getValue().packId()), false, BuiltInPackSource.fixedResources(e.getValue()), PackType.CLIENT_RESOURCES, Pack.Position.BOTTOM, PackSource.DEFAULT); - if (modPack == null) { - // Vanilla only logs an error, instead of propagating, so handle null and warn that something went wrong - ModLoader.get().addWarning(new ModLoadingWarning(mod, ModLoadingStage.ERROR, "fml.modloading.brokenresources", e.getKey())); - continue; - } - LOGGER.debug(Logging.CORE, "Generating PackInfo named {} for mod file {}", name, e.getKey().getFilePath()); - if (mod.getOwningFile().showAsResourcePack()) { - packAcceptor.accept(modPack); - } else { - hiddenPacks.add(e.getValue()); - } - } - - // Create a resource pack merging all mod resources that should be hidden - final Pack modResourcesPack = Pack.readMetaAndCreate("mod_resources", Component.literal("Mod Resources"), true, - BuiltInPackSource.fromName( - id -> new DelegatingPackResources(id, false, - new PackMetadataSection( - Component.translatable("fml.resources.modresources", hiddenPacks.size()), - SharedConstants.getCurrentVersion().getPackVersion(PackType.CLIENT_RESOURCES), - Optional.of(new InclusiveRange<>(0, Integer.MAX_VALUE))), - hiddenPacks)), - PackType.CLIENT_RESOURCES, Pack.Position.BOTTOM, PackSource.DEFAULT); - packAcceptor.accept(modResourcesPack); - } } diff --git a/src/main/java/net/neoforged/neoforge/common/CommonHooks.java b/src/main/java/net/neoforged/neoforge/common/CommonHooks.java index b5438ae39f..eab6957ab1 100644 --- a/src/main/java/net/neoforged/neoforge/common/CommonHooks.java +++ b/src/main/java/net/neoforged/neoforge/common/CommonHooks.java @@ -1033,15 +1033,15 @@ public static ObjectArrayList modifyLoot(ResourceLocation lootTableId return generatedLoot; } - public static List getModPacks() { - List modpacks = ResourcePackLoader.getPackNames(); + public static List getModDataPacks() { + List modpacks = ResourcePackLoader.getDataPackNames(); if (modpacks.isEmpty()) throw new IllegalStateException("Attempted to retrieve mod packs before they were loaded in!"); return modpacks; } - public static List getModPacksWithVanilla() { - List modpacks = getModPacks(); + public static List getModDataPacksWithVanilla() { + List modpacks = getModDataPacks(); modpacks.add("vanilla"); return modpacks; } diff --git a/src/main/java/net/neoforged/neoforge/common/data/ExistingFileHelper.java b/src/main/java/net/neoforged/neoforge/common/data/ExistingFileHelper.java index ead9ebb696..0f1f4c4bf5 100644 --- a/src/main/java/net/neoforged/neoforge/common/data/ExistingFileHelper.java +++ b/src/main/java/net/neoforged/neoforge/common/data/ExistingFileHelper.java @@ -117,9 +117,10 @@ public ExistingFileHelper(Collection existingPacks, final Set exis for (String existingMod : existingMods) { IModFileInfo modFileInfo = ModList.get().getModFileById(existingMod); if (modFileInfo != null) { - PackResources pack = ResourcePackLoader.createPackForMod(modFileInfo); - candidateClientResources.add(pack); - candidateServerResources.add(pack); + // Only opens primary packs - overlays are not currently considered for datagen + final String name = "mod:" + existingMod; + candidateClientResources.add(ResourcePackLoader.createPackForMod(modFileInfo).openPrimary(name)); + candidateServerResources.add(ResourcePackLoader.createPackForMod(modFileInfo).openPrimary(name)); } } diff --git a/src/main/java/net/neoforged/neoforge/common/extensions/IPackResourcesExtension.java b/src/main/java/net/neoforged/neoforge/common/extensions/IPackResourcesExtension.java index 76658ae7c7..6064ace2a2 100644 --- a/src/main/java/net/neoforged/neoforge/common/extensions/IPackResourcesExtension.java +++ b/src/main/java/net/neoforged/neoforge/common/extensions/IPackResourcesExtension.java @@ -5,12 +5,6 @@ package net.neoforged.neoforge.common.extensions; -import java.util.Collection; -import net.minecraft.server.packs.PackResources; -import net.minecraft.server.packs.resources.ResourceManager; -import net.neoforged.neoforge.resource.DelegatingPackResources; -import org.jetbrains.annotations.Nullable; - public interface IPackResourcesExtension { /** * {@return {@code true} if the pack should be hidden from any user interfaces} @@ -18,18 +12,4 @@ public interface IPackResourcesExtension { default boolean isHidden() { return false; } - - /** - * Gets a collection of {@code PackResource} instances nested inside this pack. - * Used to merge several packs into one entry in the resource pack selection UI without - * losing the ability for each pack to return a resource in - * {@link ResourceManager#getResourceStack(ResourceLocation)} - * - * @return Collection of nested {@code PackResource}, or null if this pack has no children - * @see DelegatingPackResources - */ - @Nullable - default Collection getChildren() { - return null; - } } diff --git a/src/main/java/net/neoforged/neoforge/resource/DelegatingPackResources.java b/src/main/java/net/neoforged/neoforge/resource/DelegatingPackResources.java deleted file mode 100644 index 4d63da4b99..0000000000 --- a/src/main/java/net/neoforged/neoforge/resource/DelegatingPackResources.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.neoforged.neoforge.resource; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.packs.AbstractPackResources; -import net.minecraft.server.packs.PackResources; -import net.minecraft.server.packs.PackType; -import net.minecraft.server.packs.metadata.MetadataSectionSerializer; -import net.minecraft.server.packs.metadata.pack.PackMetadataSection; -import net.minecraft.server.packs.resources.IoSupplier; -import org.jetbrains.annotations.Nullable; - -public class DelegatingPackResources extends AbstractPackResources { - private final PackMetadataSection packMeta; - private final List delegates; - private final Map> namespacesAssets; - private final Map> namespacesData; - - public DelegatingPackResources(String packId, boolean isBuiltin, PackMetadataSection packMeta, List packs) { - super(packId, isBuiltin); - this.packMeta = packMeta; - this.delegates = ImmutableList.copyOf(packs); - this.namespacesAssets = this.buildNamespaceMap(PackType.CLIENT_RESOURCES, delegates); - this.namespacesData = this.buildNamespaceMap(PackType.SERVER_DATA, delegates); - } - - private Map> buildNamespaceMap(PackType type, List packList) { - Map> map = new HashMap<>(); - for (PackResources pack : packList) { - for (String namespace : pack.getNamespaces(type)) { - map.computeIfAbsent(namespace, k -> new ArrayList<>()).add(pack); - } - } - map.replaceAll((k, list) -> ImmutableList.copyOf(list)); - return ImmutableMap.copyOf(map); - } - - @SuppressWarnings("unchecked") - @Nullable - @Override - public T getMetadataSection(MetadataSectionSerializer deserializer) throws IOException { - return deserializer.getMetadataSectionName().equals("pack") ? (T) this.packMeta : null; - } - - @Override - public void listResources(PackType type, String resourceNamespace, String paths, ResourceOutput resourceOutput) { - for (PackResources delegate : this.delegates) { - delegate.listResources(type, resourceNamespace, paths, resourceOutput); - } - } - - @Override - public Set getNamespaces(PackType type) { - return type == PackType.CLIENT_RESOURCES ? namespacesAssets.keySet() : namespacesData.keySet(); - } - - @Override - public void close() { - for (PackResources pack : delegates) { - pack.close(); - } - } - - @Nullable - @Override - public IoSupplier getRootResource(String... paths) { - // Root resources do not make sense here - return null; - } - - @Nullable - @Override - public IoSupplier getResource(PackType type, ResourceLocation location) { - for (PackResources pack : getCandidatePacks(type, location)) { - IoSupplier ioSupplier = pack.getResource(type, location); - if (ioSupplier != null) - return ioSupplier; - } - - return null; - } - - @Nullable - public Collection getChildren() { - return delegates; - } - - private List getCandidatePacks(PackType type, ResourceLocation location) { - Map> map = type == PackType.CLIENT_RESOURCES ? namespacesAssets : namespacesData; - List packsWithNamespace = map.get(location.getNamespace()); - return packsWithNamespace == null ? Collections.emptyList() : packsWithNamespace; - } -} diff --git a/src/main/java/net/neoforged/neoforge/resource/EmptyPackResources.java b/src/main/java/net/neoforged/neoforge/resource/EmptyPackResources.java new file mode 100644 index 0000000000..4e140c0c2e --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/resource/EmptyPackResources.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.resource; + +import java.io.InputStream; +import java.util.Collections; +import java.util.Set; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.AbstractPackResources; +import net.minecraft.server.packs.PackResources; +import net.minecraft.server.packs.PackType; +import net.minecraft.server.packs.metadata.MetadataSectionSerializer; +import net.minecraft.server.packs.metadata.pack.PackMetadataSection; +import net.minecraft.server.packs.repository.Pack; +import net.minecraft.server.packs.resources.IoSupplier; +import org.jetbrains.annotations.Nullable; + +public class EmptyPackResources extends AbstractPackResources { + private final PackMetadataSection packMeta; + + public EmptyPackResources(String packId, boolean isBuiltin, PackMetadataSection packMeta) { + super(packId, isBuiltin); + this.packMeta = packMeta; + } + + @SuppressWarnings("unchecked") + @Nullable + @Override + public T getMetadataSection(MetadataSectionSerializer deserializer) { + return deserializer.getMetadataSectionName().equals("pack") ? (T) this.packMeta : null; + } + + @Override + public void close() {} + + @Override + public void listResources(PackType type, String resourceNamespace, String paths, ResourceOutput resourceOutput) {} + + @Override + public Set getNamespaces(PackType type) { + return Collections.emptySet(); + } + + @Nullable + @Override + public IoSupplier getRootResource(String... paths) { + // Root resources do not make sense here + return null; + } + + @Nullable + @Override + public IoSupplier getResource(PackType type, ResourceLocation location) { + return null; + } + + public static class EmptyResourcesSupplier implements Pack.ResourcesSupplier { + private final PackMetadataSection packMeta; + private final boolean isBuiltin; + + public EmptyResourcesSupplier(PackMetadataSection packMeta, boolean isBuiltin) { + this.packMeta = packMeta; + this.isBuiltin = isBuiltin; + } + + @Override + public PackResources openPrimary(String id) { + return new EmptyPackResources(id, isBuiltin, packMeta); + } + + @Override + public PackResources openFull(String id, Pack.Info info) { + return openPrimary(id); + } + } +} diff --git a/src/main/java/net/neoforged/neoforge/resource/ResourcePackLoader.java b/src/main/java/net/neoforged/neoforge/resource/ResourcePackLoader.java index a7c19956ea..7ba5064b85 100644 --- a/src/main/java/net/neoforged/neoforge/resource/ResourcePackLoader.java +++ b/src/main/java/net/neoforged/neoforge/resource/ResourcePackLoader.java @@ -8,54 +8,120 @@ import com.mojang.datafixers.util.Pair; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Optional; +import java.util.*; +import java.util.function.Consumer; import java.util.function.Function; import java.util.stream.Collectors; -import net.minecraft.server.packs.PackResources; +import net.minecraft.SharedConstants; +import net.minecraft.network.chat.Component; +import net.minecraft.server.packs.PackType; import net.minecraft.server.packs.PathPackResources; +import net.minecraft.server.packs.metadata.pack.PackMetadataSection; +import net.minecraft.server.packs.repository.Pack; import net.minecraft.server.packs.repository.PackRepository; +import net.minecraft.server.packs.repository.PackSource; import net.minecraft.server.packs.repository.RepositorySource; -import net.neoforged.fml.ModList; +import net.neoforged.fml.*; import net.neoforged.neoforgespi.language.IModFileInfo; +import net.neoforged.neoforgespi.language.IModInfo; import net.neoforged.neoforgespi.locating.IModFile; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; public class ResourcePackLoader { - private static Map modResourcePacks; + public static final String MOD_DATA_ID = "mod_data"; + public static final String MOD_RESOURCES_ID = "mod_resources"; + private static Map modResourcePacks; + private static final Logger LOGGER = LogManager.getLogger(); - public static Optional getPackFor(String modId) { + public static Optional getPackFor(String modId) { return Optional.ofNullable(ModList.get().getModFileById(modId)).map(IModFileInfo::getFile).map(mf -> modResourcePacks.get(mf)); } - public static void loadResourcePacks(PackRepository resourcePacks, Function, ? extends RepositorySource> packFinder) { - modResourcePacks = ModList.get().getModFiles().stream() - .filter(mf -> mf.requiredLanguageLoaders().stream().noneMatch(ls -> ls.languageName().equals("minecraft"))) - .map(mf -> Pair.of(mf, createPackForMod(mf))) - .collect(Collectors.toMap(p -> p.getFirst().getFile(), Pair::getSecond, (u, v) -> { - throw new IllegalStateException(String.format(Locale.ENGLISH, "Duplicate key %s", u)); - }, LinkedHashMap::new)); + public static void loadResourcePacks(PackRepository resourcePacks, Function, RepositorySource> packFinder) { + findResourcePacks(); resourcePacks.addPackFinder(packFinder.apply(modResourcePacks)); } + private synchronized static void findResourcePacks() { + if (modResourcePacks == null) { + modResourcePacks = ModList.get().getModFiles().stream() + .filter(mf -> mf.requiredLanguageLoaders().stream().noneMatch(ls -> ls.languageName().equals("minecraft"))) + .map(mf -> Pair.of(mf, createPackForMod(mf))) + .collect(Collectors.toMap(p -> p.getFirst().getFile(), Pair::getSecond, (u, v) -> { + throw new IllegalStateException(String.format(Locale.ENGLISH, "Duplicate key %s", u)); + }, LinkedHashMap::new)); + } + } + + public static RepositorySource buildPackFinder(Map modResourcePacks, PackType packType) { + return packAcceptor -> packFinder(modResourcePacks, packAcceptor, packType); + } + + private static void packFinder(Map modResourcePacks, Consumer packAcceptor, PackType packType) { + var hiddenPacks = new ArrayList(); + for (Map.Entry e : modResourcePacks.entrySet()) { + IModInfo mod = e.getKey().getModInfos().get(0); + if ("minecraft".equals(mod.getModId())) continue; // skip the minecraft "mod" + final String name = "mod:" + mod.getModId(); + final String packName = mod.getOwningFile().getFile().getFileName(); + Pack modPack = Pack.readMetaAndCreate( + name, + Component.literal(packName.isEmpty() ? "[unnamed]" : packName), + false, + e.getValue(), + packType, + Pack.Position.BOTTOM, + PackSource.DEFAULT); + if (modPack == null) { + // Vanilla only logs an error, instead of propagating, so handle null and warn that something went wrong + ModLoader.get().addWarning(new ModLoadingWarning(mod, ModLoadingStage.ERROR, "fml.modloading.brokenresources", e.getKey())); + continue; + } + LOGGER.debug(Logging.CORE, "Generating PackInfo named {} for mod file {}", name, e.getKey().getFilePath()); + if ((packType == PackType.CLIENT_RESOURCES && mod.getOwningFile().showAsResourcePack()) || (packType == PackType.SERVER_DATA && mod.getOwningFile().showAsDataPack())) { + packAcceptor.accept(modPack); + } else { + hiddenPacks.add(modPack.hidden()); + } + } + + packAcceptor.accept(makePack(packType, hiddenPacks)); + } + + private static Pack makePack(PackType packType, ArrayList hiddenPacks) { + final String id = packType == PackType.CLIENT_RESOURCES ? MOD_RESOURCES_ID : MOD_DATA_ID; + final String name = packType == PackType.CLIENT_RESOURCES ? "Mod Resources" : "Mod Data"; + final String descriptionKey = packType == PackType.CLIENT_RESOURCES ? "fml.resources.modresources" : "fml.resources.moddata"; + return Pack.readMetaAndCreate(id, Component.literal(name), true, + new EmptyPackResources.EmptyResourcesSupplier(new PackMetadataSection(Component.translatable(descriptionKey, hiddenPacks.size()), + SharedConstants.getCurrentVersion().getPackVersion(packType)), false), + packType, Pack.Position.BOTTOM, PackSource.DEFAULT).withChildren(hiddenPacks); + } + @NotNull - public static PackResources createPackForMod(IModFileInfo mf) { - return new PathPackResources(mf.getFile().getFileName(), mf.getFile().getSecureJar().getRootPath(), true); + public static Pack.ResourcesSupplier createPackForMod(IModFileInfo mf) { + return new PathPackResources.PathResourcesSupplier(mf.getFile().getSecureJar().getRootPath(), true); } - public static List getPackNames() { - return ModList.get().applyForEachModFile(mf -> "mod:" + mf.getModInfos().get(0).getModId()).filter(n -> !n.equals("mod:minecraft")).collect(Collectors.toList()); + public static List getDataPackNames() { + List ids = new ArrayList<>(ModList.get().getModFiles().stream().filter(IModFileInfo::showAsDataPack) + .map(IModFileInfo::getFile) + .map(mf -> "mod:" + mf.getModInfos().get(0).getModId()).filter(n -> !n.equals("mod:minecraft")) + .toList()); + ids.add(MOD_DATA_ID); + return ids; } - public static Comparator> getSorter() { + public static Comparator> getSorter(PackType packType) { List order = new ArrayList<>(); order.add("vanilla"); - order.add("mod_resources"); + if (packType == PackType.CLIENT_RESOURCES) { + order.add(MOD_RESOURCES_ID); + } else { + order.add(MOD_DATA_ID); + } ModList.get().getModFiles().stream() .filter(mf -> mf.requiredLanguageLoaders().stream().noneMatch(ls -> ls.languageName().equals("minecraft"))) diff --git a/src/main/java/net/neoforged/neoforge/server/ServerLifecycleHooks.java b/src/main/java/net/neoforged/neoforge/server/ServerLifecycleHooks.java index 89ddf2d8f6..ee435b5072 100644 --- a/src/main/java/net/neoforged/neoforge/server/ServerLifecycleHooks.java +++ b/src/main/java/net/neoforged/neoforge/server/ServerLifecycleHooks.java @@ -10,12 +10,8 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Objects; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Consumer; import net.minecraft.core.Holder; import net.minecraft.core.RegistryAccess; import net.minecraft.core.registries.Registries; @@ -27,17 +23,7 @@ import net.minecraft.network.protocol.handshake.ClientIntentionPacket; import net.minecraft.network.protocol.login.ClientboundLoginDisconnectPacket; import net.minecraft.server.MinecraftServer; -import net.minecraft.server.packs.PackResources; -import net.minecraft.server.packs.PackType; -import net.minecraft.server.packs.repository.BuiltInPackSource; -import net.minecraft.server.packs.repository.Pack; -import net.minecraft.server.packs.repository.PackSource; -import net.minecraft.server.packs.repository.RepositorySource; import net.minecraft.world.level.storage.LevelResource; -import net.neoforged.fml.Logging; -import net.neoforged.fml.ModLoader; -import net.neoforged.fml.ModLoadingStage; -import net.neoforged.fml.ModLoadingWarning; import net.neoforged.fml.config.ConfigTracker; import net.neoforged.fml.config.ModConfig; import net.neoforged.fml.loading.FMLEnvironment; @@ -60,13 +46,10 @@ import net.neoforged.neoforge.registries.NeoForgeRegistries.Keys; import net.neoforged.neoforge.registries.RegistryManager; import net.neoforged.neoforge.server.permission.PermissionAPI; -import net.neoforged.neoforgespi.language.IModInfo; -import net.neoforged.neoforgespi.locating.IModFile; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Marker; import org.apache.logging.log4j.MarkerManager; -import org.jetbrains.annotations.ApiStatus; public class ServerLifecycleHooks { private static final Logger LOGGER = LogManager.getLogger(); @@ -199,27 +182,6 @@ public static void handleExit(int retVal) { System.exit(retVal); } - @ApiStatus.Internal - public static RepositorySource buildPackFinder(Map modResourcePacks) { - return packAcceptor -> serverPackFinder(modResourcePacks, packAcceptor); - } - - private static void serverPackFinder(Map modResourcePacks, Consumer packAcceptor) { - for (Entry e : modResourcePacks.entrySet()) { - IModInfo mod = e.getKey().getModInfos().get(0); - if (Objects.equals(mod.getModId(), "minecraft")) continue; // skip the minecraft "mod" - final String name = "mod:" + mod.getModId(); - final Pack modPack = Pack.readMetaAndCreate(name, Component.literal(e.getValue().packId()), false, BuiltInPackSource.fixedResources(e.getValue()), PackType.SERVER_DATA, Pack.Position.BOTTOM, PackSource.DEFAULT); - if (modPack == null) { - // Vanilla only logs an error, instead of propagating, so handle null and warn that something went wrong - ModLoader.get().addWarning(new ModLoadingWarning(mod, ModLoadingStage.ERROR, "fml.modloading.brokenresources", e.getKey())); - continue; - } - LOGGER.debug(Logging.CORE, "Generating PackInfo named {} for mod file {}", name, e.getKey().getFilePath()); - packAcceptor.accept(modPack); - } - } - private static void runModifiers(final MinecraftServer server) { final RegistryAccess registries = server.registryAccess(); diff --git a/src/main/resources/assets/neoforge/lang/en_us.json b/src/main/resources/assets/neoforge/lang/en_us.json index 82f2e254d5..e2d0995079 100644 --- a/src/main/resources/assets/neoforge/lang/en_us.json +++ b/src/main/resources/assets/neoforge/lang/en_us.json @@ -81,6 +81,7 @@ "fml.modloading.brokenresources": "File {2} failed to load a valid ResourcePackInfo", "fml.modloading.missinglicense": "Missing License Information in file {3}", "fml.resources.modresources": "Resources for {0} mod files", + "fml.resources.moddata": "Data for {0} mod files", "fml.messages.artifactversion.ornotinstalled":"{0,ornull,fml.messages.artifactversion.notinstalled}", "fml.messages.artifactversion":"{0,ornull,fml.messages.artifactversion.none}", diff --git a/tests/src/generated/resources/pack.mcmeta b/tests/src/generated/resources/pack.mcmeta index 94ca988b2b..188ccf2149 100644 --- a/tests/src/generated/resources/pack.mcmeta +++ b/tests/src/generated/resources/pack.mcmeta @@ -1,4 +1,15 @@ { + "overlays": { + "entries": [ + { + "directory": "pack_overlays_test", + "formats": [ + 0, + 2147483647 + ] + } + ] + }, "pack": { "description": "NeoForge tests resource pack", "neoforge": { diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/resources/OverlayTests.java b/tests/src/main/java/net/neoforged/neoforge/debug/resources/OverlayTests.java new file mode 100644 index 0000000000..43c58c44ad --- /dev/null +++ b/tests/src/main/java/net/neoforged/neoforge/debug/resources/OverlayTests.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.debug.resources; + +import net.minecraft.core.registries.Registries; +import net.minecraft.gametest.framework.GameTest; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagKey; +import net.minecraft.world.level.block.Blocks; +import net.neoforged.testframework.DynamicTest; +import net.neoforged.testframework.annotation.ForEachTest; +import net.neoforged.testframework.annotation.TestHolder; +import net.neoforged.testframework.gametest.EmptyTemplate; + +@ForEachTest(groups = OverlayTests.GROUP) +public class OverlayTests { + public static final String GROUP = "resources"; + + @GameTest + @EmptyTemplate + @TestHolder(description = "Tests if pack overlays from mods work") + static void packOverlay(final DynamicTest test) { + var tagKey = TagKey.create(Registries.BLOCK, new ResourceLocation("pack_overlays_test", "must_be_overlayed")); + test.onGameTest(helper -> { + helper.assertTrue(Blocks.DIAMOND_BLOCK.defaultBlockState().is(tagKey), "Overlay was not applied"); + helper.assertFalse(Blocks.COBBLESTONE.defaultBlockState().is(tagKey), "File under overlay was applied"); + helper.succeed(); + }); + } +} diff --git a/tests/src/main/java/net/neoforged/neoforge/oldtest/DataGeneratorTest.java b/tests/src/main/java/net/neoforged/neoforge/oldtest/DataGeneratorTest.java index 0374178d1b..10015013ea 100644 --- a/tests/src/main/java/net/neoforged/neoforge/oldtest/DataGeneratorTest.java +++ b/tests/src/main/java/net/neoforged/neoforge/oldtest/DataGeneratorTest.java @@ -40,6 +40,7 @@ import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.OverlayMetadataSection; import net.minecraft.server.packs.PackType; import net.minecraft.server.packs.metadata.pack.PackMetadataSection; import net.minecraft.server.packs.resources.Resource; @@ -103,6 +104,8 @@ public static void gatherData(GatherDataEvent event) { CompletableFuture lookupProvider = event.getLookupProvider(); gen.addProvider(true, new PackMetadataGenerator(packOutput) + .add(OverlayMetadataSection.TYPE, new OverlayMetadataSection(List.of( + new OverlayMetadataSection.OverlayEntry(new InclusiveRange<>(0, Integer.MAX_VALUE), "pack_overlays_test")))) .add(PackMetadataSection.TYPE, new PackMetadataSection( Component.literal("NeoForge tests resource pack"), DetectedVersion.BUILT_IN.getPackVersion(PackType.CLIENT_RESOURCES), diff --git a/tests/src/main/resources/data/pack_overlays_test/tags/blocks/must_be_overlayed.json b/tests/src/main/resources/data/pack_overlays_test/tags/blocks/must_be_overlayed.json new file mode 100644 index 0000000000..0865115273 --- /dev/null +++ b/tests/src/main/resources/data/pack_overlays_test/tags/blocks/must_be_overlayed.json @@ -0,0 +1,5 @@ +{ + "values": [ + "minecraft:cobblestone" + ] +} diff --git a/tests/src/main/resources/pack_overlays_test/data/pack_overlays_test/tags/blocks/must_be_overlayed.json b/tests/src/main/resources/pack_overlays_test/data/pack_overlays_test/tags/blocks/must_be_overlayed.json new file mode 100644 index 0000000000..ad41f7f735 --- /dev/null +++ b/tests/src/main/resources/pack_overlays_test/data/pack_overlays_test/tags/blocks/must_be_overlayed.json @@ -0,0 +1,5 @@ +{ + "values": [ + "minecraft:diamond_block" + ] +}