mirror of
https://github.com/FabricMC/fabric.git
synced 2024-11-14 19:25:23 -05:00
Refactor resource loader internals (#3473)
* First step toward fixing resource pack grouping
* Placeholder pack and pack dependency
* Various fixes
* Fix wrong variable in serialization code
* Hide packs in PackScreen and DatapackCommand
* Apparently Japanese people aren't alone in having their currency signs used as special chars...
* Inject directly to Pack
* Add temporary logging, fix bug
* Add proper sorting
* Improve logging
* Fix duplicate name registration
* Fix client pack handling
* Fix FMJ
* Stop using interface injection for internal interface
* Delete unused GroupResourcePack
* Move refreshAutoEnabledPacks to util
* Improve logging
* Make a few things private
* Use vanilla metadata serialization logic
* Improve javadoc
* Add junit test
* Some final refactors
* Update ja_jp.json
---------
Co-authored-by: modmuss <modmuss50@gmail.com>
(cherry picked from commit 707e4d1bfd
)
This commit is contained in:
parent
bbf5165de7
commit
3feeb09499
29 changed files with 931 additions and 589 deletions
|
@ -1,63 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.impl.client.resource.loader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.resource.AbstractFileResourcePack;
|
||||
import net.minecraft.resource.InputSupplier;
|
||||
import net.minecraft.resource.ResourceType;
|
||||
import net.minecraft.resource.metadata.ResourceMetadataReader;
|
||||
|
||||
import net.fabricmc.fabric.api.resource.ModResourcePack;
|
||||
import net.fabricmc.fabric.impl.resource.loader.GroupResourcePack;
|
||||
|
||||
/**
|
||||
* Represents a vanilla built-in resource pack with support for modded content.
|
||||
*
|
||||
* <p>Vanilla resources are provided as usual through the original resource pack (if not overridden),
|
||||
* all other resources will be searched for in the provided modded resource packs.</p>
|
||||
*/
|
||||
public class FabricWrappedVanillaResourcePack extends GroupResourcePack {
|
||||
private final AbstractFileResourcePack originalResourcePack;
|
||||
|
||||
public FabricWrappedVanillaResourcePack(AbstractFileResourcePack originalResourcePack, List<ModResourcePack> modResourcePacks) {
|
||||
// Mod resource packs have higher priority, add them last (so vanilla assets can be overridden)
|
||||
super(ResourceType.CLIENT_RESOURCES, Stream.concat(Stream.of(originalResourcePack), modResourcePacks.stream()).toList());
|
||||
this.originalResourcePack = originalResourcePack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputSupplier<InputStream> openRoot(String... pathSegments) {
|
||||
return this.originalResourcePack.openRoot(pathSegments);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> @Nullable T parseMetadata(ResourceMetadataReader<T> metaReader) throws IOException {
|
||||
return this.originalResourcePack.parseMetadata(metaReader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return this.originalResourcePack.getName();
|
||||
}
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.mixin.resource.loader.client;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
||||
|
||||
import net.minecraft.client.resource.DefaultClientResourcePackProvider;
|
||||
import net.minecraft.resource.AbstractFileResourcePack;
|
||||
import net.minecraft.resource.ResourcePack;
|
||||
import net.minecraft.resource.ResourcePackProfile;
|
||||
import net.minecraft.resource.ResourcePackSource;
|
||||
import net.minecraft.resource.ResourceType;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
import net.fabricmc.fabric.api.resource.ModResourcePack;
|
||||
import net.fabricmc.fabric.impl.client.resource.loader.FabricWrappedVanillaResourcePack;
|
||||
import net.fabricmc.fabric.impl.resource.loader.ModResourcePackUtil;
|
||||
|
||||
@Mixin(DefaultClientResourcePackProvider.class)
|
||||
public class DefaultClientResourcePackProviderMixin {
|
||||
/**
|
||||
* Injects into the method which registers/creates vanilla built-in resource packs,
|
||||
* and replaces the local {@link net.minecraft.resource.ResourcePackProfile.PackFactory}
|
||||
* instance with our custom wrapper that supports loading from mods.
|
||||
*/
|
||||
@ModifyArg(
|
||||
method = "create(Ljava/lang/String;Lnet/minecraft/resource/ResourcePackProfile$PackFactory;Lnet/minecraft/text/Text;)Lnet/minecraft/resource/ResourcePackProfile;",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/resource/ResourcePackProfile;create(Ljava/lang/String;Lnet/minecraft/text/Text;ZLnet/minecraft/resource/ResourcePackProfile$PackFactory;Lnet/minecraft/resource/ResourceType;Lnet/minecraft/resource/ResourcePackProfile$InsertionPosition;Lnet/minecraft/resource/ResourcePackSource;)Lnet/minecraft/resource/ResourcePackProfile;"
|
||||
),
|
||||
index = 3
|
||||
)
|
||||
private ResourcePackProfile.PackFactory onCreateVanillaBuiltinResourcePack(String name, Text displayName, boolean alwaysEnabled,
|
||||
ResourcePackProfile.PackFactory packFactory, ResourceType type, ResourcePackProfile.InsertionPosition position, ResourcePackSource source) {
|
||||
return new ResourcePackProfile.PackFactory() {
|
||||
@Override
|
||||
public ResourcePack open(String name) {
|
||||
return new FabricWrappedVanillaResourcePack((AbstractFileResourcePack) packFactory.open(name), getModResourcePacks(name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourcePack openWithOverlays(String string, ResourcePackProfile.Metadata metadata) {
|
||||
// VanillaResourcePackProvider does not handle overlays
|
||||
return open(name);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return all baked-in mod resource packs that provide resources in the specified subPath}.
|
||||
*/
|
||||
private static List<ModResourcePack> getModResourcePacks(String subPath) {
|
||||
List<ModResourcePack> packs = new ArrayList<>();
|
||||
ModResourcePackUtil.appendModResourcePacks(packs, ResourceType.CLIENT_RESOURCES, subPath);
|
||||
return packs;
|
||||
}
|
||||
}
|
|
@ -99,7 +99,7 @@ public class GameOptionsMixin {
|
|||
|
||||
for (ResourcePackProfile profile : profiles) {
|
||||
// Always add "Fabric Mods" pack to enabled resource packs.
|
||||
if (profile.getSource() == ModResourcePackCreator.RESOURCE_PACK_SOURCE) {
|
||||
if (profile.getName().equals(ModResourcePackCreator.FABRIC)) {
|
||||
resourcePacks.add(profile.getName());
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.mixin.resource.loader.client;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.resource.ResourcePackManager;
|
||||
import net.minecraft.resource.ResourcePackProfile;
|
||||
|
||||
import net.fabricmc.fabric.impl.resource.loader.FabricResourcePackProfile;
|
||||
|
||||
/**
|
||||
* Mixins to the anonymous class in #write method.
|
||||
*/
|
||||
@Mixin(targets = "net/minecraft/client/option/GameOptions$3")
|
||||
public class GameOptionsWriteVisitorMixin {
|
||||
@Unique
|
||||
private static List<String> toPackListString(List<String> packs) {
|
||||
List<String> copy = new ArrayList<>(packs.size());
|
||||
ResourcePackManager manager = MinecraftClient.getInstance().getResourcePackManager();
|
||||
|
||||
for (String pack : packs) {
|
||||
ResourcePackProfile profile = manager.getProfile(pack);
|
||||
|
||||
// Nonexistent pack profiles should be handled in the same way as vanilla
|
||||
if (profile == null || !((FabricResourcePackProfile) profile).fabric_isHidden()) copy.add(pack);
|
||||
}
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@ModifyArg(method = "visitObject", at = @At(value = "INVOKE", target = "Ljava/util/function/Function;apply(Ljava/lang/Object;)Ljava/lang/Object;"))
|
||||
private <T> T skipHiddenPacks(T value, @Local String key) {
|
||||
if ("resourcePacks".equals(key) && value instanceof List) {
|
||||
return (T) toPackListString((List<String>) value);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.mixin.resource.loader.client;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import net.minecraft.client.gui.screen.pack.ResourcePackOrganizer;
|
||||
import net.minecraft.resource.ResourcePackManager;
|
||||
import net.minecraft.resource.ResourcePackProfile;
|
||||
|
||||
import net.fabricmc.fabric.impl.resource.loader.FabricResourcePackProfile;
|
||||
|
||||
@Mixin(ResourcePackOrganizer.class)
|
||||
public class ResourcePackOrganizerMixin {
|
||||
@Shadow
|
||||
@Final
|
||||
List<ResourcePackProfile> enabledPacks;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
List<ResourcePackProfile> disabledPacks;
|
||||
|
||||
/**
|
||||
* Do not list hidden packs in either enabledPacks or disabledPacks.
|
||||
* They are managed entirely by ResourcePackManager on save, and are invisible to client.
|
||||
*/
|
||||
@Inject(method = "<init>", at = @At("TAIL"))
|
||||
private void removeHiddenPacksInit(Runnable updateCallback, Function iconIdSupplier, ResourcePackManager resourcePackManager, Consumer applier, CallbackInfo ci) {
|
||||
this.enabledPacks.removeIf(profile -> ((FabricResourcePackProfile) profile).fabric_isHidden());
|
||||
this.disabledPacks.removeIf(profile -> ((FabricResourcePackProfile) profile).fabric_isHidden());
|
||||
}
|
||||
|
||||
@Inject(method = "refresh", at = @At("TAIL"))
|
||||
private void removeHiddenPacksRefresh(CallbackInfo ci) {
|
||||
this.enabledPacks.removeIf(profile -> ((FabricResourcePackProfile) profile).fabric_isHidden());
|
||||
this.disabledPacks.removeIf(profile -> ((FabricResourcePackProfile) profile).fabric_isHidden());
|
||||
}
|
||||
}
|
|
@ -3,12 +3,13 @@
|
|||
"package": "net.fabricmc.fabric.mixin.resource.loader.client",
|
||||
"compatibilityLevel": "JAVA_17",
|
||||
"client": [
|
||||
"VanillaResourcePackProviderMixin",
|
||||
"DefaultClientResourcePackProviderMixin",
|
||||
"CreateWorldScreenMixin",
|
||||
"FontManagerMixin",
|
||||
"GameOptionsMixin",
|
||||
"KeyedResourceReloadListenerClientMixin"
|
||||
"KeyedResourceReloadListenerClientMixin",
|
||||
"ResourcePackOrganizerMixin",
|
||||
"VanillaResourcePackProviderMixin",
|
||||
"GameOptionsWriteVisitorMixin"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
|
|
|
@ -29,4 +29,6 @@ public interface ModResourcePack extends ResourcePack {
|
|||
* resource pack.
|
||||
*/
|
||||
ModMetadata getFabricModMetadata();
|
||||
|
||||
ModResourcePack createOverlay(String overlay);
|
||||
}
|
||||
|
|
|
@ -17,27 +17,48 @@
|
|||
/**
|
||||
* The Resource Loader, version 0.
|
||||
*
|
||||
* <p><h3>Quick note about vocabulary in Resource Loader and Minecraft:</h3>
|
||||
* <ul>
|
||||
* <li>Resource Pack refers to both client-sided resource pack and data pack.</li>
|
||||
* <li>Virtual Resource Pack refers to a resource pack that may be generated at runtime, or simply doesn't exist directly on disk.</li>
|
||||
* <li>Group Resource Pack refers to a virtual resource pack that groups multiple resource packs together.</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
* <p><h3>Quick note about vocabulary in Minecraft:</h3></p>
|
||||
* <p>Resource Pack refers to both client-sided resource pack and data pack.</p>
|
||||
*
|
||||
* <p><h3>Modded Resource Pack Handling</h3></p>
|
||||
* <p>The Resource Loader will create a resource pack for each mod that provides resources in {@code assets} or {@code data}
|
||||
* sub-directories.
|
||||
* Those mod resource packs are grouped into a single always-enabled group resource pack which is shown in the resource pack screen.</p>
|
||||
* <p>There are two types of resource packs that mods can provide.</p>
|
||||
*
|
||||
* <p><h4>Bundled Resource Pack</h4></p>
|
||||
* <p>Mods can "bundle" resource packs with the mod by putting files inside the {@code assets} or {@code data}
|
||||
* sub-directories of the {@code resources} directory. They are always enabled, initially loaded after the vanilla pack,
|
||||
* and cannot be disabled. Individual mods' packs are hidden to users in the Resource Pack option screen or the {@code /datapack}
|
||||
* command. Instead, a placeholder pack, named "Fabric Mods", will be displayed. This pack, with the ID {@code fabric}, provides
|
||||
* no resource itself; it is merely a marker used internally.</p>
|
||||
*
|
||||
* <p><h4>Built-in Mod Resource Pack</h4></p>
|
||||
* <p>The Resource Loader adds manually registered mod resource packs. Those resource packs are registered with
|
||||
* {@link net.fabricmc.fabric.api.resource.ResourceManagerHelper#registerBuiltinResourcePack(net.minecraft.util.Identifier, net.fabricmc.loader.api.ModContainer, net.fabricmc.fabric.api.resource.ResourcePackActivationType)}</p>
|
||||
* <p>The Resource Loader adds manually registered mod resource packs. Those resource packs are located inside the
|
||||
* {@code resources/resourcepacks/} directory. For example, a built-in data pack with the ID {@code example:test} should be placed
|
||||
* under {@code resources/resourcepacks/test/data/}. The packs are then registered with
|
||||
* {@link net.fabricmc.fabric.api.resource.ResourceManagerHelper#registerBuiltinResourcePack(net.minecraft.util.Identifier, net.fabricmc.loader.api.ModContainer, net.fabricmc.fabric.api.resource.ResourcePackActivationType)}.
|
||||
* Users can manually enable or disable the packs, unless it is specified to be always enabled.</p>
|
||||
*
|
||||
* <p><h4>Vanilla Built-in Resource Packs</h4></p>
|
||||
* <p>The Resource Loader will inject resources into the Programmer Art and High Contrast resource packs for each mod
|
||||
* that provides resources in the {@code programmer_art} or {@code high_contrast} top-level directory of the mod
|
||||
* whose structure is similar to a normal resource pack.</p>
|
||||
* <p><h4>Programmer Art and High Contrast Support</h4></p>
|
||||
* <p>Bundled resource packs support Programmer Art and High Contrast vanilla resource packs. Simply place assets
|
||||
* under {@code resources/programmer_art/assets/} or {@code resources/high_contrast/assets/}, respectively.
|
||||
* Internally, these are treated as a separate internal pack, loaded just after the respective vanilla pack.
|
||||
* Toggling the vanilla packs automatically toggles the bundled ones as well; you cannot separately enable or disable them.</p>
|
||||
*
|
||||
* <p><h4>Example</h4></p>
|
||||
* <p>Mod A ({@code mod_a} provides a bundled resource pack with both Programmer Art and High Contrast support.
|
||||
* Mod B ({@code mod_b}) provides a bundled resource pack and one built-in resource pack, Extra ({@code mod_b:extra}).
|
||||
* When neither the Programmer Art nor High Contrast is enabled, the user sees "Vanilla", "Fabric Mods", and "Extra" in the
|
||||
* Resource Packs screen. Internally, between Fabric Mods and Extra packs, two hidden packs exist: {@code mod_a} and {@code mod_b}.</p>
|
||||
*
|
||||
* <p>Suppose the user then enables both the Programmer Art and High Contrast, and the Resource Packs screen lists
|
||||
* "Vanilla", "Fabric", Programmer Art, "Extra", and High Contrast. Internally, there are 4 hidden packs:</p>
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@code mod_a} and {@code mod_b} between "Fabric" and Programmer Art.</li>
|
||||
* <li>{@code mod_a_programmer_art}, containing Mod A's Programmer Art assets, just after Programmer Art pack.</li>
|
||||
* <li>{@code mod_a_high_contrast}, containing Mod A's High Contrast assets, just after High Contrast pack.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Note that while the current behavior is to sort bundled resource packs by mod ID in descending order (A to Z), this may change over time.</p>
|
||||
*
|
||||
* <p><h3>Resource Reload Listener</h3></p>
|
||||
* <p>The Resource Loader allows mods to register resource reload listeners through
|
||||
|
|
|
@ -1,86 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.impl.resource.loader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.SharedConstants;
|
||||
import net.minecraft.resource.AbstractFileResourcePack;
|
||||
import net.minecraft.resource.InputSupplier;
|
||||
import net.minecraft.resource.ResourceType;
|
||||
import net.minecraft.resource.metadata.ResourceMetadataReader;
|
||||
|
||||
import net.fabricmc.fabric.api.resource.ModResourcePack;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
|
||||
/**
|
||||
* The Fabric mods resource pack, holds all the mod resource packs as one pack.
|
||||
*/
|
||||
public class FabricModResourcePack extends GroupResourcePack {
|
||||
public FabricModResourcePack(ResourceType type, List<ModResourcePack> packs) {
|
||||
super(type, packs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputSupplier<InputStream> openRoot(String... pathSegments) {
|
||||
String fileName = String.join("/", pathSegments);
|
||||
|
||||
if ("pack.mcmeta".equals(fileName)) {
|
||||
String description = "pack.description.modResources";
|
||||
String fallback = "Mod resources.";
|
||||
String pack = String.format("{\"pack\":{\"pack_format\":" + SharedConstants.getGameVersion().getResourceVersion(type) + ",\"description\":{\"translate\":\"%s\",\"fallback\":\"%s.\"}}}", description, fallback);
|
||||
return () -> IOUtils.toInputStream(pack, Charsets.UTF_8);
|
||||
} else if ("pack.png".equals(fileName)) {
|
||||
return FabricLoader.getInstance().getModContainer("fabric-resource-loader-v0")
|
||||
.flatMap(container -> container.getMetadata().getIconPath(512).flatMap(container::findPath))
|
||||
.map(path -> (InputSupplier<InputStream>) (() -> Files.newInputStream(path)))
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> @Nullable T parseMetadata(ResourceMetadataReader<T> metaReader) throws IOException {
|
||||
InputSupplier<InputStream> inputSupplier = this.openRoot("pack.mcmeta");
|
||||
|
||||
if (inputSupplier != null) {
|
||||
try (InputStream input = inputSupplier.get()) {
|
||||
return AbstractFileResourcePack.parseMetadata(metaReader, input);
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "fabric";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAlwaysStable() {
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.impl.resource.loader;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* Fabric addition to ResourcePackProfile.
|
||||
* @see ModResourcePackCreator
|
||||
*/
|
||||
public interface FabricResourcePackProfile {
|
||||
/**
|
||||
* Returns whether the pack is internal and hidden from end users.
|
||||
*/
|
||||
default boolean fabric_isHidden() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether every parent is enabled. If this is not empty, the pack's status
|
||||
* is synced to that of the parent pack(s), where the pack gets enabled if and only
|
||||
* if each of the parent is enabled. Note that non-Fabric packs always return {@code true}.
|
||||
*
|
||||
* @return whether every parent is enabled.
|
||||
*/
|
||||
default boolean fabric_parentsEnabled(Set<String> enabled) {
|
||||
return true;
|
||||
}
|
||||
|
||||
default void fabric_setParentsPredicate(Predicate<Set<String>> predicate) {
|
||||
}
|
||||
}
|
|
@ -1,123 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.impl.resource.loader;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
|
||||
import net.minecraft.resource.InputSupplier;
|
||||
import net.minecraft.resource.NamespaceResourceManager;
|
||||
import net.minecraft.resource.Resource;
|
||||
import net.minecraft.resource.ResourcePack;
|
||||
import net.minecraft.resource.ResourceType;
|
||||
import net.minecraft.resource.metadata.ResourceMetadata;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
/**
|
||||
* Represents a group resource pack, holds multiple resource packs as one.
|
||||
*/
|
||||
public abstract class GroupResourcePack implements ResourcePack {
|
||||
protected final ResourceType type;
|
||||
protected final List<? extends ResourcePack> packs;
|
||||
protected final Map<String, List<ResourcePack>> namespacedPacks = new Object2ObjectOpenHashMap<>();
|
||||
|
||||
public GroupResourcePack(ResourceType type, List<? extends ResourcePack> packs) {
|
||||
this.type = type;
|
||||
this.packs = packs;
|
||||
this.packs.forEach(pack -> pack.getNamespaces(this.type)
|
||||
.forEach(namespace -> this.namespacedPacks.computeIfAbsent(namespace, value -> new ArrayList<>())
|
||||
.add(pack)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputSupplier<InputStream> open(ResourceType type, Identifier id) {
|
||||
List<? extends ResourcePack> packs = this.namespacedPacks.get(id.getNamespace());
|
||||
|
||||
if (packs != null) {
|
||||
// Last to first, since higher priority packs are at the end
|
||||
for (int i = packs.size() - 1; i >= 0; i--) {
|
||||
ResourcePack pack = packs.get(i);
|
||||
InputSupplier<InputStream> supplier = pack.open(type, id);
|
||||
|
||||
if (supplier != null) {
|
||||
return supplier;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void findResources(ResourceType type, String namespace, String prefix, ResultConsumer consumer) {
|
||||
List<? extends ResourcePack> packs = this.namespacedPacks.get(namespace);
|
||||
|
||||
if (packs == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// First to last, since later calls override previously returned data
|
||||
for (ResourcePack pack : packs) {
|
||||
pack.findResources(type, namespace, prefix, consumer);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getNamespaces(ResourceType type) {
|
||||
return this.namespacedPacks.keySet();
|
||||
}
|
||||
|
||||
public void appendResources(ResourceType type, Identifier id, List<Resource> resources) {
|
||||
List<? extends ResourcePack> packs = this.namespacedPacks.get(id.getNamespace());
|
||||
|
||||
if (packs == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Identifier metadataId = NamespaceResourceManager.getMetadataPath(id);
|
||||
|
||||
// Last to first, since higher priority packs are at the end
|
||||
for (int i = packs.size() - 1; i >= 0; i--) {
|
||||
ResourcePack pack = packs.get(i);
|
||||
InputSupplier<InputStream> supplier = pack.open(type, id);
|
||||
|
||||
if (supplier != null) {
|
||||
InputSupplier<ResourceMetadata> metadataSupplier = () -> {
|
||||
InputSupplier<InputStream> rawMetadataSupplier = pack.open(this.type, metadataId);
|
||||
return rawMetadataSupplier != null ? NamespaceResourceManager.loadMetadata(rawMetadataSupplier) : ResourceMetadata.NONE;
|
||||
};
|
||||
|
||||
resources.add(new Resource(pack, supplier, metadataSupplier));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String getFullName() {
|
||||
return this.getName() + " (" + this.packs.stream().map(ResourcePack::getName).collect(Collectors.joining(", ")) + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
this.packs.forEach(ResourcePack::close);
|
||||
}
|
||||
}
|
|
@ -33,6 +33,7 @@ import java.util.HashSet;
|
|||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
@ -59,14 +60,18 @@ public class ModNioResourcePack implements ResourcePack, ModResourcePack {
|
|||
private static final FileSystem DEFAULT_FS = FileSystems.getDefault();
|
||||
|
||||
private final String id;
|
||||
private final ModMetadata modInfo;
|
||||
private final ModContainer mod;
|
||||
private final List<Path> basePaths;
|
||||
private final ResourceType type;
|
||||
private final AutoCloseable closer;
|
||||
private final ResourcePackActivationType activationType;
|
||||
private final Map<ResourceType, Set<String>> namespaces;
|
||||
/**
|
||||
* Whether the pack is bundled and loaded by default, as opposed to registered built-in packs.
|
||||
* @see ModResourcePackUtil#appendModResourcePacks(List, ResourceType, String)
|
||||
*/
|
||||
private final boolean modBundled;
|
||||
|
||||
public static ModNioResourcePack create(String id, ModContainer mod, String subPath, ResourceType type, ResourcePackActivationType activationType) {
|
||||
public static ModNioResourcePack create(String id, ModContainer mod, String subPath, ResourceType type, ResourcePackActivationType activationType, boolean modBundled) {
|
||||
List<Path> rootPaths = mod.getRootPaths();
|
||||
List<Path> paths;
|
||||
|
||||
|
@ -89,19 +94,27 @@ public class ModNioResourcePack implements ResourcePack, ModResourcePack {
|
|||
|
||||
if (paths.isEmpty()) return null;
|
||||
|
||||
ModNioResourcePack ret = new ModNioResourcePack(id, mod.getMetadata(), paths, type, null, activationType);
|
||||
ModNioResourcePack ret = new ModNioResourcePack(id, mod, paths, type, activationType, modBundled);
|
||||
|
||||
return ret.getNamespaces(type).isEmpty() ? null : ret;
|
||||
}
|
||||
|
||||
private ModNioResourcePack(String id, ModMetadata modInfo, List<Path> paths, ResourceType type, AutoCloseable closer, ResourcePackActivationType activationType) {
|
||||
private ModNioResourcePack(String id, ModContainer mod, List<Path> paths, ResourceType type, ResourcePackActivationType activationType, boolean modBundled) {
|
||||
this.id = id;
|
||||
this.modInfo = modInfo;
|
||||
this.mod = mod;
|
||||
this.basePaths = paths;
|
||||
this.type = type;
|
||||
this.closer = closer;
|
||||
this.activationType = activationType;
|
||||
this.namespaces = readNamespaces(paths, modInfo.getId());
|
||||
this.modBundled = modBundled;
|
||||
this.namespaces = readNamespaces(paths, mod.getMetadata().getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModNioResourcePack createOverlay(String overlay) {
|
||||
// See DirectoryResourcePack.
|
||||
return new ModNioResourcePack(id, mod, basePaths.stream().map(
|
||||
path -> path.resolve(overlay)
|
||||
).toList(), type, activationType, modBundled);
|
||||
}
|
||||
|
||||
static Map<ResourceType, Set<String>> readNamespaces(List<Path> paths, String modId) {
|
||||
|
@ -188,8 +201,8 @@ public class ModNioResourcePack implements ResourcePack, ModResourcePack {
|
|||
return () -> Files.newInputStream(path);
|
||||
}
|
||||
|
||||
if (ModResourcePackUtil.containsDefault(this.modInfo, filename)) {
|
||||
return () -> ModResourcePackUtil.openDefault(this.modInfo, this.type, filename);
|
||||
if (ModResourcePackUtil.containsDefault(filename, this.modBundled)) {
|
||||
return () -> ModResourcePackUtil.openDefault(this.mod, this.type, filename);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -239,7 +252,7 @@ public class ModNioResourcePack implements ResourcePack, ModResourcePack {
|
|||
}
|
||||
});
|
||||
} catch (IOException e) {
|
||||
LOGGER.warn("findResources at " + path + " in namespace " + namespace + ", mod " + modInfo.getId() + " failed!", e);
|
||||
LOGGER.warn("findResources at " + path + " in namespace " + namespace + ", mod " + mod.getMetadata().getId() + " failed!", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -251,25 +264,18 @@ public class ModNioResourcePack implements ResourcePack, ModResourcePack {
|
|||
|
||||
@Override
|
||||
public <T> T parseMetadata(ResourceMetadataReader<T> metaReader) throws IOException {
|
||||
try (InputStream is = openFile("pack.mcmeta").get()) {
|
||||
try (InputStream is = Objects.requireNonNull(openFile("pack.mcmeta")).get()) {
|
||||
return AbstractFileResourcePack.parseMetadata(metaReader, is);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (closer != null) {
|
||||
try {
|
||||
closer.close();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModMetadata getFabricModMetadata() {
|
||||
return modInfo;
|
||||
return mod.getMetadata();
|
||||
}
|
||||
|
||||
public ResourcePackActivationType getActivationType() {
|
||||
|
@ -281,6 +287,11 @@ public class ModNioResourcePack implements ResourcePack, ModResourcePack {
|
|||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAlwaysStable() {
|
||||
return this.modBundled;
|
||||
}
|
||||
|
||||
private static boolean exists(Path path) {
|
||||
// NIO Files.exists is notoriously slow when checking the file system
|
||||
return path.getFileSystem() == DEFAULT_FS ? path.toFile().exists() : Files.exists(path);
|
||||
|
|
|
@ -18,15 +18,17 @@ package net.fabricmc.fabric.impl.resource.loader;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.VisibleForTesting;
|
||||
|
||||
import net.minecraft.resource.OverlayResourcePack;
|
||||
import net.minecraft.resource.ResourcePack;
|
||||
import net.minecraft.resource.ResourcePackProfile;
|
||||
import net.minecraft.resource.ResourcePackProvider;
|
||||
import net.minecraft.resource.ResourcePackSource;
|
||||
import net.minecraft.resource.ResourceType;
|
||||
import net.minecraft.text.MutableText;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
import net.fabricmc.fabric.api.resource.ModResourcePack;
|
||||
|
@ -35,6 +37,22 @@ import net.fabricmc.fabric.api.resource.ModResourcePack;
|
|||
* Represents a resource pack provider for mods and built-in mods resource packs.
|
||||
*/
|
||||
public class ModResourcePackCreator implements ResourcePackProvider {
|
||||
/**
|
||||
* The ID of the root resource pack profile for bundled packs.
|
||||
*/
|
||||
public static final String FABRIC = "fabric";
|
||||
private static final String PROGRAMMER_ART = "programmer_art";
|
||||
private static final String HIGH_CONTRAST = "high_contrast";
|
||||
public static final Set<String> POST_CHANGE_HANDLE_REQUIRED = Set.of(FABRIC, PROGRAMMER_ART, HIGH_CONTRAST);
|
||||
@VisibleForTesting
|
||||
public static final Predicate<Set<String>> BASE_PARENT = enabled -> enabled.contains(FABRIC);
|
||||
@VisibleForTesting
|
||||
public static final Predicate<Set<String>> PROGRAMMER_ART_PARENT = enabled -> enabled.contains(FABRIC) && enabled.contains(PROGRAMMER_ART);
|
||||
@VisibleForTesting
|
||||
public static final Predicate<Set<String>> HIGH_CONTRAST_PARENT = enabled -> enabled.contains(FABRIC) && enabled.contains(HIGH_CONTRAST);
|
||||
/**
|
||||
* This can be used to check if a pack profile is for mod-provided packs.
|
||||
*/
|
||||
public static final ResourcePackSource RESOURCE_PACK_SOURCE = new ResourcePackSource() {
|
||||
@Override
|
||||
public Text decorate(Text packName) {
|
||||
|
@ -72,49 +90,51 @@ public class ModResourcePackCreator implements ResourcePackProvider {
|
|||
4. User resource packs
|
||||
*/
|
||||
|
||||
consumer.accept(ResourcePackProfile.create(
|
||||
FABRIC,
|
||||
Text.translatable("pack.name.fabricMods"),
|
||||
true,
|
||||
new PlaceholderResourcePack.Factory(this.type),
|
||||
this.type,
|
||||
ResourcePackProfile.InsertionPosition.TOP,
|
||||
RESOURCE_PACK_SOURCE
|
||||
));
|
||||
|
||||
// Build a list of mod resource packs.
|
||||
List<ModResourcePack> packs = new ArrayList<>();
|
||||
ModResourcePackUtil.appendModResourcePacks(packs, type, null);
|
||||
registerModPack(consumer, null, BASE_PARENT);
|
||||
|
||||
if (!packs.isEmpty()) {
|
||||
// Make the resource pack profile for mod resource packs.
|
||||
// Mod resource packs must always be enabled to avoid issues, and they are inserted
|
||||
// on top to ensure that they are applied after vanilla built-in resource packs.
|
||||
MutableText title = Text.translatable("pack.name.fabricMods");
|
||||
ResourcePackProfile resourcePackProfile = ResourcePackProfile.create("fabric", title, true, new ResourcePackProfile.PackFactory() {
|
||||
@Override
|
||||
public ResourcePack open(String name) {
|
||||
return new FabricModResourcePack(type, packs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourcePack openWithOverlays(String name, ResourcePackProfile.Metadata metadata) {
|
||||
final ResourcePack basePack = open(name);
|
||||
final List<String> overlays = metadata.overlays();
|
||||
|
||||
if (overlays.isEmpty()) {
|
||||
return basePack;
|
||||
}
|
||||
|
||||
final List<ResourcePack> overlayedPacks = new ArrayList<>(overlays.size());
|
||||
|
||||
for (String overlay : overlays) {
|
||||
List<ModResourcePack> innerPacks = new ArrayList<>();
|
||||
ModResourcePackUtil.appendModResourcePacks(innerPacks, type, overlay);
|
||||
|
||||
overlayedPacks.add(new FabricModResourcePack(type, innerPacks));
|
||||
}
|
||||
|
||||
return new OverlayResourcePack(basePack, overlayedPacks);
|
||||
}
|
||||
}, type, ResourcePackProfile.InsertionPosition.TOP, RESOURCE_PACK_SOURCE);
|
||||
|
||||
if (resourcePackProfile != null) {
|
||||
consumer.accept(resourcePackProfile);
|
||||
}
|
||||
if (this.type == ResourceType.CLIENT_RESOURCES) {
|
||||
// Programmer Art/High Contrast data packs can never be enabled.
|
||||
registerModPack(consumer, PROGRAMMER_ART, PROGRAMMER_ART_PARENT);
|
||||
registerModPack(consumer, HIGH_CONTRAST, HIGH_CONTRAST_PARENT);
|
||||
}
|
||||
|
||||
// Register all built-in resource packs provided by mods.
|
||||
ResourceManagerHelperImpl.registerBuiltinResourcePacks(this.type, consumer);
|
||||
}
|
||||
|
||||
private void registerModPack(Consumer<ResourcePackProfile> consumer, @Nullable String subPath, Predicate<Set<String>> parents) {
|
||||
List<ModResourcePack> packs = new ArrayList<>();
|
||||
ModResourcePackUtil.appendModResourcePacks(packs, this.type, subPath);
|
||||
|
||||
for (ModResourcePack pack : packs) {
|
||||
Text displayName = subPath == null
|
||||
? Text.translatable("pack.name.fabricMod", pack.getFabricModMetadata().getName())
|
||||
: Text.translatable("pack.name.fabricMod.subPack", pack.getFabricModMetadata().getName(), Text.translatable("resourcePack." + subPath + ".name"));
|
||||
ResourcePackProfile profile = ResourcePackProfile.create(
|
||||
subPath == null ? pack.getName() : pack.getName() + "_" + subPath,
|
||||
displayName,
|
||||
subPath == null,
|
||||
new ModResourcePackFactory(pack),
|
||||
this.type,
|
||||
ResourcePackProfile.InsertionPosition.TOP,
|
||||
RESOURCE_PACK_SOURCE
|
||||
);
|
||||
|
||||
if (profile != null) {
|
||||
((FabricResourcePackProfile) profile).fabric_setParentsPredicate(parents);
|
||||
consumer.accept(profile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.impl.resource.loader;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.resource.OverlayResourcePack;
|
||||
import net.minecraft.resource.ResourcePack;
|
||||
import net.minecraft.resource.ResourcePackProfile;
|
||||
|
||||
import net.fabricmc.fabric.api.resource.ModResourcePack;
|
||||
|
||||
public record ModResourcePackFactory(ModResourcePack pack) implements ResourcePackProfile.PackFactory {
|
||||
@Override
|
||||
public ResourcePack open(String name) {
|
||||
return pack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourcePack openWithOverlays(String name, ResourcePackProfile.Metadata metadata) {
|
||||
if (metadata.overlays().isEmpty()) {
|
||||
return pack;
|
||||
} else {
|
||||
List<ResourcePack> overlays = new ArrayList<>(metadata.overlays().size());
|
||||
|
||||
for (String overlay : metadata.overlays()) {
|
||||
overlays.add(pack.createOverlay(overlay));
|
||||
}
|
||||
|
||||
return new OverlayResourcePack(pack, overlays);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,12 +16,19 @@
|
|||
|
||||
package net.fabricmc.fabric.impl.resource.loader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
|
@ -29,6 +36,8 @@ import com.google.gson.Gson;
|
|||
import com.google.gson.JsonObject;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import net.minecraft.SharedConstants;
|
||||
import net.minecraft.resource.DataConfiguration;
|
||||
|
@ -37,6 +46,7 @@ import net.minecraft.resource.ResourcePack;
|
|||
import net.minecraft.resource.ResourcePackProfile;
|
||||
import net.minecraft.resource.ResourceType;
|
||||
import net.minecraft.resource.featuretoggle.FeatureFlags;
|
||||
import net.minecraft.resource.metadata.PackResourceMetadata;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
import net.fabricmc.fabric.api.resource.ModResourcePack;
|
||||
|
@ -49,7 +59,8 @@ import net.fabricmc.loader.api.metadata.ModMetadata;
|
|||
* Internal utilities for managing resource packs.
|
||||
*/
|
||||
public final class ModResourcePackUtil {
|
||||
private static final Gson GSON = new Gson();
|
||||
public static final Gson GSON = new Gson();
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ModResourcePackUtil.class);
|
||||
|
||||
private ModResourcePackUtil() {
|
||||
}
|
||||
|
@ -67,7 +78,7 @@ public final class ModResourcePackUtil {
|
|||
continue;
|
||||
}
|
||||
|
||||
ModResourcePack pack = ModNioResourcePack.create(container.getMetadata().getId(), container, subPath, type, ResourcePackActivationType.ALWAYS_ENABLED);
|
||||
ModResourcePack pack = ModNioResourcePack.create(container.getMetadata().getId(), container, subPath, type, ResourcePackActivationType.ALWAYS_ENABLED, true);
|
||||
|
||||
if (pack != null) {
|
||||
packs.add(pack);
|
||||
|
@ -75,25 +86,76 @@ public final class ModResourcePackUtil {
|
|||
}
|
||||
}
|
||||
|
||||
public static boolean containsDefault(ModMetadata info, String filename) {
|
||||
return "pack.mcmeta".equals(filename);
|
||||
public static void refreshAutoEnabledPacks(List<ResourcePackProfile> enabledProfiles, Map<String, ResourcePackProfile> allProfiles) {
|
||||
LOGGER.debug("[Fabric] Starting internal pack sorting with: {}", enabledProfiles.stream().map(ResourcePackProfile::getName).toList());
|
||||
enabledProfiles.removeIf(profile -> ((FabricResourcePackProfile) profile).fabric_isHidden());
|
||||
LOGGER.debug("[Fabric] Removed all internal packs, result: {}", enabledProfiles.stream().map(ResourcePackProfile::getName).toList());
|
||||
ListIterator<ResourcePackProfile> it = enabledProfiles.listIterator();
|
||||
Set<String> seen = new LinkedHashSet<>();
|
||||
|
||||
while (it.hasNext()) {
|
||||
ResourcePackProfile profile = it.next();
|
||||
seen.add(profile.getName());
|
||||
|
||||
for (ResourcePackProfile p : allProfiles.values()) {
|
||||
FabricResourcePackProfile fp = (FabricResourcePackProfile) p;
|
||||
|
||||
if (fp.fabric_isHidden() && fp.fabric_parentsEnabled(seen) && seen.add(p.getName())) {
|
||||
it.add(p);
|
||||
LOGGER.debug("[Fabric] cur @ {}, auto-enabled {}, currently enabled: {}", profile.getName(), p.getName(), seen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LOGGER.debug("[Fabric] Final sorting result: {}", enabledProfiles.stream().map(ResourcePackProfile::getName).toList());
|
||||
}
|
||||
|
||||
public static InputStream openDefault(ModMetadata info, ResourceType type, String filename) {
|
||||
public static boolean containsDefault(String filename, boolean modBundled) {
|
||||
return "pack.mcmeta".equals(filename) || (modBundled && "pack.png".equals(filename));
|
||||
}
|
||||
|
||||
public static InputStream getDefaultIcon() throws IOException {
|
||||
Optional<Path> loaderIconPath = FabricLoader.getInstance().getModContainer("fabric-resource-loader-v0")
|
||||
.flatMap(resourceLoaderContainer -> resourceLoaderContainer.getMetadata().getIconPath(512).flatMap(resourceLoaderContainer::findPath));
|
||||
|
||||
if (loaderIconPath.isPresent()) {
|
||||
return Files.newInputStream(loaderIconPath.get());
|
||||
}
|
||||
|
||||
// Should never happen in practice
|
||||
return null;
|
||||
}
|
||||
|
||||
public static InputStream openDefault(ModContainer container, ResourceType type, String filename) throws IOException {
|
||||
switch (filename) {
|
||||
case "pack.mcmeta":
|
||||
String description = Objects.requireNonNullElse(info.getName(), "");
|
||||
String description = Objects.requireNonNullElse(container.getMetadata().getName(), "");
|
||||
String metadata = serializeMetadata(SharedConstants.getGameVersion().getResourceVersion(type), description);
|
||||
return IOUtils.toInputStream(metadata, Charsets.UTF_8);
|
||||
case "pack.png":
|
||||
Optional<Path> path = container.getMetadata().getIconPath(512).flatMap(container::findPath);
|
||||
|
||||
if (path.isPresent()) {
|
||||
return Files.newInputStream(path.get());
|
||||
} else {
|
||||
return getDefaultIcon();
|
||||
}
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static PackResourceMetadata getMetadataPack(int packVersion, Text description) {
|
||||
return new PackResourceMetadata(description, packVersion, Optional.empty());
|
||||
}
|
||||
|
||||
public static JsonObject getMetadataPackJson(int packVersion, Text description) {
|
||||
return PackResourceMetadata.SERIALIZER.toJson(getMetadataPack(packVersion, description));
|
||||
}
|
||||
|
||||
public static String serializeMetadata(int packVersion, String description) {
|
||||
JsonObject pack = new JsonObject();
|
||||
pack.addProperty("pack_format", packVersion);
|
||||
pack.addProperty("description", description);
|
||||
// This seems to be still manually deserialized
|
||||
JsonObject pack = getMetadataPackJson(packVersion, Text.literal(description));
|
||||
JsonObject metadata = new JsonObject();
|
||||
metadata.add("pack", pack);
|
||||
return GSON.toJson(metadata);
|
||||
|
@ -123,8 +185,13 @@ public final class ModResourcePackUtil {
|
|||
// This ensures that any built-in registered data packs by mods which needs to be enabled by default are
|
||||
// as the data pack screen automatically put any data pack as disabled except the Default data pack.
|
||||
for (ResourcePackProfile profile : moddedResourcePacks) {
|
||||
if (profile.getSource() == ModResourcePackCreator.RESOURCE_PACK_SOURCE) {
|
||||
enabled.add(profile.getName());
|
||||
continue;
|
||||
}
|
||||
|
||||
try (ResourcePack pack = profile.createResourcePack()) {
|
||||
if (pack instanceof FabricModResourcePack || (pack instanceof ModNioResourcePack && ((ModNioResourcePack) pack).getActivationType().isEnabledByDefault())) {
|
||||
if (pack instanceof ModNioResourcePack && ((ModNioResourcePack) pack).getActivationType().isEnabledByDefault()) {
|
||||
enabled.add(profile.getName());
|
||||
} else {
|
||||
disabled.add(profile.getName());
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.impl.resource.loader;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.SharedConstants;
|
||||
import net.minecraft.resource.InputSupplier;
|
||||
import net.minecraft.resource.ResourcePack;
|
||||
import net.minecraft.resource.ResourcePackProfile;
|
||||
import net.minecraft.resource.ResourceType;
|
||||
import net.minecraft.resource.metadata.PackResourceMetadata;
|
||||
import net.minecraft.resource.metadata.ResourceMetadataMap;
|
||||
import net.minecraft.resource.metadata.ResourceMetadataReader;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
public record PlaceholderResourcePack(ResourceType type) implements ResourcePack {
|
||||
private static final Text DESCRIPTION_TEXT = Text.translatable("pack.description.modResources");
|
||||
|
||||
public PackResourceMetadata getMetadata() {
|
||||
return ModResourcePackUtil.getMetadataPack(
|
||||
SharedConstants.getGameVersion().getResourceVersion(type),
|
||||
DESCRIPTION_TEXT
|
||||
);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public InputSupplier<InputStream> openRoot(String... segments) {
|
||||
if (segments.length > 0) {
|
||||
switch (segments[0]) {
|
||||
case "pack.mcmeta":
|
||||
return () -> {
|
||||
String metadata = ModResourcePackUtil.GSON.toJson(PackResourceMetadata.SERIALIZER.toJson(getMetadata()));
|
||||
return IOUtils.toInputStream(metadata, StandardCharsets.UTF_8);
|
||||
};
|
||||
case "pack.png":
|
||||
return ModResourcePackUtil::getDefaultIcon;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This pack has no actual contents.
|
||||
*/
|
||||
@Nullable
|
||||
@Override
|
||||
public InputSupplier<InputStream> open(ResourceType type, Identifier id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void findResources(ResourceType type, String namespace, String prefix, ResultConsumer consumer) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getNamespaces(ResourceType type) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public <T> T parseMetadata(ResourceMetadataReader<T> metaReader) {
|
||||
return ResourceMetadataMap.of(PackResourceMetadata.SERIALIZER, getMetadata()).get(metaReader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return ModResourcePackCreator.FABRIC;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
public record Factory(ResourceType type) implements ResourcePackProfile.PackFactory {
|
||||
@Override
|
||||
public ResourcePack open(String name) {
|
||||
return new PlaceholderResourcePack(this.type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourcePack openWithOverlays(String name, ResourcePackProfile.Metadata metadata) {
|
||||
return open(name);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -74,8 +74,8 @@ public class ResourceManagerHelperImpl implements ResourceManagerHelper {
|
|||
List<Path> paths = container.getRootPaths();
|
||||
String separator = paths.get(0).getFileSystem().getSeparator();
|
||||
subPath = subPath.replace("/", separator);
|
||||
ModNioResourcePack resourcePack = ModNioResourcePack.create(id.toString(), container, subPath, ResourceType.CLIENT_RESOURCES, activationType);
|
||||
ModNioResourcePack dataPack = ModNioResourcePack.create(id.toString(), container, subPath, ResourceType.SERVER_DATA, activationType);
|
||||
ModNioResourcePack resourcePack = ModNioResourcePack.create(id.toString(), container, subPath, ResourceType.CLIENT_RESOURCES, activationType, false);
|
||||
ModNioResourcePack dataPack = ModNioResourcePack.create(id.toString(), container, subPath, ResourceType.SERVER_DATA, activationType, false);
|
||||
if (resourcePack == null && dataPack == null) return false;
|
||||
|
||||
if (resourcePack != null) {
|
||||
|
|
|
@ -28,7 +28,6 @@ import net.minecraft.resource.ResourcePackSource;
|
|||
* See {@link net.fabricmc.fabric.mixin.resource.loader.ResourcePackProfileMixin ResourcePackProfileMixin}.
|
||||
*
|
||||
* <p>The sources are later read for use in {@link FabricResource} and {@link FabricResourceImpl}.
|
||||
* See {@link net.fabricmc.fabric.mixin.resource.loader.NamespaceResourceManagerMixin NamespaceResourceManagerMixin}.
|
||||
*/
|
||||
public final class ResourcePackSourceTracker {
|
||||
// Use a weak hash map so that if resource packs would be deleted, this won't keep them alive.
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.mixin.resource.loader;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.exceptions.DynamicCommandExceptionType;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import net.minecraft.resource.ResourcePackManager;
|
||||
import net.minecraft.resource.ResourcePackProfile;
|
||||
import net.minecraft.server.command.DatapackCommand;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
import net.fabricmc.fabric.impl.resource.loader.FabricResourcePackProfile;
|
||||
|
||||
/**
|
||||
* Disables enabling/disabling internal data packs.
|
||||
* Listing them is still allowed, but they do not appear in suggestions.
|
||||
*/
|
||||
@Mixin(DatapackCommand.class)
|
||||
public class DatapackCommandMixin {
|
||||
@Unique
|
||||
private static final DynamicCommandExceptionType INTERNAL_PACK_EXCEPTION = new DynamicCommandExceptionType(
|
||||
packName -> Text.stringifiedTranslatable("commands.datapack.fabric.internal", packName));
|
||||
|
||||
@Redirect(method = "method_13136", at = @At(value = "INVOKE", target = "Lnet/minecraft/resource/ResourcePackManager;getEnabledNames()Ljava/util/Collection;"))
|
||||
private static Collection<String> filterEnabledPackSuggestions(ResourcePackManager dataPackManager) {
|
||||
return dataPackManager.getEnabledProfiles().stream().filter(profile -> !((FabricResourcePackProfile) profile).fabric_isHidden()).map(ResourcePackProfile::getName).toList();
|
||||
}
|
||||
|
||||
@WrapOperation(method = "method_13120", at = @At(value = "INVOKE", target = "Ljava/util/stream/Stream;filter(Ljava/util/function/Predicate;)Ljava/util/stream/Stream;", ordinal = 0))
|
||||
private static Stream<ResourcePackProfile> filterDisabledPackSuggestions(Stream<ResourcePackProfile> instance, Predicate<? super ResourcePackProfile> predicate, Operation<Stream<ResourcePackProfile>> original) {
|
||||
return original.call(instance, predicate).filter(profile -> !((FabricResourcePackProfile) profile).fabric_isHidden());
|
||||
}
|
||||
|
||||
@Inject(method = "getPackContainer", at = @At(value = "INVOKE", target = "Ljava/util/Collection;contains(Ljava/lang/Object;)Z", shift = At.Shift.BEFORE))
|
||||
private static void errorOnInternalPack(CommandContext<ServerCommandSource> context, String name, boolean enable, CallbackInfoReturnable<ResourcePackProfile> cir, @Local ResourcePackProfile profile) throws CommandSyntaxException {
|
||||
if (((FabricResourcePackProfile) profile).fabric_isHidden()) throw INTERNAL_PACK_EXCEPTION.create(profile.getName());
|
||||
}
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.mixin.resource.loader;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
|
||||
|
||||
import net.minecraft.resource.InputSupplier;
|
||||
import net.minecraft.resource.NamespaceResourceManager;
|
||||
import net.minecraft.resource.Resource;
|
||||
import net.minecraft.resource.ResourcePack;
|
||||
import net.minecraft.resource.ResourceType;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.impl.resource.loader.GroupResourcePack;
|
||||
|
||||
/**
|
||||
* Patches getAllResources and method_41265 to work with GroupResourcePack.
|
||||
*/
|
||||
@Mixin(NamespaceResourceManager.class)
|
||||
public class NamespaceResourceManagerMixin {
|
||||
private final ThreadLocal<List<Resource>> fabric$getAllResources$resources = new ThreadLocal<>();
|
||||
|
||||
@Inject(method = "getAllResources",
|
||||
at = @At(value = "INVOKE", target = "Ljava/util/List;size()I"),
|
||||
locals = LocalCapture.CAPTURE_FAILHARD)
|
||||
private void onGetAllResources(Identifier id, CallbackInfoReturnable<List<Resource>> cir, Identifier metadataId, List<Resource> resources) {
|
||||
this.fabric$getAllResources$resources.set(resources);
|
||||
}
|
||||
|
||||
@Redirect(method = "getAllResources",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/resource/ResourcePack;open(Lnet/minecraft/resource/ResourceType;Lnet/minecraft/util/Identifier;)Lnet/minecraft/resource/InputSupplier;"))
|
||||
private InputSupplier<InputStream> onResourceAdd(ResourcePack pack, ResourceType type, Identifier id) {
|
||||
if (pack instanceof GroupResourcePack) {
|
||||
((GroupResourcePack) pack).appendResources(type, id, this.fabric$getAllResources$resources.get());
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return pack.open(type, id);
|
||||
}
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.mixin.resource.loader;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import net.minecraft.resource.ReloadableResourceManagerImpl;
|
||||
import net.minecraft.resource.ResourcePack;
|
||||
|
||||
import net.fabricmc.fabric.impl.resource.loader.GroupResourcePack;
|
||||
|
||||
@Mixin(ReloadableResourceManagerImpl.class)
|
||||
public class ReloadableResourceManagerImplMixin {
|
||||
// private static synthetic method_29491(Ljava/util/List;)Ljava/lang/Object;
|
||||
// Supplier lambda in beginMonitoredReload method.
|
||||
@Inject(method = "method_29491", at = @At("HEAD"), cancellable = true)
|
||||
private static void getResourcePackNames(List<ResourcePack> packs, CallbackInfoReturnable<String> cir) {
|
||||
cir.setReturnValue(packs.stream().map(pack -> {
|
||||
if (pack instanceof GroupResourcePack) {
|
||||
return ((GroupResourcePack) pack).getFullName();
|
||||
} else {
|
||||
return pack.getName();
|
||||
}
|
||||
}).collect(Collectors.joining(", ")));
|
||||
}
|
||||
}
|
|
@ -27,8 +27,6 @@ import net.fabricmc.fabric.impl.resource.loader.ResourcePackSourceTracker;
|
|||
/**
|
||||
* Implements {@link FabricResource} (resource source getter/setter)
|
||||
* for vanilla's basic {@link Resource} used for most game resources.
|
||||
*
|
||||
* @see NamespaceResourceManagerMixin the usage site for this mixin
|
||||
*/
|
||||
@Mixin(Resource.class)
|
||||
class ResourceMixin implements FabricResource {
|
||||
|
|
|
@ -16,16 +16,25 @@
|
|||
|
||||
package net.fabricmc.fabric.mixin.resource.loader;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Mutable;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import net.minecraft.resource.FileResourcePackProvider;
|
||||
import net.minecraft.resource.ResourcePackManager;
|
||||
|
@ -34,14 +43,22 @@ import net.minecraft.resource.ResourcePackProvider;
|
|||
import net.minecraft.resource.ResourcePackSource;
|
||||
import net.minecraft.resource.ResourceType;
|
||||
|
||||
import net.fabricmc.fabric.impl.resource.loader.FabricResourcePackProfile;
|
||||
import net.fabricmc.fabric.impl.resource.loader.ModResourcePackCreator;
|
||||
import net.fabricmc.fabric.impl.resource.loader.ModResourcePackUtil;
|
||||
|
||||
@Mixin(ResourcePackManager.class)
|
||||
public abstract class ResourcePackManagerMixin<T extends ResourcePackProfile> {
|
||||
public abstract class ResourcePackManagerMixin {
|
||||
@Unique
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger("ResourcePackManagerMixin");
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
@Mutable
|
||||
private Set<ResourcePackProvider> providers;
|
||||
public Set<ResourcePackProvider> providers;
|
||||
|
||||
@Shadow
|
||||
private Map<String, ResourcePackProfile> profiles;
|
||||
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
public void construct(ResourcePackProvider[] resourcePackProviders, CallbackInfo info) {
|
||||
|
@ -65,4 +82,25 @@ public abstract class ResourcePackManagerMixin<T extends ResourcePackProfile> {
|
|||
providers.add(new ModResourcePackCreator(ResourceType.SERVER_DATA));
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "buildEnabledProfiles", at = @At(value = "INVOKE", target = "Lcom/google/common/collect/ImmutableList;copyOf(Ljava/util/Collection;)Lcom/google/common/collect/ImmutableList;", shift = At.Shift.BEFORE))
|
||||
private void handleAutoEnableDisable(Collection<String> enabledNames, CallbackInfoReturnable<List<ResourcePackProfile>> cir, @Local List<ResourcePackProfile> enabledAfterFirstRun) {
|
||||
ModResourcePackUtil.refreshAutoEnabledPacks(enabledAfterFirstRun, this.profiles);
|
||||
}
|
||||
|
||||
@Inject(method = "enable", at = @At(value = "INVOKE", target = "Ljava/util/List;add(Ljava/lang/Object;)Z", shift = At.Shift.AFTER))
|
||||
private void handleAutoEnable(String profile, CallbackInfoReturnable<Boolean> cir, @Local List<ResourcePackProfile> newlyEnabled) {
|
||||
if (ModResourcePackCreator.POST_CHANGE_HANDLE_REQUIRED.contains(profile)) {
|
||||
ModResourcePackUtil.refreshAutoEnabledPacks(newlyEnabled, this.profiles);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "disable", at = @At(value = "INVOKE", target = "Ljava/util/List;remove(Ljava/lang/Object;)Z"))
|
||||
private void handleAutoDisable(String profile, CallbackInfoReturnable<Boolean> cir, @Local List<ResourcePackProfile> enabled) {
|
||||
if (ModResourcePackCreator.POST_CHANGE_HANDLE_REQUIRED.contains(profile)) {
|
||||
Set<String> currentlyEnabled = enabled.stream().map(ResourcePackProfile::getName).collect(Collectors.toSet());
|
||||
enabled.removeIf(p -> !((FabricResourcePackProfile) p).fabric_parentsEnabled(currentlyEnabled));
|
||||
LOGGER.debug("[Fabric] Internal pack auto-removed upon disabling {}, result: {}", profile, enabled.stream().map(ResourcePackProfile::getName).toList());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,9 +16,13 @@
|
|||
|
||||
package net.fabricmc.fabric.mixin.resource.loader;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
@ -27,6 +31,7 @@ import net.minecraft.resource.ResourcePack;
|
|||
import net.minecraft.resource.ResourcePackProfile;
|
||||
import net.minecraft.resource.ResourcePackSource;
|
||||
|
||||
import net.fabricmc.fabric.impl.resource.loader.FabricResourcePackProfile;
|
||||
import net.fabricmc.fabric.impl.resource.loader.ResourcePackSourceTracker;
|
||||
|
||||
/**
|
||||
|
@ -37,13 +42,32 @@ import net.fabricmc.fabric.impl.resource.loader.ResourcePackSourceTracker;
|
|||
* @see ResourcePackSourceTracker
|
||||
*/
|
||||
@Mixin(ResourcePackProfile.class)
|
||||
abstract class ResourcePackProfileMixin {
|
||||
abstract class ResourcePackProfileMixin implements FabricResourcePackProfile {
|
||||
@Unique
|
||||
private static final Predicate<Set<String>> DEFAULT_PARENT_PREDICATE = parents -> true;
|
||||
@Shadow
|
||||
@Final
|
||||
private ResourcePackSource source;
|
||||
@Unique
|
||||
private Predicate<Set<String>> parentsPredicate = DEFAULT_PARENT_PREDICATE;
|
||||
|
||||
@Inject(method = "createResourcePack", at = @At("RETURN"))
|
||||
private void onCreateResourcePack(CallbackInfoReturnable<ResourcePack> info) {
|
||||
ResourcePackSourceTracker.setSource(info.getReturnValue(), source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean fabric_isHidden() {
|
||||
return parentsPredicate != DEFAULT_PARENT_PREDICATE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean fabric_parentsEnabled(Set<String> enabled) {
|
||||
return parentsPredicate.test(enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fabric_setParentsPredicate(Predicate<Set<String>> predicate) {
|
||||
this.parentsPredicate = predicate;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,5 +3,7 @@
|
|||
"pack.source.fabricmod": "Fabric mod",
|
||||
"pack.source.builtinMod": "built-in: %s",
|
||||
"pack.name.fabricMod": "Fabric Mod \"%s\"",
|
||||
"pack.name.fabricMods": "Fabric Mods"
|
||||
"pack.name.fabricMods": "Fabric Mods",
|
||||
"pack.name.fabricMod.subPack": "Fabric Mod \"%s\" (%s)",
|
||||
"commands.datapack.fabric.internal": "Cannot enable or disable Fabric internal pack \"%s\"."
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"pack.source.fabricmod": "Fabric mod",
|
||||
"pack.source.builtinMod": "ビルトイン: %s",
|
||||
"pack.name.fabricMods": "Fabric Mod"
|
||||
"pack.name.fabricMods": "Fabric Mod",
|
||||
"commands.datapack.fabric.internal": "「%s」はFabric内部で管理されているため、有効化・無効化できません"
|
||||
}
|
||||
|
|
|
@ -3,11 +3,10 @@
|
|||
"package": "net.fabricmc.fabric.mixin.resource.loader",
|
||||
"compatibilityLevel": "JAVA_17",
|
||||
"mixins": [
|
||||
"DatapackCommandMixin",
|
||||
"KeyedResourceReloadListenerMixin",
|
||||
"LifecycledResourceManagerImplMixin",
|
||||
"MinecraftServerMixin",
|
||||
"NamespaceResourceManagerMixin",
|
||||
"ReloadableResourceManagerImplMixin",
|
||||
"ResourceMixin",
|
||||
"ResourcePackManagerMixin",
|
||||
"ResourcePackProfileMixin",
|
||||
|
|
|
@ -0,0 +1,248 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.test.resource.loader.unit;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import net.minecraft.Bootstrap;
|
||||
import net.minecraft.SharedConstants;
|
||||
import net.minecraft.resource.ResourcePackProfile;
|
||||
|
||||
import net.fabricmc.fabric.impl.resource.loader.FabricResourcePackProfile;
|
||||
import net.fabricmc.fabric.impl.resource.loader.ModResourcePackCreator;
|
||||
import net.fabricmc.fabric.impl.resource.loader.ModResourcePackUtil;
|
||||
|
||||
public class ModResourcePackUtilTests {
|
||||
private static final Gson GSON = new Gson();
|
||||
|
||||
@BeforeAll
|
||||
static void beforeAll() {
|
||||
SharedConstants.createGameVersion();
|
||||
Bootstrap.initialize();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRefreshAutoEnabledPacks() {
|
||||
// Vanilla uses tree map, and we test the behavior
|
||||
Map<String, ResourcePackProfile> profiles = new TreeMap<>();
|
||||
Map<String, ResourcePackProfile> modAProfiles = new TreeMap<>();
|
||||
Map<String, ResourcePackProfile> modBProfiles = new TreeMap<>();
|
||||
Map<String, ResourcePackProfile> allProfiles = new TreeMap<>();
|
||||
ResourcePackProfile vanilla = mockProfile(profiles, "vanilla", null);
|
||||
ResourcePackProfile fabric = mockProfile(profiles, ModResourcePackCreator.FABRIC, null);
|
||||
ResourcePackProfile modA = mockProfile(modAProfiles, "mod_a", ModResourcePackCreator.BASE_PARENT);
|
||||
ResourcePackProfile modAProg = mockProfile(modAProfiles, "mod_a_programmer_art", ModResourcePackCreator.PROGRAMMER_ART_PARENT);
|
||||
ResourcePackProfile modAHigh = mockProfile(modAProfiles, "mod_a_high_contrast", ModResourcePackCreator.HIGH_CONTRAST_PARENT);
|
||||
ResourcePackProfile modB = mockProfile(modBProfiles, "mod_b", ModResourcePackCreator.BASE_PARENT);
|
||||
ResourcePackProfile modBProg = mockProfile(modBProfiles, "mod_b_programmer_art", ModResourcePackCreator.PROGRAMMER_ART_PARENT);
|
||||
ResourcePackProfile modBHigh = mockProfile(modBProfiles, "mod_b_high_contrast", ModResourcePackCreator.HIGH_CONTRAST_PARENT);
|
||||
ResourcePackProfile programmerArt = mockProfile(profiles, "programmer_art", null);
|
||||
ResourcePackProfile highContrast = mockProfile(profiles, "high_contrast", null);
|
||||
ResourcePackProfile userPackA = mockProfile(profiles, "user_pack_a", null);
|
||||
ResourcePackProfile userPackB = mockProfile(profiles, "user_pack_b", null);
|
||||
modAProfiles.putAll(profiles);
|
||||
modBProfiles.putAll(profiles);
|
||||
allProfiles.putAll(modAProfiles);
|
||||
allProfiles.putAll(modBProfiles);
|
||||
|
||||
testRefreshAutoEnabledPacks(
|
||||
profiles,
|
||||
List.of(vanilla, fabric),
|
||||
List.of(vanilla, fabric),
|
||||
"keep (no mods)"
|
||||
);
|
||||
testRefreshAutoEnabledPacks(
|
||||
profiles,
|
||||
List.of(vanilla, fabric, userPackA),
|
||||
List.of(vanilla, fabric, userPackA),
|
||||
"keep (no mods, keep user pack)"
|
||||
);
|
||||
testRefreshAutoEnabledPacks(
|
||||
modAProfiles,
|
||||
List.of(vanilla, fabric, modA),
|
||||
List.of(vanilla, fabric, modA),
|
||||
"keep (mod A only)"
|
||||
);
|
||||
testRefreshAutoEnabledPacks(
|
||||
modAProfiles,
|
||||
List.of(vanilla, fabric, modA, programmerArt, modAProg),
|
||||
List.of(vanilla, fabric, modA, programmerArt, modAProg),
|
||||
"keep (programmer_art)"
|
||||
);
|
||||
testRefreshAutoEnabledPacks(
|
||||
allProfiles,
|
||||
List.of(vanilla, fabric, modA, modB, programmerArt, modAProg, modBProg),
|
||||
List.of(vanilla, fabric, modA, modB, programmerArt, modAProg, modBProg),
|
||||
"keep (mod A and mod B, programmer_art)"
|
||||
);
|
||||
testRefreshAutoEnabledPacks(
|
||||
allProfiles,
|
||||
List.of(vanilla, fabric, modA, modB, programmerArt, modAProg, modBProg, highContrast, modAHigh, modBHigh),
|
||||
List.of(vanilla, fabric, modA, modB, programmerArt, modAProg, modBProg, highContrast, modAHigh, modBHigh),
|
||||
"keep (mod A and mod B, both)"
|
||||
);
|
||||
testRefreshAutoEnabledPacks(
|
||||
allProfiles,
|
||||
List.of(vanilla, fabric, modA, modB, highContrast, modAHigh, modBHigh, programmerArt, modAProg, modBProg),
|
||||
List.of(vanilla, fabric, modA, modB, highContrast, modAHigh, modBHigh, programmerArt, modAProg, modBProg),
|
||||
"keep (remembers programmer_art-high_contrast order)"
|
||||
);
|
||||
testRefreshAutoEnabledPacks(
|
||||
modAProfiles,
|
||||
List.of(vanilla, fabric),
|
||||
List.of(vanilla, fabric, modA),
|
||||
"fix (adding missing mods)"
|
||||
);
|
||||
testRefreshAutoEnabledPacks(
|
||||
allProfiles,
|
||||
List.of(vanilla, fabric, userPackA),
|
||||
List.of(vanilla, fabric, modA, modB, userPackA),
|
||||
"fix (adding missing mods at the right place)"
|
||||
);
|
||||
testRefreshAutoEnabledPacks(
|
||||
allProfiles,
|
||||
List.of(vanilla, fabric, modB, modA),
|
||||
List.of(vanilla, fabric, modA, modB),
|
||||
"fix (mod A and B, sorting)"
|
||||
);
|
||||
testRefreshAutoEnabledPacks(
|
||||
modAProfiles,
|
||||
List.of(vanilla, fabric, userPackB, modA, userPackA),
|
||||
List.of(vanilla, fabric, modA, userPackB, userPackA),
|
||||
"fix (user pack goes last)"
|
||||
);
|
||||
testRefreshAutoEnabledPacks(
|
||||
modAProfiles,
|
||||
List.of(vanilla, fabric, modA, programmerArt),
|
||||
List.of(vanilla, fabric, modA, programmerArt, modAProg),
|
||||
"fix (adding 1 met dep)"
|
||||
);
|
||||
testRefreshAutoEnabledPacks(
|
||||
modBProfiles,
|
||||
List.of(vanilla, fabric, modB, highContrast),
|
||||
List.of(vanilla, fabric, modB, highContrast, modBHigh),
|
||||
"fix (adding 1 met dep, part 2)"
|
||||
);
|
||||
testRefreshAutoEnabledPacks(
|
||||
modAProfiles,
|
||||
List.of(vanilla, fabric, modA, programmerArt, highContrast),
|
||||
List.of(vanilla, fabric, modA, programmerArt, modAProg, highContrast, modAHigh),
|
||||
"fix (adding 2 met deps)"
|
||||
);
|
||||
testRefreshAutoEnabledPacks(
|
||||
modAProfiles,
|
||||
List.of(vanilla, fabric, modA, programmerArt, modAProg, highContrast),
|
||||
List.of(vanilla, fabric, modA, programmerArt, modAProg, highContrast, modAHigh),
|
||||
"fix (adding 2 met deps + preexisting)"
|
||||
);
|
||||
testRefreshAutoEnabledPacks(
|
||||
modAProfiles,
|
||||
List.of(vanilla, fabric, modA, modAProg, modAHigh),
|
||||
List.of(vanilla, fabric, modA),
|
||||
"fix (removing 2 unmet deps)"
|
||||
);
|
||||
testRefreshAutoEnabledPacks(
|
||||
modAProfiles,
|
||||
List.of(vanilla, fabric, modA, programmerArt, modAProg, modAHigh),
|
||||
List.of(vanilla, fabric, modA, programmerArt, modAProg),
|
||||
"fix (removing 1 unmet dep)"
|
||||
);
|
||||
testRefreshAutoEnabledPacks(
|
||||
modBProfiles,
|
||||
List.of(vanilla, fabric, modB, highContrast, modBProg, modBHigh),
|
||||
List.of(vanilla, fabric, modB, highContrast, modBHigh),
|
||||
"fix (removing 1 unmet dep, part 2)"
|
||||
);
|
||||
testRefreshAutoEnabledPacks(
|
||||
modAProfiles,
|
||||
List.of(vanilla, fabric, modAProg, programmerArt, modA),
|
||||
List.of(vanilla, fabric, modA, programmerArt, modAProg),
|
||||
"reorder (bundled comes just after parents)"
|
||||
);
|
||||
testRefreshAutoEnabledPacks(
|
||||
modAProfiles,
|
||||
List.of(vanilla, fabric, modAProg, userPackA, programmerArt, modA, userPackB),
|
||||
List.of(vanilla, fabric, modA, userPackA, programmerArt, modAProg, userPackB),
|
||||
"reorder (keep user pack order)"
|
||||
);
|
||||
testRefreshAutoEnabledPacks(
|
||||
modAProfiles,
|
||||
List.of(vanilla, fabric, userPackB, modA, programmerArt, userPackA, modAProg),
|
||||
List.of(vanilla, fabric, modA, userPackB, programmerArt, modAProg, userPackA),
|
||||
"reorder (no user pack between parent-bundled)"
|
||||
);
|
||||
}
|
||||
|
||||
private ResourcePackProfile mockProfile(Map<String, ResourcePackProfile> profiles, String id, @Nullable Predicate<Set<String>> parents) {
|
||||
ResourcePackProfile profile = ResourcePackProfile.of(
|
||||
id,
|
||||
null,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
false,
|
||||
ModResourcePackCreator.RESOURCE_PACK_SOURCE
|
||||
);
|
||||
|
||||
if (parents != null) ((FabricResourcePackProfile) profile).fabric_setParentsPredicate(parents);
|
||||
|
||||
profiles.put(id, profile);
|
||||
return profile;
|
||||
}
|
||||
|
||||
private void testRefreshAutoEnabledPacks(Map<String, ResourcePackProfile> profiles, List<ResourcePackProfile> before, List<ResourcePackProfile> after, String reason) {
|
||||
List<ResourcePackProfile> processed = new ArrayList<>(before);
|
||||
ModResourcePackUtil.refreshAutoEnabledPacks(processed, profiles);
|
||||
assertEquals(
|
||||
after.stream().map(ResourcePackProfile::getName).toList(),
|
||||
processed.stream().map(ResourcePackProfile::getName).toList(),
|
||||
() -> "Testing %s; input %s".formatted(reason, before.stream().map(ResourcePackProfile::getName).toList())
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSerializeMetadata() {
|
||||
// Test various metadata serialization issues (#2407)
|
||||
testMetadataSerialization("");
|
||||
testMetadataSerialization("Quotes: \"\" \"");
|
||||
testMetadataSerialization("Backslash: \\ \\\\");
|
||||
}
|
||||
|
||||
private void testMetadataSerialization(String description) throws JsonParseException {
|
||||
String metadata = ModResourcePackUtil.serializeMetadata(1, description);
|
||||
JsonObject json = assertDoesNotThrow(() -> GSON.fromJson(metadata, JsonObject.class), () -> "Failed to serialize " + description);
|
||||
|
||||
String parsedDescription = json.get("pack").getAsJsonObject().get("description").getAsString();
|
||||
assertEquals(description, parsedDescription, "Parsed description differs from original one");
|
||||
}
|
||||
}
|
|
@ -16,9 +16,6 @@
|
|||
|
||||
package net.fabricmc.fabric.test.resource.loader;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -28,7 +25,6 @@ import net.minecraft.util.Identifier;
|
|||
import net.fabricmc.api.ModInitializer;
|
||||
import net.fabricmc.fabric.api.resource.ResourceManagerHelper;
|
||||
import net.fabricmc.fabric.api.resource.ResourcePackActivationType;
|
||||
import net.fabricmc.fabric.impl.resource.loader.ModResourcePackUtil;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
|
||||
public class BuiltinResourcePackTestMod implements ModInitializer {
|
||||
|
@ -36,8 +32,6 @@ public class BuiltinResourcePackTestMod implements ModInitializer {
|
|||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(BuiltinResourcePackTestMod.class);
|
||||
|
||||
private static final Gson GSON = new Gson();
|
||||
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
// Should always be present as it's **this** mod.
|
||||
|
@ -49,27 +43,5 @@ public class BuiltinResourcePackTestMod implements ModInitializer {
|
|||
.map(container -> ResourceManagerHelper.registerBuiltinResourcePack(new Identifier(MODID, "test2"),
|
||||
container, ResourcePackActivationType.NORMAL))
|
||||
.filter(success -> !success).ifPresent(success -> LOGGER.warn("Could not register built-in resource pack."));
|
||||
|
||||
// Test various metadata serialization issues (#2407)
|
||||
testMetadataSerialization("");
|
||||
testMetadataSerialization("Quotes: \"\" \"");
|
||||
testMetadataSerialization("Backslash: \\ \\\\");
|
||||
}
|
||||
|
||||
private void testMetadataSerialization(String description) {
|
||||
String metadata = ModResourcePackUtil.serializeMetadata(1, description);
|
||||
JsonObject json;
|
||||
|
||||
try {
|
||||
json = GSON.fromJson(metadata, JsonObject.class);
|
||||
} catch (JsonParseException exc) {
|
||||
throw new AssertionError("Metadata parsing test for description \"%s\" failed".formatted(description), exc);
|
||||
}
|
||||
|
||||
String parsedDescription = json.get("pack").getAsJsonObject().get("description").getAsString();
|
||||
|
||||
if (!description.equals(parsedDescription)) {
|
||||
throw new AssertionError("Metadata parsing test for description failed: expected \"%s\", got \"%s\"".formatted(description, parsedDescription));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue