4343import net .minecraft .world .level .block .state .StateDefinition ;
4444import org .embeddedt .modernfix .ModernFix ;
4545import org .jetbrains .annotations .NotNull ;
46+ import org .jetbrains .annotations .Nullable ;
4647
4748import java .io .Reader ;
4849import java .lang .ref .WeakReference ;
4950import java .util .AbstractSet ;
5051import java .util .ArrayList ;
5152import java .util .Collection ;
5253import java .util .Collections ;
54+ import java .util .HashMap ;
5355import java .util .HashSet ;
5456import java .util .Iterator ;
5557import java .util .List ;
@@ -97,6 +99,9 @@ public class DynamicModelProvider {
9799 private final Map <ModelResourceLocation , BakedModel > mrlModelOverrides = new ConcurrentHashMap <>();
98100 private final Map <ResourceLocation , ItemModel > itemStackModelOverrides = new ConcurrentHashMap <>();
99101 private final Map <ResourceLocation , BakedModel > standaloneModelOverrides = new ConcurrentHashMap <>();
102+ private final Map <ModelResourceLocation , UnbakedBlockStateModel > unbakedBlockStateModelOverrides = new ConcurrentHashMap <>();
103+
104+ private final List <DynamicModelProvider .DynamicModelPlugin > pluginList = new ArrayList <>();
100105
101106 private static final boolean DEBUG_DYNAMIC_MODEL_LOADING = Boolean .getBoolean ("modernfix.debugDynamicModelLoading" );
102107
@@ -129,6 +134,12 @@ public TextureAtlasSprite reportMissingReference(ModelDebugName modelDebugName,
129134 this .itemModelGenerator = new ItemModelGenerator ();
130135 this .missingModel = this .bakeModel (this .unbakedMissingModel , () -> "missing" );
131136 this .missingItemModel = new MissingItemModel (this .missingModel );
137+ try {
138+ Class .forName ("net.fabricmc.fabric.api.client.model.loading.v1.ModelLoadingPlugin" );
139+ pluginList .add (new FabricDynamicModelHandler (this ));
140+ } catch (Exception ignored ) {
141+ // Fabric API likely not present
142+ }
132143 }
133144
134145 public BakedModel getMissingBakedModel () {
@@ -329,7 +340,21 @@ private Optional<BlockStateModelLoader.LoadedModels> loadBlockStateDefinition(Re
329340 ModernFix .LOGGER .error ("Failed to load blockstate definition {} from pack '{}'" , location , resource .sourcePackId (), e );
330341 }
331342 }
332- return Optional .of (BlockStateModelLoader .loadBlockStateDefinitionStack (location , stateDefinition , loadedDefinitions , this .unbakedMissingModel ));
343+ var loadedModels = new HashMap <>(BlockStateModelLoader .loadBlockStateDefinitionStack (location , stateDefinition , loadedDefinitions , this .unbakedMissingModel ).models ());
344+ if (!pluginList .isEmpty ()) {
345+ loadedModels .replaceAll ((mrl , oldModel ) -> {
346+ UnbakedBlockStateModel ubm = oldModel .model ();
347+ for (var plugin : pluginList ) {
348+ ubm = plugin .modifyBlockModelOnLoad (ubm , mrl , oldModel .state ());
349+ }
350+ if (ubm == oldModel .model ()) {
351+ return oldModel ;
352+ } else {
353+ return new BlockStateModelLoader .LoadedModel (oldModel .state (), ubm );
354+ }
355+ });
356+ }
357+ return Optional .of (new BlockStateModelLoader .LoadedModels (loadedModels ));
333358 }
334359
335360 private BakedModel bakeModel (UnbakedModel model , ModelDebugName name ) {
@@ -364,15 +389,18 @@ private Optional<BakedModel> loadBakedModel(ModelResourceLocation location) {
364389 if (location .variant ().equals ("standalone" ) || location .variant ().equals ("fabric_resource" )) {
365390 return this .loadStandaloneModel (location .id ());
366391 } else {
367- var optLoadedModels = this .loadedStateDefinitions .getUnchecked (location .id ());
368- Optional <UnbakedBlockStateModel > unbakedModelOpt = optLoadedModels .map (loadedModels -> {
369- var loadedModel = loadedModels .models ().get (location );
370- if (loadedModel != null ) {
371- return loadedModel .model ();
372- } else {
373- return null ;
374- }
375- });
392+ Optional <UnbakedBlockStateModel > unbakedModelOpt = Optional .ofNullable (this .unbakedBlockStateModelOverrides .get (location ));
393+ if (unbakedModelOpt .isEmpty ()) {
394+ var optLoadedModels = this .loadedStateDefinitions .getUnchecked (location .id ());
395+ unbakedModelOpt = optLoadedModels .map (loadedModels -> {
396+ var loadedModel = loadedModels .models ().get (location );
397+ if (loadedModel != null ) {
398+ return loadedModel .model ();
399+ } else {
400+ return null ;
401+ }
402+ });
403+ }
376404 return unbakedModelOpt .map (unbakedModel -> {
377405 return this .bakeModel (unbakedModel , location ::toString );
378406 });
@@ -389,12 +417,14 @@ private Optional<BakedModel> loadStandaloneModel(ResourceLocation location) {
389417 });
390418 }
391419
392- private Optional <UnbakedModel > loadBlockModel (ResourceLocation location ) {
420+ private Optional <UnbakedModel > loadBlockModelDefault (ResourceLocation location ) {
393421 if (DEBUG_DYNAMIC_MODEL_LOADING ) {
394422 ModernFix .LOGGER .info ("Loading block model '{}'" , location );
395423 }
396424 if (location .equals (ItemModelGenerator .GENERATED_ITEM_MODEL_ID )) {
397425 return Optional .of (this .itemModelGenerator );
426+ } else if (location .equals (MissingBlockModel .LOCATION )) {
427+ return Optional .of (this .unbakedMissingModel );
398428 }
399429 var resource = this .resourceManager .getResource (ResourceLocation .fromNamespaceAndPath (location .getNamespace (), "models/" + location .getPath () + ".json" ));
400430 if (resource .isPresent ()) {
@@ -411,6 +441,15 @@ private Optional<UnbakedModel> loadBlockModel(ResourceLocation location) {
411441 }
412442 }
413443
444+ private Optional <UnbakedModel > loadBlockModel (ResourceLocation location ) {
445+ Optional <UnbakedModel > value = loadBlockModelDefault (location );
446+ for (var plugin : this .pluginList ) {
447+ value = plugin .modifyModelOnLoad (value , location );
448+ }
449+ return value ;
450+ }
451+
452+
414453 private Optional <ClientItem > loadClientItemProperties (ResourceLocation location ) {
415454 if (DEBUG_DYNAMIC_MODEL_LOADING ) {
416455 ModernFix .LOGGER .info ("Loading client item '{}'" , location );
@@ -460,6 +499,10 @@ public BakedModel getStandaloneModel(ResourceLocation location) {
460499 return this .loadedStandaloneModels .getUnchecked (location ).orElse (this .missingModel );
461500 }
462501
502+ public void addUnbakedBlockStateOverride (ModelResourceLocation location , UnbakedBlockStateModel model ) {
503+ this .unbakedBlockStateModelOverrides .put (location , model );
504+ }
505+
463506 private class DynamicBaker implements ModelBaker {
464507 private final ModelDebugName modelDebugName ;
465508
@@ -469,7 +512,7 @@ private DynamicBaker(ModelDebugName modelDebugName) {
469512
470513 @ Override
471514 public BakedModel bake (ResourceLocation location , ModelState transform ) {
472- return DynamicModelProvider .this .loadBlockModel (location ).map (unbakedModel -> {
515+ return DynamicModelProvider .this .loadedBlockModels . getUnchecked (location ).map (unbakedModel -> {
473516 DynamicModelProvider .this .resolver .clearResolver ();
474517 unbakedModel .resolveDependencies (DynamicModelProvider .this .resolver );
475518 return UnbakedModel .bakeWithTopModelValues (unbakedModel , this , transform );
@@ -526,4 +569,9 @@ public void clearResolver() {
526569 public interface ModelManagerExtension {
527570 DynamicModelProvider mfix$getModelProvider ();
528571 }
572+
573+ public interface DynamicModelPlugin {
574+ Optional <UnbakedModel > modifyModelOnLoad (Optional <UnbakedModel > model , ResourceLocation id );
575+ UnbakedBlockStateModel modifyBlockModelOnLoad (UnbakedBlockStateModel model , ModelResourceLocation id , BlockState state );
576+ }
529577}
0 commit comments