From 49ee251992ed7e968608fe1459251a7c45984cf1 Mon Sep 17 00:00:00 2001 From: XFactHD Date: Mon, 20 Jan 2025 03:05:49 +0100 Subject: [PATCH 1/6] Add model loading plugins to replace ModelEvent.ModifyBakingResult --- .../model/ClientItemInfoLoader.java.patch | 35 ++ .../resources/model/ModelBakery.java.patch | 55 +++- .../resources/model/ModelDiscovery.java.patch | 35 +- .../resources/model/ModelManager.java.patch | 47 ++- .../neoforge/client/ClientHooks.java | 2 + .../neoforge/client/event/ModelEvent.java | 32 +- .../loadingplugin/BlockStateResolver.java | 24 ++ .../loadingplugin/ModelLoadingPlugin.java | 22 ++ .../ModelLoadingPluginManager.java | 309 ++++++++++++++++++ .../model/loadingplugin/ModelModifier.java | 81 +++++ .../PreparableModelLoadingPlugin.java | 16 + .../model/loadingplugin/package-info.java | 13 + .../oldtest/client/model/MegaModelTest.java | 16 +- .../client/model/TRSRTransformerTest.java | 20 +- 14 files changed, 673 insertions(+), 34 deletions(-) create mode 100644 patches/net/minecraft/client/resources/model/ClientItemInfoLoader.java.patch create mode 100644 src/client/java/net/neoforged/neoforge/client/model/loadingplugin/BlockStateResolver.java create mode 100644 src/client/java/net/neoforged/neoforge/client/model/loadingplugin/ModelLoadingPlugin.java create mode 100644 src/client/java/net/neoforged/neoforge/client/model/loadingplugin/ModelLoadingPluginManager.java create mode 100644 src/client/java/net/neoforged/neoforge/client/model/loadingplugin/ModelModifier.java create mode 100644 src/client/java/net/neoforged/neoforge/client/model/loadingplugin/PreparableModelLoadingPlugin.java create mode 100644 src/client/java/net/neoforged/neoforge/client/model/loadingplugin/package-info.java diff --git a/patches/net/minecraft/client/resources/model/ClientItemInfoLoader.java.patch b/patches/net/minecraft/client/resources/model/ClientItemInfoLoader.java.patch new file mode 100644 index 00000000000..7a17cf246b3 --- /dev/null +++ b/patches/net/minecraft/client/resources/model/ClientItemInfoLoader.java.patch @@ -0,0 +1,35 @@ +--- a/net/minecraft/client/resources/model/ClientItemInfoLoader.java ++++ b/net/minecraft/client/resources/model/ClientItemInfoLoader.java +@@ -32,7 +_,13 @@ + private static final Logger LOGGER = LogUtils.getLogger(); + private static final FileToIdConverter LISTER = FileToIdConverter.json("items"); + ++ /** @deprecated Neo: use {@link #scheduleLoad(ResourceManager, Executor, CompletableFuture)} instead */ ++ @Deprecated + public static CompletableFuture scheduleLoad(ResourceManager p_390398_, Executor p_390441_) { ++ return scheduleLoad(p_390398_, p_390441_, CompletableFuture.completedFuture(null)); ++ } ++ ++ public static CompletableFuture scheduleLoad(ResourceManager p_390398_, Executor p_390441_, CompletableFuture pluginFuture) { + RegistryAccess.Frozen registryaccess$frozen = ClientRegistryLayer.createRegistryAccess().compositeAccess(); + return CompletableFuture.>supplyAsync(() -> LISTER.listMatchingResources(p_390398_), p_390441_) + .thenCompose( +@@ -79,12 +_,16 @@ + ) + ) + ); +- return Util.sequence(list).thenApply(p_390406_ -> { ++ return Util.sequence(list).thenCombine(pluginFuture, (p_390406_, pluginManager) -> { + Map map = new HashMap<>(); + + for (ClientItemInfoLoader.PendingLoad clientiteminfoloader$pendingload : p_390406_) { + if (clientiteminfoloader$pendingload.clientItemInfo != null) { +- map.put(clientiteminfoloader$pendingload.id, clientiteminfoloader$pendingload.clientItemInfo); ++ ClientItem clientItem = clientiteminfoloader$pendingload.clientItemInfo; ++ if (pluginManager != null) { ++ clientItem = pluginManager.modifyItemModelOnLoad(clientiteminfoloader$pendingload.id, clientItem); ++ } ++ map.put(clientiteminfoloader$pendingload.id, clientItem); + } + } + diff --git a/patches/net/minecraft/client/resources/model/ModelBakery.java.patch b/patches/net/minecraft/client/resources/model/ModelBakery.java.patch index 35bf1171d23..b399a10411b 100644 --- a/patches/net/minecraft/client/resources/model/ModelBakery.java.patch +++ b/patches/net/minecraft/client/resources/model/ModelBakery.java.patch @@ -1,23 +1,25 @@ --- a/net/minecraft/client/resources/model/ModelBakery.java +++ b/net/minecraft/client/resources/model/ModelBakery.java -@@ -55,7 +_,12 @@ +@@ -55,7 +_,14 @@ private final Map clientInfos; final Map resolvedModels; final ResolvedModel missingModel; + private final net.neoforged.neoforge.client.model.standalone.StandaloneModelLoader.LoadedModels standaloneModels; ++ @org.jetbrains.annotations.Nullable ++ private final net.neoforged.neoforge.client.model.loadingplugin.ModelLoadingPluginManager pluginManager; + /** -+ * @deprecated Neo: use {@link #ModelBakery(EntityModelSet, Map, Map, Map, ResolvedModel, net.neoforged.neoforge.client.model.standalone.StandaloneModelLoader.LoadedModels)} instead ++ * @deprecated Neo: use {@link #ModelBakery(EntityModelSet, Map, Map, Map, ResolvedModel, net.neoforged.neoforge.client.model.standalone.StandaloneModelLoader.LoadedModels, net.neoforged.neoforge.client.model.loadingplugin.ModelLoadingPluginManager)} instead + */ + @Deprecated public ModelBakery( EntityModelSet p_388903_, Map p_251087_, -@@ -63,11 +_,23 @@ +@@ -63,11 +_,25 @@ Map p_388404_, ResolvedModel p_404940_ ) { -+ this(p_388903_, p_251087_, p_250416_, p_388404_, p_404940_, net.neoforged.neoforge.client.model.standalone.StandaloneModelLoader.LoadedModels.EMPTY); ++ this(p_388903_, p_251087_, p_250416_, p_388404_, p_404940_, net.neoforged.neoforge.client.model.standalone.StandaloneModelLoader.LoadedModels.EMPTY, null); + } + + public ModelBakery( @@ -26,7 +28,8 @@ + Map p_250416_, + Map p_388404_, + ResolvedModel p_404940_, -+ net.neoforged.neoforge.client.model.standalone.StandaloneModelLoader.LoadedModels standaloneModels ++ net.neoforged.neoforge.client.model.standalone.StandaloneModelLoader.LoadedModels standaloneModels, ++ @org.jetbrains.annotations.Nullable net.neoforged.neoforge.client.model.loadingplugin.ModelLoadingPluginManager pluginManager + ) { this.entityModelSet = p_388903_; this.unbakedBlockStateModels = p_251087_; @@ -34,9 +37,51 @@ this.resolvedModels = p_388404_; this.missingModel = p_404940_; + this.standaloneModels = standaloneModels; ++ this.pluginManager = pluginManager; } public CompletableFuture bakeModels(SpriteGetter p_404922_, Executor p_405407_) { +@@ -76,7 +_,14 @@ + CompletableFuture> completablefuture = ParallelMapTransform.schedule( + this.unbakedBlockStateModels, (p_409109_, p_409110_) -> { + try { +- return p_409110_.bake(p_409109_, modelbakery$modelbakerimpl); ++ if (this.pluginManager != null) { ++ p_409110_ = this.pluginManager.modifyBlockModelBeforeBake(p_409109_, p_409110_, modelbakery$modelbakerimpl); ++ } ++ BlockStateModel bakedModel = p_409110_.bake(p_409109_, modelbakery$modelbakerimpl); ++ if (this.pluginManager != null) { ++ bakedModel = this.pluginManager.modifyBlockModelAfterBake(p_409109_, bakedModel, p_409110_, modelbakery$modelbakerimpl); ++ } ++ return bakedModel; + } catch (Exception exception) { + LOGGER.warn("Unable to bake model: '{}': {}", p_409109_, exception); + return null; +@@ -87,12 +_,18 @@ + this.clientInfos, + (p_404123_, p_404124_) -> { + try { +- return p_404124_.model() +- .bake( +- new ItemModel.BakingContext( +- modelbakery$modelbakerimpl, this.entityModelSet, modelbakery$missingmodels.item, p_404124_.registrySwapper() +- ) +- ); ++ ItemModel.BakingContext bakingContext = new ItemModel.BakingContext( ++ modelbakery$modelbakerimpl, this.entityModelSet, modelbakery$missingmodels.item, p_404124_.registrySwapper() ++ ); ++ ItemModel.Unbaked unbakedModel = p_404124_.model(); ++ if (this.pluginManager != null) { ++ unbakedModel = this.pluginManager.modifyItemModelBeforeBake(p_404123_, unbakedModel, p_404124_, bakingContext); ++ } ++ ItemModel bakedModel = unbakedModel.bake(bakingContext); ++ if (this.pluginManager != null) { ++ bakedModel = this.pluginManager.modifyItemModelAfterBake(p_404123_, bakedModel, unbakedModel, p_404124_, bakingContext); ++ } ++ return bakedModel; + } catch (Exception exception) { + LOGGER.warn("Unable to bake item model: '{}'", p_404123_, exception); + return null; @@ -100,6 +_,8 @@ }, p_405407_ diff --git a/patches/net/minecraft/client/resources/model/ModelDiscovery.java.patch b/patches/net/minecraft/client/resources/model/ModelDiscovery.java.patch index d715b76a29e..fc9d17b5541 100644 --- a/patches/net/minecraft/client/resources/model/ModelDiscovery.java.patch +++ b/patches/net/minecraft/client/resources/model/ModelDiscovery.java.patch @@ -1,6 +1,33 @@ --- a/net/minecraft/client/resources/model/ModelDiscovery.java +++ b/net/minecraft/client/resources/model/ModelDiscovery.java -@@ -55,7 +_,13 @@ +@@ -33,8 +_,18 @@ + private final Object2ObjectFunction uncachedResolver; + private final ResolvableModel.Resolver resolver; + private final Queue parentDiscoveryQueue = new ArrayDeque<>(); ++ @org.jetbrains.annotations.Nullable ++ private final net.neoforged.neoforge.client.model.loadingplugin.ModelLoadingPluginManager pluginManager; + ++ /** ++ * @deprecated Use {@link ModelDiscovery(Map, UnbakedModel, net.neoforged.neoforge.client.model.loadingplugin.ModelLoadingPluginManager) instead} ++ */ ++ @Deprecated + public ModelDiscovery(Map p_360750_, UnbakedModel p_365355_) { ++ this(p_360750_, p_365355_, null); ++ } ++ ++ public ModelDiscovery(Map p_360750_, UnbakedModel p_365355_, @org.jetbrains.annotations.Nullable net.neoforged.neoforge.client.model.loadingplugin.ModelLoadingPluginManager pluginManager) { + this.missingModel = new ModelDiscovery.ModelWrapper(MissingBlockModel.LOCATION, p_365355_, true); + this.modelWrappers.put(MissingBlockModel.LOCATION, this.missingModel); + this.uncachedResolver = p_404130_ -> { +@@ -48,6 +_,7 @@ + } + }; + this.resolver = this::getOrCreateModel; ++ this.pluginManager = pluginManager; + } + + private static boolean isRoot(UnbakedModel p_405616_) { +@@ -55,15 +_,26 @@ } private ModelDiscovery.ModelWrapper getOrCreateModel(ResourceLocation p_405299_) { @@ -15,7 +42,11 @@ } private ModelDiscovery.ModelWrapper createAndQueueWrapper(ResourceLocation p_405734_, UnbakedModel p_404997_) { -@@ -64,6 +_,8 @@ ++ if (this.pluginManager != null) { ++ p_404997_ = this.pluginManager.modifyModelOnLoad(p_405734_, p_404997_); ++ } + boolean flag = isRoot(p_404997_); + ModelDiscovery.ModelWrapper modeldiscovery$modelwrapper = new ModelDiscovery.ModelWrapper(p_405734_, p_404997_, flag); if (!flag) { this.parentDiscoveryQueue.add(modeldiscovery$modelwrapper); } diff --git a/patches/net/minecraft/client/resources/model/ModelManager.java.patch b/patches/net/minecraft/client/resources/model/ModelManager.java.patch index ab445b072b8..4d787da8637 100644 --- a/patches/net/minecraft/client/resources/model/ModelManager.java.patch +++ b/patches/net/minecraft/client/resources/model/ModelManager.java.patch @@ -1,12 +1,13 @@ --- a/net/minecraft/client/resources/model/ModelManager.java +++ b/net/minecraft/client/resources/model/ModelManager.java -@@ -86,11 +_,15 @@ +@@ -86,11 +_,16 @@ private int maxMipmapLevels; private ModelBakery.MissingModels missingModels; private Object2IntMap modelGroups = Object2IntMaps.emptyMap(); + private final java.util.concurrent.atomic.AtomicReference modelBakery = new java.util.concurrent.atomic.AtomicReference<>(null); + private net.neoforged.neoforge.client.model.standalone.StandaloneModelLoader.BakedModels bakedStandaloneModels; + private Set reportedMissingItemModels = new java.util.HashSet<>(); ++ private volatile CompletableFuture pluginFuture = CompletableFuture.completedFuture(null); public ModelManager(TextureManager p_119406_, BlockColors p_119407_, int p_119408_) { this.blockColors = p_119407_; @@ -32,37 +33,54 @@ } public ClientItem.Properties getItemProperties(ResourceLocation p_390438_) { -@@ -119,8 +_,10 @@ +@@ -117,10 +_,14 @@ + CompletableFuture completablefuture = CompletableFuture.supplyAsync(EntityModelSet::vanilla, p_250550_); + CompletableFuture completablefuture1 = completablefuture.thenApplyAsync(SpecialBlockModelRenderer::vanilla, p_250550_); CompletableFuture> completablefuture2 = loadBlockModels(p_251134_, p_250550_); - CompletableFuture completablefuture3 = BlockStateModelLoader.loadBlockStates(p_251134_, p_250550_); - CompletableFuture completablefuture4 = ClientItemInfoLoader.scheduleLoad(p_251134_, p_250550_); +- CompletableFuture completablefuture3 = BlockStateModelLoader.loadBlockStates(p_251134_, p_250550_); +- CompletableFuture completablefuture4 = ClientItemInfoLoader.scheduleLoad(p_251134_, p_250550_); - CompletableFuture completablefuture5 = CompletableFuture.allOf(completablefuture2, completablefuture3, completablefuture4) - .thenApplyAsync(p_404152_ -> discoverModelDependencies(completablefuture2.join(), completablefuture3.join(), completablefuture4.join()), p_250550_); ++ this.pluginFuture = net.neoforged.neoforge.client.model.loadingplugin.ModelLoadingPluginManager.prepare(p_251134_, p_250550_); ++ CompletableFuture completablefuture3 = BlockStateModelLoader.loadBlockStates(p_251134_, p_250550_) ++ .thenCombine(this.pluginFuture, (models, pluginManager) -> pluginManager.modifyBlockModelsOnLoad(models)); ++ CompletableFuture completablefuture4 = ClientItemInfoLoader.scheduleLoad(p_251134_, p_250550_, this.pluginFuture); + CompletableFuture standaloneModelsFuture = + net.neoforged.neoforge.client.model.standalone.StandaloneModelLoader.load(p_250550_); -+ CompletableFuture completablefuture5 = CompletableFuture.allOf(completablefuture2, completablefuture3, completablefuture4, standaloneModelsFuture) -+ .thenApplyAsync(p_404152_ -> discoverModelDependencies(completablefuture2.join(), completablefuture3.join(), completablefuture4.join(), standaloneModelsFuture.join()), p_250550_); ++ CompletableFuture completablefuture5 = CompletableFuture.allOf(completablefuture2, completablefuture3, completablefuture4, standaloneModelsFuture, this.pluginFuture) ++ .thenApplyAsync(p_404152_ -> discoverModelDependencies(completablefuture2.join(), completablefuture3.join(), completablefuture4.join(), standaloneModelsFuture.join(), this.pluginFuture.join()), p_250550_); CompletableFuture> completablefuture6 = completablefuture3.thenApplyAsync( p_359309_ -> buildModelGroups(this.blockColors, p_359309_), p_250550_ ); -@@ -136,6 +_,7 @@ +@@ -136,6 +_,8 @@ completablefuture, completablefuture1, completablefuture2 -+ , standaloneModelsFuture ++ , standaloneModelsFuture, ++ this.pluginFuture ) ) .toArray(CompletableFuture[]::new) -@@ -156,7 +_,9 @@ +@@ -156,12 +_,19 @@ completablefuture4.join().contents(), modelmanager$resolvedmodels.models(), modelmanager$resolvedmodels.missing() -+ , standaloneModelsFuture.join() ++ , standaloneModelsFuture.join(), ++ this.pluginFuture.join() ); + this.modelBakery.set(modelbakery); return loadModels(map1, modelbakery, object2intmap, completablefuture.join(), completablefuture1.join(), p_250550_); }, p_250550_ + ) + .thenCompose(p_252255_ -> p_252255_.readyForUpload.thenApply(p_251581_ -> (ModelManager.ReloadState)p_252255_)) ++ .thenApplyAsync(state -> { ++ this.pluginFuture = CompletableFuture.completedFuture(null); ++ return state; ++ }) + .thenCompose(p_249079_::wait) + .thenAcceptAsync(p_372566_ -> this.apply(p_372566_, Profiler.get()), p_249221_); + } @@ -170,7 +_,7 @@ return CompletableFuture.>supplyAsync(() -> MODEL_LISTER.listMatchingResources(p_251361_), p_252189_) .thenCompose( @@ -86,21 +104,22 @@ } + /** -+ * @deprecated Neo: use {@link #discoverModelDependencies(Map, BlockStateModelLoader.LoadedModels, ClientItemInfoLoader.LoadedClientInfos, net.neoforged.neoforge.client.model.standalone.StandaloneModelLoader.LoadedModels)} instead ++ * @deprecated Neo: use {@link #discoverModelDependencies(Map, BlockStateModelLoader.LoadedModels, ClientItemInfoLoader.LoadedClientInfos, net.neoforged.neoforge.client.model.standalone.StandaloneModelLoader.LoadedModels, net.neoforged.neoforge.client.model.loadingplugin.ModelLoadingPluginManager)} instead + */ + @Deprecated private static ModelManager.ResolvedModels discoverModelDependencies( Map p_363228_, BlockStateModelLoader.LoadedModels p_361624_, ClientItemInfoLoader.LoadedClientInfos p_390496_ ) { -+ return discoverModelDependencies(p_363228_, p_361624_, p_390496_, net.neoforged.neoforge.client.model.standalone.StandaloneModelLoader.LoadedModels.EMPTY); ++ return discoverModelDependencies(p_363228_, p_361624_, p_390496_, net.neoforged.neoforge.client.model.standalone.StandaloneModelLoader.LoadedModels.EMPTY, null); + } + + private static ModelManager.ResolvedModels discoverModelDependencies( -+ Map p_363228_, BlockStateModelLoader.LoadedModels p_361624_, ClientItemInfoLoader.LoadedClientInfos p_390496_, net.neoforged.neoforge.client.model.standalone.StandaloneModelLoader.LoadedModels standaloneModels ++ Map p_363228_, BlockStateModelLoader.LoadedModels p_361624_, ClientItemInfoLoader.LoadedClientInfos p_390496_, net.neoforged.neoforge.client.model.standalone.StandaloneModelLoader.LoadedModels standaloneModels, @org.jetbrains.annotations.Nullable net.neoforged.neoforge.client.model.loadingplugin.ModelLoadingPluginManager pluginManager + ) { ModelManager.ResolvedModels modelmanager$resolvedmodels; try (Zone zone = Profiler.get().zone("dependencies")) { - ModelDiscovery modeldiscovery = new ModelDiscovery(p_363228_, MissingBlockModel.missingModel()); +- ModelDiscovery modeldiscovery = new ModelDiscovery(p_363228_, MissingBlockModel.missingModel()); ++ ModelDiscovery modeldiscovery = new ModelDiscovery(p_363228_, MissingBlockModel.missingModel(), pluginManager); modeldiscovery.addSpecialModel(ItemModelGenerator.GENERATED_ITEM_MODEL_ID, new ItemModelGenerator()); p_361624_.models().values().forEach(modeldiscovery::addRoot); p_390496_.contents().values().forEach(p_390109_ -> modeldiscovery.addRoot(p_390109_.model())); diff --git a/src/client/java/net/neoforged/neoforge/client/ClientHooks.java b/src/client/java/net/neoforged/neoforge/client/ClientHooks.java index 285d22cfd43..9e381110b91 100644 --- a/src/client/java/net/neoforged/neoforge/client/ClientHooks.java +++ b/src/client/java/net/neoforged/neoforge/client/ClientHooks.java @@ -189,6 +189,7 @@ import net.neoforged.neoforge.client.internal.ForgeSnapshotsModClient; import net.neoforged.neoforge.client.loading.NeoForgeLoadingOverlay; import net.neoforged.neoforge.client.model.block.BlockStateModelHooks; +import net.neoforged.neoforge.client.model.loadingplugin.ModelLoadingPluginManager; import net.neoforged.neoforge.client.pipeline.PipelineModifiers; import net.neoforged.neoforge.client.renderstate.RegisterRenderStateModifiersEvent; import net.neoforged.neoforge.common.NeoForge; @@ -1014,6 +1015,7 @@ public static void initClientHooks(Minecraft mc, ReloadableResourceManager resou DimensionTransitionScreenManager.init(); RenderPipelines.registerCustomPipelines(); PipelineModifiers.init(); + ModelLoadingPluginManager.init(); } // Runs during Minecraft construction, before initial resource loading and during datagen startup diff --git a/src/client/java/net/neoforged/neoforge/client/event/ModelEvent.java b/src/client/java/net/neoforged/neoforge/client/event/ModelEvent.java index c8c4e610b06..0466b1d7c75 100644 --- a/src/client/java/net/neoforged/neoforge/client/event/ModelEvent.java +++ b/src/client/java/net/neoforged/neoforge/client/event/ModelEvent.java @@ -19,6 +19,10 @@ import net.neoforged.fml.LogicalSide; import net.neoforged.fml.event.IModBusEvent; import net.neoforged.neoforge.client.model.UnbakedModelLoader; +import net.neoforged.neoforge.client.model.loadingplugin.BlockStateResolver; +import net.neoforged.neoforge.client.model.loadingplugin.ModelLoadingPlugin; +import net.neoforged.neoforge.client.model.loadingplugin.ModelModifier; +import net.neoforged.neoforge.client.model.loadingplugin.PreparableModelLoadingPlugin; import net.neoforged.neoforge.client.model.standalone.StandaloneModelBaker; import net.neoforged.neoforge.client.model.standalone.StandaloneModelKey; import org.jetbrains.annotations.ApiStatus; @@ -44,7 +48,10 @@ protected ModelEvent() {} *

This event is not {@linkplain ICancellableEvent cancellable}.

* *

This event is fired on the mod-specific event bus, only on the {@linkplain LogicalSide#CLIENT logical client}.

+ * + * @deprecated Use {@link BlockStateResolver}s or {@link ModelModifier}s instead */ + @Deprecated(forRemoval = true, since = "1.21.5") public static class ModifyBakingResult extends ModelEvent implements IModBusEvent { private final ModelBakery.BakingResult bakingResult; private final Function textureGetter; @@ -87,7 +94,7 @@ public ModelBakery getModelBakery() { * Fired when the {@link ModelManager} is notified of the resource manager reloading. * Called after the model registry is set up and cached in the {@link net.minecraft.client.renderer.block.BlockModelShaper}.
* The model registry given by this event is unmodifiable. To modify the model registry, use - * {@link ModelEvent.ModifyBakingResult} instead. + * {@link BlockStateResolver}s or {@link ModelModifier}s instead. * *

This event is not {@linkplain ICancellableEvent cancellable}.

* @@ -182,4 +189,27 @@ public void register(ResourceLocation key, UnbakedModelLoader loader) { loaders.put(key, loader); } } + + public static class RegisterLoadingPlugins extends ModelEvent implements IModBusEvent { + private final Map plugins; + private final Map> preparablePlugins; + + @ApiStatus.Internal + public RegisterLoadingPlugins(Map plugins, Map> preparablePlugins) { + this.plugins = plugins; + this.preparablePlugins = preparablePlugins; + } + + public void registerPlugin(ResourceLocation id, ModelLoadingPlugin plugin) { + if (plugins.putIfAbsent(id, plugin) != null) { + throw new IllegalStateException("Duplicate ModelLoadingPlugin registration with ID " + id); + } + } + + public void registerPlugin(ResourceLocation id, PreparableModelLoadingPlugin plugin) { + if (preparablePlugins.putIfAbsent(id, plugin) != null) { + throw new IllegalStateException("Duplicate PreparableModelLoadingPlugin registration with ID " + id); + } + } + } } diff --git a/src/client/java/net/neoforged/neoforge/client/model/loadingplugin/BlockStateResolver.java b/src/client/java/net/neoforged/neoforge/client/model/loadingplugin/BlockStateResolver.java new file mode 100644 index 00000000000..72f122e9948 --- /dev/null +++ b/src/client/java/net/neoforged/neoforge/client/model/loadingplugin/BlockStateResolver.java @@ -0,0 +1,24 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.client.model.loadingplugin; + +import net.minecraft.client.renderer.block.model.BlockStateModel; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import org.jetbrains.annotations.Nullable; + +public interface BlockStateResolver { + void resolveBlockStates(Context context); + + interface Context { + Block getBlock(); + + @Nullable + BlockStateModel.UnbakedRoot getModel(BlockState state); + + void setModel(BlockState state, BlockStateModel.UnbakedRoot model); + } +} diff --git a/src/client/java/net/neoforged/neoforge/client/model/loadingplugin/ModelLoadingPlugin.java b/src/client/java/net/neoforged/neoforge/client/model/loadingplugin/ModelLoadingPlugin.java new file mode 100644 index 00000000000..e5f93f35b0e --- /dev/null +++ b/src/client/java/net/neoforged/neoforge/client/model/loadingplugin/ModelLoadingPlugin.java @@ -0,0 +1,22 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.client.model.loadingplugin; + +import net.minecraft.world.level.block.Block; + +public interface ModelLoadingPlugin { + void initialize(Context context); + + interface Context { + void registerResolver(Block block, BlockStateResolver resolver); + + default void registerModifier(ModelModifier modifier) { + registerModifier(ModelModifier.Phase.DEFAULT, modifier); + } + + void registerModifier(ModelModifier.Phase phase, ModelModifier modifier); + } +} diff --git a/src/client/java/net/neoforged/neoforge/client/model/loadingplugin/ModelLoadingPluginManager.java b/src/client/java/net/neoforged/neoforge/client/model/loadingplugin/ModelLoadingPluginManager.java new file mode 100644 index 00000000000..ffa60526151 --- /dev/null +++ b/src/client/java/net/neoforged/neoforge/client/model/loadingplugin/ModelLoadingPluginManager.java @@ -0,0 +1,309 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.client.model.loadingplugin; + +import com.google.common.collect.ImmutableList; +import com.mojang.logging.LogUtils; +import it.unimi.dsi.fastutil.objects.Object2ReferenceLinkedOpenHashMap; +import it.unimi.dsi.fastutil.objects.Reference2ReferenceLinkedOpenHashMap; +import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import net.minecraft.Util; +import net.minecraft.client.renderer.block.model.BlockStateModel; +import net.minecraft.client.renderer.item.ClientItem; +import net.minecraft.client.renderer.item.ItemModel; +import net.minecraft.client.resources.model.BlockStateModelLoader; +import net.minecraft.client.resources.model.ModelBaker; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.resources.ResourceManager; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.neoforged.fml.ModLoader; +import net.neoforged.neoforge.client.event.ModelEvent; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.UnknownNullability; +import org.slf4j.Logger; + +public final class ModelLoadingPluginManager { + private static final Logger LOGGER = LogUtils.getLogger(); + private static final Map PLUGINS = new Object2ReferenceLinkedOpenHashMap<>(); + private static final Map> PREPARABLE_PLUGINS = new Object2ReferenceLinkedOpenHashMap<>(); + + private final Map resolvers = new Reference2ReferenceLinkedOpenHashMap<>(); + private final List> onLoadModifiers = new ArrayList<>(); + private final List> onLoadBlockModifiers = new ArrayList<>(); + private final List> beforeBakeBlockModifiers = new ArrayList<>(); + private final List> afterBakeBlockModifiers = new ArrayList<>(); + private final List> onLoadItemModifiers = new ArrayList<>(); + private final List> beforeBakeItemModifiers = new ArrayList<>(); + private final List> afterBakeItemModifiers = new ArrayList<>(); + + private ModelLoadingPluginManager(List plugins) { + var context = new ModelLoadingPlugin.Context() { + @UnknownNullability + private ResourceLocation pluginId; + + @Override + public void registerResolver(Block block, BlockStateResolver resolver) { + if (resolvers.putIfAbsent(block, resolver) != null) { + throw new IllegalStateException("Duplicate BlockStateResolver registration for block " + block); + } + } + + @Override + public void registerModifier(ModelModifier.Phase phase, ModelModifier modifier) { + switch (modifier) { + case ModelModifier.ModifyOnLoad onLoad -> onLoadModifiers.add(new ModifierEntry<>(pluginId, phase, onLoad)); + case ModelModifier.ModifyBlockOnLoad onLoadBlock -> onLoadBlockModifiers.add(new ModifierEntry<>(pluginId, phase, onLoadBlock)); + case ModelModifier.ModifyBlockBeforeBake beforeBakeBlock -> beforeBakeBlockModifiers.add(new ModifierEntry<>(pluginId, phase, beforeBakeBlock)); + case ModelModifier.ModifyBlockAfterBake afterBakeBlock -> afterBakeBlockModifiers.add(new ModifierEntry<>(pluginId, phase, afterBakeBlock)); + case ModelModifier.ModifyItemOnLoad onLoadItem -> onLoadItemModifiers.add(new ModifierEntry<>(pluginId, phase, onLoadItem)); + case ModelModifier.ModifyItemBeforeBake beforeBakeItem -> beforeBakeItemModifiers.add(new ModifierEntry<>(pluginId, phase, beforeBakeItem)); + case ModelModifier.ModifyItemAfterBake afterBakeItem -> afterBakeItemModifiers.add(new ModifierEntry<>(pluginId, phase, afterBakeItem)); + } + } + }; + for (PluginEntry plugin : plugins) { + context.pluginId = plugin.id; + plugin.plugin.initialize(context); + } + + Comparator> comp = Comparator.comparing(ModifierEntry::phase); + this.onLoadModifiers.sort(comp); + this.onLoadBlockModifiers.sort(comp); + this.beforeBakeBlockModifiers.sort(comp); + this.afterBakeBlockModifiers.sort(comp); + this.onLoadItemModifiers.sort(comp); + this.beforeBakeItemModifiers.sort(comp); + this.afterBakeItemModifiers.sort(comp); + } + + @ApiStatus.Internal + public static void init() { + ModLoader.postEvent(new ModelEvent.RegisterLoadingPlugins(PLUGINS, PREPARABLE_PLUGINS)); + } + + public static CompletableFuture prepare(ResourceManager resourceManager, Executor executor) { + List> pluginFutures = new ArrayList<>(); + for (Map.Entry entry : PLUGINS.entrySet()) { + pluginFutures.add(CompletableFuture.completedFuture(new PluginEntry(entry.getKey(), entry.getValue()))); + } + for (Map.Entry> entry : PREPARABLE_PLUGINS.entrySet()) { + pluginFutures.add(preparePlugin(entry.getKey(), entry.getValue(), resourceManager, executor)); + } + return Util.sequence(pluginFutures).thenApplyAsync(ModelLoadingPluginManager::new, executor); + } + + private static CompletableFuture preparePlugin(ResourceLocation pluginId, PreparableModelLoadingPlugin plugin, ResourceManager resourceManager, Executor executor) { + CompletableFuture dataFuture = plugin.load(resourceManager, executor); + return dataFuture.thenApply(data -> new PluginEntry(pluginId, ctx -> plugin.initialize(data, ctx))); + } + + public UnbakedModel modifyModelOnLoad(ResourceLocation id, UnbakedModel model) { + if (onLoadModifiers.isEmpty()) return model; + + ModelModifier.ModifyOnLoad.Context context = new ModelModifier.ModifyOnLoad.Context(id); + for (ModifierEntry entry : onLoadModifiers) { + try { + model = entry.modifier.modifyModelOnLoad(model, context); + } catch (Throwable t) { + LOGGER.error("On-load modifier {} from plugin {} threw an exception", entry.modifier, entry.owningPlugin, t); + } + } + return model; + } + + public BlockStateModelLoader.LoadedModels modifyBlockModelsOnLoad(BlockStateModelLoader.LoadedModels models) { + if (resolvers.isEmpty() && onLoadBlockModifiers.isEmpty()) return models; + + Map map = models.models(); + if (!(map instanceof HashMap)) { + map = new HashMap<>(map); + models = new BlockStateModelLoader.LoadedModels(map); + } + + if (!resolvers.isEmpty()) { + resolveBlockStates(map); + } + if (!onLoadBlockModifiers.isEmpty()) { + modifyBlockModelsOnLoad(map); + } + + return models; + } + + private void resolveBlockStates(Map map) { + var context = new BlockStateResolver.Context() { + private final Map models = new Reference2ReferenceOpenHashMap<>(); + @UnknownNullability + private Block block; + + @Override + public Block getBlock() { + return block; + } + + @Override + @Nullable + public BlockStateModel.UnbakedRoot getModel(BlockState state) { + return map.get(state); + } + + @Override + public void setModel(BlockState state, BlockStateModel.UnbakedRoot model) { + Objects.requireNonNull(state, "State cannot be null"); + Objects.requireNonNull(model, "Model cannot be null"); + + if (!state.is(this.block)) { + throw new IllegalArgumentException("Attempted to set model for state " + state + " on block " + this.block); + } + + if (this.models.putIfAbsent(state, model) != null) { + throw new IllegalStateException("Duplicate model for state " + state + " on block " + this.block); + } + } + }; + for (Map.Entry entry : resolvers.entrySet()) { + Block block = entry.getKey(); + context.block = block; + + boolean errored = false; + try { + entry.getValue().resolveBlockStates(context); + } catch (Throwable t) { + LOGGER.error("Failed to resolve block state models for block {}. Using missing model for all states.", block, t); + errored = true; + } + + Map resolvedModels = context.models; + if (!errored) { + ImmutableList states = block.getStateDefinition().getPossibleStates(); + + if (resolvedModels.size() == states.size()) { + map.putAll(resolvedModels); + } else { + for (BlockState state : states) { + BlockStateModel.UnbakedRoot model = resolvedModels.get(state); + if (model != null) { + map.put(state, model); + } else { + LOGGER.error("Block state resolver did not provide a model for state {} in block {}. Using missing model.", state, block); + } + } + } + } + resolvedModels.clear(); + } + } + + private void modifyBlockModelsOnLoad(Map map) { + var context = new ModelModifier.ModifyBlockOnLoad.Context() { + @UnknownNullability + private BlockState state; + + @Override + public BlockState state() { + return state; + } + }; + map.replaceAll((state, model) -> { + context.state = state; + + for (ModifierEntry entry : onLoadBlockModifiers) { + try { + model = entry.modifier.modifyBlockModelOnLoad(model, context); + } catch (Throwable t) { + LOGGER.error("On-load-block modifier {} from plugin {} threw an exception", entry.modifier, entry.owningPlugin, t); + } + } + return model; + }); + } + + public BlockStateModel.UnbakedRoot modifyBlockModelBeforeBake(BlockState state, BlockStateModel.UnbakedRoot model, ModelBaker baker) { + if (beforeBakeBlockModifiers.isEmpty()) return model; + + ModelModifier.ModifyBlockBeforeBake.Context context = new ModelModifier.ModifyBlockBeforeBake.Context(state, baker); + for (ModifierEntry entry : beforeBakeBlockModifiers) { + try { + model = entry.modifier.modifyBlockModelBeforeBake(model, context); + } catch (Throwable t) { + LOGGER.error("Before-bake-block modifier {} from plugin {} threw an exception", entry.modifier, entry.owningPlugin, t); + } + } + return model; + } + + public BlockStateModel modifyBlockModelAfterBake(BlockState state, BlockStateModel model, BlockStateModel.UnbakedRoot sourceModel, ModelBaker baker) { + if (afterBakeBlockModifiers.isEmpty()) return model; + + ModelModifier.ModifyBlockAfterBake.Context context = new ModelModifier.ModifyBlockAfterBake.Context(state, sourceModel, baker); + for (ModifierEntry entry : afterBakeBlockModifiers) { + try { + model = entry.modifier.modifyBlockModelAfterBake(model, context); + } catch (Throwable t) { + LOGGER.error("After-bake-block modifier {} from plugin {} threw an exception", entry.modifier, entry.owningPlugin, t); + } + } + return model; + } + + public ClientItem modifyItemModelOnLoad(ResourceLocation id, ClientItem clientItem) { + if (onLoadItemModifiers.isEmpty()) return clientItem; + + ModelModifier.ModifyItemOnLoad.Context context = new ModelModifier.ModifyItemOnLoad.Context(id); + ItemModel.Unbaked model = clientItem.model(); + for (ModifierEntry entry : onLoadItemModifiers) { + try { + model = entry.modifier.modifyItemModelOnLoad(model, context); + } catch (Throwable t) { + LOGGER.error("On-load-item modifier {} from plugin {} threw an exception", entry.modifier, entry.owningPlugin, t); + } + } + return new ClientItem(model, clientItem.properties(), clientItem.registrySwapper()); + } + + public ItemModel.Unbaked modifyItemModelBeforeBake(ResourceLocation id, ItemModel.Unbaked model, ClientItem clientItem, ItemModel.BakingContext bakingContext) { + if (beforeBakeItemModifiers.isEmpty()) return model; + + ModelModifier.ModifyItemBeforeBake.Context context = new ModelModifier.ModifyItemBeforeBake.Context(id, clientItem, bakingContext); + for (ModifierEntry entry : beforeBakeItemModifiers) { + try { + model = entry.modifier.modifyItemModelBeforeBake(model, context); + } catch (Throwable t) { + LOGGER.error("Before-bake-item modifier {} from plugin {} threw an exception", entry.modifier, entry.owningPlugin, t); + } + } + return model; + } + + public ItemModel modifyItemModelAfterBake(ResourceLocation id, ItemModel model, ItemModel.Unbaked sourceModel, ClientItem clientItem, ItemModel.BakingContext bakingContext) { + if (afterBakeItemModifiers.isEmpty()) return model; + + ModelModifier.ModifyItemAfterBake.Context context = new ModelModifier.ModifyItemAfterBake.Context(id, sourceModel, clientItem, bakingContext); + for (ModifierEntry entry : afterBakeItemModifiers) { + try { + model = entry.modifier.modifyItemModelAfterBake(model, context); + } catch (Throwable t) { + LOGGER.error("After-bake-item modifier {} from plugin {} threw an exception", entry.modifier, entry.owningPlugin, t); + } + } + return model; + } + + private record PluginEntry(ResourceLocation id, ModelLoadingPlugin plugin) {} + + private record ModifierEntry(ResourceLocation owningPlugin, ModelModifier.Phase phase, T modifier) {} +} diff --git a/src/client/java/net/neoforged/neoforge/client/model/loadingplugin/ModelModifier.java b/src/client/java/net/neoforged/neoforge/client/model/loadingplugin/ModelModifier.java new file mode 100644 index 00000000000..ac8f8bb7a31 --- /dev/null +++ b/src/client/java/net/neoforged/neoforge/client/model/loadingplugin/ModelModifier.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.client.model.loadingplugin; + +import net.minecraft.client.renderer.block.model.BlockStateModel; +import net.minecraft.client.renderer.item.ClientItem; +import net.minecraft.client.renderer.item.ItemModel; +import net.minecraft.client.resources.model.ModelBaker; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.block.state.BlockState; + +public sealed interface ModelModifier { + non-sealed interface ModifyOnLoad extends ModelModifier { + default UnbakedModel modifyModelOnLoad(UnbakedModel model, Context context) { + return model; + } + + record Context(ResourceLocation id) {} + } + + non-sealed interface ModifyBlockOnLoad extends ModelModifier { + default BlockStateModel.UnbakedRoot modifyBlockModelOnLoad(BlockStateModel.UnbakedRoot model, Context context) { + return model; + } + + interface Context { + BlockState state(); + } + } + + non-sealed interface ModifyBlockBeforeBake extends ModelModifier { + default BlockStateModel.UnbakedRoot modifyBlockModelBeforeBake(BlockStateModel.UnbakedRoot model, Context context) { + return model; + } + + record Context(BlockState state, ModelBaker baker) {} + } + + non-sealed interface ModifyBlockAfterBake extends ModelModifier { + default BlockStateModel modifyBlockModelAfterBake(BlockStateModel model, Context context) { + return model; + } + + record Context(BlockState state, BlockStateModel.UnbakedRoot sourceModel, ModelBaker baker) {} + } + + non-sealed interface ModifyItemOnLoad extends ModelModifier { + default ItemModel.Unbaked modifyItemModelOnLoad(ItemModel.Unbaked model, Context context) { + return model; + } + + record Context(ResourceLocation id) {} + } + + non-sealed interface ModifyItemBeforeBake extends ModelModifier { + default ItemModel.Unbaked modifyItemModelBeforeBake(ItemModel.Unbaked model, Context context) { + return model; + } + + record Context(ResourceLocation id, ClientItem clientItem, ItemModel.BakingContext bakingContext) {} + } + + non-sealed interface ModifyItemAfterBake extends ModelModifier { + default ItemModel modifyItemModelAfterBake(ItemModel model, Context context) { + return model; + } + + record Context(ResourceLocation id, ItemModel.Unbaked sourceModel, ClientItem clientItem, ItemModel.BakingContext bakingContext) {} + } + + enum Phase { + OVERRIDE, + DEFAULT, + WRAP, + WRAP_LAST, + } +} diff --git a/src/client/java/net/neoforged/neoforge/client/model/loadingplugin/PreparableModelLoadingPlugin.java b/src/client/java/net/neoforged/neoforge/client/model/loadingplugin/PreparableModelLoadingPlugin.java new file mode 100644 index 00000000000..23cbaeeda14 --- /dev/null +++ b/src/client/java/net/neoforged/neoforge/client/model/loadingplugin/PreparableModelLoadingPlugin.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.client.model.loadingplugin; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import net.minecraft.server.packs.resources.ResourceManager; + +public interface PreparableModelLoadingPlugin { + CompletableFuture load(ResourceManager resourceManager, Executor executor); + + void initialize(T data, ModelLoadingPlugin.Context context); +} diff --git a/src/client/java/net/neoforged/neoforge/client/model/loadingplugin/package-info.java b/src/client/java/net/neoforged/neoforge/client/model/loadingplugin/package-info.java new file mode 100644 index 00000000000..f09f18901e2 --- /dev/null +++ b/src/client/java/net/neoforged/neoforge/client/model/loadingplugin/package-info.java @@ -0,0 +1,13 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +@FieldsAreNonnullByDefault +@MethodsReturnNonnullByDefault +@ParametersAreNonnullByDefault +package net.neoforged.neoforge.client.model.loadingplugin; + +import javax.annotation.ParametersAreNonnullByDefault; +import net.minecraft.FieldsAreNonnullByDefault; +import net.minecraft.MethodsReturnNonnullByDefault; diff --git a/tests/src/main/java/net/neoforged/neoforge/oldtest/client/model/MegaModelTest.java b/tests/src/main/java/net/neoforged/neoforge/oldtest/client/model/MegaModelTest.java index 0be37cb36d5..f527612423f 100644 --- a/tests/src/main/java/net/neoforged/neoforge/oldtest/client/model/MegaModelTest.java +++ b/tests/src/main/java/net/neoforged/neoforge/oldtest/client/model/MegaModelTest.java @@ -17,6 +17,7 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; import net.minecraft.util.Mth; import net.minecraft.util.RandomSource; import net.minecraft.world.InteractionHand; @@ -44,6 +45,7 @@ import net.neoforged.neoforge.client.model.DelegateBlockStateModel; import net.neoforged.neoforge.client.model.IQuadTransformer; import net.neoforged.neoforge.client.model.QuadTransformers; +import net.neoforged.neoforge.client.model.loadingplugin.ModelModifier; import net.neoforged.neoforge.event.BuildCreativeModeTabContentsEvent; import net.neoforged.neoforge.model.data.ModelData; import net.neoforged.neoforge.model.data.ModelProperty; @@ -99,10 +101,16 @@ private void addCreative(BuildCreativeModeTabContentsEvent event) { @EventBusSubscriber(value = Dist.CLIENT, modid = MOD_ID, bus = EventBusSubscriber.Bus.MOD) public static class ClientEvents { @SubscribeEvent - public static void onModelBakingCompleted(ModelEvent.ModifyBakingResult event) { - event.getBakingResult().blockStateModels().computeIfPresent( - TEST_BLOCK.value().defaultBlockState(), - (n, m) -> new TransformingModelWrapper(m)); + public static void onRegisterModelLoadingPlugins(ModelEvent.RegisterLoadingPlugins event) { + event.registerPlugin(ResourceLocation.fromNamespaceAndPath(MOD_ID, "wrap"), ctx -> ctx.registerModifier(new ModelModifier.ModifyBlockAfterBake() { + @Override + public BlockStateModel modifyBlockModelAfterBake(BlockStateModel model, Context context) { + if (context.state().is(TEST_BLOCK)) { + return new TransformingModelWrapper(model); + } + return model; + } + })); } } diff --git a/tests/src/main/java/net/neoforged/neoforge/oldtest/client/model/TRSRTransformerTest.java b/tests/src/main/java/net/neoforged/neoforge/oldtest/client/model/TRSRTransformerTest.java index 5b63814908b..0b47921ee98 100644 --- a/tests/src/main/java/net/neoforged/neoforge/oldtest/client/model/TRSRTransformerTest.java +++ b/tests/src/main/java/net/neoforged/neoforge/oldtest/client/model/TRSRTransformerTest.java @@ -8,7 +8,6 @@ import com.mojang.math.Transformation; import java.util.Arrays; import java.util.List; -import java.util.Map; import net.minecraft.Util; import net.minecraft.client.renderer.block.model.BakedQuad; import net.minecraft.client.renderer.block.model.BlockModelPart; @@ -17,6 +16,7 @@ import net.minecraft.client.resources.model.QuadCollection; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; +import net.minecraft.resources.ResourceLocation; import net.minecraft.util.RandomSource; import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.CreativeModeTabs; @@ -31,6 +31,7 @@ import net.neoforged.neoforge.client.model.DelegateBlockStateModel; import net.neoforged.neoforge.client.model.IQuadTransformer; import net.neoforged.neoforge.client.model.QuadTransformers; +import net.neoforged.neoforge.client.model.loadingplugin.ModelModifier; import net.neoforged.neoforge.common.util.TransformationHelper; import net.neoforged.neoforge.event.BuildCreativeModeTabContentsEvent; import net.neoforged.neoforge.registries.DeferredBlock; @@ -51,7 +52,7 @@ public class TRSRTransformerTest { public TRSRTransformerTest(IEventBus modEventBus) { if (FMLEnvironment.dist.isClient()) { - modEventBus.addListener(TRSRTransformerTest::onModelBake); + modEventBus.addListener(TRSRTransformerTest::onRegisterModelLoadingPlugins); } BLOCKS.register(modEventBus); ITEMS.register(modEventBus); @@ -63,13 +64,16 @@ private static void addCreative(BuildCreativeModeTabContentsEvent event) { event.accept(TEST_ITEM); } - private static void onModelBake(ModelEvent.ModifyBakingResult e) { - Map models = e.getBakingResult().blockStateModels(); - for (BlockState state : models.keySet()) { - if (state.is(TEST_BLOCK)) { - models.put(state, new MyBakedModel(models.get(state))); + private static void onRegisterModelLoadingPlugins(ModelEvent.RegisterLoadingPlugins event) { + event.registerPlugin(ResourceLocation.fromNamespaceAndPath(MODID, "wrap"), ctx -> ctx.registerModifier(new ModelModifier.ModifyBlockAfterBake() { + @Override + public BlockStateModel modifyBlockModelAfterBake(BlockStateModel model, Context context) { + if (context.state().is(TEST_BLOCK)) { + return new MyBakedModel(model); + } + return model; } - } + })); } private static class MyBakedModel extends DelegateBlockStateModel { From 83a4cd8190444e2c1b6d8ababae50818f5fd7550 Mon Sep 17 00:00:00 2001 From: XFactHD Date: Thu, 27 Mar 2025 20:03:29 +0100 Subject: [PATCH 2/6] Remove blockstate resolvers in favor of custom block model definitions --- .../neoforge/client/event/ModelEvent.java | 6 +- .../loadingplugin/BlockStateResolver.java | 24 ----- .../loadingplugin/ModelLoadingPlugin.java | 4 - .../ModelLoadingPluginManager.java | 93 +------------------ 4 files changed, 6 insertions(+), 121 deletions(-) delete mode 100644 src/client/java/net/neoforged/neoforge/client/model/loadingplugin/BlockStateResolver.java diff --git a/src/client/java/net/neoforged/neoforge/client/event/ModelEvent.java b/src/client/java/net/neoforged/neoforge/client/event/ModelEvent.java index 0466b1d7c75..1a245407aff 100644 --- a/src/client/java/net/neoforged/neoforge/client/event/ModelEvent.java +++ b/src/client/java/net/neoforged/neoforge/client/event/ModelEvent.java @@ -19,7 +19,7 @@ import net.neoforged.fml.LogicalSide; import net.neoforged.fml.event.IModBusEvent; import net.neoforged.neoforge.client.model.UnbakedModelLoader; -import net.neoforged.neoforge.client.model.loadingplugin.BlockStateResolver; +import net.neoforged.neoforge.client.model.block.CustomBlockModelDefinition; import net.neoforged.neoforge.client.model.loadingplugin.ModelLoadingPlugin; import net.neoforged.neoforge.client.model.loadingplugin.ModelModifier; import net.neoforged.neoforge.client.model.loadingplugin.PreparableModelLoadingPlugin; @@ -49,7 +49,7 @@ protected ModelEvent() {} * *

This event is fired on the mod-specific event bus, only on the {@linkplain LogicalSide#CLIENT logical client}.

* - * @deprecated Use {@link BlockStateResolver}s or {@link ModelModifier}s instead + * @deprecated Use {@link CustomBlockModelDefinition}s or {@link ModelModifier}s instead */ @Deprecated(forRemoval = true, since = "1.21.5") public static class ModifyBakingResult extends ModelEvent implements IModBusEvent { @@ -94,7 +94,7 @@ public ModelBakery getModelBakery() { * Fired when the {@link ModelManager} is notified of the resource manager reloading. * Called after the model registry is set up and cached in the {@link net.minecraft.client.renderer.block.BlockModelShaper}.
* The model registry given by this event is unmodifiable. To modify the model registry, use - * {@link BlockStateResolver}s or {@link ModelModifier}s instead. + * {@link CustomBlockModelDefinition}s or {@link ModelModifier}s instead. * *

This event is not {@linkplain ICancellableEvent cancellable}.

* diff --git a/src/client/java/net/neoforged/neoforge/client/model/loadingplugin/BlockStateResolver.java b/src/client/java/net/neoforged/neoforge/client/model/loadingplugin/BlockStateResolver.java deleted file mode 100644 index 72f122e9948..00000000000 --- a/src/client/java/net/neoforged/neoforge/client/model/loadingplugin/BlockStateResolver.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) NeoForged and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.neoforged.neoforge.client.model.loadingplugin; - -import net.minecraft.client.renderer.block.model.BlockStateModel; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.state.BlockState; -import org.jetbrains.annotations.Nullable; - -public interface BlockStateResolver { - void resolveBlockStates(Context context); - - interface Context { - Block getBlock(); - - @Nullable - BlockStateModel.UnbakedRoot getModel(BlockState state); - - void setModel(BlockState state, BlockStateModel.UnbakedRoot model); - } -} diff --git a/src/client/java/net/neoforged/neoforge/client/model/loadingplugin/ModelLoadingPlugin.java b/src/client/java/net/neoforged/neoforge/client/model/loadingplugin/ModelLoadingPlugin.java index e5f93f35b0e..2f3251b25c5 100644 --- a/src/client/java/net/neoforged/neoforge/client/model/loadingplugin/ModelLoadingPlugin.java +++ b/src/client/java/net/neoforged/neoforge/client/model/loadingplugin/ModelLoadingPlugin.java @@ -5,14 +5,10 @@ package net.neoforged.neoforge.client.model.loadingplugin; -import net.minecraft.world.level.block.Block; - public interface ModelLoadingPlugin { void initialize(Context context); interface Context { - void registerResolver(Block block, BlockStateResolver resolver); - default void registerModifier(ModelModifier modifier) { registerModifier(ModelModifier.Phase.DEFAULT, modifier); } diff --git a/src/client/java/net/neoforged/neoforge/client/model/loadingplugin/ModelLoadingPluginManager.java b/src/client/java/net/neoforged/neoforge/client/model/loadingplugin/ModelLoadingPluginManager.java index ffa60526151..d6e82da83a6 100644 --- a/src/client/java/net/neoforged/neoforge/client/model/loadingplugin/ModelLoadingPluginManager.java +++ b/src/client/java/net/neoforged/neoforge/client/model/loadingplugin/ModelLoadingPluginManager.java @@ -5,17 +5,13 @@ package net.neoforged.neoforge.client.model.loadingplugin; -import com.google.common.collect.ImmutableList; import com.mojang.logging.LogUtils; import it.unimi.dsi.fastutil.objects.Object2ReferenceLinkedOpenHashMap; -import it.unimi.dsi.fastutil.objects.Reference2ReferenceLinkedOpenHashMap; -import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap; import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; import net.minecraft.Util; @@ -27,12 +23,10 @@ import net.minecraft.client.resources.model.UnbakedModel; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.packs.resources.ResourceManager; -import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; import net.neoforged.fml.ModLoader; import net.neoforged.neoforge.client.event.ModelEvent; import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.UnknownNullability; import org.slf4j.Logger; @@ -41,7 +35,6 @@ public final class ModelLoadingPluginManager { private static final Map PLUGINS = new Object2ReferenceLinkedOpenHashMap<>(); private static final Map> PREPARABLE_PLUGINS = new Object2ReferenceLinkedOpenHashMap<>(); - private final Map resolvers = new Reference2ReferenceLinkedOpenHashMap<>(); private final List> onLoadModifiers = new ArrayList<>(); private final List> onLoadBlockModifiers = new ArrayList<>(); private final List> beforeBakeBlockModifiers = new ArrayList<>(); @@ -55,13 +48,6 @@ private ModelLoadingPluginManager(List plugins) { @UnknownNullability private ResourceLocation pluginId; - @Override - public void registerResolver(Block block, BlockStateResolver resolver) { - if (resolvers.putIfAbsent(block, resolver) != null) { - throw new IllegalStateException("Duplicate BlockStateResolver registration for block " + block); - } - } - @Override public void registerModifier(ModelModifier.Phase phase, ModelModifier modifier) { switch (modifier) { @@ -126,7 +112,7 @@ public UnbakedModel modifyModelOnLoad(ResourceLocation id, UnbakedModel model) { } public BlockStateModelLoader.LoadedModels modifyBlockModelsOnLoad(BlockStateModelLoader.LoadedModels models) { - if (resolvers.isEmpty() && onLoadBlockModifiers.isEmpty()) return models; + if (onLoadBlockModifiers.isEmpty()) return models; Map map = models.models(); if (!(map instanceof HashMap)) { @@ -134,81 +120,6 @@ public BlockStateModelLoader.LoadedModels modifyBlockModelsOnLoad(BlockStateMode models = new BlockStateModelLoader.LoadedModels(map); } - if (!resolvers.isEmpty()) { - resolveBlockStates(map); - } - if (!onLoadBlockModifiers.isEmpty()) { - modifyBlockModelsOnLoad(map); - } - - return models; - } - - private void resolveBlockStates(Map map) { - var context = new BlockStateResolver.Context() { - private final Map models = new Reference2ReferenceOpenHashMap<>(); - @UnknownNullability - private Block block; - - @Override - public Block getBlock() { - return block; - } - - @Override - @Nullable - public BlockStateModel.UnbakedRoot getModel(BlockState state) { - return map.get(state); - } - - @Override - public void setModel(BlockState state, BlockStateModel.UnbakedRoot model) { - Objects.requireNonNull(state, "State cannot be null"); - Objects.requireNonNull(model, "Model cannot be null"); - - if (!state.is(this.block)) { - throw new IllegalArgumentException("Attempted to set model for state " + state + " on block " + this.block); - } - - if (this.models.putIfAbsent(state, model) != null) { - throw new IllegalStateException("Duplicate model for state " + state + " on block " + this.block); - } - } - }; - for (Map.Entry entry : resolvers.entrySet()) { - Block block = entry.getKey(); - context.block = block; - - boolean errored = false; - try { - entry.getValue().resolveBlockStates(context); - } catch (Throwable t) { - LOGGER.error("Failed to resolve block state models for block {}. Using missing model for all states.", block, t); - errored = true; - } - - Map resolvedModels = context.models; - if (!errored) { - ImmutableList states = block.getStateDefinition().getPossibleStates(); - - if (resolvedModels.size() == states.size()) { - map.putAll(resolvedModels); - } else { - for (BlockState state : states) { - BlockStateModel.UnbakedRoot model = resolvedModels.get(state); - if (model != null) { - map.put(state, model); - } else { - LOGGER.error("Block state resolver did not provide a model for state {} in block {}. Using missing model.", state, block); - } - } - } - } - resolvedModels.clear(); - } - } - - private void modifyBlockModelsOnLoad(Map map) { var context = new ModelModifier.ModifyBlockOnLoad.Context() { @UnknownNullability private BlockState state; @@ -230,6 +141,8 @@ public BlockState state() { } return model; }); + + return models; } public BlockStateModel.UnbakedRoot modifyBlockModelBeforeBake(BlockState state, BlockStateModel.UnbakedRoot model, ModelBaker baker) { From 000daa6d36736703f6ffeffe75d2ce2b7e2fc828 Mon Sep 17 00:00:00 2001 From: XFactHD Date: Thu, 27 Mar 2025 20:22:40 +0100 Subject: [PATCH 3/6] Remove item on-load modifier It's effectively equivalent to the item before bake modifier --- .../model/ClientItemInfoLoader.java.patch | 35 ------------------- .../resources/model/ModelManager.java.patch | 7 ++-- 2 files changed, 3 insertions(+), 39 deletions(-) delete mode 100644 patches/net/minecraft/client/resources/model/ClientItemInfoLoader.java.patch diff --git a/patches/net/minecraft/client/resources/model/ClientItemInfoLoader.java.patch b/patches/net/minecraft/client/resources/model/ClientItemInfoLoader.java.patch deleted file mode 100644 index 7a17cf246b3..00000000000 --- a/patches/net/minecraft/client/resources/model/ClientItemInfoLoader.java.patch +++ /dev/null @@ -1,35 +0,0 @@ ---- a/net/minecraft/client/resources/model/ClientItemInfoLoader.java -+++ b/net/minecraft/client/resources/model/ClientItemInfoLoader.java -@@ -32,7 +_,13 @@ - private static final Logger LOGGER = LogUtils.getLogger(); - private static final FileToIdConverter LISTER = FileToIdConverter.json("items"); - -+ /** @deprecated Neo: use {@link #scheduleLoad(ResourceManager, Executor, CompletableFuture)} instead */ -+ @Deprecated - public static CompletableFuture scheduleLoad(ResourceManager p_390398_, Executor p_390441_) { -+ return scheduleLoad(p_390398_, p_390441_, CompletableFuture.completedFuture(null)); -+ } -+ -+ public static CompletableFuture scheduleLoad(ResourceManager p_390398_, Executor p_390441_, CompletableFuture pluginFuture) { - RegistryAccess.Frozen registryaccess$frozen = ClientRegistryLayer.createRegistryAccess().compositeAccess(); - return CompletableFuture.>supplyAsync(() -> LISTER.listMatchingResources(p_390398_), p_390441_) - .thenCompose( -@@ -79,12 +_,16 @@ - ) - ) - ); -- return Util.sequence(list).thenApply(p_390406_ -> { -+ return Util.sequence(list).thenCombine(pluginFuture, (p_390406_, pluginManager) -> { - Map map = new HashMap<>(); - - for (ClientItemInfoLoader.PendingLoad clientiteminfoloader$pendingload : p_390406_) { - if (clientiteminfoloader$pendingload.clientItemInfo != null) { -- map.put(clientiteminfoloader$pendingload.id, clientiteminfoloader$pendingload.clientItemInfo); -+ ClientItem clientItem = clientiteminfoloader$pendingload.clientItemInfo; -+ if (pluginManager != null) { -+ clientItem = pluginManager.modifyItemModelOnLoad(clientiteminfoloader$pendingload.id, clientItem); -+ } -+ map.put(clientiteminfoloader$pendingload.id, clientItem); - } - } - diff --git a/patches/net/minecraft/client/resources/model/ModelManager.java.patch b/patches/net/minecraft/client/resources/model/ModelManager.java.patch index 4d787da8637..fd27aafac91 100644 --- a/patches/net/minecraft/client/resources/model/ModelManager.java.patch +++ b/patches/net/minecraft/client/resources/model/ModelManager.java.patch @@ -38,13 +38,12 @@ CompletableFuture completablefuture1 = completablefuture.thenApplyAsync(SpecialBlockModelRenderer::vanilla, p_250550_); CompletableFuture> completablefuture2 = loadBlockModels(p_251134_, p_250550_); - CompletableFuture completablefuture3 = BlockStateModelLoader.loadBlockStates(p_251134_, p_250550_); -- CompletableFuture completablefuture4 = ClientItemInfoLoader.scheduleLoad(p_251134_, p_250550_); -- CompletableFuture completablefuture5 = CompletableFuture.allOf(completablefuture2, completablefuture3, completablefuture4) -- .thenApplyAsync(p_404152_ -> discoverModelDependencies(completablefuture2.join(), completablefuture3.join(), completablefuture4.join()), p_250550_); + this.pluginFuture = net.neoforged.neoforge.client.model.loadingplugin.ModelLoadingPluginManager.prepare(p_251134_, p_250550_); + CompletableFuture completablefuture3 = BlockStateModelLoader.loadBlockStates(p_251134_, p_250550_) + .thenCombine(this.pluginFuture, (models, pluginManager) -> pluginManager.modifyBlockModelsOnLoad(models)); -+ CompletableFuture completablefuture4 = ClientItemInfoLoader.scheduleLoad(p_251134_, p_250550_, this.pluginFuture); + CompletableFuture completablefuture4 = ClientItemInfoLoader.scheduleLoad(p_251134_, p_250550_); +- CompletableFuture completablefuture5 = CompletableFuture.allOf(completablefuture2, completablefuture3, completablefuture4) +- .thenApplyAsync(p_404152_ -> discoverModelDependencies(completablefuture2.join(), completablefuture3.join(), completablefuture4.join()), p_250550_); + CompletableFuture standaloneModelsFuture = + net.neoforged.neoforge.client.model.standalone.StandaloneModelLoader.load(p_250550_); + CompletableFuture completablefuture5 = CompletableFuture.allOf(completablefuture2, completablefuture3, completablefuture4, standaloneModelsFuture, this.pluginFuture) From 928eb5e41a890360589833ee0971a59e889a1243 Mon Sep 17 00:00:00 2001 From: XFactHD Date: Thu, 27 Mar 2025 23:42:13 +0100 Subject: [PATCH 4/6] Move on-load modifier injection point --- .../resources/model/ModelDiscovery.java.patch | 35 ++----------------- .../resources/model/ModelManager.java.patch | 20 ++++++----- .../ModelLoadingPluginManager.java | 27 ++++++++------ 3 files changed, 30 insertions(+), 52 deletions(-) diff --git a/patches/net/minecraft/client/resources/model/ModelDiscovery.java.patch b/patches/net/minecraft/client/resources/model/ModelDiscovery.java.patch index fc9d17b5541..d715b76a29e 100644 --- a/patches/net/minecraft/client/resources/model/ModelDiscovery.java.patch +++ b/patches/net/minecraft/client/resources/model/ModelDiscovery.java.patch @@ -1,33 +1,6 @@ --- a/net/minecraft/client/resources/model/ModelDiscovery.java +++ b/net/minecraft/client/resources/model/ModelDiscovery.java -@@ -33,8 +_,18 @@ - private final Object2ObjectFunction uncachedResolver; - private final ResolvableModel.Resolver resolver; - private final Queue parentDiscoveryQueue = new ArrayDeque<>(); -+ @org.jetbrains.annotations.Nullable -+ private final net.neoforged.neoforge.client.model.loadingplugin.ModelLoadingPluginManager pluginManager; - -+ /** -+ * @deprecated Use {@link ModelDiscovery(Map, UnbakedModel, net.neoforged.neoforge.client.model.loadingplugin.ModelLoadingPluginManager) instead} -+ */ -+ @Deprecated - public ModelDiscovery(Map p_360750_, UnbakedModel p_365355_) { -+ this(p_360750_, p_365355_, null); -+ } -+ -+ public ModelDiscovery(Map p_360750_, UnbakedModel p_365355_, @org.jetbrains.annotations.Nullable net.neoforged.neoforge.client.model.loadingplugin.ModelLoadingPluginManager pluginManager) { - this.missingModel = new ModelDiscovery.ModelWrapper(MissingBlockModel.LOCATION, p_365355_, true); - this.modelWrappers.put(MissingBlockModel.LOCATION, this.missingModel); - this.uncachedResolver = p_404130_ -> { -@@ -48,6 +_,7 @@ - } - }; - this.resolver = this::getOrCreateModel; -+ this.pluginManager = pluginManager; - } - - private static boolean isRoot(UnbakedModel p_405616_) { -@@ -55,15 +_,26 @@ +@@ -55,7 +_,13 @@ } private ModelDiscovery.ModelWrapper getOrCreateModel(ResourceLocation p_405299_) { @@ -42,11 +15,7 @@ } private ModelDiscovery.ModelWrapper createAndQueueWrapper(ResourceLocation p_405734_, UnbakedModel p_404997_) { -+ if (this.pluginManager != null) { -+ p_404997_ = this.pluginManager.modifyModelOnLoad(p_405734_, p_404997_); -+ } - boolean flag = isRoot(p_404997_); - ModelDiscovery.ModelWrapper modeldiscovery$modelwrapper = new ModelDiscovery.ModelWrapper(p_405734_, p_404997_, flag); +@@ -64,6 +_,8 @@ if (!flag) { this.parentDiscoveryQueue.add(modeldiscovery$modelwrapper); } diff --git a/patches/net/minecraft/client/resources/model/ModelManager.java.patch b/patches/net/minecraft/client/resources/model/ModelManager.java.patch index fd27aafac91..51e11db90b3 100644 --- a/patches/net/minecraft/client/resources/model/ModelManager.java.patch +++ b/patches/net/minecraft/client/resources/model/ModelManager.java.patch @@ -33,12 +33,15 @@ } public ClientItem.Properties getItemProperties(ResourceLocation p_390438_) { -@@ -117,10 +_,14 @@ +@@ -116,11 +_,16 @@ + ) { CompletableFuture completablefuture = CompletableFuture.supplyAsync(EntityModelSet::vanilla, p_250550_); CompletableFuture completablefuture1 = completablefuture.thenApplyAsync(SpecialBlockModelRenderer::vanilla, p_250550_); - CompletableFuture> completablefuture2 = loadBlockModels(p_251134_, p_250550_); +- CompletableFuture> completablefuture2 = loadBlockModels(p_251134_, p_250550_); - CompletableFuture completablefuture3 = BlockStateModelLoader.loadBlockStates(p_251134_, p_250550_); + this.pluginFuture = net.neoforged.neoforge.client.model.loadingplugin.ModelLoadingPluginManager.prepare(p_251134_, p_250550_); ++ CompletableFuture> completablefuture2 = loadBlockModels(p_251134_, p_250550_) ++ .thenCombine(this.pluginFuture, (models, pluginManager) -> pluginManager.modifyModelsOnLoad(models)); + CompletableFuture completablefuture3 = BlockStateModelLoader.loadBlockStates(p_251134_, p_250550_) + .thenCombine(this.pluginFuture, (models, pluginManager) -> pluginManager.modifyBlockModelsOnLoad(models)); CompletableFuture completablefuture4 = ClientItemInfoLoader.scheduleLoad(p_251134_, p_250550_); @@ -46,8 +49,8 @@ - .thenApplyAsync(p_404152_ -> discoverModelDependencies(completablefuture2.join(), completablefuture3.join(), completablefuture4.join()), p_250550_); + CompletableFuture standaloneModelsFuture = + net.neoforged.neoforge.client.model.standalone.StandaloneModelLoader.load(p_250550_); -+ CompletableFuture completablefuture5 = CompletableFuture.allOf(completablefuture2, completablefuture3, completablefuture4, standaloneModelsFuture, this.pluginFuture) -+ .thenApplyAsync(p_404152_ -> discoverModelDependencies(completablefuture2.join(), completablefuture3.join(), completablefuture4.join(), standaloneModelsFuture.join(), this.pluginFuture.join()), p_250550_); ++ CompletableFuture completablefuture5 = CompletableFuture.allOf(completablefuture2, completablefuture3, completablefuture4, standaloneModelsFuture) ++ .thenApplyAsync(p_404152_ -> discoverModelDependencies(completablefuture2.join(), completablefuture3.join(), completablefuture4.join(), standaloneModelsFuture.join()), p_250550_); CompletableFuture> completablefuture6 = completablefuture3.thenApplyAsync( p_359309_ -> buildModelGroups(this.blockColors, p_359309_), p_250550_ ); @@ -103,22 +106,21 @@ } + /** -+ * @deprecated Neo: use {@link #discoverModelDependencies(Map, BlockStateModelLoader.LoadedModels, ClientItemInfoLoader.LoadedClientInfos, net.neoforged.neoforge.client.model.standalone.StandaloneModelLoader.LoadedModels, net.neoforged.neoforge.client.model.loadingplugin.ModelLoadingPluginManager)} instead ++ * @deprecated Neo: use {@link #discoverModelDependencies(Map, BlockStateModelLoader.LoadedModels, ClientItemInfoLoader.LoadedClientInfos, net.neoforged.neoforge.client.model.standalone.StandaloneModelLoader.LoadedModels)} instead + */ + @Deprecated private static ModelManager.ResolvedModels discoverModelDependencies( Map p_363228_, BlockStateModelLoader.LoadedModels p_361624_, ClientItemInfoLoader.LoadedClientInfos p_390496_ ) { -+ return discoverModelDependencies(p_363228_, p_361624_, p_390496_, net.neoforged.neoforge.client.model.standalone.StandaloneModelLoader.LoadedModels.EMPTY, null); ++ return discoverModelDependencies(p_363228_, p_361624_, p_390496_, net.neoforged.neoforge.client.model.standalone.StandaloneModelLoader.LoadedModels.EMPTY); + } + + private static ModelManager.ResolvedModels discoverModelDependencies( -+ Map p_363228_, BlockStateModelLoader.LoadedModels p_361624_, ClientItemInfoLoader.LoadedClientInfos p_390496_, net.neoforged.neoforge.client.model.standalone.StandaloneModelLoader.LoadedModels standaloneModels, @org.jetbrains.annotations.Nullable net.neoforged.neoforge.client.model.loadingplugin.ModelLoadingPluginManager pluginManager ++ Map p_363228_, BlockStateModelLoader.LoadedModels p_361624_, ClientItemInfoLoader.LoadedClientInfos p_390496_, net.neoforged.neoforge.client.model.standalone.StandaloneModelLoader.LoadedModels standaloneModels + ) { ModelManager.ResolvedModels modelmanager$resolvedmodels; try (Zone zone = Profiler.get().zone("dependencies")) { -- ModelDiscovery modeldiscovery = new ModelDiscovery(p_363228_, MissingBlockModel.missingModel()); -+ ModelDiscovery modeldiscovery = new ModelDiscovery(p_363228_, MissingBlockModel.missingModel(), pluginManager); + ModelDiscovery modeldiscovery = new ModelDiscovery(p_363228_, MissingBlockModel.missingModel()); modeldiscovery.addSpecialModel(ItemModelGenerator.GENERATED_ITEM_MODEL_ID, new ItemModelGenerator()); p_361624_.models().values().forEach(modeldiscovery::addRoot); p_390496_.contents().values().forEach(p_390109_ -> modeldiscovery.addRoot(p_390109_.model())); diff --git a/src/client/java/net/neoforged/neoforge/client/model/loadingplugin/ModelLoadingPluginManager.java b/src/client/java/net/neoforged/neoforge/client/model/loadingplugin/ModelLoadingPluginManager.java index d6e82da83a6..41939d8dd63 100644 --- a/src/client/java/net/neoforged/neoforge/client/model/loadingplugin/ModelLoadingPluginManager.java +++ b/src/client/java/net/neoforged/neoforge/client/model/loadingplugin/ModelLoadingPluginManager.java @@ -97,18 +97,25 @@ private static CompletableFuture preparePlugin(ResourceLocation return dataFuture.thenApply(data -> new PluginEntry(pluginId, ctx -> plugin.initialize(data, ctx))); } - public UnbakedModel modifyModelOnLoad(ResourceLocation id, UnbakedModel model) { - if (onLoadModifiers.isEmpty()) return model; + public Map modifyModelsOnLoad(Map models) { + if (onLoadModifiers.isEmpty()) return models; - ModelModifier.ModifyOnLoad.Context context = new ModelModifier.ModifyOnLoad.Context(id); - for (ModifierEntry entry : onLoadModifiers) { - try { - model = entry.modifier.modifyModelOnLoad(model, context); - } catch (Throwable t) { - LOGGER.error("On-load modifier {} from plugin {} threw an exception", entry.modifier, entry.owningPlugin, t); - } + if (!(models instanceof HashMap)) { + models = new HashMap<>(models); } - return model; + + models.replaceAll((id, model) -> { + ModelModifier.ModifyOnLoad.Context context = new ModelModifier.ModifyOnLoad.Context(id); + for (ModifierEntry entry : onLoadModifiers) { + try { + model = entry.modifier.modifyModelOnLoad(model, context); + } catch (Throwable t) { + LOGGER.error("On-load modifier {} from plugin {} threw an exception", entry.modifier, entry.owningPlugin, t); + } + } + return model; + }); + return models; } public BlockStateModelLoader.LoadedModels modifyBlockModelsOnLoad(BlockStateModelLoader.LoadedModels models) { From 2828bcc03899e7feb28bc1c94436c5e77dbe82fd Mon Sep 17 00:00:00 2001 From: XFactHD Date: Fri, 28 Mar 2025 00:02:43 +0100 Subject: [PATCH 5/6] Actually remove item on-load modifier --- .../ModelLoadingPluginManager.java | 18 ------------------ .../model/loadingplugin/ModelModifier.java | 8 -------- 2 files changed, 26 deletions(-) diff --git a/src/client/java/net/neoforged/neoforge/client/model/loadingplugin/ModelLoadingPluginManager.java b/src/client/java/net/neoforged/neoforge/client/model/loadingplugin/ModelLoadingPluginManager.java index 41939d8dd63..7ae10771af1 100644 --- a/src/client/java/net/neoforged/neoforge/client/model/loadingplugin/ModelLoadingPluginManager.java +++ b/src/client/java/net/neoforged/neoforge/client/model/loadingplugin/ModelLoadingPluginManager.java @@ -39,7 +39,6 @@ public final class ModelLoadingPluginManager { private final List> onLoadBlockModifiers = new ArrayList<>(); private final List> beforeBakeBlockModifiers = new ArrayList<>(); private final List> afterBakeBlockModifiers = new ArrayList<>(); - private final List> onLoadItemModifiers = new ArrayList<>(); private final List> beforeBakeItemModifiers = new ArrayList<>(); private final List> afterBakeItemModifiers = new ArrayList<>(); @@ -55,7 +54,6 @@ public void registerModifier(ModelModifier.Phase phase, ModelModifier modifier) case ModelModifier.ModifyBlockOnLoad onLoadBlock -> onLoadBlockModifiers.add(new ModifierEntry<>(pluginId, phase, onLoadBlock)); case ModelModifier.ModifyBlockBeforeBake beforeBakeBlock -> beforeBakeBlockModifiers.add(new ModifierEntry<>(pluginId, phase, beforeBakeBlock)); case ModelModifier.ModifyBlockAfterBake afterBakeBlock -> afterBakeBlockModifiers.add(new ModifierEntry<>(pluginId, phase, afterBakeBlock)); - case ModelModifier.ModifyItemOnLoad onLoadItem -> onLoadItemModifiers.add(new ModifierEntry<>(pluginId, phase, onLoadItem)); case ModelModifier.ModifyItemBeforeBake beforeBakeItem -> beforeBakeItemModifiers.add(new ModifierEntry<>(pluginId, phase, beforeBakeItem)); case ModelModifier.ModifyItemAfterBake afterBakeItem -> afterBakeItemModifiers.add(new ModifierEntry<>(pluginId, phase, afterBakeItem)); } @@ -71,7 +69,6 @@ public void registerModifier(ModelModifier.Phase phase, ModelModifier modifier) this.onLoadBlockModifiers.sort(comp); this.beforeBakeBlockModifiers.sort(comp); this.afterBakeBlockModifiers.sort(comp); - this.onLoadItemModifiers.sort(comp); this.beforeBakeItemModifiers.sort(comp); this.afterBakeItemModifiers.sort(comp); } @@ -180,21 +177,6 @@ public BlockStateModel modifyBlockModelAfterBake(BlockState state, BlockStateMod return model; } - public ClientItem modifyItemModelOnLoad(ResourceLocation id, ClientItem clientItem) { - if (onLoadItemModifiers.isEmpty()) return clientItem; - - ModelModifier.ModifyItemOnLoad.Context context = new ModelModifier.ModifyItemOnLoad.Context(id); - ItemModel.Unbaked model = clientItem.model(); - for (ModifierEntry entry : onLoadItemModifiers) { - try { - model = entry.modifier.modifyItemModelOnLoad(model, context); - } catch (Throwable t) { - LOGGER.error("On-load-item modifier {} from plugin {} threw an exception", entry.modifier, entry.owningPlugin, t); - } - } - return new ClientItem(model, clientItem.properties(), clientItem.registrySwapper()); - } - public ItemModel.Unbaked modifyItemModelBeforeBake(ResourceLocation id, ItemModel.Unbaked model, ClientItem clientItem, ItemModel.BakingContext bakingContext) { if (beforeBakeItemModifiers.isEmpty()) return model; diff --git a/src/client/java/net/neoforged/neoforge/client/model/loadingplugin/ModelModifier.java b/src/client/java/net/neoforged/neoforge/client/model/loadingplugin/ModelModifier.java index ac8f8bb7a31..e026629f616 100644 --- a/src/client/java/net/neoforged/neoforge/client/model/loadingplugin/ModelModifier.java +++ b/src/client/java/net/neoforged/neoforge/client/model/loadingplugin/ModelModifier.java @@ -48,14 +48,6 @@ default BlockStateModel modifyBlockModelAfterBake(BlockStateModel model, Context record Context(BlockState state, BlockStateModel.UnbakedRoot sourceModel, ModelBaker baker) {} } - non-sealed interface ModifyItemOnLoad extends ModelModifier { - default ItemModel.Unbaked modifyItemModelOnLoad(ItemModel.Unbaked model, Context context) { - return model; - } - - record Context(ResourceLocation id) {} - } - non-sealed interface ModifyItemBeforeBake extends ModelModifier { default ItemModel.Unbaked modifyItemModelBeforeBake(ItemModel.Unbaked model, Context context) { return model; From 7947f32e41a0a9fc23a37f15c2b8c71fdf51eaf6 Mon Sep 17 00:00:00 2001 From: XFactHD Date: Wed, 2 Apr 2025 22:51:56 +0200 Subject: [PATCH 6/6] Re-use modifier context objects --- .../ModelLoadingPluginManager.java | 144 +++++++++++++++++- .../model/loadingplugin/ModelModifier.java | 36 ++++- 2 files changed, 170 insertions(+), 10 deletions(-) diff --git a/src/client/java/net/neoforged/neoforge/client/model/loadingplugin/ModelLoadingPluginManager.java b/src/client/java/net/neoforged/neoforge/client/model/loadingplugin/ModelLoadingPluginManager.java index 7ae10771af1..b11a82560fe 100644 --- a/src/client/java/net/neoforged/neoforge/client/model/loadingplugin/ModelLoadingPluginManager.java +++ b/src/client/java/net/neoforged/neoforge/client/model/loadingplugin/ModelLoadingPluginManager.java @@ -41,6 +41,10 @@ public final class ModelLoadingPluginManager { private final List> afterBakeBlockModifiers = new ArrayList<>(); private final List> beforeBakeItemModifiers = new ArrayList<>(); private final List> afterBakeItemModifiers = new ArrayList<>(); + private final ThreadLocal beforeBakeBlockContext = ThreadLocal.withInitial(BlockBeforeBakeContextImpl::new); + private final ThreadLocal afterBakeBlockContext = ThreadLocal.withInitial(BlockAfterBakeContextImpl::new); + private final ThreadLocal beforeBakeItemContext = ThreadLocal.withInitial(ItemBeforeBakeContextImpl::new); + private final ThreadLocal afterBakeItemContext = ThreadLocal.withInitial(ItemAfterBakeContextImpl::new); private ModelLoadingPluginManager(List plugins) { var context = new ModelLoadingPlugin.Context() { @@ -101,8 +105,18 @@ public Map modifyModelsOnLoad(Map(models); } + var context = new ModelModifier.ModifyOnLoad.Context() { + @UnknownNullability + private ResourceLocation id; + + @Override + public ResourceLocation id() { + return this.id; + } + }; + models.replaceAll((id, model) -> { - ModelModifier.ModifyOnLoad.Context context = new ModelModifier.ModifyOnLoad.Context(id); + context.id = id; for (ModifierEntry entry : onLoadModifiers) { try { model = entry.modifier.modifyModelOnLoad(model, context); @@ -152,7 +166,7 @@ public BlockState state() { public BlockStateModel.UnbakedRoot modifyBlockModelBeforeBake(BlockState state, BlockStateModel.UnbakedRoot model, ModelBaker baker) { if (beforeBakeBlockModifiers.isEmpty()) return model; - ModelModifier.ModifyBlockBeforeBake.Context context = new ModelModifier.ModifyBlockBeforeBake.Context(state, baker); + BlockBeforeBakeContextImpl context = beforeBakeBlockContext.get().setup(state, baker); for (ModifierEntry entry : beforeBakeBlockModifiers) { try { model = entry.modifier.modifyBlockModelBeforeBake(model, context); @@ -166,7 +180,7 @@ public BlockStateModel.UnbakedRoot modifyBlockModelBeforeBake(BlockState state, public BlockStateModel modifyBlockModelAfterBake(BlockState state, BlockStateModel model, BlockStateModel.UnbakedRoot sourceModel, ModelBaker baker) { if (afterBakeBlockModifiers.isEmpty()) return model; - ModelModifier.ModifyBlockAfterBake.Context context = new ModelModifier.ModifyBlockAfterBake.Context(state, sourceModel, baker); + BlockAfterBakeContextImpl context = afterBakeBlockContext.get().setup(state, sourceModel, baker); for (ModifierEntry entry : afterBakeBlockModifiers) { try { model = entry.modifier.modifyBlockModelAfterBake(model, context); @@ -180,7 +194,7 @@ public BlockStateModel modifyBlockModelAfterBake(BlockState state, BlockStateMod public ItemModel.Unbaked modifyItemModelBeforeBake(ResourceLocation id, ItemModel.Unbaked model, ClientItem clientItem, ItemModel.BakingContext bakingContext) { if (beforeBakeItemModifiers.isEmpty()) return model; - ModelModifier.ModifyItemBeforeBake.Context context = new ModelModifier.ModifyItemBeforeBake.Context(id, clientItem, bakingContext); + ItemBeforeBakeContextImpl context = beforeBakeItemContext.get().setup(id, clientItem, bakingContext); for (ModifierEntry entry : beforeBakeItemModifiers) { try { model = entry.modifier.modifyItemModelBeforeBake(model, context); @@ -194,7 +208,7 @@ public ItemModel.Unbaked modifyItemModelBeforeBake(ResourceLocation id, ItemMode public ItemModel modifyItemModelAfterBake(ResourceLocation id, ItemModel model, ItemModel.Unbaked sourceModel, ClientItem clientItem, ItemModel.BakingContext bakingContext) { if (afterBakeItemModifiers.isEmpty()) return model; - ModelModifier.ModifyItemAfterBake.Context context = new ModelModifier.ModifyItemAfterBake.Context(id, sourceModel, clientItem, bakingContext); + ItemAfterBakeContextImpl context = afterBakeItemContext.get().setup(id, sourceModel, clientItem, bakingContext); for (ModifierEntry entry : afterBakeItemModifiers) { try { model = entry.modifier.modifyItemModelAfterBake(model, context); @@ -208,4 +222,124 @@ public ItemModel modifyItemModelAfterBake(ResourceLocation id, ItemModel model, private record PluginEntry(ResourceLocation id, ModelLoadingPlugin plugin) {} private record ModifierEntry(ResourceLocation owningPlugin, ModelModifier.Phase phase, T modifier) {} + + private static final class BlockBeforeBakeContextImpl implements ModelModifier.ModifyBlockBeforeBake.Context { + @UnknownNullability + private BlockState state; + @UnknownNullability + private ModelBaker baker; + + @Override + public BlockState state() { + return this.state; + } + + @Override + public ModelBaker baker() { + return this.baker; + } + + public BlockBeforeBakeContextImpl setup(BlockState state, ModelBaker baker) { + this.state = state; + this.baker = baker; + return this; + } + } + + private static final class BlockAfterBakeContextImpl implements ModelModifier.ModifyBlockAfterBake.Context { + @UnknownNullability + private BlockState state; + private BlockStateModel.@UnknownNullability UnbakedRoot sourceModel; + @UnknownNullability + private ModelBaker baker; + + @Override + public BlockState state() { + return this.state; + } + + @Override + public BlockStateModel.UnbakedRoot sourceModel() { + return this.sourceModel; + } + + @Override + public ModelBaker baker() { + return this.baker; + } + + public BlockAfterBakeContextImpl setup(BlockState state, BlockStateModel.UnbakedRoot sourceModel, ModelBaker baker) { + this.state = state; + this.sourceModel = sourceModel; + this.baker = baker; + return this; + } + } + + private static final class ItemBeforeBakeContextImpl implements ModelModifier.ModifyItemBeforeBake.Context { + @UnknownNullability + private ResourceLocation id; + @UnknownNullability + private ClientItem clientItem; + private ItemModel.@UnknownNullability BakingContext bakingContext; + + @Override + public ResourceLocation id() { + return this.id; + } + + @Override + public ClientItem clientItem() { + return this.clientItem; + } + + @Override + public ItemModel.BakingContext bakingContext() { + return this.bakingContext; + } + + public ItemBeforeBakeContextImpl setup(ResourceLocation id, ClientItem clientItem, ItemModel.BakingContext bakingContext) { + this.id = id; + this.clientItem = clientItem; + this.bakingContext = bakingContext; + return this; + } + } + + private static final class ItemAfterBakeContextImpl implements ModelModifier.ModifyItemAfterBake.Context { + @UnknownNullability + private ResourceLocation id; + private ItemModel.@UnknownNullability Unbaked sourceModel; + @UnknownNullability + private ClientItem clientItem; + private ItemModel.@UnknownNullability BakingContext bakingContext; + + @Override + public ResourceLocation id() { + return this.id; + } + + @Override + public ItemModel.Unbaked sourceModel() { + return this.sourceModel; + } + + @Override + public ClientItem clientItem() { + return this.clientItem; + } + + @Override + public ItemModel.BakingContext bakingContext() { + return this.bakingContext; + } + + public ItemAfterBakeContextImpl setup(ResourceLocation id, ItemModel.Unbaked sourceModel, ClientItem clientItem, ItemModel.BakingContext bakingContext) { + this.id = id; + this.sourceModel = sourceModel; + this.clientItem = clientItem; + this.bakingContext = bakingContext; + return this; + } + } } diff --git a/src/client/java/net/neoforged/neoforge/client/model/loadingplugin/ModelModifier.java b/src/client/java/net/neoforged/neoforge/client/model/loadingplugin/ModelModifier.java index e026629f616..d8031d419e2 100644 --- a/src/client/java/net/neoforged/neoforge/client/model/loadingplugin/ModelModifier.java +++ b/src/client/java/net/neoforged/neoforge/client/model/loadingplugin/ModelModifier.java @@ -19,7 +19,9 @@ default UnbakedModel modifyModelOnLoad(UnbakedModel model, Context context) { return model; } - record Context(ResourceLocation id) {} + interface Context { + ResourceLocation id(); + } } non-sealed interface ModifyBlockOnLoad extends ModelModifier { @@ -37,7 +39,11 @@ default BlockStateModel.UnbakedRoot modifyBlockModelBeforeBake(BlockStateModel.U return model; } - record Context(BlockState state, ModelBaker baker) {} + interface Context { + BlockState state(); + + ModelBaker baker(); + } } non-sealed interface ModifyBlockAfterBake extends ModelModifier { @@ -45,7 +51,13 @@ default BlockStateModel modifyBlockModelAfterBake(BlockStateModel model, Context return model; } - record Context(BlockState state, BlockStateModel.UnbakedRoot sourceModel, ModelBaker baker) {} + interface Context { + BlockState state(); + + BlockStateModel.UnbakedRoot sourceModel(); + + ModelBaker baker(); + } } non-sealed interface ModifyItemBeforeBake extends ModelModifier { @@ -53,7 +65,13 @@ default ItemModel.Unbaked modifyItemModelBeforeBake(ItemModel.Unbaked model, Con return model; } - record Context(ResourceLocation id, ClientItem clientItem, ItemModel.BakingContext bakingContext) {} + interface Context { + ResourceLocation id(); + + ClientItem clientItem(); + + ItemModel.BakingContext bakingContext(); + } } non-sealed interface ModifyItemAfterBake extends ModelModifier { @@ -61,7 +79,15 @@ default ItemModel modifyItemModelAfterBake(ItemModel model, Context context) { return model; } - record Context(ResourceLocation id, ItemModel.Unbaked sourceModel, ClientItem clientItem, ItemModel.BakingContext bakingContext) {} + interface Context { + ResourceLocation id(); + + ItemModel.Unbaked sourceModel(); + + ClientItem clientItem(); + + ItemModel.BakingContext bakingContext(); + } } enum Phase {