diff --git a/fabric-lifecycle-events-v1/src/client/java/net/fabricmc/fabric/api/client/event/lifecycle/v1/ClientLifecycleEvents.java b/fabric-lifecycle-events-v1/src/client/java/net/fabricmc/fabric/api/client/event/lifecycle/v1/ClientLifecycleEvents.java index b2e3fb82ade..bad21420111 100644 --- a/fabric-lifecycle-events-v1/src/client/java/net/fabricmc/fabric/api/client/event/lifecycle/v1/ClientLifecycleEvents.java +++ b/fabric-lifecycle-events-v1/src/client/java/net/fabricmc/fabric/api/client/event/lifecycle/v1/ClientLifecycleEvents.java @@ -48,6 +48,24 @@ private ClientLifecycleEvents() { } }); + /** + * Called before the client begins loading resources. + */ + public static final Event START_RESOURCE_RELOAD = EventFactory.createArrayBacked(StartResourceReload.class, callbacks -> (client, isFirst) -> { + for (StartResourceReload callback : callbacks) { + callback.startResourceReload(client, isFirst); + } + }); + + /** + * Called after the client has finished reloading resources. + */ + public static final Event END_RESOURCE_RELOAD = EventFactory.createArrayBacked(EndResourceReload.class, callbacks -> (client, isFirst) -> { + for (EndResourceReload callback : callbacks) { + callback.endResourceReload(client, isFirst); + } + }); + @FunctionalInterface public interface ClientStarted { void onClientStarted(Minecraft client); @@ -57,4 +75,14 @@ public interface ClientStarted { public interface ClientStopping { void onClientStopping(Minecraft client); } + + @FunctionalInterface + public interface StartResourceReload { + void startResourceReload(Minecraft client, boolean isFirst); + } + + @FunctionalInterface + public interface EndResourceReload { + void endResourceReload(Minecraft client, boolean isFirst); + } } diff --git a/fabric-lifecycle-events-v1/src/client/java/net/fabricmc/fabric/mixin/event/lifecycle/client/MinecraftMixin.java b/fabric-lifecycle-events-v1/src/client/java/net/fabricmc/fabric/mixin/event/lifecycle/client/MinecraftMixin.java index 28179abe9df..9d1b234e821 100644 --- a/fabric-lifecycle-events-v1/src/client/java/net/fabricmc/fabric/mixin/event/lifecycle/client/MinecraftMixin.java +++ b/fabric-lifecycle-events-v1/src/client/java/net/fabricmc/fabric/mixin/event/lifecycle/client/MinecraftMixin.java @@ -16,10 +16,17 @@ package net.fabricmc.fabric.mixin.event.lifecycle.client; +import java.util.concurrent.CompletableFuture; + +import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; 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.Coerce; 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.client.Minecraft; import net.minecraft.client.multiplayer.ClientLevel; @@ -30,6 +37,8 @@ @Mixin(Minecraft.class) public abstract class MinecraftMixin { + @Shadow private boolean gameLoadFinished; + @Inject(at = @At("HEAD"), method = "tick") private void onStartTick(CallbackInfo info) { ClientTickEvents.START_CLIENT_TICK.invoker().onStartTick((Minecraft) (Object) this); @@ -51,6 +60,23 @@ private void onStart(CallbackInfo ci) { ClientLifecycleEvents.CLIENT_STARTED.invoker().onClientStarted((Minecraft) (Object) this); } + @Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/server/packs/resources/ReloadableResourceManager;createReload(Ljava/util/concurrent/Executor;Ljava/util/concurrent/Executor;Ljava/util/concurrent/CompletableFuture;Ljava/util/List;)Lnet/minecraft/server/packs/resources/ReloadInstance;"), method = "") + private void onStartResourceLoad(CallbackInfo ci) { + ClientLifecycleEvents.START_RESOURCE_RELOAD.invoker().startResourceReload((Minecraft) (Object) this, true); + } + + @Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/client/ResourceLoadStateTracker;startReload(Lnet/minecraft/client/ResourceLoadStateTracker$ReloadReason;Ljava/util/List;)V"), method = "reloadResourcePacks(ZLnet/minecraft/client/Minecraft$GameLoadCookie;)Ljava/util/concurrent/CompletableFuture;") + private void onStartResourceReload(CallbackInfoReturnable> cir) { + ClientLifecycleEvents.START_RESOURCE_RELOAD.invoker().startResourceReload((Minecraft) (Object) this, false); + } + + @WrapMethod(method = "onResourceLoadFinished") + private void onResourceLoadFinished(@Coerce Object gameLoadCookie, Operation original) { + boolean first = !this.gameLoadFinished; + original.call(gameLoadCookie); + ClientLifecycleEvents.END_RESOURCE_RELOAD.invoker().endResourceReload((Minecraft) (Object) this, first); + } + @Inject(method = "updateLevelInEngines", at = @At("TAIL")) private void afterClientWorldChange(ClientLevel world, CallbackInfo ci) { if (world != null) { diff --git a/fabric-lifecycle-events-v1/src/testmodClient/java/net/fabricmc/fabric/test/event/lifecycle/client/ClientLifecycleTests.java b/fabric-lifecycle-events-v1/src/testmodClient/java/net/fabricmc/fabric/test/event/lifecycle/client/ClientLifecycleTests.java index eab20bb1e75..59e7dd83aa5 100644 --- a/fabric-lifecycle-events-v1/src/testmodClient/java/net/fabricmc/fabric/test/event/lifecycle/client/ClientLifecycleTests.java +++ b/fabric-lifecycle-events-v1/src/testmodClient/java/net/fabricmc/fabric/test/event/lifecycle/client/ClientLifecycleTests.java @@ -50,6 +50,14 @@ public void onInitializeClient() { System.out.println("Client has started stopping!"); }); + ClientLifecycleEvents.START_RESOURCE_RELOAD.register((client, first) -> { + LOGGER.info((first ? "First r" : "R") + "esource load starting"); + }); + + ClientLifecycleEvents.END_RESOURCE_RELOAD.register((client, first) -> { + LOGGER.info((first ? "First r" : "R") + "esource load completed"); + }); + ClientWorldEvents.AFTER_CLIENT_WORLD_CHANGE.register((client, world) -> { LOGGER.info("Client world changed to {}", world.dimension().identifier()); });