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;
}
if (!context.canPlace()) {
return InteractionResult.FAIL;
return InteractionResult.PASS;
}
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.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<CommandSende
Player player = context.sender();
BukkitServerPlayer serverPlayer = plugin().adapt(player);
Item<?> item = serverPlayer.getItemInHand(InteractionHand.MAIN_HAND);
if (item == null) {
if (ItemUtils.isEmpty(item)) {
handleFeedback(context, MessageConstants.COMMAND_SEARCH_RECIPE_NO_ITEM);
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.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<CommandSender
Player player = context.sender();
BukkitServerPlayer serverPlayer = plugin().adapt(player);
Item<?> item = serverPlayer.getItemInHand(InteractionHand.MAIN_HAND);
if (item.isEmpty()) {
if (ItemUtils.isEmpty(item)) {
handleFeedback(context, MessageConstants.COMMAND_SEARCH_USAGE_NO_ITEM);
return;
}

View File

@@ -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<CommandSender> {
@@ -20,22 +15,9 @@ public class TestCommand extends BukkitCommandFeature<CommandSender> {
@Override
public Command.Builder<? extends CommandSender> assembleCommand(org.incendo.cloud.CommandManager<CommandSender> manager, Command.Builder<CommandSender> 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指令
});
}

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

View File

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

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

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

View File

@@ -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<ChunkPos> trackedChunks = Collections.synchronizedSet(new HashSet<>());
// relighted chunks
private final Set<ChunkPos> relightedChunks = Collections.synchronizedSet(new HashSet<>());
private ConcurrentLong2ReferenceChainedHashTable<ChunkStatus> trackedChunks;
// entity view
private final Map<Integer, EntityPacketHandler> entityTypeView = new ConcurrentHashMap<>();
private Map<Integer, EntityPacketHandler> 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

View File

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

View File

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

View File

@@ -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<ImmutableBlockState> getPossibleStates(CompoundTag nbt);

View File

@@ -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<? extends BlockEntity> blockEntityType;
ImmutableBlockState(
Holder<CustomBlock> owner,
@@ -48,6 +51,14 @@ public final class ImmutableBlockState extends BlockStateHolder {
this.settings = settings;
}
public BlockEntityType<? extends BlockEntity> blockEntityType() {
return blockEntityType;
}
public void setBlockEntityType(BlockEntityType<? extends BlockEntity> 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;
}

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

View File

@@ -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<Boolean> condition;
@@ -26,7 +27,11 @@ public enum Debugger {
public void warn(Supplier<String> 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());
}
}
}
}

View File

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

View File

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

View File

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

View File

@@ -12,10 +12,11 @@ import java.util.Objects;
public class ConstantBoundRegistry<T> extends AbstractMappedRegistry<T> {
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) {
super(key);
public ConstantBoundRegistry(ResourceKey<? extends Registry<T>> key, int expectedSize) {
super(key, expectedSize);
this.byValue = new IdentityHashMap<>(expectedSize);
}
@Override

View File

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

View File

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

View File

@@ -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<Integer, BlockEntityState> blockEntities;
private final Map<BlockPos, BlockEntity> 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<Integer, BlockEntityState> 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<BlockEntity> blockEntities = DefaultBlockEntitySerializer.deserialize(this, blockEntitiesTag);
for (BlockEntity blockEntity : blockEntities) {
this.setBlockEntity(blockEntity);
}
}
public Map<Integer, BlockEntityState> 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<BlockPos, BlockEntity> 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);
}

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;
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<Integer, BlockEntityState> tiles) {
public static ListTag serialize(Map<BlockPos, BlockEntity> tiles) {
ListTag result = new ListTag();
Map<CompoundTag, int[]> nbtToPosMap = new Object2ObjectOpenHashMap<>(Math.max(tiles.size(), 10), 0.75f);
for (Map.Entry<Integer, BlockEntityState> 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<CompoundTag, int[]> entry : nbtToPosMap.entrySet()) {
CompoundTag blockEntityTag = new CompoundTag();
blockEntityTag.put("data", entry.getKey());
blockEntityTag.putIntArray("pos", entry.getValue());
result.add(blockEntityTag);
for (Map.Entry<BlockPos, BlockEntity> entry : tiles.entrySet()) {
result.add(entry.getValue().saveAsTag());
}
return result;
}
@ApiStatus.Experimental
public static Map<Integer, BlockEntityState> deserialize(ListTag tag) {
Map<Integer, BlockEntityState> result = new Object2ObjectOpenHashMap<>(Math.max(tag.size(), 16), 0.5f);
public static List<BlockEntity> deserialize(CEChunk chunk, ListTag tag) {
List<BlockEntity> 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;
}
}

View File

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