diff --git a/common/src/main/java/software/bluelib/BlueLibCommon.java b/common/src/main/java/software/bluelib/BlueLibCommon.java index 69e04322..b807558b 100644 --- a/common/src/main/java/software/bluelib/BlueLibCommon.java +++ b/common/src/main/java/software/bluelib/BlueLibCommon.java @@ -9,6 +9,7 @@ import static software.bluelib.BlueLibConstants.SCHEDULER; +import java.nio.file.Path; import java.util.concurrent.TimeUnit; import net.minecraft.network.chat.Component; import org.jetbrains.annotations.ApiStatus; @@ -16,16 +17,30 @@ import org.spongepowered.asm.launch.MixinBootstrap; import software.bluelib.api.event.mod.ModIntegration; import software.bluelib.api.net.NetworkRegistry; +import software.bluelib.api.registry.AbstractRegistryBuilder; +import software.bluelib.api.registry.BlueRegistryBuilder; import software.bluelib.api.utils.logging.BaseLogLevel; import software.bluelib.api.utils.logging.BaseLogger; import software.bluelib.internal.BlueTranslation; import software.bluelib.internal.registry.BlueNetworkRegistry; import software.bluelib.internal.registry.BlueRecipeSerializerRegistry; import software.bluelib.internal.registry.BlueRecipeTypeRegistry; +import software.bluelib.internal.registry.TestEntityReg; @ApiStatus.Internal public class BlueLibCommon { + /** + * Initializes the {@link AbstractRegistryBuilder} instance with the mod ID. Replace {@link BlueLibConstants#MOD_ID} with your mod's unique mod ID to register content under your mod's namespace. + *

+ * This is essential for registering mod content such as items, blocks, and entities. + *

+ * Do not remove, as it will break the mod's registration system. + *

+ * Do not use this, you need to add this line into your own mod. + */ + public static AbstractRegistryBuilder REGISTRIES = new BlueRegistryBuilder(BlueLibConstants.MOD_ID); + private BlueLibCommon() {} public static void init() { @@ -49,6 +64,8 @@ public static void doRegistration() { InternalNetworkRegistry.networkServer(); BlueRecipeTypeRegistry.init(); BlueRecipeSerializerRegistry.init(); + + TestEntityReg.init(); } public static void doClientRegistration() { diff --git a/common/src/main/java/software/bluelib/api/registry/AbstractRegistryBuilder.java b/common/src/main/java/software/bluelib/api/registry/AbstractRegistryBuilder.java new file mode 100644 index 00000000..0cc29d0f --- /dev/null +++ b/common/src/main/java/software/bluelib/api/registry/AbstractRegistryBuilder.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2024 BlueLib Contributors + * + * This Source Code Form is subject to the terms of the MIT License. + * If a copy of the MIT License was not distributed with this file, + * You can obtain one at https://opensource.org/licenses/MIT. + */ +package software.bluelib.api.registry; + +import java.util.List; +import java.util.function.Function; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.MobCategory; +import net.minecraft.world.item.Item; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityType; +import software.bluelib.api.registry.builders.blocks.BlockBuilder; +import software.bluelib.api.registry.builders.blocks.BlockEntityBuilder; +import software.bluelib.api.registry.builders.entity.LivingEntityBuilder; +import software.bluelib.api.registry.builders.entity.ProjectileBuilder; +import software.bluelib.api.registry.builders.items.ItemBuilder; +import software.bluelib.api.registry.builders.keybinds.KeybindBuilder; +import software.bluelib.api.registry.builders.tabs.CreativeTabBuilder; +import software.bluelib.api.registry.datagen.entity.EntityTagBuilder; + +public abstract class AbstractRegistryBuilder { + + private final String modID; + + public AbstractRegistryBuilder(final String pModId) { + modID = pModId; + } + + public String getModID() { + return modID; + } + + public LivingEntityBuilder livingEntity(String pName, EntityType.EntityFactory pFactory, MobCategory pCategory) { + return new LivingEntityBuilder<>(pName, pFactory, pCategory, modID); + } + + public ProjectileBuilder projectile(String pName, EntityType.EntityFactory pFactory, MobCategory pCategory, Class pEntityClass) { + return new ProjectileBuilder<>(pName, pFactory, pCategory, pEntityClass, modID); + } + + public BlockBuilder block(String pName, Function pFactory) { + return new BlockBuilder<>(pName, pFactory, modID); + } + + public BlockEntityBuilder blockEntity(String pName, BlockEntityType.BlockEntitySupplier pFactory) { + return new BlockEntityBuilder<>(pName, pFactory, modID); + } + + public ItemBuilder item(String pName, Function pConstructor) { + return new ItemBuilder<>(pName, pConstructor, modID); + } + + public EntityTagBuilder entityTag(String pName) { + return new EntityTagBuilder(pName, modID); + } + + public CreativeTabBuilder tab(String pId) { + return new CreativeTabBuilder(pId, modID); + } + + public KeybindBuilder keybind(String pName, int pKeyCode) { + return new KeybindBuilder(pName, pKeyCode, modID); + } +} diff --git a/common/src/main/java/software/bluelib/api/registry/BlueRegistryBuilder.java b/common/src/main/java/software/bluelib/api/registry/BlueRegistryBuilder.java new file mode 100644 index 00000000..83c07745 --- /dev/null +++ b/common/src/main/java/software/bluelib/api/registry/BlueRegistryBuilder.java @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2024 BlueLib Contributors + * + * This Source Code Form is subject to the terms of the MIT License. + * If a copy of the MIT License was not distributed with this file, + * You can obtain one at https://opensource.org/licenses/MIT. + */ +package software.bluelib.api.registry; + +public class BlueRegistryBuilder extends AbstractRegistryBuilder { + + public BlueRegistryBuilder(String pModId) { + super(pModId); + } +} diff --git a/common/src/main/java/software/bluelib/api/registry/builders/BuilderUtils.java b/common/src/main/java/software/bluelib/api/registry/builders/BuilderUtils.java new file mode 100644 index 00000000..b7d31da7 --- /dev/null +++ b/common/src/main/java/software/bluelib/api/registry/builders/BuilderUtils.java @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2024 BlueLib Contributors + * + * This Source Code Form is subject to the terms of the MIT License. + * If a copy of the MIT License was not distributed with this file, + * You can obtain one at https://opensource.org/licenses/MIT. + */ +package software.bluelib.api.registry.builders; + +public class BuilderUtils { + + private BuilderUtils() {} +} diff --git a/common/src/main/java/software/bluelib/api/registry/builders/blocks/BlockBuilder.java b/common/src/main/java/software/bluelib/api/registry/builders/blocks/BlockBuilder.java new file mode 100644 index 00000000..2cc4e637 --- /dev/null +++ b/common/src/main/java/software/bluelib/api/registry/builders/blocks/BlockBuilder.java @@ -0,0 +1,377 @@ +/* + * Copyright (C) 2024 BlueLib Contributors + * + * This Source Code Form is subject to the terms of the MIT License. + * If a copy of the MIT License was not distributed with this file, + * You can obtain one at https://opensource.org/licenses/MIT. + */ +package software.bluelib.api.registry.builders.blocks; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.BiConsumer; +import java.util.function.Function; +import java.util.function.Supplier; +import net.minecraft.data.recipes.RecipeOutput; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.Item; +import net.minecraft.world.level.block.Block; +import software.bluelib.BlueLibConstants; +import software.bluelib.api.registry.builders.items.ItemBuilder; +import software.bluelib.api.registry.datagen.blocks.BlockModelGenerator; +import software.bluelib.api.registry.datagen.blocks.BlockModelTemplates; +import software.bluelib.api.registry.datagen.blockstates.BlockstateGenerator; +import software.bluelib.api.registry.datagen.blockstates.BlockstateTemplates; +import software.bluelib.api.registry.datagen.items.ItemModelGenerator; +import software.bluelib.api.registry.datagen.items.ItemModelTemplates; +import software.bluelib.api.registry.datagen.recipe.RecipeGenerator; + +public class BlockBuilder { + + public static final List> REGISTERED_BUILDERS = new ArrayList<>(); + private final String modId; + public final String blockName; + public final Function blockConstructor; + public Block.Properties properties; + public boolean createDefaultItem = false; + public T registeredBlock; + public static BlockstateTemplates blockstateTemplate; + public static BlockModelTemplates blockModelTemplate; + private BiConsumer recipeConsumer; + private boolean isOre = false; + private boolean hasRaw = false; + private boolean hasIngot = false; + private boolean hasNugget = false; + private boolean hasDeepslate = false; + + private boolean hasLog = false; + private boolean hasStrippedLog = false; + private boolean hasPlanks = false; + private boolean hasFence = false; + private boolean hasDoor = false; + private boolean hasButton = false; + private boolean hasSlab = false; + private boolean hasPressurePlate = false; + private boolean hasStairs = false; + private boolean hasTrapdoor = false; + private boolean hasFenceGate = false; + //private boolean hasSign = false; + //private boolean hasHangingSign = false; + + public BlockBuilder(String name, Function blockConstructor, String pModId) { + this.blockName = name; + this.blockConstructor = blockConstructor; + this.modId = pModId; + } + + public BlockBuilder properties(Block.Properties properties) { + this.properties = properties; + return this; + } + + public BlockBuilder defaultItem() { + this.createDefaultItem = true; + return this; + } + + public BlockBuilder defaultBlockstate() { + return this.datagen() + .blockstate(BlockstateTemplates.SIMPLE_BLOCK) + .model(BlockModelTemplates.CUBE_ALL) + .finish(); + } + + public OreBuilder ore() { + this.isOre = true; + return new OreBuilder(); + } + + public WoodBuilder wood() { + return new WoodBuilder(); + } + + public BlockBuilder recipe(BiConsumer recipeConsumer) { + this.recipeConsumer = recipeConsumer; + return this; + } + + private void generate(String blockName, BlockstateTemplates state, BlockModelTemplates model, ItemModelTemplates item) { + BlockstateGenerator.generateBlockstate(modId, blockName, state); + BlockModelGenerator.generateBlockModel(modId, blockName, model); + ItemModelGenerator.generateItemModel(modId, blockName, item); + } + + public BlockstateBuilder datagen() { + return new BlockstateBuilder(); + } + + public Supplier register() { + if (properties == null) { + properties = Block.Properties.of(); + } + + Supplier blockSupplier = null; + + if (!isOre) { + blockSupplier = BlueLibConstants.PlatformHelper.REGISTRY.registerBlock(blockName, () -> { + T block = blockConstructor.apply(properties); + this.registeredBlock = block; + return block; + }); + + if (createDefaultItem) { + Supplier itemSupplier = () -> new BlockItem(registeredBlock, new Item.Properties()); + BlueLibConstants.PlatformHelper.REGISTRY.registerItem(blockName, itemSupplier); + } + } + + if (isOre) { + blockSupplier = BlueLibConstants.PlatformHelper.REGISTRY.registerBlock(blockName + "_block", () -> { + T block = blockConstructor.apply(properties); + this.registeredBlock = block; + return block; + }); + + generate(blockName + "_ore", BlockstateTemplates.SIMPLE_BLOCK, BlockModelTemplates.CUBE_ALL, ItemModelTemplates.BLOCK_ITEM); + generate(blockName + "_block", BlockstateTemplates.SIMPLE_BLOCK, BlockModelTemplates.CUBE_ALL, ItemModelTemplates.BLOCK_ITEM); + + if (createDefaultItem) { + Supplier itemSupplier = () -> new BlockItem(registeredBlock, new Item.Properties()); + BlueLibConstants.PlatformHelper.REGISTRY.registerItem(blockName + "_block", itemSupplier); + } + + if (hasDeepslate) { + generate(blockName + "_deepslate_ore", BlockstateTemplates.SIMPLE_BLOCK, BlockModelTemplates.CUBE_ALL, ItemModelTemplates.BLOCK_ITEM); + } + if (hasRaw) { + ItemBuilder.item("raw_" + blockName, Item::new, modId) + .model(ItemModelTemplates.GENERATED) + .register(); + } + if (hasIngot) { + ItemBuilder.item(blockName + "_ingot", Item::new, modId) + .model(ItemModelTemplates.GENERATED) + .register(); + } + if (hasNugget) { + ItemBuilder.item(blockName + "_nugget", Item::new, modId) + .model(ItemModelTemplates.GENERATED) + .register(); + } + } + + REGISTERED_BUILDERS.add(this); + return blockSupplier; + } + + public static void doBlockModelGen(String modId) { + for (BlockBuilder builder : REGISTERED_BUILDERS) { + if (!builder.isOre && builder.createDefaultItem) { + ItemModelGenerator.generateItemModel(modId, builder.blockName, ItemModelTemplates.BLOCK_ITEM); + } + if (!builder.isOre && blockstateTemplate != null) { + BlockstateGenerator.generateBlockstate(modId, builder.blockName, blockstateTemplate); + BlockModelGenerator.generateBlockModel(modId, builder.blockName, blockModelTemplate); + } + } + } + + public static void doRecipeGen(String modId) { + for (BlockBuilder builder : REGISTERED_BUILDERS) { + if (builder.recipeConsumer != null) { + RecipeGenerator.generateRecipe(modId, builder.blockName, (recipeOutput, jsonSupplier) -> { + RecipeContext ctx = new RecipeContext(builder.registeredBlock); + builder.recipeConsumer.accept(ctx, recipeOutput); + }); + } + } + } + + public static class RecipeContext { + + private final Block block; + + public RecipeContext(Block block) { + this.block = block; + } + + public Block getEntry() { + return block; + } + } + + public class BlockstateBuilder { + + private final BlockBuilder parent; + + public BlockstateBuilder() { + this.parent = BlockBuilder.this; + } + + public BlockstateBuilder blockstate(BlockstateTemplates blockstates) { + blockstateTemplate = blockstates; + return this; + } + + public BlockstateBuilder model(BlockModelTemplates models) { + blockModelTemplate = models; + return this; + } + + public BlockBuilder finish() { + return parent; + } + } + + public class OreBuilder { + + private final BlockBuilder parent; + + public OreBuilder() { + this.parent = BlockBuilder.this; + } + + public OreBuilder hasRaw() { + hasRaw = true; + return this; + } + + public OreBuilder hasIngot() { + hasIngot = true; + return this; + } + + public OreBuilder hasNugget() { + hasNugget = true; + return this; + } + + public OreBuilder hasDeepslate() { + hasDeepslate = true; + return this; + } + + public BlockBuilder finish() { + return parent; + } + } + + public class WoodBuilder { + + private final BlockBuilder parent; + + public WoodBuilder() { + this.parent = BlockBuilder.this; + } + + public WoodBuilder hasLog() { + hasLog = true; + return this; + } + + public WoodBuilder hasStrippedLog() { + hasStrippedLog = true; + return this; + } + + public WoodBuilder hasPlanks() { + hasPlanks = true; + return this; + } + + public WoodBuilder hasFence() { + hasFence = true; + return this; + } + + public WoodBuilder hasDoor() { + hasDoor = true; + return this; + } + + public WoodBuilder hasButton() { + hasButton = true; + return this; + } + + public WoodBuilder hasSlab() { + hasSlab = true; + return this; + } + + public WoodBuilder hasPressurePlate() { + hasPressurePlate = true; + return this; + } + + public WoodBuilder hasStairs() { + hasStairs = true; + return this; + } + + public WoodBuilder hasTrapdoor() { + hasTrapdoor = true; + return this; + } + + public WoodBuilder hasFenceGate() { + hasFenceGate = true; + return this; + } + + /*public WoodBuilder hasSign() { + hasSign = true; + return this; + } + + public WoodBuilder hasHangingSign() { + hasHangingSign = true; + return this; + }*/ + + public BlockBuilder finish() { + if (hasLog) { + generate(blockName + "_log", BlockstateTemplates.ORIENTED_BLOCK, BlockModelTemplates.COLUMN, ItemModelTemplates.BLOCK_ITEM); + } + if (hasStrippedLog) { + generate(blockName + "_log_stripped", BlockstateTemplates.ORIENTED_BLOCK, BlockModelTemplates.COLUMN, ItemModelTemplates.BLOCK_ITEM); + } + if (hasPlanks) { + generate(blockName + "_planks", BlockstateTemplates.SIMPLE_BLOCK, BlockModelTemplates.CUBE_ALL, ItemModelTemplates.BLOCK_ITEM); + } + if (hasFence) { + generate(blockName + "_fence", BlockstateTemplates.FENCE_BLOCK, BlockModelTemplates.FENCE, ItemModelTemplates.BLOCK_WITH_INVENTORY_MODEL); + ItemModelGenerator.generateItemModel(modId, blockName + "_fence", ItemModelTemplates.GENERATED); + } + if (hasDoor) { + generate(blockName + "_door", BlockstateTemplates.DOOR_BLOCK, BlockModelTemplates.DOOR, ItemModelTemplates.BLOCK_SPRITE); + } + if (hasButton) { + generate(blockName + "_button", BlockstateTemplates.BUTTON_BLOCK, BlockModelTemplates.BUTTON, ItemModelTemplates.BLOCK_WITH_INVENTORY_MODEL); + } + if (hasSlab) { + generate(blockName + "_slab", BlockstateTemplates.SLAB_BLOCK, BlockModelTemplates.SLAB, ItemModelTemplates.BLOCK_SPRITE); + } + if (hasPressurePlate) { + generate(blockName + "_pressure_plate", BlockstateTemplates.PRESSURE_PLATE_BLOCK, BlockModelTemplates.PRESSURE_PLATE, ItemModelTemplates.BLOCK_SPRITE); + } + if (hasStairs) { + generate(blockName + "_stairs", BlockstateTemplates.STAIRS_BLOCK, BlockModelTemplates.STAIRS, ItemModelTemplates.BLOCK_SPRITE); + } + if (hasTrapdoor) { + generate(blockName + "_trapdoor", BlockstateTemplates.TRAPDOOR_BLOCK, BlockModelTemplates.TRAPDOOR, ItemModelTemplates.BLOCK_SPRITE); + } + if (hasFenceGate) { + generate(blockName + "_fence_gate", BlockstateTemplates.FENCE_GATE_BLOCK, BlockModelTemplates.FENCE_GATE, ItemModelTemplates.BLOCK_SPRITE); + } + /*if (hasSign) { + generate(blockName + "_fence_gate", BlockstateTemplates.FENCE_GATE_BLOCK, BlockModelTemplates.FENCE_GATE, ItemModelTemplates.BLOCK_SPRITE); + } + if (hasHangingSign) { + generate(blockName + "_fence_gate", BlockstateTemplates.FENCE_GATE_BLOCK, BlockModelTemplates.FENCE_GATE, ItemModelTemplates.BLOCK_SPRITE); + }*/ + + return parent; + } + } +} diff --git a/common/src/main/java/software/bluelib/api/registry/builders/blocks/BlockEntityBuilder.java b/common/src/main/java/software/bluelib/api/registry/builders/blocks/BlockEntityBuilder.java new file mode 100644 index 00000000..e1829e19 --- /dev/null +++ b/common/src/main/java/software/bluelib/api/registry/builders/blocks/BlockEntityBuilder.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2024 BlueLib Contributors + * + * This Source Code Form is subject to the terms of the MIT License. + * If a copy of the MIT License was not distributed with this file, + * You can obtain one at https://opensource.org/licenses/MIT. + */ +package software.bluelib.api.registry.builders.blocks; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.function.Supplier; +import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityType; +import software.bluelib.BlueLibConstants; +import software.bluelib.api.registry.helpers.entity.RenderHelper; + +public class BlockEntityBuilder { + + protected static final List> REGISTERED_BUILDERS = new ArrayList<>(); + + protected final String modID; + protected final String name; + protected final BlockEntityType.BlockEntitySupplier blockEntityFactory; + protected Supplier> rendererProvider; + protected Supplier> blockEntityType; + protected List> validBlockSuppliers; + + public BlockEntityBuilder(String name, BlockEntityType.BlockEntitySupplier blockEntityFactory, String pModID) { + this.modID = pModID; + this.name = name; + this.blockEntityFactory = blockEntityFactory; + this.validBlockSuppliers = new ArrayList<>(); + } + + public BlockEntityBuilder validBlocks(Supplier... blockSuppliers) { + this.validBlockSuppliers = Arrays.asList(blockSuppliers); + return this; + } + + public BlockEntityBuilder renderer(BlockEntityRendererProvider renderer) { + this.rendererProvider = () -> renderer; + return this; + } + + public Supplier> register() { + Supplier> blockEntityTypeSupplier = BlueLibConstants.PlatformHelper.REGISTRY.registerBlockEntity(name, () -> { + Block[] blocks = validBlockSuppliers.stream() + .map(Supplier::get) + .toArray(Block[]::new); + BlockEntityType type = BlockEntityType.Builder.of(blockEntityFactory, blocks).build(null); + this.blockEntityType = () -> type; + return type; + }); + + if (rendererProvider != null) { + new RenderHelper().queueRenderer((entityConsumer, blockConsumer) -> { + blockConsumer.accept(blockEntityType.get(), rendererProvider.get()); + }); + } + + REGISTERED_BUILDERS.add(this); + return blockEntityTypeSupplier; + } + + public String getName() { + return name; + } + + public Supplier> getBlockEntityType() { + return blockEntityType; + } +} diff --git a/common/src/main/java/software/bluelib/api/registry/builders/entity/EntityBuilder.java b/common/src/main/java/software/bluelib/api/registry/builders/entity/EntityBuilder.java new file mode 100644 index 00000000..815604d9 --- /dev/null +++ b/common/src/main/java/software/bluelib/api/registry/builders/entity/EntityBuilder.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2024 BlueLib Contributors + * + * This Source Code Form is subject to the terms of the MIT License. + * If a copy of the MIT License was not distributed with this file, + * You can obtain one at https://opensource.org/licenses/MIT. + */ +package software.bluelib.api.registry.builders.entity; + +import java.util.function.Supplier; +import net.minecraft.client.renderer.entity.EntityRendererProvider; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.MobCategory; +import software.bluelib.BlueLibConstants; +import software.bluelib.api.registry.helpers.entity.RenderHelper; + +public abstract class EntityBuilder> { + + protected final String modId; + protected final String name; + protected final EntityType.EntityFactory factory; + protected final MobCategory category; + protected float width; + protected float height; + protected EntityRendererProvider rendererProvider = null; + + public EntityBuilder(String pName, EntityType.EntityFactory pFactory, MobCategory pCategory, String pModId) { + this.modId = pModId; + this.name = pName; + this.factory = pFactory; + this.category = pCategory; + } + + public SELF sized(float pWidth, float pHeight) { + this.width = pWidth; + this.height = pHeight; + return self(); + } + + public SELF renderer(EntityRendererProvider pRendererProvider) { + this.rendererProvider = pRendererProvider; + return self(); + } + + public Supplier> register() { + Supplier> entityTypeSupplier = BlueLibConstants.PlatformHelper.REGISTRY.registerEntity(name, + () -> EntityType.Builder.of(factory, category) + .sized(width, height) + .build(name)); + + if (rendererProvider != null) { + new RenderHelper().queueRenderer((entityConsumer, blockConsumer) -> { + entityConsumer.accept(entityTypeSupplier.get(), rendererProvider); + }); + } + + return entityTypeSupplier; + } + + protected abstract SELF self(); +} diff --git a/common/src/main/java/software/bluelib/api/registry/builders/entity/LivingEntityBuilder.java b/common/src/main/java/software/bluelib/api/registry/builders/entity/LivingEntityBuilder.java new file mode 100644 index 00000000..43679751 --- /dev/null +++ b/common/src/main/java/software/bluelib/api/registry/builders/entity/LivingEntityBuilder.java @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2024 BlueLib Contributors + * + * This Source Code Form is subject to the terms of the MIT License. + * If a copy of the MIT License was not distributed with this file, + * You can obtain one at https://opensource.org/licenses/MIT. + */ +package software.bluelib.api.registry.builders.entity; + +import java.util.*; +import java.util.function.Supplier; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.MobCategory; +import net.minecraft.world.entity.ai.attributes.AttributeSupplier; +import net.minecraft.world.item.CreativeModeTab; +import net.minecraft.world.item.Item; +import software.bluelib.BlueLibConstants; +import software.bluelib.api.registry.datagen.items.ItemModelGenerator; +import software.bluelib.api.registry.datagen.items.ItemModelTemplates; +import software.bluelib.api.registry.helpers.entity.AttributeHelper; +import software.bluelib.api.registry.helpers.entity.RenderHelper; +import software.bluelib.api.registry.helpers.items.BlueSpawnEggItem; + +public class LivingEntityBuilder extends EntityBuilder> { + + private boolean hasSpawnEgg = false; + private int primaryEggColor; + private int secondaryEggColor; + private Supplier attributeBuilder; + private boolean hasVariants = false; + private final List entityNames = new ArrayList<>(); + private Supplier tabSupplier; + private final Map, List>> spawnEggsByTab = new HashMap<>(); + + public LivingEntityBuilder(String pName, EntityType.EntityFactory pFactory, MobCategory pCategory, String pModId) { + super(pName, pFactory, pCategory, pModId); + } + + public LivingEntityBuilder spawnEgg(int pPrimaryColor, int pSecondaryColor) { + this.hasSpawnEgg = true; + this.primaryEggColor = pPrimaryColor; + this.secondaryEggColor = pSecondaryColor; + return this; + } + + public LivingEntityBuilder attributes(Supplier pAttributes) { + this.attributeBuilder = pAttributes; + return this; + } + + public LivingEntityBuilder loadVariants() { + this.hasVariants = true; + return this; + } + + public LivingEntityBuilder tab(Supplier pTabSupplier) { + this.tabSupplier = pTabSupplier; + return this; + } + + public List> getSpawnEggsForTab(CreativeModeTab pTab) { + List> spawnEggs = new ArrayList<>(); + for (Map.Entry, List>> entry : spawnEggsByTab.entrySet()) { + if (Objects.equals(entry.getKey().get(), pTab)) { + spawnEggs.addAll(entry.getValue()); + } + } + return Collections.unmodifiableList(spawnEggs); + } + + @Override + public Supplier> register() { + Supplier> entityTypeSupplier = BlueLibConstants.PlatformHelper.REGISTRY.registerEntity( + name, + () -> EntityType.Builder.of(factory, category).sized(width, height).build(name)); + + if (hasSpawnEgg && Mob.class.isAssignableFrom(factory.getClass())) { + registerSpawnEgg(name, (Supplier>) (Supplier) entityTypeSupplier, + primaryEggColor, secondaryEggColor, tabSupplier); + } + + if (attributeBuilder != null) { + new AttributeHelper().queueAttributes(entityTypeSupplier, attributeBuilder); + } + + if (rendererProvider != null) { + new RenderHelper().queueRenderer((entityConsumer, blockConsumer) -> entityConsumer.accept(entityTypeSupplier.get(), rendererProvider)); + } + + if (hasVariants) { + entityNames.add(name); + } + + return entityTypeSupplier; + } + + public void doSpawnEggDatagen(String pModId) { + if (hasSpawnEgg) { + String spawnEggName = name + "_spawn_egg"; + ItemModelGenerator.generateItemModel(pModId, spawnEggName, ItemModelTemplates.SPAWN_EGG); + } + } + + public List getEntityNames() { + return Collections.unmodifiableList(entityNames); + } + + public Supplier registerSpawnEgg( + String pName, + Supplier> pEntityType, + int pPrimaryColor, + int pSecondaryColor, + Supplier pTabSupplier) { + hasSpawnEgg = true; + Supplier spawnEggSupplier = BlueLibConstants.PlatformHelper.REGISTRY.registerItem( + pName + "_spawn_egg", + () -> new BlueSpawnEggItem( + pEntityType.get(), + pPrimaryColor, + pSecondaryColor, + new Item.Properties())); + if (pTabSupplier != null) { + spawnEggsByTab.computeIfAbsent(pTabSupplier, k -> new ArrayList<>()).add(spawnEggSupplier); + } + return spawnEggSupplier; + } + + @Override + protected LivingEntityBuilder self() { + return this; + } +} diff --git a/common/src/main/java/software/bluelib/api/registry/builders/entity/ProjectileBuilder.java b/common/src/main/java/software/bluelib/api/registry/builders/entity/ProjectileBuilder.java new file mode 100644 index 00000000..9ba7c313 --- /dev/null +++ b/common/src/main/java/software/bluelib/api/registry/builders/entity/ProjectileBuilder.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2024 BlueLib Contributors + * + * This Source Code Form is subject to the terms of the MIT License. + * If a copy of the MIT License was not distributed with this file, + * You can obtain one at https://opensource.org/licenses/MIT. + */ +package software.bluelib.api.registry.builders.entity; + +import net.minecraft.client.renderer.entity.EntityRendererProvider; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.MobCategory; + +public class ProjectileBuilder extends EntityBuilder> { + + private final Class entityClass; + + public ProjectileBuilder(String pName, EntityType.EntityFactory pFactory, MobCategory pCategory, Class pEntityClass, String pModId) { + super(pName, pFactory, pCategory, pModId); + this.entityClass = pEntityClass; + } + + public ProjectileBuilder sized(float pWidth, float pHeight) { + this.width = pWidth; + this.height = pHeight; + return this; + } + + public ProjectileBuilder renderer(EntityRendererProvider pRendererProvider) { + this.rendererProvider = pRendererProvider; + return this; + } + + @Override + protected ProjectileBuilder self() { + return this; + } +} diff --git a/common/src/main/java/software/bluelib/api/registry/builders/items/ItemBuilder.java b/common/src/main/java/software/bluelib/api/registry/builders/items/ItemBuilder.java new file mode 100644 index 00000000..1b21b61f --- /dev/null +++ b/common/src/main/java/software/bluelib/api/registry/builders/items/ItemBuilder.java @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2024 BlueLib Contributors + * + * This Source Code Form is subject to the terms of the MIT License. + * If a copy of the MIT License was not distributed with this file, + * You can obtain one at https://opensource.org/licenses/MIT. + */ +package software.bluelib.api.registry.builders.items; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import net.minecraft.core.Holder; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.data.recipes.RecipeOutput; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.*; +import software.bluelib.BlueLibConstants; +import software.bluelib.api.registry.datagen.items.ItemModelGenerator; +import software.bluelib.api.registry.datagen.items.ItemModelTemplates; +import software.bluelib.api.registry.datagen.recipe.RecipeGenerator; +import software.bluelib.api.registry.helpers.ArmorSetConfig; +import software.bluelib.api.registry.helpers.ToolsetConfig; + +@SuppressWarnings("unchecked") +public class ItemBuilder { + + public final List generatedItems = new ArrayList<>(); + public final Map customModelMap = new HashMap<>(); + public final List> REGISTERED_BUILDERS = new ArrayList<>(); + public final String itemName; + public final Function itemConstructor; + public Consumer propertiesConsumer = props -> {}; + public static final Map>> TOOLSETS = new HashMap<>(); + public static final Map>> ARMORSETS = new HashMap<>(); + private final String modId; + private BiConsumer recipeConsumer; + private T registeredItem; + + public ItemBuilder(String name, Function itemConstructor, String pModId) { + this.modId = pModId; + this.itemName = name; + this.itemConstructor = itemConstructor; + } + + public static ItemBuilder item(String name, Function itemConstructor, String pModId) { + return new ItemBuilder<>(name, itemConstructor, pModId); + } + + public void doItemModelGen(String modId) { + for (String itemName : generatedItems) { + ItemModelTemplates template = customModelMap.getOrDefault(itemName, ItemModelTemplates.HANDHELD); + + if (itemName.endsWith("_sword") || itemName.endsWith("_pickaxe") || itemName.endsWith("_axe") || itemName.endsWith("_shovel") || itemName.endsWith("_hoe")) { + template = ItemModelTemplates.HANDHELD; + } else if (itemName.endsWith("_helmet") || itemName.endsWith("_chestplate") || itemName.endsWith("_leggings") || itemName.endsWith("_boots")) { + template = ItemModelTemplates.GENERATED; + } + + ItemModelGenerator.generateItemModel(modId, itemName, template); + } + } + + public void doRecipeGen(String modId) { + for (ItemBuilder builder : REGISTERED_BUILDERS) { + if (builder.registeredItem != null && builder.recipeConsumer != null) { + RecipeGenerator.generateRecipe(modId, builder.itemName, (jsonConsumer, jsonSupplier) -> { + RecipeContext ctx = new RecipeContext(builder.registeredItem); + builder.recipeConsumer.accept(ctx, jsonConsumer); + }); + } + } + } + + public ItemBuilder properties(Consumer consumer) { + this.propertiesConsumer = consumer; + return this; + } + + public ItemBuilder model(ItemModelTemplates template) { + customModelMap.put(itemName, template); + return this; + } + + public ItemBuilder recipe(BiConsumer recipeConsumer) { + this.recipeConsumer = recipeConsumer; + return this; + } + + private Supplier registerTool(String toolName, Function toolConstructor, Consumer toolProperties) { + Item.Properties properties = new Item.Properties(); + propertiesConsumer.accept(properties); + if (toolProperties != null) { + toolProperties.accept(properties); + } + Supplier itemSupplier = () -> toolConstructor.apply(properties); + BlueLibConstants.PlatformHelper.REGISTRY.registerItem(toolName, (Supplier) itemSupplier); + generatedItems.add(toolName); + + return itemSupplier; + } + + public ItemBuilder sword(Consumer swordProperties, Tier tier, int attackDamage, float attackSpeed) { + String toolName = itemName + "_sword"; + registerTool(toolName, props -> new SwordItem(tier, props.attributes(PickaxeItem.createAttributes(tier, attackDamage, attackSpeed))), swordProperties); + return (ItemBuilder) this; + } + + public ItemBuilder pickaxe(Consumer pickaxeProperties, Tier tier, int attackDamage, float attackSpeed) { + String toolName = itemName + "_pickaxe"; + registerTool(toolName, props -> new PickaxeItem(tier, props.attributes(PickaxeItem.createAttributes(tier, attackDamage, attackSpeed))), pickaxeProperties); + return (ItemBuilder) this; + } + + public ItemBuilder axe(Consumer axeProperties, Tier tier, int attackDamage, float attackSpeed) { + String toolName = itemName + "_axe"; + registerTool(toolName, props -> new AxeItem(tier, props.attributes(PickaxeItem.createAttributes(tier, attackDamage, attackSpeed))), axeProperties); + return (ItemBuilder) this; + } + + public ItemBuilder shovel(Consumer shovelProperties, Tier tier, int attackDamage, float attackSpeed) { + String toolName = itemName + "_shovel"; + registerTool(toolName, props -> new ShovelItem(tier, props.attributes(PickaxeItem.createAttributes(tier, attackDamage, attackSpeed))), shovelProperties); + return (ItemBuilder) this; + } + + public ItemBuilder hoe(Consumer hoeProperties, Tier tier, int attackDamage, float attackSpeed) { + String toolName = itemName + "_hoe"; + registerTool(toolName, props -> new HoeItem(tier, props.attributes(PickaxeItem.createAttributes(tier, attackDamage, attackSpeed))), hoeProperties); + return (ItemBuilder) this; + } + + public ItemBuilder toolset(Tier tier, ToolsetConfig config) { + if (config.swordProperties != null) { + sword(config.swordProperties, tier, config.swordAttackDamage, config.swordAttackSpeed); + } + if (config.pickaxeProperties != null) { + pickaxe(config.pickaxeProperties, tier, config.pickaxeAttackDamage, config.pickaxeAttackSpeed); + } + if (config.axeProperties != null) { + axe(config.axeProperties, tier, config.axeAttackDamage, config.axeAttackSpeed); + } + if (config.shovelProperties != null) { + shovel(config.shovelProperties, tier, config.shovelAttackDamage, config.shovelAttackSpeed); + } + if (config.hoeProperties != null) { + hoe(config.hoeProperties, tier, config.hoeAttackDamage, config.hoeAttackSpeed); + } + + TOOLSETS.put(itemName, generatedItems.stream() + .map(itemName -> (Supplier) () -> BuiltInRegistries.ITEM.get(ResourceLocation.fromNamespaceAndPath(modId, itemName))) + .collect(Collectors.toList())); + + return this; + } + + public ItemBuilder helmet(Consumer properties, Holder material) { + String armorName = itemName + "_helmet"; + registerTool(armorName, props -> new ArmorItem(material, ArmorItem.Type.HELMET, props), properties); + return (ItemBuilder) this; + } + + public ItemBuilder chestplate(Consumer properties, Holder material) { + String armorName = itemName + "_chestplate"; + registerTool(armorName, props -> new ArmorItem(material, ArmorItem.Type.CHESTPLATE, props), properties); + return (ItemBuilder) this; + } + + public ItemBuilder leggings(Consumer properties, Holder material) { + String armorName = itemName + "_leggings"; + registerTool(armorName, props -> new ArmorItem(material, ArmorItem.Type.LEGGINGS, props), properties); + return (ItemBuilder) this; + } + + public ItemBuilder boots(Consumer properties, Holder material) { + String armorName = itemName + "_boots"; + registerTool(armorName, props -> new ArmorItem(material, ArmorItem.Type.BOOTS, props), properties); + return (ItemBuilder) this; + } + + public ItemBuilder armorSet(Holder material, ArmorSetConfig config) { + if (config.helmetProperties != null) + helmet(config.helmetProperties, material); + if (config.chestplateProperties != null) + chestplate(config.chestplateProperties, material); + if (config.leggingsProperties != null) + leggings(config.leggingsProperties, material); + if (config.bootsProperties != null) + boots(config.bootsProperties, material); + + ARMORSETS.put(itemName, generatedItems.stream() + .map(name -> (Supplier) () -> BuiltInRegistries.ITEM.get(ResourceLocation.fromNamespaceAndPath(modId, name))) + .collect(Collectors.toList())); + + return this; + } + + public Supplier register() { + Item.Properties properties = new Item.Properties(); + propertiesConsumer.accept(properties); + generatedItems.add(itemName); + Supplier itemSupplier = () -> { + T item = itemConstructor.apply(properties); + this.registeredItem = item; + return item; + }; + BlueLibConstants.PlatformHelper.REGISTRY.registerItem(itemName, itemSupplier); + REGISTERED_BUILDERS.add(this); + return itemSupplier; + } + + public static class RecipeContext { + + private final Item item; + + public RecipeContext(Item item) { + this.item = item; + } + + public Item getEntry() { + return item; + } + } +} diff --git a/common/src/main/java/software/bluelib/api/registry/builders/keybinds/KeybindBuilder.java b/common/src/main/java/software/bluelib/api/registry/builders/keybinds/KeybindBuilder.java new file mode 100644 index 00000000..2091364a --- /dev/null +++ b/common/src/main/java/software/bluelib/api/registry/builders/keybinds/KeybindBuilder.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2024 BlueLib Contributors + * + * This Source Code Form is subject to the terms of the MIT License. + * If a copy of the MIT License was not distributed with this file, + * You can obtain one at https://opensource.org/licenses/MIT. + */ +package software.bluelib.api.registry.builders.keybinds; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Supplier; +import net.minecraft.client.KeyMapping; +import software.bluelib.BlueLibConstants; + +public class KeybindBuilder { + + protected final List REGISTERED_BUILDERS = new ArrayList<>(); + protected final String modId; + private final String name; + private final int keyCode; + private String category; + private Supplier keyMappingSupplier; + + public KeybindBuilder(String name, int keyCode, String pModId) { + this.name = name; + this.keyCode = keyCode; + this.modId = pModId; + this.category = "key.categories." + pModId; + } + + public KeybindBuilder category(String category) { + this.category = category; + return this; + } + + public Supplier register() { + keyMappingSupplier = BlueLibConstants.PlatformHelper.REGISTRY.registerKeybind(name, () -> new KeyMapping( + "key." + modId + "." + name, + keyCode, + category)); + + REGISTERED_BUILDERS.add(this); + return keyMappingSupplier; + } + + public String getName() { + return name; + } + + public Supplier getKeyMapping() { + return keyMappingSupplier; + } +} diff --git a/common/src/main/java/software/bluelib/api/registry/builders/menu/MenuBuilder.java b/common/src/main/java/software/bluelib/api/registry/builders/menu/MenuBuilder.java new file mode 100644 index 00000000..a54fa816 --- /dev/null +++ b/common/src/main/java/software/bluelib/api/registry/builders/menu/MenuBuilder.java @@ -0,0 +1,14 @@ +/* + * Copyright (C) 2024 BlueLib Contributors + * + * This Source Code Form is subject to the terms of the MIT License. + * If a copy of the MIT License was not distributed with this file, + * You can obtain one at https://opensource.org/licenses/MIT. + */ +package software.bluelib.api.registry.builders.menu; + +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.gui.screens.inventory.MenuAccess; +import net.minecraft.world.inventory.AbstractContainerMenu; + +public class MenuBuilder> {} diff --git a/common/src/main/java/software/bluelib/api/registry/builders/tabs/CreativeTabBuilder.java b/common/src/main/java/software/bluelib/api/registry/builders/tabs/CreativeTabBuilder.java new file mode 100644 index 00000000..3d00a0f4 --- /dev/null +++ b/common/src/main/java/software/bluelib/api/registry/builders/tabs/CreativeTabBuilder.java @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2024 BlueLib Contributors + * + * This Source Code Form is subject to the terms of the MIT License. + * If a copy of the MIT License was not distributed with this file, + * You can obtain one at https://opensource.org/licenses/MIT. + */ +package software.bluelib.api.registry.builders.tabs; + +import java.util.*; +import java.util.function.Supplier; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.item.CreativeModeTab; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import software.bluelib.BlueLibConstants; +import software.bluelib.api.registry.builders.entity.LivingEntityBuilder; +import software.bluelib.api.registry.builders.items.ItemBuilder; +import software.bluelib.api.utils.logging.BaseLogLevel; +import software.bluelib.api.utils.logging.BaseLogger; + +public class CreativeTabBuilder { + + private final String id; + private final String modId; + private Supplier iconSupplier; + private CreativeModeTab.DisplayItemsGenerator displayItemsGenerator; + private String backgroundSuffix; + private final Map, CreativeTabBuilder> TAB_BUILDERS = new HashMap<>(); + + public CreativeTabBuilder(String id, String modId) { + this.id = id; + this.modId = modId; + } + + public CreativeTabBuilder icon(Supplier iconSupplier) { + this.iconSupplier = iconSupplier; + return this; + } + + public CreativeTabBuilder icon(Item iconItem) { + this.iconSupplier = () -> iconItem; + return this; + } + + public Supplier useSpawnEgg(Supplier> entityTypeSupplier) { + return () -> { + EntityType entityType = entityTypeSupplier.get(); + if (entityType != null) { + String entityId = BuiltInRegistries.ENTITY_TYPE.getKey(entityType).getPath(); + String spawnEggId = entityId + "_spawn_egg"; + return BuiltInRegistries.ITEM.get(ResourceLocation.fromNamespaceAndPath(modId, spawnEggId)); + } + return Items.AIR; + }; + } + + public CreativeTabBuilder displayItems(CreativeModeTab.DisplayItemsGenerator displayItemsGenerator) { + this.displayItemsGenerator = displayItemsGenerator; + return this; + } + + public CreativeTabBuilder background(String backgroundSuffix) { + this.backgroundSuffix = backgroundSuffix; + return this; + } + + public Supplier register() { + CreativeModeTab.Builder tabBuilder = CreativeModeTab.builder(CreativeModeTab.Row.TOP, 0) + .title(Component.translatable(id)) + .icon(() -> { + Item item = iconSupplier.get(); + return item != null ? new ItemStack(item) : ItemStack.EMPTY; + }) + .displayItems((parameters, output) -> { + Set addedItems = new HashSet<>(); // Track added items + if (displayItemsGenerator != null) { + displayItemsGenerator.accept(parameters, (stack, tabVisibility) -> { + Item item = stack.getItem(); + if (item != null && addedItems.add(item)) { + output.accept(stack, tabVisibility); + BaseLogger.log(BaseLogLevel.INFO, "Adding item from displayItemsGenerator: " + BuiltInRegistries.ITEM.getKey(item)); + } + }); + } + Item item = iconSupplier.get(); + if (item != null) { + List> toolsetItems = ItemBuilder.TOOLSETS.get(item.getDescriptionId()); + if (toolsetItems != null) { + for (Supplier tool : toolsetItems) { + Item toolItem = tool.get(); + if (toolItem != null && addedItems.add(toolItem)) { + output.accept(new ItemStack(toolItem), CreativeModeTab.TabVisibility.PARENT_AND_SEARCH_TABS); + BaseLogger.log(BaseLogLevel.INFO, "Adding icon toolset item: " + BuiltInRegistries.ITEM.getKey(toolItem)); + } + } + } else if (addedItems.add(item)) { + output.accept(new ItemStack(item), CreativeModeTab.TabVisibility.PARENT_AND_SEARCH_TABS); + BaseLogger.log(BaseLogLevel.INFO, "Adding icon item: " + BuiltInRegistries.ITEM.getKey(item)); + } + } + }); + + CreativeModeTab tab = tabBuilder.build(); + Supplier tabSupplier = () -> tab; + TAB_BUILDERS.put(tabSupplier, this); + BlueLibConstants.PlatformHelper.REGISTRY.registerTab(id, tabSupplier); + return tabSupplier; + } + + public void addToolset(Item item, CreativeModeTab.Output populator) { + if (item != null) { + Set addedItems = new HashSet<>(); // Track added items + String fullId = BuiltInRegistries.ITEM.getKey(item).getPath(); + String[] toolSuffixes = { "_sword", "_pickaxe", "_axe", "_shovel", "_hoe" }; + String baseId = fullId; + for (String suffix : toolSuffixes) { + if (fullId.endsWith(suffix)) { + baseId = fullId.substring(0, fullId.length() - suffix.length()); + break; + } + } + + List> toolset = ItemBuilder.TOOLSETS.get(baseId); + if (toolset != null) { + for (Supplier tool : toolset) { + Item toolItem = tool.get(); + if (toolItem != null && addedItems.add(toolItem)) { // Only add if not already present + populator.accept(new ItemStack(toolItem), CreativeModeTab.TabVisibility.PARENT_AND_SEARCH_TABS); + BaseLogger.log(BaseLogLevel.INFO, "Adding toolset item: " + BuiltInRegistries.ITEM.getKey(toolItem)); + } + } + return; + } + + if (addedItems.add(item)) { // Only add if not already present + populator.accept(new ItemStack(item), CreativeModeTab.TabVisibility.PARENT_AND_SEARCH_TABS); + BaseLogger.log(BaseLogLevel.INFO, "Adding single toolset item: " + fullId); + } + } + } + + public void addArmorSet(Item item, CreativeModeTab.Output populator) { + if (item != null) { + Set addedItems = new HashSet<>(); // Track added items + String fullId = BuiltInRegistries.ITEM.getKey(item).getPath(); + String[] toolSuffixes = { "_helmet", "_chestplate", "_leggings", "_boots" }; + String baseId = fullId; + for (String suffix : toolSuffixes) { + if (fullId.endsWith(suffix)) { + baseId = fullId.substring(0, fullId.length() - suffix.length()); + break; + } + } + + List> armorSets = ItemBuilder.ARMORSETS.get(baseId); + if (armorSets != null) { + for (Supplier armor : armorSets) { + Item armorItem = armor.get(); + if (armorItem != null && addedItems.add(armorItem)) { // Only add if not already present + populator.accept(new ItemStack(armorItem), CreativeModeTab.TabVisibility.PARENT_AND_SEARCH_TABS); + BaseLogger.log(BaseLogLevel.INFO, "Adding armor item: " + BuiltInRegistries.ITEM.getKey(armorItem)); + } + } + return; + } + + if (addedItems.add(item)) { // Only add if not already present + populator.accept(new ItemStack(item), CreativeModeTab.TabVisibility.PARENT_AND_SEARCH_TABS); + BaseLogger.log(BaseLogLevel.INFO, "Adding single armor item: " + fullId); + } + } + } + + public void addSpawnEgg(EntityType entityType, CreativeModeTab.Output populator) { + if (entityType != null) { + String entityId = BuiltInRegistries.ENTITY_TYPE.getKey(entityType).getPath(); + String spawnEggId = entityId + "_spawn_egg"; + Item spawnEggItem = BuiltInRegistries.ITEM.get(ResourceLocation.fromNamespaceAndPath(modId, spawnEggId)); + populator.accept(spawnEggItem); + } + } + + /*public void addAllSpawnEggs(CreativeModeTab.Output populator) { + List names = LivingEntityBuilder.getEntityNames(); + for (String name : names) { + String spawnEggId = name + "_spawn_egg"; + try { + Item spawnEggItem = BuiltInRegistries.ITEM.get(ResourceLocation.fromNamespaceAndPath(modId, spawnEggId)); + populator.accept(spawnEggItem); + } catch (Exception e) { + BaseLogger.log(BaseLogLevel.ERROR, Component.literal("Spawn egg for entity " + name + " not found!")); + } + } + }*/ +} diff --git a/common/src/main/java/software/bluelib/api/registry/datagen/DataGenUtils.java b/common/src/main/java/software/bluelib/api/registry/datagen/DataGenUtils.java new file mode 100644 index 00000000..e6d77a8d --- /dev/null +++ b/common/src/main/java/software/bluelib/api/registry/datagen/DataGenUtils.java @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2024 BlueLib Contributors + * + * This Source Code Form is subject to the terms of the MIT License. + * If a copy of the MIT License was not distributed with this file, + * You can obtain one at https://opensource.org/licenses/MIT. + */ +package software.bluelib.api.registry.datagen; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +public class DataGenUtils { + + public static final Gson GSON = new GsonBuilder() + .setPrettyPrinting() + .disableHtmlEscaping() + .create(); +} diff --git a/common/src/main/java/software/bluelib/api/registry/datagen/blocks/BlockModelGenerator.java b/common/src/main/java/software/bluelib/api/registry/datagen/blocks/BlockModelGenerator.java new file mode 100644 index 00000000..43e1d7b9 --- /dev/null +++ b/common/src/main/java/software/bluelib/api/registry/datagen/blocks/BlockModelGenerator.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2024 BlueLib Contributors + * + * This Source Code Form is subject to the terms of the MIT License. + * If a copy of the MIT License was not distributed with this file, + * You can obtain one at https://opensource.org/licenses/MIT. + */ +package software.bluelib.api.registry.datagen.blocks; + +import com.google.gson.JsonObject; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.Collections; +import java.util.Map; +import software.bluelib.BlueLibConstants; +import software.bluelib.api.registry.datagen.DataGenUtils; + +public class BlockModelGenerator extends DataGenUtils { + + public static void generateBlockModel(String modId, String name, BlockModelTemplates blockModelTemplate) { + generateBlockModel(modId, name, blockModelTemplate, Collections.emptyMap()); + } + + public static void generateBlockModel(String modId, String name, BlockModelTemplates blockModelTemplate, Map properties) { + Map blockModelJsons = blockModelTemplate.generateBlockModel(modId, name, properties); + + for (Map.Entry entry : blockModelJsons.entrySet()) { + String modelName = entry.getKey(); + JsonObject blockModelJson = entry.getValue(); + Path blockModelPath = Path.of(BlueLibConstants.PlatformHelper.PLATFORM.getAssetsDir(true, modId) + "/models/block/" + name + ".json"); + + try { + if (Files.exists(blockModelPath)) { + System.out.println("Block model for '" + modelName + "' already exists at: " + blockModelPath + ". Skipping creation."); + continue; + } + + Files.createDirectories(blockModelPath.getParent()); + Files.write(blockModelPath, GSON.toJson(blockModelJson).getBytes(), StandardOpenOption.CREATE_NEW); + System.out.println("Block model for '" + modelName + "' created at: " + blockModelPath); + System.out.println("Generated JSON for '" + modId + ":models/block/" + modelName + "':\n" + GSON.toJson(blockModelJson)); + + } catch (IOException e) { + System.err.println("[ERROR]: Failed to create block model for '" + modelName + "' at " + blockModelPath + ": " + e.getMessage()); + } + } + } +} diff --git a/common/src/main/java/software/bluelib/api/registry/datagen/blocks/BlockModelTemplates.java b/common/src/main/java/software/bluelib/api/registry/datagen/blocks/BlockModelTemplates.java new file mode 100644 index 00000000..374b91c1 --- /dev/null +++ b/common/src/main/java/software/bluelib/api/registry/datagen/blocks/BlockModelTemplates.java @@ -0,0 +1,327 @@ +/* + * Copyright (C) 2024 BlueLib Contributors + * + * This Source Code Form is subject to the terms of the MIT License. + * If a copy of the MIT License was not distributed with this file, + * You can obtain one at https://opensource.org/licenses/MIT. + */ +package software.bluelib.api.registry.datagen.blocks; + +import com.google.gson.JsonObject; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public abstract class BlockModelTemplates { + + public abstract Map generateBlockModel(String modId, String blockName, Map properties); + + public static final BlockModelTemplates CUBE_ALL = new BlockModelTemplates() { + + @Override + public Map generateBlockModel(String modId, String blockName, Map properties) { + JsonObject model = new JsonObject(); + model.addProperty("parent", "minecraft:block/cube_all"); + JsonObject textures = new JsonObject(); + String texture = properties.getOrDefault("all", modId + ":block/" + blockName); + textures.addProperty("all", texture); + model.add("textures", textures); + return Collections.singletonMap(blockName, model); + } + }; + + public static final BlockModelTemplates COLUMN = new BlockModelTemplates() { + + @Override + public Map generateBlockModel(String modId, String blockName, Map properties) { + JsonObject model = new JsonObject(); + model.addProperty("parent", "minecraft:block/cube_column"); + JsonObject textures = new JsonObject(); + String topTexture = properties.getOrDefault("top", modId + ":block/" + blockName + "_top"); + String sideTexture = properties.getOrDefault("side", modId + ":block/" + blockName); + textures.addProperty("end", topTexture); + textures.addProperty("side", sideTexture); + model.add("textures", textures); + return Collections.singletonMap(blockName, model); + } + }; + + public static final BlockModelTemplates CUBE = new BlockModelTemplates() { + + @Override + public Map generateBlockModel(String modId, String blockName, Map properties) { + JsonObject model = new JsonObject(); + model.addProperty("parent", "minecraft:block/cube"); + JsonObject textures = new JsonObject(); + textures.addProperty("up", properties.getOrDefault("up", modId + ":block/" + blockName + "_top")); + textures.addProperty("down", properties.getOrDefault("down", modId + ":block/" + blockName + "_bottom")); + textures.addProperty("north", properties.getOrDefault("north", modId + ":block/" + blockName)); + textures.addProperty("south", properties.getOrDefault("south", modId + ":block/" + blockName)); + textures.addProperty("east", properties.getOrDefault("east", modId + ":block/" + blockName)); + textures.addProperty("west", properties.getOrDefault("west", modId + ":block/" + blockName)); + model.add("textures", textures); + return Collections.singletonMap(blockName, model); + } + }; + + public static final BlockModelTemplates DOOR = new BlockModelTemplates() { + + @Override + public Map generateBlockModel(String modId, String blockName, Map properties) { + Map models = new HashMap<>(); + String bottomTexture = properties.getOrDefault("bottom", modId + ":block/" + blockName + "_bottom"); + String topTexture = properties.getOrDefault("top", modId + ":block/" + blockName + "_top"); + + String[] halves = { "bottom", "top" }; + String[] hinges = { "left", "right" }; + String[] opens = { "", "_open" }; + + for (String half : halves) { + for (String hinge : hinges) { + for (String open : opens) { + String modelName = String.format("%s_%s_%s%s", blockName, half, hinge, open); + JsonObject model = new JsonObject(); + String parent = String.format("minecraft:block/door_%s_%s%s", half, hinge, open); + model.addProperty("parent", parent); + JsonObject textures = new JsonObject(); + textures.addProperty("bottom", bottomTexture); + textures.addProperty("top", topTexture); + model.add("textures", textures); + models.put(modelName, model); + } + } + } + + return models; + } + }; + + public static final BlockModelTemplates FENCE = new BlockModelTemplates() { + + @Override + public Map generateBlockModel(String modId, String blockName, Map properties) { + Map models = new HashMap<>(); + String prefix = properties.getOrDefault("prefix", ""); + String s = prefix.isEmpty() ? blockName : prefix + "/" + blockName; + String texture = properties.getOrDefault("texture", modId + ":block/" + s); + + JsonObject postModel = new JsonObject(); + postModel.addProperty("parent", "minecraft:block/fence_post"); + JsonObject postTextures = new JsonObject(); + postTextures.addProperty("texture", texture); + postModel.add("textures", postTextures); + models.put(s + "_fence_post", postModel); + + JsonObject sideModel = new JsonObject(); + sideModel.addProperty("parent", "minecraft:block/fence_side"); + JsonObject sideTextures = new JsonObject(); + sideTextures.addProperty("texture", texture); + sideModel.add("textures", sideTextures); + models.put(s + "_fence_side", sideModel); + + JsonObject inventoryModel = new JsonObject(); + inventoryModel.addProperty("parent", "minecraft:block/fence_inventory"); + JsonObject inventoryTextures = new JsonObject(); + inventoryTextures.addProperty("texture", texture); + inventoryModel.add("textures", inventoryTextures); + models.put(blockName + "_fence_inventory", inventoryModel); + + return models; + } + }; + + public static final BlockModelTemplates BUTTON = new BlockModelTemplates() { + + @Override + public Map generateBlockModel(String modId, String blockName, Map properties) { + Map models = new HashMap<>(); + String texture = properties.getOrDefault("texture", modId + ":block/" + blockName); + + JsonObject buttonModel = new JsonObject(); + buttonModel.addProperty("parent", "minecraft:block/button"); + JsonObject buttonTextures = new JsonObject(); + buttonTextures.addProperty("texture", texture); + buttonModel.add("textures", buttonTextures); + models.put(blockName, buttonModel); + + JsonObject pressedModel = new JsonObject(); + pressedModel.addProperty("parent", "minecraft:block/button_pressed"); + JsonObject pressedTextures = new JsonObject(); + pressedTextures.addProperty("texture", texture); + pressedModel.add("textures", pressedTextures); + models.put(blockName + "_pressed", pressedModel); + + JsonObject inventoryModel = new JsonObject(); + inventoryModel.addProperty("parent", "minecraft:block/button_inventory"); + JsonObject inventoryTextures = new JsonObject(); + inventoryTextures.addProperty("texture", texture); + inventoryModel.add("textures", inventoryTextures); + models.put(blockName + "_inventory", inventoryModel); + + return models; + } + }; + + public static final BlockModelTemplates SLAB = new BlockModelTemplates() { + + @Override + public Map generateBlockModel(String modId, String blockName, Map properties) { + Map models = new HashMap<>(); + String texture = properties.getOrDefault("texture", modId + ":block/" + blockName.replace("_slab", "_planks")); + + JsonObject slabModel = new JsonObject(); + slabModel.addProperty("parent", "minecraft:block/slab"); + JsonObject slabTextures = new JsonObject(); + slabTextures.addProperty("bottom", texture); + slabTextures.addProperty("top", texture); + slabTextures.addProperty("side", texture); + slabModel.add("textures", slabTextures); + models.put(blockName, slabModel); + + JsonObject slabTopModel = new JsonObject(); + slabTopModel.addProperty("parent", "minecraft:block/slab_top"); + JsonObject slabTopTextures = new JsonObject(); + slabTopTextures.addProperty("bottom", texture); + slabTopTextures.addProperty("top", texture); + slabTopTextures.addProperty("side", texture); + slabTopModel.add("textures", slabTopTextures); + models.put(blockName + "_top", slabTopModel); + + return models; + } + }; + + public static final BlockModelTemplates TRAPDOOR = new BlockModelTemplates() { + + @Override + public Map generateBlockModel(String modId, String blockName, Map properties) { + Map models = new HashMap<>(); + String texture = properties.getOrDefault("texture", modId + ":block/" + blockName); + + JsonObject bottomModel = new JsonObject(); + bottomModel.addProperty("parent", "minecraft:block/trapdoor_bottom"); + JsonObject bottomTextures = new JsonObject(); + bottomTextures.addProperty("texture", texture); + bottomModel.add("textures", bottomTextures); + models.put(blockName + "_bottom", bottomModel); + + JsonObject topModel = new JsonObject(); + topModel.addProperty("parent", "minecraft:block/trapdoor_top"); + JsonObject topTextures = new JsonObject(); + topTextures.addProperty("texture", texture); + topModel.add("textures", topTextures); + models.put(blockName + "_top", topModel); + + JsonObject openModel = new JsonObject(); + openModel.addProperty("parent", "minecraft:block/trapdoor_open"); + JsonObject openTextures = new JsonObject(); + openTextures.addProperty("texture", texture); + openModel.add("textures", openTextures); + models.put(blockName + "_open", openModel); + + return models; + } + }; + + public static final BlockModelTemplates STAIRS = new BlockModelTemplates() { + + @Override + public Map generateBlockModel(String modId, String blockName, Map properties) { + Map models = new HashMap<>(); + String texture = properties.getOrDefault("texture", modId + ":block/" + blockName.replace("_stairs", "_planks")); + + JsonObject stairsModel = new JsonObject(); + stairsModel.addProperty("parent", "minecraft:block/stairs"); + JsonObject stairsTextures = new JsonObject(); + stairsTextures.addProperty("bottom", texture); + stairsTextures.addProperty("top", texture); + stairsTextures.addProperty("side", texture); + stairsModel.add("textures", stairsTextures); + models.put(blockName, stairsModel); + + JsonObject innerModel = new JsonObject(); + innerModel.addProperty("parent", "minecraft:block/inner_stairs"); + JsonObject innerTextures = new JsonObject(); + innerTextures.addProperty("bottom", texture); + innerTextures.addProperty("top", texture); + innerTextures.addProperty("side", texture); + innerModel.add("textures", innerTextures); + models.put(blockName + "_inner", innerModel); + + JsonObject outerModel = new JsonObject(); + outerModel.addProperty("parent", "minecraft:block/outer_stairs"); + JsonObject outerTextures = new JsonObject(); + outerTextures.addProperty("bottom", texture); + outerTextures.addProperty("top", texture); + outerTextures.addProperty("side", texture); + outerModel.add("textures", outerTextures); + models.put(blockName + "_outer", outerModel); + + return models; + } + }; + + public static final BlockModelTemplates PRESSURE_PLATE = new BlockModelTemplates() { + + @Override + public Map generateBlockModel(String modId, String blockName, Map properties) { + Map models = new HashMap<>(); + String texture = properties.getOrDefault("texture", modId + ":block/" + blockName); + + JsonObject plateModel = new JsonObject(); + plateModel.addProperty("parent", "minecraft:block/pressure_plate_up"); + JsonObject plateTextures = new JsonObject(); + plateTextures.addProperty("texture", texture); + plateModel.add("textures", plateTextures); + models.put(blockName, plateModel); + + JsonObject downModel = new JsonObject(); + downModel.addProperty("parent", "minecraft:block/pressure_plate_down"); + JsonObject downTextures = new JsonObject(); + downTextures.addProperty("texture", texture); + downModel.add("textures", downTextures); + models.put(blockName + "_down", downModel); + + return models; + } + }; + + public static final BlockModelTemplates FENCE_GATE = new BlockModelTemplates() { + + @Override + public Map generateBlockModel(String modId, String blockName, Map properties) { + Map models = new HashMap<>(); + String texture = properties.getOrDefault("texture", modId + ":block/" + blockName.replace("_fence_gate", "_planks")); + + JsonObject gateModel = new JsonObject(); + gateModel.addProperty("parent", "minecraft:block/fence_gate"); + JsonObject gateTextures = new JsonObject(); + gateTextures.addProperty("texture", texture); + gateModel.add("textures", gateTextures); + models.put(blockName, gateModel); + + JsonObject gateOpenModel = new JsonObject(); + gateOpenModel.addProperty("parent", "minecraft:block/fence_gate_open"); + JsonObject gateOpenTextures = new JsonObject(); + gateOpenTextures.addProperty("texture", texture); + gateOpenModel.add("textures", gateOpenTextures); + models.put(blockName + "_open", gateOpenModel); + + JsonObject gateWallModel = new JsonObject(); + gateWallModel.addProperty("parent", "minecraft:block/fence_gate_wall"); + JsonObject gateWallTextures = new JsonObject(); + gateWallTextures.addProperty("texture", texture); + gateWallModel.add("textures", gateWallTextures); + models.put(blockName + "_wall", gateWallModel); + + JsonObject gateWallOpenModel = new JsonObject(); + gateWallOpenModel.addProperty("parent", "minecraft:block/fence_gate_wall_open"); + JsonObject gateWallOpenTextures = new JsonObject(); + gateWallOpenTextures.addProperty("texture", texture); + gateWallOpenModel.add("textures", gateWallOpenTextures); + models.put(blockName + "_wall_open", gateWallOpenModel); + + return models; + } + }; +} diff --git a/common/src/main/java/software/bluelib/api/registry/datagen/blockstates/BlockstateGenerator.java b/common/src/main/java/software/bluelib/api/registry/datagen/blockstates/BlockstateGenerator.java new file mode 100644 index 00000000..db74b0bd --- /dev/null +++ b/common/src/main/java/software/bluelib/api/registry/datagen/blockstates/BlockstateGenerator.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2024 BlueLib Contributors + * + * This Source Code Form is subject to the terms of the MIT License. + * If a copy of the MIT License was not distributed with this file, + * You can obtain one at https://opensource.org/licenses/MIT. + */ +package software.bluelib.api.registry.datagen.blockstates; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.Collections; +import java.util.Map; +import software.bluelib.BlueLibConstants; +import software.bluelib.api.registry.datagen.DataGenUtils; + +public class BlockstateGenerator extends DataGenUtils { + + public static void generateBlockstate(String modId, String name, BlockstateTemplates blockstateTemplate) { + generateBlockstate(modId, name, blockstateTemplate, Collections.emptyMap()); + } + + public static void generateBlockstate(String modId, String name, BlockstateTemplates blockstateTemplate, Map properties) { + Path blockstatePath = Path.of(BlueLibConstants.PlatformHelper.PLATFORM.getAssetsDir(true, modId) + "/blockstates/" + name + ".json"); + + try { + if (Files.exists(blockstatePath)) { + System.out.println("Blockstate for '" + name + "' already exists at: " + blockstatePath + ". Skipping creation."); + return; + } + + JsonElement blockstateJson = generateBlockstateJson(modId, name, blockstateTemplate, properties); + + Files.createDirectories(blockstatePath.getParent()); + Files.write(blockstatePath, GSON.toJson(blockstateJson).getBytes(), StandardOpenOption.CREATE_NEW); + System.out.println("Blockstate for '" + name + "' created at: " + blockstatePath); + + } catch (IOException e) { + System.err.println("[ERROR]: Failed to create blockstate for '" + name + "' at " + blockstatePath + ": " + e.getMessage()); + } + } + + private static JsonElement generateBlockstateJson(String modId, String name, BlockstateTemplates blockstateTemplate, Map properties) { + JsonObject blockstateJson = blockstateTemplate.generateBlockstate(modId, name, properties); + System.out.println("Generated JSON for '" + modId + ":blockstates/" + name + "':\n" + GSON.toJson(blockstateJson)); + return blockstateJson; + } +} diff --git a/common/src/main/java/software/bluelib/api/registry/datagen/blockstates/BlockstateTemplates.java b/common/src/main/java/software/bluelib/api/registry/datagen/blockstates/BlockstateTemplates.java new file mode 100644 index 00000000..2dcabbd5 --- /dev/null +++ b/common/src/main/java/software/bluelib/api/registry/datagen/blockstates/BlockstateTemplates.java @@ -0,0 +1,426 @@ +/* + * Copyright (C) 2024 BlueLib Contributors + * + * This Source Code Form is subject to the terms of the MIT License. + * If a copy of the MIT License was not distributed with this file, + * You can obtain one at https://opensource.org/licenses/MIT. + */ +package software.bluelib.api.registry.datagen.blockstates; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import java.util.Map; + +public abstract class BlockstateTemplates { + + public abstract JsonObject generateBlockstate(String modId, String blockName, Map properties); + + public static final BlockstateTemplates SIMPLE_BLOCK = new BlockstateTemplates() { + + @Override + public JsonObject generateBlockstate(String modId, String blockName, Map properties) { + JsonObject blockstate = new JsonObject(); + JsonObject variants = new JsonObject(); + JsonObject variant = new JsonObject(); + variant.addProperty("model", modId + ":block/" + blockName); + variants.add("", variant); + blockstate.add("variants", variants); + return blockstate; + } + }; + + public static final BlockstateTemplates ORIENTED_BLOCK = new BlockstateTemplates() { + + @Override + public JsonObject generateBlockstate(String modId, String blockName, Map properties) { + JsonObject blockstate = new JsonObject(); + JsonObject variants = new JsonObject(); + + String model = modId + ":block/" + blockName; + String horizontalModel = properties.getOrDefault("horizontal_model", model); + + JsonObject xVariant = new JsonObject(); + xVariant.addProperty("model", horizontalModel); + xVariant.addProperty("x", 90); + xVariant.addProperty("y", 90); + variants.add("axis=x", xVariant); + + JsonObject yVariant = new JsonObject(); + yVariant.addProperty("model", model); + variants.add("axis=y", yVariant); + + JsonObject zVariant = new JsonObject(); + zVariant.addProperty("model", horizontalModel); + zVariant.addProperty("x", 90); + variants.add("axis=z", zVariant); + + blockstate.add("variants", variants); + return blockstate; + } + }; + + public static final BlockstateTemplates VARIANT_BLOCK = new BlockstateTemplates() { + + @Override + public JsonObject generateBlockstate(String modId, String blockName, Map properties) { + JsonObject blockstate = new JsonObject(); + JsonObject variants = new JsonObject(); + String propertyName = properties.getOrDefault("property", "type"); + String[] values = properties.getOrDefault("values", "default").split(","); + for (String value : values) { + JsonObject variant = new JsonObject(); + variant.addProperty("model", modId + ":block/" + blockName + "_" + value.trim()); + variants.add(propertyName + "=" + value.trim(), variant); + } + blockstate.add("variants", variants); + return blockstate; + } + }; + + public static final BlockstateTemplates MULTIPART_BLOCK = new BlockstateTemplates() { + + @Override + public JsonObject generateBlockstate(String modId, String blockName, Map properties) { + JsonObject blockstate = new JsonObject(); + JsonArray multipart = new JsonArray(); + + JsonObject basePart = new JsonObject(); + basePart.addProperty("model", modId + ":block/" + blockName); + multipart.add(basePart); + + String[] directions = { "north", "east", "south", "west" }; + for (String dir : directions) { + JsonObject part = new JsonObject(); + JsonObject when = new JsonObject(); + when.addProperty(dir, "true"); + part.add("when", when); + JsonObject apply = new JsonObject(); + apply.addProperty("model", modId + ":block/" + blockName + "_" + dir); + part.add("apply", apply); + multipart.add(part); + } + + blockstate.add("multipart", multipart); + return blockstate; + } + }; + + public static final BlockstateTemplates DOOR_BLOCK = new BlockstateTemplates() { + + @Override + public JsonObject generateBlockstate(String modId, String blockName, Map properties) { + JsonObject blockstate = new JsonObject(); + JsonObject variants = new JsonObject(); + + String modelPrefix = properties.getOrDefault("model_prefix", modId + ":block/" + blockName); + + String[] facings = { "east", "north", "south", "west" }; + String[] halves = { "lower", "upper" }; + String[] hinges = { "left", "right" }; + String[] opens = { "false", "true" }; + + int[][] rotations = { + { 0, 90, 270 }, + { 270, 0, 180 }, + { 90, 180, 0 }, + { 180, 270, 90 } + }; + + for (int f = 0; f < facings.length; f++) { + String facing = facings[f]; + for (String half : halves) { + for (String hinge : hinges) { + for (String open : opens) { + String variantKey = String.format("facing=%s,half=%s,hinge=%s,open=%s", facing, half, hinge, open); + JsonObject variant = new JsonObject(); + + String modelSuffix = String.format("%s_%s%s", + half.equals("lower") ? "bottom" : "top", + hinge, + open.equals("true") ? "_open" : ""); + String model = modelPrefix + "_" + modelSuffix; + variant.addProperty("model", model); + + int rotationIndex = open.equals("true") ? (hinge.equals("left") ? 1 : 2) : 0; + int yRotation = rotations[f][rotationIndex]; + if (yRotation != 0) { + variant.addProperty("y", yRotation); + } + + variants.add(variantKey, variant); + } + } + } + } + + blockstate.add("variants", variants); + return blockstate; + } + }; + + public static final BlockstateTemplates FENCE_BLOCK = new BlockstateTemplates() { + + @Override + public JsonObject generateBlockstate(String modId, String blockName, Map properties) { + JsonObject blockstate = new JsonObject(); + JsonArray multipart = new JsonArray(); + String prefix = properties.getOrDefault("prefix", ""); + String modelPath = prefix.isEmpty() ? modId + ":block/" + blockName : modId + ":block/" + prefix + "/" + blockName; + + JsonObject postPart = new JsonObject(); + postPart.addProperty("model", modelPath + "_fence_post"); + multipart.add(postPart); + + String[] directions = { "north", "east", "south", "west" }; + int[] rotations = { 0, 90, 180, 270 }; + for (int i = 0; i < directions.length; i++) { + String dir = directions[i]; + JsonObject part = new JsonObject(); + JsonObject when = new JsonObject(); + when.addProperty(dir, "true"); + part.add("when", when); + JsonObject apply = new JsonObject(); + apply.addProperty("model", modelPath + "_fence_side"); + apply.addProperty("uvlock", true); + if (rotations[i] != 0) { + apply.addProperty("y", rotations[i]); + } + part.add("apply", apply); + multipart.add(part); + } + + blockstate.add("multipart", multipart); + return blockstate; + } + }; + + public static final BlockstateTemplates BUTTON_BLOCK = new BlockstateTemplates() { + + @Override + public JsonObject generateBlockstate(String modId, String blockName, Map properties) { + JsonObject blockstate = new JsonObject(); + JsonObject variants = new JsonObject(); + String[] faces = { "ceiling", "floor", "wall" }; + String[] facings = { "east", "north", "south", "west" }; + String[] powered = { "false", "true" }; + int[][] ceilingRotations = { { 270, 180, 0, 90 } }; // east, north, south, west + int[][] floorRotations = { { 90, 0, 180, 270 } }; + int[][] wallRotations = { { 90, 0, 180, 270 } }; + + for (String face : faces) { + for (int f = 0; f < facings.length; f++) { + String facing = facings[f]; + for (String power : powered) { + String variantKey = String.format("face=%s,facing=%s,powered=%s", face, facing, power); + JsonObject variant = new JsonObject(); + String model = power.equals("true") ? modId + ":block/" + blockName + "_pressed" : modId + ":block/" + blockName; + variant.addProperty("model", model); + + int xRotation = face.equals("ceiling") ? 180 : face.equals("wall") ? 90 : 0; + int yRotation = switch (face) { + case "ceiling" -> ceilingRotations[0][f]; + case "floor" -> floorRotations[0][f]; + case "wall" -> wallRotations[0][f]; + default -> 0; + }; + + if (xRotation != 0) { + variant.addProperty("x", xRotation); + } + if (yRotation != 0) { + variant.addProperty("y", yRotation); + } + if (face.equals("wall")) { + variant.addProperty("uvlock", true); + } + + variants.add(variantKey, variant); + } + } + } + + blockstate.add("variants", variants); + return blockstate; + } + }; + + public static final BlockstateTemplates SLAB_BLOCK = new BlockstateTemplates() { + + @Override + public JsonObject generateBlockstate(String modId, String blockName, Map properties) { + JsonObject blockstate = new JsonObject(); + JsonObject variants = new JsonObject(); + String doubleModel = properties.getOrDefault("double_model", modId + ":block/" + blockName.replace("_slab", "_planks")); + + JsonObject bottomVariant = new JsonObject(); + bottomVariant.addProperty("model", modId + ":block/" + blockName); + variants.add("type=bottom", bottomVariant); + + JsonObject topVariant = new JsonObject(); + topVariant.addProperty("model", modId + ":block/" + blockName + "_top"); + variants.add("type=top", topVariant); + + JsonObject doubleVariant = new JsonObject(); + doubleVariant.addProperty("model", doubleModel); + variants.add("type=double", doubleVariant); + + blockstate.add("variants", variants); + return blockstate; + } + }; + + public static final BlockstateTemplates TRAPDOOR_BLOCK = new BlockstateTemplates() { + + @Override + public JsonObject generateBlockstate(String modId, String blockName, Map properties) { + JsonObject blockstate = new JsonObject(); + JsonObject variants = new JsonObject(); + String[] facings = { "east", "north", "south", "west" }; + String[] halves = { "bottom", "top" }; + String[] opens = { "false", "true" }; + int[][] rotations = { { 90, 0, 180, 270 }, { 270, 180, 0, 90 } }; // bottom/top: east, north, south, west + + for (String facing : facings) { + for (String half : halves) { + for (String open : opens) { + String variantKey = String.format("facing=%s,half=%s,open=%s", facing, half, open); + JsonObject variant = new JsonObject(); + String model; + if (open.equals("true")) { + model = modId + ":block/" + blockName + "_open"; + } else { + model = modId + ":block/" + blockName + (half.equals("bottom") ? "_bottom" : "_top"); + } + variant.addProperty("model", model); + + int yRotation = rotations[half.equals("bottom") ? 0 : 1][java.util.Arrays.asList(facings).indexOf(facing)]; + if (yRotation != 0) { + variant.addProperty("y", yRotation); + } + if (open.equals("true") && half.equals("top")) { + variant.addProperty("x", 180); + } + + variants.add(variantKey, variant); + } + } + } + + blockstate.add("variants", variants); + return blockstate; + } + }; + + public static final BlockstateTemplates STAIRS_BLOCK = new BlockstateTemplates() { + + @Override + public JsonObject generateBlockstate(String modId, String blockName, Map properties) { + JsonObject blockstate = new JsonObject(); + JsonObject variants = new JsonObject(); + String[] facings = { "east", "north", "south", "west" }; + String[] halves = { "bottom", "top" }; + String[] shapes = { "inner_left", "inner_right", "outer_left", "outer_right", "straight" }; + int[][] bottomRotations = { { 0, 270, 90, 180 }, { 270, 0, 180, 90 } }; // inner_left/inner_right, outer_left/outer_right: east, north, south, west + int[][] topRotations = { { 0, 270, 90, 180 }, { 90, 0, 180, 270 } }; + + for (String facing : facings) { + for (String half : halves) { + for (String shape : shapes) { + String variantKey = String.format("facing=%s,half=%s,shape=%s", facing, half, shape); + JsonObject variant = new JsonObject(); + String modelSuffix = shape.startsWith("inner") ? "_inner" : shape.startsWith("outer") ? "_outer" : ""; + variant.addProperty("model", modId + ":block/" + blockName + modelSuffix); + + int facingIndex = java.util.Arrays.asList(facings).indexOf(facing); + int yRotation = 0; + boolean uvlock = !shape.equals("straight"); + + if (half.equals("bottom")) { + yRotation = switch (shape) { + case "inner_left", "outer_left" -> bottomRotations[0][facingIndex]; + case "inner_right", "outer_right" -> bottomRotations[1][facingIndex]; + case "straight" -> bottomRotations[1][facingIndex]; + default -> yRotation; + }; + } else { + variant.addProperty("x", 180); + yRotation = switch (shape) { + case "inner_left", "outer_left" -> topRotations[0][facingIndex]; + case "inner_right", "outer_right" -> topRotations[1][facingIndex]; + case "straight" -> topRotations[1][facingIndex]; + default -> yRotation; + }; + } + + if (yRotation != 0) { + variant.addProperty("y", yRotation); + } + if (uvlock) { + variant.addProperty("uvlock", true); + } + + variants.add(variantKey, variant); + } + } + } + + blockstate.add("variants", variants); + return blockstate; + } + }; + + public static final BlockstateTemplates PRESSURE_PLATE_BLOCK = new BlockstateTemplates() { + + @Override + public JsonObject generateBlockstate(String modId, String blockName, Map properties) { + JsonObject blockstate = new JsonObject(); + JsonObject variants = new JsonObject(); + + JsonObject unpoweredVariant = new JsonObject(); + unpoweredVariant.addProperty("model", modId + ":block/" + blockName); + variants.add("powered=false", unpoweredVariant); + + JsonObject poweredVariant = new JsonObject(); + poweredVariant.addProperty("model", modId + ":block/" + blockName + "_down"); + variants.add("powered=true", poweredVariant); + + blockstate.add("variants", variants); + return blockstate; + } + }; + + public static final BlockstateTemplates FENCE_GATE_BLOCK = new BlockstateTemplates() { + + @Override + public JsonObject generateBlockstate(String modId, String blockName, Map properties) { + JsonObject blockstate = new JsonObject(); + JsonObject variants = new JsonObject(); + String[] facings = { "east", "north", "south", "west" }; + String[] inWalls = { "false", "true" }; + String[] opens = { "false", "true" }; + int[] yRotations = { 270, 180, 0, 90 }; // east, north, south, west + + for (int f = 0; f < facings.length; f++) { + String facing = facings[f]; + for (String inWall : inWalls) { + for (String open : opens) { + String variantKey = String.format("facing=%s,in_wall=%s,open=%s", facing, inWall, open); + JsonObject variant = new JsonObject(); + String modelSuffix = inWall.equals("true") ? "_wall" : ""; + modelSuffix += open.equals("true") ? "_open" : ""; + String model = modId + ":block/" + blockName + modelSuffix; + variant.addProperty("model", model); + variant.addProperty("uvlock", true); + if (yRotations[f] != 0) { + variant.addProperty("y", yRotations[f]); + } + variants.add(variantKey, variant); + } + } + } + + blockstate.add("variants", variants); + return blockstate; + } + }; +} diff --git a/common/src/main/java/software/bluelib/api/registry/datagen/entity/EntityTagBuilder.java b/common/src/main/java/software/bluelib/api/registry/datagen/entity/EntityTagBuilder.java new file mode 100644 index 00000000..688f33cb --- /dev/null +++ b/common/src/main/java/software/bluelib/api/registry/datagen/entity/EntityTagBuilder.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2024 BlueLib Contributors + * + * This Source Code Form is subject to the terms of the MIT License. + * If a copy of the MIT License was not distributed with this file, + * You can obtain one at https://opensource.org/licenses/MIT. + */ +package software.bluelib.api.registry.datagen.entity; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.util.*; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagKey; +import net.minecraft.world.entity.EntityType; +import software.bluelib.api.registry.AbstractRegistryBuilder; +import software.bluelib.api.registry.datagen.DataGenUtils; + +public class EntityTagBuilder extends DataGenUtils { + + private final List generatedTags = new ArrayList<>(); + private final Map>> tagEntityTypes = new HashMap<>(); + private final String name; + protected final String modId; + private final List> entityTypes = new ArrayList<>(); + + public EntityTagBuilder(String name, String pModId) { + this.name = name; + this.modId = pModId; + } + + public EntityTagBuilder addEntries(EntityType... entityTypes) { + this.entityTypes.addAll(Arrays.asList(entityTypes)); + return this; + } + + public TagKey> build() { + generatedTags.add(name); + tagEntityTypes.put(name, new ArrayList<>(entityTypes)); + return TagKey.create(Registries.ENTITY_TYPE, ResourceLocation.fromNamespaceAndPath(modId, name)); + } + + public void doTagJsonGen(String modId) { + for (String tagName : generatedTags) { + List> entities = tagEntityTypes.getOrDefault(tagName, new ArrayList<>()); + generateTagJson(modId, tagName, entities); + } + } + + private void generateTagJson(String modId, String tagName, List> entityTypes) { + Path tagPath = findProjectRoot().resolve(modId + "/tags/entity_type/" + tagName + ".json"); + + try { + if (Files.exists(tagPath)) { + System.out.println("Entity tag for '" + tagName + "' already exists at: " + tagPath + ". Skipping creation."); + return; + } + + JsonObject tagJson = new JsonObject(); + JsonArray values = new JsonArray(); + + for (EntityType entityType : entityTypes) { + ResourceLocation registryName = EntityType.getKey(entityType); + if (registryName != null) { + values.add(registryName.toString()); + } + } + + tagJson.add("values", values); + + Files.createDirectories(tagPath.getParent()); + Files.write(tagPath, GSON.toJson(tagJson).getBytes(), StandardOpenOption.CREATE_NEW); + System.out.println("Entity tag for '" + tagName + "' created at: " + tagPath); + + } catch (IOException e) { + System.err.println("Failed [ERROR]: Failed to create entity tag for '" + tagName + "' at " + tagPath + ": " + e.getMessage()); + } + } + + public Path findProjectRoot() { + Path current = Paths.get(System.getProperty("user.dir")).toAbsolutePath(); + while (current != null) { + Path resources = findResourcesPath(current); + if (resources != null) return resources; + current = current.getParent(); + } + throw new IllegalStateException("Could not locate project root"); + } + + private Path findResourcesPath(Path current) { + String[] potentialPaths = { + "src/main/resources/data", + "common/src/main/resources/data" + }; + + for (String path : potentialPaths) { + Path resources = current.resolve(path); + if (Files.exists(resources) && Files.isDirectory(resources)) { + return resources; + } + } + + String currentDirName = current.getFileName() != null ? current.getFileName().toString() : ""; + if (currentDirName.matches("fabric|forge|neoforge|quilt")) { + for (String path : potentialPaths) { + Path parentResources = current.getParent().resolve(path); + if (Files.exists(parentResources) && Files.isDirectory(parentResources)) { + return parentResources; + } + } + } + + return null; + } +} diff --git a/common/src/main/java/software/bluelib/api/registry/datagen/items/ItemModelGenerator.java b/common/src/main/java/software/bluelib/api/registry/datagen/items/ItemModelGenerator.java new file mode 100644 index 00000000..4690e64b --- /dev/null +++ b/common/src/main/java/software/bluelib/api/registry/datagen/items/ItemModelGenerator.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2024 BlueLib Contributors + * + * This Source Code Form is subject to the terms of the MIT License. + * If a copy of the MIT License was not distributed with this file, + * You can obtain one at https://opensource.org/licenses/MIT. + */ +package software.bluelib.api.registry.datagen.items; + +import com.google.gson.JsonElement; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.function.BiConsumer; +import java.util.function.Supplier; +import net.minecraft.data.models.model.ModelTemplate; +import net.minecraft.data.models.model.TextureMapping; +import net.minecraft.resources.ResourceLocation; +import software.bluelib.BlueLibConstants; +import software.bluelib.api.registry.datagen.DataGenUtils; + +public class ItemModelGenerator extends DataGenUtils { + + public static void generateItemModel(String modId, String name, ItemModelTemplates modelTemplate) { + Path itemModelPath = Path.of(BlueLibConstants.PlatformHelper.PLATFORM.getAssetsDir(true, modId) + "/models/item/" + name + ".json"); + + try { + if (Files.exists(itemModelPath)) { + System.out.println("Item model for '" + name + "' already exists at: " + itemModelPath + ". Skipping creation."); + return; + } + + JsonElement modelJson = generateModelJson(modId, name, modelTemplate); + + Files.createDirectories(itemModelPath.getParent()); + Files.write(itemModelPath, GSON.toJson(modelJson).getBytes(), StandardOpenOption.CREATE_NEW); + System.out.println("Item model for '" + name + "' created at: " + itemModelPath); + + } catch (IOException e) { + System.err.println("Failed [ERROR]: Failed to create item model for '" + name + "' at " + itemModelPath + ": " + e.getMessage()); + } + } + + private static JsonElement generateModelJson(String modId, String name, ItemModelTemplates modelTemplate) { + ResourceLocation modelLocation = ResourceLocation.fromNamespaceAndPath(modId, "item/" + name); + ModelTemplate template = modelTemplate.getTemplate(); + + final JsonElement[] capturedJson = new JsonElement[1]; + BiConsumer> tempConsumer = (location, jsonSupplier) -> { + capturedJson[0] = jsonSupplier.get(); + System.out.println("Generated JSON for '" + location + "':\n" + GSON.toJson(capturedJson[0])); + }; + + template.create(modelLocation, TextureMapping.layer0(ResourceLocation.fromNamespaceAndPath(modId, "item/" + name)), tempConsumer); + return capturedJson[0]; + } +} diff --git a/common/src/main/java/software/bluelib/api/registry/datagen/items/ItemModelTemplates.java b/common/src/main/java/software/bluelib/api/registry/datagen/items/ItemModelTemplates.java new file mode 100644 index 00000000..2fd288a8 --- /dev/null +++ b/common/src/main/java/software/bluelib/api/registry/datagen/items/ItemModelTemplates.java @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2024 BlueLib Contributors + * + * This Source Code Form is subject to the terms of the MIT License. + * If a copy of the MIT License was not distributed with this file, + * You can obtain one at https://opensource.org/licenses/MIT. + */ +package software.bluelib.api.registry.datagen.items; + +import com.google.gson.JsonObject; +import java.util.Map; +import java.util.Optional; +import net.minecraft.data.models.model.ModelTemplate; +import net.minecraft.data.models.model.TextureSlot; +import net.minecraft.resources.ResourceLocation; +import org.jetbrains.annotations.NotNull; + +public abstract class ItemModelTemplates { + + public static final ItemModelTemplates BLOCK_ITEM = new ItemModelTemplates() { + + @Override + public ModelTemplate getTemplate() { + return new ModelTemplate( + Optional.empty(), + Optional.empty()) { + + @Override + public @NotNull JsonObject createBaseTemplate(@NotNull ResourceLocation modelLocation, @NotNull Map modelGetter) { + JsonObject jsonObject = new JsonObject(); + String blockName = modelLocation.getPath().replace("item/", "block/"); + jsonObject.addProperty("parent", modelLocation.getNamespace() + ":" + blockName); + return jsonObject; + } + }; + } + }; + + public static final ItemModelTemplates BLOCK_WITH_INVENTORY_MODEL = new ItemModelTemplates() { + + @Override + public ModelTemplate getTemplate() { + return new ModelTemplate(Optional.empty(), Optional.empty()) { + + @Override + public @NotNull JsonObject createBaseTemplate(@NotNull ResourceLocation modelLocation, @NotNull Map modelGetter) { + JsonObject jsonObject = new JsonObject(); + String path = modelLocation.getPath().startsWith("item/") + ? modelLocation.getPath().substring(5) + : modelLocation.getPath(); + String blockName = path + "_fence_inventory"; + jsonObject.addProperty("parent", modelLocation.getNamespace() + ":block/" + blockName); + return jsonObject; + } + }; + } + }; + + public static final ItemModelTemplates BLOCK_SPRITE = new ItemModelTemplates() { + + @Override + public ModelTemplate getTemplate() { + return new ModelTemplate( + Optional.of(ResourceLocation.withDefaultNamespace("item/generated")), + Optional.empty(), + TextureSlot.LAYER0) { + + @Override + public @NotNull JsonObject createBaseTemplate(@NotNull ResourceLocation modelLocation, @NotNull Map modelGetter) { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("parent", "minecraft:item/generated"); + JsonObject textures = new JsonObject(); + ResourceLocation texture = modelGetter.getOrDefault(TextureSlot.LAYER0, + ResourceLocation.fromNamespaceAndPath(modelLocation.getNamespace(), modelLocation.getPath().replace("item/", "block/"))); + textures.addProperty("layer0", texture.toString()); + jsonObject.add("textures", textures); + return jsonObject; + } + }; + } + }; + + public static final ItemModelTemplates GENERATED = new ItemModelTemplates() { + + @Override + public ModelTemplate getTemplate() { + return new ModelTemplate( + Optional.of(ResourceLocation.withDefaultNamespace("item/generated")), + Optional.empty(), + TextureSlot.LAYER0) { + + @Override + public @NotNull JsonObject createBaseTemplate(@NotNull ResourceLocation modelLocation, @NotNull Map modelGetter) { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("parent", "minecraft:item/generated"); + JsonObject textures = new JsonObject(); + int layerCount = 0; + for (TextureSlot slot : modelGetter.keySet()) { + if (slot == TextureSlot.LAYER0 || slot.getId().startsWith("layer")) { + textures.addProperty("layer" + layerCount, modelGetter.get(slot).toString()); + layerCount++; + } + } + if (!modelGetter.containsKey(TextureSlot.LAYER0)) { + textures.addProperty("layer0", modelLocation.getNamespace() + ":item/" + modelLocation.getPath().replace("item/", "")); + } + jsonObject.add("textures", textures); + return jsonObject; + } + }; + } + }; + + public static final ItemModelTemplates HANDHELD = new ItemModelTemplates() { + + @Override + public ModelTemplate getTemplate() { + return new ModelTemplate( + Optional.of(ResourceLocation.withDefaultNamespace("item/handheld")), + Optional.empty(), + TextureSlot.LAYER0) { + + @Override + public @NotNull JsonObject createBaseTemplate(@NotNull ResourceLocation modelLocation, @NotNull Map modelGetter) { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("parent", "minecraft:item/handheld"); + JsonObject textures = new JsonObject(); + ResourceLocation texture = modelGetter.getOrDefault(TextureSlot.LAYER0, + ResourceLocation.fromNamespaceAndPath(modelLocation.getNamespace(), modelLocation.getPath())); + textures.addProperty("layer0", texture.toString()); + jsonObject.add("textures", textures); + return jsonObject; + } + }; + } + }; + + public static final ItemModelTemplates SPAWN_EGG = new ItemModelTemplates() { + + @Override + public ModelTemplate getTemplate() { + return new ModelTemplate( + Optional.of(ResourceLocation.fromNamespaceAndPath("minecraft", "item/template_spawn_egg")), + Optional.empty()) { + + @Override + public @NotNull JsonObject createBaseTemplate(@NotNull ResourceLocation modelLocation, @NotNull Map modelGetter) { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("parent", "minecraft:item/template_spawn_egg"); + return jsonObject; + } + }; + } + }; + + public abstract ModelTemplate getTemplate(); +} diff --git a/common/src/main/java/software/bluelib/api/registry/datagen/recipe/RecipeGenerator.java b/common/src/main/java/software/bluelib/api/registry/datagen/recipe/RecipeGenerator.java new file mode 100644 index 00000000..ba3180cf --- /dev/null +++ b/common/src/main/java/software/bluelib/api/registry/datagen/recipe/RecipeGenerator.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2024 BlueLib Contributors + * + * This Source Code Form is subject to the terms of the MIT License. + * If a copy of the MIT License was not distributed with this file, + * You can obtain one at https://opensource.org/licenses/MIT. + */ +package software.bluelib.api.registry.datagen.recipe; + +import com.google.gson.JsonElement; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.function.BiConsumer; +import java.util.function.Supplier; +import net.minecraft.data.recipes.RecipeOutput; +import software.bluelib.BlueLibConstants; +import software.bluelib.api.registry.datagen.DataGenUtils; + +public class RecipeGenerator extends DataGenUtils { + + public static void generateRecipe(String modId, String name, BiConsumer> recipeConsumer) { + Path recipePath = Path.of(BlueLibConstants.PlatformHelper.PLATFORM.getDataDir(true, modId) + "/recipe/" + name + ".json"); + + try { + if (Files.exists(recipePath)) { + System.out.println("Recipe for '" + name + "' already exists at: " + recipePath + ". Skipping creation."); + return; + } + + JsonElement recipeJson = BlueLibConstants.PlatformHelper.PLATFORM.generateRecipeJson(modId, name, recipeConsumer); + + if (recipeJson == null) { + System.err.println("Failed to generate recipe JSON for '" + name + "'. Skipping file creation."); + return; + } + + Files.createDirectories(recipePath.getParent()); + Files.write(recipePath, GSON.toJson(recipeJson).getBytes(), StandardOpenOption.CREATE_NEW); + System.out.println("Recipe for '" + name + "' created at: " + recipePath); + + } catch (IOException e) { + System.err.println("Failed [ERROR]: Failed to create recipe for '" + name + "' at " + recipePath + ": " + e.getMessage()); + } + } +} diff --git a/common/src/main/java/software/bluelib/api/registry/datagen/recipe/RecipeUtils.java b/common/src/main/java/software/bluelib/api/registry/datagen/recipe/RecipeUtils.java new file mode 100644 index 00000000..b17409ca --- /dev/null +++ b/common/src/main/java/software/bluelib/api/registry/datagen/recipe/RecipeUtils.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2024 BlueLib Contributors + * + * This Source Code Form is subject to the terms of the MIT License. + * If a copy of the MIT License was not distributed with this file, + * You can obtain one at https://opensource.org/licenses/MIT. + */ +package software.bluelib.api.registry.datagen.recipe; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.mojang.serialization.JsonOps; +import java.lang.reflect.Field; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Ingredient; +import net.minecraft.world.item.crafting.Recipe; + +public class RecipeUtils { + + public static JsonObject serializeIngredient(Ingredient ingredient) { + JsonElement json = Ingredient.CODEC.encodeStart(JsonOps.INSTANCE, ingredient).result().orElseThrow(); + return json.getAsJsonObject(); + } + + public static JsonObject serializeResult(ItemStack stack) { + JsonObject result = new JsonObject(); + ResourceLocation id = BuiltInRegistries.ITEM.getKey(stack.getItem()); + result.addProperty("id", id.toString()); + if (stack.getCount() > 1) { + result.addProperty("count", stack.getCount()); + } + return result; + } + + public static ItemStack getSmithingRecipeResult(Recipe recipe) { + try { + Field resultField = recipe.getClass().getDeclaredField("result"); + resultField.setAccessible(true); + Object value = resultField.get(recipe); + if (value instanceof ItemStack stack) { + return stack; + } + } catch (Exception ignored) {} + return ItemStack.EMPTY; + } +} diff --git a/common/src/main/java/software/bluelib/api/registry/helpers/ArmorSetConfig.java b/common/src/main/java/software/bluelib/api/registry/helpers/ArmorSetConfig.java new file mode 100644 index 00000000..f45caa72 --- /dev/null +++ b/common/src/main/java/software/bluelib/api/registry/helpers/ArmorSetConfig.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2024 BlueLib Contributors + * + * This Source Code Form is subject to the terms of the MIT License. + * If a copy of the MIT License was not distributed with this file, + * You can obtain one at https://opensource.org/licenses/MIT. + */ +package software.bluelib.api.registry.helpers; + +import java.util.function.Consumer; +import net.minecraft.world.item.Item; + +public class ArmorSetConfig { + + public Consumer helmetProperties; + public Consumer chestplateProperties; + public Consumer leggingsProperties; + public Consumer bootsProperties; + + public ArmorSetConfig helmet(Consumer properties) { + this.helmetProperties = properties; + return this; + } + + public ArmorSetConfig chestplate(Consumer properties) { + this.chestplateProperties = properties; + return this; + } + + public ArmorSetConfig leggings(Consumer properties) { + this.leggingsProperties = properties; + return this; + } + + public ArmorSetConfig boots(Consumer properties) { + this.bootsProperties = properties; + return this; + } +} diff --git a/common/src/main/java/software/bluelib/api/registry/helpers/ToolsetConfig.java b/common/src/main/java/software/bluelib/api/registry/helpers/ToolsetConfig.java new file mode 100644 index 00000000..65137cfa --- /dev/null +++ b/common/src/main/java/software/bluelib/api/registry/helpers/ToolsetConfig.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2024 BlueLib Contributors + * + * This Source Code Form is subject to the terms of the MIT License. + * If a copy of the MIT License was not distributed with this file, + * You can obtain one at https://opensource.org/licenses/MIT. + */ +package software.bluelib.api.registry.helpers; + +import java.util.function.Consumer; +import net.minecraft.world.item.Item; + +public class ToolsetConfig { + + public Consumer swordProperties; + public Consumer pickaxeProperties; + public Consumer axeProperties; + public Consumer shovelProperties; + public Consumer hoeProperties; + + public int swordAttackDamage = 3; + public float swordAttackSpeed = -2.4F; + public int pickaxeAttackDamage = 1; + public float pickaxeAttackSpeed = -2.8F; + public int axeAttackDamage = 6; + public float axeAttackSpeed = -3.0F; + public int shovelAttackDamage = 1; + public float shovelAttackSpeed = -3.0F; + public int hoeAttackDamage = 0; + public float hoeAttackSpeed = -3.0F; + + public ToolsetConfig sword(int attackDamage, float attackSpeed, Consumer properties) { + this.swordAttackDamage = attackDamage; + this.swordAttackSpeed = attackSpeed; + this.swordProperties = properties; + return this; + } + + public ToolsetConfig pickaxe(int attackDamage, float attackSpeed, Consumer properties) { + this.pickaxeAttackDamage = attackDamage; + this.pickaxeAttackSpeed = attackSpeed; + this.pickaxeProperties = properties; + return this; + } + + public ToolsetConfig axe(int attackDamage, float attackSpeed, Consumer properties) { + this.axeAttackDamage = attackDamage; + this.axeAttackSpeed = attackSpeed; + this.axeProperties = properties; + return this; + } + + public ToolsetConfig shovel(int attackDamage, float attackSpeed, Consumer properties) { + this.shovelAttackDamage = attackDamage; + this.shovelAttackSpeed = attackSpeed; + this.shovelProperties = properties; + return this; + } + + public ToolsetConfig hoe(int attackDamage, float attackSpeed, Consumer properties) { + this.hoeAttackDamage = attackDamage; + this.hoeAttackSpeed = attackSpeed; + this.hoeProperties = properties; + return this; + } +} diff --git a/common/src/main/java/software/bluelib/api/registry/helpers/entity/AttributeHelper.java b/common/src/main/java/software/bluelib/api/registry/helpers/entity/AttributeHelper.java new file mode 100644 index 00000000..e4aebb44 --- /dev/null +++ b/common/src/main/java/software/bluelib/api/registry/helpers/entity/AttributeHelper.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2024 BlueLib Contributors + * + * This Source Code Form is subject to the terms of the MIT License. + * If a copy of the MIT License was not distributed with this file, + * You can obtain one at https://opensource.org/licenses/MIT. + */ +package software.bluelib.api.registry.helpers.entity; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.BiConsumer; +import java.util.function.Supplier; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.ai.attributes.AttributeSupplier; + +public class AttributeHelper { + + private final List> ENTRIES = new ArrayList<>(); + + public void queueAttributes(Supplier> type, Supplier attributes) { + ENTRIES.add(new Entry<>(type, attributes)); + } + + public void registerAttributes(BiConsumer, AttributeSupplier> registrar) { + for (Entry entry : ENTRIES) { + registrar.accept(entry.type.get(), entry.attributes.get().build()); + } + } + + private record Entry(Supplier> type, + Supplier attributes) {} +} diff --git a/common/src/main/java/software/bluelib/api/registry/helpers/entity/RenderHelper.java b/common/src/main/java/software/bluelib/api/registry/helpers/entity/RenderHelper.java new file mode 100644 index 00000000..8e53c897 --- /dev/null +++ b/common/src/main/java/software/bluelib/api/registry/helpers/entity/RenderHelper.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2024 BlueLib Contributors + * + * This Source Code Form is subject to the terms of the MIT License. + * If a copy of the MIT License was not distributed with this file, + * You can obtain one at https://opensource.org/licenses/MIT. + */ +package software.bluelib.api.registry.helpers.entity; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.BiConsumer; +import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; +import net.minecraft.client.renderer.entity.EntityRendererProvider; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityType; + +public class RenderHelper { + + private final List, EntityRendererProvider>, BiConsumer, BlockEntityRendererProvider>>> renderers = new ArrayList<>(); + + public void queueRenderer(BiConsumer, EntityRendererProvider>, BiConsumer, BlockEntityRendererProvider>> consumer) { + renderers.add(consumer); + } + + public void registerRenderers(BiConsumer, EntityRendererProvider> entityConsumer, BiConsumer, BlockEntityRendererProvider> blockConsumer) { + for (var r : renderers) { + r.accept(entityConsumer, blockConsumer); + } + } +} diff --git a/common/src/main/java/software/bluelib/api/registry/helpers/items/BlueSpawnEggItem.java b/common/src/main/java/software/bluelib/api/registry/helpers/items/BlueSpawnEggItem.java new file mode 100644 index 00000000..3953d5ae --- /dev/null +++ b/common/src/main/java/software/bluelib/api/registry/helpers/items/BlueSpawnEggItem.java @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2024 BlueLib Contributors + * + * This Source Code Form is subject to the terms of the MIT License. + * If a copy of the MIT License was not distributed with this file, + * You can obtain one at https://opensource.org/licenses/MIT. + */ +package software.bluelib.api.registry.helpers.items; + +import com.google.common.collect.Iterables; +import com.google.common.collect.Maps; +import com.mojang.serialization.MapCodec; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import javax.annotation.Nullable; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.component.DataComponents; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.stats.Stats; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.InteractionResultHolder; +import net.minecraft.world.entity.*; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.flag.FeatureFlagSet; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.component.CustomData; +import net.minecraft.world.item.context.UseOnContext; +import net.minecraft.world.level.ClipContext; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.Spawner; +import net.minecraft.world.level.block.LiquidBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.gameevent.GameEvent; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.HitResult; +import net.minecraft.world.phys.Vec3; +import org.jetbrains.annotations.NotNull; + +public class BlueSpawnEggItem extends Item { + + private static final Map, BlueSpawnEggItem> BY_ID = Maps.newIdentityHashMap(); + private static final MapCodec> ENTITY_TYPE_FIELD_CODEC = BuiltInRegistries.ENTITY_TYPE.byNameCodec().fieldOf("id"); + private final int backgroundColor; + private final int highlightColor; + private final EntityType defaultType; + + public BlueSpawnEggItem(EntityType defaultType, int backgroundColor, int highlightColor, Item.Properties properties) { + super(properties); + this.defaultType = defaultType; + this.backgroundColor = backgroundColor; + this.highlightColor = highlightColor; + BY_ID.put(defaultType, this); + } + + @Override + public @NotNull InteractionResult useOn(UseOnContext context) { + Level level = context.getLevel(); + if (!(level instanceof ServerLevel)) { + return InteractionResult.SUCCESS; + } else { + ItemStack itemstack = context.getItemInHand(); + BlockPos blockpos = context.getClickedPos(); + Direction direction = context.getClickedFace(); + BlockState blockstate = level.getBlockState(blockpos); + if (level.getBlockEntity(blockpos) instanceof Spawner spawner) { + EntityType entitytype1 = this.getType(itemstack); + spawner.setEntityId(entitytype1, level.getRandom()); + level.sendBlockUpdated(blockpos, blockstate, blockstate, 3); + level.gameEvent(context.getPlayer(), GameEvent.BLOCK_CHANGE, blockpos); + itemstack.shrink(1); + } else { + BlockPos blockpos1; + if (blockstate.getCollisionShape(level, blockpos).isEmpty()) { + blockpos1 = blockpos; + } else { + blockpos1 = blockpos.relative(direction); + } + + EntityType entitytype = this.getType(itemstack); + if (entitytype.spawn( + (ServerLevel) level, + itemstack, + context.getPlayer(), + blockpos1, + MobSpawnType.SPAWN_EGG, + true, + !Objects.equals(blockpos, blockpos1) && direction == Direction.UP) != null) { + itemstack.shrink(1); + level.gameEvent(context.getPlayer(), GameEvent.ENTITY_PLACE, blockpos); + } + + } + return InteractionResult.CONSUME; + } + } + + @Override + public @NotNull InteractionResultHolder use(@NotNull Level level, Player player, @NotNull InteractionHand hand) { + ItemStack itemstack = player.getItemInHand(hand); + BlockHitResult blockhitresult = getPlayerPOVHitResult(level, player, ClipContext.Fluid.SOURCE_ONLY); + if (blockhitresult.getType() != HitResult.Type.BLOCK) { + return InteractionResultHolder.pass(itemstack); + } else if (!(level instanceof ServerLevel)) { + return InteractionResultHolder.success(itemstack); + } else { + BlockPos blockpos = blockhitresult.getBlockPos(); + if (!(level.getBlockState(blockpos).getBlock() instanceof LiquidBlock)) { + return InteractionResultHolder.pass(itemstack); + } else if (level.mayInteract(player, blockpos) && player.mayUseItemAt(blockpos, blockhitresult.getDirection(), itemstack)) { + EntityType entitytype = this.getType(itemstack); + Entity entity = entitytype.spawn((ServerLevel) level, itemstack, player, blockpos, MobSpawnType.SPAWN_EGG, false, false); + if (entity == null) { + return InteractionResultHolder.pass(itemstack); + } else { + itemstack.consume(1, player); + player.awardStat(Stats.ITEM_USED.get(this)); + level.gameEvent(player, GameEvent.ENTITY_PLACE, entity.position()); + return InteractionResultHolder.consume(itemstack); + } + } else { + return InteractionResultHolder.fail(itemstack); + } + } + } + + public boolean spawnsEntity(ItemStack stack, EntityType entityType) { + return Objects.equals(this.getType(stack), entityType); + } + + public int getColor(int tintIndex) { + return tintIndex == 0 ? this.backgroundColor : this.highlightColor; + } + + @Nullable + public static BlueSpawnEggItem byId(@Nullable EntityType type) { + return BY_ID.get(type); + } + + public static Iterable eggs() { + return Iterables.unmodifiableIterable(BY_ID.values()); + } + + public EntityType getType(ItemStack stack) { + CustomData customdata = stack.getOrDefault(DataComponents.ENTITY_DATA, CustomData.EMPTY); + return !customdata.isEmpty() ? customdata.read(ENTITY_TYPE_FIELD_CODEC).result().orElse(this.defaultType) : this.defaultType; + } + + @Override + public @NotNull FeatureFlagSet requiredFeatures() { + return this.defaultType.requiredFeatures(); + } + + public Optional spawnOffspringFromSpawnEgg(Player player, Mob p_mob, EntityType entityType, ServerLevel serverLevel, Vec3 pos, ItemStack stack) { + if (!this.spawnsEntity(stack, entityType)) { + return Optional.empty(); + } else { + Mob mob; + if (p_mob instanceof AgeableMob) { + mob = ((AgeableMob) p_mob).getBreedOffspring(serverLevel, (AgeableMob) p_mob); + } else { + mob = entityType.create(serverLevel); + } + + if (mob == null) { + return Optional.empty(); + } else { + mob.setBaby(true); + if (!mob.isBaby()) { + return Optional.empty(); + } else { + mob.moveTo(pos.x(), pos.y(), pos.z(), 0.0F, 0.0F); + serverLevel.addFreshEntityWithPassengers(mob); + mob.setCustomName(stack.get(DataComponents.CUSTOM_NAME)); + stack.consume(1, player); + return Optional.of(mob); + } + } + } + } +} diff --git a/common/src/main/java/software/bluelib/internal/registry/TestEntityReg.java b/common/src/main/java/software/bluelib/internal/registry/TestEntityReg.java new file mode 100644 index 00000000..f19811bc --- /dev/null +++ b/common/src/main/java/software/bluelib/internal/registry/TestEntityReg.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2024 BlueLib Contributors + * + * This Source Code Form is subject to the terms of the MIT License. + * If a copy of the MIT License was not distributed with this file, + * You can obtain one at https://opensource.org/licenses/MIT. + */ +package software.bluelib.internal.registry; + +import static software.bluelib.BlueLibCommon.REGISTRIES; + +import java.util.function.Supplier; +import net.minecraft.client.renderer.entity.PigRenderer; +import net.minecraft.data.recipes.*; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.MobCategory; +import net.minecraft.world.entity.animal.Pig; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.crafting.Ingredient; +import net.minecraft.world.level.block.Block; + +public class TestEntityReg { + + public static void init() { + //BaseLogger.log(BaseLogLevel.SUCCESS, "Registered Entities!"); + } + + public static final Supplier> TEST_ENTITY = REGISTRIES.livingEntity("test_e", Pig::new, MobCategory.CREATURE) + .attributes(Pig::createAttributes) + .renderer(PigRenderer::new) + .spawnEgg(0x0000, 0x0000) + .register(); + + public static final Supplier TEST_BLOCK = REGISTRIES.block("test_block", Block::new) + .properties(Block.Properties.of().strength(1.0F, 1.0F)) + .defaultItem() + .recipe((ctx, prov) -> ShapelessRecipeBuilder.shapeless(RecipeCategory.MISC, ctx.getEntry()) + .requires(Items.APPLE) + .unlockedBy("has_diamond", RecipeProvider.has(Items.DIAMOND)) + .save(prov)) + .defaultBlockstate() + .register(); + + public static final Supplier TEST_ITEM = REGISTRIES.item("test_item", Item::new) + .recipe((ctx, prov) -> ShapedRecipeBuilder.shaped(RecipeCategory.MISC, ctx.getEntry(), 5) + .pattern(" X ") + .pattern("XWX") + .pattern(" X ") + .define('X', Items.RABBIT_FOOT) + .define('W', Items.APPLE) + .unlockedBy("has_diamond", RecipeProvider.has(Items.DIAMOND)) + .save(prov)) + .register(); + + public static final Supplier TEXT_ITEM = REGISTRIES.item("text_item", Item::new) + .recipe((ctx, prov) -> SimpleCookingRecipeBuilder.smoking(Ingredient.of(TEST_BLOCK.get()), RecipeCategory.MISC, ctx.getEntry(), 5, 3) + .unlockedBy("has_diamond", RecipeProvider.has(Items.DIAMOND)) + .save(prov)) + .register(); + + public static final Supplier TASTE_ITEM = REGISTRIES.item("taste_item", Item::new) + .recipe((ctx, prov) -> ShapelessRecipeBuilder.shapeless(RecipeCategory.MISC, ctx.getEntry()) + .requires(Items.CARROT) + .unlockedBy("has_diamond", RecipeProvider.has(Items.DIAMOND)) + .save(prov)) + .register(); + + public static final Supplier TASTE_TEST_ITEM = REGISTRIES.item("taste_test_item", Item::new) + .recipe((ctx, prov) -> SmithingTransformRecipeBuilder.smithing(Ingredient.of(Items.COOKIE), Ingredient.of(Items.COOKIE), + Ingredient.of(Items.COOKIE), RecipeCategory.MISC, ctx.getEntry()) + .unlocks("has_diamond", RecipeProvider.has(Items.DIAMOND)) + .save(prov, ResourceLocation.fromNamespaceAndPath(REGISTRIES.getModID(), "taste_test_item"))) + .register(); +} diff --git a/common/src/main/java/software/bluelib/platform/IPlatformHelper.java b/common/src/main/java/software/bluelib/platform/IPlatformHelper.java index e618a12f..59f965c5 100644 --- a/common/src/main/java/software/bluelib/platform/IPlatformHelper.java +++ b/common/src/main/java/software/bluelib/platform/IPlatformHelper.java @@ -7,8 +7,13 @@ */ package software.bluelib.platform; +import com.google.gson.JsonElement; +import java.nio.file.Path; import java.util.List; import java.util.Set; +import java.util.function.BiConsumer; +import java.util.function.Supplier; +import net.minecraft.data.recipes.RecipeOutput; import net.minecraft.server.MinecraftServer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -43,6 +48,12 @@ default String getEnvironmentName() { @NotNull ModAPI getAPI(); + JsonElement generateRecipeJson(String modId, String name, BiConsumer> recipeConsumer); + + Path getAssetsDir(boolean isCommon, String pModID); + + Path getDataDir(boolean isCommon, String pModID); + @Nullable MinecraftServer getServer(); } diff --git a/common/src/main/java/software/bluelib/platform/IRegistryHelper.java b/common/src/main/java/software/bluelib/platform/IRegistryHelper.java index 35bd17f4..3ccb0b07 100644 --- a/common/src/main/java/software/bluelib/platform/IRegistryHelper.java +++ b/common/src/main/java/software/bluelib/platform/IRegistryHelper.java @@ -8,8 +8,18 @@ package software.bluelib.platform; import java.util.function.Supplier; +import net.minecraft.client.KeyMapping; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.CreativeModeTab; +import net.minecraft.world.item.Item; import net.minecraft.world.item.crafting.RecipeSerializer; import net.minecraft.world.item.crafting.RecipeType; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityType; import org.jetbrains.annotations.NotNull; import software.bluelib.api.net.NetworkManager; @@ -18,6 +28,22 @@ public interface IRegistryHelper { @NotNull NetworkManager getNetwork(); + Supplier> registerEntity(String pId, Supplier> pEntity); + + Supplier registerTab(String pId, Supplier pTab); + + Supplier registerItem(String id, Supplier pItem); + + Supplier registerBlock(String pId, Supplier pBlock); + + Supplier> registerBlockEntity(String pId, Supplier> pBlockEntity); + + > Supplier registerMenu(String pId, Supplier pMenu); + + Supplier registerBiome(String pId, Supplier pBiome); + + Supplier registerKeybind(String pId, Supplier pKeybind); + @NotNull > Supplier registerRecipeType(@NotNull String pId, @NotNull Supplier pRecipeType); diff --git a/common/src/main/resources/META-INF/accesstransformer-nf.cfg b/common/src/main/resources/META-INF/accesstransformer-nf.cfg index a7fc4ca3..97fe964f 100644 --- a/common/src/main/resources/META-INF/accesstransformer-nf.cfg +++ b/common/src/main/resources/META-INF/accesstransformer-nf.cfg @@ -1,4 +1,21 @@ +public net.minecraft.world.level.block.entity.BlockEntityType$BlockEntitySupplier +public net.minecraft.core.registries.BuiltInRegistries registerSimple(Lnet/minecraft/resources/ResourceKey;Lnet/minecraft/core/registries/BuiltInRegistries$RegistryBootstrap;)Lnet/minecraft/core/Registry; # registerSimple +public net.minecraft.core.registries.BuiltInRegistries$RegistryBootstrap +public net.minecraft.world.inventory.MenuType$MenuSupplier +public net.minecraft.world.inventory.MenuType (Lnet/minecraft/world/inventory/MenuType$MenuSupplier;Lnet/minecraft/world/flag/FeatureFlagSet;)V +public net.minecraft.client.gui.screens.MenuScreens$ScreenConstructor +public net.minecraft.world.item.CreativeModeTab$TabVisibility +public net.minecraft.data.recipes.RecipeProvider has(Lnet/minecraft/world/level/ItemLike;)Lnet/minecraft/advancements/Criterion; # has +public net.minecraft.data.recipes.RecipeProvider has(Lnet/minecraft/advancements/critereon/MinMaxBounds$Ints;Lnet/minecraft/world/level/ItemLike;)Lnet/minecraft/advancements/Criterion; # has public net.minecraft.world.inventory.BrewingStandMenu$IngredientsSlot public net.minecraft.world.inventory.BrewingStandMenu$FuelSlot -public net.minecraft.world.inventory.BrewingStandMenu$PotionSlot \ No newline at end of file +public net.minecraft.world.inventory.BrewingStandMenu$PotionSlot + +public net.minecraft.world.item.crafting.SmithingTransformRecipe template # template +public net.minecraft.world.item.crafting.SmithingTransformRecipe base # base +public net.minecraft.world.item.crafting.SmithingTransformRecipe addition # addition + +public net.minecraft.world.item.crafting.SmithingTrimRecipe template # template +public net.minecraft.world.item.crafting.SmithingTrimRecipe base # base +public net.minecraft.world.item.crafting.SmithingTrimRecipe addition # addition \ No newline at end of file diff --git a/fabric/src/main/java/software/bluelib/BlueLib.java b/fabric/src/main/java/software/bluelib/BlueLib.java index c8161df6..6878377e 100644 --- a/fabric/src/main/java/software/bluelib/BlueLib.java +++ b/fabric/src/main/java/software/bluelib/BlueLib.java @@ -53,4 +53,22 @@ private void clientEndTick() { }); } } + /*@Override + public void onInitialize() { + ReloadHandler.registerProvider(new VariantProvider()); + BlueLibCommon.doRegistration(); + FabricNetworkManager.registerMessages(); + FabricNetworkManager.registerServerHandlers(); + AttributeHelper.registerAttributes(FabricDefaultAttributeRegistry::register); + RenderHelper.registerRenderers(EntityRendererRegistry::register, BlockEntityRenderers::register); + registerModEventListeners(); + if (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT) { + ClientTickEvents.END_CLIENT_TICK.register(client -> { + if (!hasInitialized) { + hasInitialized = true; + BlueLibCommon.init(); + } + }); + } + }*/ } diff --git a/fabric/src/main/java/software/bluelib/api/registry/FabricRecipeGenerator.java b/fabric/src/main/java/software/bluelib/api/registry/FabricRecipeGenerator.java new file mode 100644 index 00000000..95a6f5ab --- /dev/null +++ b/fabric/src/main/java/software/bluelib/api/registry/FabricRecipeGenerator.java @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2024 BlueLib Contributors + * + * This Source Code Form is subject to the terms of the MIT License. + * If a copy of the MIT License was not distributed with this file, + * You can obtain one at https://opensource.org/licenses/MIT. + */ +package software.bluelib.api.registry; + +import static software.bluelib.api.registry.datagen.recipe.RecipeUtils.*; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; +import java.util.function.Supplier; +import net.minecraft.advancements.Advancement; +import net.minecraft.advancements.AdvancementHolder; +import net.minecraft.data.recipes.RecipeOutput; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.crafting.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class FabricRecipeGenerator { + + public JsonElement generateRecipeJson(BiConsumer> recipeConsumer) { + final JsonElement[] capturedJson = new JsonElement[1]; + + RecipeOutput tempOutput = new RecipeOutput() { + + @Override + public void accept(ResourceLocation pPath, Recipe pRecipe, @Nullable AdvancementHolder pAdvancement) { + switch (pRecipe) { + case ShapedRecipe shaped -> { + JsonObject json = new JsonObject(); + json.addProperty("type", "minecraft:crafting_shaped"); + json.addProperty("category", "misc"); + List patternList = null; + Map keyMap; + JsonArray pattern = new JsonArray(); + JsonObject key = new JsonObject(); + + int width = shaped.getWidth(); + int height = shaped.getHeight(); + char symbol = 'A'; + char[][] symbols = new char[height][width]; + Map ingredientSymbols = new HashMap<>(); + for (int i = 0; i < height; i++) { + StringBuilder row = new StringBuilder(); + for (int j = 0; j < width; j++) { + Ingredient ingredient = shaped.getIngredients().get(i * width + j); + if (ingredient.isEmpty()) { + row.append(' '); + symbols[i][j] = ' '; + } else { + ingredientSymbols.putIfAbsent(ingredient, symbol); + char c = ingredientSymbols.get(ingredient); + row.append(c); + symbols[i][j] = c; + symbol++; + } + } + pattern.add(row.toString()); + } + for (var entry : ingredientSymbols.entrySet()) + key.add(String.valueOf(entry.getValue()), serializeIngredient(entry.getKey())); + + json.add("pattern", pattern); + json.add("key", key); + json.add("result", serializeResult(shaped.getResultItem(null))); + capturedJson[0] = json; + } + case ShapelessRecipe shapeless -> { + JsonObject json = new JsonObject(); + json.addProperty("type", "minecraft:crafting_shapeless"); + json.addProperty("category", "misc"); + JsonArray ingredients = new JsonArray(); + for (Ingredient ing : shapeless.getIngredients()) { + ingredients.add(serializeIngredient(ing)); + } + json.add("ingredients", ingredients); + json.add("result", serializeResult(shapeless.getResultItem(null))); + capturedJson[0] = json; + } + case AbstractCookingRecipe cooking -> { + JsonObject json = new JsonObject(); + String type; + if (cooking.getSerializer().equals(RecipeSerializer.SMELTING_RECIPE)) { + type = "minecraft:smelting"; + } else if (cooking.getSerializer().equals(RecipeSerializer.BLASTING_RECIPE)) { + type = "minecraft:blasting"; + } else if (cooking.getSerializer().equals(RecipeSerializer.SMOKING_RECIPE)) { + type = "minecraft:smoking"; + } else if (cooking.getSerializer().equals(RecipeSerializer.CAMPFIRE_COOKING_RECIPE)) { + type = "minecraft:campfire_cooking"; + } else { + type = cooking.getSerializer().toString(); + } + json.addProperty("type", type); + json.addProperty("category", "misc"); + JsonArray ingredients = new JsonArray(); + ingredients.add(serializeIngredient(cooking.getIngredients().get(0))); + json.add("ingredient", ingredients.get(0)); + json.add("result", serializeResult(cooking.getResultItem(null))); + json.addProperty("experience", cooking.getExperience()); + json.addProperty("cookingtime", cooking.getCookingTime()); + capturedJson[0] = json; + } + case SmithingTransformRecipe smithingTransform -> { + JsonObject json = new JsonObject(); + json.addProperty("type", "minecraft:smithing_transform"); + json.add("template", serializeIngredient(smithingTransform.template)); + json.add("base", serializeIngredient(smithingTransform.base)); + json.add("addition", serializeIngredient(smithingTransform.addition)); + json.add("result", serializeResult(smithingTransform.getResultItem(null))); + capturedJson[0] = json; + } + case SmithingTrimRecipe smithingTrim -> { + JsonObject json = new JsonObject(); + json.addProperty("type", "minecraft:smithing_trim"); + json.add("template", serializeIngredient(smithingTrim.template)); + json.add("base", serializeIngredient(smithingTrim.base)); + json.add("addition", serializeIngredient(smithingTrim.addition)); + json.add("result", serializeResult(getSmithingRecipeResult(smithingTrim))); + capturedJson[0] = json; + } + default -> { + JsonObject json = new JsonObject(); + json.addProperty("type", pRecipe.getSerializer().toString()); + capturedJson[0] = json; + } + } + } + + @Override + public Advancement.@NotNull Builder advancement() { + return Advancement.Builder.advancement(); + } + }; + + recipeConsumer.accept(tempOutput, () -> capturedJson[0]); + return capturedJson[0] != null ? capturedJson[0] : new JsonObject(); + } +} diff --git a/fabric/src/main/java/software/bluelib/platform/FabricPlatformHelper.java b/fabric/src/main/java/software/bluelib/platform/FabricPlatformHelper.java index 2db87759..d342a2a2 100644 --- a/fabric/src/main/java/software/bluelib/platform/FabricPlatformHelper.java +++ b/fabric/src/main/java/software/bluelib/platform/FabricPlatformHelper.java @@ -7,14 +7,19 @@ */ package software.bluelib.platform; +import com.google.gson.JsonElement; +import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.Set; +import java.util.function.BiConsumer; +import java.util.function.Supplier; import java.util.stream.Collectors; import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.ModContainer; import net.minecraft.client.Minecraft; +import net.minecraft.data.recipes.RecipeOutput; import net.minecraft.server.MinecraftServer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -22,6 +27,7 @@ import software.bluelib.api.Environment; import software.bluelib.api.ModAPI; import software.bluelib.api.event.mod.ModMeta; +import software.bluelib.api.registry.FabricRecipeGenerator; public class FabricPlatformHelper implements IPlatformHelper { @@ -78,4 +84,25 @@ public boolean isDevelopmentEnvironment() { public @Nullable MinecraftServer getServer() { return this.getEnvironment() == Environment.CLIENT ? Minecraft.getInstance().getSingleplayerServer() : BlueLibConstants.server; } + + @Override + public JsonElement generateRecipeJson(String modId, String name, BiConsumer> recipeConsumer) { + return new FabricRecipeGenerator().generateRecipeJson(recipeConsumer); + } + + @Override + public Path getAssetsDir(boolean isCommon, String pModID) { + if (isCommon) { + return FabricLoader.getInstance().getGameDir().getParent().getParent().resolve("common/src/main/resources/assets/" + pModID); + } + return FabricLoader.getInstance().getGameDir().getParent().getParent().resolve("src/main/resources/assets/" + pModID); + } + + @Override + public Path getDataDir(boolean isCommon, String pModID) { + if (isCommon) { + return FabricLoader.getInstance().getGameDir().getParent().getParent().resolve("common/src/main/resources/data/" + pModID); + } + return FabricLoader.getInstance().getGameDir().getParent().getParent().resolve("src/main/resources/data/" + pModID); + } } diff --git a/fabric/src/main/java/software/bluelib/platform/FabricRegistryHelper.java b/fabric/src/main/java/software/bluelib/platform/FabricRegistryHelper.java index 0eab5d17..69f6da27 100644 --- a/fabric/src/main/java/software/bluelib/platform/FabricRegistryHelper.java +++ b/fabric/src/main/java/software/bluelib/platform/FabricRegistryHelper.java @@ -8,12 +8,23 @@ package software.bluelib.platform; import java.util.function.Supplier; +import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; +import net.minecraft.client.KeyMapping; import net.minecraft.core.Holder; import net.minecraft.core.Registry; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.CreativeModeTab; +import net.minecraft.world.item.Item; import net.minecraft.world.item.crafting.RecipeSerializer; import net.minecraft.world.item.crafting.RecipeType; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityType; import org.jetbrains.annotations.NotNull; import software.bluelib.BlueLibConstants; import software.bluelib.api.net.NetworkManager; @@ -22,6 +33,48 @@ @SuppressWarnings({ "unchecked", "unused" }) public class FabricRegistryHelper implements IRegistryHelper { + @Override + public Supplier> registerEntity(String pId, Supplier> pEntity) { + return registerSupplier(BuiltInRegistries.ENTITY_TYPE, pId, pEntity); + } + + @Override + public Supplier registerTab(String pId, Supplier pTab) { + return registerSupplier(BuiltInRegistries.CREATIVE_MODE_TAB, pId, pTab); + } + + @Override + public Supplier registerItem(String id, Supplier item) { + return registerSupplier(BuiltInRegistries.ITEM, id, item); + } + + @Override + public Supplier registerBlock(String pId, Supplier pBlock) { + return registerSupplier(BuiltInRegistries.BLOCK, pId, pBlock); + } + + @Override + public Supplier> registerBlockEntity(String pId, Supplier> pBlockEntity) { + return registerSupplier(BuiltInRegistries.BLOCK_ENTITY_TYPE, pId, pBlockEntity); + } + + @Override + public > Supplier registerMenu(String pId, Supplier pMenu) { + return registerSupplier(BuiltInRegistries.MENU, pId, pMenu); + } + + @Override + public Supplier registerBiome(String pId, Supplier pMenu) { + return pMenu/*registerSupplier(BuiltInRegistries.BIOME, pId, pMenu)*/; + } + + @Override + public Supplier registerKeybind(String pId, Supplier pKeybind) { + KeyMapping keyMapping = pKeybind.get(); + KeyBindingHelper.registerKeyBinding(keyMapping); + return () -> keyMapping; + } + @Override public @NotNull NetworkManager getNetwork() { return new FabricNetworkManager(); diff --git a/fabric/src/main/resources/bluelib.accesswidener b/fabric/src/main/resources/bluelib.accesswidener index 2d986474..5f9a8e63 100644 --- a/fabric/src/main/resources/bluelib.accesswidener +++ b/fabric/src/main/resources/bluelib.accesswidener @@ -1,5 +1,23 @@ accessWidener v2 named +transitive-accessible class net/minecraft/world/level/block/entity/BlockEntityType$BlockEntitySupplier +transitive-accessible method net/minecraft/core/registries/BuiltInRegistries registerSimple (Lnet/minecraft/resources/ResourceKey;Lnet/minecraft/core/registries/BuiltInRegistries$RegistryBootstrap;)Lnet/minecraft/core/Registry; +transitive-accessible class net/minecraft/core/registries/BuiltInRegistries$RegistryBootstrap +transitive-accessible class net/minecraft/world/inventory/MenuType$MenuSupplier +transitive-accessible class net/minecraft/world/inventory/MenuType +transitive-accessible class net/minecraft/client/gui/screens/MenuScreens$ScreenConstructor +transitive-accessible method net/minecraft/world/inventory/MenuType (Lnet/minecraft/world/inventory/MenuType$MenuSupplier;Lnet/minecraft/world/flag/FeatureFlagSet;)V +transitive-accessible class net/minecraft/world/item/CreativeModeTab$TabVisibility +transitive-accessible method net/minecraft/data/recipes/RecipeProvider has (Lnet/minecraft/advancements/critereon/MinMaxBounds$Ints;Lnet/minecraft/world/level/ItemLike;)Lnet/minecraft/advancements/Criterion; +transitive-accessible method net/minecraft/data/recipes/RecipeProvider has (Lnet/minecraft/world/level/ItemLike;)Lnet/minecraft/advancements/Criterion; transitive-accessible class net/minecraft/world/inventory/BrewingStandMenu$IngredientsSlot transitive-accessible class net/minecraft/world/inventory/BrewingStandMenu$FuelSlot -transitive-accessible class net/minecraft/world/inventory/BrewingStandMenu$PotionSlot \ No newline at end of file +transitive-accessible class net/minecraft/world/inventory/BrewingStandMenu$PotionSlot + +transitive-accessible field net/minecraft/world/item/crafting/SmithingTransformRecipe template Lnet/minecraft/world/item/crafting/Ingredient; +transitive-accessible field net/minecraft/world/item/crafting/SmithingTransformRecipe base Lnet/minecraft/world/item/crafting/Ingredient; +transitive-accessible field net/minecraft/world/item/crafting/SmithingTransformRecipe addition Lnet/minecraft/world/item/crafting/Ingredient; + +transitive-accessible field net/minecraft/world/item/crafting/SmithingTrimRecipe template Lnet/minecraft/world/item/crafting/Ingredient; +transitive-accessible field net/minecraft/world/item/crafting/SmithingTrimRecipe base Lnet/minecraft/world/item/crafting/Ingredient; +transitive-accessible field net/minecraft/world/item/crafting/SmithingTrimRecipe addition Lnet/minecraft/world/item/crafting/Ingredient; \ No newline at end of file diff --git a/neoforge/src/main/java/software/bluelib/BlueLib.java b/neoforge/src/main/java/software/bluelib/BlueLib.java index 63763c62..cff3ee40 100644 --- a/neoforge/src/main/java/software/bluelib/BlueLib.java +++ b/neoforge/src/main/java/software/bluelib/BlueLib.java @@ -28,9 +28,9 @@ public class BlueLib { public BlueLib(@NotNull IEventBus pModEventBus, @NotNull ModContainer pModContainer) { - NeoForgeRegistryHelper.register(pModEventBus); - BlueLibCommon.doRegistration(); + NeoRegistries.register(pModEventBus); + NeoForgeRegistryHelper.register(pModEventBus); if (FMLEnvironment.dist == Dist.CLIENT) BlueLibClient.init(pModContainer); diff --git a/neoforge/src/main/java/software/bluelib/NeoRegistries.java b/neoforge/src/main/java/software/bluelib/NeoRegistries.java new file mode 100644 index 00000000..4a98cff8 --- /dev/null +++ b/neoforge/src/main/java/software/bluelib/NeoRegistries.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2024 BlueLib Contributors + * + * This Source Code Form is subject to the terms of the MIT License. + * If a copy of the MIT License was not distributed with this file, + * You can obtain one at https://opensource.org/licenses/MIT. + */ +package software.bluelib; + +import net.minecraft.core.registries.Registries; +import net.minecraft.world.item.crafting.RecipeSerializer; +import net.minecraft.world.item.crafting.RecipeType; +import net.neoforged.bus.api.IEventBus; +import net.neoforged.neoforge.registries.DeferredRegister; +import org.jetbrains.annotations.ApiStatus; + +@ApiStatus.Internal +public class NeoRegistries { + + public static final DeferredRegister> RECIPE_TYPES = DeferredRegister.create(Registries.RECIPE_TYPE, BlueLibConstants.MOD_ID); + + public static final DeferredRegister> RECIPE_SERIALIZERS = DeferredRegister.create(Registries.RECIPE_SERIALIZER, BlueLibConstants.MOD_ID); + + public static void register(IEventBus pModEventBus) { + RECIPE_TYPES.register(pModEventBus); + RECIPE_SERIALIZERS.register(pModEventBus); + } +} diff --git a/neoforge/src/main/java/software/bluelib/api/registry/NeoRecipeGenerator.java b/neoforge/src/main/java/software/bluelib/api/registry/NeoRecipeGenerator.java new file mode 100644 index 00000000..7b54a28a --- /dev/null +++ b/neoforge/src/main/java/software/bluelib/api/registry/NeoRecipeGenerator.java @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2024 BlueLib Contributors + * + * This Source Code Form is subject to the terms of the MIT License. + * If a copy of the MIT License was not distributed with this file, + * You can obtain one at https://opensource.org/licenses/MIT. + */ +package software.bluelib.api.registry; + +import static software.bluelib.api.registry.datagen.recipe.RecipeUtils.*; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import java.util.HashMap; +import java.util.Map; +import java.util.function.BiConsumer; +import java.util.function.Supplier; +import net.minecraft.advancements.Advancement; +import net.minecraft.advancements.AdvancementHolder; +import net.minecraft.data.recipes.RecipeOutput; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.crafting.*; +import net.minecraft.world.item.crafting.Recipe; +import net.neoforged.neoforge.common.conditions.ICondition; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class NeoRecipeGenerator { + + public JsonElement generateRecipeJson(BiConsumer> recipeConsumer) { + final JsonElement[] capturedJson = new JsonElement[1]; + + RecipeOutput tempOutput = new RecipeOutput() { + + @Override + public void accept(@NotNull ResourceLocation pPath, @NotNull Recipe pRecipe, @Nullable AdvancementHolder pAdvancement, ICondition @NotNull... iConditions) { + switch (pRecipe) { + case ShapedRecipe shaped -> { + JsonObject json = new JsonObject(); + json.addProperty("type", "minecraft:crafting_shaped"); + json.addProperty("category", "misc"); + JsonArray pattern = new JsonArray(); + JsonObject key = new JsonObject(); + + int width = shaped.getWidth(); + int height = shaped.getHeight(); + char symbol = 'A'; + char[][] symbols = new char[height][width]; + Map ingredientSymbols = new HashMap<>(); + for (int i = 0; i < height; i++) { + StringBuilder row = new StringBuilder(); + for (int j = 0; j < width; j++) { + Ingredient ingredient = shaped.getIngredients().get(i * width + j); + if (ingredient.isEmpty()) { + row.append(' '); + symbols[i][j] = ' '; + } else { + ingredientSymbols.putIfAbsent(ingredient, symbol); + char c = ingredientSymbols.get(ingredient); + row.append(c); + symbols[i][j] = c; + symbol++; + } + } + pattern.add(row.toString()); + } + for (var entry : ingredientSymbols.entrySet()) + key.add(String.valueOf(entry.getValue()), serializeIngredient(entry.getKey())); + + json.add("pattern", pattern); + json.add("key", key); + json.add("result", serializeResult(shaped.getResultItem(null))); + capturedJson[0] = json; + } + case ShapelessRecipe shapeless -> { + JsonObject json = new JsonObject(); + json.addProperty("type", "minecraft:crafting_shapeless"); + json.addProperty("category", "misc"); + JsonArray ingredients = new JsonArray(); + for (Ingredient ing : shapeless.getIngredients()) { + ingredients.add(serializeIngredient(ing)); + } + json.add("ingredients", ingredients); + json.add("result", serializeResult(shapeless.getResultItem(null))); + capturedJson[0] = json; + } + case AbstractCookingRecipe cooking -> { + JsonObject json = new JsonObject(); + String type; + if (cooking.getSerializer().equals(RecipeSerializer.SMELTING_RECIPE)) { + type = "minecraft:smelting"; + } else if (cooking.getSerializer().equals(RecipeSerializer.BLASTING_RECIPE)) { + type = "minecraft:blasting"; + } else if (cooking.getSerializer().equals(RecipeSerializer.SMOKING_RECIPE)) { + type = "minecraft:smoking"; + } else if (cooking.getSerializer().equals(RecipeSerializer.CAMPFIRE_COOKING_RECIPE)) { + type = "minecraft:campfire_cooking"; + } else { + type = cooking.getSerializer().toString(); + } + json.addProperty("type", type); + json.addProperty("category", "misc"); + JsonArray ingredients = new JsonArray(); + ingredients.add(serializeIngredient(cooking.getIngredients().get(0))); + json.add("ingredient", ingredients.get(0)); + json.add("result", serializeResult(cooking.getResultItem(null))); + json.addProperty("experience", cooking.getExperience()); + json.addProperty("cookingtime", cooking.getCookingTime()); + capturedJson[0] = json; + } + case SmithingTransformRecipe smithingTransform -> { + JsonObject json = new JsonObject(); + json.addProperty("type", "minecraft:smithing_transform"); + json.add("template", serializeIngredient(smithingTransform.template)); + json.add("base", serializeIngredient(smithingTransform.base)); + json.add("addition", serializeIngredient(smithingTransform.addition)); + json.add("result", serializeResult(smithingTransform.getResultItem(null))); + capturedJson[0] = json; + } + //Figure out how to make this work. + /*case SmithingTrimRecipe smithingTrim -> { + JsonObject json = new JsonObject(); + json.addProperty("type", "minecraft:smithing_trim"); + json.add("template", serializeIngredient(smithingTrim.template)); + json.add("base", serializeIngredient(smithingTrim.base)); + json.add("addition", serializeIngredient(smithingTrim.addition)); + json.add("result", serializeResult(smithingTrim.getResultItem(null))); + capturedJson[0] = json; + }*/ + default -> { + JsonObject json = new JsonObject(); + json.addProperty("type", pRecipe.getSerializer().toString()); + capturedJson[0] = json; + } + } + } + + @Override + public Advancement.@NotNull Builder advancement() { + return Advancement.Builder.advancement(); + } + }; + + recipeConsumer.accept(tempOutput, () -> capturedJson[0]); + return capturedJson[0] != null ? capturedJson[0] : new JsonObject(); + } +} diff --git a/neoforge/src/main/java/software/bluelib/platform/NeoForgePlatformHelper.java b/neoforge/src/main/java/software/bluelib/platform/NeoForgePlatformHelper.java index e78e51b9..c23656d3 100644 --- a/neoforge/src/main/java/software/bluelib/platform/NeoForgePlatformHelper.java +++ b/neoforge/src/main/java/software/bluelib/platform/NeoForgePlatformHelper.java @@ -7,15 +7,22 @@ */ package software.bluelib.platform; +import com.google.gson.JsonElement; +import java.nio.file.Path; +import java.util.*; import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.Set; +import java.util.function.BiConsumer; +import java.util.function.Supplier; import java.util.stream.Collectors; +import net.minecraft.data.recipes.RecipeOutput; import net.minecraft.server.MinecraftServer; import net.neoforged.fml.ModList; import net.neoforged.fml.loading.FMLEnvironment; import net.neoforged.fml.loading.FMLLoader; +import net.neoforged.fml.loading.FMLPaths; import net.neoforged.neoforge.server.ServerLifecycleHooks; import net.neoforged.neoforgespi.language.IModInfo; import org.jetbrains.annotations.NotNull; @@ -23,6 +30,7 @@ import software.bluelib.api.Environment; import software.bluelib.api.ModAPI; import software.bluelib.api.event.mod.ModMeta; +import software.bluelib.api.registry.NeoRecipeGenerator; public class NeoForgePlatformHelper implements IPlatformHelper { @@ -76,4 +84,25 @@ public boolean isDevelopmentEnvironment() { public @Nullable MinecraftServer getServer() { return ServerLifecycleHooks.getCurrentServer(); } + + @Override + public JsonElement generateRecipeJson(String modId, String name, BiConsumer> recipeConsumer) { + return new NeoRecipeGenerator().generateRecipeJson(recipeConsumer); + } + + @Override + public Path getAssetsDir(boolean isCommon, String pModID) { + if (isCommon) { + return FMLPaths.GAMEDIR.get().getParent().getParent().resolve("common/src/main/resources/assets/" + pModID); + } + return FMLPaths.GAMEDIR.get().getParent().getParent().resolve("src/main/resources/assets/" + pModID); + } + + @Override + public Path getDataDir(boolean isCommon, String pModID) { + if (isCommon) { + return FMLPaths.GAMEDIR.get().getParent().getParent().resolve("common/src/main/resources/data/" + pModID); + } + return FMLPaths.GAMEDIR.get().getParent().getParent().resolve("src/main/resources/data/" + pModID); + } } diff --git a/neoforge/src/main/java/software/bluelib/platform/NeoForgeRegistryHelper.java b/neoforge/src/main/java/software/bluelib/platform/NeoForgeRegistryHelper.java index af653b82..8acf40f2 100644 --- a/neoforge/src/main/java/software/bluelib/platform/NeoForgeRegistryHelper.java +++ b/neoforge/src/main/java/software/bluelib/platform/NeoForgeRegistryHelper.java @@ -8,17 +8,35 @@ package software.bluelib.platform; import java.util.function.Supplier; +import net.minecraft.client.KeyMapping; import net.minecraft.core.registries.Registries; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.CreativeModeTab; +import net.minecraft.world.item.Item; import net.minecraft.world.item.crafting.RecipeSerializer; import net.minecraft.world.item.crafting.RecipeType; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityType; import net.neoforged.bus.api.IEventBus; import net.neoforged.neoforge.registries.DeferredRegister; import org.jetbrains.annotations.NotNull; import software.bluelib.BlueLibConstants; import software.bluelib.api.net.NetworkManager; +import software.bluelib.api.registry.AbstractRegistryBuilder; import software.bluelib.net.NeoForgeNetworkManager; public class NeoForgeRegistryHelper implements IRegistryHelper { + private final DeferredRegister itemRegistry = DeferredRegister.create(Registries.ITEM, BlueLibConstants.MOD_ID); + private final DeferredRegister blockRegistry = DeferredRegister.create(Registries.BLOCK, BlueLibConstants.MOD_ID); + private final DeferredRegister> entityRegistry = DeferredRegister.create(Registries.ENTITY_TYPE, BlueLibConstants.MOD_ID); + private final DeferredRegister tabRegistry = DeferredRegister.create(Registries.CREATIVE_MODE_TAB, BlueLibConstants.MOD_ID); + private final DeferredRegister> menuRegistry = DeferredRegister.create(Registries.MENU, BlueLibConstants.MOD_ID); + private final DeferredRegister> blockEntityRegistry = DeferredRegister.create(Registries.BLOCK_ENTITY_TYPE, BlueLibConstants.MOD_ID); + private final DeferredRegister biomeRegistry = DeferredRegister.create(Registries.BIOME, BlueLibConstants.MOD_ID); @NotNull public static final DeferredRegister> RECIPE_TYPES = DeferredRegister.create(Registries.RECIPE_TYPE, BlueLibConstants.MOD_ID); @@ -40,6 +58,47 @@ public class NeoForgeRegistryHelper implements IRegistryHelper { return RECIPE_SERIALIZERS.register(pId, pRecipeSerializer); } + @Override + public Supplier registerTab(String pId, Supplier pTab) { + return tabRegistry.register(pId, pTab); + } + + @Override + public Supplier> registerEntity(String pId, Supplier> pEntity) { + return entityRegistry.register(pId, pEntity); + } + + @Override + public Supplier registerItem(String id, Supplier item) { + return itemRegistry.register(id, item); + } + + @Override + public Supplier registerBlock(String id, Supplier block) { + return blockRegistry.register(id, block); + } + + @Override + public Supplier> registerBlockEntity(String pId, Supplier> pBlockEntity) { + return blockEntityRegistry.register(pId, pBlockEntity); + } + + @Override + public > Supplier registerMenu(String pId, Supplier pMenu) { + menuRegistry.register(pId, pMenu); + return pMenu; + } + + @Override + public Supplier registerBiome(String id, Supplier pBiome) { + return biomeRegistry.register(id, pBiome); + } + + @Override + public Supplier registerKeybind(String pId, Supplier pKeybind) { + return pKeybind; + } + public static void register(@NotNull IEventBus pModEventBus) { RECIPE_TYPES.register(pModEventBus); RECIPE_SERIALIZERS.register(pModEventBus);