diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BlockItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BlockItemBehavior.java index 3422f0352..b6164a8ef 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BlockItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BlockItemBehavior.java @@ -73,7 +73,7 @@ public class BlockItemBehavior extends BlockBoundItemBehavior { return InteractionResult.FAIL; } if (!context.canPlace()) { - return InteractionResult.FAIL; + return InteractionResult.PASS; } Player player = context.getPlayer(); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SearchRecipePlayerCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SearchRecipePlayerCommand.java index 1082e366a..e1163bcb0 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SearchRecipePlayerCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SearchRecipePlayerCommand.java @@ -8,6 +8,7 @@ import net.momirealms.craftengine.core.item.recipe.Recipe; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager; import net.momirealms.craftengine.core.plugin.locale.MessageConstants; +import net.momirealms.craftengine.core.util.ItemUtils; import net.momirealms.craftengine.core.util.Key; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -30,7 +31,7 @@ public class SearchRecipePlayerCommand extends BukkitCommandFeature item = serverPlayer.getItemInHand(InteractionHand.MAIN_HAND); - if (item == null) { + if (ItemUtils.isEmpty(item)) { handleFeedback(context, MessageConstants.COMMAND_SEARCH_RECIPE_NO_ITEM); return; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SearchUsagePlayerCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SearchUsagePlayerCommand.java index 9425e7a3c..9cd21ae36 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SearchUsagePlayerCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SearchUsagePlayerCommand.java @@ -8,6 +8,7 @@ import net.momirealms.craftengine.core.item.recipe.Recipe; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager; import net.momirealms.craftengine.core.plugin.locale.MessageConstants; +import net.momirealms.craftengine.core.util.ItemUtils; import net.momirealms.craftengine.core.util.Key; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -30,7 +31,7 @@ public class SearchUsagePlayerCommand extends BukkitCommandFeature item = serverPlayer.getItemInHand(InteractionHand.MAIN_HAND); - if (item.isEmpty()) { + if (ItemUtils.isEmpty(item)) { handleFeedback(context, MessageConstants.COMMAND_SEARCH_USAGE_NO_ITEM); return; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/TestCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/TestCommand.java index e5f42e169..437855e70 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/TestCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/TestCommand.java @@ -1,15 +1,10 @@ package net.momirealms.craftengine.bukkit.plugin.command.feature; import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature; -import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager; -import org.bukkit.Location; -import org.bukkit.block.data.BlockData; import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; import org.incendo.cloud.Command; -import org.incendo.cloud.parser.standard.IntegerParser; public class TestCommand extends BukkitCommandFeature { @@ -20,22 +15,9 @@ public class TestCommand extends BukkitCommandFeature { @Override public Command.Builder assembleCommand(org.incendo.cloud.CommandManager manager, Command.Builder builder) { return builder - .required("start", IntegerParser.integerParser(0)) - .senderType(Player.class) .handler(context -> { - Player sender = context.sender(); - int start = context.get("start"); - int x = sender.getChunk().getX() * 16; - int z = sender.getChunk().getZ() * 16; - int y = (sender.getLocation().getBlockY() / 16) * 16; - for (int a = 0; a < 16; a++) { - for (int b = 0; b < 16; b++) { - for (int c = 0; c < 16; c++) { - BlockData blockData = BlockStateUtils.fromBlockData(BlockStateUtils.idToBlockState(start + a + b * 16 + c * 256)); - sender.getWorld().setBlockData(new Location(sender.getWorld(), x + a, y + b, z + c), blockData); - } - } - } + // DO NOT PUSH ANY CODE FOR TEST COMMAND + // 禁止推送含有实现的Test指令 }); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java index e5cf08ac7..903712053 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java @@ -64,6 +64,7 @@ import net.momirealms.craftengine.core.plugin.network.*; import net.momirealms.craftengine.core.plugin.text.component.ComponentProvider; import net.momirealms.craftengine.core.util.*; import net.momirealms.craftengine.core.world.*; +import net.momirealms.craftengine.core.world.chunk.ChunkStatus; import net.momirealms.craftengine.core.world.chunk.Palette; import net.momirealms.craftengine.core.world.chunk.PalettedContainer; import net.momirealms.craftengine.core.world.chunk.packet.BlockEntityData; @@ -263,11 +264,11 @@ public class PacketConsumers { FriendlyByteBuf buf = event.getBuffer(); if (VersionHelper.isOrAbove1_20_2()) { long chunkPos = buf.readLong(); - user.setChunkTrackStatus(new ChunkPos(chunkPos), false); + user.removeTrackedChunk(chunkPos); } else { int x = buf.readInt(); int y = buf.readInt(); - user.setChunkTrackStatus(ChunkPos.of(x, y), false); + user.removeTrackedChunk(ChunkPos.asLong(x, y)); } } catch (Exception e) { CraftEngine.instance().logger().warn("Failed to handle ClientboundForgetLevelChunkPacket", e); @@ -280,7 +281,6 @@ public class PacketConsumers { FriendlyByteBuf buf = event.getBuffer(); int chunkX = buf.readInt(); int chunkZ = buf.readInt(); - player.setChunkTrackStatus(ChunkPos.of(chunkX, chunkZ), true); boolean named = !VersionHelper.isOrAbove1_20_2(); // ClientboundLevelChunkPacketData int heightmapsCount = 0; @@ -366,6 +366,9 @@ public class PacketConsumers { } buffer = newBuf.array(); } + + // 开始修改 + event.setChanged(true); buf.clear(); buf.writeVarInt(event.packetID()); buf.writeInt(chunkX); @@ -394,7 +397,10 @@ public class PacketConsumers { buf.writeBitSet(emptyBlockYMask); buf.writeByteArrayList(skyUpdates); buf.writeByteArrayList(blockUpdates); - event.setChanged(true); + + ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ); + // 记录加载的区块 + player.addTrackedChunk(chunkPos.longKey, new ChunkStatus()); } catch (Exception e) { CraftEngine.instance().logger().warn("Failed to handle ClientboundLevelChunkWithLightPacket", e); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/protocol/CancelBlockUpdatePacket.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/protocol/CancelBlockUpdatePacket.java index 2ff964065..215046487 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/protocol/CancelBlockUpdatePacket.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/protocol/CancelBlockUpdatePacket.java @@ -1,9 +1,9 @@ package net.momirealms.craftengine.bukkit.plugin.network.payload.protocol; -import net.momirealms.craftengine.core.plugin.network.ModPacket; import net.momirealms.craftengine.bukkit.plugin.network.payload.PayloadHelper; -import net.momirealms.craftengine.core.plugin.network.codec.NetworkCodec; +import net.momirealms.craftengine.core.plugin.network.ModPacket; import net.momirealms.craftengine.core.plugin.network.NetWorkUser; +import net.momirealms.craftengine.core.plugin.network.codec.NetworkCodec; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.util.FriendlyByteBuf; import net.momirealms.craftengine.core.util.Key; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/protocol/ClientBlockStateSizePacket.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/protocol/ClientBlockStateSizePacket.java index 7e87ee564..e2840c816 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/protocol/ClientBlockStateSizePacket.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/protocol/ClientBlockStateSizePacket.java @@ -2,8 +2,8 @@ package net.momirealms.craftengine.bukkit.plugin.network.payload.protocol; import net.momirealms.craftengine.core.plugin.network.ModPacket; -import net.momirealms.craftengine.core.plugin.network.codec.NetworkCodec; import net.momirealms.craftengine.core.plugin.network.NetWorkUser; +import net.momirealms.craftengine.core.plugin.network.codec.NetworkCodec; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.util.FriendlyByteBuf; import net.momirealms.craftengine.core.util.IntIdentityList; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/protocol/ClientCustomBlockPacket.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/protocol/ClientCustomBlockPacket.java index 9bfe2a7f8..911438c58 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/protocol/ClientCustomBlockPacket.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/protocol/ClientCustomBlockPacket.java @@ -3,10 +3,10 @@ package net.momirealms.craftengine.bukkit.plugin.network.payload.protocol; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TranslationArgument; -import net.momirealms.craftengine.core.plugin.network.ModPacket; -import net.momirealms.craftengine.core.plugin.network.codec.NetworkCodec; import net.momirealms.craftengine.bukkit.util.RegistryUtils; +import net.momirealms.craftengine.core.plugin.network.ModPacket; import net.momirealms.craftengine.core.plugin.network.NetWorkUser; +import net.momirealms.craftengine.core.plugin.network.codec.NetworkCodec; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.util.FriendlyByteBuf; import net.momirealms.craftengine.core.util.IntIdentityList; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java index 04927c89c..c448b31cd 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.bukkit.plugin.user; +import ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable; import com.google.common.collect.Lists; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; @@ -35,9 +36,9 @@ import net.momirealms.craftengine.core.util.IntIdentityList; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.world.BlockPos; -import net.momirealms.craftengine.core.world.ChunkPos; import net.momirealms.craftengine.core.world.World; import net.momirealms.craftengine.core.world.WorldEvents; +import net.momirealms.craftengine.core.world.chunk.ChunkStatus; import org.bukkit.*; import org.bukkit.attribute.Attribute; import org.bukkit.attribute.AttributeInstance; @@ -113,11 +114,9 @@ public class BukkitServerPlayer extends Player { // cooldown data private CooldownData cooldownData; // tracked chunks - private final Set trackedChunks = Collections.synchronizedSet(new HashSet<>()); - // relighted chunks - private final Set relightedChunks = Collections.synchronizedSet(new HashSet<>()); + private ConcurrentLong2ReferenceChainedHashTable trackedChunks; // entity view - private final Map entityTypeView = new ConcurrentHashMap<>(); + private Map entityTypeView; public BukkitServerPlayer(BukkitCraftEngine plugin, @Nullable Channel channel) { this.channel = channel; @@ -139,6 +138,8 @@ public class BukkitServerPlayer extends Player { this.uuid = player.getUniqueId(); this.name = player.getName(); byte[] bytes = player.getPersistentDataContainer().get(KeyUtils.toNamespacedKey(CooldownData.COOLDOWN_KEY), PersistentDataType.BYTE_ARRAY); + this.trackedChunks = ConcurrentLong2ReferenceChainedHashTable.createWithCapacity(768, 0.5f); + this.entityTypeView = new ConcurrentHashMap<>(256); try { this.cooldownData = CooldownData.fromBytes(bytes); } catch (IOException e) { @@ -1050,17 +1051,23 @@ public class BukkitServerPlayer extends Player { } @Override - public boolean isChunkTracked(ChunkPos chunkPos) { - return this.trackedChunks.contains(chunkPos); + public boolean isChunkTracked(long chunkPos) { + return this.trackedChunks.containsKey(chunkPos); } @Override - public void setChunkTrackStatus(ChunkPos chunkPos, boolean tracked) { - if (tracked) { - this.trackedChunks.add(chunkPos); - } else { - this.trackedChunks.remove(chunkPos); - } + public ChunkStatus getTrackedChunk(long chunkPos) { + return this.trackedChunks.get(chunkPos); + } + + @Override + public void addTrackedChunk(long chunkPos, ChunkStatus chunkStatus) { + this.trackedChunks.put(chunkPos, chunkStatus); + } + + @Override + public void removeTrackedChunk(long chunkPos) { + this.trackedChunks.remove(chunkPos); } @Override diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/LightUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/LightUtils.java index 636c5c1e3..f51715074 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/LightUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/LightUtils.java @@ -36,4 +36,42 @@ public final class LightUtils { CraftEngine.instance().logger().warn("Could not update light for world " + world.getName(), e); } } +// +// public static void relightChunk(BukkitServerPlayer player, ChunkPos pos) { +// long chunkKey = pos.longKey; +// ChunkStatus status = player.getTrackedChunk(chunkKey); +// // 不处理未加载区块 +// if (status == null || status.relighted()) return; +// for (ChunkPos anotherPos : pos.adjacentChunkPos()) { +// // 要求周围区块必须都加载 +// if (player.getTrackedChunk(anotherPos.longKey) == null) { +// return; +// } +// } +// status.setRelighted(true); +// net.momirealms.craftengine.core.world.World world = player.world(); +// Object serverLevel = world.serverWorld(); +// Object chunkSource = FastNMS.INSTANCE.method$ServerLevel$getChunkSource(serverLevel); +// Object chunkHolder = FastNMS.INSTANCE.method$ServerChunkCache$getVisibleChunkIfPresent(chunkSource, chunkKey); +// if (chunkHolder == null) return; +// CEWorld ceWorld = BukkitWorldManager.instance().getWorld(world.uuid()); +// CEChunk ceChunk = ceWorld.getChunkAtIfLoaded(chunkKey); +// if (ceChunk == null) return; +// CESection[] sections = ceChunk.sections(); +// BitSet bitSet = new BitSet(); +// for (int i = 0; i < sections.length; i++) { +// if (!sections[i].statesContainer().isEmpty()) { +// bitSet.set(i); +// } +// } +// if (bitSet.isEmpty()) return; +// try { +// Object lightEngine = CoreReflections.field$ChunkHolder$lightEngine.get(chunkHolder); +// Object chunkPos = FastNMS.INSTANCE.constructor$ChunkPos((int) chunkKey, (int) (chunkKey >> 32)); +// Object lightPacket = FastNMS.INSTANCE.constructor$ClientboundLightUpdatePacket(chunkPos, lightEngine, bitSet, bitSet); +// player.sendPacket(lightPacket, false); +// } catch (Throwable t) { +// CraftEngine.instance().logger().warn("Could not send relight packet for " + player.name() + " at " + player.world().name() + " " + pos.x + "," + pos.z, t); +// } +// } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitCEWorld.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitCEWorld.java index aadb65e6f..8d13cf1b1 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitCEWorld.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/world/BukkitCEWorld.java @@ -23,6 +23,7 @@ public class BukkitCEWorld extends CEWorld { @Override public void tick() { + super.tick(); HashSet poses; synchronized (super.updatedSectionSet) { poses = new HashSet<>(super.updatedSectionSet); diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/CustomBlock.java b/core/src/main/java/net/momirealms/craftengine/core/block/CustomBlock.java index c40700c12..13948d536 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/CustomBlock.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/CustomBlock.java @@ -19,11 +19,13 @@ public interface CustomBlock { Key id(); - @Nullable LootTable lootTable(); + @Nullable + LootTable lootTable(); void execute(PlayerOptionalContext context, EventTrigger trigger); - @NotNull BlockStateVariantProvider variantProvider(); + @NotNull + BlockStateVariantProvider variantProvider(); List getPossibleStates(CompoundTag nbt); 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 fbf649bba..aac373b56 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 @@ -1,6 +1,8 @@ package net.momirealms.craftengine.core.block; import it.unimi.dsi.fastutil.objects.Reference2ObjectArrayMap; +import net.momirealms.craftengine.core.block.entity.BlockEntity; +import net.momirealms.craftengine.core.block.entity.BlockEntityType; import net.momirealms.craftengine.core.block.properties.Property; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.item.Item; @@ -24,6 +26,7 @@ public final class ImmutableBlockState extends BlockStateHolder { private BlockBehavior behavior; private Integer hashCode; private BlockSettings settings; + private BlockEntityType blockEntityType; ImmutableBlockState( Holder owner, @@ -48,6 +51,14 @@ public final class ImmutableBlockState extends BlockStateHolder { this.settings = settings; } + public BlockEntityType blockEntityType() { + return blockEntityType; + } + + public void setBlockEntityType(BlockEntityType blockEntityType) { + this.blockEntityType = blockEntityType; + } + public boolean isEmpty() { return this == EmptyBlock.STATE; } @@ -67,6 +78,10 @@ public final class ImmutableBlockState extends BlockStateHolder { return this.hashCode; } + public boolean hasBlockEntity() { + return this.blockEntityType != null; + } + public BlockStateWrapper customBlockState() { return this.customBlockState; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/entity/BlockEntity.java b/core/src/main/java/net/momirealms/craftengine/core/block/entity/BlockEntity.java new file mode 100644 index 000000000..0c988377c --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/entity/BlockEntity.java @@ -0,0 +1,91 @@ +package net.momirealms.craftengine.core.block.entity; + +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.world.BlockPos; +import net.momirealms.craftengine.core.world.CEWorld; +import net.momirealms.craftengine.core.world.ChunkPos; +import net.momirealms.craftengine.core.world.SectionPos; +import net.momirealms.sparrow.nbt.CompoundTag; + +public abstract class BlockEntity { + protected final BlockPos pos; + protected final ImmutableBlockState blockState; + protected BlockEntityType type; + protected CEWorld world; + protected boolean valid; + + protected BlockEntity(BlockEntityType type, BlockPos pos, ImmutableBlockState blockState) { + this.pos = pos; + this.blockState = blockState; + this.type = type; + } + + public final CompoundTag saveAsTag() { + CompoundTag tag = new CompoundTag(); + this.savePos(tag); + this.saveCustomData(tag); + return tag; + } + + public CEWorld world() { + return world; + } + + public void setWorld(CEWorld world) { + this.world = world; + } + + public boolean isValid() { + return valid; + } + + public void setValid(boolean valid) { + this.valid = valid; + } + + private void savePos(CompoundTag tag) { + tag.putInt("x", this.pos.x()); + tag.putInt("y", this.pos.y()); + tag.putInt("z", this.pos.z()); + } + + protected void saveCustomData(CompoundTag tag) { + } + + protected void readCustomData(CompoundTag tag) { + } + + public static BlockPos readPos(CompoundTag tag) { + return new BlockPos(tag.getInt("x"), tag.getInt("y"), tag.getInt("z")); + } + + public BlockEntityType type() { + return type; + } + + public static BlockPos readPosAndVerify(CompoundTag tag, ChunkPos chunkPos) { + int x = tag.getInt("x", 0); + int y = tag.getInt("y", 0); + int z = tag.getInt("z", 0); + int sectionX = SectionPos.blockToSectionCoord(x); + int sectionZ = SectionPos.blockToSectionCoord(z); + if (sectionX != chunkPos.x || sectionZ != chunkPos.z) { + x = chunkPos.x * 16 + SectionPos.sectionRelative(x); + z = chunkPos.z * 16 + SectionPos.sectionRelative(z); + } + return new BlockPos(x, y, z); + } + + public BlockPos pos() { + return pos; + } + + public boolean isValidBlockState(ImmutableBlockState blockState) { + return blockState.blockEntityType() == this.type; + } + + public interface Factory { + + T create(BlockPos pos, ImmutableBlockState state); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/entity/BlockEntityType.java b/core/src/main/java/net/momirealms/craftengine/core/block/entity/BlockEntityType.java new file mode 100644 index 000000000..4291b6de5 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/entity/BlockEntityType.java @@ -0,0 +1,6 @@ +package net.momirealms.craftengine.core.block.entity; + +import net.momirealms.craftengine.core.util.Key; + +public record BlockEntityType(Key id, BlockEntity.Factory factory) { +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/entity/BlockEntityTypes.java b/core/src/main/java/net/momirealms/craftengine/core/block/entity/BlockEntityTypes.java new file mode 100644 index 000000000..1f90b42c5 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/entity/BlockEntityTypes.java @@ -0,0 +1,5 @@ +package net.momirealms.craftengine.core.block.entity; + +public class BlockEntityTypes { + +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/entity/tick/BlockEntityTicker.java b/core/src/main/java/net/momirealms/craftengine/core/block/entity/tick/BlockEntityTicker.java new file mode 100644 index 000000000..6b5f57399 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/entity/tick/BlockEntityTicker.java @@ -0,0 +1,11 @@ +package net.momirealms.craftengine.core.block.entity.tick; + +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.block.entity.BlockEntity; +import net.momirealms.craftengine.core.world.BlockPos; +import net.momirealms.craftengine.core.world.CEWorld; + +public interface BlockEntityTicker { + + void tick(CEWorld world, BlockPos pos, ImmutableBlockState state, T blockEntity); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/entity/tick/ReplaceableTickingBlockEntity.java b/core/src/main/java/net/momirealms/craftengine/core/block/entity/tick/ReplaceableTickingBlockEntity.java new file mode 100644 index 000000000..c9526faae --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/entity/tick/ReplaceableTickingBlockEntity.java @@ -0,0 +1,34 @@ +package net.momirealms.craftengine.core.block.entity.tick; + +import net.momirealms.craftengine.core.world.BlockPos; + +public class ReplaceableTickingBlockEntity implements TickingBlockEntity { + private TickingBlockEntity target; + + public ReplaceableTickingBlockEntity(TickingBlockEntity target) { + this.target = target; + } + + public TickingBlockEntity target() { + return target; + } + + public void setTarget(TickingBlockEntity target) { + this.target = target; + } + + @Override + public BlockPos pos() { + return this.target.pos(); + } + + @Override + public void tick() { + this.target.tick(); + } + + @Override + public boolean isValid() { + return this.target.isValid(); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/entity/tick/TickingBlockEntity.java b/core/src/main/java/net/momirealms/craftengine/core/block/entity/tick/TickingBlockEntity.java new file mode 100644 index 000000000..99599744d --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/entity/tick/TickingBlockEntity.java @@ -0,0 +1,12 @@ +package net.momirealms.craftengine.core.block.entity.tick; + +import net.momirealms.craftengine.core.world.BlockPos; + +public interface TickingBlockEntity { + + void tick(); + + boolean isValid(); + + BlockPos pos(); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/entity/tick/TickingBlockEntityImpl.java b/core/src/main/java/net/momirealms/craftengine/core/block/entity/tick/TickingBlockEntityImpl.java new file mode 100644 index 000000000..ee323e5a9 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/entity/tick/TickingBlockEntityImpl.java @@ -0,0 +1,51 @@ +package net.momirealms.craftengine.core.block.entity.tick; + +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.block.entity.BlockEntity; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.logger.Debugger; +import net.momirealms.craftengine.core.world.BlockPos; +import net.momirealms.craftengine.core.world.chunk.CEChunk; + +public class TickingBlockEntityImpl implements TickingBlockEntity { + private final T blockEntity; + private final BlockEntityTicker ticker; + private final CEChunk chunk; + + public TickingBlockEntityImpl(CEChunk chunk, T blockEntity, BlockEntityTicker ticker) { + this.blockEntity = blockEntity; + this.ticker = ticker; + this.chunk = chunk; + } + + @Override + public BlockPos pos() { + return this.blockEntity.pos(); + } + + @Override + public void tick() { + // 已无效 + if (!this.isValid()) return; + // 还没加载完全 + if (this.blockEntity.world() == null) return; + BlockPos pos = pos(); + ImmutableBlockState state = this.chunk.getBlockState(pos); + // 不是合法方块 + if (!this.blockEntity.isValidBlockState(state)) { + this.chunk.removeBlockEntity(pos); + Debugger.BLOCK_ENTITY.warn(() -> "Invalid block entity(" + this.blockEntity.getClass().getSimpleName() + ") with state " + state + " found at world " + this.chunk.world().name() + " " + pos, null); + return; + } + try { + this.ticker.tick(this.chunk.world(), pos, state, this.blockEntity); + } catch (Throwable t) { + CraftEngine.instance().logger().warn("Failed to tick block entity(" + this.blockEntity.getClass().getSimpleName() + ") at world " + this.chunk.world().name() + " " + pos, t); + } + } + + @Override + public boolean isValid() { + return this.blockEntity.isValid(); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java index 8956ca100..2a207bb57 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java @@ -120,6 +120,7 @@ public class Config { protected boolean block$predict_breaking; protected int block$predict_breaking_interval; protected double block$extended_interaction_range; + protected boolean block$chunk_relighter; protected boolean recipe$enable; protected boolean recipe$disable_vanilla_recipes$all; @@ -224,6 +225,8 @@ public class Config { .builder() .setVersioning(new BasicVersioning("config-version")) .addIgnoredRoute(PluginProperties.getValue("config"), "resource-pack.delivery.hosting", '.') + .addIgnoredRoute(PluginProperties.getValue("config"), "chunk-system.process-invalid-blocks.convert", '.') + .addIgnoredRoute(PluginProperties.getValue("config"), "chunk-system.process-invalid-furniture.convert", '.') .build()); } try { @@ -384,6 +387,7 @@ public class Config { block$predict_breaking = config.getBoolean("block.predict-breaking.enable", true); block$predict_breaking_interval = Math.max(config.getInt("block.predict-breaking.interval", 10), 1); block$extended_interaction_range = Math.max(config.getDouble("block.predict-breaking.extended-interaction-range", 0.5), 0.0); + block$chunk_relighter = config.getBoolean("block.chunk-relighter", true); // recipe recipe$enable = config.getBoolean("recipe.enable", true); @@ -449,6 +453,10 @@ public class Config { return instance.debug$item; } + public static boolean debugBlockEntity() { + return false; + } + public static boolean debugFurniture() { return instance.debug$furniture; } @@ -873,6 +881,10 @@ public class Config { return instance.item$update_triggers$drop; } + public static boolean enableChunkRelighter() { + return instance.block$chunk_relighter; + } + public void setObf(boolean enable) { this.resource_pack$protection$obfuscation$enable = enable; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/logger/Debugger.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/logger/Debugger.java index fba7f7fa9..9ac5d509d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/logger/Debugger.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/logger/Debugger.java @@ -10,7 +10,8 @@ public enum Debugger { PACKET(Config::debugPacket), FURNITURE(Config::debugFurniture), RESOURCE_PACK(Config::debugResourcePack), - ITEM(Config::debugItem); + ITEM(Config::debugItem), + BLOCK_ENTITY(Config::debugBlockEntity); private final Supplier condition; @@ -26,7 +27,11 @@ public enum Debugger { public void warn(Supplier message, Throwable e) { if (this.condition.get()) { - CraftEngine.instance().logger().warn("[DEBUG] " + message.get(), e); + if (e != null) { + CraftEngine.instance().logger().warn("[DEBUG] " + message.get(), e); + } else { + CraftEngine.instance().logger().warn("[DEBUG] " + message.get()); + } } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetWorkUser.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetWorkUser.java index 32a50d188..eaf72bf24 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetWorkUser.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetWorkUser.java @@ -6,7 +6,7 @@ import net.kyori.adventure.text.Component; import net.momirealms.craftengine.core.plugin.Plugin; import net.momirealms.craftengine.core.util.IntIdentityList; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.world.ChunkPos; +import net.momirealms.craftengine.core.world.chunk.ChunkStatus; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; @@ -79,13 +79,17 @@ public interface NetWorkUser { boolean shouldProcessFinishConfiguration(); - boolean isChunkTracked(ChunkPos chunkPos); + boolean isChunkTracked(long chunkPos); - void setChunkTrackStatus(ChunkPos chunkPos, boolean tracked); + ChunkStatus getTrackedChunk(long chunkPos); + + void addTrackedChunk(long chunkPos, ChunkStatus chunkStatus); void clearTrackedChunks(); - void setClientBlockList(IntIdentityList blockList); + void removeTrackedChunk(long chunkPos); IntIdentityList clientBlockList(); + + void setClientBlockList(IntIdentityList integers); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/registry/AbstractMappedRegistry.java b/core/src/main/java/net/momirealms/craftengine/core/registry/AbstractMappedRegistry.java index efefaa5ba..c095c0f32 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/registry/AbstractMappedRegistry.java +++ b/core/src/main/java/net/momirealms/craftengine/core/registry/AbstractMappedRegistry.java @@ -9,12 +9,15 @@ import java.util.*; public abstract class AbstractMappedRegistry implements WritableRegistry { protected final ResourceKey> key; - protected final Map> byResourceLocation = new HashMap<>(512); - protected final Map, Holder.Reference> byResourceKey = new HashMap<>(512); - protected final List> byId = new ArrayList<>(512); + protected final Map> byResourceLocation; + protected final Map, Holder.Reference> byResourceKey; + protected final List> byId; - protected AbstractMappedRegistry(ResourceKey> key) { + protected AbstractMappedRegistry(ResourceKey> key, int expectedSize) { this.key = key; + this.byResourceLocation = new HashMap<>(expectedSize); + this.byResourceKey = new HashMap<>(expectedSize); + this.byId = new ArrayList<>(expectedSize); } @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java b/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java index e90410cab..d002e007a 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java +++ b/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java @@ -46,51 +46,51 @@ import net.momirealms.craftengine.core.util.FriendlyByteBuf; import net.momirealms.craftengine.core.util.ResourceKey; public class BuiltInRegistries { - public static final Registry BLOCK = createDynamicBoundRegistry(Registries.BLOCK); - public static final Registry BLOCK_BEHAVIOR_FACTORY = createConstantBoundRegistry(Registries.BLOCK_BEHAVIOR_FACTORY); - public static final Registry> ITEM_DATA_MODIFIER_FACTORY = createConstantBoundRegistry(Registries.ITEM_DATA_MODIFIER_FACTORY); - public static final Registry ITEM_BEHAVIOR_FACTORY = createConstantBoundRegistry(Registries.ITEM_BEHAVIOR_FACTORY); - public static final Registry PROPERTY_FACTORY = createConstantBoundRegistry(Registries.PROPERTY_FACTORY); - public static final Registry> LOOT_FUNCTION_FACTORY = createConstantBoundRegistry(Registries.LOOT_FUNCTION_FACTORY); - public static final Registry> LOOT_CONDITION_FACTORY = createConstantBoundRegistry(Registries.LOOT_CONDITION_FACTORY); - public static final Registry> LOOT_ENTRY_CONTAINER_FACTORY = createConstantBoundRegistry(Registries.LOOT_ENTRY_CONTAINER_FACTORY); - public static final Registry NUMBER_PROVIDER_FACTORY = createConstantBoundRegistry(Registries.NUMBER_PROVIDER_FACTORY); - public static final Registry TEMPLATE_ARGUMENT_FACTORY = createConstantBoundRegistry(Registries.TEMPLATE_ARGUMENT_FACTORY); - public static final Registry ITEM_MODEL_FACTORY = createConstantBoundRegistry(Registries.ITEM_MODEL_FACTORY); - public static final Registry ITEM_MODEL_READER = createConstantBoundRegistry(Registries.ITEM_MODEL_READER); - public static final Registry TINT_FACTORY = createConstantBoundRegistry(Registries.TINT_FACTORY); - public static final Registry TINT_READER = createConstantBoundRegistry(Registries.TINT_READER); - public static final Registry SPECIAL_MODEL_FACTORY = createConstantBoundRegistry(Registries.SPECIAL_MODEL_FACTORY); - public static final Registry SPECIAL_MODEL_READER = createConstantBoundRegistry(Registries.SPECIAL_MODEL_READER); - public static final Registry RANGE_DISPATCH_PROPERTY_FACTORY = createConstantBoundRegistry(Registries.RANGE_DISPATCH_PROPERTY_FACTORY); - public static final Registry RANGE_DISPATCH_PROPERTY_READER = createConstantBoundRegistry(Registries.RANGE_DISPATCH_PROPERTY_READER); - public static final Registry CONDITION_PROPERTY_FACTORY = createConstantBoundRegistry(Registries.CONDITION_PROPERTY_FACTORY); - public static final Registry CONDITION_PROPERTY_READER = createConstantBoundRegistry(Registries.CONDITION_PROPERTY_READER); - public static final Registry SELECT_PROPERTY_FACTORY = createConstantBoundRegistry(Registries.SELECT_PROPERTY_FACTORY); - public static final Registry SELECT_PROPERTY_READER = createConstantBoundRegistry(Registries.SELECT_PROPERTY_READER); - public static final Registry>> RECIPE_SERIALIZER = createConstantBoundRegistry(Registries.RECIPE_FACTORY); - public static final Registry FORMULA_FACTORY = createConstantBoundRegistry(Registries.FORMULA_FACTORY); - public static final Registry> PATH_MATCHER_FACTORY = createConstantBoundRegistry(Registries.PATH_MATCHER_FACTORY); - public static final Registry RESOLUTION_FACTORY = createConstantBoundRegistry(Registries.RESOLUTION_FACTORY); - public static final Registry SMITHING_RESULT_PROCESSOR_FACTORY = createConstantBoundRegistry(Registries.SMITHING_RESULT_PROCESSOR_FACTORY); - public static final Registry HITBOX_FACTORY = createConstantBoundRegistry(Registries.HITBOX_FACTORY); - public static final Registry RESOURCE_PACK_HOST_FACTORY = createConstantBoundRegistry(Registries.RESOURCE_PACK_HOST_FACTORY); - public static final Registry> EVENT_FUNCTION_FACTORY = createConstantBoundRegistry(Registries.EVENT_FUNCTION_FACTORY); - public static final Registry> EVENT_CONDITION_FACTORY = createConstantBoundRegistry(Registries.EVENT_CONDITION_FACTORY); - public static final Registry> PLAYER_SELECTOR_FACTORY = createConstantBoundRegistry(Registries.PLAYER_SELECTOR_FACTORY); - public static final Registry EQUIPMENT_FACTORY = createConstantBoundRegistry(Registries.EQUIPMENT_FACTORY); - public static final Registry SLOT_DISPLAY_TYPE = createConstantBoundRegistry(Registries.SLOT_DISPLAY_TYPE); - public static final Registry RECIPE_DISPLAY_TYPE = createConstantBoundRegistry(Registries.RECIPE_DISPLAY_TYPE); - public static final Registry LEGACY_RECIPE_TYPE = createConstantBoundRegistry(Registries.LEGACY_RECIPE_TYPE); - public static final Registry> RECIPE_POST_PROCESSOR_TYPE = createConstantBoundRegistry(Registries.RECIPE_POST_PROCESSOR_TYPE); - public static final Registry> ITEM_UPDATER_TYPE = createConstantBoundRegistry(Registries.ITEM_UPDATER_TYPE); - public static final Registry> MOD_PACKET = createConstantBoundRegistry(Registries.MOD_PACKET); + public static final Registry BLOCK = createDynamicBoundRegistry(Registries.BLOCK, 512); + public static final Registry BLOCK_BEHAVIOR_FACTORY = createConstantBoundRegistry(Registries.BLOCK_BEHAVIOR_FACTORY, 64); + public static final Registry> ITEM_DATA_MODIFIER_FACTORY = createConstantBoundRegistry(Registries.ITEM_DATA_MODIFIER_FACTORY, 64); + public static final Registry ITEM_BEHAVIOR_FACTORY = createConstantBoundRegistry(Registries.ITEM_BEHAVIOR_FACTORY, 64); + public static final Registry PROPERTY_FACTORY = createConstantBoundRegistry(Registries.PROPERTY_FACTORY, 16); + public static final Registry> LOOT_FUNCTION_FACTORY = createConstantBoundRegistry(Registries.LOOT_FUNCTION_FACTORY, 32); + public static final Registry> LOOT_CONDITION_FACTORY = createConstantBoundRegistry(Registries.LOOT_CONDITION_FACTORY, 32); + public static final Registry> LOOT_ENTRY_CONTAINER_FACTORY = createConstantBoundRegistry(Registries.LOOT_ENTRY_CONTAINER_FACTORY, 16); + public static final Registry NUMBER_PROVIDER_FACTORY = createConstantBoundRegistry(Registries.NUMBER_PROVIDER_FACTORY, 16); + public static final Registry TEMPLATE_ARGUMENT_FACTORY = createConstantBoundRegistry(Registries.TEMPLATE_ARGUMENT_FACTORY, 16); + public static final Registry ITEM_MODEL_FACTORY = createConstantBoundRegistry(Registries.ITEM_MODEL_FACTORY, 16); + public static final Registry ITEM_MODEL_READER = createConstantBoundRegistry(Registries.ITEM_MODEL_READER, 16); + public static final Registry TINT_FACTORY = createConstantBoundRegistry(Registries.TINT_FACTORY, 16); + public static final Registry TINT_READER = createConstantBoundRegistry(Registries.TINT_READER, 16); + public static final Registry SPECIAL_MODEL_FACTORY = createConstantBoundRegistry(Registries.SPECIAL_MODEL_FACTORY, 16); + public static final Registry SPECIAL_MODEL_READER = createConstantBoundRegistry(Registries.SPECIAL_MODEL_READER, 16); + public static final Registry RANGE_DISPATCH_PROPERTY_FACTORY = createConstantBoundRegistry(Registries.RANGE_DISPATCH_PROPERTY_FACTORY, 16); + public static final Registry RANGE_DISPATCH_PROPERTY_READER = createConstantBoundRegistry(Registries.RANGE_DISPATCH_PROPERTY_READER, 16); + public static final Registry CONDITION_PROPERTY_FACTORY = createConstantBoundRegistry(Registries.CONDITION_PROPERTY_FACTORY, 16); + public static final Registry CONDITION_PROPERTY_READER = createConstantBoundRegistry(Registries.CONDITION_PROPERTY_READER, 16); + public static final Registry SELECT_PROPERTY_FACTORY = createConstantBoundRegistry(Registries.SELECT_PROPERTY_FACTORY, 16); + public static final Registry SELECT_PROPERTY_READER = createConstantBoundRegistry(Registries.SELECT_PROPERTY_READER, 16); + public static final Registry>> RECIPE_SERIALIZER = createConstantBoundRegistry(Registries.RECIPE_FACTORY, 16); + public static final Registry FORMULA_FACTORY = createConstantBoundRegistry(Registries.FORMULA_FACTORY, 16); + public static final Registry> PATH_MATCHER_FACTORY = createConstantBoundRegistry(Registries.PATH_MATCHER_FACTORY, 16); + public static final Registry RESOLUTION_FACTORY = createConstantBoundRegistry(Registries.RESOLUTION_FACTORY, 16); + public static final Registry SMITHING_RESULT_PROCESSOR_FACTORY = createConstantBoundRegistry(Registries.SMITHING_RESULT_PROCESSOR_FACTORY, 16); + public static final Registry HITBOX_FACTORY = createConstantBoundRegistry(Registries.HITBOX_FACTORY, 16); + public static final Registry RESOURCE_PACK_HOST_FACTORY = createConstantBoundRegistry(Registries.RESOURCE_PACK_HOST_FACTORY, 16); + public static final Registry> EVENT_FUNCTION_FACTORY = createConstantBoundRegistry(Registries.EVENT_FUNCTION_FACTORY, 128); + public static final Registry> EVENT_CONDITION_FACTORY = createConstantBoundRegistry(Registries.EVENT_CONDITION_FACTORY, 128); + public static final Registry> PLAYER_SELECTOR_FACTORY = createConstantBoundRegistry(Registries.PLAYER_SELECTOR_FACTORY, 16); + public static final Registry EQUIPMENT_FACTORY = createConstantBoundRegistry(Registries.EQUIPMENT_FACTORY, 8); + public static final Registry SLOT_DISPLAY_TYPE = createConstantBoundRegistry(Registries.SLOT_DISPLAY_TYPE, 16); + public static final Registry RECIPE_DISPLAY_TYPE = createConstantBoundRegistry(Registries.RECIPE_DISPLAY_TYPE, 16); + public static final Registry LEGACY_RECIPE_TYPE = createConstantBoundRegistry(Registries.LEGACY_RECIPE_TYPE, 16); + public static final Registry> RECIPE_POST_PROCESSOR_TYPE = createConstantBoundRegistry(Registries.RECIPE_POST_PROCESSOR_TYPE, 16); + public static final Registry> ITEM_UPDATER_TYPE = createConstantBoundRegistry(Registries.ITEM_UPDATER_TYPE, 16); + public static final Registry> MOD_PACKET = createConstantBoundRegistry(Registries.MOD_PACKET, 16); - private static Registry createConstantBoundRegistry(ResourceKey> key) { - return new ConstantBoundRegistry<>(key); + private static Registry createConstantBoundRegistry(ResourceKey> key, int expectedSize) { + return new ConstantBoundRegistry<>(key, expectedSize); } - private static Registry createDynamicBoundRegistry(ResourceKey> key) { - return new DynamicBoundRegistry<>(key); + private static Registry createDynamicBoundRegistry(ResourceKey> key, int expectedSize) { + return new DynamicBoundRegistry<>(key, expectedSize); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/registry/ConstantBoundRegistry.java b/core/src/main/java/net/momirealms/craftengine/core/registry/ConstantBoundRegistry.java index cf3da4675..1c24d56fc 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/registry/ConstantBoundRegistry.java +++ b/core/src/main/java/net/momirealms/craftengine/core/registry/ConstantBoundRegistry.java @@ -12,10 +12,11 @@ import java.util.Objects; public class ConstantBoundRegistry extends AbstractMappedRegistry { protected final Reference2IntMap toId = MCUtils.make(new Reference2IntOpenHashMap<>(), map -> map.defaultReturnValue(-1)); - protected final Map> byValue = new IdentityHashMap<>(512); + protected final Map> byValue; - public ConstantBoundRegistry(ResourceKey> key) { - super(key); + public ConstantBoundRegistry(ResourceKey> key, int expectedSize) { + super(key, expectedSize); + this.byValue = new IdentityHashMap<>(expectedSize); } @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/registry/DynamicBoundRegistry.java b/core/src/main/java/net/momirealms/craftengine/core/registry/DynamicBoundRegistry.java index 47808d13f..fef205d22 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/registry/DynamicBoundRegistry.java +++ b/core/src/main/java/net/momirealms/craftengine/core/registry/DynamicBoundRegistry.java @@ -8,8 +8,8 @@ import java.util.Objects; public class DynamicBoundRegistry extends AbstractMappedRegistry { - public DynamicBoundRegistry(ResourceKey> key) { - super(key); + public DynamicBoundRegistry(ResourceKey> key, int expectedSize) { + super(key, expectedSize); } @Override diff --git a/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java b/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java index 8b8725e2d..b4fda0671 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java +++ b/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java @@ -86,5 +86,5 @@ public class Registries { public static final ResourceKey> LEGACY_RECIPE_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("legacy_recipe_type")); public static final ResourceKey>> RECIPE_POST_PROCESSOR_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("recipe_post_processor_type")); public static final ResourceKey>> ITEM_UPDATER_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("item_updater_type")); - public static final ResourceKey>> MOD_PACKET = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("mod_packet")); + public static final ResourceKey>> MOD_PACKET = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("mod_packet_type")); } 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 62dfa77da..2807754bd 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 @@ -1,7 +1,10 @@ package net.momirealms.craftengine.core.world; import ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable; +import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.block.entity.BlockEntity; +import net.momirealms.craftengine.core.block.entity.tick.TickingBlockEntity; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.world.chunk.CEChunk; import net.momirealms.craftengine.core.world.chunk.storage.StorageAdaptor; @@ -9,8 +12,7 @@ import net.momirealms.craftengine.core.world.chunk.storage.WorldDataStorage; import org.jetbrains.annotations.Nullable; import java.io.IOException; -import java.util.Collection; -import java.util.Set; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; public abstract class CEWorld { @@ -20,6 +22,9 @@ public abstract class CEWorld { protected final WorldDataStorage worldDataStorage; protected final WorldHeight worldHeightAccessor; protected final Set updatedSectionSet = ConcurrentHashMap.newKeySet(128); + protected final List tickingBlockEntities = new ArrayList<>(); + protected final List pendingTickingBlockEntities = new ArrayList<>(); + protected boolean isTickingBlockEntities = false; private CEChunk lastChunk; private long lastChunkPos; @@ -40,6 +45,14 @@ public abstract class CEWorld { this.lastChunkPos = ChunkPos.INVALID_CHUNK_POS; } + public String name() { + return this.world.name(); + } + + public UUID uuid() { + return this.world.uuid(); + } + public void save() { try { for (ConcurrentLong2ReferenceChainedHashTable.TableEntry entry : this.loadedChunkMap.entrySet()) { @@ -76,16 +89,6 @@ public abstract class CEWorld { @Nullable public CEChunk getChunkAtIfLoaded(long chunkPos) { - return getChunkAtIfLoadedMainThread(chunkPos); - } - - @Nullable - public CEChunk getChunkAtIfLoaded(int x, int z) { - return getChunkAtIfLoaded(ChunkPos.asLong(x, z)); - } - - @Nullable - public CEChunk getChunkAtIfLoadedMainThread(long chunkPos) { if (chunkPos == this.lastChunkPos) { return this.lastChunk; } @@ -98,14 +101,11 @@ public abstract class CEWorld { } @Nullable - public CEChunk getChunkAtIfLoadedMainThread(int x, int z) { - return getChunkAtIfLoadedMainThread(ChunkPos.asLong(x, z)); - } - - public WorldHeight worldHeight() { - return worldHeightAccessor; + public CEChunk getChunkAtIfLoaded(int x, int z) { + return getChunkAtIfLoaded(ChunkPos.asLong(x, z)); } + @Nullable public ImmutableBlockState getBlockStateAtIfLoaded(int x, int y, int z) { CEChunk chunk = getChunkAtIfLoaded(x >> 4, z >> 4); if (chunk == null) { @@ -114,6 +114,7 @@ public abstract class CEWorld { return chunk.getBlockState(x, y, z); } + @Nullable public ImmutableBlockState getBlockStateAtIfLoaded(BlockPos blockPos) { CEChunk chunk = getChunkAtIfLoaded(blockPos.x() >> 4, blockPos.z() >> 4); if (chunk == null) { @@ -123,7 +124,7 @@ public abstract class CEWorld { } public boolean setBlockStateAtIfLoaded(BlockPos blockPos, ImmutableBlockState blockState) { - if (worldHeightAccessor.isOutsideBuildHeight(blockPos)) { + if (this.worldHeightAccessor.isOutsideBuildHeight(blockPos)) { return false; } CEChunk chunk = getChunkAtIfLoaded(blockPos.x() >> 4, blockPos.z() >> 4); @@ -134,6 +135,18 @@ public abstract class CEWorld { return true; } + @Nullable + public BlockEntity getBlockEntityAtIfLoaded(BlockPos blockPos) { + if (this.worldHeightAccessor.isOutsideBuildHeight(blockPos)) { + return null; + } + CEChunk chunk = getChunkAtIfLoaded(blockPos.x() >> 4, blockPos.z() >> 4); + if (chunk == null) { + return null; + } + return chunk.getBlockEntity(blockPos); + } + public WorldDataStorage worldDataStorage() { return worldDataStorage; } @@ -146,5 +159,29 @@ public abstract class CEWorld { this.updatedSectionSet.addAll(pos); } - public abstract void tick(); + public WorldHeight worldHeight() { + return this.worldHeightAccessor; + } + + public void tick() { + this.tickBlockEntities(); + } + + protected void tickBlockEntities() { + this.isTickingBlockEntities = true; + if (!this.pendingTickingBlockEntities.isEmpty()) { + this.tickingBlockEntities.addAll(this.pendingTickingBlockEntities); + this.pendingTickingBlockEntities.clear(); + } + ReferenceOpenHashSet toRemove = new ReferenceOpenHashSet<>(); + for (TickingBlockEntity blockEntity : this.tickingBlockEntities) { + if (!blockEntity.isValid()) { + blockEntity.tick(); + } else { + toRemove.add(blockEntity); + } + } + this.tickingBlockEntities.removeAll(toRemove); + this.isTickingBlockEntities = false; + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/ChunkPos.java b/core/src/main/java/net/momirealms/craftengine/core/world/ChunkPos.java index a053afd79..32eb4788f 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/ChunkPos.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/ChunkPos.java @@ -65,10 +65,23 @@ public class ChunkPos { return longKey; } + public ChunkPos[] adjacentChunkPos() { + return adjacentChunkPos(this); + } + public static long asLong(int chunkX, int chunkZ) { return (long) chunkX & 4294967295L | ((long) chunkZ & 4294967295L) << 32; } + public static ChunkPos[] adjacentChunkPos(ChunkPos chunkPos) { + return new ChunkPos[] { + new ChunkPos(chunkPos.x, chunkPos.z - 1), + new ChunkPos(chunkPos.x, chunkPos.z + 1), + new ChunkPos(chunkPos.x + 1, chunkPos.z), + new ChunkPos(chunkPos.x - 1, chunkPos.z) + }; + } + @Override public final boolean equals(Object o) { if (o == null) return false; diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/SectionPos.java b/core/src/main/java/net/momirealms/craftengine/core/world/SectionPos.java index 3897374da..fac06da92 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/SectionPos.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/SectionPos.java @@ -13,4 +13,8 @@ public class SectionPos extends Vec3i { public static SectionPos of(BlockPos pos) { return new SectionPos(pos.x() >> 4, pos.y() >> 4, pos.z() >> 4); } + + public static int sectionRelative(int rel) { + return rel & 15; + } } 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 fb00cc0dc..7684dea88 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 @@ -1,13 +1,18 @@ package net.momirealms.craftengine.core.world.chunk; -import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; -import net.momirealms.craftengine.core.block.BlockEntityState; +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.entity.BlockEntity; +import net.momirealms.craftengine.core.plugin.logger.Debugger; import net.momirealms.craftengine.core.world.*; +import net.momirealms.craftengine.core.world.chunk.serialization.DefaultBlockEntitySerializer; +import net.momirealms.sparrow.nbt.ListTag; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Collections; +import java.util.List; import java.util.Map; public class CEChunk { @@ -16,7 +21,7 @@ public class CEChunk { private final ChunkPos chunkPos; private final CESection[] sections; private final WorldHeight worldHeightAccessor; - private final Map blockEntities; + private final Map blockEntities; private boolean dirty; public CEChunk(CEWorld world, ChunkPos chunkPos) { @@ -24,14 +29,14 @@ public class CEChunk { this.chunkPos = chunkPos; this.worldHeightAccessor = world.worldHeight(); this.sections = new CESection[this.worldHeightAccessor.getSectionsCount()]; - this.blockEntities = new Int2ObjectOpenHashMap<>(16, 0.5f); + this.blockEntities = new Object2ObjectOpenHashMap<>(16, 0.5f); this.fillEmptySection(); } - public CEChunk(CEWorld world, ChunkPos chunkPos, CESection[] sections, Map blockEntities) { + public CEChunk(CEWorld world, ChunkPos chunkPos, CESection[] sections, ListTag blockEntitiesTag) { this.world = world; this.chunkPos = chunkPos; - this.blockEntities = blockEntities; + this.blockEntities = new Object2ObjectOpenHashMap<>(Math.max(blockEntitiesTag.size(), 16), 0.5f); this.worldHeightAccessor = world.worldHeight(); int sectionCount = this.worldHeightAccessor.getSectionsCount(); this.sections = new CESection[sectionCount]; @@ -44,10 +49,48 @@ public class CEChunk { } } this.fillEmptySection(); + List blockEntities = DefaultBlockEntitySerializer.deserialize(this, blockEntitiesTag); + for (BlockEntity blockEntity : blockEntities) { + this.setBlockEntity(blockEntity); + } } - public Map blockEntities() { - return this.blockEntities; + public void addBlockEntity(BlockEntity blockEntity) { + this.setBlockEntity(blockEntity); + } + + public void removeBlockEntity(BlockPos blockPos) { + + } + + public void setBlockEntity(BlockEntity blockEntity) { + BlockPos pos = blockEntity.pos(); + ImmutableBlockState blockState = this.getBlockState(pos); + if (!blockState.hasBlockEntity()) { + Debugger.BLOCK_ENTITY.debug(() -> "Failed to add invalid block entity " + blockEntity.saveAsTag() + " at " + pos); + return; + } + // 设置方块实体所在世界 + blockEntity.setWorld(this.world); + blockEntity.setValid(true); + BlockEntity previous = this.blockEntities.put(pos, blockEntity); + // 标记旧的方块实体无效 + if (previous != null && previous != blockEntity) { + previous.setValid(false); + } + } + + @Nullable + public BlockEntity getBlockEntity(BlockPos pos) { + BlockEntity blockEntity = this.blockEntities.get(pos); + if (blockEntity == null) { + + } + return blockEntity; + } + + public Map blockEntities() { + return Collections.unmodifiableMap(this.blockEntities); } public boolean dirty() { @@ -93,17 +136,17 @@ public class CEChunk { } } - @Nullable + @NotNull public ImmutableBlockState getBlockState(BlockPos pos) { return getBlockState(pos.x(), pos.y(), pos.z()); } - @Nullable + @NotNull public ImmutableBlockState getBlockState(int x, int y, int z) { int index = sectionIndex(SectionPos.blockToSectionCoord(y)); CESection section = this.sections[index]; if (section == null) { - return null; + return EmptyBlock.STATE; } return section.getBlockState((y & 15) << 8 | (z & 15) << 4 | x & 15); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/ChunkStatus.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/ChunkStatus.java new file mode 100644 index 000000000..744068aa6 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/ChunkStatus.java @@ -0,0 +1,7 @@ +package net.momirealms.craftengine.core.world.chunk; + +public class ChunkStatus { + + public ChunkStatus() { + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultBlockEntitySerializer.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultBlockEntitySerializer.java index 8f8be31d9..f8b91a5f0 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultBlockEntitySerializer.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultBlockEntitySerializer.java @@ -1,48 +1,33 @@ package net.momirealms.craftengine.core.world.chunk.serialization; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -import net.momirealms.craftengine.core.block.BlockEntityState; +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.block.entity.BlockEntity; +import net.momirealms.craftengine.core.world.BlockPos; +import net.momirealms.craftengine.core.world.chunk.CEChunk; import net.momirealms.sparrow.nbt.CompoundTag; import net.momirealms.sparrow.nbt.ListTag; -import org.jetbrains.annotations.ApiStatus; +import java.util.ArrayList; +import java.util.List; import java.util.Map; public final class DefaultBlockEntitySerializer { - @ApiStatus.Experimental - public static ListTag serialize(Map tiles) { + public static ListTag serialize(Map tiles) { ListTag result = new ListTag(); - Map nbtToPosMap = new Object2ObjectOpenHashMap<>(Math.max(tiles.size(), 10), 0.75f); - for (Map.Entry entry : tiles.entrySet()) { - int pos = entry.getKey(); - CompoundTag tag = entry.getValue().nbt(); - int[] previous = nbtToPosMap.computeIfAbsent(tag, k -> new int[] {pos}); - int[] newPoses = new int[previous.length + 1]; - System.arraycopy(previous, 0, newPoses, 0, previous.length); - newPoses[previous.length] = pos; - nbtToPosMap.put(tag, newPoses); - } - for (Map.Entry entry : nbtToPosMap.entrySet()) { - CompoundTag blockEntityTag = new CompoundTag(); - blockEntityTag.put("data", entry.getKey()); - blockEntityTag.putIntArray("pos", entry.getValue()); - result.add(blockEntityTag); + for (Map.Entry entry : tiles.entrySet()) { + result.add(entry.getValue().saveAsTag()); } return result; } - @ApiStatus.Experimental - public static Map deserialize(ListTag tag) { - Map result = new Object2ObjectOpenHashMap<>(Math.max(tag.size(), 16), 0.5f); + public static List deserialize(CEChunk chunk, ListTag tag) { + List blockEntities = new ArrayList<>(tag.size()); for (int i = 0; i < tag.size(); i++) { - CompoundTag blockEntityTag = tag.getCompound(i); - CompoundTag data = blockEntityTag.getCompound("data"); - int[] pos = blockEntityTag.getIntArray("pos"); - for (int j = 0; j < pos.length; j++) { - result.put(j, new BlockEntityState(data)); - } + CompoundTag data = tag.getCompound(i); + BlockPos pos = BlockEntity.readPosAndVerify(data, chunk.chunkPos()); + ImmutableBlockState blockState = chunk.getBlockState(pos); } - return result; + return blockEntities; } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultChunkSerializer.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultChunkSerializer.java index 1a44e0efc..fafceb99c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultChunkSerializer.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/serialization/DefaultChunkSerializer.java @@ -47,6 +47,6 @@ public final class DefaultChunkSerializer { } } ListTag blockEntities = Optional.ofNullable(chunkNbt.getList("block_entities")).orElse(new ListTag()); - return new CEChunk(world, pos, sectionArray, DefaultBlockEntitySerializer.deserialize(blockEntities)); + return new CEChunk(world, pos, sectionArray, blockEntities); } }