9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-26 18:39:20 +00:00

Merge pull request #448 from Xiao-MoMi/dev

部分修复
This commit is contained in:
XiaoMoMi
2025-11-09 15:36:22 +08:00
committed by GitHub
92 changed files with 1210 additions and 347 deletions

View File

@@ -37,8 +37,9 @@ dependencies {
compileOnly("com.infernalsuite.asp:api:4.0.0-SNAPSHOT")
// ModelEngine
compileOnly("com.ticxo.modelengine:ModelEngine:R4.0.8")
// BetterModels
compileOnly("io.github.toxicity188:BetterModel:1.7.0")
// BetterModel
compileOnly("io.github.toxicity188:bettermodel:1.14.0")
compileOnly("com.mojang:authlib:${rootProject.properties["authlib_version"]}")
// MMOItems
compileOnly("net.Indyuce:MMOItems-API:6.10-SNAPSHOT")
compileOnly("io.lumine:MythicLib-dist:1.6.2-SNAPSHOT")

View File

@@ -26,6 +26,14 @@ public class LegacySlimeWorldDataStorage implements WorldDataStorage {
return slimeWorld.get();
}
@Override
public CEChunk readNewChunkAt(CEWorld world, ChunkPos pos) {
SlimeChunk slimeChunk = getWorld().getChunk(pos.x, pos.z);
if (slimeChunk == null) return new CEChunk(world, pos);
slimeChunk.getExtraData().getValue().remove("craftengine");
return new CEChunk(world, pos);
}
@Override
public @NotNull CEChunk readChunkAt(@NotNull CEWorld world, @NotNull ChunkPos pos) {
SlimeChunk slimeChunk = getWorld().getChunk(pos.x, pos.z);
@@ -57,6 +65,13 @@ public class LegacySlimeWorldDataStorage implements WorldDataStorage {
}
}
@Override
public void clearChunkAt(@NotNull ChunkPos pos) {
SlimeChunk slimeChunk = getWorld().getChunk(pos.x, pos.z);
if (slimeChunk == null) return;
slimeChunk.getExtraData().getValue().remove("craftengine");
}
@Override
public void flush() {
}

View File

@@ -3,6 +3,7 @@ package net.momirealms.craftengine.bukkit.compatibility.model.bettermodel;
import kr.toxicity.model.api.BetterModel;
import kr.toxicity.model.api.data.renderer.ModelRenderer;
import kr.toxicity.model.api.tracker.DummyTracker;
import kr.toxicity.model.api.tracker.TrackerModifier;
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElement;
import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.world.BlockPos;
@@ -23,11 +24,13 @@ public class BetterModelBlockEntityElement implements BlockEntityElement {
}
private DummyTracker createDummyTracker() {
ModelRenderer modelRenderer = BetterModel.plugin().modelManager().renderer(this.config.model());
ModelRenderer modelRenderer = BetterModel.plugin().modelManager().model(this.config.model());
if (modelRenderer == null) {
return null;
} else {
return modelRenderer.create(this.location);
return modelRenderer.create(this.location, TrackerModifier.builder()
.sightTrace(this.config.sightTrace())
.build());
}
}

View File

@@ -15,28 +15,38 @@ public class BetterModelBlockEntityElementConfig implements BlockEntityElementCo
private final float yaw;
private final float pitch;
private final String model;
private final boolean sightTrace;
public BetterModelBlockEntityElementConfig(String model, Vector3f position, float yaw, float pitch) {
public BetterModelBlockEntityElementConfig(String model,
Vector3f position,
float yaw,
float pitch,
boolean sightTrace) {
this.pitch = pitch;
this.position = position;
this.yaw = yaw;
this.model = model;
this.sightTrace = sightTrace;
}
public String model() {
return model;
return this.model;
}
public float pitch() {
return pitch;
return this.pitch;
}
public Vector3f position() {
return position;
return this.position;
}
public float yaw() {
return yaw;
return this.yaw;
}
public boolean sightTrace() {
return this.sightTrace;
}
@Override
@@ -44,6 +54,11 @@ public class BetterModelBlockEntityElementConfig implements BlockEntityElementCo
return new BetterModelBlockEntityElement(world, pos, this);
}
@Override
public Class<BetterModelBlockEntityElement> elementClass() {
return BetterModelBlockEntityElement.class;
}
public static class Factory implements BlockEntityElementConfigFactory {
@SuppressWarnings("unchecked")
@@ -54,7 +69,8 @@ public class BetterModelBlockEntityElementConfig implements BlockEntityElementCo
model,
ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0.5f), "position"),
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("yaw", 0f), "yaw"),
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("pitch", 0f), "pitch")
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("pitch", 0f), "pitch"),
ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("sight-trace", true), "sight-trace")
);
}
}

View File

@@ -9,7 +9,7 @@ import org.bukkit.entity.Entity;
public class BetterModelUtils {
public static void bindModel(Entity base, String id) {
ModelRenderer renderer = BetterModel.plugin().modelManager().renderer(id);
ModelRenderer renderer = BetterModel.plugin().modelManager().model(id);
if (renderer == null) {
throw new NullPointerException("Could not find BetterModel blueprint " + id);
}

View File

@@ -44,6 +44,11 @@ public class ModelEngineBlockEntityElementConfig implements BlockEntityElementCo
return new ModelEngineBlockEntityElement(world, pos, this);
}
@Override
public Class<ModelEngineBlockEntityElement> elementClass() {
return ModelEngineBlockEntityElement.class;
}
public static class Factory implements BlockEntityElementConfigFactory {
@SuppressWarnings("unchecked")

View File

@@ -27,6 +27,14 @@ public class SlimeWorldDataStorage implements WorldDataStorage {
return slimeWorld.get();
}
@Override
public CEChunk readNewChunkAt(CEWorld world, ChunkPos pos) {
SlimeChunk slimeChunk = getWorld().getChunk(pos.x, pos.z);
if (slimeChunk == null) return new CEChunk(world, pos);
slimeChunk.getExtraData().remove("craftengine");
return new CEChunk(world, pos);
}
@Override
public @NotNull CEChunk readChunkAt(@NotNull CEWorld world, @NotNull ChunkPos pos) {
SlimeChunk slimeChunk = getWorld().getChunk(pos.x, pos.z);
@@ -42,7 +50,7 @@ public class SlimeWorldDataStorage implements WorldDataStorage {
}
}
@SuppressWarnings("unchecked")
@SuppressWarnings({"unchecked", "rawtypes"})
@Override
public void writeChunkAt(@NotNull ChunkPos pos, @NotNull CEChunk chunk) {
SlimeChunk slimeChunk = getWorld().getChunk(pos.x, pos.z);
@@ -53,8 +61,7 @@ public class SlimeWorldDataStorage implements WorldDataStorage {
} else {
try {
Object tag = adaptor.bytesToByteArrayTag(NBT.toBytes(nbt));
Map<String, ?> data1 = slimeChunk.getExtraData();
Map<String, Object> data2 = (Map<String, Object>) data1;
Map<String, Object> data2 = (Map) slimeChunk.getExtraData();
data2.put("craftengine", tag);
} catch (Exception e) {
throw new RuntimeException("Failed to write chunk tag to slime world. " + pos, e);
@@ -62,6 +69,13 @@ public class SlimeWorldDataStorage implements WorldDataStorage {
}
}
@Override
public void clearChunkAt(@NotNull ChunkPos pos) {
SlimeChunk slimeChunk = getWorld().getChunk(pos.x, pos.z);
if (slimeChunk == null) return;
slimeChunk.getExtraData().remove("craftengine");
}
@Override
public void flush() {
}

View File

@@ -59,7 +59,7 @@ public final class BukkitBlockManager extends AbstractBlockManager {
private Map<Integer, List<String>> clientBoundTags = Map.of();
private Map<Integer, List<String>> previousClientBoundTags = Map.of();
// 缓存的原版方块tag包
private Object cachedUpdateTagsPacket;
private List<TagUtils.TagEntry> cachedUpdateTags = List.of();
// 被移除声音的原版方块
private Set<Object> missingPlaceSounds = Set.of();
private Set<Object> missingBreakSounds = Set.of();
@@ -150,23 +150,14 @@ public final class BukkitBlockManager extends AbstractBlockManager {
}
@Override
protected void resendTags() {
protected void updateTags() {
// if there's no change
if (this.clientBoundTags.equals(this.previousClientBoundTags)) return;
List<TagUtils.TagEntry> list = new ArrayList<>();
for (Map.Entry<Integer, List<String>> entry : this.clientBoundTags.entrySet()) {
list.add(new TagUtils.TagEntry(entry.getKey(), entry.getValue()));
}
Object packet = TagUtils.createUpdateTagsPacket(Map.of(MRegistries.BLOCK, list));
for (BukkitServerPlayer player : this.plugin.networkManager().onlineUsers()) {
player.sendPacket(packet, false);
}
// 如果空,那么新来的玩家就没必要收到更新包了
if (list.isEmpty()) {
this.cachedUpdateTagsPacket = null;
} else {
this.cachedUpdateTagsPacket = packet;
}
this.cachedUpdateTags = list;
}
@Nullable
@@ -299,6 +290,10 @@ public final class BukkitBlockManager extends AbstractBlockManager {
this.burnOdds.put(nmsBlock, settings.fireSpreadChance());
this.burnableBlocks.add(nmsBlock);
}
Key vanillaBlockId = state.vanillaBlockState().ownerId();
BlockGenerator.field$CraftEngineBlock$isNoteBlock().set(nmsBlock, vanillaBlockId.equals(BlockKeys.NOTE_BLOCK));
BlockGenerator.field$CraftEngineBlock$isTripwire().set(nmsBlock, vanillaBlockId.equals(BlockKeys.TRIPWIRE));
} catch (ReflectiveOperationException e) {
this.plugin.logger().warn("Failed to apply platform block settings for block state " + state, e);
}
@@ -364,8 +359,8 @@ public final class BukkitBlockManager extends AbstractBlockManager {
}
}
public Object cachedUpdateTagsPacket() {
return this.cachedUpdateTagsPacket;
public List<TagUtils.TagEntry> cachedUpdateTags() {
return this.cachedUpdateTags;
}
public VisualBlockStatePacket cachedVisualBlockStatePacket() {

View File

@@ -1,6 +1,5 @@
package net.momirealms.craftengine.bukkit.block.entity;
import net.momirealms.craftengine.bukkit.block.behavior.SeatBlockBehavior;
import net.momirealms.craftengine.bukkit.entity.seat.BukkitSeat;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.entity.BlockEntity;
@@ -14,8 +13,6 @@ import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.WorldPosition;
import net.momirealms.sparrow.nbt.CompoundTag;
import java.util.Optional;
public class SeatBlockEntity extends BlockEntity implements SeatOwner {
private final Seat<SeatBlockEntity>[] seats;
@@ -41,25 +38,20 @@ public class SeatBlockEntity extends BlockEntity implements SeatOwner {
}
public boolean spawnSeat(Player player) {
Optional<SeatBlockBehavior> seatBehavior = super.blockState.behavior().getAs(SeatBlockBehavior.class);
if (seatBehavior.isEmpty()) {
return false;
}
float yRot = 0;
Property<HorizontalDirection> directionProperty = seatBehavior.get().directionProperty();
if (directionProperty != null) {
HorizontalDirection direction = super.blockState.get(directionProperty);
if (direction == HorizontalDirection.NORTH) {
yRot = 180;
} else if (direction == HorizontalDirection.EAST) {
yRot = -90;
} else if (direction == HorizontalDirection.WEST) {
yRot = 90;
}
Property<?> facing = super.blockState.owner().value().getProperty("facing");
int yRot = 0;
if (facing != null && facing.valueClass() == HorizontalDirection.class) {
HorizontalDirection direction = (HorizontalDirection) super.blockState.get(facing);
yRot = switch (direction) {
case NORTH -> 0;
case SOUTH -> 180;
case WEST -> 270;
case EAST -> 90;
};
}
for (Seat<SeatBlockEntity> seat : this.seats) {
if (!seat.isOccupied()) {
if (seat.spawnSeat(player, new WorldPosition(super.world.world(), super.pos.x() + 0.5, super.pos.y(), super.pos.z() + 0.5, 0, 180 - yRot))) {
if (seat.spawnSeat(player, new WorldPosition(super.world.world(), super.pos.x() + 0.5, super.pos.y(), super.pos.z() + 0.5, 0, yRot))) {
return true;
}
}

View File

@@ -7,6 +7,7 @@ public class BukkitBlockEntityElementConfigs extends BlockEntityElementConfigs {
static {
register(ITEM_DISPLAY, ItemDisplayBlockEntityElementConfig.FACTORY);
register(TEXT_DISPLAY, TextDisplayBlockEntityElementConfig.FACTORY);
register(ITEM, ItemBlockEntityElementConfig.FACTORY);
}
private BukkitBlockEntityElementConfigs() {}

View File

@@ -0,0 +1,65 @@
package net.momirealms.craftengine.bukkit.block.entity.renderer.element;
import it.unimi.dsi.fastutil.ints.IntList;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes;
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElement;
import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.world.BlockPos;
import org.joml.Vector3f;
import java.util.List;
import java.util.UUID;
public class ItemBlockEntityElement implements BlockEntityElement {
public final ItemBlockEntityElementConfig config;
public final Object cachedSpawnPacket1;
public final Object cachedSpawnPacket2;
public final Object cachedRidePacket;
public final Object cachedDespawnPacket;
public final Object cachedUpdatePosPacket;
public final int entityId1;
public final int entityId2;
public ItemBlockEntityElement(ItemBlockEntityElementConfig config, BlockPos pos) {
this(config, pos, CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(), CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(), false);
}
public ItemBlockEntityElement(ItemBlockEntityElementConfig config, BlockPos pos, int entityId1, int entityId2, boolean posChanged) {
this.config = config;
Vector3f position = config.position();
this.cachedSpawnPacket1 = FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
entityId1, UUID.randomUUID(), pos.x() + position.x, pos.y() + position.y, pos.z() + position.z,
0, 0, MEntityTypes.ITEM_DISPLAY, 0, CoreReflections.instance$Vec3$Zero, 0
);
this.cachedSpawnPacket2 = FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
entityId2, UUID.randomUUID(), pos.x() + position.x, pos.y() + position.y, pos.z() + position.z,
0, 0, MEntityTypes.ITEM, 0, CoreReflections.instance$Vec3$Zero, 0
);
this.cachedRidePacket = FastNMS.INSTANCE.constructor$ClientboundSetPassengersPacket(entityId1, entityId2);
this.cachedDespawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(IntList.of(entityId1, entityId2));
this.entityId1 = entityId1;
this.entityId2 = entityId2;
this.cachedUpdatePosPacket = posChanged ? FastNMS.INSTANCE.constructor$ClientboundEntityPositionSyncPacket(this.entityId1, pos.x() + position.x, pos.y() + position.y, pos.z() + position.z, 0, 0, false) : null;
}
@Override
public void hide(Player player) {
player.sendPacket(this.cachedDespawnPacket, false);
}
@Override
public void show(Player player) {
player.sendPackets(List.of(this.cachedSpawnPacket1, this.cachedSpawnPacket2, this.cachedRidePacket, FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId2, this.config.metadataValues(player))), false);
}
@Override
public void transform(Player player) {
if (this.cachedUpdatePosPacket != null) {
player.sendPackets(List.of(this.cachedUpdatePosPacket, FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId2, this.config.metadataValues(player))), false);
} else {
player.sendPacket(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId2, this.config.metadataValues(player)), false);
}
}
}

View File

@@ -0,0 +1,77 @@
package net.momirealms.craftengine.bukkit.block.entity.renderer.element;
import net.momirealms.craftengine.bukkit.entity.data.ItemEntityData;
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElement;
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfig;
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfigFactory;
import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.World;
import org.joml.Vector3f;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
public class ItemBlockEntityElementConfig implements BlockEntityElementConfig<ItemBlockEntityElement> {
public static final Factory FACTORY = new Factory();
private final Function<Player, List<Object>> lazyMetadataPacket;
private final Function<Player, Item<?>> item;
private final Vector3f position;
public ItemBlockEntityElementConfig(Function<Player, Item<?>> item, Vector3f position) {
this.item = item;
this.position = position;
this.lazyMetadataPacket = player -> {
List<Object> dataValues = new ArrayList<>();
ItemEntityData.Item.addEntityDataIfNotDefaultValue(item.apply(player).getLiteralObject(), dataValues);
ItemEntityData.NoGravity.addEntityDataIfNotDefaultValue(true, dataValues);
return dataValues;
};
}
@Override
public ItemBlockEntityElement create(World world, BlockPos pos) {
return new ItemBlockEntityElement(this, pos);
}
@Override
public ItemBlockEntityElement create(World world, BlockPos pos, ItemBlockEntityElement previous) {
return new ItemBlockEntityElement(this, pos, previous.entityId1, previous.entityId2, !previous.config.position.equals(this.position));
}
@Override
public Class<ItemBlockEntityElement> elementClass() {
return ItemBlockEntityElement.class;
}
public Vector3f position() {
return position;
}
public Item<?> item(Player player) {
return this.item.apply(player);
}
public List<Object> metadataValues(Player player) {
return this.lazyMetadataPacket.apply(player);
}
public static class Factory implements BlockEntityElementConfigFactory {
@SuppressWarnings("unchecked")
@Override
public <E extends BlockEntityElement> BlockEntityElementConfig<E> create(Map<String, Object> arguments) {
Key itemId = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("item"), "warning.config.block.state.entity_renderer.item_display.missing_item"));
return (BlockEntityElementConfig<E>) new ItemBlockEntityElementConfig(
player -> BukkitItemManager.instance().createWrappedItem(itemId, player),
ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0.5f), "position")
);
}
}
}

View File

@@ -13,13 +13,17 @@ import java.util.List;
import java.util.UUID;
public class ItemDisplayBlockEntityElement implements BlockEntityElement {
private final ItemDisplayBlockEntityElementConfig config;
private final Object cachedSpawnPacket;
private final Object cachedDespawnPacket;
private final int entityId;
public final ItemDisplayBlockEntityElementConfig config;
public final Object cachedSpawnPacket;
public final Object cachedDespawnPacket;
public final Object cachedUpdatePosPacket;
public final int entityId;
public ItemDisplayBlockEntityElement(ItemDisplayBlockEntityElementConfig config, BlockPos pos) {
int entityId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet();
this(config, pos, CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(), false);
}
public ItemDisplayBlockEntityElement(ItemDisplayBlockEntityElementConfig config, BlockPos pos, int entityId, boolean posChanged) {
Vector3f position = config.position();
this.cachedSpawnPacket = FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
entityId, UUID.randomUUID(), pos.x() + position.x, pos.y() + position.y, pos.z() + position.z,
@@ -28,6 +32,7 @@ public class ItemDisplayBlockEntityElement implements BlockEntityElement {
this.config = config;
this.cachedDespawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(IntList.of(entityId));
this.entityId = entityId;
this.cachedUpdatePosPacket = posChanged ? FastNMS.INSTANCE.constructor$ClientboundEntityPositionSyncPacket(this.entityId, pos.x() + position.x, pos.y() + position.y, pos.z() + position.z, config.yRot(), config.xRot(), false) : null;
}
@Override
@@ -37,6 +42,15 @@ public class ItemDisplayBlockEntityElement implements BlockEntityElement {
@Override
public void show(Player player) {
player.sendPackets(List.of(this.cachedSpawnPacket, FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId, this.config.metadataValues(player))), true);
player.sendPackets(List.of(this.cachedSpawnPacket, FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId, this.config.metadataValues(player))), false);
}
@Override
public void transform(Player player) {
if (this.cachedUpdatePosPacket != null) {
player.sendPackets(List.of(this.cachedUpdatePosPacket, FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId, this.config.metadataValues(player))), false);
} else {
player.sendPacket(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId, this.config.metadataValues(player)), false);
}
}
}

View File

@@ -78,6 +78,20 @@ public class ItemDisplayBlockEntityElementConfig implements BlockEntityElementCo
return new ItemDisplayBlockEntityElement(this, pos);
}
@Override
public ItemDisplayBlockEntityElement create(World world, BlockPos pos, ItemDisplayBlockEntityElement previous) {
Quaternionf previousRotation = previous.config.rotation;
if (previousRotation.x != 0 || previousRotation.y != 0 || previousRotation.z != 0 || previousRotation.w != 1) {
return null;
}
return new ItemDisplayBlockEntityElement(this, pos, previous.entityId, previous.config.yRot != this.yRot || previous.config.xRot != this.xRot || !previous.config.position.equals(this.position));
}
@Override
public Class<ItemDisplayBlockEntityElement> elementClass() {
return ItemDisplayBlockEntityElement.class;
}
public Item<?> item(Player player) {
return this.item.apply(player);
}

View File

@@ -13,13 +13,17 @@ import java.util.List;
import java.util.UUID;
public class TextDisplayBlockEntityElement implements BlockEntityElement {
private final TextDisplayBlockEntityElementConfig config;
private final Object cachedSpawnPacket;
private final Object cachedDespawnPacket;
private final int entityId;
public final TextDisplayBlockEntityElementConfig config;
public final Object cachedSpawnPacket;
public final Object cachedDespawnPacket;
public final Object cachedUpdatePosPacket;
public final int entityId;
public TextDisplayBlockEntityElement(TextDisplayBlockEntityElementConfig config, BlockPos pos) {
int entityId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet();
this(config, pos, CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(), false);
}
public TextDisplayBlockEntityElement(TextDisplayBlockEntityElementConfig config, BlockPos pos, int entityId, boolean posChanged) {
Vector3f position = config.position();
this.cachedSpawnPacket = FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
entityId, UUID.randomUUID(), pos.x() + position.x, pos.y() + position.y, pos.z() + position.z,
@@ -28,6 +32,7 @@ public class TextDisplayBlockEntityElement implements BlockEntityElement {
this.config = config;
this.cachedDespawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(IntList.of(entityId));
this.entityId = entityId;
this.cachedUpdatePosPacket = posChanged ? FastNMS.INSTANCE.constructor$ClientboundEntityPositionSyncPacket(this.entityId, pos.x() + position.x, pos.y() + position.y, pos.z() + position.z, config.yRot(), config.xRot(), false) : null;
}
@Override
@@ -37,6 +42,19 @@ public class TextDisplayBlockEntityElement implements BlockEntityElement {
@Override
public void show(Player player) {
player.sendPackets(List.of(this.cachedSpawnPacket, FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId, this.config.metadataValues(player))), true);
player.sendPackets(List.of(this.cachedSpawnPacket, FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId, this.config.metadataValues(player))), false);
}
@Override
public void transform(Player player) {
if (this.cachedUpdatePosPacket != null) {
player.sendPackets(List.of(this.cachedUpdatePosPacket, FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId, this.config.metadataValues(player))), false);
} else {
player.sendPacket(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId, this.config.metadataValues(player)), false);
}
}
public int entityId() {
return entityId;
}
}

View File

@@ -66,6 +66,20 @@ public class TextDisplayBlockEntityElementConfig implements BlockEntityElementCo
return new TextDisplayBlockEntityElement(this, pos);
}
@Override
public TextDisplayBlockEntityElement create(World world, BlockPos pos, TextDisplayBlockEntityElement previous) {
Quaternionf previousRotation = previous.config.rotation;
if (previousRotation.x != 0 || previousRotation.y != 0 || previousRotation.z != 0 || previousRotation.w != 1) {
return null;
}
return new TextDisplayBlockEntityElement(this, pos, previous.entityId, previous.config.yRot != this.yRot || previous.config.xRot != this.xRot || !previous.config.position.equals(this.position));
}
@Override
public Class<TextDisplayBlockEntityElement> elementClass() {
return TextDisplayBlockEntityElement.class;
}
public Component text(Player player) {
return AdventureHelper.miniMessage().deserialize(this.text, PlayerOptionalContext.of(player).tagResolvers());
}

View File

@@ -0,0 +1,11 @@
package net.momirealms.craftengine.bukkit.entity.data;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
public class ItemEntityData<T> extends BaseEntityData<T> {
public static final ItemEntityData<Object> Item = new ItemEntityData<>(ItemEntityData.class, EntityDataValue.Serializers$ITEM_STACK, CoreReflections.instance$ItemStack$EMPTY);
public ItemEntityData(Class<?> clazz, Object serializer, T defaultValue) {
super(clazz, serializer, defaultValue);
}
}

View File

@@ -28,7 +28,7 @@ import org.jetbrains.annotations.NotNull;
import java.io.IOException;
public class BukkitSeatManager implements SeatManager {
public class BukkitSeatManager implements SeatManager, Listener {
private static BukkitSeatManager instance;
public static final NamespacedKey SEAT_KEY = KeyUtils.toNamespacedKey(SeatManager.SEAT_KEY);
public static final NamespacedKey SEAT_EXTRA_DATA_KEY = KeyUtils.toNamespacedKey(SeatManager.SEAT_EXTRA_DATA_KEY);
@@ -113,9 +113,9 @@ public class BukkitSeatManager implements SeatManager {
if (!isSeat) return;
Location location = seat.getLocation();
if (seat instanceof ArmorStand) {
location.add(0, 0.9875,0);
location.add(0, 0.3875,0);
} else {
location.add(0,0.25,0);
location.add(0,-0.35,0);
}
seat.remove();
EntityUtils.safeDismount(player, location);

View File

@@ -501,10 +501,17 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory<ComponentItemW
if (trim == null) {
item.resetComponent(DataComponentTypes.TRIM);
} else {
item.setJavaComponent(DataComponentTypes.TRIM, Map.of(
"pattern", trim.pattern().asString(),
"material", trim.material().asString()
));
try {
item.setJavaComponent(DataComponentTypes.TRIM, Map.of(
"pattern", trim.pattern().asString(),
"material", trim.material().asString()
));
} catch (Exception e) {
// 预防未启用基于纹饰盔甲时,锁链甲可能产生的网络问题(由用户配置决定)
if (!trim.material().equals(Key.of("minecraft", "custom")) && !trim.pattern().equals(Key.of("minecraft", "chainmail"))) {
this.plugin.logger().warn("Failed to apply trim " + trim, e);
}
}
}
}

View File

@@ -201,6 +201,14 @@ public final class BlockGenerator {
field$CraftEngineBlock$isTripwire = clazz$CraftEngineBlock.getField("isClientSideTripwire");
}
public static Field field$CraftEngineBlock$isNoteBlock() {
return field$CraftEngineBlock$isNoteBlock;
}
public static Field field$CraftEngineBlock$isTripwire() {
return field$CraftEngineBlock$isTripwire;
}
public static DelegatingBlock generateBlock(Key blockId) throws Throwable {
ObjectHolder<BlockBehavior> behaviorHolder = new ObjectHolder<>(EmptyBlockBehavior.INSTANCE);
ObjectHolder<BlockShape> shapeHolder = new ObjectHolder<>(STONE_SHAPE);

View File

@@ -19,6 +19,7 @@ import net.momirealms.craftengine.core.block.DelegatingBlockState;
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.block.entity.render.ConstantBlockEntityRenderer;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.config.Config;
import net.momirealms.craftengine.core.util.ReflectionUtils;
@@ -226,7 +227,10 @@ public final class WorldStorageInjector {
// 处理 自定义块到自定义块或原版块到自定义块
CEChunk chunk = holder.ceChunk();
chunk.setDirty(true);
ConstantBlockEntityRenderer previousRenderer = null;
// 如果两个方块没有相同的主人 且 旧方块有方块实体
if (!previousImmutableBlockState.isEmpty()) {
if (previousImmutableBlockState.owner() != newImmutableBlockState.owner() && previousImmutableBlockState.hasBlockEntity()) {
BlockPos pos = new BlockPos(chunk.chunkPos.x * 16 + x, section.sectionY * 16 + y, chunk.chunkPos.z * 16 + z);
@@ -242,9 +246,11 @@ public final class WorldStorageInjector {
}
if (previousImmutableBlockState.hasConstantBlockEntityRenderer()) {
BlockPos pos = new BlockPos(chunk.chunkPos.x * 16 + x, section.sectionY * 16 + y, chunk.chunkPos.z * 16 + z);
chunk.removeConstantBlockEntityRenderer(pos);
// 如果新状态没有entity renderer,那么直接移除,否则暂存
previousRenderer = chunk.removeConstantBlockEntityRenderer(pos, !newImmutableBlockState.hasConstantBlockEntityRenderer());
}
}
if (newImmutableBlockState.hasBlockEntity()) {
BlockPos pos = new BlockPos(chunk.chunkPos.x * 16 + x, section.sectionY * 16 + y, chunk.chunkPos.z * 16 + z);
BlockEntity blockEntity = chunk.getBlockEntity(pos, false);
@@ -264,10 +270,13 @@ public final class WorldStorageInjector {
chunk.createDynamicBlockEntityRenderer(blockEntity);
}
}
// 处理新老entity renderer更替
if (newImmutableBlockState.hasConstantBlockEntityRenderer()) {
BlockPos pos = new BlockPos(chunk.chunkPos.x * 16 + x, section.sectionY * 16 + y, chunk.chunkPos.z * 16 + z);
chunk.addConstantBlockEntityRenderer(pos, newImmutableBlockState);
chunk.addConstantBlockEntityRenderer(pos, newImmutableBlockState, previousRenderer);
}
// 如果新方块的光照属性和客户端认为的不同
if (Config.enableLightSystem()) {
if (previousImmutableBlockState.isEmpty()) {

View File

@@ -241,6 +241,19 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
this.c2sGamePacketListeners[id] = new ByteBufferPacketListenerHolder(name, listener);
}
@Override
public void delayedLoad() {
this.resendTags();
}
@SuppressWarnings("unchecked")
public void resendTags() {
Object packet = TagUtils.createUpdateTagsPacket(Map.of(MRegistries.BLOCK, BukkitBlockManager.instance().cachedUpdateTags()), FastNMS.INSTANCE.method$TagNetworkSerialization$serializeTagsToNetwork());
for (BukkitServerPlayer player : onlineUsers()) {
player.sendPacket(packet, false);
}
}
public void addFakePlayer(Player player) {
FakeBukkitServerPlayer fakePlayer = new FakeBukkitServerPlayer(this.plugin);
fakePlayer.setPlayer(player);
@@ -1857,11 +1870,17 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
public static class UpdateTagsListener implements NMSPacketListener {
@SuppressWarnings("unchecked")
@Override
public void onPacketSend(NetWorkUser user, NMSPacketEvent event, Object packet) {
Object modifiedPacket = BukkitBlockManager.instance().cachedUpdateTagsPacket();
if (packet.equals(modifiedPacket) || modifiedPacket == null) return;
event.replacePacket(modifiedPacket);
List<TagUtils.TagEntry> cachedUpdateTags = BukkitBlockManager.instance().cachedUpdateTags();
if (cachedUpdateTags.isEmpty()) return;
Map<Object, Object> tags = FastNMS.INSTANCE.field$ClientboundUpdateTagsPacket$tags(packet);
// 已经替换过了
if (tags instanceof MarkedHashMap<Object, Object>) return;
// 需要虚假的block
if (tags.get(MRegistries.BLOCK) == null) return;
event.replacePacket(TagUtils.createUpdateTagsPacket(Map.of(MRegistries.BLOCK, cachedUpdateTags), tags));
}
}
@@ -4132,16 +4151,22 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
CompoundTag tag = (CompoundTag) buf.readNbt(named);
// todo 刷怪笼里的物品?
// 展示架
if (VersionHelper.isOrAbove1_21_9() && tag != null && tag.containsKey("Items")) {
// 通用方块实体存储的物品
if (tag != null && tag.containsKey("Items")) {
BukkitItemManager itemManager = BukkitItemManager.instance();
ListTag itemsTag = tag.getList("Items");
List<Pair<Byte, ItemStack>> items = new ArrayList<>();
for (Tag itemTag : itemsTag) {
if (itemTag instanceof CompoundTag itemCompoundTag) {
byte slot = itemCompoundTag.getByte("Slot");
Object nmsStack = CoreReflections.instance$ItemStack$CODEC.parse(MRegistryOps.SPARROW_NBT, itemCompoundTag)
.resultOrPartial((error) -> CraftEngine.instance().logger().severe("Tried to parse invalid item: '" + error + "'")).orElse(null);
Object nmsStack;
if (VersionHelper.isOrAbove1_20_5()) {
nmsStack = CoreReflections.instance$ItemStack$CODEC.parse(MRegistryOps.SPARROW_NBT, itemCompoundTag)
.resultOrPartial((error) -> CraftEngine.instance().logger().severe("Tried to parse invalid item: '" + error + "'")).orElse(null);
} else {
Object nmsTag = MRegistryOps.SPARROW_NBT.convertTo(MRegistryOps.NBT, itemTag);
nmsStack = FastNMS.INSTANCE.method$ItemStack$of(nmsTag);
}
ItemStack bukkitStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(nmsStack);
Optional<ItemStack> optional = itemManager.s2c(bukkitStack, (BukkitServerPlayer) user);
if (optional.isPresent()) {
@@ -4155,8 +4180,14 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
if (changed) {
ListTag newItemsTag = new ListTag();
for (Pair<Byte, ItemStack> pair : items) {
CompoundTag newItemCompoundTag = (CompoundTag) CoreReflections.instance$ItemStack$CODEC.encodeStart(MRegistryOps.SPARROW_NBT, FastNMS.INSTANCE.field$CraftItemStack$handle(pair.right()))
.resultOrPartial((error) -> CraftEngine.instance().logger().severe("Tried to encode invalid item: '" + error + "'")).orElse(null);
CompoundTag newItemCompoundTag;
if (VersionHelper.isOrAbove1_20_5()) {
newItemCompoundTag = (CompoundTag) CoreReflections.instance$ItemStack$CODEC.encodeStart(MRegistryOps.SPARROW_NBT, FastNMS.INSTANCE.field$CraftItemStack$handle(pair.right()))
.resultOrPartial((error) -> CraftEngine.instance().logger().severe("Tried to encode invalid item: '" + error + "'")).orElse(null);
} else {
Object nmsTag = FastNMS.INSTANCE.method$itemStack$save(FastNMS.INSTANCE.field$CraftItemStack$handle(pair.right()), FastNMS.INSTANCE.constructor$CompoundTag());
newItemCompoundTag = (CompoundTag) MRegistryOps.NBT.convertTo(MRegistryOps.SPARROW_NBT, nmsTag);
}
if (newItemCompoundTag != null) {
newItemCompoundTag.putByte("Slot", pair.left());
newItemsTag.add(newItemCompoundTag);

View File

@@ -56,7 +56,7 @@ public record ClientCustomBlockPacket(int vanillaSize, int currentSize) implemen
return;
}
int serverBlockRegistrySize = RegistryUtils.currentBlockRegistrySize();
if (this.currentSize != serverBlockRegistrySize) {
if (this.currentSize < serverBlockRegistrySize) {
user.kick(Component.translatable(
"disconnect.craftengine.current_block_registry_mismatch",
TranslationArgument.numeric(this.currentSize),

View File

@@ -1082,7 +1082,7 @@ public final class CoreReflections {
public static final Class<?> clazz$MobEffect = requireNonNull(
ReflectionUtils.getClazz(
BukkitReflectionUtils.assembleMCClass("world.effect.MobEffectList"), // 这里paper会自动获取到NM.world.effect.MobEffect
BukkitReflectionUtils.assembleMCClass("world.effect.MobEffect") // 如果插件是mojmap就会走这个
BukkitReflectionUtils.assembleMCClass("world.effect.MobEffect") // paper柠檬酸了
)
);
@@ -1125,7 +1125,7 @@ public final class CoreReflections {
public static final Class<?> clazz$Fluid = requireNonNull(
ReflectionUtils.getClazz(
BukkitReflectionUtils.assembleMCClass("world.level.material.FluidType"), // 这里paper会自动获取到NM.world.level.material.Fluid
BukkitReflectionUtils.assembleMCClass("world.level.material.Fluid") // 如果插件是mojmap就会走这个
BukkitReflectionUtils.assembleMCClass("world.level.material.Fluid") // paper柠檬酸了
)
);
@@ -3133,9 +3133,9 @@ public final class CoreReflections {
);
public static final Class<?> clazz$ChunkMap = requireNonNull(
BukkitReflectionUtils.findReobfOrMojmapClass(
"server.level.PlayerChunkMap",
"server.level.ChunkMap"
ReflectionUtils.getClazz(
BukkitReflectionUtils.assembleMCClass("server.level.PlayerChunkMap"), // 这里paper会自动获取到NM.server.level.ChunkMap
BukkitReflectionUtils.assembleMCClass("server.level.ChunkMap") // paper柠檬酸了
)
);
@@ -3238,7 +3238,7 @@ public final class CoreReflections {
public static final Class<?> clazz$ResourceManager = requireNonNull(
ReflectionUtils.getClazz(
BukkitReflectionUtils.assembleMCClass("server.packs.resources.IResourceManager"), // 这里paper会自动获取到NM.server.packs.resources.ResourceManager
BukkitReflectionUtils.assembleMCClass("server.packs.resources.ResourceManager") // 如果插件是mojmap就会走这个
BukkitReflectionUtils.assembleMCClass("server.packs.resources.ResourceManager") // paper柠檬酸了
)
);
@@ -4536,4 +4536,16 @@ public final class CoreReflections {
public static final Method method$DismountHelper$canDismountTo1 = requireNonNull(
ReflectionUtils.getStaticMethod(clazz$DismountHelper, boolean.class, clazz$CollisionGetter, clazz$Vec3, clazz$LivingEntity, clazz$Pose)
);
public static final Class<?> clazz$WorldGenContext = MiscUtils.requireNonNullIf(
ReflectionUtils.getClazz(BukkitReflectionUtils.assembleMCClass("world.level.chunk.status.WorldGenContext")), VersionHelper.isOrAbove1_20_5()
);
public static final Field field$ChunkMap$worldGenContext = MiscUtils.requireNonNullIf(
ReflectionUtils.getDeclaredField(clazz$ChunkMap, clazz$WorldGenContext, 0), VersionHelper.isOrAbove1_20_5()
);
public static final Field field$ChunkMap$chunkGenerator = MiscUtils.requireNonNullIf(
ReflectionUtils.getDeclaredField(clazz$ChunkMap, clazz$ChunkGenerator, 0), !VersionHelper.isOrAbove1_20_5()
);
}

View File

@@ -1720,4 +1720,10 @@ public final class NetworkReflections {
"network.protocol.game.ClientboundBlockEntityDataPacket"
)
);
public static final Field field$ClientboundUpdateTagsPacket$tags = requireNonNull(
ReflectionUtils.getDeclaredField(
clazz$ClientboundUpdateTagsPacket, Map.class, 0
)
);
}

View File

@@ -63,32 +63,34 @@ public final class EntityUtils {
BlockPos pos = new BlockPos(MiscUtils.fastFloor(x), MiscUtils.fastFloor(y), MiscUtils.fastFloor(z));
try {
double floorHeight = (double) CoreReflections.method$BlockGetter$getBlockFloorHeight.invoke(serverLevel, LocationUtils.toBlockPos(pos));
if (pos.y() + floorHeight > y + 0.75) {
if (pos.y() + floorHeight > y + 0.75 || !isBlockFloorValid(floorHeight)) {
floorHeight = (double) CoreReflections.method$BlockGetter$getBlockFloorHeight.invoke(serverLevel, LocationUtils.toBlockPos(pos.below()));
if (pos.y() + floorHeight - 1 < y - 0.75 || !isBlockFloorValid(floorHeight)) {
continue;
}
floorHeight -= 1;
}
Object aabb = CoreReflections.method$LivingEntity$getLocalBoundsForPose.invoke(serverPlayer, pose);
Object vec3 = FastNMS.INSTANCE.constructor$Vec3(x, pos.y() + floorHeight, z);
Object newAABB = FastNMS.INSTANCE.method$AABB$move(aabb, vec3);
boolean canDismount = (boolean) CoreReflections.method$DismountHelper$canDismountTo0.invoke(null, serverLevel, serverPlayer, newAABB);
if (!canDismount) {
continue;
}
if (isBlockFloorValid(floorHeight)) {
Object aabb = CoreReflections.method$LivingEntity$getLocalBoundsForPose.invoke(serverPlayer, pose);
Object vec3 = FastNMS.INSTANCE.constructor$Vec3(x, pos.y() + floorHeight, z);
Object newAABB = FastNMS.INSTANCE.method$AABB$move(aabb, vec3);
boolean canDismount = (boolean) CoreReflections.method$DismountHelper$canDismountTo0.invoke(null, serverLevel, serverPlayer, newAABB);
if (!canDismount) {
continue;
}
if (!FastNMS.INSTANCE.checkEntityCollision(serverLevel, List.of(newAABB))) {
continue;
}
if (VersionHelper.isFolia()) {
player.teleportAsync(new Location(player.getWorld(), x, pos.y() + floorHeight, z, player.getYaw(), player.getPitch()));
} else {
player.teleport(new Location(player.getWorld(), x, pos.y() + floorHeight, z, player.getYaw(), player.getPitch()));
}
if (pose == CoreReflections.instance$Pose$STANDING) {
player.setPose(Pose.STANDING);
} else if (pose == CoreReflections.instance$Pose$CROUCHING) {
player.setPose(Pose.SNEAKING);
} else if (pose == CoreReflections.instance$Pose$SWIMMING) {
player.setPose(Pose.SWIMMING);
}
if (!FastNMS.INSTANCE.checkEntityCollision(serverLevel, List.of(newAABB))) {
continue;
}
if (VersionHelper.isFolia()) {
player.teleportAsync(new Location(player.getWorld(), x, pos.y() + floorHeight, z, player.getYaw(), player.getPitch()));
} else {
player.teleport(new Location(player.getWorld(), x, pos.y() + floorHeight, z, player.getYaw(), player.getPitch()));
}
if (pose == CoreReflections.instance$Pose$STANDING) {
player.setPose(Pose.STANDING);
} else if (pose == CoreReflections.instance$Pose$CROUCHING) {
player.setPose(Pose.SNEAKING);
} else if (pose == CoreReflections.instance$Pose$SWIMMING) {
player.setPose(Pose.SWIMMING);
}
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);

View File

@@ -34,6 +34,7 @@ import org.bukkit.attribute.AttributeInstance;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.Directional;
import org.bukkit.block.data.Levelled;
import org.bukkit.block.data.Lightable;
import org.bukkit.block.data.type.*;
@@ -714,6 +715,37 @@ public final class InteractUtils {
registerInteraction(BlockKeys.BAMBOO_WALL_HANGING_SIGN, (player, item, blockState, result) -> true);
registerInteraction(BlockKeys.CRIMSON_WALL_HANGING_SIGN, (player, item, blockState, result) -> true);
registerInteraction(BlockKeys.WARPED_WALL_HANGING_SIGN, (player, item, blockState, result) -> true);
// 展示柜
registerInteraction(BlockKeys.OAK_SHELF, (player, item, blockState, result) -> blockState instanceof Directional directional && DirectionUtils.toBlockFace(result.getDirection()).equals(directional.getFacing()));
registerInteraction(BlockKeys.SPRUCE_SHELF, (player, item, blockState, result) -> blockState instanceof Directional directional && DirectionUtils.toBlockFace(result.getDirection()).equals(directional.getFacing()));
registerInteraction(BlockKeys.BIRCH_SHELF, (player, item, blockState, result) -> blockState instanceof Directional directional && DirectionUtils.toBlockFace(result.getDirection()).equals(directional.getFacing()));
registerInteraction(BlockKeys.JUNGLE_SHELF, (player, item, blockState, result) -> blockState instanceof Directional directional && DirectionUtils.toBlockFace(result.getDirection()).equals(directional.getFacing()));
registerInteraction(BlockKeys.ACACIA_SHELF, (player, item, blockState, result) -> blockState instanceof Directional directional && DirectionUtils.toBlockFace(result.getDirection()).equals(directional.getFacing()));
registerInteraction(BlockKeys.DARK_OAK_SHELF, (player, item, blockState, result) -> blockState instanceof Directional directional && DirectionUtils.toBlockFace(result.getDirection()).equals(directional.getFacing()));
registerInteraction(BlockKeys.MANGROVE_SHELF, (player, item, blockState, result) -> blockState instanceof Directional directional && DirectionUtils.toBlockFace(result.getDirection()).equals(directional.getFacing()));
registerInteraction(BlockKeys.CHERRY_SHELF, (player, item, blockState, result) -> blockState instanceof Directional directional && DirectionUtils.toBlockFace(result.getDirection()).equals(directional.getFacing()));
registerInteraction(BlockKeys.PALE_OAK_SHELF, (player, item, blockState, result) -> blockState instanceof Directional directional && DirectionUtils.toBlockFace(result.getDirection()).equals(directional.getFacing()));
registerInteraction(BlockKeys.BAMBOO_SHELF, (player, item, blockState, result) -> blockState instanceof Directional directional && DirectionUtils.toBlockFace(result.getDirection()).equals(directional.getFacing()));
registerInteraction(BlockKeys.CRIMSON_SHELF, (player, item, blockState, result) -> blockState instanceof Directional directional && DirectionUtils.toBlockFace(result.getDirection()).equals(directional.getFacing()));
registerInteraction(BlockKeys.WARPED_SHELF, (player, item, blockState, result) -> blockState instanceof Directional directional && DirectionUtils.toBlockFace(result.getDirection()).equals(directional.getFacing()));
// 铜傀儡雕像
registerInteraction(BlockKeys.COPPER_GOLEM_STATUE, ((player, item, blockData, result) -> true));
registerInteraction(BlockKeys.EXPOSED_COPPER_GOLEM_STATUE, ((player, item, blockData, result) -> true));
registerInteraction(BlockKeys.WEATHERED_COPPER_GOLEM_STATUE, ((player, item, blockData, result) -> true));
registerInteraction(BlockKeys.OXIDIZED_COPPER_GOLEM_STATUE, ((player, item, blockData, result) -> true));
registerInteraction(BlockKeys.WAXED_COPPER_GOLEM_STATUE, ((player, item, blockData, result) -> true));
registerInteraction(BlockKeys.WAXED_EXPOSED_COPPER_GOLEM_STATUE, ((player, item, blockData, result) -> true));
registerInteraction(BlockKeys.WAXED_WEATHERED_COPPER_GOLEM_STATUE, ((player, item, blockData, result) -> true));
registerInteraction(BlockKeys.WAXED_OXIDIZED_COPPER_GOLEM_STATUE, ((player, item, blockData, result) -> true));
// 铜箱子
registerInteraction(BlockKeys.COPPER_CHEST, ((player, item, blockData, result) -> true));
registerInteraction(BlockKeys.EXPOSED_COPPER_CHEST, ((player, item, blockData, result) -> true));
registerInteraction(BlockKeys.WEATHERED_COPPER_CHEST, ((player, item, blockData, result) -> true));
registerInteraction(BlockKeys.OXIDIZED_COPPER_CHEST, ((player, item, blockData, result) -> true));
registerInteraction(BlockKeys.WAXED_COPPER_CHEST, ((player, item, blockData, result) -> true));
registerInteraction(BlockKeys.WAXED_EXPOSED_COPPER_CHEST, ((player, item, blockData, result) -> true));
registerInteraction(BlockKeys.WAXED_WEATHERED_COPPER_CHEST, ((player, item, blockData, result) -> true));
registerInteraction(BlockKeys.WAXED_OXIDIZED_COPPER_CHEST, ((player, item, blockData, result) -> true));
}
static {

View File

@@ -5,6 +5,7 @@ import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.core.util.FriendlyByteBuf;
import net.momirealms.craftengine.core.util.MarkedHashMap;
import java.util.ArrayList;
import java.util.HashMap;
@@ -15,6 +16,9 @@ public final class TagUtils {
private TagUtils() {}
public record TagEntry(int id, List<String> tags) {
}
/**
* 构建模拟标签更新数据包(用于向客户端添加虚拟标签)
*
@@ -39,11 +43,9 @@ public final class TagUtils {
*
* @return 可发送给客户端的 ClientboundUpdateTagsPacket 数据包对象
*/
@SuppressWarnings("unchecked")
public static Object createUpdateTagsPacket(Map<Object, List<TagEntry>> tags) {
Map<Object, Object> registriesNetworkPayload = (Map<Object, Object>) FastNMS.INSTANCE.method$TagNetworkSerialization$serializeTagsToNetwork();
Map<Object, Object> modified = new HashMap<>();
for (Map.Entry<Object, Object> payload : registriesNetworkPayload.entrySet()) {
public static Object createUpdateTagsPacket(Map<Object, List<TagEntry>> tags, Map<Object, Object> existingTags) {
Map<Object, Object> modified = new MarkedHashMap<>();
for (Map.Entry<Object, Object> payload : existingTags.entrySet()) {
List<TagEntry> overrides = tags.get(payload.getKey());
if (overrides == null || overrides.isEmpty()) {
modified.put(payload.getKey(), payload.getValue());
@@ -83,7 +85,4 @@ public final class TagUtils {
}
return FastNMS.INSTANCE.constructor$ClientboundUpdateTagsPacket(modified);
}
public record TagEntry(int id, List<String> tags) {
}
}

View File

@@ -92,10 +92,12 @@ public class BukkitWorldManager implements WorldManager, Listener {
public void delayedInit() {
// load loaded chunks
for (World world : Bukkit.getWorlds()) {
BukkitWorld wrappedWorld = new BukkitWorld(world);
try {
CEWorld ceWorld = this.worlds.computeIfAbsent(world.getUID(), k -> new BukkitCEWorld(new BukkitWorld(world), this.storageAdaptor));
CEWorld ceWorld = this.worlds.computeIfAbsent(world.getUID(), k -> new BukkitCEWorld(wrappedWorld, this.storageAdaptor));
injectChunkGenerator(ceWorld);
for (Chunk chunk : world.getLoadedChunks()) {
handleChunkLoad(ceWorld, chunk);
handleChunkLoad(ceWorld, chunk, false);
}
ceWorld.setTicking(true);
} catch (Exception e) {
@@ -142,8 +144,9 @@ public class BukkitWorldManager implements WorldManager, Listener {
CEWorld ceWorld = new BukkitCEWorld(world, this.storageAdaptor);
this.worlds.put(uuid, ceWorld);
this.resetWorldArray();
this.injectChunkGenerator(ceWorld);
for (Chunk chunk : ((World) world.platformWorld()).getLoadedChunks()) {
handleChunkLoad(ceWorld, chunk);
handleChunkLoad(ceWorld, chunk, false);
}
ceWorld.setTicking(true);
}
@@ -154,12 +157,20 @@ public class BukkitWorldManager implements WorldManager, Listener {
if (this.worlds.containsKey(uuid)) return;
this.worlds.put(uuid, world);
this.resetWorldArray();
this.injectChunkGenerator(world);
for (Chunk chunk : ((World) world.world().platformWorld()).getLoadedChunks()) {
handleChunkLoad(world, chunk);
handleChunkLoad(world, chunk, false);
}
world.setTicking(true);
}
private void injectChunkGenerator(CEWorld world) {
Object serverLevel = world.world.serverWorld();
Object serverChunkCache = FastNMS.INSTANCE.method$ServerLevel$getChunkSource(serverLevel);
Object chunkMap = FastNMS.INSTANCE.field$ServerChunkCache$chunkMap(serverChunkCache);
FastNMS.INSTANCE.injectedWorldGen(world, chunkMap);
}
@Override
public CEWorld createWorld(net.momirealms.craftengine.core.world.World world, WorldDataStorage storage) {
return new BukkitCEWorld(world, storage);
@@ -219,7 +230,7 @@ public class BukkitWorldManager implements WorldManager, Listener {
if (world == null) {
return;
}
handleChunkLoad(world, event.getChunk());
handleChunkLoad(world, event.getChunk(), event.isNewChunk());
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
@@ -273,14 +284,42 @@ public class BukkitWorldManager implements WorldManager, Listener {
FastNMS.INSTANCE.method$LevelChunk$markUnsaved(levelChunk);
}
ceChunk.unload();
ceChunk.deactivateAllBlockEntities();
}
}
private void handleChunkLoad(CEWorld ceWorld, Chunk chunk) {
public void handleChunkGenerate(CEWorld ceWorld, ChunkPos chunkPos, Object chunkAccess) {
Object[] sections = FastNMS.INSTANCE.method$ChunkAccess$getSections(chunkAccess);
CEChunk ceChunk;
try {
ceChunk = ceWorld.worldDataStorage().readNewChunkAt(ceWorld, chunkPos);
CESection[] ceSections = ceChunk.sections();
synchronized (sections) {
for (int i = 0; i < ceSections.length; i++) {
CESection ceSection = ceSections[i];
Object section = sections[i];
int finalI = i;
WorldStorageInjector.injectLevelChunkSection(section, ceSection, ceChunk, new SectionPos(chunkPos.x, ceChunk.sectionY(i), chunkPos.z),
(injected) -> sections[finalI] = injected);
}
}
ceChunk.load();
} catch (IOException e) {
this.plugin.logger().warn("Failed to read new chunk at " + chunkPos.x + " " + chunkPos.z, e);
}
}
private void handleChunkLoad(CEWorld ceWorld, Chunk chunk, boolean isNew) {
int chunkX = chunk.getX();
int chunkZ = chunk.getZ();
ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ);
if (ceWorld.isChunkLoaded(chunkPos.longKey)) return;
CEChunk chunkAtIfLoaded = ceWorld.getChunkAtIfLoaded(chunkPos.longKey);
if (chunkAtIfLoaded != null) {
if (isNew) {
chunkAtIfLoaded.activateAllBlockEntities();
}
return;
}
CEChunk ceChunk;
try {
ceChunk = ceWorld.worldDataStorage().readChunkAt(ceWorld, chunkPos);
@@ -382,5 +421,6 @@ public class BukkitWorldManager implements WorldManager, Listener {
return;
}
ceChunk.load();
ceChunk.activateAllBlockEntities();
}
}