9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-25 01:49:30 +00:00

添加异步方块实体tick

This commit is contained in:
XiaoMoMi
2025-09-17 19:55:52 +08:00
parent e70ddcfe24
commit ca25e7b792
19 changed files with 246 additions and 80 deletions

View File

@@ -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;

View File

@@ -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;

View File

@@ -48,7 +48,7 @@ public class SimpleParticleBlockBehavior extends BukkitBlockBehavior implements
}
@Override
public <T extends BlockEntity> BlockEntityTicker<T> createBlockEntityTicker(CEWorld level, ImmutableBlockState state, BlockEntityType<T> blockEntityType) {
public <T extends BlockEntity> BlockEntityTicker<T> createAsyncBlockEntityTicker(CEWorld level, ImmutableBlockState state, BlockEntityType<T> blockEntityType) {
if (this.particles.length == 0) return null;
return EntityBlockBehavior.createTickerHelper(SimpleParticleBlockEntity::tick);
}

View File

@@ -57,7 +57,7 @@ public class WallTorchParticleBlockBehavior extends BukkitBlockBehavior implemen
}
@Override
public <T extends BlockEntity> BlockEntityTicker<T> createBlockEntityTicker(CEWorld level, ImmutableBlockState state, BlockEntityType<T> blockEntityType) {
public <T extends BlockEntity> BlockEntityTicker<T> createAsyncBlockEntityTicker(CEWorld level, ImmutableBlockState state, BlockEntityType<T> blockEntityType) {
if (this.particles.length == 0) return null;
return EntityBlockBehavior.createTickerHelper(WallTorchParticleBlockEntity::tick);
}

View File

@@ -610,14 +610,19 @@ public class RecipeEventListener implements Listener {
inventory.setResult(null);
return;
}
CraftingInput<ItemStack> 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<ItemStack> 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<ItemStack> 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<ItemStack> 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);

View File

@@ -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;

View File

@@ -152,9 +152,16 @@ public final class ImmutableBlockState extends BlockStateHolder {
}
@SuppressWarnings("unchecked")
public <T extends BlockEntity> BlockEntityTicker<T> createBlockEntityTicker(CEWorld world, BlockEntityType<? extends BlockEntity> type) {
public <T extends BlockEntity> BlockEntityTicker<T> createSyncBlockEntityTicker(CEWorld world, BlockEntityType<? extends BlockEntity> type) {
EntityBlockBehavior blockBehavior = this.behavior.getEntityBehavior();
if (blockBehavior == null) return null;
return (BlockEntityTicker<T>) blockBehavior.createBlockEntityTicker(world, this, type);
return (BlockEntityTicker<T>) blockBehavior.createSyncBlockEntityTicker(world, this, type);
}
@SuppressWarnings("unchecked")
public <T extends BlockEntity> BlockEntityTicker<T> createAsyncBlockEntityTicker(CEWorld world, BlockEntityType<? extends BlockEntity> type) {
EntityBlockBehavior blockBehavior = this.behavior.getEntityBehavior();
if (blockBehavior == null) return null;
return (BlockEntityTicker<T>) blockBehavior.createAsyncBlockEntityTicker(world, this, type);
}
}

View File

@@ -15,7 +15,11 @@ public interface EntityBlockBehavior {
BlockEntity createBlockEntity(BlockPos pos, ImmutableBlockState state);
default <T extends BlockEntity> BlockEntityTicker<T> createBlockEntityTicker(CEWorld level, ImmutableBlockState state, BlockEntityType<T> blockEntityType) {
default <T extends BlockEntity> BlockEntityTicker<T> createSyncBlockEntityTicker(CEWorld level, ImmutableBlockState state, BlockEntityType<T> blockEntityType) {
return null;
}
default <T extends BlockEntity> BlockEntityTicker<T> createAsyncBlockEntityTicker(CEWorld level, ImmutableBlockState state, BlockEntityType<T> blockEntityType) {
return null;
}

View File

@@ -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<T, R extends Recipe<T>> implement
@SuppressWarnings("unchecked")
protected Function<PlayerOptionalContext>[] functions(Map<String, Object> arguments) {
Object functions = arguments.get("functions");
Object functions = ResourceConfigUtils.get(arguments, "functions", "function");
if (functions == null) return null;
List<Function<PlayerOptionalContext>> functionList = ResourceConfigUtils.parseConfigAsList(functions, EventFunctions::fromMap);
return functionList.toArray(new Function[0]);
}
protected Condition<PlayerOptionalContext> conditions(Map<String, Object> arguments) {
Object functions = ResourceConfigUtils.get(arguments, "conditions", "condition");
if (functions == null) return null;
List<Condition<PlayerOptionalContext>> 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<String, Object> arguments) {
return ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("show-notification", true), "show-notification");
}

View File

@@ -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);
}

View File

@@ -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<T> extends AbstractGroupedRecipe<T> {
public abstract class CustomCraftingTableRecipe<T> extends AbstractGroupedRecipe<T> implements ConditionalRecipe {
protected final CraftingRecipeCategory category;
private final CustomRecipeResult<T> visualResult;
private final Function<PlayerOptionalContext>[] craftingFunctions;
private final Condition<PlayerOptionalContext> craftingCondition;
protected CustomCraftingTableRecipe(Key id,
boolean showNotification,
@@ -20,11 +22,20 @@ public abstract class CustomCraftingTableRecipe<T> extends AbstractGroupedRecipe
@Nullable CustomRecipeResult<T> visualResult,
String group,
CraftingRecipeCategory category,
Function<PlayerOptionalContext>[] craftingFunctions) {
Function<PlayerOptionalContext>[] craftingFunctions,
Condition<PlayerOptionalContext> 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() {

View File

@@ -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<T> extends CustomCraftingTableRecipe<T> {
String group,
CraftingRecipeCategory category,
Pattern<T> pattern,
Function<PlayerOptionalContext>[] craftingFunctions) {
super(id, showNotification, result, visualResult, group, category, craftingFunctions);
Function<PlayerOptionalContext>[] craftingFunctions,
Condition<PlayerOptionalContext> craftingCondition) {
super(id, showNotification, result, visualResult, group, category, craftingFunctions, craftingCondition);
this.pattern = pattern;
this.parsedPattern = pattern.parse();
}
@@ -174,7 +175,8 @@ public class CustomShapedRecipe<T> extends CustomCraftingTableRecipe<T> {
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<T> extends CustomCraftingTableRecipe<T> {
VANILLA_RECIPE_HELPER.readGroup(json),
VANILLA_RECIPE_HELPER.craftingCategory(json),
new Pattern<>(VANILLA_RECIPE_HELPER.craftingShapedPattern(json), ingredients),
null,
null
);
}

View File

@@ -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<T> extends CustomCraftingTableRecipe<T> {
String group,
CraftingRecipeCategory category,
List<Ingredient<T>> ingredients,
Function<PlayerOptionalContext>[] craftingFunctions) {
super(id, showNotification, result, visualResult, group, category, craftingFunctions);
Function<PlayerOptionalContext>[] craftingFunctions,
Condition<PlayerOptionalContext> 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<T> extends CustomCraftingTableRecipe<T> {
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<T> extends CustomCraftingTableRecipe<T> {
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
);
}

View File

@@ -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<T> extends AbstractedFixedResultRecipe<T> {
public class CustomSmithingTransformRecipe<T> extends AbstractedFixedResultRecipe<T> implements ConditionalRecipe {
public static final Serializer<?> SERIALIZER = new Serializer<>();
private final Ingredient<T> base;
private final Ingredient<T> template;
private final Ingredient<T> addition;
private final boolean mergeComponents;
private final List<ItemDataProcessor> processors;
private final Condition<PlayerOptionalContext> condition;
public CustomSmithingTransformRecipe(Key id,
boolean showNotification,
@@ -35,7 +38,8 @@ public class CustomSmithingTransformRecipe<T> extends AbstractedFixedResultRecip
@Nullable Ingredient<T> addition,
CustomRecipeResult<T> result,
List<ItemDataProcessor> processors,
boolean mergeComponents
boolean mergeComponents,
Condition<PlayerOptionalContext> condition
) {
super(id, showNotification, result);
this.base = base;
@@ -43,6 +47,13 @@ public class CustomSmithingTransformRecipe<T> 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<T> extends AbstractedFixedResultRecip
toIngredient(addition),
parseResult(arguments),
ItemDataProcessors.fromMapList(processors),
mergeComponents
mergeComponents,
conditions(arguments)
);
}
@Override
public CustomSmithingTransformRecipe<A> 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
);
}
}

View File

@@ -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<T> extends AbstractRecipe<T> {
public class CustomSmithingTrimRecipe<T> extends AbstractRecipe<T> implements ConditionalRecipe {
public static final Serializer<?> SERIALIZER = new Serializer<>();
private final Ingredient<T> base;
private final Ingredient<T> template;
private final Ingredient<T> addition;
@Nullable // 1.21.5
private final Key pattern;
@Nullable
private final Condition<PlayerOptionalContext> condition;
public CustomSmithingTrimRecipe(@NotNull Key id,
boolean showNotification,
@NotNull Ingredient<T> template,
@NotNull Ingredient<T> base,
@NotNull Ingredient<T> addition,
@Nullable Key pattern
@Nullable Key pattern,
@Nullable Condition<PlayerOptionalContext> 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<T> extends AbstractRecipe<T> {
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<T> extends AbstractRecipe<T> {
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
);
}
}

View File

@@ -25,9 +25,12 @@ public abstract class CEWorld {
protected final WorldHeight worldHeightAccessor;
protected List<SectionPos> pendingLightSections = new ArrayList<>();
protected final Set<SectionPos> lightSections = ConcurrentHashMap.newKeySet(128);
protected final BlockEntityTickersList tickingBlockEntities = new BlockEntityTickersList();
protected final List<TickingBlockEntity> pendingTickingBlockEntities = new ArrayList<>();
protected volatile boolean isTickingBlockEntities = false;
protected final BlockEntityTickersList tickingSyncBlockEntities = new BlockEntityTickersList();
protected final List<TickingBlockEntity> pendingSyncTickingBlockEntities = new ArrayList<>();
protected final BlockEntityTickersList tickingAsyncBlockEntities = new BlockEntityTickersList();
protected final List<TickingBlockEntity> 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;
}
}

View File

@@ -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<BlockPos, BlockEntity> blockEntities; // 从区域线程上访问,安全
public final Map<BlockPos, ReplaceableTickingBlockEntity> tickingBlockEntitiesByPos; // 从区域线程上访问,安全
public final Map<BlockPos, ReplaceableTickingBlockEntity> tickingSyncBlockEntitiesByPos; // 从区域线程上访问,安全
public final Map<BlockPos, ReplaceableTickingBlockEntity> tickingAsyncBlockEntitiesByPos; // 从区域线程上访问,安全
public final Map<BlockPos, ConstantBlockEntityRenderer> constantBlockEntityRenderers; // 会从区域线程上读写netty线程上读取
public final Map<BlockPos, DynamicBlockEntityRenderer> 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 <T extends BlockEntity> void replaceOrCreateTickingBlockEntity(T blockEntity) {
ImmutableBlockState blockState = blockEntity.blockState();
BlockEntityTicker<T> 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<T> syncTicker = (BlockEntityTicker<T>) 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<T> asyncTicker = (BlockEntityTicker<T>) 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);

View File

@@ -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;

View File

@@ -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