9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-19 15:09:15 +00:00

方块实体1

This commit is contained in:
XiaoMoMi
2025-09-02 04:34:18 +08:00
parent 0b05473df4
commit d4ca37a3e1
35 changed files with 545 additions and 168 deletions

View File

@@ -73,7 +73,7 @@ public class BlockItemBehavior extends BlockBoundItemBehavior {
return InteractionResult.FAIL; return InteractionResult.FAIL;
} }
if (!context.canPlace()) { if (!context.canPlace()) {
return InteractionResult.FAIL; return InteractionResult.PASS;
} }
Player player = context.getPlayer(); Player player = context.getPlayer();

View File

@@ -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.CraftEngine;
import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager; import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager;
import net.momirealms.craftengine.core.plugin.locale.MessageConstants; import net.momirealms.craftengine.core.plugin.locale.MessageConstants;
import net.momirealms.craftengine.core.util.ItemUtils;
import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.Key;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@@ -30,7 +31,7 @@ public class SearchRecipePlayerCommand extends BukkitCommandFeature<CommandSende
Player player = context.sender(); Player player = context.sender();
BukkitServerPlayer serverPlayer = plugin().adapt(player); BukkitServerPlayer serverPlayer = plugin().adapt(player);
Item<?> item = serverPlayer.getItemInHand(InteractionHand.MAIN_HAND); Item<?> item = serverPlayer.getItemInHand(InteractionHand.MAIN_HAND);
if (item == null) { if (ItemUtils.isEmpty(item)) {
handleFeedback(context, MessageConstants.COMMAND_SEARCH_RECIPE_NO_ITEM); handleFeedback(context, MessageConstants.COMMAND_SEARCH_RECIPE_NO_ITEM);
return; return;
} }

View File

@@ -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.CraftEngine;
import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager; import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager;
import net.momirealms.craftengine.core.plugin.locale.MessageConstants; import net.momirealms.craftengine.core.plugin.locale.MessageConstants;
import net.momirealms.craftengine.core.util.ItemUtils;
import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.Key;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@@ -30,7 +31,7 @@ public class SearchUsagePlayerCommand extends BukkitCommandFeature<CommandSender
Player player = context.sender(); Player player = context.sender();
BukkitServerPlayer serverPlayer = plugin().adapt(player); BukkitServerPlayer serverPlayer = plugin().adapt(player);
Item<?> item = serverPlayer.getItemInHand(InteractionHand.MAIN_HAND); Item<?> item = serverPlayer.getItemInHand(InteractionHand.MAIN_HAND);
if (item.isEmpty()) { if (ItemUtils.isEmpty(item)) {
handleFeedback(context, MessageConstants.COMMAND_SEARCH_USAGE_NO_ITEM); handleFeedback(context, MessageConstants.COMMAND_SEARCH_USAGE_NO_ITEM);
return; return;
} }

View File

@@ -1,15 +1,10 @@
package net.momirealms.craftengine.bukkit.plugin.command.feature; package net.momirealms.craftengine.bukkit.plugin.command.feature;
import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature; 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.CraftEngine;
import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager; 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.command.CommandSender;
import org.bukkit.entity.Player;
import org.incendo.cloud.Command; import org.incendo.cloud.Command;
import org.incendo.cloud.parser.standard.IntegerParser;
public class TestCommand extends BukkitCommandFeature<CommandSender> { public class TestCommand extends BukkitCommandFeature<CommandSender> {
@@ -20,22 +15,9 @@ public class TestCommand extends BukkitCommandFeature<CommandSender> {
@Override @Override
public Command.Builder<? extends CommandSender> assembleCommand(org.incendo.cloud.CommandManager<CommandSender> manager, Command.Builder<CommandSender> builder) { public Command.Builder<? extends CommandSender> assembleCommand(org.incendo.cloud.CommandManager<CommandSender> manager, Command.Builder<CommandSender> builder) {
return builder return builder
.required("start", IntegerParser.integerParser(0))
.senderType(Player.class)
.handler(context -> { .handler(context -> {
Player sender = context.sender(); // DO NOT PUSH ANY CODE FOR TEST COMMAND
int start = context.get("start"); // 禁止推送含有实现的Test指令
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);
}
}
}
}); });
} }

View File

@@ -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.plugin.text.component.ComponentProvider;
import net.momirealms.craftengine.core.util.*; import net.momirealms.craftengine.core.util.*;
import net.momirealms.craftengine.core.world.*; 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.Palette;
import net.momirealms.craftengine.core.world.chunk.PalettedContainer; import net.momirealms.craftengine.core.world.chunk.PalettedContainer;
import net.momirealms.craftengine.core.world.chunk.packet.BlockEntityData; import net.momirealms.craftengine.core.world.chunk.packet.BlockEntityData;
@@ -263,11 +264,11 @@ public class PacketConsumers {
FriendlyByteBuf buf = event.getBuffer(); FriendlyByteBuf buf = event.getBuffer();
if (VersionHelper.isOrAbove1_20_2()) { if (VersionHelper.isOrAbove1_20_2()) {
long chunkPos = buf.readLong(); long chunkPos = buf.readLong();
user.setChunkTrackStatus(new ChunkPos(chunkPos), false); user.removeTrackedChunk(chunkPos);
} else { } else {
int x = buf.readInt(); int x = buf.readInt();
int y = buf.readInt(); int y = buf.readInt();
user.setChunkTrackStatus(ChunkPos.of(x, y), false); user.removeTrackedChunk(ChunkPos.asLong(x, y));
} }
} catch (Exception e) { } catch (Exception e) {
CraftEngine.instance().logger().warn("Failed to handle ClientboundForgetLevelChunkPacket", e); CraftEngine.instance().logger().warn("Failed to handle ClientboundForgetLevelChunkPacket", e);
@@ -280,7 +281,6 @@ public class PacketConsumers {
FriendlyByteBuf buf = event.getBuffer(); FriendlyByteBuf buf = event.getBuffer();
int chunkX = buf.readInt(); int chunkX = buf.readInt();
int chunkZ = buf.readInt(); int chunkZ = buf.readInt();
player.setChunkTrackStatus(ChunkPos.of(chunkX, chunkZ), true);
boolean named = !VersionHelper.isOrAbove1_20_2(); boolean named = !VersionHelper.isOrAbove1_20_2();
// ClientboundLevelChunkPacketData // ClientboundLevelChunkPacketData
int heightmapsCount = 0; int heightmapsCount = 0;
@@ -366,6 +366,9 @@ public class PacketConsumers {
} }
buffer = newBuf.array(); buffer = newBuf.array();
} }
// 开始修改
event.setChanged(true);
buf.clear(); buf.clear();
buf.writeVarInt(event.packetID()); buf.writeVarInt(event.packetID());
buf.writeInt(chunkX); buf.writeInt(chunkX);
@@ -394,7 +397,10 @@ public class PacketConsumers {
buf.writeBitSet(emptyBlockYMask); buf.writeBitSet(emptyBlockYMask);
buf.writeByteArrayList(skyUpdates); buf.writeByteArrayList(skyUpdates);
buf.writeByteArrayList(blockUpdates); buf.writeByteArrayList(blockUpdates);
event.setChanged(true);
ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ);
// 记录加载的区块
player.addTrackedChunk(chunkPos.longKey, new ChunkStatus());
} catch (Exception e) { } catch (Exception e) {
CraftEngine.instance().logger().warn("Failed to handle ClientboundLevelChunkWithLightPacket", e); CraftEngine.instance().logger().warn("Failed to handle ClientboundLevelChunkWithLightPacket", e);
} }

View File

@@ -1,9 +1,9 @@
package net.momirealms.craftengine.bukkit.plugin.network.payload.protocol; 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.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.NetWorkUser;
import net.momirealms.craftengine.core.plugin.network.codec.NetworkCodec;
import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.BuiltInRegistries;
import net.momirealms.craftengine.core.util.FriendlyByteBuf; import net.momirealms.craftengine.core.util.FriendlyByteBuf;
import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.Key;

View File

@@ -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.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.NetWorkUser;
import net.momirealms.craftengine.core.plugin.network.codec.NetworkCodec;
import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.BuiltInRegistries;
import net.momirealms.craftengine.core.util.FriendlyByteBuf; import net.momirealms.craftengine.core.util.FriendlyByteBuf;
import net.momirealms.craftengine.core.util.IntIdentityList; import net.momirealms.craftengine.core.util.IntIdentityList;

View File

@@ -3,10 +3,10 @@ package net.momirealms.craftengine.bukkit.plugin.network.payload.protocol;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TranslationArgument; 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.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.NetWorkUser;
import net.momirealms.craftengine.core.plugin.network.codec.NetworkCodec;
import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.BuiltInRegistries;
import net.momirealms.craftengine.core.util.FriendlyByteBuf; import net.momirealms.craftengine.core.util.FriendlyByteBuf;
import net.momirealms.craftengine.core.util.IntIdentityList; import net.momirealms.craftengine.core.util.IntIdentityList;

View File

@@ -1,5 +1,6 @@
package net.momirealms.craftengine.bukkit.plugin.user; package net.momirealms.craftengine.bukkit.plugin.user;
import ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.Channel; 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.Key;
import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.util.VersionHelper;
import net.momirealms.craftengine.core.world.BlockPos; 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.World;
import net.momirealms.craftengine.core.world.WorldEvents; import net.momirealms.craftengine.core.world.WorldEvents;
import net.momirealms.craftengine.core.world.chunk.ChunkStatus;
import org.bukkit.*; import org.bukkit.*;
import org.bukkit.attribute.Attribute; import org.bukkit.attribute.Attribute;
import org.bukkit.attribute.AttributeInstance; import org.bukkit.attribute.AttributeInstance;
@@ -113,11 +114,9 @@ public class BukkitServerPlayer extends Player {
// cooldown data // cooldown data
private CooldownData cooldownData; private CooldownData cooldownData;
// tracked chunks // tracked chunks
private final Set<ChunkPos> trackedChunks = Collections.synchronizedSet(new HashSet<>()); private ConcurrentLong2ReferenceChainedHashTable<ChunkStatus> trackedChunks;
// relighted chunks
private final Set<ChunkPos> relightedChunks = Collections.synchronizedSet(new HashSet<>());
// entity view // entity view
private final Map<Integer, EntityPacketHandler> entityTypeView = new ConcurrentHashMap<>(); private Map<Integer, EntityPacketHandler> entityTypeView;
public BukkitServerPlayer(BukkitCraftEngine plugin, @Nullable Channel channel) { public BukkitServerPlayer(BukkitCraftEngine plugin, @Nullable Channel channel) {
this.channel = channel; this.channel = channel;
@@ -139,6 +138,8 @@ public class BukkitServerPlayer extends Player {
this.uuid = player.getUniqueId(); this.uuid = player.getUniqueId();
this.name = player.getName(); this.name = player.getName();
byte[] bytes = player.getPersistentDataContainer().get(KeyUtils.toNamespacedKey(CooldownData.COOLDOWN_KEY), PersistentDataType.BYTE_ARRAY); 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 { try {
this.cooldownData = CooldownData.fromBytes(bytes); this.cooldownData = CooldownData.fromBytes(bytes);
} catch (IOException e) { } catch (IOException e) {
@@ -1050,17 +1051,23 @@ public class BukkitServerPlayer extends Player {
} }
@Override @Override
public boolean isChunkTracked(ChunkPos chunkPos) { public boolean isChunkTracked(long chunkPos) {
return this.trackedChunks.contains(chunkPos); return this.trackedChunks.containsKey(chunkPos);
} }
@Override @Override
public void setChunkTrackStatus(ChunkPos chunkPos, boolean tracked) { public ChunkStatus getTrackedChunk(long chunkPos) {
if (tracked) { return this.trackedChunks.get(chunkPos);
this.trackedChunks.add(chunkPos);
} else {
this.trackedChunks.remove(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 @Override

View File

@@ -36,4 +36,42 @@ public final class LightUtils {
CraftEngine.instance().logger().warn("Could not update light for world " + world.getName(), e); 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);
// }
// }
} }

View File

@@ -23,6 +23,7 @@ public class BukkitCEWorld extends CEWorld {
@Override @Override
public void tick() { public void tick() {
super.tick();
HashSet<SectionPos> poses; HashSet<SectionPos> poses;
synchronized (super.updatedSectionSet) { synchronized (super.updatedSectionSet) {
poses = new HashSet<>(super.updatedSectionSet); poses = new HashSet<>(super.updatedSectionSet);

View File

@@ -19,11 +19,13 @@ public interface CustomBlock {
Key id(); Key id();
@Nullable LootTable<?> lootTable(); @Nullable
LootTable<?> lootTable();
void execute(PlayerOptionalContext context, EventTrigger trigger); void execute(PlayerOptionalContext context, EventTrigger trigger);
@NotNull BlockStateVariantProvider variantProvider(); @NotNull
BlockStateVariantProvider variantProvider();
List<ImmutableBlockState> getPossibleStates(CompoundTag nbt); List<ImmutableBlockState> getPossibleStates(CompoundTag nbt);

View File

@@ -1,6 +1,8 @@
package net.momirealms.craftengine.core.block; package net.momirealms.craftengine.core.block;
import it.unimi.dsi.fastutil.objects.Reference2ObjectArrayMap; 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.block.properties.Property;
import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.item.Item;
@@ -24,6 +26,7 @@ public final class ImmutableBlockState extends BlockStateHolder {
private BlockBehavior behavior; private BlockBehavior behavior;
private Integer hashCode; private Integer hashCode;
private BlockSettings settings; private BlockSettings settings;
private BlockEntityType<? extends BlockEntity> blockEntityType;
ImmutableBlockState( ImmutableBlockState(
Holder<CustomBlock> owner, Holder<CustomBlock> owner,
@@ -48,6 +51,14 @@ public final class ImmutableBlockState extends BlockStateHolder {
this.settings = settings; this.settings = settings;
} }
public BlockEntityType<? extends BlockEntity> blockEntityType() {
return blockEntityType;
}
public void setBlockEntityType(BlockEntityType<? extends BlockEntity> blockEntityType) {
this.blockEntityType = blockEntityType;
}
public boolean isEmpty() { public boolean isEmpty() {
return this == EmptyBlock.STATE; return this == EmptyBlock.STATE;
} }
@@ -67,6 +78,10 @@ public final class ImmutableBlockState extends BlockStateHolder {
return this.hashCode; return this.hashCode;
} }
public boolean hasBlockEntity() {
return this.blockEntityType != null;
}
public BlockStateWrapper customBlockState() { public BlockStateWrapper customBlockState() {
return this.customBlockState; return this.customBlockState;
} }

View File

@@ -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<? extends BlockEntity> type;
protected CEWorld world;
protected boolean valid;
protected BlockEntity(BlockEntityType<? extends BlockEntity> 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<? extends BlockEntity> 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 extends BlockEntity> {
T create(BlockPos pos, ImmutableBlockState state);
}
}

View File

@@ -0,0 +1,6 @@
package net.momirealms.craftengine.core.block.entity;
import net.momirealms.craftengine.core.util.Key;
public record BlockEntityType<T extends BlockEntity>(Key id, BlockEntity.Factory<T> factory) {
}

View File

@@ -0,0 +1,5 @@
package net.momirealms.craftengine.core.block.entity;
public class BlockEntityTypes {
}

View File

@@ -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<T extends BlockEntity> {
void tick(CEWorld world, BlockPos pos, ImmutableBlockState state, T blockEntity);
}

View File

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

View File

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

View File

@@ -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<T extends BlockEntity> implements TickingBlockEntity {
private final T blockEntity;
private final BlockEntityTicker<T> ticker;
private final CEChunk chunk;
public TickingBlockEntityImpl(CEChunk chunk, T blockEntity, BlockEntityTicker<T> 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();
}
}

View File

@@ -120,6 +120,7 @@ public class Config {
protected boolean block$predict_breaking; protected boolean block$predict_breaking;
protected int block$predict_breaking_interval; protected int block$predict_breaking_interval;
protected double block$extended_interaction_range; protected double block$extended_interaction_range;
protected boolean block$chunk_relighter;
protected boolean recipe$enable; protected boolean recipe$enable;
protected boolean recipe$disable_vanilla_recipes$all; protected boolean recipe$disable_vanilla_recipes$all;
@@ -224,6 +225,8 @@ public class Config {
.builder() .builder()
.setVersioning(new BasicVersioning("config-version")) .setVersioning(new BasicVersioning("config-version"))
.addIgnoredRoute(PluginProperties.getValue("config"), "resource-pack.delivery.hosting", '.') .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()); .build());
} }
try { try {
@@ -384,6 +387,7 @@ public class Config {
block$predict_breaking = config.getBoolean("block.predict-breaking.enable", true); 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$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$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
recipe$enable = config.getBoolean("recipe.enable", true); recipe$enable = config.getBoolean("recipe.enable", true);
@@ -449,6 +453,10 @@ public class Config {
return instance.debug$item; return instance.debug$item;
} }
public static boolean debugBlockEntity() {
return false;
}
public static boolean debugFurniture() { public static boolean debugFurniture() {
return instance.debug$furniture; return instance.debug$furniture;
} }
@@ -873,6 +881,10 @@ public class Config {
return instance.item$update_triggers$drop; return instance.item$update_triggers$drop;
} }
public static boolean enableChunkRelighter() {
return instance.block$chunk_relighter;
}
public void setObf(boolean enable) { public void setObf(boolean enable) {
this.resource_pack$protection$obfuscation$enable = enable; this.resource_pack$protection$obfuscation$enable = enable;
} }

View File

@@ -10,7 +10,8 @@ public enum Debugger {
PACKET(Config::debugPacket), PACKET(Config::debugPacket),
FURNITURE(Config::debugFurniture), FURNITURE(Config::debugFurniture),
RESOURCE_PACK(Config::debugResourcePack), RESOURCE_PACK(Config::debugResourcePack),
ITEM(Config::debugItem); ITEM(Config::debugItem),
BLOCK_ENTITY(Config::debugBlockEntity);
private final Supplier<Boolean> condition; private final Supplier<Boolean> condition;
@@ -26,7 +27,11 @@ public enum Debugger {
public void warn(Supplier<String> message, Throwable e) { public void warn(Supplier<String> message, Throwable e) {
if (this.condition.get()) { if (this.condition.get()) {
if (e != null) {
CraftEngine.instance().logger().warn("[DEBUG] " + message.get(), e); CraftEngine.instance().logger().warn("[DEBUG] " + message.get(), e);
} else {
CraftEngine.instance().logger().warn("[DEBUG] " + message.get());
}
} }
} }
} }

View File

@@ -6,7 +6,7 @@ import net.kyori.adventure.text.Component;
import net.momirealms.craftengine.core.plugin.Plugin; import net.momirealms.craftengine.core.plugin.Plugin;
import net.momirealms.craftengine.core.util.IntIdentityList; import net.momirealms.craftengine.core.util.IntIdentityList;
import net.momirealms.craftengine.core.util.Key; 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.ApiStatus;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -79,13 +79,17 @@ public interface NetWorkUser {
boolean shouldProcessFinishConfiguration(); 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 clearTrackedChunks();
void setClientBlockList(IntIdentityList blockList); void removeTrackedChunk(long chunkPos);
IntIdentityList clientBlockList(); IntIdentityList clientBlockList();
void setClientBlockList(IntIdentityList integers);
} }

View File

@@ -9,12 +9,15 @@ import java.util.*;
public abstract class AbstractMappedRegistry<T> implements WritableRegistry<T> { public abstract class AbstractMappedRegistry<T> implements WritableRegistry<T> {
protected final ResourceKey<? extends Registry<T>> key; protected final ResourceKey<? extends Registry<T>> key;
protected final Map<Key, Holder.Reference<T>> byResourceLocation = new HashMap<>(512); protected final Map<Key, Holder.Reference<T>> byResourceLocation;
protected final Map<ResourceKey<T>, Holder.Reference<T>> byResourceKey = new HashMap<>(512); protected final Map<ResourceKey<T>, Holder.Reference<T>> byResourceKey;
protected final List<Holder.Reference<T>> byId = new ArrayList<>(512); protected final List<Holder.Reference<T>> byId;
protected AbstractMappedRegistry(ResourceKey<? extends Registry<T>> key) { protected AbstractMappedRegistry(ResourceKey<? extends Registry<T>> key, int expectedSize) {
this.key = key; this.key = key;
this.byResourceLocation = new HashMap<>(expectedSize);
this.byResourceKey = new HashMap<>(expectedSize);
this.byId = new ArrayList<>(expectedSize);
} }
@Override @Override

View File

@@ -46,51 +46,51 @@ import net.momirealms.craftengine.core.util.FriendlyByteBuf;
import net.momirealms.craftengine.core.util.ResourceKey; import net.momirealms.craftengine.core.util.ResourceKey;
public class BuiltInRegistries { public class BuiltInRegistries {
public static final Registry<CustomBlock> BLOCK = createDynamicBoundRegistry(Registries.BLOCK); public static final Registry<CustomBlock> BLOCK = createDynamicBoundRegistry(Registries.BLOCK, 512);
public static final Registry<BlockBehaviorFactory> BLOCK_BEHAVIOR_FACTORY = createConstantBoundRegistry(Registries.BLOCK_BEHAVIOR_FACTORY); public static final Registry<BlockBehaviorFactory> BLOCK_BEHAVIOR_FACTORY = createConstantBoundRegistry(Registries.BLOCK_BEHAVIOR_FACTORY, 64);
public static final Registry<ItemDataModifierFactory<?>> ITEM_DATA_MODIFIER_FACTORY = createConstantBoundRegistry(Registries.ITEM_DATA_MODIFIER_FACTORY); public static final Registry<ItemDataModifierFactory<?>> ITEM_DATA_MODIFIER_FACTORY = createConstantBoundRegistry(Registries.ITEM_DATA_MODIFIER_FACTORY, 64);
public static final Registry<ItemBehaviorFactory> ITEM_BEHAVIOR_FACTORY = createConstantBoundRegistry(Registries.ITEM_BEHAVIOR_FACTORY); public static final Registry<ItemBehaviorFactory> ITEM_BEHAVIOR_FACTORY = createConstantBoundRegistry(Registries.ITEM_BEHAVIOR_FACTORY, 64);
public static final Registry<PropertyFactory> PROPERTY_FACTORY = createConstantBoundRegistry(Registries.PROPERTY_FACTORY); public static final Registry<PropertyFactory> PROPERTY_FACTORY = createConstantBoundRegistry(Registries.PROPERTY_FACTORY, 16);
public static final Registry<LootFunctionFactory<?>> LOOT_FUNCTION_FACTORY = createConstantBoundRegistry(Registries.LOOT_FUNCTION_FACTORY); public static final Registry<LootFunctionFactory<?>> LOOT_FUNCTION_FACTORY = createConstantBoundRegistry(Registries.LOOT_FUNCTION_FACTORY, 32);
public static final Registry<ConditionFactory<LootContext>> LOOT_CONDITION_FACTORY = createConstantBoundRegistry(Registries.LOOT_CONDITION_FACTORY); public static final Registry<ConditionFactory<LootContext>> LOOT_CONDITION_FACTORY = createConstantBoundRegistry(Registries.LOOT_CONDITION_FACTORY, 32);
public static final Registry<LootEntryContainerFactory<?>> LOOT_ENTRY_CONTAINER_FACTORY = createConstantBoundRegistry(Registries.LOOT_ENTRY_CONTAINER_FACTORY); public static final Registry<LootEntryContainerFactory<?>> LOOT_ENTRY_CONTAINER_FACTORY = createConstantBoundRegistry(Registries.LOOT_ENTRY_CONTAINER_FACTORY, 16);
public static final Registry<NumberProviderFactory> NUMBER_PROVIDER_FACTORY = createConstantBoundRegistry(Registries.NUMBER_PROVIDER_FACTORY); public static final Registry<NumberProviderFactory> NUMBER_PROVIDER_FACTORY = createConstantBoundRegistry(Registries.NUMBER_PROVIDER_FACTORY, 16);
public static final Registry<TemplateArgumentFactory> TEMPLATE_ARGUMENT_FACTORY = createConstantBoundRegistry(Registries.TEMPLATE_ARGUMENT_FACTORY); public static final Registry<TemplateArgumentFactory> TEMPLATE_ARGUMENT_FACTORY = createConstantBoundRegistry(Registries.TEMPLATE_ARGUMENT_FACTORY, 16);
public static final Registry<ItemModelFactory> ITEM_MODEL_FACTORY = createConstantBoundRegistry(Registries.ITEM_MODEL_FACTORY); public static final Registry<ItemModelFactory> ITEM_MODEL_FACTORY = createConstantBoundRegistry(Registries.ITEM_MODEL_FACTORY, 16);
public static final Registry<ItemModelReader> ITEM_MODEL_READER = createConstantBoundRegistry(Registries.ITEM_MODEL_READER); public static final Registry<ItemModelReader> ITEM_MODEL_READER = createConstantBoundRegistry(Registries.ITEM_MODEL_READER, 16);
public static final Registry<TintFactory> TINT_FACTORY = createConstantBoundRegistry(Registries.TINT_FACTORY); public static final Registry<TintFactory> TINT_FACTORY = createConstantBoundRegistry(Registries.TINT_FACTORY, 16);
public static final Registry<TintReader> TINT_READER = createConstantBoundRegistry(Registries.TINT_READER); public static final Registry<TintReader> TINT_READER = createConstantBoundRegistry(Registries.TINT_READER, 16);
public static final Registry<SpecialModelFactory> SPECIAL_MODEL_FACTORY = createConstantBoundRegistry(Registries.SPECIAL_MODEL_FACTORY); public static final Registry<SpecialModelFactory> SPECIAL_MODEL_FACTORY = createConstantBoundRegistry(Registries.SPECIAL_MODEL_FACTORY, 16);
public static final Registry<SpecialModelReader> SPECIAL_MODEL_READER = createConstantBoundRegistry(Registries.SPECIAL_MODEL_READER); public static final Registry<SpecialModelReader> SPECIAL_MODEL_READER = createConstantBoundRegistry(Registries.SPECIAL_MODEL_READER, 16);
public static final Registry<RangeDispatchPropertyFactory> RANGE_DISPATCH_PROPERTY_FACTORY = createConstantBoundRegistry(Registries.RANGE_DISPATCH_PROPERTY_FACTORY); public static final Registry<RangeDispatchPropertyFactory> RANGE_DISPATCH_PROPERTY_FACTORY = createConstantBoundRegistry(Registries.RANGE_DISPATCH_PROPERTY_FACTORY, 16);
public static final Registry<RangeDispatchPropertyReader> RANGE_DISPATCH_PROPERTY_READER = createConstantBoundRegistry(Registries.RANGE_DISPATCH_PROPERTY_READER); public static final Registry<RangeDispatchPropertyReader> RANGE_DISPATCH_PROPERTY_READER = createConstantBoundRegistry(Registries.RANGE_DISPATCH_PROPERTY_READER, 16);
public static final Registry<ConditionPropertyFactory> CONDITION_PROPERTY_FACTORY = createConstantBoundRegistry(Registries.CONDITION_PROPERTY_FACTORY); public static final Registry<ConditionPropertyFactory> CONDITION_PROPERTY_FACTORY = createConstantBoundRegistry(Registries.CONDITION_PROPERTY_FACTORY, 16);
public static final Registry<ConditionPropertyReader> CONDITION_PROPERTY_READER = createConstantBoundRegistry(Registries.CONDITION_PROPERTY_READER); public static final Registry<ConditionPropertyReader> CONDITION_PROPERTY_READER = createConstantBoundRegistry(Registries.CONDITION_PROPERTY_READER, 16);
public static final Registry<SelectPropertyFactory> SELECT_PROPERTY_FACTORY = createConstantBoundRegistry(Registries.SELECT_PROPERTY_FACTORY); public static final Registry<SelectPropertyFactory> SELECT_PROPERTY_FACTORY = createConstantBoundRegistry(Registries.SELECT_PROPERTY_FACTORY, 16);
public static final Registry<SelectPropertyReader> SELECT_PROPERTY_READER = createConstantBoundRegistry(Registries.SELECT_PROPERTY_READER); public static final Registry<SelectPropertyReader> SELECT_PROPERTY_READER = createConstantBoundRegistry(Registries.SELECT_PROPERTY_READER, 16);
public static final Registry<RecipeSerializer<?, ? extends Recipe<?>>> RECIPE_SERIALIZER = createConstantBoundRegistry(Registries.RECIPE_FACTORY); public static final Registry<RecipeSerializer<?, ? extends Recipe<?>>> RECIPE_SERIALIZER = createConstantBoundRegistry(Registries.RECIPE_FACTORY, 16);
public static final Registry<ApplyBonusCountFunction.FormulaFactory> FORMULA_FACTORY = createConstantBoundRegistry(Registries.FORMULA_FACTORY); public static final Registry<ApplyBonusCountFunction.FormulaFactory> FORMULA_FACTORY = createConstantBoundRegistry(Registries.FORMULA_FACTORY, 16);
public static final Registry<ConditionFactory<PathContext>> PATH_MATCHER_FACTORY = createConstantBoundRegistry(Registries.PATH_MATCHER_FACTORY); public static final Registry<ConditionFactory<PathContext>> PATH_MATCHER_FACTORY = createConstantBoundRegistry(Registries.PATH_MATCHER_FACTORY, 16);
public static final Registry<ResolutionFactory> RESOLUTION_FACTORY = createConstantBoundRegistry(Registries.RESOLUTION_FACTORY); public static final Registry<ResolutionFactory> RESOLUTION_FACTORY = createConstantBoundRegistry(Registries.RESOLUTION_FACTORY, 16);
public static final Registry<CustomSmithingTransformRecipe.ItemDataProcessor.ProcessorFactory> SMITHING_RESULT_PROCESSOR_FACTORY = createConstantBoundRegistry(Registries.SMITHING_RESULT_PROCESSOR_FACTORY); public static final Registry<CustomSmithingTransformRecipe.ItemDataProcessor.ProcessorFactory> SMITHING_RESULT_PROCESSOR_FACTORY = createConstantBoundRegistry(Registries.SMITHING_RESULT_PROCESSOR_FACTORY, 16);
public static final Registry<HitBoxFactory> HITBOX_FACTORY = createConstantBoundRegistry(Registries.HITBOX_FACTORY); public static final Registry<HitBoxFactory> HITBOX_FACTORY = createConstantBoundRegistry(Registries.HITBOX_FACTORY, 16);
public static final Registry<ResourcePackHostFactory> RESOURCE_PACK_HOST_FACTORY = createConstantBoundRegistry(Registries.RESOURCE_PACK_HOST_FACTORY); public static final Registry<ResourcePackHostFactory> RESOURCE_PACK_HOST_FACTORY = createConstantBoundRegistry(Registries.RESOURCE_PACK_HOST_FACTORY, 16);
public static final Registry<FunctionFactory<PlayerOptionalContext>> EVENT_FUNCTION_FACTORY = createConstantBoundRegistry(Registries.EVENT_FUNCTION_FACTORY); public static final Registry<FunctionFactory<PlayerOptionalContext>> EVENT_FUNCTION_FACTORY = createConstantBoundRegistry(Registries.EVENT_FUNCTION_FACTORY, 128);
public static final Registry<ConditionFactory<PlayerOptionalContext>> EVENT_CONDITION_FACTORY = createConstantBoundRegistry(Registries.EVENT_CONDITION_FACTORY); public static final Registry<ConditionFactory<PlayerOptionalContext>> EVENT_CONDITION_FACTORY = createConstantBoundRegistry(Registries.EVENT_CONDITION_FACTORY, 128);
public static final Registry<PlayerSelectorFactory<?>> PLAYER_SELECTOR_FACTORY = createConstantBoundRegistry(Registries.PLAYER_SELECTOR_FACTORY); public static final Registry<PlayerSelectorFactory<?>> PLAYER_SELECTOR_FACTORY = createConstantBoundRegistry(Registries.PLAYER_SELECTOR_FACTORY, 16);
public static final Registry<EquipmentFactory> EQUIPMENT_FACTORY = createConstantBoundRegistry(Registries.EQUIPMENT_FACTORY); public static final Registry<EquipmentFactory> EQUIPMENT_FACTORY = createConstantBoundRegistry(Registries.EQUIPMENT_FACTORY, 8);
public static final Registry<SlotDisplay.Type> SLOT_DISPLAY_TYPE = createConstantBoundRegistry(Registries.SLOT_DISPLAY_TYPE); public static final Registry<SlotDisplay.Type> SLOT_DISPLAY_TYPE = createConstantBoundRegistry(Registries.SLOT_DISPLAY_TYPE, 16);
public static final Registry<RecipeDisplay.Type> RECIPE_DISPLAY_TYPE = createConstantBoundRegistry(Registries.RECIPE_DISPLAY_TYPE); public static final Registry<RecipeDisplay.Type> RECIPE_DISPLAY_TYPE = createConstantBoundRegistry(Registries.RECIPE_DISPLAY_TYPE, 16);
public static final Registry<LegacyRecipe.Type> LEGACY_RECIPE_TYPE = createConstantBoundRegistry(Registries.LEGACY_RECIPE_TYPE); public static final Registry<LegacyRecipe.Type> LEGACY_RECIPE_TYPE = createConstantBoundRegistry(Registries.LEGACY_RECIPE_TYPE, 16);
public static final Registry<PostProcessor.Type<?>> RECIPE_POST_PROCESSOR_TYPE = createConstantBoundRegistry(Registries.RECIPE_POST_PROCESSOR_TYPE); public static final Registry<PostProcessor.Type<?>> RECIPE_POST_PROCESSOR_TYPE = createConstantBoundRegistry(Registries.RECIPE_POST_PROCESSOR_TYPE, 16);
public static final Registry<ItemUpdaterType<?>> ITEM_UPDATER_TYPE = createConstantBoundRegistry(Registries.ITEM_UPDATER_TYPE); public static final Registry<ItemUpdaterType<?>> ITEM_UPDATER_TYPE = createConstantBoundRegistry(Registries.ITEM_UPDATER_TYPE, 16);
public static final Registry<NetworkCodec<FriendlyByteBuf, ? extends ModPacket>> MOD_PACKET = createConstantBoundRegistry(Registries.MOD_PACKET); public static final Registry<NetworkCodec<FriendlyByteBuf, ? extends ModPacket>> MOD_PACKET = createConstantBoundRegistry(Registries.MOD_PACKET, 16);
private static <T> Registry<T> createConstantBoundRegistry(ResourceKey<? extends Registry<T>> key) { private static <T> Registry<T> createConstantBoundRegistry(ResourceKey<? extends Registry<T>> key, int expectedSize) {
return new ConstantBoundRegistry<>(key); return new ConstantBoundRegistry<>(key, expectedSize);
} }
private static <T> Registry<T> createDynamicBoundRegistry(ResourceKey<? extends Registry<T>> key) { private static <T> Registry<T> createDynamicBoundRegistry(ResourceKey<? extends Registry<T>> key, int expectedSize) {
return new DynamicBoundRegistry<>(key); return new DynamicBoundRegistry<>(key, expectedSize);
} }
} }

View File

@@ -12,10 +12,11 @@ import java.util.Objects;
public class ConstantBoundRegistry<T> extends AbstractMappedRegistry<T> { public class ConstantBoundRegistry<T> extends AbstractMappedRegistry<T> {
protected final Reference2IntMap<T> toId = MCUtils.make(new Reference2IntOpenHashMap<>(), map -> map.defaultReturnValue(-1)); protected final Reference2IntMap<T> toId = MCUtils.make(new Reference2IntOpenHashMap<>(), map -> map.defaultReturnValue(-1));
protected final Map<T, Holder.Reference<T>> byValue = new IdentityHashMap<>(512); protected final Map<T, Holder.Reference<T>> byValue;
public ConstantBoundRegistry(ResourceKey<? extends Registry<T>> key) { public ConstantBoundRegistry(ResourceKey<? extends Registry<T>> key, int expectedSize) {
super(key); super(key, expectedSize);
this.byValue = new IdentityHashMap<>(expectedSize);
} }
@Override @Override

View File

@@ -8,8 +8,8 @@ import java.util.Objects;
public class DynamicBoundRegistry<T> extends AbstractMappedRegistry<T> { public class DynamicBoundRegistry<T> extends AbstractMappedRegistry<T> {
public DynamicBoundRegistry(ResourceKey<? extends Registry<T>> key) { public DynamicBoundRegistry(ResourceKey<? extends Registry<T>> key, int expectedSize) {
super(key); super(key, expectedSize);
} }
@Override @Override

View File

@@ -86,5 +86,5 @@ public class Registries {
public static final ResourceKey<Registry<LegacyRecipe.Type>> LEGACY_RECIPE_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("legacy_recipe_type")); public static final ResourceKey<Registry<LegacyRecipe.Type>> LEGACY_RECIPE_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("legacy_recipe_type"));
public static final ResourceKey<Registry<PostProcessor.Type<?>>> RECIPE_POST_PROCESSOR_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("recipe_post_processor_type")); public static final ResourceKey<Registry<PostProcessor.Type<?>>> RECIPE_POST_PROCESSOR_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("recipe_post_processor_type"));
public static final ResourceKey<Registry<ItemUpdaterType<?>>> ITEM_UPDATER_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("item_updater_type")); public static final ResourceKey<Registry<ItemUpdaterType<?>>> ITEM_UPDATER_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("item_updater_type"));
public static final ResourceKey<Registry<NetworkCodec<FriendlyByteBuf, ? extends ModPacket>>> MOD_PACKET = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("mod_packet")); public static final ResourceKey<Registry<NetworkCodec<FriendlyByteBuf, ? extends ModPacket>>> MOD_PACKET = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("mod_packet_type"));
} }

View File

@@ -1,7 +1,10 @@
package net.momirealms.craftengine.core.world; package net.momirealms.craftengine.core.world;
import ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable; 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.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.plugin.CraftEngine;
import net.momirealms.craftengine.core.world.chunk.CEChunk; import net.momirealms.craftengine.core.world.chunk.CEChunk;
import net.momirealms.craftengine.core.world.chunk.storage.StorageAdaptor; 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 org.jetbrains.annotations.Nullable;
import java.io.IOException; import java.io.IOException;
import java.util.Collection; import java.util.*;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
public abstract class CEWorld { public abstract class CEWorld {
@@ -20,6 +22,9 @@ public abstract class CEWorld {
protected final WorldDataStorage worldDataStorage; protected final WorldDataStorage worldDataStorage;
protected final WorldHeight worldHeightAccessor; protected final WorldHeight worldHeightAccessor;
protected final Set<SectionPos> updatedSectionSet = ConcurrentHashMap.newKeySet(128); protected final Set<SectionPos> updatedSectionSet = ConcurrentHashMap.newKeySet(128);
protected final List<TickingBlockEntity> tickingBlockEntities = new ArrayList<>();
protected final List<TickingBlockEntity> pendingTickingBlockEntities = new ArrayList<>();
protected boolean isTickingBlockEntities = false;
private CEChunk lastChunk; private CEChunk lastChunk;
private long lastChunkPos; private long lastChunkPos;
@@ -40,6 +45,14 @@ public abstract class CEWorld {
this.lastChunkPos = ChunkPos.INVALID_CHUNK_POS; this.lastChunkPos = ChunkPos.INVALID_CHUNK_POS;
} }
public String name() {
return this.world.name();
}
public UUID uuid() {
return this.world.uuid();
}
public void save() { public void save() {
try { try {
for (ConcurrentLong2ReferenceChainedHashTable.TableEntry<CEChunk> entry : this.loadedChunkMap.entrySet()) { for (ConcurrentLong2ReferenceChainedHashTable.TableEntry<CEChunk> entry : this.loadedChunkMap.entrySet()) {
@@ -76,16 +89,6 @@ public abstract class CEWorld {
@Nullable @Nullable
public CEChunk getChunkAtIfLoaded(long chunkPos) { 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) { if (chunkPos == this.lastChunkPos) {
return this.lastChunk; return this.lastChunk;
} }
@@ -98,14 +101,11 @@ public abstract class CEWorld {
} }
@Nullable @Nullable
public CEChunk getChunkAtIfLoadedMainThread(int x, int z) { public CEChunk getChunkAtIfLoaded(int x, int z) {
return getChunkAtIfLoadedMainThread(ChunkPos.asLong(x, z)); return getChunkAtIfLoaded(ChunkPos.asLong(x, z));
}
public WorldHeight worldHeight() {
return worldHeightAccessor;
} }
@Nullable
public ImmutableBlockState getBlockStateAtIfLoaded(int x, int y, int z) { public ImmutableBlockState getBlockStateAtIfLoaded(int x, int y, int z) {
CEChunk chunk = getChunkAtIfLoaded(x >> 4, z >> 4); CEChunk chunk = getChunkAtIfLoaded(x >> 4, z >> 4);
if (chunk == null) { if (chunk == null) {
@@ -114,6 +114,7 @@ public abstract class CEWorld {
return chunk.getBlockState(x, y, z); return chunk.getBlockState(x, y, z);
} }
@Nullable
public ImmutableBlockState getBlockStateAtIfLoaded(BlockPos blockPos) { public ImmutableBlockState getBlockStateAtIfLoaded(BlockPos blockPos) {
CEChunk chunk = getChunkAtIfLoaded(blockPos.x() >> 4, blockPos.z() >> 4); CEChunk chunk = getChunkAtIfLoaded(blockPos.x() >> 4, blockPos.z() >> 4);
if (chunk == null) { if (chunk == null) {
@@ -123,7 +124,7 @@ public abstract class CEWorld {
} }
public boolean setBlockStateAtIfLoaded(BlockPos blockPos, ImmutableBlockState blockState) { public boolean setBlockStateAtIfLoaded(BlockPos blockPos, ImmutableBlockState blockState) {
if (worldHeightAccessor.isOutsideBuildHeight(blockPos)) { if (this.worldHeightAccessor.isOutsideBuildHeight(blockPos)) {
return false; return false;
} }
CEChunk chunk = getChunkAtIfLoaded(blockPos.x() >> 4, blockPos.z() >> 4); CEChunk chunk = getChunkAtIfLoaded(blockPos.x() >> 4, blockPos.z() >> 4);
@@ -134,6 +135,18 @@ public abstract class CEWorld {
return true; 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() { public WorldDataStorage worldDataStorage() {
return worldDataStorage; return worldDataStorage;
} }
@@ -146,5 +159,29 @@ public abstract class CEWorld {
this.updatedSectionSet.addAll(pos); 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<TickingBlockEntity> toRemove = new ReferenceOpenHashSet<>();
for (TickingBlockEntity blockEntity : this.tickingBlockEntities) {
if (!blockEntity.isValid()) {
blockEntity.tick();
} else {
toRemove.add(blockEntity);
}
}
this.tickingBlockEntities.removeAll(toRemove);
this.isTickingBlockEntities = false;
}
} }

View File

@@ -65,10 +65,23 @@ public class ChunkPos {
return longKey; return longKey;
} }
public ChunkPos[] adjacentChunkPos() {
return adjacentChunkPos(this);
}
public static long asLong(int chunkX, int chunkZ) { public static long asLong(int chunkX, int chunkZ) {
return (long) chunkX & 4294967295L | ((long) chunkZ & 4294967295L) << 32; 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 @Override
public final boolean equals(Object o) { public final boolean equals(Object o) {
if (o == null) return false; if (o == null) return false;

View File

@@ -13,4 +13,8 @@ public class SectionPos extends Vec3i {
public static SectionPos of(BlockPos pos) { public static SectionPos of(BlockPos pos) {
return new SectionPos(pos.x() >> 4, pos.y() >> 4, pos.z() >> 4); return new SectionPos(pos.x() >> 4, pos.y() >> 4, pos.z() >> 4);
} }
public static int sectionRelative(int rel) {
return rel & 15;
}
} }

View File

@@ -1,13 +1,18 @@
package net.momirealms.craftengine.core.world.chunk; package net.momirealms.craftengine.core.world.chunk;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import net.momirealms.craftengine.core.block.BlockEntityState;
import net.momirealms.craftengine.core.block.EmptyBlock; import net.momirealms.craftengine.core.block.EmptyBlock;
import net.momirealms.craftengine.core.block.ImmutableBlockState; 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.*;
import net.momirealms.craftengine.core.world.chunk.serialization.DefaultBlockEntitySerializer;
import net.momirealms.sparrow.nbt.ListTag;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.List;
import java.util.Map; import java.util.Map;
public class CEChunk { public class CEChunk {
@@ -16,7 +21,7 @@ public class CEChunk {
private final ChunkPos chunkPos; private final ChunkPos chunkPos;
private final CESection[] sections; private final CESection[] sections;
private final WorldHeight worldHeightAccessor; private final WorldHeight worldHeightAccessor;
private final Map<Integer, BlockEntityState> blockEntities; private final Map<BlockPos, BlockEntity> blockEntities;
private boolean dirty; private boolean dirty;
public CEChunk(CEWorld world, ChunkPos chunkPos) { public CEChunk(CEWorld world, ChunkPos chunkPos) {
@@ -24,14 +29,14 @@ public class CEChunk {
this.chunkPos = chunkPos; this.chunkPos = chunkPos;
this.worldHeightAccessor = world.worldHeight(); this.worldHeightAccessor = world.worldHeight();
this.sections = new CESection[this.worldHeightAccessor.getSectionsCount()]; this.sections = new CESection[this.worldHeightAccessor.getSectionsCount()];
this.blockEntities = new Int2ObjectOpenHashMap<>(16, 0.5f); this.blockEntities = new Object2ObjectOpenHashMap<>(16, 0.5f);
this.fillEmptySection(); this.fillEmptySection();
} }
public CEChunk(CEWorld world, ChunkPos chunkPos, CESection[] sections, Map<Integer, BlockEntityState> blockEntities) { public CEChunk(CEWorld world, ChunkPos chunkPos, CESection[] sections, ListTag blockEntitiesTag) {
this.world = world; this.world = world;
this.chunkPos = chunkPos; this.chunkPos = chunkPos;
this.blockEntities = blockEntities; this.blockEntities = new Object2ObjectOpenHashMap<>(Math.max(blockEntitiesTag.size(), 16), 0.5f);
this.worldHeightAccessor = world.worldHeight(); this.worldHeightAccessor = world.worldHeight();
int sectionCount = this.worldHeightAccessor.getSectionsCount(); int sectionCount = this.worldHeightAccessor.getSectionsCount();
this.sections = new CESection[sectionCount]; this.sections = new CESection[sectionCount];
@@ -44,10 +49,48 @@ public class CEChunk {
} }
} }
this.fillEmptySection(); this.fillEmptySection();
List<BlockEntity> blockEntities = DefaultBlockEntitySerializer.deserialize(this, blockEntitiesTag);
for (BlockEntity blockEntity : blockEntities) {
this.setBlockEntity(blockEntity);
}
} }
public Map<Integer, BlockEntityState> blockEntities() { public void addBlockEntity(BlockEntity blockEntity) {
return this.blockEntities; 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<BlockPos, BlockEntity> blockEntities() {
return Collections.unmodifiableMap(this.blockEntities);
} }
public boolean dirty() { public boolean dirty() {
@@ -93,17 +136,17 @@ public class CEChunk {
} }
} }
@Nullable @NotNull
public ImmutableBlockState getBlockState(BlockPos pos) { public ImmutableBlockState getBlockState(BlockPos pos) {
return getBlockState(pos.x(), pos.y(), pos.z()); return getBlockState(pos.x(), pos.y(), pos.z());
} }
@Nullable @NotNull
public ImmutableBlockState getBlockState(int x, int y, int z) { public ImmutableBlockState getBlockState(int x, int y, int z) {
int index = sectionIndex(SectionPos.blockToSectionCoord(y)); int index = sectionIndex(SectionPos.blockToSectionCoord(y));
CESection section = this.sections[index]; CESection section = this.sections[index];
if (section == null) { if (section == null) {
return null; return EmptyBlock.STATE;
} }
return section.getBlockState((y & 15) << 8 | (z & 15) << 4 | x & 15); return section.getBlockState((y & 15) << 8 | (z & 15) << 4 | x & 15);
} }

View File

@@ -0,0 +1,7 @@
package net.momirealms.craftengine.core.world.chunk;
public class ChunkStatus {
public ChunkStatus() {
}
}

View File

@@ -1,48 +1,33 @@
package net.momirealms.craftengine.core.world.chunk.serialization; package net.momirealms.craftengine.core.world.chunk.serialization;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.BlockEntityState; 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.CompoundTag;
import net.momirealms.sparrow.nbt.ListTag; import net.momirealms.sparrow.nbt.ListTag;
import org.jetbrains.annotations.ApiStatus;
import java.util.ArrayList;
import java.util.List;
import java.util.Map; import java.util.Map;
public final class DefaultBlockEntitySerializer { public final class DefaultBlockEntitySerializer {
@ApiStatus.Experimental public static ListTag serialize(Map<BlockPos, BlockEntity> tiles) {
public static ListTag serialize(Map<Integer, BlockEntityState> tiles) {
ListTag result = new ListTag(); ListTag result = new ListTag();
Map<CompoundTag, int[]> nbtToPosMap = new Object2ObjectOpenHashMap<>(Math.max(tiles.size(), 10), 0.75f); for (Map.Entry<BlockPos, BlockEntity> entry : tiles.entrySet()) {
for (Map.Entry<Integer, BlockEntityState> entry : tiles.entrySet()) { result.add(entry.getValue().saveAsTag());
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<CompoundTag, int[]> entry : nbtToPosMap.entrySet()) {
CompoundTag blockEntityTag = new CompoundTag();
blockEntityTag.put("data", entry.getKey());
blockEntityTag.putIntArray("pos", entry.getValue());
result.add(blockEntityTag);
} }
return result; return result;
} }
@ApiStatus.Experimental public static List<BlockEntity> deserialize(CEChunk chunk, ListTag tag) {
public static Map<Integer, BlockEntityState> deserialize(ListTag tag) { List<BlockEntity> blockEntities = new ArrayList<>(tag.size());
Map<Integer, BlockEntityState> result = new Object2ObjectOpenHashMap<>(Math.max(tag.size(), 16), 0.5f);
for (int i = 0; i < tag.size(); i++) { for (int i = 0; i < tag.size(); i++) {
CompoundTag blockEntityTag = tag.getCompound(i); CompoundTag data = tag.getCompound(i);
CompoundTag data = blockEntityTag.getCompound("data"); BlockPos pos = BlockEntity.readPosAndVerify(data, chunk.chunkPos());
int[] pos = blockEntityTag.getIntArray("pos"); ImmutableBlockState blockState = chunk.getBlockState(pos);
for (int j = 0; j < pos.length; j++) {
result.put(j, new BlockEntityState(data));
} }
} return blockEntities;
return result;
} }
} }

View File

@@ -47,6 +47,6 @@ public final class DefaultChunkSerializer {
} }
} }
ListTag blockEntities = Optional.ofNullable(chunkNbt.getList("block_entities")).orElse(new ListTag()); 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);
} }
} }