Use knownPackInfo on mod data packs to avoid registry syncing ()

* use knownPack to avoid syncing mod data packs.

* remove empty class

* checkstyle and licenses

* move debug logging from testmod into main module

* fix knownPackInfo not working for build-in data packs.

* make bundled data packs not required for client, and don't auto enable

* improve testmod with added builtin data pack + better error message

* Increase max known packs in C2S packet

* validate size before sending
* changeable with system property

* change mixin from ModifyConstant to ModifyArg to be more clear.

* Apply suggestions from code review

Co-authored-by: modmuss <modmuss50@gmail.com>

* store list of knownPacks as server start

avoids desync on pack change (since registries aren't reloaded)

* be extra safe: only request knownPacks that were enabled at server start and still are.

Doesn't rely on the fact that registries aren't synced anymore.

---------

Co-authored-by: modmuss <modmuss50@gmail.com>
(cherry picked from commit c0e5481f61)
This commit is contained in:
Jochen Jacobs 2024-05-07 20:03:31 +02:00 committed by modmuss50
parent 619abec242
commit e8b7ff012a
21 changed files with 380 additions and 63 deletions
fabric-resource-loader-v0/src

View file

@ -0,0 +1,55 @@
/*
* 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 com.llamalad7.mixinextras.injector.ModifyReturnValue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.Redirect;
import net.minecraft.client.resource.ClientDataPackManager;
import net.minecraft.registry.VersionedIdentifier;
import net.minecraft.resource.ResourcePackManager;
import net.fabricmc.fabric.impl.resource.loader.ModResourcePackCreator;
import net.fabricmc.fabric.impl.resource.loader.ModResourcePackUtil;
@Mixin(ClientDataPackManager.class)
public class ClientDataPackManagerMixin {
@Unique
private static final Logger LOGGER = LoggerFactory.getLogger("ClientDataPackManagerMixin");
@Redirect(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/resource/VanillaDataPackProvider;createClientManager()Lnet/minecraft/resource/ResourcePackManager;"))
public ResourcePackManager createClientManager() {
return ModResourcePackUtil.createClientManager();
}
@ModifyReturnValue(method = "getCommonKnownPacks", at = @At("RETURN"))
List<VersionedIdentifier> getCommonKnownPacksReturn(List<VersionedIdentifier> original) {
if (original.size() > ModResourcePackCreator.MAX_KNOWN_PACKS) {
LOGGER.warn("Too many knownPacks: Found {}; max {}", original.size(), ModResourcePackCreator.MAX_KNOWN_PACKS);
return original.subList(0, ModResourcePackCreator.MAX_KNOWN_PACKS);
}
return original;
}
}

View file

@ -9,7 +9,8 @@
"KeyedResourceReloadListenerClientMixin",
"ResourcePackOrganizerMixin",
"VanillaResourcePackProviderMixin",
"GameOptionsWriteVisitorMixin"
"GameOptionsWriteVisitorMixin",
"ClientDataPackManagerMixin"
],
"injectors": {
"defaultRequire": 1

View file

@ -0,0 +1,28 @@
/*
* 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.List;
import net.minecraft.registry.VersionedIdentifier;
public interface FabricOriginalKnownPacksGetter {
/**
* @return the data packs known at server start
*/
List<VersionedIdentifier> fabric_getOriginalKnownPacks();
}

View file

@ -42,6 +42,7 @@ import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.minecraft.registry.VersionedIdentifier;
import net.minecraft.resource.AbstractFileResourcePack;
import net.minecraft.resource.InputSupplier;
import net.minecraft.resource.ResourcePack;
@ -106,7 +107,7 @@ public class ModNioResourcePack implements ResourcePack, ModResourcePack {
packId,
displayName,
ModResourcePackCreator.RESOURCE_PACK_SOURCE,
Optional.empty()
Optional.of(new VersionedIdentifier(ModResourcePackCreator.FABRIC, packId, mod.getMetadata().getVersion().getFriendlyString()))
);
ModNioResourcePack ret = new ModNioResourcePack(packId, mod, paths, type, activationType, modBundled, metadata);

View file

@ -68,12 +68,23 @@ public class ModResourcePackCreator implements ResourcePackProvider {
}
};
public static final ModResourcePackCreator CLIENT_RESOURCE_PACK_PROVIDER = new ModResourcePackCreator(ResourceType.CLIENT_RESOURCES);
private static final ResourcePackPosition ACTIVATION_INFO = new ResourcePackPosition(true, ResourcePackProfile.InsertionPosition.TOP, false);
/**
* The maximum ammount of known data packs requested from the client, including vanilla data packs.
*/
public static final int MAX_KNOWN_PACKS = Integer.getInteger("fabric-resource-loader-v0:maxKnownPacks", 1024);
private final ResourceType type;
private final ResourcePackPosition activationInfo;
private final boolean forClientDataPackManager;
public ModResourcePackCreator(ResourceType type) {
this(type, false);
}
protected ModResourcePackCreator(ResourceType type, boolean forClientDataPackManager) {
this.type = type;
this.activationInfo = new ResourcePackPosition(!forClientDataPackManager, ResourcePackProfile.InsertionPosition.TOP, false);
this.forClientDataPackManager = forClientDataPackManager;
}
/**
@ -106,7 +117,7 @@ public class ModResourcePackCreator implements ResourcePackProvider {
metadata,
new PlaceholderResourcePack.Factory(this.type, metadata),
this.type,
ACTIVATION_INFO
this.activationInfo
));
// Build a list of mod resource packs.
@ -131,11 +142,14 @@ public class ModResourcePackCreator implements ResourcePackProvider {
pack.getInfo(),
new ModResourcePackFactory(pack),
this.type,
ACTIVATION_INFO
this.activationInfo
);
if (profile != null) {
((FabricResourcePackProfile) profile).fabric_setParentsPredicate(parents);
if (!forClientDataPackManager) {
((FabricResourcePackProfile) profile).fabric_setParentsPredicate(parents);
}
consumer.accept(profile);
}
}

View file

@ -43,11 +43,14 @@ import net.minecraft.SharedConstants;
import net.minecraft.resource.DataConfiguration;
import net.minecraft.resource.DataPackSettings;
import net.minecraft.resource.ResourcePack;
import net.minecraft.resource.ResourcePackManager;
import net.minecraft.resource.ResourcePackProfile;
import net.minecraft.resource.ResourceType;
import net.minecraft.resource.VanillaDataPackProvider;
import net.minecraft.resource.featuretoggle.FeatureFlags;
import net.minecraft.resource.metadata.PackResourceMetadata;
import net.minecraft.text.Text;
import net.minecraft.util.path.SymlinkFinder;
import net.fabricmc.fabric.api.resource.ModResourcePack;
import net.fabricmc.fabric.api.resource.ResourcePackActivationType;
@ -233,4 +236,12 @@ public final class ModResourcePackUtil {
return new DataPackSettings(enabled, disabled);
}
/**
* Creates the ResousePackManager used by the ClientDataPackManager and replaces
* {@code VanillaDataPackProvider.createClientManager} used by vanilla.
*/
public static ResourcePackManager createClientManager() {
return new ResourcePackManager(new VanillaDataPackProvider(new SymlinkFinder((path) -> true)), new ModResourcePackCreator(ResourceType.SERVER_DATA, true));
}
}

View file

@ -25,7 +25,6 @@ import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
@ -119,7 +118,7 @@ public class ResourceManagerHelperImpl implements ResourceManagerHelper {
entry.getRight().getId(),
entry.getLeft(),
new BuiltinModResourcePackSource(pack.getFabricModMetadata().getName()),
Optional.empty()
entry.getRight().getKnownPackInfo()
);
ResourcePackPosition info2 = new ResourcePackPosition(
pack.getActivationType() == ResourcePackActivationType.ALWAYS_ENABLED,

View file

@ -16,22 +16,41 @@
package net.fabricmc.fabric.mixin.resource.loader;
import java.net.Proxy;
import java.util.List;
import com.mojang.datafixers.DataFixer;
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.CallbackInfo;
import net.minecraft.registry.VersionedIdentifier;
import net.minecraft.resource.ResourcePack;
import net.minecraft.resource.ResourcePackManager;
import net.minecraft.resource.ResourcePackProfile;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.SaveLoader;
import net.minecraft.server.WorldGenerationProgressListenerFactory;
import net.minecraft.util.ApiServices;
import net.minecraft.world.level.storage.LevelStorage;
import net.fabricmc.fabric.impl.resource.loader.BuiltinModResourcePackSource;
import net.fabricmc.fabric.impl.resource.loader.FabricOriginalKnownPacksGetter;
import net.fabricmc.fabric.impl.resource.loader.ModNioResourcePack;
@Mixin(MinecraftServer.class)
public class MinecraftServerMixin {
public class MinecraftServerMixin implements FabricOriginalKnownPacksGetter {
@Unique
private List<VersionedIdentifier> fabric_originalKnownPacks;
@Inject(method = "<init>", at = @At("TAIL"))
private void init(Thread serverThread, LevelStorage.Session session, ResourcePackManager dataPackManager, SaveLoader saveLoader, Proxy proxy, DataFixer dataFixer, ApiServices apiServices, WorldGenerationProgressListenerFactory worldGenerationProgressListenerFactory, CallbackInfo ci) {
this.fabric_originalKnownPacks = saveLoader.resourceManager().streamResourcePacks().flatMap(pack -> pack.getInfo().knownPackInfo().stream()).toList();
}
@Redirect(method = "loadDataPacks(Lnet/minecraft/resource/ResourcePackManager;Lnet/minecraft/resource/DataConfiguration;ZZ)Lnet/minecraft/resource/DataConfiguration;", at = @At(value = "INVOKE", target = "Ljava/util/List;contains(Ljava/lang/Object;)Z"))
private static boolean onCheckDisabled(List<String> list, Object o, ResourcePackManager resourcePackManager) {
String profileId = (String) o;
@ -52,4 +71,9 @@ public class MinecraftServerMixin {
return false;
}
@Override
public List<VersionedIdentifier> fabric_getOriginalKnownPacks() {
return this.fabric_originalKnownPacks;
}
}

View file

@ -1,51 +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.Optional;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import com.llamalad7.mixinextras.sugar.Local;
import com.mojang.serialization.Lifecycle;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import net.minecraft.registry.RegistryLoader;
import net.minecraft.registry.entry.RegistryEntryInfo;
import net.minecraft.resource.Resource;
import net.fabricmc.fabric.api.resource.ModResourcePack;
@Mixin(RegistryLoader.class)
public class RegistryLoaderMixin {
@Unique
private static final RegistryEntryInfo MOD_PROVIDED_INFO = new RegistryEntryInfo(Optional.empty(), Lifecycle.stable());
// On the server side, loading mod-provided DRM entries should not show experiments screen.
// While the lifecycle is set to experimental on the client side (a de-sync),
// there is no good way to fix this without breaking protocol; the lifecycle seems to be unused on
// the client side anyway.
@ModifyExpressionValue(method = "loadFromResource(Lnet/minecraft/resource/ResourceManager;Lnet/minecraft/registry/RegistryOps$RegistryInfoGetter;Lnet/minecraft/registry/MutableRegistry;Lcom/mojang/serialization/Decoder;Ljava/util/Map;)V", at = @At(value = "INVOKE", target = "Ljava/util/function/Function;apply(Ljava/lang/Object;)Ljava/lang/Object;"))
private static Object markModProvidedAsStable(Object original, @Local Resource resource) {
if (original instanceof RegistryEntryInfo info && info.knownPackInfo().isEmpty() && resource.getPack() instanceof ModResourcePack) {
return MOD_PROVIDED_INFO;
}
return original;
}
}

View file

@ -0,0 +1,33 @@
/*
* 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 org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyArg;
import net.minecraft.network.packet.c2s.config.SelectKnownPacksC2SPacket;
import net.fabricmc.fabric.impl.resource.loader.ModResourcePackCreator;
@Mixin(SelectKnownPacksC2SPacket.class)
public class SelectKnownPacksC2SPacketMixin {
@ModifyArg(method = "<clinit>", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/codec/PacketCodecs;toList(I)Lnet/minecraft/network/codec/PacketCodec$ResultFunction;"))
private static int setMaxKnownPacks(int constant) {
return ModResourcePackCreator.MAX_KNOWN_PACKS;
}
}

View file

@ -0,0 +1,49 @@
/*
* 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 org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyArg;
import net.minecraft.network.ClientConnection;
import net.minecraft.registry.VersionedIdentifier;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ConnectedClientData;
import net.minecraft.server.network.ServerCommonNetworkHandler;
import net.minecraft.server.network.ServerConfigurationNetworkHandler;
import net.fabricmc.fabric.impl.resource.loader.FabricOriginalKnownPacksGetter;
@Mixin(ServerConfigurationNetworkHandler.class)
public abstract class ServerConfigurationNetworkHandlerMixin extends ServerCommonNetworkHandler {
public ServerConfigurationNetworkHandlerMixin(MinecraftServer server, ClientConnection connection, ConnectedClientData clientData) {
super(server, connection, clientData);
}
/**
* Only use packs that were enabled at server start and are enabled now. This avoids a descync when packs have been
* enabled or disabled before the client joins. Since the server registry contents aren't reloaded, we don't want
* the client to use the new data pack data.
*/
@ModifyArg(method = "sendConfigurations", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/network/SynchronizeRegistriesTask;<init>(Ljava/util/List;Lnet/minecraft/registry/CombinedDynamicRegistries;)V", ordinal = 0))
public List<VersionedIdentifier> filterKnownPacks(List<VersionedIdentifier> currentKnownPacks) {
return ((FabricOriginalKnownPacksGetter) this.server).fabric_getOriginalKnownPacks().stream().filter(currentKnownPacks::contains).toList();
}
}

View file

@ -0,0 +1,73 @@
/*
* 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.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
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.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 net.minecraft.network.packet.Packet;
import net.minecraft.network.packet.s2c.config.SelectKnownPacksS2CPacket;
import net.minecraft.registry.VersionedIdentifier;
import net.minecraft.server.network.SynchronizeRegistriesTask;
import net.fabricmc.fabric.impl.resource.loader.ModResourcePackCreator;
@Mixin(SynchronizeRegistriesTask.class)
public abstract class SynchronizeRegistriesTaskMixin {
@Unique
private static final Logger LOGGER = LoggerFactory.getLogger("SynchronizeRegistriesTaskMixin");
@Shadow
@Final
private List<VersionedIdentifier> knownPacks;
@Shadow
protected abstract void syncRegistryAndTags(Consumer<Packet<?>> sender, Set<VersionedIdentifier> commonKnownPacks);
@Inject(method = "onSelectKnownPacks", at = @At("HEAD"), cancellable = true)
public void onSelectKnownPacks(List<VersionedIdentifier> clientKnownPacks, Consumer<Packet<?>> sender, CallbackInfo ci) {
if (new HashSet<>(this.knownPacks).containsAll(clientKnownPacks)) {
this.syncRegistryAndTags(sender, Set.copyOf(clientKnownPacks));
ci.cancel();
}
}
@Inject(method = "syncRegistryAndTags", at = @At("HEAD"))
public void syncRegistryAndTags(Consumer<Packet<?>> sender, Set<VersionedIdentifier> commonKnownPacks, CallbackInfo ci) {
LOGGER.debug("Syncronizing registries with common known packs: {}", commonKnownPacks);
}
@Inject(method = "sendPacket", at = @At("HEAD"), cancellable = true)
private void sendPacket(Consumer<Packet<?>> sender, CallbackInfo ci) {
if (this.knownPacks.size() > ModResourcePackCreator.MAX_KNOWN_PACKS) {
LOGGER.warn("Too many knownPacks: Found {}; max {}", this.knownPacks.size(), ModResourcePackCreator.MAX_KNOWN_PACKS);
sender.accept(new SelectKnownPacksS2CPacket(this.knownPacks.subList(0, ModResourcePackCreator.MAX_KNOWN_PACKS)));
ci.cancel();
}
}
}

View file

@ -7,12 +7,14 @@
"KeyedResourceReloadListenerMixin",
"LifecycledResourceManagerImplMixin",
"MinecraftServerMixin",
"RegistryLoaderMixin",
"ResourceMixin",
"ResourcePackManagerMixin",
"ResourcePackProfileMixin",
"SelectKnownPacksC2SPacketMixin",
"ServerConfigurationNetworkHandlerMixin",
"ServerPropertiesHandlerMixin",
"SimpleResourceReloadMixin",
"SynchronizeRegistriesTaskMixin",
"TestServerMixin"
],
"server": [

View file

@ -0,0 +1,50 @@
/*
* 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.mixin.resource.loader;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
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.network.packet.Packet;
import net.minecraft.registry.VersionedIdentifier;
import net.minecraft.server.network.SynchronizeRegistriesTask;
import net.fabricmc.fabric.test.resource.loader.BuiltinResourcePackTestMod;
@Mixin(SynchronizeRegistriesTask.class)
public class SynchronizeRegistriesTaskMixin {
@Shadow
@Final
private List<VersionedIdentifier> knownPacks;
@Inject(method = "syncRegistryAndTags", at = @At("HEAD"))
public void syncRegistryAndTags(Consumer<Packet<?>> sender, Set<VersionedIdentifier> commonKnownPacks, CallbackInfo ci) {
BuiltinResourcePackTestMod.LOGGER.info("Syncronizing registries with common known packs: {}", commonKnownPacks);
if (!commonKnownPacks.containsAll(this.knownPacks)) {
BuiltinResourcePackTestMod.LOGGER.error("(Ignore when not local client) Not all server mod data packs known to client. Missing: {}", this.knownPacks.stream().filter(knownPack -> !commonKnownPacks.contains(knownPack)).toList());
}
}
}

View file

@ -30,7 +30,7 @@ import net.fabricmc.loader.api.FabricLoader;
public class BuiltinResourcePackTestMod implements ModInitializer {
public static final String MODID = "fabric-resource-loader-v0-testmod";
private static final Logger LOGGER = LoggerFactory.getLogger(BuiltinResourcePackTestMod.class);
public static final Logger LOGGER = LoggerFactory.getLogger(BuiltinResourcePackTestMod.class);
@Override
public void onInitialize() {

View file

@ -0,0 +1,6 @@
{
"wild_texture": "fabric-resource-loader-v0-testmod:entity/wolf/green",
"angry_texture": "fabric-resource-loader-v0-testmod:entity/wolf/green",
"tame_texture": "fabric-resource-loader-v0-testmod:entity/wolf/green",
"biomes": "minecraft:forest"
}

View file

@ -0,0 +1,13 @@
{
"required": true,
"package": "net.fabricmc.fabric.test.mixin.resource.loader",
"compatibilityLevel": "JAVA_17",
"mixins": [
"SynchronizeRegistriesTaskMixin"
],
"server": [
],
"injectors": {
"defaultRequire": 1
}
}

View file

@ -17,5 +17,8 @@
"server": [
"net.fabricmc.fabric.test.resource.loader.LanguageTestMod"
]
}
},
"mixins": [
"fabric-resource-loader-v0-testmod.mixins.json"
]
}

View file

@ -0,0 +1,6 @@
{
"wild_texture": "fabric-resource-loader-v0-testmod:entity/wolf/pink",
"angry_texture": "fabric-resource-loader-v0-testmod:entity/wolf/pink",
"tame_texture": "fabric-resource-loader-v0-testmod:entity/wolf/pink",
"biomes": "minecraft:forest"
}