mirror of
https://github.com/FabricMC/fabric.git
synced 2024-11-14 19:25:23 -05:00
Add SyncDataPackContents and TagsLoaded lifecycle events (#2265)
* Add SyncDataPackContents and TagsLoaded lifecycle events
* tagsLoaded -> onTagsLoaded, isClient -> client
* Compile error in testmod
* Make SyncDataPackContents fire for every player
* syncDataPackContents -> onSyncDataPackContents
(cherry picked from commit cc71601c72
)
This commit is contained in:
parent
e1dbfca1b3
commit
33fbc73844
18 changed files with 172 additions and 149 deletions
|
@ -1,11 +0,0 @@
|
|||
{
|
||||
"required": true,
|
||||
"package": "net.fabricmc.fabric.mixin.content.registry.client",
|
||||
"compatibilityLevel": "JAVA_16",
|
||||
"client": [
|
||||
"MixinClientPlayNetworkHandler"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
}
|
||||
}
|
|
@ -31,7 +31,7 @@ import net.minecraft.item.Item;
|
|||
import net.minecraft.item.ItemConvertible;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.CommonLifecycleEvents;
|
||||
import net.fabricmc.fabric.api.registry.FuelRegistry;
|
||||
|
||||
// TODO: Clamp values to 32767 (+ add hook for mods which extend the limit to disable the check?)
|
||||
|
@ -42,10 +42,9 @@ public final class FuelRegistryImpl implements FuelRegistry {
|
|||
private volatile Map<Item, Integer> fuelTimeCache = null; // thread safe via copy-on-write mechanism
|
||||
|
||||
public FuelRegistryImpl() {
|
||||
ServerLifecycleEvents.END_DATA_PACK_RELOAD.register((server, serverResourceManager, success) -> {
|
||||
if (success) {
|
||||
resetCache();
|
||||
}
|
||||
// Reset cache after tags change since it depends on tags.
|
||||
CommonLifecycleEvents.TAGS_LOADED.register((registries, client) -> {
|
||||
resetCache();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -23,11 +23,7 @@
|
|||
},
|
||||
"description": "Adds registries for vanilla mechanics that are missing them.",
|
||||
"mixins": [
|
||||
"fabric-content-registries-v0.mixins.json",
|
||||
{
|
||||
"config": "fabric-content-registries-v0.client.mixins.json",
|
||||
"environment": "client"
|
||||
}
|
||||
"fabric-content-registries-v0.mixins.json"
|
||||
],
|
||||
"accessWidener" : "fabric-content-registries-v0.accesswidener",
|
||||
"custom": {
|
||||
|
|
|
@ -28,12 +28,15 @@ import net.minecraft.client.world.ClientWorld;
|
|||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.network.packet.s2c.play.GameJoinS2CPacket;
|
||||
import net.minecraft.network.packet.s2c.play.PlayerRespawnS2CPacket;
|
||||
import net.minecraft.network.packet.s2c.play.SynchronizeTagsS2CPacket;
|
||||
import net.minecraft.util.registry.DynamicRegistryManager;
|
||||
import net.minecraft.world.chunk.WorldChunk;
|
||||
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientBlockEntityEvents;
|
||||
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientEntityEvents;
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.CommonLifecycleEvents;
|
||||
import net.fabricmc.fabric.impl.event.lifecycle.LoadedChunksCache;
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
|
@ -41,6 +44,8 @@ import net.fabricmc.fabric.impl.event.lifecycle.LoadedChunksCache;
|
|||
abstract class ClientPlayNetworkHandlerMixin {
|
||||
@Shadow
|
||||
private ClientWorld world;
|
||||
@Shadow
|
||||
private DynamicRegistryManager.Immutable registryManager;
|
||||
|
||||
@Inject(method = "onPlayerRespawn", at = @At(value = "NEW", target = "net/minecraft/client/world/ClientWorld"))
|
||||
private void onPlayerRespawn(PlayerRespawnS2CPacket packet, CallbackInfo ci) {
|
||||
|
@ -96,4 +101,16 @@ abstract class ClientPlayNetworkHandlerMixin {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "onSynchronizeTags",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "java/util/Map.forEach(Ljava/util/function/BiConsumer;)V",
|
||||
shift = At.Shift.AFTER, by = 1
|
||||
)
|
||||
)
|
||||
private void hookOnSynchronizeTags(SynchronizeTagsS2CPacket packet, CallbackInfo ci) {
|
||||
CommonLifecycleEvents.TAGS_LOADED.invoker().onTagsLoaded(registryManager, true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* 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.api.event.lifecycle.v1;
|
||||
|
||||
import net.minecraft.util.registry.DynamicRegistryManager;
|
||||
|
||||
import net.fabricmc.fabric.api.event.Event;
|
||||
import net.fabricmc.fabric.api.event.EventFactory;
|
||||
|
||||
public class CommonLifecycleEvents {
|
||||
private CommonLifecycleEvents() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when tags are loaded or updated.
|
||||
*/
|
||||
public static final Event<TagsLoaded> TAGS_LOADED = EventFactory.createArrayBacked(TagsLoaded.class, callbacks -> (registries, client) -> {
|
||||
for (TagsLoaded callback : callbacks) {
|
||||
callback.onTagsLoaded(registries, client);
|
||||
}
|
||||
});
|
||||
|
||||
public interface TagsLoaded {
|
||||
/**
|
||||
* @param registries Up-to-date registries from which the tags can be retrieved.
|
||||
* @param client True if the client just received a sync packet, false if the server just (re)loaded the tags.
|
||||
*/
|
||||
void onTagsLoaded(DynamicRegistryManager registries, boolean client);
|
||||
}
|
||||
}
|
|
@ -19,6 +19,7 @@ package net.fabricmc.fabric.api.event.lifecycle.v1;
|
|||
import net.minecraft.resource.LifecycledResourceManager;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.PlayerManager;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
|
||||
import net.fabricmc.fabric.api.event.Event;
|
||||
import net.fabricmc.fabric.api.event.EventFactory;
|
||||
|
@ -76,6 +77,16 @@ public final class ServerLifecycleEvents {
|
|||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Called when a Minecraft server is about to send tag and recipe data to a player.
|
||||
* @see SyncDataPackContents
|
||||
*/
|
||||
public static final Event<SyncDataPackContents> SYNC_DATA_PACK_CONTENTS = EventFactory.createArrayBacked(SyncDataPackContents.class, callbacks -> (player, joined) -> {
|
||||
for (SyncDataPackContents callback : callbacks) {
|
||||
callback.onSyncDataPackContents(player, joined);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Called before a Minecraft server reloads data packs.
|
||||
*/
|
||||
|
@ -116,6 +127,21 @@ public final class ServerLifecycleEvents {
|
|||
void onServerStopped(MinecraftServer server);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface SyncDataPackContents {
|
||||
/**
|
||||
* Called right before tags and recipes are sent to a player,
|
||||
* either because the player joined, or because the server reloaded resources.
|
||||
* The {@linkplain MinecraftServer#getResourceManager() server resource manager} is up-to-date when this is called.
|
||||
*
|
||||
* <p>For example, this event can be used to sync data loaded with custom resource reloaders.
|
||||
*
|
||||
* @param player Player to which the data is being sent.
|
||||
* @param joined True if the player is joining the server, false if the server finished a successful resource reload.
|
||||
*/
|
||||
void onSyncDataPackContents(ServerPlayerEntity player, boolean joined);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface StartDataPackReload {
|
||||
void startDataPackReload(MinecraftServer server, LifecycledResourceManager resourceManager);
|
||||
|
|
|
@ -14,23 +14,22 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.mixin.content.registry.client;
|
||||
package net.fabricmc.fabric.mixin.event.lifecycle;
|
||||
|
||||
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.CallbackInfo;
|
||||
|
||||
import net.minecraft.client.network.ClientPlayNetworkHandler;
|
||||
import net.minecraft.network.packet.s2c.play.SynchronizeTagsS2CPacket;
|
||||
import net.minecraft.server.DataPackContents;
|
||||
import net.minecraft.util.registry.DynamicRegistryManager;
|
||||
|
||||
import net.fabricmc.fabric.api.registry.FuelRegistry;
|
||||
import net.fabricmc.fabric.impl.content.registry.FuelRegistryImpl;
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.CommonLifecycleEvents;
|
||||
|
||||
@Mixin(ClientPlayNetworkHandler.class)
|
||||
public abstract class MixinClientPlayNetworkHandler {
|
||||
@Inject(at = @At("TAIL"), method = "onSynchronizeTags")
|
||||
private void onSynchronizeTagsHook(SynchronizeTagsS2CPacket packet, CallbackInfo info) {
|
||||
((FuelRegistryImpl) FuelRegistry.INSTANCE).resetCache();
|
||||
@Mixin(DataPackContents.class)
|
||||
public class DataPackContentsMixin {
|
||||
@Inject(method = "refresh", at = @At("TAIL"))
|
||||
private void hookRefresh(DynamicRegistryManager registries, CallbackInfo ci) {
|
||||
CommonLifecycleEvents.TAGS_LOADED.invoker().onTagsLoaded(registries, false);
|
||||
}
|
||||
}
|
|
@ -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.event.lifecycle;
|
||||
|
||||
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.CallbackInfo;
|
||||
|
||||
import net.minecraft.network.ClientConnection;
|
||||
import net.minecraft.server.PlayerManager;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
|
||||
|
||||
@Mixin(PlayerManager.class)
|
||||
public class PlayerManagerMixin {
|
||||
@Inject(
|
||||
method = "onPlayerConnect",
|
||||
at = @At(value = "INVOKE", target = "net/minecraft/network/packet/s2c/play/SynchronizeRecipesS2CPacket.<init>(Ljava/util/Collection;)V")
|
||||
)
|
||||
private void hookOnPlayerConnect(ClientConnection connection, ServerPlayerEntity player, CallbackInfo ci) {
|
||||
ServerLifecycleEvents.SYNC_DATA_PACK_CONTENTS.invoker().onSyncDataPackContents(player, true);
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "onDataPacksReloaded",
|
||||
at = @At(value = "INVOKE", target = "net/minecraft/network/packet/s2c/play/SynchronizeTagsS2CPacket.<init>(Ljava/util/Map;)V")
|
||||
)
|
||||
private void hookOnDataPacksReloaded(CallbackInfo ci) {
|
||||
for (ServerPlayerEntity player : ((PlayerManager) (Object) this).getPlayerList()) {
|
||||
ServerLifecycleEvents.SYNC_DATA_PACK_CONTENTS.invoker().onSyncDataPackContents(player, false);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,7 +3,9 @@
|
|||
"package": "net.fabricmc.fabric.mixin.event.lifecycle",
|
||||
"compatibilityLevel": "JAVA_16",
|
||||
"mixins": [
|
||||
"DataPackContentsMixin",
|
||||
"MinecraftServerMixin",
|
||||
"PlayerManagerMixin",
|
||||
"ServerWorldEntityLoaderMixin",
|
||||
"ServerWorldMixin",
|
||||
"ThreadedAnvilChunkStorageMixin",
|
||||
|
|
|
@ -14,16 +14,16 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.impl.mininglevel;
|
||||
|
||||
import net.minecraft.resource.ResourceType;
|
||||
package net.fabricmc.fabric.test.event.lifecycle;
|
||||
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import net.fabricmc.fabric.api.resource.ResourceManagerHelper;
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.CommonLifecycleEvents;
|
||||
|
||||
public final class FabricMiningLevelInit implements ModInitializer {
|
||||
public class CommonLifecycleTests implements ModInitializer {
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
ResourceManagerHelper.get(ResourceType.SERVER_DATA).registerReloadListener(new MiningLevelCacheInvalidator());
|
||||
CommonLifecycleEvents.TAGS_LOADED.register((registries, client) -> {
|
||||
ServerLifecycleTests.LOGGER.info("Tags (re)loaded on {} {}", client ? "client" : "server", Thread.currentThread());
|
||||
});
|
||||
}
|
||||
}
|
|
@ -50,5 +50,9 @@ public final class ServerLifecycleTests implements ModInitializer {
|
|||
ServerWorldEvents.UNLOAD.register((server, world) -> {
|
||||
LOGGER.info("Unloaded world " + world.getRegistryKey().getValue().toString());
|
||||
});
|
||||
|
||||
ServerLifecycleEvents.SYNC_DATA_PACK_CONTENTS.register((player, joined) -> {
|
||||
LOGGER.info("SyncDataPackContents received for {}", joined ? "join" : "reload");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
},
|
||||
"entrypoints": {
|
||||
"main": [
|
||||
"net.fabricmc.fabric.test.event.lifecycle.CommonLifecycleTests",
|
||||
"net.fabricmc.fabric.test.event.lifecycle.ServerBlockEntityLifecycleTests",
|
||||
"net.fabricmc.fabric.test.event.lifecycle.ServerEntityLifecycleTests",
|
||||
"net.fabricmc.fabric.test.event.lifecycle.ServerLifecycleTests",
|
||||
|
|
|
@ -3,6 +3,7 @@ version = getSubprojectVersion(project)
|
|||
|
||||
moduleDependencies(project, [
|
||||
'fabric-api-base',
|
||||
'fabric-lifecycle-events-v1',
|
||||
'fabric-resource-loader-v0'
|
||||
])
|
||||
|
||||
|
|
|
@ -1,37 +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.mininglevel.client;
|
||||
|
||||
import org.objectweb.asm.Opcodes;
|
||||
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.Slice;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import net.minecraft.client.network.ClientPlayNetworkHandler;
|
||||
import net.minecraft.network.packet.s2c.play.SynchronizeTagsS2CPacket;
|
||||
|
||||
import net.fabricmc.fabric.impl.mininglevel.MiningLevelManagerImpl;
|
||||
|
||||
@Mixin(ClientPlayNetworkHandler.class)
|
||||
abstract class ClientPlayNetworkHandlerMixin {
|
||||
@Inject(method = "onSynchronizeTags", at = @At("RETURN"), slice = @Slice(from = @At(value = "FIELD", target = "Lnet/minecraft/client/network/ClientPlayNetworkHandler;tagManager:Lnet/minecraft/tag/TagManager;", opcode = Opcodes.PUTFIELD)))
|
||||
private void fabric$clearMiningLevelCache(SynchronizeTagsS2CPacket packet, CallbackInfo info) {
|
||||
MiningLevelManagerImpl.clearCache();
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
{
|
||||
"required": true,
|
||||
"package": "net.fabricmc.fabric.mixin.mininglevel.client",
|
||||
"compatibilityLevel": "JAVA_16",
|
||||
"client": [
|
||||
"ClientPlayNetworkHandlerMixin"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
}
|
||||
}
|
|
@ -1,47 +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.mininglevel;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import net.minecraft.resource.ResourceManager;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.api.resource.ResourceReloadListenerKeys;
|
||||
import net.fabricmc.fabric.api.resource.SimpleSynchronousResourceReloadListener;
|
||||
|
||||
final class MiningLevelCacheInvalidator implements SimpleSynchronousResourceReloadListener {
|
||||
private static final Identifier ID = new Identifier("fabric-mining-level-api-v1", "cache_invalidator");
|
||||
private static final Set<Identifier> DEPENDENCIES = Collections.singleton(ResourceReloadListenerKeys.TAGS);
|
||||
|
||||
@Override
|
||||
public Identifier getFabricId() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Identifier> getFabricDependencies() {
|
||||
return DEPENDENCIES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload(ResourceManager manager) {
|
||||
MiningLevelManagerImpl.clearCache();
|
||||
}
|
||||
}
|
|
@ -29,6 +29,7 @@ import net.minecraft.block.BlockState;
|
|||
import net.minecraft.tag.BlockTags;
|
||||
import net.minecraft.tag.TagKey;
|
||||
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.CommonLifecycleEvents;
|
||||
import net.fabricmc.yarn.constants.MiningLevels;
|
||||
|
||||
public final class MiningLevelManagerImpl {
|
||||
|
@ -36,11 +37,13 @@ public final class MiningLevelManagerImpl {
|
|||
private static final String TOOL_TAG_NAMESPACE = "fabric";
|
||||
private static final Pattern TOOL_TAG_PATTERN = Pattern.compile("^needs_tool_level_([0-9]+)$");
|
||||
|
||||
// A cache of block state mining levels. Cleared by
|
||||
// - MiningLevelCacheInvalidator when tags are reloaded
|
||||
// - ClientPlayNetworkHandlerMixin when tags are synced
|
||||
// A cache of block state mining levels. Cleared when tags are updated.
|
||||
private static final ThreadLocal<Reference2IntMap<BlockState>> CACHE = ThreadLocal.withInitial(Reference2IntOpenHashMap::new);
|
||||
|
||||
static {
|
||||
CommonLifecycleEvents.TAGS_LOADED.register((registries, client) -> CACHE.get().clear());
|
||||
}
|
||||
|
||||
public static int getRequiredMiningLevel(BlockState state) {
|
||||
return CACHE.get().computeIfAbsent(state, s -> {
|
||||
int miningLevel = MiningLevels.HAND;
|
||||
|
@ -75,8 +78,4 @@ public final class MiningLevelManagerImpl {
|
|||
return miningLevel;
|
||||
});
|
||||
}
|
||||
|
||||
public static void clearCache() {
|
||||
CACHE.get().clear();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,23 +15,15 @@
|
|||
"authors": [
|
||||
"FabricMC"
|
||||
],
|
||||
"entrypoints": {
|
||||
"main": [
|
||||
"net.fabricmc.fabric.impl.mininglevel.FabricMiningLevelInit"
|
||||
]
|
||||
},
|
||||
"depends": {
|
||||
"fabricloader": ">=0.4.0",
|
||||
"fabric-api-base": "*",
|
||||
"fabric-lifecycle-events-v1": "*",
|
||||
"fabric-resource-loader-v0": "*"
|
||||
},
|
||||
"description": "Adds support for custom mining levels.",
|
||||
"mixins": [
|
||||
"fabric-mining-level-api-v1.mixins.json",
|
||||
{
|
||||
"config": "fabric-mining-level-api-v1.client.mixins.json",
|
||||
"environment": "client"
|
||||
}
|
||||
"fabric-mining-level-api-v1.mixins.json"
|
||||
],
|
||||
"custom": {
|
||||
"fabric-api:module-lifecycle": "stable"
|
||||
|
|
Loading…
Reference in a new issue