diff --git a/changelog.md b/changelog.md index 7c56455e..898fb6ad 100644 --- a/changelog.md +++ b/changelog.md @@ -1,10 +1,12 @@ ## Ex Deorum 1.21 +- Added Crook recipes. It is now possible to add drops to the crook, for example, you could make it so that using a Crook on tall grass would have a 1% chance of dropping a diamond. +- Added Crucible Heat Source recipes. Instead of using KubeJS, crucible heat sources can now be added with datapacks. Old KubeJS scripts will still work fine. - Added Jade compatibility -- Added partial compatibility with Roughly Enough Items to hide compat blocks for mods that aren't installed (use REI Plugin Compatibilities mod to gain full compatibility with Ex Deorum) +- Added Roughly Enough Items compatibility to hide compat blocks for mods that aren't installed (use REI Plugin Compatibilities mod to gain full compatibility with Ex Deorum) - Added configurable sifting interval to limit speed of sifting by hand in order to curb the speed of autoclickers on sieves. (PR #47 by CPearl0) - Added Chinese translation (PR #45 by CPearl0) -- Infested leaves string drop rate can now be configured in the server config. - Fixed dedicated server crash when using Mycelium Spores to convert a cow into a Mooshroom (PR #44 by CPearl0) +- Fixed incorrect block lighting in JEI displays for Crucible heat sources and Crook recipes ## Ex Deorum 1.20 - Added Mechanical Hammer, a machine that uses FE to hammer blocks automatically. Uses 20 FE a tick by default and takes 200 ticks (10 seconds) to hammer an item with a hammer that has no efficiency. diff --git a/src/generated/resources/.cache/59eb3dbb5f86130e09b3c62d89b9525ee01cf52d b/src/generated/resources/.cache/59eb3dbb5f86130e09b3c62d89b9525ee01cf52d index 322f7b72..8421ea36 100644 --- a/src/generated/resources/.cache/59eb3dbb5f86130e09b3c62d89b9525ee01cf52d +++ b/src/generated/resources/.cache/59eb3dbb5f86130e09b3c62d89b9525ee01cf52d @@ -1,4 +1,4 @@ -// 1.20.1 2024-02-01T18:59:58.2832542 Loot Tables +// 1.20.1 2024-02-09T12:37:53.8355693 Loot Tables 105d8a61ea7145d7798146d385d4aad24fd1588d data/exdeorum/loot_tables/blocks/acacia_barrel.json 1e77127a82cbba0937bb02694f65cf1893aeffcb data/exdeorum/loot_tables/blocks/acacia_crucible.json fcc00910a8cc94bed6339d6833fcec53c501a0d7 data/exdeorum/loot_tables/blocks/acacia_sieve.json @@ -51,7 +51,6 @@ b3b8c57f4614b036263f1e107bb1e0acf4a69122 data/exdeorum/loot_tables/blocks/green_ 32602e363024f1bbd0a174a19c61b4cb0e4f3b2c data/exdeorum/loot_tables/blocks/hellbark_barrel.json cbf36cffd7f38d618435ca03142ded5e74f217aa data/exdeorum/loot_tables/blocks/hellbark_crucible.json bf41c8c33133269ddde2c6b098aca056acd7e4ea data/exdeorum/loot_tables/blocks/hellbark_sieve.json -0834fe6d98295ca2ced32a3a50a04de3ae6204e9 data/exdeorum/loot_tables/blocks/infested_leaves.json ce4254c53a4ce6c2712775708415f14e63f21b67 data/exdeorum/loot_tables/blocks/jacaranda_barrel.json b23b0494ef9f535744f7390f74a35527442aee6f data/exdeorum/loot_tables/blocks/jacaranda_crucible.json 1817eb94fb9656a3ad853f2cca138c1cd012bd26 data/exdeorum/loot_tables/blocks/jacaranda_sieve.json diff --git a/src/generated/resources/.cache/93943142017732f21fbc4fa325d116c728b69767 b/src/generated/resources/.cache/93943142017732f21fbc4fa325d116c728b69767 index 8ff8df69..0869c358 100644 --- a/src/generated/resources/.cache/93943142017732f21fbc4fa325d116c728b69767 +++ b/src/generated/resources/.cache/93943142017732f21fbc4fa325d116c728b69767 @@ -1,2 +1,2 @@ -// 1.20.1 2024-02-07T15:07:17.5326478 ModKit Language: en_us for mod 'exdeorum' -313d90779f26fbca4597861071060dba9e94411a assets/exdeorum/lang/en_us.json +// 1.20.1 2024-02-09T20:14:05.8728621 ModKit Language: en_us for mod 'exdeorum' +43e0f1f023ef626ab7571ad14c1ecdf5cd594823 assets/exdeorum/lang/en_us.json diff --git a/src/generated/resources/.cache/9fb1092f32d4fcbf9e061ffd718d4ec689c6c95e b/src/generated/resources/.cache/9fb1092f32d4fcbf9e061ffd718d4ec689c6c95e index 52488d5a..29042778 100644 --- a/src/generated/resources/.cache/9fb1092f32d4fcbf9e061ffd718d4ec689c6c95e +++ b/src/generated/resources/.cache/9fb1092f32d4fcbf9e061ffd718d4ec689c6c95e @@ -1,4 +1,4 @@ -// 1.20.1 2024-02-07T21:04:33.4555528 Recipes +// 1.20.1 2024-02-09T20:14:05.875864 Recipes e37b64428f17e304e91539ac0513456d7ce40cd1 data/exdeorum/advancements/recipes/building_blocks/sponge.json 5ad481a0c376c1a1785a5d3b992064d0ec0bf3b0 data/exdeorum/advancements/recipes/food/end_cake.json 25dd027e844a72b03c95dbe5e3c3dd8c738ceb00 data/exdeorum/advancements/recipes/misc/acacia_barrel.json @@ -207,6 +207,19 @@ ba90fbda213e52dc15ea532c8f10c63ea47747de data/exdeorum/recipes/cobblestone.json f6a9036c1bbc4fa0cdc25ef4c851de5ddac0a4ff data/exdeorum/recipes/crook.json ae7e544d3e51182204eeb1efe15079103ea09802 data/exdeorum/recipes/crook/silkworm.json b8636418dc9384bd44313de1d17fd4bbc06c0152 data/exdeorum/recipes/crook/silkworm_bonus.json +00b082310773af07d2d418d34c25f5772d0e4a20 data/exdeorum/recipes/crook/string_roll_1.json +47dce4f15c91271429cfab63d22ce4d33ff3fa99 data/exdeorum/recipes/crook/string_roll_2.json +96f8d146949641588a2cc309e031ac19ed80e041 data/exdeorum/recipes/crucible_heat_source/fire.json +e70d081bb3c73fad2e7925b9a6e606f6e4c4a418 data/exdeorum/recipes/crucible_heat_source/lantern.json +43a4cc20f18334fc43f35879bc9af381254c5671 data/exdeorum/recipes/crucible_heat_source/lava.json +fac62befa53f1a335136c50d24808df52b8a050b data/exdeorum/recipes/crucible_heat_source/lit_campfire.json +01c9ed5d8be13335868f9b5ac500425b277c3745 data/exdeorum/recipes/crucible_heat_source/lit_soul_campfire.json +77eb7e88dbfedd36935e66b171163cfe642f981b data/exdeorum/recipes/crucible_heat_source/soul_fire.json +09735fcbbd7aa4379d87bda8e1a3c776e057d8fc data/exdeorum/recipes/crucible_heat_source/soul_lantern.json +c633abee6307560c55a4de010a1a827ef4870b70 data/exdeorum/recipes/crucible_heat_source/soul_torch.json +46a18c4e353ef57c4cedb047dc2c66a84338fcf0 data/exdeorum/recipes/crucible_heat_source/soul_wall_torch.json +d6996ab8fbd7d4b49a497947c8bab2652d2e665f data/exdeorum/recipes/crucible_heat_source/torch.json +85e99f299ad22301b07dc770a981b6d69c6d307a data/exdeorum/recipes/crucible_heat_source/wall_torch.json c81379fe022f3667ce506e4927aac97d1b66999f data/exdeorum/recipes/crystallized_barrel.json 77cf611f4e10c507fc4ec116ba0a9d9d1ce58273 data/exdeorum/recipes/crystallized_crucible.json 324b6222287940a7fb008b9c6a1e9caecada8af3 data/exdeorum/recipes/crystallized_sieve.json diff --git a/src/generated/resources/assets/exdeorum/lang/en_us.json b/src/generated/resources/assets/exdeorum/lang/en_us.json index 1837f0cf..c5186d50 100644 --- a/src/generated/resources/assets/exdeorum/lang/en_us.json +++ b/src/generated/resources/assets/exdeorum/lang/en_us.json @@ -63,6 +63,7 @@ "block.exdeorum.hellbark_crucible": "Hellbark Crucible", "block.exdeorum.hellbark_sieve": "Hellbark Sieve", "block.exdeorum.infested_leaves": "Infested Leaves", + "block.exdeorum.infested_leaves.fully_infested": "Fully Infested", "block.exdeorum.jacaranda_barrel": "Jacaranda Barrel", "block.exdeorum.jacaranda_crucible": "Jacaranda Crucible", "block.exdeorum.jacaranda_sieve": "Jacaranda Sieve", @@ -132,6 +133,8 @@ "gui.exdeorum.category.barrel_fluid_mixing": "Barrel Fluid Mixing", "gui.exdeorum.category.barrel_fluid_mixing.contents_are_consumed": "Contents are consumed", "gui.exdeorum.category.barrel_mixing": "Barrel Mixing", + "gui.exdeorum.category.crook": "Crook", + "gui.exdeorum.category.crook.requires_state": "Requires properties:", "gui.exdeorum.category.crucible_heat_source": "Crucible Heat Sources", "gui.exdeorum.category.crucible_heat_source.multiplier": "Melt Rate: %sx", "gui.exdeorum.category.hammer": "Hammer", diff --git a/src/generated/resources/data/exdeorum/loot_tables/blocks/infested_leaves.json b/src/generated/resources/data/exdeorum/loot_tables/blocks/infested_leaves.json deleted file mode 100644 index 5af69b46..00000000 --- a/src/generated/resources/data/exdeorum/loot_tables/blocks/infested_leaves.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "type": "minecraft:block", - "pools": [ - { - "bonus_rolls": 0.0, - "entries": [ - { - "type": "minecraft:item", - "functions": [ - { - "function": "exdeorum:infested_string" - } - ], - "name": "minecraft:string" - } - ], - "rolls": 1.0 - } - ], - "random_sequence": "exdeorum:blocks/infested_leaves" -} \ No newline at end of file diff --git a/src/generated/resources/data/exdeorum/recipes/crook/string_roll_1.json b/src/generated/resources/data/exdeorum/recipes/crook/string_roll_1.json new file mode 100644 index 00000000..74570c45 --- /dev/null +++ b/src/generated/resources/data/exdeorum/recipes/crook/string_roll_1.json @@ -0,0 +1,11 @@ +{ + "type": "exdeorum:crook", + "block_predicate": { + "block": "exdeorum:infested_leaves", + "state": { + "fully_infested": "true" + } + }, + "chance": 0.4, + "result": "minecraft:string" +} \ No newline at end of file diff --git a/src/generated/resources/data/exdeorum/recipes/crook/string_roll_2.json b/src/generated/resources/data/exdeorum/recipes/crook/string_roll_2.json new file mode 100644 index 00000000..44891c32 --- /dev/null +++ b/src/generated/resources/data/exdeorum/recipes/crook/string_roll_2.json @@ -0,0 +1,11 @@ +{ + "type": "exdeorum:crook", + "block_predicate": { + "block": "exdeorum:infested_leaves", + "state": { + "fully_infested": "true" + } + }, + "chance": 0.1, + "result": "minecraft:string" +} \ No newline at end of file diff --git a/src/generated/resources/data/exdeorum/recipes/crucible_heat_source/fire.json b/src/generated/resources/data/exdeorum/recipes/crucible_heat_source/fire.json new file mode 100644 index 00000000..b0628cb9 --- /dev/null +++ b/src/generated/resources/data/exdeorum/recipes/crucible_heat_source/fire.json @@ -0,0 +1,7 @@ +{ + "type": "exdeorum:crucible_heat_source", + "block_predicate": { + "block": "minecraft:fire" + }, + "heat_value": 5 +} \ No newline at end of file diff --git a/src/generated/resources/data/exdeorum/recipes/crucible_heat_source/lantern.json b/src/generated/resources/data/exdeorum/recipes/crucible_heat_source/lantern.json new file mode 100644 index 00000000..b31822dd --- /dev/null +++ b/src/generated/resources/data/exdeorum/recipes/crucible_heat_source/lantern.json @@ -0,0 +1,7 @@ +{ + "type": "exdeorum:crucible_heat_source", + "block_predicate": { + "block": "minecraft:lantern" + }, + "heat_value": 1 +} \ No newline at end of file diff --git a/src/generated/resources/data/exdeorum/recipes/crucible_heat_source/lava.json b/src/generated/resources/data/exdeorum/recipes/crucible_heat_source/lava.json new file mode 100644 index 00000000..0c119bd7 --- /dev/null +++ b/src/generated/resources/data/exdeorum/recipes/crucible_heat_source/lava.json @@ -0,0 +1,7 @@ +{ + "type": "exdeorum:crucible_heat_source", + "block_predicate": { + "block": "minecraft:lava" + }, + "heat_value": 3 +} \ No newline at end of file diff --git a/src/generated/resources/data/exdeorum/recipes/crucible_heat_source/lit_campfire.json b/src/generated/resources/data/exdeorum/recipes/crucible_heat_source/lit_campfire.json new file mode 100644 index 00000000..f8fd6133 --- /dev/null +++ b/src/generated/resources/data/exdeorum/recipes/crucible_heat_source/lit_campfire.json @@ -0,0 +1,10 @@ +{ + "type": "exdeorum:crucible_heat_source", + "block_predicate": { + "block": "minecraft:campfire", + "state": { + "lit": "true" + } + }, + "heat_value": 2 +} \ No newline at end of file diff --git a/src/generated/resources/data/exdeorum/recipes/crucible_heat_source/lit_soul_campfire.json b/src/generated/resources/data/exdeorum/recipes/crucible_heat_source/lit_soul_campfire.json new file mode 100644 index 00000000..5300d30f --- /dev/null +++ b/src/generated/resources/data/exdeorum/recipes/crucible_heat_source/lit_soul_campfire.json @@ -0,0 +1,10 @@ +{ + "type": "exdeorum:crucible_heat_source", + "block_predicate": { + "block": "minecraft:soul_campfire", + "state": { + "lit": "true" + } + }, + "heat_value": 2 +} \ No newline at end of file diff --git a/src/generated/resources/data/exdeorum/recipes/crucible_heat_source/soul_fire.json b/src/generated/resources/data/exdeorum/recipes/crucible_heat_source/soul_fire.json new file mode 100644 index 00000000..58f8c66b --- /dev/null +++ b/src/generated/resources/data/exdeorum/recipes/crucible_heat_source/soul_fire.json @@ -0,0 +1,7 @@ +{ + "type": "exdeorum:crucible_heat_source", + "block_predicate": { + "block": "minecraft:soul_fire" + }, + "heat_value": 5 +} \ No newline at end of file diff --git a/src/generated/resources/data/exdeorum/recipes/crucible_heat_source/soul_lantern.json b/src/generated/resources/data/exdeorum/recipes/crucible_heat_source/soul_lantern.json new file mode 100644 index 00000000..15c29b37 --- /dev/null +++ b/src/generated/resources/data/exdeorum/recipes/crucible_heat_source/soul_lantern.json @@ -0,0 +1,7 @@ +{ + "type": "exdeorum:crucible_heat_source", + "block_predicate": { + "block": "minecraft:soul_lantern" + }, + "heat_value": 2 +} \ No newline at end of file diff --git a/src/generated/resources/data/exdeorum/recipes/crucible_heat_source/soul_torch.json b/src/generated/resources/data/exdeorum/recipes/crucible_heat_source/soul_torch.json new file mode 100644 index 00000000..e03a4e32 --- /dev/null +++ b/src/generated/resources/data/exdeorum/recipes/crucible_heat_source/soul_torch.json @@ -0,0 +1,7 @@ +{ + "type": "exdeorum:crucible_heat_source", + "block_predicate": { + "block": "minecraft:soul_torch" + }, + "heat_value": 2 +} \ No newline at end of file diff --git a/src/generated/resources/data/exdeorum/recipes/crucible_heat_source/soul_wall_torch.json b/src/generated/resources/data/exdeorum/recipes/crucible_heat_source/soul_wall_torch.json new file mode 100644 index 00000000..fd216c6f --- /dev/null +++ b/src/generated/resources/data/exdeorum/recipes/crucible_heat_source/soul_wall_torch.json @@ -0,0 +1,7 @@ +{ + "type": "exdeorum:crucible_heat_source", + "block_predicate": { + "block": "minecraft:soul_wall_torch" + }, + "heat_value": 2 +} \ No newline at end of file diff --git a/src/generated/resources/data/exdeorum/recipes/crucible_heat_source/torch.json b/src/generated/resources/data/exdeorum/recipes/crucible_heat_source/torch.json new file mode 100644 index 00000000..ca2965ee --- /dev/null +++ b/src/generated/resources/data/exdeorum/recipes/crucible_heat_source/torch.json @@ -0,0 +1,7 @@ +{ + "type": "exdeorum:crucible_heat_source", + "block_predicate": { + "block": "minecraft:torch" + }, + "heat_value": 1 +} \ No newline at end of file diff --git a/src/generated/resources/data/exdeorum/recipes/crucible_heat_source/wall_torch.json b/src/generated/resources/data/exdeorum/recipes/crucible_heat_source/wall_torch.json new file mode 100644 index 00000000..16dfb681 --- /dev/null +++ b/src/generated/resources/data/exdeorum/recipes/crucible_heat_source/wall_torch.json @@ -0,0 +1,7 @@ +{ + "type": "exdeorum:crucible_heat_source", + "block_predicate": { + "block": "minecraft:wall_torch" + }, + "heat_value": 1 +} \ No newline at end of file diff --git a/src/main/java/thedarkcolour/exdeorum/blockentity/LavaCrucibleBlockEntity.java b/src/main/java/thedarkcolour/exdeorum/blockentity/LavaCrucibleBlockEntity.java index 2449ece1..cdcd06cb 100644 --- a/src/main/java/thedarkcolour/exdeorum/blockentity/LavaCrucibleBlockEntity.java +++ b/src/main/java/thedarkcolour/exdeorum/blockentity/LavaCrucibleBlockEntity.java @@ -18,84 +18,25 @@ package thedarkcolour.exdeorum.blockentity; -import it.unimi.dsi.fastutil.objects.Object2IntLinkedOpenHashMap; -import it.unimi.dsi.fastutil.objects.Object2IntMap; -import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import net.minecraft.core.BlockPos; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.CampfireBlock; import net.minecraft.world.level.block.state.BlockState; import thedarkcolour.exdeorum.recipe.RecipeUtil; import thedarkcolour.exdeorum.recipe.crucible.CrucibleRecipe; import thedarkcolour.exdeorum.registry.EBlockEntities; import javax.annotation.Nullable; -import java.util.function.Predicate; public class LavaCrucibleBlockEntity extends AbstractCrucibleBlockEntity { - public static final Object2IntMap HEAT_REGISTRY = new Object2IntOpenHashMap<>(); - public static final Object2IntMap KUBEJS_HEAT_VALUES = new Object2IntLinkedOpenHashMap<>(); - - static { - putDefaultHeatValues(); - } - - public static void putDefaultHeatValues() { - HEAT_REGISTRY.clear(); - - putDefaults(HEAT_REGISTRY); - - for (var entry : KUBEJS_HEAT_VALUES.object2IntEntrySet()) { - if (entry.getIntValue() <= 0) { - HEAT_REGISTRY.removeInt(entry.getKey()); - } else { - HEAT_REGISTRY.put(entry.getKey(), entry.getIntValue()); - } - } - - KUBEJS_HEAT_VALUES.clear(); - } - - public static void putDefaults(Object2IntMap heatMap) { - putAllStates(Blocks.TORCH, 1, heatMap); - putAllStates(Blocks.WALL_TORCH, 1, heatMap); - putAllStates(Blocks.LANTERN, 1, heatMap); - putAllStates(Blocks.SOUL_TORCH, 2, heatMap); - putAllStates(Blocks.SOUL_WALL_TORCH, 2, heatMap); - putAllStates(Blocks.SOUL_LANTERN, 2, heatMap); - putAllStates(Blocks.LAVA, 3, heatMap); - putAllStates(Blocks.FIRE, 5, heatMap); - putAllStates(Blocks.SOUL_FIRE, 5, heatMap); - - putStates(Blocks.CAMPFIRE, 2, state -> state.getValue(CampfireBlock.LIT), heatMap); - putStates(Blocks.SOUL_CAMPFIRE, 2, state -> state.getValue(CampfireBlock.LIT), heatMap); - } - - public static void putAllStates(Block block, int heat, Object2IntMap heatMap) { - for (var state : block.getStateDefinition().getPossibleStates()) { - heatMap.put(state, heat); - } - } - - public static void putStates(Block block, int heat, Predicate predicate, Object2IntMap heatMap) { - for (var state : block.getStateDefinition().getPossibleStates()) { - if (predicate.test(state)) { - heatMap.put(state, heat); - } - } - } - public LavaCrucibleBlockEntity(BlockPos pos, BlockState state) { super(EBlockEntities.LAVA_CRUCIBLE.get(), pos, state); } @Override public int getMeltingRate() { - BlockState state = this.level.getBlockState(getBlockPos().below()); - - return HEAT_REGISTRY.getInt(state); + return RecipeUtil.getHeatValue(this.level.getBlockState(getBlockPos().below())); } @Override diff --git a/src/main/java/thedarkcolour/exdeorum/compat/ModIds.java b/src/main/java/thedarkcolour/exdeorum/compat/ModIds.java index 7b040515..e6f8bec6 100644 --- a/src/main/java/thedarkcolour/exdeorum/compat/ModIds.java +++ b/src/main/java/thedarkcolour/exdeorum/compat/ModIds.java @@ -43,4 +43,5 @@ public class ModIds { public static final String NUCLEARCRAFT_NEOTERIC = "nuclearcraft"; public static final String JEI = "jei"; public static final String INVENTORY_SORTER = "inventorysorter"; + public static final String REI_PC = "rei_plugin_compatibilities"; } diff --git a/src/main/java/thedarkcolour/exdeorum/compat/jade/InfestedLeavesComponentProvider.java b/src/main/java/thedarkcolour/exdeorum/compat/jade/InfestedLeavesComponentProvider.java index 360765ed..d12d4db9 100644 --- a/src/main/java/thedarkcolour/exdeorum/compat/jade/InfestedLeavesComponentProvider.java +++ b/src/main/java/thedarkcolour/exdeorum/compat/jade/InfestedLeavesComponentProvider.java @@ -18,6 +18,7 @@ package thedarkcolour.exdeorum.compat.jade; +import net.minecraft.ChatFormatting; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; import snownee.jade.api.BlockAccessor; @@ -25,6 +26,7 @@ import snownee.jade.api.IBlockComponentProvider; import snownee.jade.api.ITooltip; import snownee.jade.api.config.IPluginConfig; import thedarkcolour.exdeorum.blockentity.InfestedLeavesBlockEntity; +import thedarkcolour.exdeorum.data.TranslationKeys; enum InfestedLeavesComponentProvider implements IBlockComponentProvider { INSTANCE; @@ -33,7 +35,11 @@ enum InfestedLeavesComponentProvider implements IBlockComponentProvider { public void appendTooltip(ITooltip tooltip, BlockAccessor blockAccessor, IPluginConfig config) { if (blockAccessor.getBlockEntity() instanceof InfestedLeavesBlockEntity leaves) { int progress = (int) (leaves.getProgress() * 100.0f); - tooltip.add(Component.literal("Progress: ").append(Component.literal(progress + "%"))); + if (progress == 100) { + tooltip.add(Component.translatable(TranslationKeys.INFESTED_LEAVES_FULLY_INFESTED)); + } else { + tooltip.add(Component.literal("Progress: ").append(Component.literal(progress + "%"))); + } } } diff --git a/src/main/java/thedarkcolour/exdeorum/compat/jei/ClientJeiUtil.java b/src/main/java/thedarkcolour/exdeorum/compat/jei/ClientJeiUtil.java index 1a1109ad..fbdf6adb 100644 --- a/src/main/java/thedarkcolour/exdeorum/compat/jei/ClientJeiUtil.java +++ b/src/main/java/thedarkcolour/exdeorum/compat/jei/ClientJeiUtil.java @@ -24,13 +24,20 @@ import com.mojang.blaze3d.vertex.BufferBuilder; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.Tesselator; import com.mojang.math.Axis; +import me.shedaniel.rei.api.client.view.ViewSearchBuilder; +import me.shedaniel.rei.jeicompat.JEIPluginDetector; import mezz.jei.api.ingredients.IIngredientRenderer; +import mezz.jei.api.ingredients.IIngredientType; +import mezz.jei.api.ingredients.ITypedIngredient; +import mezz.jei.api.recipe.IFocusFactory; +import mezz.jei.api.recipe.RecipeIngredientRole; +import mezz.jei.api.runtime.IIngredientManager; +import mezz.jei.api.runtime.IRecipesGui; import net.minecraft.ChatFormatting; import net.minecraft.CrashReport; import net.minecraft.CrashReportCategory; import net.minecraft.ReportedException; import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.renderer.ItemBlockRenderTypes; import net.minecraft.client.renderer.MultiBufferSource; @@ -55,19 +62,27 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.lighting.LevelLightEngine; import net.minecraft.world.level.material.FluidState; import net.minecraft.world.level.material.Fluids; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fml.ModList; import org.jetbrains.annotations.Nullable; import org.joml.Matrix4f; +import org.joml.Vector3f; +import thedarkcolour.exdeorum.compat.ModIds; import java.util.ArrayList; import java.util.List; +import java.util.function.Consumer; class ClientJeiUtil { private static final FluidState EMPTY = Fluids.EMPTY.defaultFluidState(); private static final BlockState AIR = Blocks.AIR.defaultBlockState(); + // From https://github.com/The-Aether-Team/Nitrogen/blob/1.20.1-develop/src/main/java/com/aetherteam/nitrogen/integration/jei/BlockStateRenderer.java + private static final Vector3f L1 = new Vector3f(0.4F, 0.0F, 1.0F).normalize(); + private static final Vector3f L2 = new Vector3f(-0.4F, 1.0F, -0.2F).normalize(); + // https://github.com/way2muchnoise/JustEnoughResources/blob/89ee40ff068c8d6eb6ab103f76381445691cffc9/Common/src/main/java/jeresources/util/RenderHelper.java#L100 - static void renderBlock(GuiGraphics guiGraphics, BlockState block, float x, float y, float z, float scale) { - Minecraft mc = Minecraft.getInstance(); + static void renderBlock(GuiGraphics guiGraphics, BlockState block, float x, float y, float z, float scale, RenderBlockFn renderFunction) { PoseStack poseStack = guiGraphics.pose(); poseStack.translate(x, y, z); @@ -85,9 +100,10 @@ class ClientJeiUtil { FluidState fluidState = block.getFluidState(); if (fluidState.isEmpty()) { - MultiBufferSource.BufferSource buffers = mc.renderBuffers().bufferSource(); + MultiBufferSource.BufferSource buffers = Minecraft.getInstance().renderBuffers().bufferSource(); - mc.getBlockRenderer().renderSingleBlock(block, poseStack, buffers, 15728880, OverlayTexture.NO_OVERLAY); + RenderSystem.setupGui3DDiffuseLighting(L1, L2); + renderFunction.renderBlock(block, poseStack, buffers); buffers.endBatch(); } else { @@ -104,7 +120,7 @@ class ClientJeiUtil { Dummy.tempState = block; Dummy.tempFluid = fluidState; - mc.getBlockRenderer().renderLiquid(BlockPos.ZERO, Dummy.INSTANCE, builder, block, block.getFluidState()); + Minecraft.getInstance().getBlockRenderer().renderLiquid(BlockPos.ZERO, Dummy.INSTANCE, builder, block, block.getFluidState()); Dummy.tempFluid = EMPTY; Dummy.tempState = AIR; @@ -174,6 +190,35 @@ class ClientJeiUtil { RenderSystem.disableBlend(); } + // Required due to broken JEI implementation in REI plugin compatibility + public static void checkTypedIngredient(IIngredientManager manager, IIngredientType ingredientType, T uncheckedIngredient, Consumer> action) { + if ((uncheckedIngredient instanceof ItemStack stack && !stack.isEmpty()) || (uncheckedIngredient instanceof FluidStack fluidStack && !fluidStack.isEmpty())) { + manager.createTypedIngredient(ingredientType, uncheckedIngredient).ifPresent(action); + } + } + + public static void showRecipes(IFocusFactory focusFactory, ITypedIngredient ingredient) { + if (Minecraft.getInstance().screen instanceof IRecipesGui recipesGui) { + recipesGui.show(focusFactory.createFocus(RecipeIngredientRole.OUTPUT, ingredient)); + } else if (ModList.get().isLoaded(ModIds.REI_PC)) { + ViewSearchBuilder.builder().addRecipesFor(JEIPluginDetector.unwrapStack(ingredient)).open(); + } + } + + public static void showUsages(IFocusFactory focusFactory, ITypedIngredient ingredient) { + if (Minecraft.getInstance().screen instanceof IRecipesGui recipesGui) { + // input + catalyst + recipesGui.show(List.of(focusFactory.createFocus(RecipeIngredientRole.INPUT, ingredient), focusFactory.createFocus(RecipeIngredientRole.CATALYST, ingredient))); + } else if (ModList.get().isLoaded(ModIds.REI_PC)) { + ViewSearchBuilder.builder().addUsagesFor(JEIPluginDetector.unwrapStack(ingredient)).open(); + } + } + + @FunctionalInterface + interface RenderBlockFn { + void renderBlock(BlockState block, PoseStack poseStack, MultiBufferSource.BufferSource buffers); + } + private enum Dummy implements BlockAndTintGetter { INSTANCE; diff --git a/src/main/java/thedarkcolour/exdeorum/compat/jei/CrookCategory.java b/src/main/java/thedarkcolour/exdeorum/compat/jei/CrookCategory.java index 9b066364..5d25089c 100644 --- a/src/main/java/thedarkcolour/exdeorum/compat/jei/CrookCategory.java +++ b/src/main/java/thedarkcolour/exdeorum/compat/jei/CrookCategory.java @@ -18,6 +18,8 @@ package thedarkcolour.exdeorum.compat.jei; +import com.mojang.blaze3d.platform.InputConstants; +import mezz.jei.api.constants.VanillaTypes; import mezz.jei.api.gui.builder.IRecipeLayoutBuilder; import mezz.jei.api.gui.drawable.IDrawable; import mezz.jei.api.gui.ingredient.IRecipeSlotsView; @@ -29,12 +31,17 @@ import mezz.jei.api.recipe.RecipeIngredientRole; import mezz.jei.api.recipe.RecipeType; import mezz.jei.api.recipe.category.IRecipeCategory; import mezz.jei.api.runtime.IIngredientManager; +import net.minecraft.ChatFormatting; +import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.renderer.texture.OverlayTexture; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.network.chat.Component; +import net.minecraft.util.RandomSource; import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; -import net.minecraftforge.registries.ForgeRegistries; +import net.minecraftforge.client.RenderTypeHelper; +import net.minecraftforge.client.model.data.ModelData; import thedarkcolour.exdeorum.data.TranslationKeys; import thedarkcolour.exdeorum.registry.EBlocks; import thedarkcolour.exdeorum.registry.EItems; @@ -43,7 +50,7 @@ import java.util.ArrayList; import java.util.List; public class CrookCategory implements IRecipeCategory { - private static final Component REQUIRES_CERTAIN_STATE = Component.translatable(TranslationKeys.CROOK_CATEGORY_REQUIRES_STATE); + private static final Component REQUIRES_CERTAIN_STATE = Component.translatable(TranslationKeys.CROOK_CATEGORY_REQUIRES_STATE).withStyle(ChatFormatting.GRAY); private final IDrawable background; private final IDrawable icon; @@ -93,32 +100,47 @@ public class CrookCategory implements IRecipeCategory { @Override public void setRecipe(IRecipeLayoutBuilder builder, CrookJeiRecipe recipe, IFocusGroup focuses) { recipe.addIngredients(builder); - builder.addSlot(RecipeIngredientRole.OUTPUT, 80, 18).addItemStack(new ItemStack(recipe.result)); + builder.addSlot(RecipeIngredientRole.OUTPUT, 80, 18).addItemStack(new ItemStack(recipe.result)).addTooltipCallback((recipeSlotView, tooltip) -> { + tooltip.add(SieveCategory.formatChance(recipe.chance)); + }); } @Override public void draw(CrookJeiRecipe recipe, IRecipeSlotsView recipeSlotsView, GuiGraphics graphics, double mouseX, double mouseY) { this.timer.onDraw(); - this.arrow.draw(graphics, 52, 18); + this.arrow.draw(graphics, 50, 18); this.slot.draw(graphics, 79, 17); BlockState state = this.timer.getCycledItem(recipe.states); if (state.is(EBlocks.INFESTED_LEAVES.get())) { - state = Blocks.OAK_LEAVES.defaultBlockState(); + ClientJeiUtil.renderBlock(graphics, state, 28, 18, 10, 20f, (block, poseStack, buffers) -> { + var blockRenderer = Minecraft.getInstance().getBlockRenderer(); + var bakedmodel = blockRenderer.getBlockModel(state); + + for (var renderType : bakedmodel.getRenderTypes(state, RandomSource.create(42), ModelData.EMPTY)) { + blockRenderer.getModelRenderer().renderModel(poseStack.last(), buffers.getBuffer(RenderTypeHelper.getEntityRenderType(renderType, false)), state, bakedmodel, 1f, 1f, 1f, 15728880, OverlayTexture.NO_OVERLAY, ModelData.EMPTY, renderType); + } + }); + } else { + ClientJeiUtil.renderBlock(graphics, state, 28, 18, 10, 20f, (block, poseStack, buffers) -> { + Minecraft.getInstance().getBlockRenderer().renderSingleBlock(block, poseStack, buffers, 15728880, OverlayTexture.NO_OVERLAY); + }); } - ClientJeiUtil.renderBlock(graphics, state, 28, 18, 10, 20F); } @Override public List getTooltipStrings(CrookJeiRecipe recipe, IRecipeSlotsView recipeSlotsView, double mouseX, double mouseY) { - if (44.0 < mouseX && mouseX < 76.0 && 16 < mouseY && mouseY < 48) { + if (12 < mouseX && mouseX < 44 && 10 < mouseY && mouseY < 42) { var block = this.timer.getCycledItem(recipe.states).getBlock(); - var modId = ForgeRegistries.BLOCKS.getKey(block).getNamespace(); + var modId = BuiltInRegistries.BLOCK.getKey(block).getNamespace(); var tooltip = new ArrayList(); tooltip.add(Component.translatable(block.getDescriptionId())); - if (recipe.getClass() == CrookJeiRecipe.StatesRecipe.class) { + if (recipe instanceof CrookJeiRecipe.StatesRecipe statesRecipe && !statesRecipe.requirements.isEmpty()) { tooltip.add(REQUIRES_CERTAIN_STATE); + tooltip.addAll(statesRecipe.requirements); + } else if (recipe instanceof CrookJeiRecipe.TagRecipe tagRecipe) { + tooltip.add(Component.literal("#" + tagRecipe.tag.location()).withStyle(ChatFormatting.GRAY)); } tooltip.add(Component.literal(this.modIdHelper.getFormattedModNameForModId(modId))); return tooltip; @@ -126,4 +148,24 @@ public class CrookCategory implements IRecipeCategory { return List.of(); } + + @Override + public boolean handleInput(CrookJeiRecipe recipe, double mouseX, double mouseY, InputConstants.Key input) { + if (input.getType() == InputConstants.Type.MOUSE && (input.getValue() == InputConstants.MOUSE_BUTTON_LEFT || input.getValue() == InputConstants.MOUSE_BUTTON_RIGHT)) { + if (12 < mouseX && mouseX < 44 && 10 < mouseY && mouseY < 42) { + var block = this.timer.getCycledItem(recipe.states).getBlock(); + + ClientJeiUtil.checkTypedIngredient(this.ingredientManager, VanillaTypes.ITEM_STACK, new ItemStack(block.asItem()), ingredient -> { + if (input.getValue() == InputConstants.MOUSE_BUTTON_LEFT) { + ClientJeiUtil.showRecipes(this.focusFactory, ingredient); + } else if (input.getValue() == InputConstants.MOUSE_BUTTON_RIGHT) { + ClientJeiUtil.showUsages(this.focusFactory, ingredient); + } + }); + + return true; + } + } + return false; + } } diff --git a/src/main/java/thedarkcolour/exdeorum/compat/jei/CrookJeiRecipe.java b/src/main/java/thedarkcolour/exdeorum/compat/jei/CrookJeiRecipe.java index 50090dc5..d4b98a20 100644 --- a/src/main/java/thedarkcolour/exdeorum/compat/jei/CrookJeiRecipe.java +++ b/src/main/java/thedarkcolour/exdeorum/compat/jei/CrookJeiRecipe.java @@ -19,22 +19,26 @@ package thedarkcolour.exdeorum.compat.jei; import com.google.common.collect.ImmutableList; +import com.google.gson.JsonObject; import mezz.jei.api.gui.builder.IRecipeLayoutBuilder; import mezz.jei.api.recipe.RecipeIngredientRole; +import net.minecraft.ChatFormatting; import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.network.chat.Component; import net.minecraft.tags.TagKey; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.BlockStateProperties; +import org.jetbrains.annotations.Nullable; import thedarkcolour.exdeorum.recipe.BlockPredicate; import thedarkcolour.exdeorum.recipe.crook.CrookRecipe; import java.util.ArrayList; import java.util.HashSet; import java.util.List; -import java.util.stream.Collectors; public sealed abstract class CrookJeiRecipe { public final List states; @@ -51,7 +55,7 @@ public sealed abstract class CrookJeiRecipe { static CrookJeiRecipe create(CrookRecipe recipe) { if (recipe.blockPredicate() instanceof BlockPredicate.BlockStatePredicate state) { - return new StatesRecipe(state.possibleStates().collect(Collectors.toList()), recipe.result(), recipe.chance()); + return new StatesRecipe(state, state.possibleStates().filter(blockState -> !blockState.hasProperty(BlockStateProperties.WATERLOGGED) || !blockState.getValue(BlockStateProperties.WATERLOGGED)).toList(), recipe.result(), recipe.chance()); } else if (recipe.blockPredicate() instanceof BlockPredicate.SingleBlockPredicate block) { return new BlockRecipe(block.block(), recipe.result(), recipe.chance()); } else if (recipe.blockPredicate() instanceof BlockPredicate.TagPredicate tag) { @@ -71,26 +75,38 @@ public sealed abstract class CrookJeiRecipe { sealed static class StatesRecipe extends CrookJeiRecipe { private final List itemIngredients; + public final List requirements; - StatesRecipe(List states, Item result, float chance) { + StatesRecipe(@Nullable BlockPredicate.BlockStatePredicate predicate, List states, Item result, float chance) { super(states, result, chance); - ImmutableList.Builder temp = ImmutableList.builder(); + ImmutableList.Builder itemIngredients = ImmutableList.builder(); var blocks = new HashSet(); - for (var state : states) { + for (var state : this.states) { var block = state.getBlock(); if (blocks.add(block)) { var item = block.asItem(); if (item != Items.AIR) { - temp.add(new ItemStack(item)); + itemIngredients.add(new ItemStack(item)); } } } - this.itemIngredients = temp.build(); + this.itemIngredients = itemIngredients.build(); + + ImmutableList.Builder requirements = ImmutableList.builder(); + if (predicate != null) { + var json = predicate.properties().serializeToJson(); + if (json instanceof JsonObject obj) { + for (var entry : obj.entrySet()) { + requirements.add(Component.literal(" " + entry.getKey() + "=" + entry.getValue().toString()).withStyle(ChatFormatting.GRAY)); + } + } + } + this.requirements = requirements.build(); } @Override @@ -105,7 +121,7 @@ public sealed abstract class CrookJeiRecipe { public final TagKey tag; public TagRecipe(TagKey tag, List states, Item result, float chance) { - super(states, result, chance); + super(null, states, result, chance); this.tag = tag; } } diff --git a/src/main/java/thedarkcolour/exdeorum/compat/jei/CrucibleHeatSourcesCategory.java b/src/main/java/thedarkcolour/exdeorum/compat/jei/CrucibleHeatSourcesCategory.java index 10be4a80..40d556f2 100644 --- a/src/main/java/thedarkcolour/exdeorum/compat/jei/CrucibleHeatSourcesCategory.java +++ b/src/main/java/thedarkcolour/exdeorum/compat/jei/CrucibleHeatSourcesCategory.java @@ -31,9 +31,9 @@ import mezz.jei.api.recipe.RecipeIngredientRole; import mezz.jei.api.recipe.RecipeType; import mezz.jei.api.recipe.category.IRecipeCategory; import mezz.jei.api.runtime.IIngredientManager; -import mezz.jei.api.runtime.IRecipesGui; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.renderer.texture.OverlayTexture; import net.minecraft.network.chat.Component; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.TooltipFlag; @@ -103,7 +103,9 @@ class CrucibleHeatSourcesCategory implements IRecipeCategory { + Minecraft.getInstance().getBlockRenderer().renderSingleBlock(block, poseStack, buffers, 15728880, OverlayTexture.NO_OVERLAY); + }); } @Override @@ -113,7 +115,6 @@ class CrucibleHeatSourcesCategory implements IRecipeCategory { - if (Minecraft.getInstance().screen instanceof IRecipesGui recipesGui) { - if (input.getValue() == InputConstants.MOUSE_BUTTON_LEFT) { - recipesGui.show(this.focusFactory.createFocus(RecipeIngredientRole.OUTPUT, ingredient)); - } else { - // INPUT + CATALYST - recipesGui.show(List.of(this.focusFactory.createFocus(RecipeIngredientRole.CATALYST, ingredient), this.focusFactory.createFocus(RecipeIngredientRole.INPUT, ingredient))); - } + ClientJeiUtil.checkTypedIngredient(this.ingredientManager, recipe.ingredientType(), recipe.ingredient(), ingredient -> { + if (input.getValue() == InputConstants.MOUSE_BUTTON_LEFT) { + ClientJeiUtil.showRecipes(this.focusFactory, ingredient); + } else if (input.getValue() == InputConstants.MOUSE_BUTTON_RIGHT) { + ClientJeiUtil.showUsages(this.focusFactory, ingredient); } }); } diff --git a/src/main/java/thedarkcolour/exdeorum/compat/jei/ExDeorumJeiPlugin.java b/src/main/java/thedarkcolour/exdeorum/compat/jei/ExDeorumJeiPlugin.java index 39237011..41a4602a 100644 --- a/src/main/java/thedarkcolour/exdeorum/compat/jei/ExDeorumJeiPlugin.java +++ b/src/main/java/thedarkcolour/exdeorum/compat/jei/ExDeorumJeiPlugin.java @@ -44,7 +44,6 @@ import net.minecraft.world.level.block.LiquidBlock; import net.minecraft.world.level.block.WallTorchBlock; import net.minecraftforge.fluids.FluidStack; import thedarkcolour.exdeorum.ExDeorum; -import thedarkcolour.exdeorum.blockentity.LavaCrucibleBlockEntity; import thedarkcolour.exdeorum.client.screen.MechanicalHammerScreen; import thedarkcolour.exdeorum.client.screen.MechanicalSieveScreen; import thedarkcolour.exdeorum.compat.CompatHelper; @@ -55,7 +54,6 @@ import thedarkcolour.exdeorum.recipe.RecipeUtil; import thedarkcolour.exdeorum.recipe.barrel.BarrelCompostRecipe; import thedarkcolour.exdeorum.recipe.barrel.BarrelFluidMixingRecipe; import thedarkcolour.exdeorum.recipe.barrel.BarrelMixingRecipe; -import thedarkcolour.exdeorum.recipe.crook.CrookRecipe; import thedarkcolour.exdeorum.recipe.crucible.CrucibleRecipe; import thedarkcolour.exdeorum.recipe.hammer.HammerRecipe; import thedarkcolour.exdeorum.registry.EBlocks; @@ -202,7 +200,7 @@ public class ExDeorumJeiPlugin implements IModPlugin { private static void addCrucibleHeatSources(IRecipeRegistration registration) { var values = new Object2IntOpenHashMap(); - for (var entry : LavaCrucibleBlockEntity.HEAT_REGISTRY.object2IntEntrySet()) { + for (var entry : RecipeUtil.getHeatSources()) { var state = entry.getKey(); var block = state.getBlock(); diff --git a/src/main/java/thedarkcolour/exdeorum/compat/jei/SieveCategory.java b/src/main/java/thedarkcolour/exdeorum/compat/jei/SieveCategory.java index 09587e1a..2d162854 100644 --- a/src/main/java/thedarkcolour/exdeorum/compat/jei/SieveCategory.java +++ b/src/main/java/thedarkcolour/exdeorum/compat/jei/SieveCategory.java @@ -32,6 +32,7 @@ import mezz.jei.api.recipe.category.IRecipeCategory; import net.minecraft.ChatFormatting; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.storage.loot.providers.number.BinomialDistributionGenerator; import net.minecraft.world.level.storage.loot.providers.number.ConstantValue; @@ -104,6 +105,13 @@ class SieveCategory implements IRecipeCategory { } } + // Takes a decimal probability and returns a user-friendly percentage value + // todo move into JeiUtil + public static Component formatChance(double probability) { + var chance = FORMATTER.format(probability * 100); + return Component.translatable(TranslationKeys.SIEVE_RECIPE_CHANCE, chance).withStyle(ChatFormatting.GRAY); + } + public static void addTooltips(IRecipeSlotBuilder slot, boolean byHandOnly, NumberProvider provider) { var tooltipLines = new ImmutableList.Builder(); @@ -113,8 +121,8 @@ class SieveCategory implements IRecipeCategory { } if (provider instanceof BinomialDistributionGenerator binomial) { if (binomial.n instanceof ConstantValue constant && constant.value == 1) { - var chance = FORMATTER.format(RecipeUtil.getExpectedValue(binomial.p) * 100); - tooltipLines.add(Component.translatable(TranslationKeys.SIEVE_RECIPE_CHANCE, chance).withStyle(ChatFormatting.GRAY)); + var chanceLabel = formatChance(RecipeUtil.getExpectedValue(binomial.p)); + tooltipLines.add(chanceLabel); } else { addAvgOutput(tooltipLines, RecipeUtil.getExpectedValue(provider)); } diff --git a/src/main/java/thedarkcolour/exdeorum/compat/kubejs/ExDeorumKubeJsBindings.java b/src/main/java/thedarkcolour/exdeorum/compat/kubejs/ExDeorumKubeJsBindings.java index 3442ff48..4e421139 100644 --- a/src/main/java/thedarkcolour/exdeorum/compat/kubejs/ExDeorumKubeJsBindings.java +++ b/src/main/java/thedarkcolour/exdeorum/compat/kubejs/ExDeorumKubeJsBindings.java @@ -19,18 +19,29 @@ package thedarkcolour.exdeorum.compat.kubejs; import com.mojang.brigadier.exceptions.CommandSyntaxException; +import dev.latvian.mods.kubejs.bindings.event.ServerEvents; import dev.latvian.mods.kubejs.recipe.RecipesEventJS; import dev.latvian.mods.kubejs.recipe.ReplacementMatch; import dev.latvian.mods.kubejs.recipe.filter.RecipeFilter; -import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import dev.latvian.mods.kubejs.script.ScriptType; +import dev.latvian.mods.rhino.util.HideFromJS; +import net.minecraft.advancements.critereon.StatePropertiesPredicate; import net.minecraft.commands.arguments.blocks.BlockStateParser; import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.util.StringRepresentable; +import net.minecraft.world.item.crafting.RecipeType; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.Property; +import net.minecraftforge.registries.RegistryObject; import thedarkcolour.exdeorum.ExDeorum; -import thedarkcolour.exdeorum.blockentity.LavaCrucibleBlockEntity; +import thedarkcolour.exdeorum.recipe.BlockPredicate; +import thedarkcolour.exdeorum.recipe.crucible.FinishedCrucibleHeatRecipe; import thedarkcolour.exdeorum.registry.ERecipeTypes; +import java.util.Comparator; +import java.util.function.Consumer; + @SuppressWarnings("unused") class ExDeorumKubeJsBindings { static { @@ -47,33 +58,53 @@ class ExDeorumKubeJsBindings { } // This method previously accepted a BlockState, which made it impossible to call through KubeJS. + @SuppressWarnings({"rawtypes", "unchecked"}) public void setCrucibleHeatValueForState(String stateString, int value) { - try { - LavaCrucibleBlockEntity.KUBEJS_HEAT_VALUES.put(BlockStateParser.parseForBlock(BuiltInRegistries.BLOCK.asLookup(), stateString, false).blockState(), value); - } catch (CommandSyntaxException exception) { - // Throw a more appropriate exception. - throw new IllegalArgumentException("Failed to parse BlockState string \"" + stateString + "\""); - } - } - - public void setCrucibleHeatValueForBlock(Block block, int value) { - for (var state : block.getStateDefinition().getPossibleStates()) { - LavaCrucibleBlockEntity.KUBEJS_HEAT_VALUES.put(state, value); - } - } - - public void removeDefaultSieveRecipes(RecipesEventJS recipesEvent) { - recipesEvent.remove(r -> { - return r.kjs$getType().equals(ERecipeTypes.SIEVE.getId()) && r.kjs$getOrCreateId().getNamespace().equals(ExDeorum.ID); + onRecipesEvent(event -> { + try { + var state = BlockStateParser.parseForBlock(BuiltInRegistries.BLOCK.asLookup(), stateString, false).blockState(); + var properties = StatePropertiesPredicate.Builder.properties(); + for (Property prop : state.getProperties()) { + bypassTypeChecking(properties, prop, state); + } + event.custom(new FinishedCrucibleHeatRecipe(null, BlockPredicate.blockState(state.getBlock(), properties.build()), value).serializeRecipe()); + } catch (CommandSyntaxException exception) { + // Throw a more appropriate exception. + throw new IllegalArgumentException("Failed to parse BlockState string \"" + stateString + "\""); + } }); } - // not the most elegant solution, but if it works, it works + @HideFromJS + private static > void bypassTypeChecking(StatePropertiesPredicate.Builder properties, Property prop, BlockState state) { + properties.hasProperty(prop, prop.getName(state.getValue(prop))); + } + + @SuppressWarnings("DataFlowIssue") + public void setCrucibleHeatValueForBlock(Block block, int value) { + onRecipesEvent(event -> { + event.custom(new FinishedCrucibleHeatRecipe(null, BlockPredicate.singleBlock(block), value).serializeRecipe()); + }); + } + + public void removeDefaultSieveRecipes(RecipesEventJS recipesEvent) { + removeDefaultRecipes(recipesEvent, ERecipeTypes.SIEVE); + } + public void removeDefaultHeatSources() { - var map = new Object2IntOpenHashMap(); - LavaCrucibleBlockEntity.putDefaults(map); - for (var key : map.keySet()) { - LavaCrucibleBlockEntity.KUBEJS_HEAT_VALUES.put(key, 0); - } + onRecipesEvent(event -> removeDefaultRecipes(event, ERecipeTypes.CRUCIBLE_HEAT_SOURCE)); + } + + @HideFromJS + private static void removeDefaultRecipes(RecipesEventJS event, RegistryObject> recipeType) { + event.remove(r -> r.kjs$getType().equals(recipeType.getId()) && r.kjs$getOrCreateId().getNamespace().equals(ExDeorum.ID)); + } + + @HideFromJS + private static void onRecipesEvent(Consumer action) { + ServerEvents.RECIPES.listenJava(ScriptType.SERVER, null, jsEvent -> { + action.accept((RecipesEventJS) jsEvent); + return null; + }); } } diff --git a/src/main/java/thedarkcolour/exdeorum/compat/top/ExDeorumInfoProvider.java b/src/main/java/thedarkcolour/exdeorum/compat/top/ExDeorumInfoProvider.java index ef1440fd..56480e1f 100644 --- a/src/main/java/thedarkcolour/exdeorum/compat/top/ExDeorumInfoProvider.java +++ b/src/main/java/thedarkcolour/exdeorum/compat/top/ExDeorumInfoProvider.java @@ -19,13 +19,9 @@ package thedarkcolour.exdeorum.compat.top; import it.unimi.dsi.fastutil.objects.ObjectArrayList; -import mcjty.theoneprobe.api.CompoundText; -import mcjty.theoneprobe.api.ElementAlignment; -import mcjty.theoneprobe.api.IProbeHitData; -import mcjty.theoneprobe.api.IProbeInfo; -import mcjty.theoneprobe.api.IProbeInfoProvider; -import mcjty.theoneprobe.api.ProbeMode; -import mcjty.theoneprobe.api.TextStyleClass; +import kroppeb.stareval.element.token.BinaryOperatorToken; +import mcjty.theoneprobe.api.*; +import net.minecraft.ChatFormatting; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.player.Player; @@ -37,6 +33,7 @@ import thedarkcolour.exdeorum.blockentity.AbstractCrucibleBlockEntity; import thedarkcolour.exdeorum.blockentity.BarrelBlockEntity; import thedarkcolour.exdeorum.blockentity.InfestedLeavesBlockEntity; import thedarkcolour.exdeorum.blockentity.SieveBlockEntity; +import thedarkcolour.exdeorum.data.TranslationKeys; import thedarkcolour.exdeorum.registry.EBlocks; public class ExDeorumInfoProvider implements IProbeInfoProvider { @@ -61,7 +58,11 @@ public class ExDeorumInfoProvider implements IProbeInfoProvider { if (volume == 1000 || barrel.isBrewing()) { int progress = (int) (barrel.progress * 100.0f); - info.text(CompoundText.create().style(TextStyleClass.LABEL).text("Progress: ").style(TextStyleClass.WARNING).text(progress + "%")); + if (progress == 100) { + info.text(Component.translatable(TranslationKeys.INFESTED_LEAVES_FULLY_INFESTED).withStyle(ChatFormatting.GRAY)); + } else { + info.text(CompoundText.create().style(TextStyleClass.LABEL).text("Progress: ").style(TextStyleClass.WARNING).text(progress + "%")); + } } else if (volume > 0) { int volumePercent = (int) (volume / 10.0f); diff --git a/src/main/java/thedarkcolour/exdeorum/config/EConfig.java b/src/main/java/thedarkcolour/exdeorum/config/EConfig.java index 1a218973..76fd968d 100644 --- a/src/main/java/thedarkcolour/exdeorum/config/EConfig.java +++ b/src/main/java/thedarkcolour/exdeorum/config/EConfig.java @@ -136,7 +136,6 @@ public class EConfig { public final IntValue mechanicalHammerEnergyStorage; public final IntValue mechanicalHammerEnergyConsumption; public final IntValue sieveIntervalTicks; - public final DoubleValue infestedLeavesStringChance; public Server(ForgeConfigSpec.Builder builder) { builder.comment("Server configuration for Ex Deorum").push("server"); @@ -192,9 +191,6 @@ public class EConfig { this.sieveIntervalTicks = builder .comment("The minimum number of ticks a player must wait between two sifting operations. Only affects sifting by hand. 0 means no limit.") .defineInRange("sieve_interval", 1, 0, Integer.MAX_VALUE); - this.infestedLeavesStringChance = builder - .comment("The chance for infested leaves to drop string. 0 means infested leaves never drop string, 1 means infested leaves always drop string.") - .defineInRange("infested_leaves_string_chance", 0.4, 0.0, 1.0); builder.pop(); } } diff --git a/src/main/java/thedarkcolour/exdeorum/data/BlockLoot.java b/src/main/java/thedarkcolour/exdeorum/data/BlockLoot.java index f154bf65..6a778aa9 100644 --- a/src/main/java/thedarkcolour/exdeorum/data/BlockLoot.java +++ b/src/main/java/thedarkcolour/exdeorum/data/BlockLoot.java @@ -29,7 +29,7 @@ import net.minecraft.world.level.storage.loot.LootTable; import net.minecraft.world.level.storage.loot.entries.LootItem; import net.minecraft.world.level.storage.loot.providers.number.ConstantValue; import thedarkcolour.exdeorum.ExDeorum; -import thedarkcolour.exdeorum.loot.InfestedStringFunction; +import thedarkcolour.exdeorum.block.MechanicalHammerBlock; import thedarkcolour.exdeorum.loot.MachineLootFunction; import thedarkcolour.exdeorum.registry.EBlocks; import thedarkcolour.exdeorum.registry.EItems; @@ -54,21 +54,16 @@ class BlockLoot extends BlockLootSubProvider { } }); - add(EBlocks.INFESTED_LEAVES.get(), LootTable.lootTable() - .withPool(LootPool.lootPool() - .setRolls(ConstantValue.exactly(1)) - .add(LootItem.lootTableItem(Items.STRING) - .apply(InfestedStringFunction.infestedString())))); - // see createSingleItemTable() for reference - add(EBlocks.MECHANICAL_SIEVE.get(), LootTable.lootTable() - .withPool(applyExplosionCondition(EItems.MECHANICAL_SIEVE.get(), LootPool.lootPool() + machineDrop(EBlocks.MECHANICAL_HAMMER.get()); + machineDrop(EBlocks.MECHANICAL_SIEVE.get()); + } + + // see createSingleItemTable() for reference + private void machineDrop(Block machine) { + add(machine, LootTable.lootTable() + .withPool(applyExplosionCondition(machine, LootPool.lootPool() .setRolls(ConstantValue.exactly(1.0F)) - .add(LootItem.lootTableItem(EItems.MECHANICAL_SIEVE.get()) - .apply(MachineLootFunction.machineLoot()))))); - add(EBlocks.MECHANICAL_HAMMER.get(), LootTable.lootTable() - .withPool(applyExplosionCondition(EItems.MECHANICAL_HAMMER.get(), LootPool.lootPool() - .setRolls(ConstantValue.exactly(1.0F)) - .add(LootItem.lootTableItem(EItems.MECHANICAL_HAMMER.get()) + .add(LootItem.lootTableItem(machine) .apply(MachineLootFunction.machineLoot()))))); } diff --git a/src/main/java/thedarkcolour/exdeorum/data/English.java b/src/main/java/thedarkcolour/exdeorum/data/English.java index b947bc0d..30d2739f 100644 --- a/src/main/java/thedarkcolour/exdeorum/data/English.java +++ b/src/main/java/thedarkcolour/exdeorum/data/English.java @@ -34,6 +34,8 @@ class English { english.add(TranslationKeys.MECHANICAL_HAMMER_HAMMER_LABEL, "Hammer: "); english.add(TranslationKeys.ENERGY, "Energy"); + english.add(TranslationKeys.INFESTED_LEAVES_FULLY_INFESTED, "Fully Infested"); + english.add(TranslationKeys.ROOT_ADVANCEMENT_TITLE, "Don't Look Down..."); english.add(TranslationKeys.ROOT_ADVANCEMENT_DESCRIPTION, "Spawn into a SkyBlock void world"); english.add(TranslationKeys.CROOK_ADVANCEMENT_TITLE, "Give Him The Hook"); @@ -69,7 +71,7 @@ class English { english.add(TranslationKeys.CRUCIBLE_HEAT_SOURCE_CATEGORY_MULTIPLIER, "Melt Rate: %sx"); english.add(TranslationKeys.HAMMER_CATEGORY_TITLE, "Hammer"); english.add(TranslationKeys.CROOK_CATEGORY_TITLE, "Crook"); - english.add(TranslationKeys.CROOK_CATEGORY_REQUIRES_STATE, "Requires specific block state"); + english.add(TranslationKeys.CROOK_CATEGORY_REQUIRES_STATE, "Requires properties:"); english.add(TranslationKeys.SIEVE_CATEGORY_TITLE, "Sieve"); english.add(TranslationKeys.SIEVE_RECIPE_CHANCE, "Chance: %s%%"); english.add(TranslationKeys.SIEVE_RECIPE_AVERAGE_OUTPUT, "Avg. Output: %s"); diff --git a/src/main/java/thedarkcolour/exdeorum/data/TranslationKeys.java b/src/main/java/thedarkcolour/exdeorum/data/TranslationKeys.java index 5eeec326..4f1c9262 100644 --- a/src/main/java/thedarkcolour/exdeorum/data/TranslationKeys.java +++ b/src/main/java/thedarkcolour/exdeorum/data/TranslationKeys.java @@ -30,6 +30,9 @@ public class TranslationKeys { public static final String MECHANICAL_HAMMER_HAMMER_LABEL = "item." + ExDeorum.ID + ".mechanical_hammer.hammer_label"; public static final String ENERGY = "gui." + ExDeorum.ID + ".energy_label"; + // Blocks + public static final String INFESTED_LEAVES_FULLY_INFESTED = "block." + ExDeorum.ID + ".infested_leaves.fully_infested"; + // Advancements public static final String ROOT_ADVANCEMENT_TITLE = "advancements." + ExDeorum.ID + ".core.root.title"; public static final String ROOT_ADVANCEMENT_DESCRIPTION = "advancements." + ExDeorum.ID + ".core.root.description"; diff --git a/src/main/java/thedarkcolour/exdeorum/data/recipe/Recipes.java b/src/main/java/thedarkcolour/exdeorum/data/recipe/Recipes.java index f55cc8e6..04a3432f 100644 --- a/src/main/java/thedarkcolour/exdeorum/data/recipe/Recipes.java +++ b/src/main/java/thedarkcolour/exdeorum/data/recipe/Recipes.java @@ -19,6 +19,7 @@ package thedarkcolour.exdeorum.data.recipe; import net.minecraft.advancements.critereon.StatePropertiesPredicate; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.data.recipes.FinishedRecipe; import net.minecraft.data.recipes.RecipeCategory; import net.minecraft.resources.ResourceLocation; @@ -30,7 +31,9 @@ import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.item.crafting.Ingredient; import net.minecraft.world.level.ItemLike; +import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.CampfireBlock; import net.minecraft.world.level.material.Fluid; import net.minecraft.world.level.material.Fluids; import net.minecraft.world.level.storage.loot.providers.number.ConstantValue; @@ -54,6 +57,7 @@ import thedarkcolour.exdeorum.recipe.barrel.FinishedBarrelFluidMixingRecipe; import thedarkcolour.exdeorum.recipe.barrel.FinishedBarrelMixingRecipe; import thedarkcolour.exdeorum.recipe.BlockPredicate; import thedarkcolour.exdeorum.recipe.crook.FinishedCrookRecipe; +import thedarkcolour.exdeorum.recipe.crucible.FinishedCrucibleHeatRecipe; import thedarkcolour.exdeorum.recipe.crucible.FinishedCrucibleRecipe; import thedarkcolour.exdeorum.recipe.hammer.FinishedHammerRecipe; import thedarkcolour.exdeorum.registry.EBlocks; @@ -80,6 +84,7 @@ public class Recipes { crucibleRecipes(writer); hammerRecipes(writer); crookRecipes(writer); + crucibleHeatSources(writer); barrelCompostRecipes(writer); barrelMixingRecipes(writer); } @@ -490,12 +495,37 @@ public class Recipes { crookRecipe(writer, "silkworm", BlockPredicate.blockTag(BlockTags.LEAVES), EItems.SILK_WORM.get(), 0.01f); var fullyInfestedLeaves = BlockPredicate.blockState(EBlocks.INFESTED_LEAVES.get(), StatePropertiesPredicate.Builder.properties().hasProperty(InfestedLeavesBlock.FULLY_INFESTED, true).build()); crookRecipe(writer, "silkworm_bonus", fullyInfestedLeaves, EItems.SILK_WORM.get(), 0.01f); + crookRecipe(writer, "string_roll_1", fullyInfestedLeaves, Items.STRING, 0.4f); + crookRecipe(writer, "string_roll_2", fullyInfestedLeaves, Items.STRING, 0.1f); } private static void crookRecipe(Consumer writer, String name, BlockPredicate blockPredicate, ItemLike result, float chance) { writer.accept(new FinishedCrookRecipe(new ResourceLocation(ExDeorum.ID, "crook/" + name), blockPredicate, result.asItem(), chance)); } + private static void crucibleHeatSources(Consumer writer) { + crucibleHeatSource(writer, Blocks.TORCH, 1); + crucibleHeatSource(writer, Blocks.WALL_TORCH, 1); + crucibleHeatSource(writer, Blocks.LANTERN, 1); + crucibleHeatSource(writer, Blocks.SOUL_TORCH, 2); + crucibleHeatSource(writer, Blocks.SOUL_WALL_TORCH, 2); + crucibleHeatSource(writer, Blocks.SOUL_LANTERN, 2); + crucibleHeatSource(writer, Blocks.LAVA, 3); + crucibleHeatSource(writer, Blocks.FIRE, 5); + crucibleHeatSource(writer, Blocks.SOUL_FIRE, 5); + + crucibleHeatSource(writer, "lit_campfire", BlockPredicate.blockState(Blocks.CAMPFIRE, StatePropertiesPredicate.Builder.properties().hasProperty(CampfireBlock.LIT, true).build()), 2); + crucibleHeatSource(writer, "lit_soul_campfire", BlockPredicate.blockState(Blocks.SOUL_CAMPFIRE, StatePropertiesPredicate.Builder.properties().hasProperty(CampfireBlock.LIT, true).build()), 2); + } + + private static void crucibleHeatSource(Consumer writer, Block block, int heatValue) { + crucibleHeatSource(writer, BuiltInRegistries.BLOCK.getKey(block).getPath(), BlockPredicate.singleBlock(block), heatValue); + } + + private static void crucibleHeatSource(Consumer writer, String name, BlockPredicate blockPredicate, int heatValue) { + writer.accept(new FinishedCrucibleHeatRecipe(new ResourceLocation(ExDeorum.ID, "crucible_heat_source/" + name), blockPredicate, heatValue)); + } + private static void barrelCompostRecipes(Consumer writer) { // plants barrelCompost(writer, "saplings", ingredient(ItemTags.SAPLINGS), 125); diff --git a/src/main/java/thedarkcolour/exdeorum/event/EventHandler.java b/src/main/java/thedarkcolour/exdeorum/event/EventHandler.java index b702e76b..acb5cc3b 100644 --- a/src/main/java/thedarkcolour/exdeorum/event/EventHandler.java +++ b/src/main/java/thedarkcolour/exdeorum/event/EventHandler.java @@ -242,7 +242,6 @@ public final class EventHandler { event.addListener((prepBarrier, resourceManager, prepProfiler, reloadProfiler, backgroundExecutor, gameExecutor) -> { return prepBarrier.wait(Unit.INSTANCE).thenRunAsync(() -> { RecipeUtil.reload(recipes); - LavaCrucibleBlockEntity.putDefaultHeatValues(); }, gameExecutor); }); } diff --git a/src/main/java/thedarkcolour/exdeorum/loot/InfestedStringFunction.java b/src/main/java/thedarkcolour/exdeorum/loot/InfestedStringFunction.java deleted file mode 100644 index b78a8c1d..00000000 --- a/src/main/java/thedarkcolour/exdeorum/loot/InfestedStringFunction.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Ex Deorum - * Copyright (c) 2024 thedarkcolour - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package thedarkcolour.exdeorum.loot; - -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonObject; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.storage.loot.LootContext; -import net.minecraft.world.level.storage.loot.functions.LootItemConditionalFunction; -import net.minecraft.world.level.storage.loot.functions.LootItemFunctionType; -import net.minecraft.world.level.storage.loot.parameters.LootContextParams; -import net.minecraft.world.level.storage.loot.predicates.LootItemCondition; -import thedarkcolour.exdeorum.block.InfestedLeavesBlock; -import thedarkcolour.exdeorum.blockentity.InfestedLeavesBlockEntity; -import thedarkcolour.exdeorum.config.EConfig; -import thedarkcolour.exdeorum.registry.ELootFunctions; - -// Sets the correct amount based on the progress of the infested leaves -public class InfestedStringFunction extends LootItemConditionalFunction { - protected InfestedStringFunction(LootItemCondition[] conditions) { - super(conditions); - } - - @Override - protected ItemStack run(ItemStack stack, LootContext context) { - var te = context.getParamOrNull(LootContextParams.BLOCK_ENTITY); - var state = context.getParamOrNull(LootContextParams.BLOCK_STATE); - - if (state != null && state.getValue(InfestedLeavesBlock.FULLY_INFESTED)) { - if (te instanceof InfestedLeavesBlockEntity leaves) { - var progress = leaves.getProgress(); - var rand = context.getRandom(); - var count = 0; - var chance = EConfig.SERVER.infestedLeavesStringChance.get(); - - if (rand.nextFloat() < progress * chance) { - if (rand.nextFloat() < progress * chance / 4f) { - ++count; - } - ++count; - } - - if (count > 0) { - stack.setCount(count); - return stack; - } - } - } - - return ItemStack.EMPTY; - } - - @Override - public LootItemFunctionType getType() { - return ELootFunctions.INFESTED_STRING.get(); - } - - public static LootItemConditionalFunction.Builder infestedString() { - return LootItemConditionalFunction.simpleBuilder(InfestedStringFunction::new); - } - - public static class LootSerializer extends LootItemConditionalFunction.Serializer { - @Override - public InfestedStringFunction deserialize(JsonObject json, JsonDeserializationContext ctx, LootItemCondition[] conditions) { - return new InfestedStringFunction(conditions); - } - } -} diff --git a/src/main/java/thedarkcolour/exdeorum/recipe/BlockPredicate.java b/src/main/java/thedarkcolour/exdeorum/recipe/BlockPredicate.java index 32b5df13..df588670 100644 --- a/src/main/java/thedarkcolour/exdeorum/recipe/BlockPredicate.java +++ b/src/main/java/thedarkcolour/exdeorum/recipe/BlockPredicate.java @@ -31,6 +31,7 @@ import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; import org.jetbrains.annotations.Nullable; +import java.util.Objects; import java.util.function.Predicate; import java.util.stream.Stream; import java.util.stream.StreamSupport; @@ -69,7 +70,7 @@ public sealed interface BlockPredicate extends Predicate { if (block == Blocks.AIR) return null; if (json.has("state")) { - return new BlockStatePredicate(block, StatePropertiesPredicate.fromJson(json.get("block_state"))); + return new BlockStatePredicate(block, StatePropertiesPredicate.fromJson(json.get("state"))); } else { return new SingleBlockPredicate(block); } @@ -142,6 +143,15 @@ public sealed interface BlockPredicate extends Predicate { public Stream possibleStates() { return this.block.getStateDefinition().getPossibleStates().stream().filter(this.properties::matches); } + + // Although slow, this is useful for testing + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + BlockStatePredicate that = (BlockStatePredicate) o; + return Objects.equals(block, that.block) && Objects.equals(properties.serializeToJson(), that.properties.serializeToJson()); + } } record SingleBlockPredicate(Block block) implements BlockPredicate { diff --git a/src/main/java/thedarkcolour/exdeorum/recipe/RecipeUtil.java b/src/main/java/thedarkcolour/exdeorum/recipe/RecipeUtil.java index fdd83b7c..db2d1266 100644 --- a/src/main/java/thedarkcolour/exdeorum/recipe/RecipeUtil.java +++ b/src/main/java/thedarkcolour/exdeorum/recipe/RecipeUtil.java @@ -20,7 +20,9 @@ package thedarkcolour.exdeorum.recipe; import com.google.gson.JsonObject; import com.google.gson.JsonSyntaxException; +import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.ObjectSet; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.resources.ResourceLocation; @@ -46,15 +48,13 @@ import net.minecraftforge.common.crafting.CraftingHelper; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.registries.ForgeRegistries; import org.jetbrains.annotations.Nullable; +import thedarkcolour.exdeorum.ExDeorum; import thedarkcolour.exdeorum.compat.PreferredOres; import thedarkcolour.exdeorum.item.HammerItem; import thedarkcolour.exdeorum.recipe.barrel.BarrelCompostRecipe; import thedarkcolour.exdeorum.recipe.barrel.BarrelFluidMixingRecipe; import thedarkcolour.exdeorum.recipe.barrel.BarrelMixingRecipe; -import thedarkcolour.exdeorum.recipe.cache.BarrelFluidMixingRecipeCache; -import thedarkcolour.exdeorum.recipe.cache.CrookRecipeCache; -import thedarkcolour.exdeorum.recipe.cache.SieveRecipeCache; -import thedarkcolour.exdeorum.recipe.cache.SingleIngredientRecipeCache; +import thedarkcolour.exdeorum.recipe.cache.*; import thedarkcolour.exdeorum.recipe.crook.CrookRecipe; import thedarkcolour.exdeorum.recipe.crucible.CrucibleRecipe; import thedarkcolour.exdeorum.recipe.hammer.HammerRecipe; @@ -80,6 +80,7 @@ public final class RecipeUtil { private static SieveRecipeCache sieveRecipeCache; private static BarrelFluidMixingRecipeCache barrelFluidMixingRecipeCache; private static CrookRecipeCache crookRecipeCache; + private static CrucibleHeatRecipeCache crucibleHeatRecipeCache; public static void reload(RecipeManager recipes) { barrelCompostRecipeCache = new SingleIngredientRecipeCache<>(recipes, ERecipeTypes.BARREL_COMPOST); @@ -89,6 +90,7 @@ public final class RecipeUtil { sieveRecipeCache = new SieveRecipeCache(recipes); barrelFluidMixingRecipeCache = new BarrelFluidMixingRecipeCache(recipes); crookRecipeCache = new CrookRecipeCache(recipes); + crucibleHeatRecipeCache = new CrucibleHeatRecipeCache(recipes); HammerItem.refreshValidBlocks(); } @@ -100,6 +102,7 @@ public final class RecipeUtil { sieveRecipeCache = null; barrelFluidMixingRecipeCache = null; crookRecipeCache = null; + crucibleHeatRecipeCache = null; } public static List getSieveRecipes(Item mesh, ItemStack item) { @@ -311,6 +314,26 @@ public final class RecipeUtil { } } + @Nullable + public static BlockPredicate readBlockPredicate(ResourceLocation recipeId, JsonObject json) { + BlockPredicate blockPredicate = BlockPredicate.fromJson(json.getAsJsonObject("block_predicate")); + + if (blockPredicate == null) { + ExDeorum.LOGGER.error("Invalid block_predicate for recipe {}, refer to Ex Deorum documentation for syntax: {}", recipeId, json.getAsJsonObject("block_predicate")); + } + return blockPredicate; + } + + @Nullable + public static BlockPredicate readBlockPredicateNetwork(ResourceLocation recipeId, FriendlyByteBuf buffer) { + BlockPredicate blockPredicate = BlockPredicate.fromNetwork(buffer); + + if (blockPredicate == null) { + ExDeorum.LOGGER.error("Failed to read block_predicate from network for recipe {}", recipeId); + } + return blockPredicate; + } + @SuppressWarnings("deprecation") public static boolean isTagEmpty(TagKey tag) { return BuiltInRegistries.ITEM.getTag(tag).map(set -> !set.iterator().hasNext()).orElse(PreferredOres.getPreferredOre(tag) == Items.AIR); @@ -323,4 +346,12 @@ public final class RecipeUtil { public static List getCrookRecipes(BlockState state) { return crookRecipeCache.getRecipes(state); } + + public static int getHeatValue(BlockState state) { + return crucibleHeatRecipeCache.getValue(state); + } + + public static ObjectSet> getHeatSources() { + return crucibleHeatRecipeCache.getEntries(); + } } diff --git a/src/main/java/thedarkcolour/exdeorum/recipe/cache/CrucibleHeatRecipeCache.java b/src/main/java/thedarkcolour/exdeorum/recipe/cache/CrucibleHeatRecipeCache.java new file mode 100644 index 00000000..97a556b2 --- /dev/null +++ b/src/main/java/thedarkcolour/exdeorum/recipe/cache/CrucibleHeatRecipeCache.java @@ -0,0 +1,61 @@ +/* + * Ex Deorum + * Copyright (c) 2024 thedarkcolour + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package thedarkcolour.exdeorum.recipe.cache; + +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectSet; +import net.minecraft.world.item.crafting.RecipeManager; +import net.minecraft.world.level.block.state.BlockState; +import org.jetbrains.annotations.Nullable; +import thedarkcolour.exdeorum.registry.ERecipeTypes; + +public class CrucibleHeatRecipeCache { + private RecipeManager recipeManager; + @Nullable + private Object2IntMap recipes; + + public CrucibleHeatRecipeCache(RecipeManager recipeManager) { + this.recipeManager = recipeManager; + } + + public int getValue(BlockState state) { + if (this.recipes == null) { + buildRecipes(); + } + return this.recipes.getInt(state); + } + + private void buildRecipes() { + this.recipes = new Object2IntOpenHashMap<>(); + + for (var recipe : this.recipeManager.byType(ERecipeTypes.CRUCIBLE_HEAT_SOURCE.get()).values()) { + recipe.blockPredicate().possibleStates().forEach(state -> recipes.put(state, recipe.heatValue())); + } + + this.recipeManager = null; + } + + public ObjectSet> getEntries() { + if (this.recipes == null) { + buildRecipes(); + } + return this.recipes.object2IntEntrySet(); + } +} diff --git a/src/main/java/thedarkcolour/exdeorum/recipe/crook/CrookRecipe.java b/src/main/java/thedarkcolour/exdeorum/recipe/crook/CrookRecipe.java index 776bc0d6..f070919e 100644 --- a/src/main/java/thedarkcolour/exdeorum/recipe/crook/CrookRecipe.java +++ b/src/main/java/thedarkcolour/exdeorum/recipe/crook/CrookRecipe.java @@ -31,7 +31,6 @@ import net.minecraft.world.item.crafting.Recipe; import net.minecraft.world.item.crafting.RecipeSerializer; import net.minecraft.world.item.crafting.RecipeType; import net.minecraft.world.level.Level; -import thedarkcolour.exdeorum.ExDeorum; import thedarkcolour.exdeorum.recipe.BlockPredicate; import thedarkcolour.exdeorum.recipe.RecipeUtil; import thedarkcolour.exdeorum.registry.ERecipeSerializers; @@ -77,26 +76,21 @@ public record CrookRecipe(ResourceLocation id, BlockPredicate blockPredicate, It public static class Serializer implements RecipeSerializer { @Override public CrookRecipe fromJson(ResourceLocation id, JsonObject json) { - BlockPredicate blockPredicate = BlockPredicate.fromJson(json.getAsJsonObject("block_predicate")); + BlockPredicate blockPredicate = RecipeUtil.readBlockPredicate(id, json); + if (blockPredicate == null) return null; - if (blockPredicate == null) { - ExDeorum.LOGGER.error("Invalid block_predicate for recipe {}, refer to Ex Deorum documentation for syntax: {}", id, json.getAsJsonObject("block_predicate")); - return null; - } Item result = RecipeUtil.readItem(json, "result"); float chance = json.get("chance").getAsFloat(); + return new CrookRecipe(id, blockPredicate, result, chance); } @Override public CrookRecipe fromNetwork(ResourceLocation id, FriendlyByteBuf buffer) { - BlockPredicate blockPredicate = BlockPredicate.fromNetwork(buffer); + BlockPredicate blockPredicate = RecipeUtil.readBlockPredicateNetwork(id, buffer); + if (blockPredicate == null) return null; - if (blockPredicate == null) { - ExDeorum.LOGGER.error("Failed to read block_predicate from network for recipe {}", id); - return null; - } Item result = buffer.readById(BuiltInRegistries.ITEM); if (result == null || result == Items.AIR) { return null; diff --git a/src/main/java/thedarkcolour/exdeorum/recipe/crucible/CrucibleHeatRecipe.java b/src/main/java/thedarkcolour/exdeorum/recipe/crucible/CrucibleHeatRecipe.java new file mode 100644 index 00000000..4b1486f3 --- /dev/null +++ b/src/main/java/thedarkcolour/exdeorum/recipe/crucible/CrucibleHeatRecipe.java @@ -0,0 +1,95 @@ +/* + * Ex Deorum + * Copyright (c) 2024 thedarkcolour + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package thedarkcolour.exdeorum.recipe.crucible; + +import com.google.gson.JsonObject; +import net.minecraft.core.RegistryAccess; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.Container; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Recipe; +import net.minecraft.world.item.crafting.RecipeSerializer; +import net.minecraft.world.item.crafting.RecipeType; +import net.minecraft.world.level.Level; +import thedarkcolour.exdeorum.recipe.BlockPredicate; +import thedarkcolour.exdeorum.recipe.RecipeUtil; +import thedarkcolour.exdeorum.registry.ERecipeSerializers; +import thedarkcolour.exdeorum.registry.ERecipeTypes; + +public record CrucibleHeatRecipe(ResourceLocation id, BlockPredicate blockPredicate, int heatValue) implements Recipe { + @Override + public boolean matches(Container pContainer, Level pLevel) { + return false; + } + + @Override + public ItemStack assemble(Container pContainer, RegistryAccess pRegistryAccess) { + return ItemStack.EMPTY; + } + + @Override + public boolean canCraftInDimensions(int pWidth, int pHeight) { + return false; + } + + @Override + public ItemStack getResultItem(RegistryAccess pRegistryAccess) { + return ItemStack.EMPTY; + } + + @Override + public ResourceLocation getId() { + return this.id; + } + + @Override + public RecipeSerializer getSerializer() { + return ERecipeSerializers.CRUCIBLE_HEAT_SOURCE.get(); + } + + @Override + public RecipeType getType() { + return ERecipeTypes.CRUCIBLE_HEAT_SOURCE.get(); + } + + public static class Serializer implements RecipeSerializer { + @Override + public CrucibleHeatRecipe fromJson(ResourceLocation id, JsonObject json) { + BlockPredicate blockPredicate = RecipeUtil.readBlockPredicate(id, json); + if (blockPredicate == null) return null; + int heatValue = json.get("heat_value").getAsInt(); + return new CrucibleHeatRecipe(id, blockPredicate, heatValue); + } + + @Override + public CrucibleHeatRecipe fromNetwork(ResourceLocation id, FriendlyByteBuf buffer) { + BlockPredicate blockPredicate = RecipeUtil.readBlockPredicateNetwork(id, buffer); + if (blockPredicate == null) return null; + int heatValue = buffer.readVarInt(); + return new CrucibleHeatRecipe(id, blockPredicate, heatValue); + } + + @Override + public void toNetwork(FriendlyByteBuf buffer, CrucibleHeatRecipe recipe) { + recipe.blockPredicate.toNetwork(buffer); + buffer.writeVarInt(recipe.heatValue); + } + } +} diff --git a/src/main/java/thedarkcolour/exdeorum/recipe/crucible/FinishedCrucibleHeatRecipe.java b/src/main/java/thedarkcolour/exdeorum/recipe/crucible/FinishedCrucibleHeatRecipe.java new file mode 100644 index 00000000..ef5af76c --- /dev/null +++ b/src/main/java/thedarkcolour/exdeorum/recipe/crucible/FinishedCrucibleHeatRecipe.java @@ -0,0 +1,54 @@ +/* + * Ex Deorum + * Copyright (c) 2024 thedarkcolour + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package thedarkcolour.exdeorum.recipe.crucible; + +import com.google.gson.JsonObject; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.crafting.RecipeSerializer; +import thedarkcolour.exdeorum.recipe.BlockPredicate; +import thedarkcolour.exdeorum.recipe.EFinishedRecipe; +import thedarkcolour.exdeorum.registry.ERecipeSerializers; + +public class FinishedCrucibleHeatRecipe implements EFinishedRecipe { + private final ResourceLocation id; + private final BlockPredicate blockPredicate; + private final int heatValue; + + public FinishedCrucibleHeatRecipe(ResourceLocation id, BlockPredicate blockPredicate, int heatValue) { + this.id = id; + this.blockPredicate = blockPredicate; + this.heatValue = heatValue; + } + + @Override + public void serializeRecipeData(JsonObject json) { + json.add("block_predicate", this.blockPredicate.toJson()); + json.addProperty("heat_value", this. heatValue); + } + + @Override + public ResourceLocation getId() { + return this.id; + } + + @Override + public RecipeSerializer getType() { + return ERecipeSerializers.CRUCIBLE_HEAT_SOURCE.get(); + } +} diff --git a/src/main/java/thedarkcolour/exdeorum/recipe/crucible/FinishedCrucibleRecipe.java b/src/main/java/thedarkcolour/exdeorum/recipe/crucible/FinishedCrucibleRecipe.java index 14df4005..dbcfad74 100644 --- a/src/main/java/thedarkcolour/exdeorum/recipe/crucible/FinishedCrucibleRecipe.java +++ b/src/main/java/thedarkcolour/exdeorum/recipe/crucible/FinishedCrucibleRecipe.java @@ -44,7 +44,6 @@ public class FinishedCrucibleRecipe implements EFinishedRecipe { this.fluidStack = fluidStack; } - @Override public void serializeRecipeData(JsonObject json) { json.add("ingredient", this.ingredient.toJson()); diff --git a/src/main/java/thedarkcolour/exdeorum/registry/EBlocks.java b/src/main/java/thedarkcolour/exdeorum/registry/EBlocks.java index 39babe66..5f8d5d4f 100644 --- a/src/main/java/thedarkcolour/exdeorum/registry/EBlocks.java +++ b/src/main/java/thedarkcolour/exdeorum/registry/EBlocks.java @@ -172,7 +172,7 @@ public class EBlocks { public static final RegistryObject MAPLE_CRUCIBLE = registerWaterCrucible("maple_crucible"); // Misc - public static final RegistryObject INFESTED_LEAVES = BLOCKS.register("infested_leaves", () -> new InfestedLeavesBlock(copy(Blocks.OAK_LEAVES))); + public static final RegistryObject INFESTED_LEAVES = BLOCKS.register("infested_leaves", () -> new InfestedLeavesBlock(copy(Blocks.OAK_LEAVES).noLootTable())); public static final RegistryObject WITCH_WATER = BLOCKS.register("witch_water", () -> new WitchWaterBlock(EFluids.WITCH_WATER, copy(Blocks.WATER).mapColor(MapColor.COLOR_PURPLE))); public static final RegistryObject END_CAKE = BLOCKS.register("end_cake", () -> new EndCakeBlock(of().noLootTable().mapColor(MapColor.COLOR_BLACK).forceSolidOn().strength(0.5F).sound(SoundType.WOOL).pushReaction(PushReaction.BLOCK))); diff --git a/src/main/java/thedarkcolour/exdeorum/registry/ELootFunctions.java b/src/main/java/thedarkcolour/exdeorum/registry/ELootFunctions.java index 4e73645a..8facd433 100644 --- a/src/main/java/thedarkcolour/exdeorum/registry/ELootFunctions.java +++ b/src/main/java/thedarkcolour/exdeorum/registry/ELootFunctions.java @@ -23,12 +23,10 @@ import net.minecraft.world.level.storage.loot.functions.LootItemFunctionType; import net.minecraftforge.registries.DeferredRegister; import net.minecraftforge.registries.RegistryObject; import thedarkcolour.exdeorum.ExDeorum; -import thedarkcolour.exdeorum.loot.InfestedStringFunction; import thedarkcolour.exdeorum.loot.MachineLootFunction; public class ELootFunctions { public static final DeferredRegister LOOT_FUNCTIONS = DeferredRegister.create(Registries.LOOT_FUNCTION_TYPE, ExDeorum.ID); - public static final RegistryObject INFESTED_STRING = LOOT_FUNCTIONS.register("infested_string", () -> new LootItemFunctionType(new InfestedStringFunction.LootSerializer())); public static final RegistryObject MACHINE = LOOT_FUNCTIONS.register("machine", () -> new LootItemFunctionType(new MachineLootFunction.LootSerializer())); } diff --git a/src/main/java/thedarkcolour/exdeorum/registry/ERecipeSerializers.java b/src/main/java/thedarkcolour/exdeorum/registry/ERecipeSerializers.java index d5061c0c..387a7092 100644 --- a/src/main/java/thedarkcolour/exdeorum/registry/ERecipeSerializers.java +++ b/src/main/java/thedarkcolour/exdeorum/registry/ERecipeSerializers.java @@ -28,6 +28,7 @@ import thedarkcolour.exdeorum.recipe.barrel.BarrelCompostRecipe; import thedarkcolour.exdeorum.recipe.barrel.BarrelFluidMixingRecipe; import thedarkcolour.exdeorum.recipe.barrel.BarrelMixingRecipe; import thedarkcolour.exdeorum.recipe.crook.CrookRecipe; +import thedarkcolour.exdeorum.recipe.crucible.CrucibleHeatRecipe; import thedarkcolour.exdeorum.recipe.crucible.CrucibleRecipe; import thedarkcolour.exdeorum.recipe.hammer.HammerRecipe; import thedarkcolour.exdeorum.recipe.sieve.SieveRecipe; @@ -41,6 +42,7 @@ public class ERecipeSerializers { public static final RegistryObject> HAMMER = RECIPE_SERIALIZERS.register("hammer", HammerRecipe.Serializer::new); public static final RegistryObject> CROOK = RECIPE_SERIALIZERS.register("crook", CrookRecipe.Serializer::new); + public static final RegistryObject> CRUCIBLE_HEAT_SOURCE = RECIPE_SERIALIZERS.register("crucible_heat_source", CrucibleHeatRecipe.Serializer::new); public static final RegistryObject> LAVA_CRUCIBLE = RECIPE_SERIALIZERS.register("lava_crucible", () -> new CrucibleRecipe.Serializer(ERecipeTypes.LAVA_CRUCIBLE.get())); public static final RegistryObject> WATER_CRUCIBLE = RECIPE_SERIALIZERS.register("water_crucible", () -> new CrucibleRecipe.Serializer(ERecipeTypes.WATER_CRUCIBLE.get())); diff --git a/src/main/java/thedarkcolour/exdeorum/registry/ERecipeTypes.java b/src/main/java/thedarkcolour/exdeorum/registry/ERecipeTypes.java index cd841d2a..3bcf1e88 100644 --- a/src/main/java/thedarkcolour/exdeorum/registry/ERecipeTypes.java +++ b/src/main/java/thedarkcolour/exdeorum/registry/ERecipeTypes.java @@ -27,6 +27,7 @@ import thedarkcolour.exdeorum.recipe.barrel.BarrelCompostRecipe; import thedarkcolour.exdeorum.recipe.barrel.BarrelFluidMixingRecipe; import thedarkcolour.exdeorum.recipe.barrel.BarrelMixingRecipe; import thedarkcolour.exdeorum.recipe.crook.CrookRecipe; +import thedarkcolour.exdeorum.recipe.crucible.CrucibleHeatRecipe; import thedarkcolour.exdeorum.recipe.crucible.CrucibleRecipe; import thedarkcolour.exdeorum.recipe.hammer.HammerRecipe; import thedarkcolour.exdeorum.recipe.sieve.SieveRecipe; @@ -43,6 +44,7 @@ public class ERecipeTypes { public static final RegistryObject> HAMMER = RECIPE_TYPES.register("hammer", () -> RecipeType.simple(ERecipeTypes.HAMMER.getId())); public static final RegistryObject> CROOK = RECIPE_TYPES.register("crook", () -> RecipeType.simple(ERecipeTypes.CROOK.getId())); + public static final RegistryObject> CRUCIBLE_HEAT_SOURCE = RECIPE_TYPES.register("crucible_heat_source", () -> RecipeType.simple(ERecipeTypes.CRUCIBLE_HEAT_SOURCE.getId())); public static final RegistryObject> SIEVE = RECIPE_TYPES.register("sieve", () -> RecipeType.simple(ERecipeTypes.SIEVE.getId())); } diff --git a/src/test/java/thedarkcolour/exdeorum/recipe/BlockPredicateTest.java b/src/test/java/thedarkcolour/exdeorum/recipe/BlockPredicateTest.java index f884f257..51d94d4e 100644 --- a/src/test/java/thedarkcolour/exdeorum/recipe/BlockPredicateTest.java +++ b/src/test/java/thedarkcolour/exdeorum/recipe/BlockPredicateTest.java @@ -18,6 +18,8 @@ package thedarkcolour.exdeorum.recipe; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; import io.netty.buffer.Unpooled; import net.minecraft.SharedConstants; import net.minecraft.advancements.critereon.StatePropertiesPredicate; @@ -30,6 +32,8 @@ import net.minecraft.world.level.block.state.properties.BlockStateProperties; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import java.util.stream.Collectors; + import static org.junit.jupiter.api.Assertions.*; class BlockPredicateTest { @@ -39,6 +43,23 @@ class BlockPredicateTest { Bootstrap.bootStrap(); } + @Test + void jsonTest() { + var testPredicate = """ + { + "block": "minecraft:oak_wood", + "state": { + "axis": "y" + } + } + """; + var json = JsonParser.parseString(testPredicate); + var expected = BlockPredicate.blockState(Blocks.OAK_WOOD, StatePropertiesPredicate.Builder.properties().hasProperty(BlockStateProperties.AXIS, Direction.Axis.Y).build()); + var actual = BlockPredicate.fromJson((JsonObject) json); + assertNotNull(actual); + assertEquals(expected.possibleStates().collect(Collectors.toSet()), actual.possibleStates().collect(Collectors.toSet())); + } + @Test void networkTest() { var buffer = new FriendlyByteBuf(Unpooled.buffer()); diff --git a/src/test/java/thedarkcolour/exdeorum/recipe/RecipeSerializationTests.java b/src/test/java/thedarkcolour/exdeorum/recipe/RecipeSerializationTests.java new file mode 100644 index 00000000..1867cebc --- /dev/null +++ b/src/test/java/thedarkcolour/exdeorum/recipe/RecipeSerializationTests.java @@ -0,0 +1,67 @@ +/* + * Ex Deorum + * Copyright (c) 2024 thedarkcolour + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package thedarkcolour.exdeorum.recipe; + +import io.netty.buffer.Unpooled; +import net.minecraft.SharedConstants; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.server.Bootstrap; +import net.minecraft.tags.BlockTags; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.crafting.Recipe; +import net.minecraft.world.item.crafting.RecipeSerializer; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import thedarkcolour.exdeorum.recipe.crook.CrookRecipe; +import thedarkcolour.exdeorum.recipe.crucible.CrucibleHeatRecipe; + +import static org.junit.jupiter.api.Assertions.*; + +// Tests JSON and network serialization/deserialization methods of all recipes to ensure everything works +public class RecipeSerializationTests { + @BeforeEach + void setUp() { + SharedConstants.tryDetectVersion(); + Bootstrap.bootStrap(); + } + + @Test + void crucibleHeatSourceNetwork() { + testNetwork(new CrucibleHeatRecipe(null, BlockPredicate.blockTag(BlockTags.DIRT), 3), new CrucibleHeatRecipe.Serializer()); + } + + @Test + void crucibleHeatSourceJson() { + fail(); + } + + @Test + void crookNetwork() { + testNetwork(new CrookRecipe(null, BlockPredicate.blockTag(BlockTags.DIRT), Items.DIAMOND, 0.0025f), new CrookRecipe.Serializer()); + } + + // Takes a recipe, uses its serializer to write it to a buffer, then reads it + // back and checks if the recipe it got is the same as the one originally written + private static > void testNetwork(T recipe, RecipeSerializer serializer) { + var id = recipe.getId(); + var buffer = new FriendlyByteBuf(Unpooled.buffer()); + serializer.toNetwork(buffer, recipe); + assertEquals(recipe, serializer.fromNetwork(id, buffer)); + } +}