diff --git a/patchwork-dispatcher/build.gradle b/patchwork-dispatcher/build.gradle index 3efbace5..6f243688 100644 --- a/patchwork-dispatcher/build.gradle +++ b/patchwork-dispatcher/build.gradle @@ -6,4 +6,5 @@ dependencies { compile project(path: ':patchwork-registries', configuration: 'dev') compile project(path: ':patchwork-events-lifecycle', configuration: 'dev') compile project(path: ':patchwork-events-rendering', configuration: 'dev') + compile project(path: ':patchwork-model-loader', configuration: 'dev') } diff --git a/patchwork-dispatcher/src/main/java/net/patchworkmc/impl/Patchwork.java b/patchwork-dispatcher/src/main/java/net/patchworkmc/impl/Patchwork.java index 541f2b18..94880ed6 100644 --- a/patchwork-dispatcher/src/main/java/net/patchworkmc/impl/Patchwork.java +++ b/patchwork-dispatcher/src/main/java/net/patchworkmc/impl/Patchwork.java @@ -53,6 +53,7 @@ import net.patchworkmc.api.ForgeInitializer; import net.patchworkmc.impl.event.lifecycle.LifecycleEvents; import net.patchworkmc.impl.event.render.RenderEvents; +import net.patchworkmc.impl.modelloader.ModelEventDispatcher; import net.patchworkmc.impl.registries.RegistryEventDispatcher; public class Patchwork implements ModInitializer { @@ -122,6 +123,7 @@ public void onInitialize() { dispatch(mods, FMLCommonSetupEvent::new); DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> { + ModelEventDispatcher.fireModelRegistryEvent(); dispatch(mods, container -> new FMLClientSetupEvent(MinecraftClient::getInstance, container)); RenderEvents.registerEventDispatcher(event -> dispatch(mods, event)); }); diff --git a/patchwork-model-loader/build.gradle b/patchwork-model-loader/build.gradle new file mode 100644 index 00000000..0baf9131 --- /dev/null +++ b/patchwork-model-loader/build.gradle @@ -0,0 +1,6 @@ +archivesBaseName = "patchwork-model-loader" +version = getSubprojectVersion(project, "0.1.0") + +dependencies { + compile project(path: ':patchwork-fml', configuration: 'dev') +} diff --git a/patchwork-model-loader/src/main/java/net/minecraftforge/client/event/ModelBakeEvent.java b/patchwork-model-loader/src/main/java/net/minecraftforge/client/event/ModelBakeEvent.java new file mode 100644 index 00000000..18ac9787 --- /dev/null +++ b/patchwork-model-loader/src/main/java/net/minecraftforge/client/event/ModelBakeEvent.java @@ -0,0 +1,59 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.client.event; + +import java.util.Map; + +import net.minecraftforge.client.model.ModelLoader; +import net.minecraftforge.eventbus.api.Event; + +import net.minecraft.client.render.model.BakedModel; +import net.minecraft.client.render.model.BakedModelManager; +import net.minecraft.util.Identifier; + +/** + * Fired when the BakedModelManager is notified of the resource manager reloading. + * Called after model registry is setup, but before it's passed to + * BlockModelShapes. + */ +public class ModelBakeEvent extends Event { + private final BakedModelManager modelManager; + private final Map modelRegistry; + private final ModelLoader modelLoader; + + public ModelBakeEvent(BakedModelManager modelManager, Map modelRegistry, + ModelLoader modelLoader) { + this.modelManager = modelManager; + this.modelRegistry = modelRegistry; + this.modelLoader = modelLoader; + } + + public BakedModelManager getModelManager() { + return modelManager; + } + + public Map getModelRegistry() { + return modelRegistry; + } + + public ModelLoader getModelLoader() { + return modelLoader; + } +} diff --git a/patchwork-model-loader/src/main/java/net/minecraftforge/client/event/ModelRegistryEvent.java b/patchwork-model-loader/src/main/java/net/minecraftforge/client/event/ModelRegistryEvent.java new file mode 100644 index 00000000..d31900d6 --- /dev/null +++ b/patchwork-model-loader/src/main/java/net/minecraftforge/client/event/ModelRegistryEvent.java @@ -0,0 +1,29 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.client.event; + +import net.minecraftforge.eventbus.api.Event; + +/** + * Fired when the {@link net.minecraftforge.client.model.ModelLoader} is ready + * to receive registrations. + */ +public class ModelRegistryEvent extends Event { +} diff --git a/patchwork-model-loader/src/main/java/net/minecraftforge/client/model/ModelLoader.java b/patchwork-model-loader/src/main/java/net/minecraftforge/client/model/ModelLoader.java new file mode 100644 index 00000000..54dcafca --- /dev/null +++ b/patchwork-model-loader/src/main/java/net/minecraftforge/client/model/ModelLoader.java @@ -0,0 +1,109 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.client.model; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import javax.annotation.Nullable; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.Marker; +import org.apache.logging.log4j.MarkerManager; + +import net.minecraft.client.color.block.BlockColors; +import net.minecraft.client.render.model.BakedModel; +import net.minecraft.client.texture.SpriteAtlasTexture; +import net.minecraft.client.util.ModelIdentifier; +import net.minecraft.resource.ResourceManager; +import net.minecraft.util.Identifier; +import net.minecraft.util.profiler.Profiler; + +import net.patchworkmc.impl.modelloader.SpecialModelProvider; + +public class ModelLoader extends net.minecraft.client.render.model.ModelLoader implements SpecialModelProvider { + private static final Marker MODELLOADING = MarkerManager.getMarker("MODELLOADING"); + private static Set specialModels = new HashSet<>(); + private static final Logger LOGGER = LogManager.getLogger(); + private final Map loadingExceptions = new HashMap<>(); + private boolean isLoading = false; + private static ModelLoader instance; + + @Nullable + public static ModelLoader instance() { + return instance; + } + + public boolean isLoading() { + return isLoading; + } + + public ModelLoader(ResourceManager resourceManager, SpriteAtlasTexture spriteAtlas, BlockColors blockColors, + Profiler profiler) { + super(resourceManager, spriteAtlas, blockColors, profiler); + } + + /** + * Indicate to vanilla that it should load and bake the given model, even if no + * blocks or items use it. This is useful if e.g. you have baked models only for + * entity renderers. Call during + * {@link net.minecraftforge.client.event.ModelRegistryEvent} + * + * @param rl The model, either {@link ModelResourceLocation} to point to a + * blockstate variant, or plain {@link ResourceLocation} to point + * directly to a json in the models folder. + */ + public static void addSpecialModel(Identifier rl) { + specialModels.add(rl); + } + + @Override + public Set getSpecialModels() { + return specialModels; + } + + /** + * Internal, do not use. + */ + public void onPostBakeEvent(Map modelRegistry) { + BakedModel missingModel = modelRegistry.get(MISSING); + + for (Map.Entry entry : loadingExceptions.entrySet()) { + // ignoring pure Identifier arguments, all things we care about pass + // ModelIdentifier + if (entry.getKey() instanceof ModelIdentifier) { + LOGGER.debug(MODELLOADING, "Model {} failed to load: {}", entry.getKey().toString(), + entry.getValue().getLocalizedMessage()); + final ModelIdentifier location = (ModelIdentifier) entry.getKey(); + final BakedModel model = modelRegistry.get(location); + + if (model == null) { + modelRegistry.put(location, missingModel); + } + } + } + + loadingExceptions.clear(); + isLoading = false; + } +} diff --git a/patchwork-model-loader/src/main/java/net/patchworkmc/impl/modelloader/ModelEventDispatcher.java b/patchwork-model-loader/src/main/java/net/patchworkmc/impl/modelloader/ModelEventDispatcher.java new file mode 100644 index 00000000..1924e55c --- /dev/null +++ b/patchwork-model-loader/src/main/java/net/patchworkmc/impl/modelloader/ModelEventDispatcher.java @@ -0,0 +1,47 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.impl.modelloader; + +import java.util.Map; + +import net.minecraftforge.client.event.ModelBakeEvent; +import net.minecraftforge.client.event.ModelRegistryEvent; +import net.minecraftforge.client.model.ModelLoader; +import net.minecraftforge.fml.ModLoader; + +import net.minecraft.client.render.model.BakedModel; +import net.minecraft.client.render.model.BakedModelManager; +import net.minecraft.util.Identifier; + +public class ModelEventDispatcher { + /** + * In Forge, ModelRegistryEvent is fired in parallel with FMLClientSetupEvent. + * Here we fire ModelRegistryEvent before FMLClientSetupEvent. + * The official forge does not set the ModLoadingContext here, so this should be fine. + */ + public static void fireModelRegistryEvent() { + ModLoader.get().postEvent(new ModelRegistryEvent()); + } + + public static void onModelBake(BakedModelManager modelManager, Map modelRegistry, ModelLoader modelLoader) { + ModLoader.get().postEvent(new ModelBakeEvent(modelManager, modelRegistry, modelLoader)); + modelLoader.onPostBakeEvent(modelRegistry); + } +} diff --git a/patchwork-model-loader/src/main/java/net/patchworkmc/impl/modelloader/Signatures.java b/patchwork-model-loader/src/main/java/net/patchworkmc/impl/modelloader/Signatures.java new file mode 100644 index 00000000..735b2b3a --- /dev/null +++ b/patchwork-model-loader/src/main/java/net/patchworkmc/impl/modelloader/Signatures.java @@ -0,0 +1,34 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.impl.modelloader; + +public class Signatures { + public static final String Profiler_swap = "net/minecraft/util/profiler/Profiler.swap(Ljava/lang/String;)V"; + + public static final String ModelLoader_new = "(" + + "Lnet/minecraft/resource/ResourceManager;" + + "Lnet/minecraft/client/texture/SpriteAtlasTexture;" + + "Lnet/minecraft/client/color/block/BlockColors;" + + "Lnet/minecraft/util/profiler/Profiler;" + + ")" + + "Lnet/minecraft/client/render/model/ModelLoader;"; + + public static final String ModelLoader_addModel = "net/minecraft/client/render/model/ModelLoader.addModel(Lnet/minecraft/client/util/ModelIdentifier;)V"; +} diff --git a/patchwork-model-loader/src/main/java/net/patchworkmc/impl/modelloader/SpecialModelProvider.java b/patchwork-model-loader/src/main/java/net/patchworkmc/impl/modelloader/SpecialModelProvider.java new file mode 100644 index 00000000..e39b5a46 --- /dev/null +++ b/patchwork-model-loader/src/main/java/net/patchworkmc/impl/modelloader/SpecialModelProvider.java @@ -0,0 +1,30 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.impl.modelloader; + +import java.util.Set; + +import net.minecraft.util.Identifier; + +public interface SpecialModelProvider { + default Set getSpecialModels() { + return java.util.Collections.emptySet(); + } +} diff --git a/patchwork-model-loader/src/main/java/net/patchworkmc/mixin/modelloader/MixinBakedModelManager.java b/patchwork-model-loader/src/main/java/net/patchworkmc/mixin/modelloader/MixinBakedModelManager.java new file mode 100644 index 00000000..e2169543 --- /dev/null +++ b/patchwork-model-loader/src/main/java/net/patchworkmc/mixin/modelloader/MixinBakedModelManager.java @@ -0,0 +1,59 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.modelloader; + +import java.util.Map; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.At.Shift; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import net.minecraft.client.color.block.BlockColors; +import net.minecraft.client.render.model.BakedModel; +import net.minecraft.client.render.model.BakedModelManager; +import net.minecraft.client.render.model.ModelLoader; +import net.minecraft.client.texture.SpriteAtlasTexture; +import net.minecraft.resource.ResourceManager; +import net.minecraft.util.Identifier; +import net.minecraft.util.profiler.Profiler; + +import net.patchworkmc.impl.modelloader.ModelEventDispatcher; +import net.patchworkmc.impl.modelloader.Signatures; + +@Mixin(BakedModelManager.class) +public abstract class MixinBakedModelManager { + @Shadow + private Map models; + + @Redirect(method = "prepare", at = @At(value = "NEW", target = Signatures.ModelLoader_new, ordinal = 0)) + private ModelLoader patchwork_prepare_new_ModelLoader(ResourceManager resourceManager, SpriteAtlasTexture spriteAtlas, BlockColors blockColors, Profiler profiler) { + return new net.minecraftforge.client.model.ModelLoader(resourceManager, spriteAtlas, blockColors, profiler); + } + + @Inject(method = "apply", at = @At(shift = Shift.BEFORE, value = "INVOKE", target = Signatures.Profiler_swap, ordinal = 0)) + protected void patchwork_apply_swap(ModelLoader modelLoader, ResourceManager resourceManager, Profiler profiler, CallbackInfo ci) { + BakedModelManager me = (BakedModelManager) (Object) this; + ModelEventDispatcher.onModelBake(me, models, (net.minecraftforge.client.model.ModelLoader) modelLoader); + } +} diff --git a/patchwork-model-loader/src/main/java/net/patchworkmc/mixin/modelloader/MixinModelLoader.java b/patchwork-model-loader/src/main/java/net/patchworkmc/mixin/modelloader/MixinModelLoader.java new file mode 100644 index 00000000..fc082222 --- /dev/null +++ b/patchwork-model-loader/src/main/java/net/patchworkmc/mixin/modelloader/MixinModelLoader.java @@ -0,0 +1,89 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.modelloader; + +import java.util.Map; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.Slice; + +import net.minecraft.client.render.model.ModelLoader; +import net.minecraft.client.render.model.UnbakedModel; +import net.minecraft.client.util.ModelIdentifier; +import net.minecraft.util.Identifier; + +import net.patchworkmc.impl.modelloader.Signatures; +import net.patchworkmc.impl.modelloader.SpecialModelProvider; + +@Mixin(ModelLoader.class) +public abstract class MixinModelLoader implements SpecialModelProvider { + @Unique + private static final ModelIdentifier TRIDENT_INV = new ModelIdentifier("minecraft:trident_in_hand#inventory"); + @Unique + private static final Logger LOGGER = LogManager.getLogger(ModelLoader.class); + + @Shadow + @Final + private Map modelsToBake; + + @Shadow + @Final + private Map unbakedModels; + + @Unique + private void patchwork$loadSpecialModel() { + for (Identifier id : getSpecialModels()) { + ModelLoader me = (ModelLoader) (Object) this; + UnbakedModel iunbakedmodel = me.getOrLoadModel(id); + this.unbakedModels.put(id, iunbakedmodel); + this.modelsToBake.put(id, iunbakedmodel); + } + } + + @Shadow + private void addModel(ModelIdentifier modelId) { } + + /** + * Due to the limitations of mixin, when targeting a constructor, we cannot use injection points other than "TAIL". + * There are multiple occurrences of addModel in the constructor, Forge inserts the patch after adding model for the trident. + * Here we just do another check to ensure that the injection point is correct. + * @param me + * @param modelId + */ + @Redirect(slice = @Slice(from = @At(value = "INVOKE_STRING", target = Signatures.Profiler_swap, args = "ldc=special")), + method = "", at = @At(value = "INVOKE", target = Signatures.ModelLoader_addModel, ordinal = 0)) + private void patchwork_addModel_return(ModelLoader me, ModelIdentifier modelId) { + addModel(modelId); + + if (modelId.equals(TRIDENT_INV)) { + LOGGER.debug("Patchwork is loading special models for Forge mods"); + patchwork$loadSpecialModel(); + } else { + LOGGER.warn("Patchwork was unable to load special models for Forge mods"); + } + } +} diff --git a/patchwork-model-loader/src/main/resources/assets/patchwork-model-loader/icon.png b/patchwork-model-loader/src/main/resources/assets/patchwork-model-loader/icon.png new file mode 100644 index 00000000..de75d2fb Binary files /dev/null and b/patchwork-model-loader/src/main/resources/assets/patchwork-model-loader/icon.png differ diff --git a/patchwork-model-loader/src/main/resources/fabric.mod.json b/patchwork-model-loader/src/main/resources/fabric.mod.json new file mode 100644 index 00000000..33b4c93d --- /dev/null +++ b/patchwork-model-loader/src/main/resources/fabric.mod.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "id": "patchwork-model-loader", + "name": "Patchwork Model Loader", + "version": "${version}", + "license": "LGPL-2.1-only", + "icon": "assets/patchwork-model-loader/icon.png", + "contact": { + "issues": "https://github.com/PatchworkMC/patchwork-api/issues", + "sources": "https://github.com/PatchworkMC/patchwork-api" + }, + "authors": [ + "PatchworkMC" + ], + "depends": { + "fabricloader": ">=0.8.4", + "patchwork-fml": "*" + }, + "mixins": [ + "patchwork-model-loader.mixins.json" + ], + "description": "Implementation of the Forge Model Loader.", + "custom": { + "modmenu:api": true, + "modmenu:parent": "patchwork" + } +} diff --git a/patchwork-model-loader/src/main/resources/patchwork-model-loader.mixins.json b/patchwork-model-loader/src/main/resources/patchwork-model-loader.mixins.json new file mode 100644 index 00000000..9bd996d7 --- /dev/null +++ b/patchwork-model-loader/src/main/resources/patchwork-model-loader.mixins.json @@ -0,0 +1,12 @@ +{ + "required": true, + "package": "net.patchworkmc.mixin.modelloader", + "compatibilityLevel": "JAVA_8", + "client": [ + "MixinBakedModelManager", + "MixinModelLoader" + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/settings.gradle b/settings.gradle index 4237a3d1..3bcce086 100644 --- a/settings.gradle +++ b/settings.gradle @@ -30,6 +30,7 @@ include 'patchwork-fml' include 'patchwork-gui' include 'patchwork-level-generators' include 'patchwork-loot' +include 'patchwork-model-loader' include 'patchwork-networking' include 'patchwork-networking-messages' include 'patchwork-recipes'