mirror of
https://github.com/Xiao-MoMi/craft-engine.git
synced 2025-12-26 18:39:20 +00:00
@@ -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")
|
||||
|
||||
@@ -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() {
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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() {
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() {}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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")
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user