diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DoubleHighBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DoubleHighBlockBehavior.java index 7845bddd2..723af40e4 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DoubleHighBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DoubleHighBlockBehavior.java @@ -6,7 +6,6 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflect import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBlocks; import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.bukkit.util.LocationUtils; -import net.momirealms.craftengine.bukkit.world.BukkitWorld; import net.momirealms.craftengine.core.block.*; import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; import net.momirealms.craftengine.core.block.properties.Property; @@ -14,7 +13,9 @@ import net.momirealms.craftengine.core.block.state.properties.DoubleBlockHalf; import net.momirealms.craftengine.core.item.context.BlockPlaceContext; import net.momirealms.craftengine.core.util.Direction; import net.momirealms.craftengine.core.util.ResourceConfigUtils; -import net.momirealms.craftengine.core.world.*; +import net.momirealms.craftengine.core.world.BlockPos; +import net.momirealms.craftengine.core.world.World; +import net.momirealms.craftengine.core.world.WorldEvents; import java.util.Map; import java.util.concurrent.Callable; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/PressurePlateBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/PressurePlateBlockBehavior.java index 16f852fd2..09c8c920d 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/PressurePlateBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/PressurePlateBlockBehavior.java @@ -8,7 +8,6 @@ import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.bukkit.util.DirectionUtils; import net.momirealms.craftengine.bukkit.util.EventUtils; import net.momirealms.craftengine.bukkit.util.LocationUtils; -import net.momirealms.craftengine.bukkit.world.BukkitWorld; import net.momirealms.craftengine.bukkit.world.BukkitWorldManager; import net.momirealms.craftengine.core.block.BlockBehavior; import net.momirealms.craftengine.core.block.CustomBlock; @@ -20,7 +19,8 @@ import net.momirealms.craftengine.core.util.Direction; import net.momirealms.craftengine.core.util.PressurePlateSensitivity; import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.craftengine.core.util.VersionHelper; -import net.momirealms.craftengine.core.world.*; +import net.momirealms.craftengine.core.world.World; +import net.momirealms.craftengine.core.world.WorldEvents; import org.bukkit.GameEvent; import org.bukkit.util.Vector; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SimpleParticleBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SimpleParticleBlockBehavior.java index d52bda8a4..fa0944580 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SimpleParticleBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SimpleParticleBlockBehavior.java @@ -48,7 +48,7 @@ public class SimpleParticleBlockBehavior extends BukkitBlockBehavior implements } @Override - public BlockEntityTicker createBlockEntityTicker(CEWorld level, ImmutableBlockState state, BlockEntityType blockEntityType) { + public BlockEntityTicker createAsyncBlockEntityTicker(CEWorld level, ImmutableBlockState state, BlockEntityType blockEntityType) { if (this.particles.length == 0) return null; return EntityBlockBehavior.createTickerHelper(SimpleParticleBlockEntity::tick); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/WallTorchParticleBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/WallTorchParticleBlockBehavior.java index 84a2d19fb..85888fc1e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/WallTorchParticleBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/WallTorchParticleBlockBehavior.java @@ -57,7 +57,7 @@ public class WallTorchParticleBlockBehavior extends BukkitBlockBehavior implemen } @Override - public BlockEntityTicker createBlockEntityTicker(CEWorld level, ImmutableBlockState state, BlockEntityType blockEntityType) { + public BlockEntityTicker createAsyncBlockEntityTicker(CEWorld level, ImmutableBlockState state, BlockEntityType blockEntityType) { if (this.particles.length == 0) return null; return EntityBlockBehavior.createTickerHelper(WallTorchParticleBlockEntity::tick); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/RecipeEventListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/RecipeEventListener.java index 6120f6855..58392558e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/RecipeEventListener.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/recipe/RecipeEventListener.java @@ -610,14 +610,19 @@ public class RecipeEventListener implements Listener { inventory.setResult(null); return; } - CraftingInput input = getCraftingInput(inventory); - if (input == null) return; Player player = InventoryUtils.getPlayerFromInventoryEvent(event); BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player); + ItemBuildContext itemBuildContext = ItemBuildContext.of(serverPlayer); + if (!craftingTableRecipe.canUse(itemBuildContext)) { + inventory.setResult(null); + return; + } + CraftingInput input = getCraftingInput(inventory); + if (input == null) return; if (craftingTableRecipe.hasVisualResult() && VersionHelper.PREMIUM) { - inventory.setResult(craftingTableRecipe.assembleVisual(input, ItemBuildContext.of(serverPlayer))); + inventory.setResult(craftingTableRecipe.assembleVisual(input, itemBuildContext)); } else { - inventory.setResult(craftingTableRecipe.assemble(input, ItemBuildContext.of(serverPlayer))); + inventory.setResult(craftingTableRecipe.assemble(input, itemBuildContext)); } } @@ -638,7 +643,7 @@ public class RecipeEventListener implements Listener { } Player player = InventoryUtils.getPlayerFromInventoryEvent(event); BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player); - if (!craftingTableRecipe.hasVisualResult()) { + if (craftingTableRecipe.hasVisualResult()) { CraftingInput input = getCraftingInput(inventory); inventory.setResult(craftingTableRecipe.assemble(input, ItemBuildContext.of(serverPlayer))); } @@ -697,10 +702,16 @@ public class RecipeEventListener implements Listener { event.setResult(null); return; } + Player player = InventoryUtils.getPlayerFromInventoryEvent(event); + ItemBuildContext itemBuildContext = ItemBuildContext.of(BukkitAdaptors.adapt(player)); + if (!smithingTrimRecipe.canUse(itemBuildContext)) { + event.setResult(null); + return; + } + SmithingInput input = getSmithingInput(inventory); if (smithingTrimRecipe.matches(input)) { - Player player = InventoryUtils.getPlayerFromInventoryEvent(event); - ItemStack result = smithingTrimRecipe.assemble(getSmithingInput(inventory), ItemBuildContext.of(BukkitAdaptors.adapt(player))); + ItemStack result = smithingTrimRecipe.assemble(getSmithingInput(inventory), itemBuildContext); event.setResult(result); } else { event.setResult(null); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/WorldStorageInjector.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/WorldStorageInjector.java index 430683add..b1e7d7ff1 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/WorldStorageInjector.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/injector/WorldStorageInjector.java @@ -14,7 +14,6 @@ import net.bytebuddy.implementation.bytecode.assign.Assigner; import net.bytebuddy.matcher.ElementMatchers; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; -import net.momirealms.craftengine.bukkit.util.LocationUtils; import net.momirealms.craftengine.core.block.BlockStateWrapper; import net.momirealms.craftengine.core.block.DelegatingBlockState; import net.momirealms.craftengine.core.block.EmptyBlock; diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/ImmutableBlockState.java b/core/src/main/java/net/momirealms/craftengine/core/block/ImmutableBlockState.java index 8bdeb7115..18e6460d2 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/ImmutableBlockState.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/ImmutableBlockState.java @@ -152,9 +152,16 @@ public final class ImmutableBlockState extends BlockStateHolder { } @SuppressWarnings("unchecked") - public BlockEntityTicker createBlockEntityTicker(CEWorld world, BlockEntityType type) { + public BlockEntityTicker createSyncBlockEntityTicker(CEWorld world, BlockEntityType type) { EntityBlockBehavior blockBehavior = this.behavior.getEntityBehavior(); if (blockBehavior == null) return null; - return (BlockEntityTicker) blockBehavior.createBlockEntityTicker(world, this, type); + return (BlockEntityTicker) blockBehavior.createSyncBlockEntityTicker(world, this, type); + } + + @SuppressWarnings("unchecked") + public BlockEntityTicker createAsyncBlockEntityTicker(CEWorld world, BlockEntityType type) { + EntityBlockBehavior blockBehavior = this.behavior.getEntityBehavior(); + if (blockBehavior == null) return null; + return (BlockEntityTicker) blockBehavior.createAsyncBlockEntityTicker(world, this, type); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/behavior/EntityBlockBehavior.java b/core/src/main/java/net/momirealms/craftengine/core/block/behavior/EntityBlockBehavior.java index f92b0e59a..2eb34bae7 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/behavior/EntityBlockBehavior.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/behavior/EntityBlockBehavior.java @@ -15,7 +15,11 @@ public interface EntityBlockBehavior { BlockEntity createBlockEntity(BlockPos pos, ImmutableBlockState state); - default BlockEntityTicker createBlockEntityTicker(CEWorld level, ImmutableBlockState state, BlockEntityType blockEntityType) { + default BlockEntityTicker createSyncBlockEntityTicker(CEWorld level, ImmutableBlockState state, BlockEntityType blockEntityType) { + return null; + } + + default BlockEntityTicker createAsyncBlockEntityTicker(CEWorld level, ImmutableBlockState state, BlockEntityType blockEntityType) { return null; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeSerializer.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeSerializer.java index 8f4632676..a95d06e39 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeSerializer.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/AbstractRecipeSerializer.java @@ -9,7 +9,10 @@ import net.momirealms.craftengine.core.item.recipe.result.CustomRecipeResult; import net.momirealms.craftengine.core.item.recipe.result.PostProcessor; import net.momirealms.craftengine.core.item.recipe.result.PostProcessors; import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.context.Condition; import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; +import net.momirealms.craftengine.core.plugin.context.condition.AllOfCondition; +import net.momirealms.craftengine.core.plugin.context.event.EventConditions; import net.momirealms.craftengine.core.plugin.context.event.EventFunctions; import net.momirealms.craftengine.core.plugin.context.function.Function; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; @@ -29,12 +32,21 @@ public abstract class AbstractRecipeSerializer> implement @SuppressWarnings("unchecked") protected Function[] functions(Map arguments) { - Object functions = arguments.get("functions"); + Object functions = ResourceConfigUtils.get(arguments, "functions", "function"); if (functions == null) return null; List> functionList = ResourceConfigUtils.parseConfigAsList(functions, EventFunctions::fromMap); return functionList.toArray(new Function[0]); } + protected Condition conditions(Map arguments) { + Object functions = ResourceConfigUtils.get(arguments, "conditions", "condition"); + if (functions == null) return null; + List> conditionList = ResourceConfigUtils.parseConfigAsList(functions, EventConditions::fromMap); + if (conditionList.isEmpty()) return null; + if (conditionList.size() == 1) return conditionList.getFirst(); + return new AllOfCondition<>(conditionList); + } + protected boolean showNotification(Map arguments) { return ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("show-notification", true), "show-notification"); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/ConditionalRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/ConditionalRecipe.java new file mode 100644 index 000000000..fd0a8b649 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/ConditionalRecipe.java @@ -0,0 +1,8 @@ +package net.momirealms.craftengine.core.item.recipe; + +import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; + +public interface ConditionalRecipe { + + boolean canUse(final PlayerOptionalContext context); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomCraftingTableRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomCraftingTableRecipe.java index d6cb7b67a..9445e2472 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomCraftingTableRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomCraftingTableRecipe.java @@ -4,15 +4,17 @@ import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.ItemBuildContext; import net.momirealms.craftengine.core.item.recipe.input.RecipeInput; import net.momirealms.craftengine.core.item.recipe.result.CustomRecipeResult; +import net.momirealms.craftengine.core.plugin.context.Condition; import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; import net.momirealms.craftengine.core.plugin.context.function.Function; import net.momirealms.craftengine.core.util.Key; import org.jetbrains.annotations.Nullable; -public abstract class CustomCraftingTableRecipe extends AbstractGroupedRecipe { +public abstract class CustomCraftingTableRecipe extends AbstractGroupedRecipe implements ConditionalRecipe { protected final CraftingRecipeCategory category; private final CustomRecipeResult visualResult; private final Function[] craftingFunctions; + private final Condition craftingCondition; protected CustomCraftingTableRecipe(Key id, boolean showNotification, @@ -20,11 +22,20 @@ public abstract class CustomCraftingTableRecipe extends AbstractGroupedRecipe @Nullable CustomRecipeResult visualResult, String group, CraftingRecipeCategory category, - Function[] craftingFunctions) { + Function[] craftingFunctions, + Condition craftingCondition) { super(id, showNotification, result, group); this.category = category == null ? CraftingRecipeCategory.MISC : category; this.visualResult = visualResult; this.craftingFunctions = craftingFunctions; + this.craftingCondition = craftingCondition; + } + + + @Override + public boolean canUse(PlayerOptionalContext context) { + if (this.craftingCondition == null) return true; + return this.craftingCondition.test(context); } public CraftingRecipeCategory category() { diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomShapedRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomShapedRecipe.java index bad93556a..75613d5d8 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomShapedRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomShapedRecipe.java @@ -5,8 +5,8 @@ import com.google.gson.JsonObject; import net.momirealms.craftengine.core.item.recipe.input.CraftingInput; import net.momirealms.craftengine.core.item.recipe.input.RecipeInput; import net.momirealms.craftengine.core.item.recipe.result.CustomRecipeResult; +import net.momirealms.craftengine.core.plugin.context.Condition; import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; -import net.momirealms.craftengine.core.plugin.context.event.EventFunctions; import net.momirealms.craftengine.core.plugin.context.function.Function; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.util.Key; @@ -28,8 +28,9 @@ public class CustomShapedRecipe extends CustomCraftingTableRecipe { String group, CraftingRecipeCategory category, Pattern pattern, - Function[] craftingFunctions) { - super(id, showNotification, result, visualResult, group, category, craftingFunctions); + Function[] craftingFunctions, + Condition craftingCondition) { + super(id, showNotification, result, visualResult, group, category, craftingFunctions, craftingCondition); this.pattern = pattern; this.parsedPattern = pattern.parse(); } @@ -174,7 +175,8 @@ public class CustomShapedRecipe extends CustomCraftingTableRecipe { parseVisualResult(arguments), arguments.containsKey("group") ? arguments.get("group").toString() : null, craftingRecipeCategory(arguments), new Pattern<>(pattern.toArray(new String[0]), ingredients), - functions(arguments) + functions(arguments), + conditions(arguments) ); } @@ -188,6 +190,7 @@ public class CustomShapedRecipe extends CustomCraftingTableRecipe { VANILLA_RECIPE_HELPER.readGroup(json), VANILLA_RECIPE_HELPER.craftingCategory(json), new Pattern<>(VANILLA_RECIPE_HELPER.craftingShapedPattern(json), ingredients), + null, null ); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomShapelessRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomShapelessRecipe.java index a23c0c4bf..a60c241a4 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomShapelessRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomShapelessRecipe.java @@ -4,6 +4,7 @@ import com.google.gson.JsonObject; import net.momirealms.craftengine.core.item.recipe.input.CraftingInput; import net.momirealms.craftengine.core.item.recipe.input.RecipeInput; import net.momirealms.craftengine.core.item.recipe.result.CustomRecipeResult; +import net.momirealms.craftengine.core.plugin.context.Condition; import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; import net.momirealms.craftengine.core.plugin.context.function.Function; import net.momirealms.craftengine.core.util.Key; @@ -26,8 +27,9 @@ public class CustomShapelessRecipe extends CustomCraftingTableRecipe { String group, CraftingRecipeCategory category, List> ingredients, - Function[] craftingFunctions) { - super(id, showNotification, result, visualResult, group, category, craftingFunctions); + Function[] craftingFunctions, + Condition craftingCondition) { + super(id, showNotification, result, visualResult, group, category, craftingFunctions, craftingCondition); this.ingredients = ingredients; this.placementInfo = PlacementInfo.create(ingredients); } @@ -92,7 +94,8 @@ public class CustomShapelessRecipe extends CustomCraftingTableRecipe { parseVisualResult(arguments), arguments.containsKey("group") ? arguments.get("group").toString() : null, craftingRecipeCategory(arguments), ingredients, - functions(arguments) + functions(arguments), + conditions(arguments) ); } @@ -104,6 +107,7 @@ public class CustomShapelessRecipe extends CustomCraftingTableRecipe { null, VANILLA_RECIPE_HELPER.readGroup(json), VANILLA_RECIPE_HELPER.craftingCategory(json), VANILLA_RECIPE_HELPER.shapelessIngredients(json.getAsJsonArray("ingredients")).stream().map(this::toIngredient).toList(), + null, null ); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTransformRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTransformRecipe.java index 9ebe583a7..852b8ff8e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTransformRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTransformRecipe.java @@ -7,6 +7,8 @@ import net.momirealms.craftengine.core.item.recipe.input.RecipeInput; import net.momirealms.craftengine.core.item.recipe.input.SmithingInput; import net.momirealms.craftengine.core.item.recipe.result.CustomRecipeResult; import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.context.Condition; +import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Registries; @@ -20,13 +22,14 @@ import java.util.List; import java.util.Map; import java.util.Objects; -public class CustomSmithingTransformRecipe extends AbstractedFixedResultRecipe { +public class CustomSmithingTransformRecipe extends AbstractedFixedResultRecipe implements ConditionalRecipe { public static final Serializer SERIALIZER = new Serializer<>(); private final Ingredient base; private final Ingredient template; private final Ingredient addition; private final boolean mergeComponents; private final List processors; + private final Condition condition; public CustomSmithingTransformRecipe(Key id, boolean showNotification, @@ -35,7 +38,8 @@ public class CustomSmithingTransformRecipe extends AbstractedFixedResultRecip @Nullable Ingredient addition, CustomRecipeResult result, List processors, - boolean mergeComponents + boolean mergeComponents, + Condition condition ) { super(id, showNotification, result); this.base = base; @@ -43,6 +47,13 @@ public class CustomSmithingTransformRecipe extends AbstractedFixedResultRecip this.addition = addition; this.processors = processors; this.mergeComponents = mergeComponents; + this.condition = condition; + } + + @Override + public boolean canUse(PlayerOptionalContext context) { + if (this.condition != null) return this.condition.test(context); + return true; } @SuppressWarnings("unchecked") @@ -140,14 +151,23 @@ public class CustomSmithingTransformRecipe extends AbstractedFixedResultRecip toIngredient(addition), parseResult(arguments), ItemDataProcessors.fromMapList(processors), - mergeComponents + mergeComponents, + conditions(arguments) ); } @Override public CustomSmithingTransformRecipe readJson(Key id, JsonObject json) { - return new CustomSmithingTransformRecipe<>(id, - true, toIngredient(VANILLA_RECIPE_HELPER.singleIngredient(json.get("template"))), Objects.requireNonNull(toIngredient(VANILLA_RECIPE_HELPER.singleIngredient(json.get("base")))), toIngredient(VANILLA_RECIPE_HELPER.singleIngredient(json.get("addition"))), parseResult(VANILLA_RECIPE_HELPER.smithingResult(json.getAsJsonObject("result"))), null, true + return new CustomSmithingTransformRecipe<>( + id, + true, + toIngredient(VANILLA_RECIPE_HELPER.singleIngredient(json.get("template"))), + Objects.requireNonNull(toIngredient(VANILLA_RECIPE_HELPER.singleIngredient(json.get("base")))), + toIngredient(VANILLA_RECIPE_HELPER.singleIngredient(json.get("addition"))), + parseResult(VANILLA_RECIPE_HELPER.smithingResult(json.getAsJsonObject("result"))), + null, + true, + null ); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTrimRecipe.java b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTrimRecipe.java index 1d1195624..020a5d460 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTrimRecipe.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/recipe/CustomSmithingTrimRecipe.java @@ -6,6 +6,8 @@ import net.momirealms.craftengine.core.item.ItemBuildContext; import net.momirealms.craftengine.core.item.recipe.input.RecipeInput; import net.momirealms.craftengine.core.item.recipe.input.SmithingInput; import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.context.Condition; +import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.ResourceConfigUtils; @@ -18,31 +20,41 @@ import java.util.List; import java.util.Map; import java.util.Objects; -public class CustomSmithingTrimRecipe extends AbstractRecipe { +public class CustomSmithingTrimRecipe extends AbstractRecipe implements ConditionalRecipe { public static final Serializer SERIALIZER = new Serializer<>(); private final Ingredient base; private final Ingredient template; private final Ingredient addition; @Nullable // 1.21.5 private final Key pattern; + @Nullable + private final Condition condition; public CustomSmithingTrimRecipe(@NotNull Key id, boolean showNotification, @NotNull Ingredient template, @NotNull Ingredient base, @NotNull Ingredient addition, - @Nullable Key pattern + @Nullable Key pattern, + @Nullable Condition condition ) { super(id, showNotification); this.base = base; this.template = template; this.addition = addition; this.pattern = pattern; + this.condition = condition; if (pattern == null && VersionHelper.isOrAbove1_21_5()) { throw new IllegalStateException("SmithingTrimRecipe cannot have a null pattern on 1.21.5 and above."); } } + @Override + public boolean canUse(PlayerOptionalContext context) { + if (this.condition != null) return this.condition.test(context); + return true; + } + @SuppressWarnings("unchecked") @Override public T assemble(RecipeInput input, ItemBuildContext context) { @@ -122,7 +134,8 @@ public class CustomSmithingTrimRecipe extends AbstractRecipe { ResourceConfigUtils.requireNonNullOrThrow(toIngredient(template), "warning.config.recipe.smithing_trim.missing_template_type"), ResourceConfigUtils.requireNonNullOrThrow(toIngredient(base), "warning.config.recipe.smithing_trim.missing_base"), ResourceConfigUtils.requireNonNullOrThrow(toIngredient(addition), "warning.config.recipe.smithing_trim.missing_addition"), - pattern + pattern, + conditions(arguments) ); } @@ -133,7 +146,8 @@ public class CustomSmithingTrimRecipe extends AbstractRecipe { Objects.requireNonNull(toIngredient(VANILLA_RECIPE_HELPER.singleIngredient(json.get("template")))), Objects.requireNonNull(toIngredient(VANILLA_RECIPE_HELPER.singleIngredient(json.get("base")))), Objects.requireNonNull(toIngredient(VANILLA_RECIPE_HELPER.singleIngredient(json.get("addition")))), - VersionHelper.isOrAbove1_21_5() ? Key.of(json.get("pattern").getAsString()) : null + VersionHelper.isOrAbove1_21_5() ? Key.of(json.get("pattern").getAsString()) : null, + null ); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/CEWorld.java b/core/src/main/java/net/momirealms/craftengine/core/world/CEWorld.java index ac187bb3a..60098466b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/CEWorld.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/CEWorld.java @@ -25,9 +25,12 @@ public abstract class CEWorld { protected final WorldHeight worldHeightAccessor; protected List pendingLightSections = new ArrayList<>(); protected final Set lightSections = ConcurrentHashMap.newKeySet(128); - protected final BlockEntityTickersList tickingBlockEntities = new BlockEntityTickersList(); - protected final List pendingTickingBlockEntities = new ArrayList<>(); - protected volatile boolean isTickingBlockEntities = false; + protected final BlockEntityTickersList tickingSyncBlockEntities = new BlockEntityTickersList(); + protected final List pendingSyncTickingBlockEntities = new ArrayList<>(); + protected final BlockEntityTickersList tickingAsyncBlockEntities = new BlockEntityTickersList(); + protected final List pendingAsyncTickingBlockEntities = new ArrayList<>(); + protected volatile boolean isTickingSyncBlockEntities = false; + protected volatile boolean isTickingAsyncBlockEntities = false; protected volatile boolean isUpdatingLights = false; protected SchedulerTask syncTickTask; protected SchedulerTask asyncTickTask; @@ -176,13 +179,14 @@ public abstract class CEWorld { } public void syncTick() { - this.tickBlockEntities(); + this.tickSyncBlockEntities(); if (!Config.asyncLightUpdate()) { this.updateLight(); } } public void asyncTick() { + this.tickAsyncBlockEntities(); if (Config.asyncLightUpdate()) { this.updateLight(); } @@ -190,32 +194,61 @@ public abstract class CEWorld { public abstract void updateLight(); - public void addBlockEntityTicker(TickingBlockEntity ticker) { - if (this.isTickingBlockEntities) { - this.pendingTickingBlockEntities.add(ticker); + public void addSyncBlockEntityTicker(TickingBlockEntity ticker) { + if (this.isTickingSyncBlockEntities) { + this.pendingSyncTickingBlockEntities.add(ticker); } else { - this.tickingBlockEntities.add(ticker); + this.tickingSyncBlockEntities.add(ticker); } } - protected void tickBlockEntities() { - this.isTickingBlockEntities = true; - if (!this.pendingTickingBlockEntities.isEmpty()) { - this.tickingBlockEntities.addAll(this.pendingTickingBlockEntities); - this.pendingTickingBlockEntities.clear(); + public void addAsyncBlockEntityTicker(TickingBlockEntity ticker) { + if (this.isTickingAsyncBlockEntities) { + this.pendingAsyncTickingBlockEntities.add(ticker); + } else { + this.tickingAsyncBlockEntities.add(ticker); } - if (!this.tickingBlockEntities.isEmpty()) { - Object[] entities = this.tickingBlockEntities.elements(); - for (int i = 0, size = this.tickingBlockEntities.size(); i < size; i++) { + } + + protected void tickSyncBlockEntities() { + this.isTickingSyncBlockEntities = true; + if (!this.pendingSyncTickingBlockEntities.isEmpty()) { + this.tickingSyncBlockEntities.addAll(this.pendingSyncTickingBlockEntities); + this.pendingSyncTickingBlockEntities.clear(); + } + if (!this.tickingSyncBlockEntities.isEmpty()) { + Object[] entities = this.tickingSyncBlockEntities.elements(); + for (int i = 0, size = this.tickingSyncBlockEntities.size(); i < size; i++) { TickingBlockEntity entity = (TickingBlockEntity) entities[i]; if (entity.isValid()) { entity.tick(); } else { - this.tickingBlockEntities.markAsRemoved(i); + this.tickingSyncBlockEntities.markAsRemoved(i); } } - this.tickingBlockEntities.removeMarkedEntries(); + this.tickingSyncBlockEntities.removeMarkedEntries(); } - this.isTickingBlockEntities = false; + this.isTickingSyncBlockEntities = false; + } + + protected void tickAsyncBlockEntities() { + this.isTickingAsyncBlockEntities = true; + if (!this.pendingAsyncTickingBlockEntities.isEmpty()) { + this.tickingAsyncBlockEntities.addAll(this.pendingAsyncTickingBlockEntities); + this.pendingAsyncTickingBlockEntities.clear(); + } + if (!this.tickingAsyncBlockEntities.isEmpty()) { + Object[] entities = this.tickingAsyncBlockEntities.elements(); + for (int i = 0, size = this.tickingAsyncBlockEntities.size(); i < size; i++) { + TickingBlockEntity entity = (TickingBlockEntity) entities[i]; + if (entity.isValid()) { + entity.tick(); + } else { + this.tickingAsyncBlockEntities.markAsRemoved(i); + } + } + this.tickingAsyncBlockEntities.removeMarkedEntries(); + } + this.isTickingAsyncBlockEntities = false; } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CEChunk.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CEChunk.java index 442ac8999..d48e09472 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CEChunk.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/CEChunk.java @@ -3,6 +3,7 @@ package net.momirealms.craftengine.core.world.chunk; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import net.momirealms.craftengine.core.block.EmptyBlock; import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.block.behavior.EntityBlockBehavior; import net.momirealms.craftengine.core.block.entity.BlockEntity; import net.momirealms.craftengine.core.block.entity.render.ConstantBlockEntityRenderer; import net.momirealms.craftengine.core.block.entity.render.DynamicBlockEntityRenderer; @@ -27,7 +28,8 @@ public class CEChunk { public final CESection[] sections; public final WorldHeight worldHeightAccessor; public final Map blockEntities; // 从区域线程上访问,安全 - public final Map tickingBlockEntitiesByPos; // 从区域线程上访问,安全 + public final Map tickingSyncBlockEntitiesByPos; // 从区域线程上访问,安全 + public final Map tickingAsyncBlockEntitiesByPos; // 从区域线程上访问,安全 public final Map constantBlockEntityRenderers; // 会从区域线程上读写,netty线程上读取 public final Map dynamicBlockEntityRenderers; // 会从区域线程上读写,netty线程上读取 private final ReentrantReadWriteLock renderLock = new ReentrantReadWriteLock(); @@ -42,7 +44,8 @@ public class CEChunk { this.blockEntities = new Object2ObjectOpenHashMap<>(10, 0.5f); this.constantBlockEntityRenderers = new Object2ObjectOpenHashMap<>(10, 0.5f); this.dynamicBlockEntityRenderers = new Object2ObjectOpenHashMap<>(10, 0.5f); - this.tickingBlockEntitiesByPos = new Object2ObjectOpenHashMap<>(10, 0.5f); + this.tickingSyncBlockEntitiesByPos = new Object2ObjectOpenHashMap<>(10, 0.5f); + this.tickingAsyncBlockEntitiesByPos = new Object2ObjectOpenHashMap<>(10, 0.5f); this.fillEmptySection(); } @@ -51,7 +54,8 @@ public class CEChunk { this.chunkPos = chunkPos; this.worldHeightAccessor = world.worldHeight(); this.dynamicBlockEntityRenderers = new Object2ObjectOpenHashMap<>(10, 0.5f); - this.tickingBlockEntitiesByPos = new Object2ObjectOpenHashMap<>(10, 0.5f); + this.tickingSyncBlockEntitiesByPos = new Object2ObjectOpenHashMap<>(10, 0.5f); + this.tickingAsyncBlockEntitiesByPos = new Object2ObjectOpenHashMap<>(10, 0.5f); int sectionCount = this.worldHeightAccessor.getSectionsCount(); this.sections = new CESection[sectionCount]; if (sections != null) { @@ -198,27 +202,51 @@ public class CEChunk { this.blockEntities.values().forEach(e -> e.setValid(false)); this.constantBlockEntityRenderers.values().forEach(ConstantBlockEntityRenderer::deactivate); this.dynamicBlockEntityRenderers.clear(); - this.tickingBlockEntitiesByPos.values().forEach((ticker) -> ticker.setTicker(DummyTickingBlockEntity.INSTANCE)); - this.tickingBlockEntitiesByPos.clear(); + this.tickingSyncBlockEntitiesByPos.values().forEach((ticker) -> ticker.setTicker(DummyTickingBlockEntity.INSTANCE)); + this.tickingSyncBlockEntitiesByPos.clear(); + this.tickingAsyncBlockEntitiesByPos.values().forEach((ticker) -> ticker.setTicker(DummyTickingBlockEntity.INSTANCE)); + this.tickingAsyncBlockEntitiesByPos.clear(); } + @SuppressWarnings("unchecked") public void replaceOrCreateTickingBlockEntity(T blockEntity) { ImmutableBlockState blockState = blockEntity.blockState(); - BlockEntityTicker ticker = blockState.createBlockEntityTicker(this.world, blockEntity.type()); - if (ticker != null) { - this.tickingBlockEntitiesByPos.compute(blockEntity.pos(), ((pos, previousTicker) -> { - TickingBlockEntity newTicker = new TickingBlockEntityImpl<>(this, blockEntity, ticker); - if (previousTicker != null) { - previousTicker.setTicker(newTicker); - return previousTicker; - } else { - ReplaceableTickingBlockEntity replaceableTicker = new ReplaceableTickingBlockEntity(newTicker); - this.world.addBlockEntityTicker(replaceableTicker); - return replaceableTicker; - } - })); - } else { + EntityBlockBehavior blockBehavior = blockState.behavior().getEntityBehavior(); + if (blockBehavior == null) { this.removeBlockEntityTicker(blockEntity.pos()); + } else { + BlockEntityTicker syncTicker = (BlockEntityTicker) blockBehavior.createSyncBlockEntityTicker(this.world, blockState, blockEntity.type()); + if (syncTicker != null) { + this.tickingSyncBlockEntitiesByPos.compute(blockEntity.pos(), ((pos, previousTicker) -> { + TickingBlockEntity newTicker = new TickingBlockEntityImpl<>(this, blockEntity, syncTicker); + if (previousTicker != null) { + previousTicker.setTicker(newTicker); + return previousTicker; + } else { + ReplaceableTickingBlockEntity replaceableTicker = new ReplaceableTickingBlockEntity(newTicker); + this.world.addSyncBlockEntityTicker(replaceableTicker); + return replaceableTicker; + } + })); + } else { + this.removeSyncBlockEntityTicker(blockEntity.pos()); + } + BlockEntityTicker asyncTicker = (BlockEntityTicker) blockBehavior.createAsyncBlockEntityTicker(this.world, blockState, blockEntity.type()); + if (asyncTicker != null) { + this.tickingAsyncBlockEntitiesByPos.compute(blockEntity.pos(), ((pos, previousTicker) -> { + TickingBlockEntity newTicker = new TickingBlockEntityImpl<>(this, blockEntity, asyncTicker); + if (previousTicker != null) { + previousTicker.setTicker(newTicker); + return previousTicker; + } else { + ReplaceableTickingBlockEntity replaceableTicker = new ReplaceableTickingBlockEntity(newTicker); + this.world.addAsyncBlockEntityTicker(replaceableTicker); + return replaceableTicker; + } + })); + } else { + this.removeAsyncBlockEntityTicker(blockEntity.pos()); + } } } @@ -250,13 +278,25 @@ public class CEChunk { } } - private void removeBlockEntityTicker(BlockPos pos) { - ReplaceableTickingBlockEntity blockEntity = this.tickingBlockEntitiesByPos.remove(pos); - if (blockEntity != null) { - blockEntity.setTicker(DummyTickingBlockEntity.INSTANCE); + private void removeSyncBlockEntityTicker(BlockPos pos) { + ReplaceableTickingBlockEntity e1 = this.tickingSyncBlockEntitiesByPos.remove(pos); + if (e1 != null) { + e1.setTicker(DummyTickingBlockEntity.INSTANCE); } } + private void removeAsyncBlockEntityTicker(BlockPos pos) { + ReplaceableTickingBlockEntity e2 = this.tickingAsyncBlockEntitiesByPos.remove(pos); + if (e2 != null) { + e2.setTicker(DummyTickingBlockEntity.INSTANCE); + } + } + + private void removeBlockEntityTicker(BlockPos pos) { + removeSyncBlockEntityTicker(pos); + removeAsyncBlockEntityTicker(pos); + } + public void setBlockEntity(BlockEntity blockEntity) { BlockPos pos = blockEntity.pos(); ImmutableBlockState blockState = this.getBlockState(pos); diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/packet/MCSection.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/packet/MCSection.java index 9967eb586..81948b867 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/packet/MCSection.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/packet/MCSection.java @@ -3,7 +3,6 @@ package net.momirealms.craftengine.core.world.chunk.packet; import net.momirealms.craftengine.core.util.FriendlyByteBuf; import net.momirealms.craftengine.core.util.IndexedIterable; import net.momirealms.craftengine.core.world.chunk.PalettedContainer; -import net.momirealms.craftengine.core.world.chunk.ReadableContainer; public class MCSection { private short nonEmptyBlockCount; diff --git a/gradle.properties b/gradle.properties index 873f9bbbc..3dfd4ae54 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,7 +2,7 @@ org.gradle.jvmargs=-Xmx1G # Project settings # Rule: [major update].[feature update].[bug fix] -project_version=0.0.63.1 +project_version=0.0.63.2 config_version=45 lang_version=29 project_group=net.momirealms