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:
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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指令
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package net.momirealms.craftengine.core.block.entity;
|
||||
|
||||
public class BlockEntityTypes {
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package net.momirealms.craftengine.core.world.chunk;
|
||||
|
||||
public class ChunkStatus {
|
||||
|
||||
public ChunkStatus() {
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user