From 4c6a073a9680f76d8615791364ad40ad8963c751 Mon Sep 17 00:00:00 2001 From: iqtester Date: Mon, 12 May 2025 11:27:52 -0400 Subject: [PATCH 01/10] Seat Type --- .../bukkit/entity/data/PlayerData.java | 15 ++ .../entity/furniture/BukkitFurniture.java | 43 +++-- .../furniture/BukkitFurnitureManager.java | 12 +- .../furniture/hitbox/BukkitHitBoxTypes.java | 11 +- .../furniture/seat/BukkitSeatEntity.java | 74 ++++++++ .../furniture/seat/BukkitSeatTypes.java | 12 ++ .../entity/furniture/seat/CrawlSeat.java | 168 ++++++++++++++++++ .../bukkit/entity/furniture/seat/LaySeat.java | 102 +++++++++++ .../bukkit/entity/furniture/seat/SitSeat.java | 110 ++++++++++++ .../bukkit/plugin/BukkitCraftEngine.java | 2 + .../DebugGetBlockInternalIdCommand.java | 11 +- .../plugin/network/BukkitNetworkManager.java | 4 + .../plugin/network/PacketConsumers.java | 8 +- .../plugin/user/BukkitServerPlayer.java | 13 ++ .../default/configuration/furniture.yml | 2 +- .../core/entity/furniture/AbstractSeat.java | 39 ++++ .../core/entity/furniture/HitBoxFactory.java | 6 +- .../core/entity/furniture/Seat.java | 21 +-- .../core/entity/furniture/SeatFactory.java | 8 + .../core/entity/furniture/SeatType.java | 42 +++++ .../core/entity/player/Player.java | 5 + .../core/entity/seat/SeatEntity.java | 14 ++ .../core/registry/BuiltInRegistries.java | 4 + .../craftengine/core/registry/Registries.java | 2 + 24 files changed, 678 insertions(+), 50 deletions(-) create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/PlayerData.java create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/BukkitSeatEntity.java create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/BukkitSeatTypes.java create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/CrawlSeat.java create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/LaySeat.java create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/SitSeat.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractSeat.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/SeatFactory.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/furniture/SeatType.java create mode 100644 core/src/main/java/net/momirealms/craftengine/core/entity/seat/SeatEntity.java diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/PlayerData.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/PlayerData.java new file mode 100644 index 000000000..23f822ae3 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/PlayerData.java @@ -0,0 +1,15 @@ +package net.momirealms.craftengine.bukkit.entity.data; + +import net.momirealms.craftengine.bukkit.util.Reflections; + +public class PlayerData extends LivingEntityData { + public static final PlayerData Pose = new PlayerData<>(6, EntityDataValue.Serializers$POSE, Reflections.instance$Pose$STANDING); + public static final PlayerData Skin = new PlayerData<>(17, EntityDataValue.Serializers$BYTE, (byte) 0); + public static final PlayerData Hand = new PlayerData<>(18, EntityDataValue.Serializers$BYTE, (byte) 0); + public static final PlayerData LShoulder = new PlayerData<>(19, EntityDataValue.Serializers$COMPOUND_TAG, Reflections.instance$CompoundTag$Empty); + public static final PlayerData RShoulder = new PlayerData<>(20, EntityDataValue.Serializers$COMPOUND_TAG, Reflections.instance$CompoundTag$Empty); + + public PlayerData(int id, Object serializer, T defaultValue) { + super(id, serializer, defaultValue); + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java index 6c8c3d4d3..5b1458d9f 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java @@ -3,22 +3,27 @@ package net.momirealms.craftengine.bukkit.entity.furniture; import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap; import it.unimi.dsi.fastutil.ints.IntArrayList; import net.momirealms.craftengine.bukkit.entity.BukkitEntity; +import net.momirealms.craftengine.bukkit.entity.furniture.seat.BukkitSeatEntity; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.util.EntityUtils; import net.momirealms.craftengine.bukkit.util.LegacyAttributeUtils; import net.momirealms.craftengine.bukkit.util.LocationUtils; +import net.momirealms.craftengine.bukkit.util.Reflections; +import net.momirealms.craftengine.bukkit.world.BukkitWorld; import net.momirealms.craftengine.core.entity.furniture.*; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.util.ArrayUtils; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.QuaternionUtils; import net.momirealms.craftengine.core.util.VersionHelper; +import net.momirealms.craftengine.core.world.Vec3d; +import net.momirealms.craftengine.core.world.World; import net.momirealms.craftengine.core.world.WorldPosition; import net.momirealms.craftengine.core.world.collision.AABB; import org.bukkit.Location; -import org.bukkit.attribute.Attribute; -import org.bukkit.entity.*; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; import org.bukkit.persistence.PersistentDataType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -50,7 +55,7 @@ public class BukkitFurniture implements Furniture { private final boolean hasExternalModel; // seats private final Set occupiedSeats = Collections.synchronizedSet(new HashSet<>()); - private final Vector> seats = new Vector<>(); + private final Map seats = Collections.synchronizedMap(new HashMap<>()); // cached spawn packet private Object cachedSpawnPacket; private Object cachedMinimizedSpawnPacket; @@ -190,24 +195,13 @@ public class BukkitFurniture implements Furniture { if (entity != null) entity.destroy(); } - for (WeakReference r : this.seats) { - Entity entity = r.get(); - if (entity == null) continue; - for (Entity passenger : entity.getPassengers()) { - entity.removePassenger(passenger); - } - entity.remove(); - } - this.seats.clear(); + destroySeats(); } @Override public void destroySeats() { - for (WeakReference entity : this.seats) { - Entity e = entity.get(); - if (e != null) { - e.remove(); - } + for (BukkitSeatEntity seat : this.seats.values()) { + seat.remove(); } this.seats.clear(); } @@ -295,7 +289,10 @@ public class BukkitFurniture implements Furniture { @Override public void spawnSeatEntityForPlayer(net.momirealms.craftengine.core.entity.player.Player player, Seat seat) { - spawnSeatEntityForPlayer((Player) player.platformPlayer(), seat); + net.momirealms.craftengine.core.entity.Entity e = seat.spawn(player, this); + BukkitSeatEntity seatEntity = (BukkitSeatEntity) e; + this.seats.put(e.entityID(), seatEntity); + player.setSeat(seatEntity); } @Override @@ -353,7 +350,7 @@ public class BukkitFurniture implements Furniture { } } - private Location calculateSeatLocation(Seat seat) { + public Location calculateSeatLocation(Seat seat) { Vector3f offset = QuaternionUtils.toQuaternionf(0, Math.toRadians(180 - this.location.getYaw()), 0).conjugate().transform(new Vector3f(seat.offset())); double yaw = seat.yaw() + this.location.getYaw(); if (yaw < -180) yaw += 360; @@ -362,4 +359,12 @@ public class BukkitFurniture implements Furniture { newLocation.add(offset.x, offset.y + 0.6, -offset.z); return newLocation; } + + public BukkitSeatEntity seatByEntityId(int id) { + return this.seats.get(id); + } + + public void removeSeatEntity(int id) { + this.seats.remove(id); + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java index e154d1e17..1bb8fed98 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java @@ -1,6 +1,8 @@ package net.momirealms.craftengine.bukkit.entity.furniture; +import net.momirealms.craftengine.bukkit.api.BukkitAdaptors; import net.momirealms.craftengine.bukkit.entity.furniture.hitbox.InteractionHitBox; +import net.momirealms.craftengine.bukkit.entity.furniture.seat.BukkitSeatEntity; import net.momirealms.craftengine.bukkit.nms.CollisionEntity; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; @@ -321,11 +323,19 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { protected void tryLeavingSeat(@NotNull Player player, @NotNull Entity vehicle) { Integer baseFurniture = vehicle.getPersistentDataContainer().get(FURNITURE_SEAT_BASE_ENTITY_KEY, PersistentDataType.INTEGER); if (baseFurniture == null) return; - vehicle.remove(); BukkitFurniture furniture = loadedFurnitureByRealEntityId(baseFurniture); if (furniture == null) { + vehicle.remove(); return; } + BukkitSeatEntity seatEntity = furniture.seatByEntityId(vehicle.getEntityId()); + if (seatEntity == null) vehicle.remove(); + else { + seatEntity.dismount(BukkitAdaptors.adapt(player)); + seatEntity.remove(); + furniture.removeSeatEntity(vehicle.getEntityId()); + } + String vector3f = vehicle.getPersistentDataContainer().get(BukkitFurnitureManager.FURNITURE_SEAT_VECTOR_3F_KEY, PersistentDataType.STRING); if (vector3f == null) { plugin.logger().warn("Failed to get vector3f for player " + player.getName() + "'s seat"); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitHitBoxTypes.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitHitBoxTypes.java index dda752638..05b0ea0cf 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitHitBoxTypes.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitHitBoxTypes.java @@ -4,12 +4,17 @@ import net.momirealms.craftengine.core.entity.furniture.HitBoxTypes; public class BukkitHitBoxTypes extends HitBoxTypes { - public static void init() {} - - static { + public static void init() { register(INTERACTION, InteractionHitBox.FACTORY); register(SHULKER, ShulkerHitBox.FACTORY); register(HAPPY_GHAST, HappyGhastHitBox.FACTORY); register(CUSTOM, CustomHitBox.FACTORY); } + + static { + //register(INTERACTION, InteractionHitBox.FACTORY); + //register(SHULKER, ShulkerHitBox.FACTORY); + //register(HAPPY_GHAST, HappyGhastHitBox.FACTORY); + //register(CUSTOM, CustomHitBox.FACTORY); + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/BukkitSeatEntity.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/BukkitSeatEntity.java new file mode 100644 index 000000000..f11dd3469 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/BukkitSeatEntity.java @@ -0,0 +1,74 @@ +package net.momirealms.craftengine.bukkit.entity.furniture.seat; + +import net.momirealms.craftengine.bukkit.world.BukkitWorld; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.entity.seat.SeatEntity; +import net.momirealms.craftengine.core.util.Direction; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.world.World; +import org.bukkit.entity.Entity; + +import java.lang.ref.WeakReference; + +public abstract class BukkitSeatEntity extends SeatEntity { + + private final WeakReference entity; + + public BukkitSeatEntity(Entity entity) { + this.entity = new WeakReference<>(entity); + } + + @Override + public void dismount(Player from) { + from.setSeat(null); + } + + @Override + public double x() { + return literalObject().getLocation().getX(); + } + + @Override + public double y() { + return literalObject().getLocation().getY(); + } + + @Override + public double z() { + return literalObject().getLocation().getZ(); + } + + @Override + public void tick() { + } + + @Override + public int entityID() { + return literalObject().getEntityId(); + } + + @Override + public float getXRot() { + return literalObject().getLocation().getYaw(); + } + + @Override + public float getYRot() { + return literalObject().getLocation().getPitch(); + } + + @Override + public World world() { + return new BukkitWorld(literalObject().getWorld()); + } + + @Override + public Direction getDirection() { + return Direction.NORTH; + } + + @Override + public org.bukkit.entity.Entity literalObject() { + return this.entity.get(); + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/BukkitSeatTypes.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/BukkitSeatTypes.java new file mode 100644 index 000000000..31ccc875c --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/BukkitSeatTypes.java @@ -0,0 +1,12 @@ +package net.momirealms.craftengine.bukkit.entity.furniture.seat; + +import net.momirealms.craftengine.core.entity.furniture.SeatType; + +public class BukkitSeatTypes extends SeatType { + + public static void init() { + register(SIT, SitSeat.FACTORY); + register(LAY, LaySeat.FACTORY); + register(CRAWL, CrawlSeat.FACTORY); + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/CrawlSeat.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/CrawlSeat.java new file mode 100644 index 000000000..99f74c7fd --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/CrawlSeat.java @@ -0,0 +1,168 @@ +package net.momirealms.craftengine.bukkit.entity.furniture.seat; + +import net.momirealms.craftengine.bukkit.api.BukkitAdaptors; +import net.momirealms.craftengine.bukkit.entity.data.ShulkerData; +import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager; +import net.momirealms.craftengine.bukkit.entity.furniture.LoadedFurniture; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; +import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; +import net.momirealms.craftengine.bukkit.util.EntityUtils; +import net.momirealms.craftengine.bukkit.util.LegacyAttributeUtils; +import net.momirealms.craftengine.bukkit.util.Reflections; +import net.momirealms.craftengine.core.entity.Entity; +import net.momirealms.craftengine.core.entity.furniture.AbstractSeat; +import net.momirealms.craftengine.core.entity.furniture.Furniture; +import net.momirealms.craftengine.core.entity.furniture.Seat; +import net.momirealms.craftengine.core.entity.furniture.SeatFactory; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.VersionHelper; +import org.bukkit.Location; +import org.bukkit.attribute.Attribute; +import org.bukkit.entity.ArmorStand; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.ItemDisplay; +import org.bukkit.entity.Pose; +import org.bukkit.persistence.PersistentDataType; +import org.joml.Vector3f; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.UUID; + +public class CrawlSeat extends AbstractSeat { + public static final SeatFactory FACTORY = new Factory(); + private static final List visualData = new ArrayList<>(); + private final boolean limitPlayerRotation; + + static { + ShulkerData.NoGravity.addEntityDataIfNotDefaultValue(true, visualData); + ShulkerData.Silent.addEntityDataIfNotDefaultValue(true, visualData); + ShulkerData.MobFlags.addEntityDataIfNotDefaultValue((byte) 0x01, visualData); + ShulkerData.SharedFlags.addEntityDataIfNotDefaultValue((byte) 0x20, visualData); + } + + public CrawlSeat(Vector3f offset, float yaw, boolean limitPlayerRotation) { + super(offset, yaw); + this.limitPlayerRotation = limitPlayerRotation; + } + + @Override + public Entity spawn(Player player, Furniture furniture) { + return spawn((org.bukkit.entity.Player) player.platformPlayer(), furniture); + } + + public Entity spawn(org.bukkit.entity.Player player, Furniture furniture) { + Location location = ((LoadedFurniture)furniture).calculateSeatLocation(this); + + int visualId = Reflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(); + List packets = new ArrayList<>(); + packets.add(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(visualId, UUID.randomUUID(), location.getX(), location.getY(), location.getZ(), location.getPitch(), location.getYaw(), + Reflections.instance$EntityType$SHULKER, 0, Reflections.instance$Vec3$Zero, 0)); + packets.add(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(visualId, List.copyOf(visualData))); + Object bundle = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(packets); + BukkitAdaptors.adapt(player).sendPacket(bundle, true); + + org.bukkit.entity.Entity seatEntity = this.limitPlayerRotation ? + EntityUtils.spawnEntity(player.getWorld(), VersionHelper.isOrAbove1_20_2() ? location.subtract(0,0.9875,0) : location.subtract(0,0.990625,0), EntityType.ARMOR_STAND, entity -> { + ArmorStand armorStand = (ArmorStand) entity; + if (VersionHelper.isOrAbove1_21_3()) { + Objects.requireNonNull(armorStand.getAttribute(Attribute.MAX_HEALTH)).setBaseValue(0.01); + } else { + LegacyAttributeUtils.setMaxHealth(armorStand); + } + armorStand.setSmall(true); + armorStand.setInvisible(true); + armorStand.setSilent(true); + armorStand.setInvulnerable(true); + armorStand.setArms(false); + armorStand.setCanTick(false); + armorStand.setAI(false); + armorStand.setGravity(false); + armorStand.setPersistent(false); + armorStand.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_SEAT_BASE_ENTITY_KEY, PersistentDataType.INTEGER, furniture.baseEntityId()); + armorStand.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_SEAT_VECTOR_3F_KEY, PersistentDataType.STRING, this.offset().x + ", " + this.offset().y + ", " + this.offset().z); + }) : + EntityUtils.spawnEntity(player.getWorld(), VersionHelper.isOrAbove1_20_2() ? location : location.subtract(0,0.25,0), EntityType.ITEM_DISPLAY, entity -> { + ItemDisplay itemDisplay = (ItemDisplay) entity; + itemDisplay.setPersistent(false); + itemDisplay.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_SEAT_BASE_ENTITY_KEY, PersistentDataType.INTEGER, furniture.baseEntityId()); + itemDisplay.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_SEAT_VECTOR_3F_KEY, PersistentDataType.STRING, this.offset().x + ", " + this.offset().y + ", " + this.offset().z); + }); + seatEntity.addPassenger(player); + // Todo 调查一下,位置y的变化是为了什么,可能是为了让玩家做下去之后的位置是seatlocation的位置 + BukkitCraftEngine.instance().scheduler().sync().runLater(() -> player.setPose(Pose.SWIMMING, true), + 1, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4); + + return new CrawlEntity(seatEntity, visualId); + // Todo 检测版本实现潜影贝缩小 + 找到合适的位置保持玩家的姿势 + 潜影贝骑乘展示实体 + } + + private static class CrawlEntity extends BukkitSeatEntity { + private final int visual; + + public CrawlEntity(org.bukkit.entity.Entity entity, int visual) { + super(entity); + this.visual = visual; + } + + @Override + public void sync(Player to) { + org.bukkit.entity.Entity entity = this.literalObject(); + for (org.bukkit.entity.Entity passenger : entity.getPassengers()) { + if (passenger instanceof org.bukkit.entity.Player) { + try { + Object serverPlayer = FastNMS.INSTANCE.method$CraftEntity$getHandle(passenger); + Reflections.method$Entity$refreshEntityData.invoke(serverPlayer, to.serverPlayer()); + } catch (Exception e) { + BukkitCraftEngine.instance().logger().warn("Failed to sync player pose", e); + } + } + } + } + + @Override + public void dismount(Player from) { + super.dismount(from); + ((org.bukkit.entity.Player) from.platformPlayer()).setPose(Pose.STANDING, false); + try { + @SuppressWarnings("Confusing primitive array argument to var-arg method PrimitiveArrayArgumentToVariableArgMethod") + Object packet = Reflections.constructor$ClientboundRemoveEntitiesPacket.newInstance(new int[]{visual}); + from.sendPacket(packet, true); + } catch (Exception e) { + BukkitCraftEngine.instance().logger().warn("Failed to send remove entity packet", e); + } + } + + @Override + public void remove() { + org.bukkit.entity.Entity entity = this.literalObject(); + if (entity == null) return; + + for (org.bukkit.entity.Entity passenger : entity.getPassengers()) { + entity.removePassenger(passenger); + if (passenger instanceof org.bukkit.entity.Player p) { + dismount(BukkitAdaptors.adapt(p)); + } + } + entity.remove(); + } + + @Override + public Key type() { + return Key.of("craftengine", "crawl"); + } + } + + public static class Factory implements SeatFactory { + + @Override + public Seat create(List args) { + if (args.size() == 1) return new CrawlSeat(MiscUtils.getAsVector3f(args.get(0), "seats"), 0, false); + return new CrawlSeat(MiscUtils.getAsVector3f(args.get(0), "seats"), Float.parseFloat(args.get(1)), true); + } + } +} \ No newline at end of file diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/LaySeat.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/LaySeat.java new file mode 100644 index 000000000..f606a46dc --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/LaySeat.java @@ -0,0 +1,102 @@ +package net.momirealms.craftengine.bukkit.entity.furniture.seat; + +import com.google.common.collect.ForwardingMultimap; +import com.google.common.collect.Multimap; +import net.momirealms.craftengine.bukkit.api.BukkitAdaptors; +import net.momirealms.craftengine.bukkit.entity.BukkitEntity; +import net.momirealms.craftengine.bukkit.entity.data.ShulkerData; +import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager; +import net.momirealms.craftengine.bukkit.entity.furniture.LoadedFurniture; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; +import net.momirealms.craftengine.bukkit.util.EntityUtils; +import net.momirealms.craftengine.bukkit.util.LegacyAttributeUtils; +import net.momirealms.craftengine.bukkit.util.Reflections; +import net.momirealms.craftengine.core.entity.Entity; +import net.momirealms.craftengine.core.entity.furniture.AbstractSeat; +import net.momirealms.craftengine.core.entity.furniture.Furniture; +import net.momirealms.craftengine.core.entity.furniture.Seat; +import net.momirealms.craftengine.core.entity.furniture.SeatFactory; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.VersionHelper; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.attribute.Attribute; +import org.bukkit.entity.ArmorStand; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.ItemDisplay; +import org.bukkit.entity.Pose; +import org.bukkit.persistence.PersistentDataType; +import org.joml.Vector3f; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.UUID; + +public class LaySeat extends AbstractSeat { + public static final SeatFactory FACTORY = new Factory(); + + public LaySeat(Vector3f offset, float yaw) { + super(offset, yaw); + } + + @Override + @SuppressWarnings("unchecked") + public Entity spawn(Player serverPlayer, Furniture furniture) { + Location location = ((LoadedFurniture)furniture).calculateSeatLocation(this); + org.bukkit.entity.Player player = (org.bukkit.entity.Player) serverPlayer.platformPlayer(); + + Object npc; + try { + Object server = Reflections.method$MinecraftServer$getServer.invoke(null); + Object level = FastNMS.INSTANCE.field$CraftWorld$ServerLevel(player.getWorld()); + Object npcProfile = Reflections.constructor$GameProfile.newInstance(UUID.randomUUID(), player.getName()); + Object pProfile = Reflections.method$ServerPlayer$getGameProfile.invoke(serverPlayer.serverPlayer()); + Multimap properties = (Multimap) Reflections.method$GameProfile$getProperties.invoke(npcProfile); + properties.putAll((Multimap) Reflections.method$GameProfile$getProperties.invoke(pProfile)); + Object information = Reflections.method$ServerPlayer$clientInformation.invoke(serverPlayer.serverPlayer()); + npc = Reflections.constructor$ServerPlayer.newInstance(server, level, npcProfile, information); + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to create NPC", e); + } + + return null; + } + + private static class LayEntity extends BukkitSeatEntity { + private final WeakReference npc; + + public LayEntity(org.bukkit.entity.Entity entity, org.bukkit.entity.Entity npc) { + super(entity); + this.npc = new WeakReference<>(npc); + } + + @Override + public void sync(Player to) { + + } + + @Override + public void remove() { + + } + + @Override + public Key type() { + return Key.of("craftengine", "lay"); + } + } + + public static class Factory implements SeatFactory { + + @Override + public Seat create(List arguments) { + return null; + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/SitSeat.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/SitSeat.java new file mode 100644 index 000000000..0e1ef4994 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/SitSeat.java @@ -0,0 +1,110 @@ +package net.momirealms.craftengine.bukkit.entity.furniture.seat; + +import net.momirealms.craftengine.bukkit.api.BukkitAdaptors; +import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager; +import net.momirealms.craftengine.bukkit.entity.furniture.LoadedFurniture; +import net.momirealms.craftengine.bukkit.util.EntityUtils; +import net.momirealms.craftengine.bukkit.util.LegacyAttributeUtils; +import net.momirealms.craftengine.core.entity.Entity; +import net.momirealms.craftengine.core.entity.furniture.AbstractSeat; +import net.momirealms.craftengine.core.entity.furniture.Furniture; +import net.momirealms.craftengine.core.entity.furniture.Seat; +import net.momirealms.craftengine.core.entity.furniture.SeatFactory; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.VersionHelper; +import org.bukkit.Location; +import org.bukkit.attribute.Attribute; +import org.bukkit.entity.ArmorStand; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.ItemDisplay; +import org.bukkit.persistence.PersistentDataType; +import org.joml.Vector3f; + +import java.util.List; +import java.util.Objects; + +public class SitSeat extends AbstractSeat { + public static final SeatFactory FACTORY = new Factory(); + private final boolean limitPlayerRotation; + + public SitSeat(Vector3f offset, float yaw, boolean limitPlayerRotation) { + super(offset, yaw); + this.limitPlayerRotation = limitPlayerRotation; + } + + @Override + public Entity spawn(Player player, Furniture furniture) { + return spawn((org.bukkit.entity.Player) player.platformPlayer(), furniture); + } + + public Entity spawn(org.bukkit.entity.Player player, Furniture furniture) { + Location location = ((LoadedFurniture)furniture).calculateSeatLocation(this); + org.bukkit.entity.Entity seatEntity = this.limitPlayerRotation ? + EntityUtils.spawnEntity(player.getWorld(), VersionHelper.isOrAbove1_20_2() ? location.subtract(0,0.9875,0) : location.subtract(0,0.990625,0), EntityType.ARMOR_STAND, entity -> { + ArmorStand armorStand = (ArmorStand) entity; + if (VersionHelper.isOrAbove1_21_3()) { + Objects.requireNonNull(armorStand.getAttribute(Attribute.MAX_HEALTH)).setBaseValue(0.01); + } else { + LegacyAttributeUtils.setMaxHealth(armorStand); + } + armorStand.setSmall(true); + armorStand.setInvisible(true); + armorStand.setSilent(true); + armorStand.setInvulnerable(true); + armorStand.setArms(false); + armorStand.setCanTick(false); + armorStand.setAI(false); + armorStand.setGravity(false); + armorStand.setPersistent(false); + armorStand.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_SEAT_BASE_ENTITY_KEY, PersistentDataType.INTEGER, furniture.baseEntityId()); + armorStand.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_SEAT_VECTOR_3F_KEY, PersistentDataType.STRING, this.offset().x + ", " + this.offset().y + ", " + this.offset().z); + }) : + EntityUtils.spawnEntity(player.getWorld(), VersionHelper.isOrAbove1_20_2() ? location : location.subtract(0,0.25,0), EntityType.ITEM_DISPLAY, entity -> { + ItemDisplay itemDisplay = (ItemDisplay) entity; + itemDisplay.setPersistent(false); + itemDisplay.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_SEAT_BASE_ENTITY_KEY, PersistentDataType.INTEGER, furniture.baseEntityId()); + itemDisplay.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_SEAT_VECTOR_3F_KEY, PersistentDataType.STRING, this.offset().x + ", " + this.offset().y + ", " + this.offset().z); + }); + seatEntity.addPassenger(player); + return new SitEntity(seatEntity); + } + + private static class SitEntity extends BukkitSeatEntity { + + public SitEntity(org.bukkit.entity.Entity entity) { + super(entity); + } + + @Override + public void sync(Player to) {} + + @Override + public void remove() { + org.bukkit.entity.Entity entity = this.literalObject(); + if (entity == null) return; + for (org.bukkit.entity.Entity passenger : entity.getPassengers()) { + entity.removePassenger(passenger); + if (passenger instanceof org.bukkit.entity.Player p) { + BukkitAdaptors.adapt(p).setSeat(null); + } + } + entity.remove(); + } + + @Override + public Key type() { + return Key.of("craftengine", "sit") ; + } + } + + public static class Factory implements SeatFactory { + + @Override + public Seat create(List args) { + if (args.size() == 1) return new SitSeat(MiscUtils.getAsVector3f(args.get(0), "seats"), 0, false); + return new SitSeat(MiscUtils.getAsVector3f(args.get(0), "seats"), Float.parseFloat(args.get(1)), true); + } + } +} \ No newline at end of file diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitCraftEngine.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitCraftEngine.java index d00647089..90203365a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitCraftEngine.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/BukkitCraftEngine.java @@ -8,6 +8,7 @@ import net.momirealms.craftengine.bukkit.block.behavior.BukkitBlockBehaviors; import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager; import net.momirealms.craftengine.bukkit.entity.furniture.hitbox.BukkitHitBoxTypes; import net.momirealms.craftengine.bukkit.entity.projectile.BukkitProjectileManager; +import net.momirealms.craftengine.bukkit.entity.furniture.seat.BukkitSeatTypes; import net.momirealms.craftengine.bukkit.font.BukkitFontManager; import net.momirealms.craftengine.bukkit.item.BukkitItemManager; import net.momirealms.craftengine.bukkit.item.behavior.BukkitItemBehaviors; @@ -179,6 +180,7 @@ public class BukkitCraftEngine extends CraftEngine { BukkitItemBehaviors.init(); BukkitHitBoxTypes.init(); PacketConsumers.initEntities(RegistryUtils.currentEntityTypeRegistrySize()); + BukkitSeatTypes.init(); super.packManager = new BukkitPackManager(this); super.senderFactory = new BukkitSenderFactory(this); super.itemManager = new BukkitItemManager(this); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugGetBlockInternalIdCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugGetBlockInternalIdCommand.java index ac1e9ae7b..7e1a63eea 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugGetBlockInternalIdCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugGetBlockInternalIdCommand.java @@ -8,6 +8,8 @@ import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager; import net.momirealms.craftengine.core.plugin.command.FlagKeys; import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.entity.Pose; import org.checkerframework.checker.nullness.qual.NonNull; import org.incendo.cloud.Command; import org.incendo.cloud.context.CommandContext; @@ -35,10 +37,13 @@ public class DebugGetBlockInternalIdCommand extends BukkitCommandFeature { + Player player = (Player) context.sender(); + if (player.hasFixedPose()) player.setPose(Pose.STANDING, false); + else player.setPose(Pose.SWIMMING, true); String data = context.get("id"); - ImmutableBlockState state = BlockStateParser.deserialize(data); - if (state == null) return; - context.sender().sendMessage(BlockStateUtils.getBlockOwnerIdFromState(state.customBlockState().handle()).toString()); + //ImmutableBlockState state = BlockStateParser.deserialize(data); + //if (state == null) return; + //context.sender().sendMessage(BlockStateUtils.getBlockOwnerIdFromState(state.customBlockState().handle()).toString()); }); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java index 21a2b347d..98a7df2ac 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java @@ -301,6 +301,10 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes return this.onlineUsers.get(player.getUniqueId()); } + public NetWorkUser getOnlineUser(UUID uuid) { + return onlineUsers.get(uuid); + } + public Channel getChannel(Player player) { return (Channel) FastNMS.INSTANCE.field$Player$connection$connection$channel(FastNMS.INSTANCE.method$CraftPlayer$getHandle(player)); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java index ea715b3b9..cf6ef4dbc 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java @@ -9,6 +9,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.IntList; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TranslationArgument; +import net.momirealms.craftengine.bukkit.api.BukkitAdaptors; import net.momirealms.craftengine.bukkit.api.CraftEngineFurniture; import net.momirealms.craftengine.bukkit.api.event.FurnitureAttemptBreakEvent; import net.momirealms.craftengine.bukkit.api.event.FurnitureBreakEvent; @@ -35,6 +36,7 @@ import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.bukkit.util.*; import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.entity.player.InteractionHand; +import net.momirealms.craftengine.core.entity.seat.SeatEntity; import net.momirealms.craftengine.core.font.FontManager; import net.momirealms.craftengine.core.font.IllegalCharacterProcessResult; import net.momirealms.craftengine.core.item.CustomItem; @@ -1642,9 +1644,9 @@ public class PacketConsumers { LocationUtils.toBlockPos(hitResult.blockPos()) ); } else { - furniture.findFirstAvailableSeat(entityId).ifPresent(seatPos -> { - if (furniture.tryOccupySeat(seatPos)) { - furniture.spawnSeatEntityForPlayer(serverPlayer, seatPos); + furniture.findFirstAvailableSeat(entityId).ifPresent(seat -> { + if (furniture.tryOccupySeat(seat)) { + furniture.spawnSeatEntityForPlayer(serverPlayer, seat); } }); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java index 3a25e446c..88cda7af2 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java @@ -23,6 +23,7 @@ import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.entity.player.GameMode; import net.momirealms.craftengine.core.entity.player.InteractionHand; import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.entity.seat.SeatEntity; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; @@ -108,6 +109,8 @@ public class BukkitServerPlayer extends Player { private double cachedInteractionRange; // cooldown data private CooldownData cooldownData; + // cache seat + private SeatEntity seatEntity; private final Map entityTypeView = new ConcurrentHashMap<>(); @@ -964,4 +967,14 @@ public class BukkitServerPlayer extends Player { public CooldownData cooldown() { return this.cooldownData; } + + @Override + public void setSeat(SeatEntity seatEntity) { + this.seatEntity = seatEntity; + } + + @Override + public SeatEntity seat() { + return this.seatEntity; + } } diff --git a/common-files/src/main/resources/resources/default/configuration/furniture.yml b/common-files/src/main/resources/resources/default/configuration/furniture.yml index 35b568623..5e3018767 100644 --- a/common-files/src/main/resources/resources/default/configuration/furniture.yml +++ b/common-files/src/main/resources/resources/default/configuration/furniture.yml @@ -36,7 +36,7 @@ items: interactive: true interaction-entity: true seats: - - 0,0,-0.1 0 + - 0,0,-0.1 0 crawl - 1,0,-0.1 0 loot: template: default:loot_table/furniture diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractSeat.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractSeat.java new file mode 100644 index 000000000..f05a6ae4d --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractSeat.java @@ -0,0 +1,39 @@ +package net.momirealms.craftengine.core.entity.furniture; + +import org.joml.Vector3f; + +import java.util.Objects; + +public abstract class AbstractSeat implements Seat{ + protected final Vector3f offset; + protected final float yaw; + + public AbstractSeat(Vector3f offset, float yaw) { + this.offset = offset; + this.yaw = yaw; + } + + @Override + public Vector3f offset() { + return offset; + } + + @Override + public float yaw() { + return yaw; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof AbstractSeat seat)) return false; + return Float.compare(yaw, seat.yaw()) == 0 && offset.equals(seat.offset()); + } + + @Override + public int hashCode() { + int result = Objects.hash(offset); + result = 31 * result + Float.hashCode(yaw); + return result; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBoxFactory.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBoxFactory.java index 3720eb9bd..2779a5d6c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBoxFactory.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBoxFactory.java @@ -13,11 +13,7 @@ public interface HitBoxFactory { static Seat[] getSeats(Map arguments) { List seats = (List) arguments.getOrDefault("seats", List.of()); return seats.stream() - .map(arg -> { - String[] split = arg.split(" "); - if (split.length == 1) return new Seat(MiscUtils.getAsVector3f(split[0], "seats"), 0, false); - return new Seat(MiscUtils.getAsVector3f(split[0], "seats"), Float.parseFloat(split[1]), true); - }) + .map(SeatType::fromString) .toArray(Seat[]::new); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Seat.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Seat.java index 885333703..d0f3eadd3 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Seat.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Seat.java @@ -1,23 +1,14 @@ package net.momirealms.craftengine.core.entity.furniture; +import net.momirealms.craftengine.core.entity.Entity; +import net.momirealms.craftengine.core.entity.player.Player; import org.joml.Vector3f; -import java.util.Objects; +public interface Seat { -public record Seat(Vector3f offset, float yaw, boolean limitPlayerRotation) { + Entity spawn(Player player, Furniture furniture); - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof Seat seat)) return false; - return Float.compare(yaw, seat.yaw) == 0 && Objects.equals(offset, seat.offset) && limitPlayerRotation == seat.limitPlayerRotation; - } + Vector3f offset(); - @Override - public int hashCode() { - int result = Objects.hashCode(offset); - result = 31 * result + Float.hashCode(yaw); - result = 31 * result + Boolean.hashCode(limitPlayerRotation); - return result; - } + float yaw(); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/SeatFactory.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/SeatFactory.java new file mode 100644 index 000000000..34b41577d --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/SeatFactory.java @@ -0,0 +1,8 @@ +package net.momirealms.craftengine.core.entity.furniture; + +import java.util.List; + +public interface SeatFactory { + + Seat create(List args); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/SeatType.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/SeatType.java new file mode 100644 index 000000000..a049595d7 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/SeatType.java @@ -0,0 +1,42 @@ +package net.momirealms.craftengine.core.entity.furniture; + +import com.google.common.collect.Lists; +import net.momirealms.craftengine.core.plugin.locale.LocalizedException; +import net.momirealms.craftengine.core.registry.BuiltInRegistries; +import net.momirealms.craftengine.core.registry.Holder; +import net.momirealms.craftengine.core.registry.Registries; +import net.momirealms.craftengine.core.registry.WritableRegistry; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceKey; + +import java.util.List; + +public class SeatType { + public static final Key SIT = Key.of("craftengine:sit"); + public static final Key LAY = Key.of("craftengine:lay"); + public static final Key CRAWL = Key.of("craftengine:crawl"); + + public static void register(Key key, SeatFactory factory) { + Holder.Reference holder =((WritableRegistry) BuiltInRegistries.SEAT_FACTORY) + .registerForHolder(new ResourceKey<>(Registries.SEAT_FACTORY.location(), key)); + holder.bindValue(factory); + } + + public static Seat fromString(String s) { + List split = Lists.newArrayList(s.split(" ")); + int last = split.size() - 1; + Key type = SIT; + SeatFactory factory; + try { + Float.parseFloat(split.get(last)); + } catch (NullPointerException | NumberFormatException e) { + type = Key.withDefaultNamespace(split.get(last), "craftengine"); + split.remove(last); + } + factory = BuiltInRegistries.SEAT_FACTORY.getValue(type); + if (factory == null) { + throw new LocalizedException("warning.config.furniture.hitbox.invalid_type"); + } + return factory.create(split); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java b/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java index e0b2d0394..51c141612 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java @@ -2,6 +2,7 @@ package net.momirealms.craftengine.core.entity.player; import net.kyori.adventure.text.Component; import net.momirealms.craftengine.core.entity.AbstractEntity; +import net.momirealms.craftengine.core.entity.seat.SeatEntity; import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.plugin.context.CooldownData; import net.momirealms.craftengine.core.plugin.network.NetWorkUser; @@ -140,4 +141,8 @@ public abstract class Player extends AbstractEntity implements NetWorkUser { public abstract void clearPotionEffects(); public abstract CooldownData cooldown(); + + public abstract void setSeat(SeatEntity seatEntity); + + public abstract SeatEntity seat(); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/seat/SeatEntity.java b/core/src/main/java/net/momirealms/craftengine/core/entity/seat/SeatEntity.java new file mode 100644 index 000000000..ccb0f1694 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/seat/SeatEntity.java @@ -0,0 +1,14 @@ +package net.momirealms.craftengine.core.entity.seat; + +import net.momirealms.craftengine.core.entity.AbstractEntity; +import net.momirealms.craftengine.core.entity.Entity; +import net.momirealms.craftengine.core.entity.player.Player; + +public abstract class SeatEntity extends AbstractEntity { + + public abstract void sync(Player to); + + public abstract void dismount(Player from); + + public abstract void remove(); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java b/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java index a28c42fe9..e09b61f34 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java +++ b/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java @@ -4,6 +4,7 @@ import net.momirealms.craftengine.core.block.CustomBlock; import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; import net.momirealms.craftengine.core.block.properties.PropertyFactory; import net.momirealms.craftengine.core.entity.furniture.HitBoxFactory; +import net.momirealms.craftengine.core.entity.furniture.SeatFactory; import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory; import net.momirealms.craftengine.core.item.recipe.CustomSmithingTransformRecipe; import net.momirealms.craftengine.core.item.recipe.RecipeFactory; @@ -56,6 +57,9 @@ public class BuiltInRegistries { public static final Registry> EVENT_FUNCTION_FACTORY = createRegistry(Registries.EVENT_FUNCTION_FACTORY); public static final Registry> EVENT_CONDITION_FACTORY = createRegistry(Registries.EVENT_CONDITION_FACTORY); public static final Registry> PLAYER_SELECTOR_FACTORY = createRegistry(Registries.PLAYER_SELECTOR_FACTORY); + public static final Registry>> PLAYER_BLOCK_FUNCTION_FACTORY = createRegistry(Registries.PLAYER_BLOCK_FUNCTION_FACTORY); + public static final Registry>> PLAYER_BLOCK_CONDITION_FACTORY = createRegistry(Registries.PLAYER_BLOCK_CONDITION_FACTORY); + public static final Registry SEAT_FACTORY = createRegistry(Registries.SEAT_FACTORY); private static Registry createRegistry(ResourceKey> key) { return new MappedRegistry<>(key); diff --git a/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java b/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java index 5d8ed2868..305556136 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java +++ b/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java @@ -4,6 +4,7 @@ import net.momirealms.craftengine.core.block.CustomBlock; import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; import net.momirealms.craftengine.core.block.properties.PropertyFactory; import net.momirealms.craftengine.core.entity.furniture.HitBoxFactory; +import net.momirealms.craftengine.core.entity.furniture.SeatFactory; import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory; import net.momirealms.craftengine.core.item.recipe.CustomSmithingTransformRecipe; import net.momirealms.craftengine.core.item.recipe.RecipeFactory; @@ -57,4 +58,5 @@ public class Registries { public static final ResourceKey>> EVENT_FUNCTION_FACTORY = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("event_function_factory")); public static final ResourceKey>> EVENT_CONDITION_FACTORY = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("event_condition_factory")); public static final ResourceKey>> PLAYER_SELECTOR_FACTORY = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("player_selector")); + public static final ResourceKey> SEAT_FACTORY = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("seat_factory")); } From 65f0e968cc4ee505aad13561053a6da7430e80d6 Mon Sep 17 00:00:00 2001 From: iqtester Date: Mon, 2 Jun 2025 14:25:29 -0400 Subject: [PATCH 02/10] Crawl and Lay --- .../entity/furniture/BukkitFurniture.java | 21 +- .../furniture/BukkitFurnitureManager.java | 28 +- .../furniture/FurnitureEventListener.java | 38 +- .../furniture/seat/BukkitSeatEntity.java | 94 ++- .../entity/furniture/seat/CrawlSeat.java | 156 ++--- .../bukkit/entity/furniture/seat/LaySeat.java | 633 ++++++++++++++++-- .../bukkit/entity/furniture/seat/SitSeat.java | 74 +- .../DebugGetBlockInternalIdCommand.java | 11 +- .../plugin/network/BukkitNetworkManager.java | 3 + .../plugin/network/PacketConsumers.java | 41 +- .../handler/FurniturePacketHandler.java | 2 +- .../craftengine/bukkit/util/EntityUtils.java | 104 ++- .../craftengine/bukkit/util/ItemUtils.java | 2 + .../craftengine/bukkit/util/PlayerUtils.java | 6 + .../default/configuration/furniture.yml | 2 +- .../src/main/resources/translations/en.yml | 1 + .../src/main/resources/translations/zh_cn.yml | 1 + .../core/entity/EquipmentSlot.java | 10 +- .../core/entity/furniture/Seat.java | 4 +- .../core/entity/furniture/SeatType.java | 4 +- .../core/entity/seat/SeatEntity.java | 22 +- .../plugin/network/EntityPacketHandler.java | 8 +- 22 files changed, 953 insertions(+), 312 deletions(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java index 5b1458d9f..92ff4c1b4 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java @@ -5,20 +5,16 @@ import it.unimi.dsi.fastutil.ints.IntArrayList; import net.momirealms.craftengine.bukkit.entity.BukkitEntity; import net.momirealms.craftengine.bukkit.entity.furniture.seat.BukkitSeatEntity; import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.network.BukkitNetworkManager; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; -import net.momirealms.craftengine.bukkit.util.EntityUtils; -import net.momirealms.craftengine.bukkit.util.LegacyAttributeUtils; +import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.bukkit.util.LocationUtils; -import net.momirealms.craftengine.bukkit.util.Reflections; -import net.momirealms.craftengine.bukkit.world.BukkitWorld; +import net.momirealms.craftengine.bukkit.util.PlayerUtils; import net.momirealms.craftengine.core.entity.furniture.*; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.util.ArrayUtils; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.QuaternionUtils; -import net.momirealms.craftengine.core.util.VersionHelper; -import net.momirealms.craftengine.core.world.Vec3d; -import net.momirealms.craftengine.core.world.World; import net.momirealms.craftengine.core.world.WorldPosition; import net.momirealms.craftengine.core.world.collision.AABB; import org.bukkit.Location; @@ -201,7 +197,7 @@ public class BukkitFurniture implements Furniture { @Override public void destroySeats() { for (BukkitSeatEntity seat : this.seats.values()) { - seat.remove(); + seat.destroy(); } this.seats.clear(); } @@ -289,10 +285,13 @@ public class BukkitFurniture implements Furniture { @Override public void spawnSeatEntityForPlayer(net.momirealms.craftengine.core.entity.player.Player player, Seat seat) { - net.momirealms.craftengine.core.entity.Entity e = seat.spawn(player, this); - BukkitSeatEntity seatEntity = (BukkitSeatEntity) e; - this.seats.put(e.entityID(), seatEntity); + BukkitSeatEntity seatEntity = (BukkitSeatEntity) seat.spawn(player, this); + this.seats.put(seatEntity.playerID(), seatEntity); player.setSeat(seatEntity); + BukkitServerPlayer serverPlayer = (BukkitServerPlayer) player; + for (Player p : PlayerUtils.getTrackedBy(serverPlayer.platformPlayer())) { + BukkitNetworkManager.instance().getOnlineUser(p).entityPacketHandlers().put(seatEntity.playerID(), seatEntity); + } } @Override diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java index 1bb8fed98..105654c31 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java @@ -15,7 +15,6 @@ import net.momirealms.craftengine.core.entity.furniture.*; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.sound.SoundData; import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.world.WorldPosition; import org.bukkit.*; @@ -25,7 +24,6 @@ import org.bukkit.event.Listener; import org.bukkit.persistence.PersistentDataType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.joml.Vector3f; import java.io.IOException; import java.util.Collection; @@ -38,7 +36,6 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { public static final NamespacedKey FURNITURE_KEY = KeyUtils.toNamespacedKey(FurnitureManager.FURNITURE_KEY); public static final NamespacedKey FURNITURE_EXTRA_DATA_KEY = KeyUtils.toNamespacedKey(FurnitureManager.FURNITURE_EXTRA_DATA_KEY); public static final NamespacedKey FURNITURE_SEAT_BASE_ENTITY_KEY = KeyUtils.toNamespacedKey(FurnitureManager.FURNITURE_SEAT_BASE_ENTITY_KEY); - public static final NamespacedKey FURNITURE_SEAT_VECTOR_3F_KEY = KeyUtils.toNamespacedKey(FurnitureManager.FURNITURE_SEAT_VECTOR_3F_KEY); public static final NamespacedKey FURNITURE_COLLISION = KeyUtils.toNamespacedKey(FurnitureManager.FURNITURE_COLLISION); public static Class COLLISION_ENTITY_CLASS = Interaction.class; public static Object NMS_COLLISION_ENTITY_TYPE = MEntityTypes.INTERACTION; @@ -321,28 +318,17 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { } protected void tryLeavingSeat(@NotNull Player player, @NotNull Entity vehicle) { - Integer baseFurniture = vehicle.getPersistentDataContainer().get(FURNITURE_SEAT_BASE_ENTITY_KEY, PersistentDataType.INTEGER); - if (baseFurniture == null) return; - BukkitFurniture furniture = loadedFurnitureByRealEntityId(baseFurniture); + net.momirealms.craftengine.core.entity.player.Player serverPlayer = BukkitAdaptors.adapt(player); + BukkitSeatEntity seatEntity = (BukkitSeatEntity) serverPlayer.seat(); + if (seatEntity == null || seatEntity.literalObject() != vehicle) return; + + BukkitFurniture furniture = seatEntity.furniture(); if (furniture == null) { - vehicle.remove(); + seatEntity.destroy(); return; } - BukkitSeatEntity seatEntity = furniture.seatByEntityId(vehicle.getEntityId()); - if (seatEntity == null) vehicle.remove(); - else { - seatEntity.dismount(BukkitAdaptors.adapt(player)); - seatEntity.remove(); - furniture.removeSeatEntity(vehicle.getEntityId()); - } - String vector3f = vehicle.getPersistentDataContainer().get(BukkitFurnitureManager.FURNITURE_SEAT_VECTOR_3F_KEY, PersistentDataType.STRING); - if (vector3f == null) { - plugin.logger().warn("Failed to get vector3f for player " + player.getName() + "'s seat"); - return; - } - Vector3f seatPos = MiscUtils.getAsVector3f(vector3f, "seat"); - furniture.removeOccupiedSeat(seatPos); + seatEntity.dismount(serverPlayer); if (player.getVehicle() != null) return; Location vehicleLocation = vehicle.getLocation(); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/FurnitureEventListener.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/FurnitureEventListener.java index f9c526233..b45c2f251 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/FurnitureEventListener.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/FurnitureEventListener.java @@ -2,6 +2,8 @@ package net.momirealms.craftengine.bukkit.entity.furniture; import com.destroystokyo.paper.event.entity.EntityAddToWorldEvent; import com.destroystokyo.paper.event.entity.EntityRemoveFromWorldEvent; +import net.momirealms.craftengine.bukkit.api.BukkitAdaptors; +import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import org.bukkit.entity.ArmorStand; import org.bukkit.entity.Entity; import org.bukkit.entity.ItemDisplay; @@ -10,7 +12,9 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.entity.PlayerDeathEvent; -import org.bukkit.event.player.PlayerInteractAtEntityEvent; +import org.bukkit.event.player.PlayerAnimationEvent; +import org.bukkit.event.player.PlayerArmorStandManipulateEvent; +import org.bukkit.event.player.PlayerItemHeldEvent; import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.event.world.ChunkUnloadEvent; import org.bukkit.event.world.EntitiesLoadEvent; @@ -123,12 +127,30 @@ public class FurnitureEventListener implements Listener { // do not allow players to put item on seats @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) - public void onInteractArmorStand(PlayerInteractAtEntityEvent event) { - Entity clicked = event.getRightClicked(); - if (clicked instanceof ArmorStand armorStand) { - Integer baseFurniture = armorStand.getPersistentDataContainer().get(BukkitFurnitureManager.FURNITURE_SEAT_BASE_ENTITY_KEY, PersistentDataType.INTEGER); - if (baseFurniture == null) return; - event.setCancelled(true); - } + public void onInteractArmorStand(PlayerArmorStandManipulateEvent event) { + ArmorStand clicked = event.getRightClicked(); + Integer baseFurniture = clicked.getPersistentDataContainer().get(BukkitFurnitureManager.FURNITURE_SEAT_BASE_ENTITY_KEY, PersistentDataType.INTEGER); + if (baseFurniture == null) return; + event.setCancelled(true); + } + + @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) + public void onMainHandChange(PlayerItemHeldEvent event) { + Player player = event.getPlayer(); + if (player.getVehicle() == null) return; + BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player); + if (serverPlayer.seat() == null) return; + + serverPlayer.seat().event(serverPlayer, event); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void onPlayerAnimation(PlayerAnimationEvent event) { + Player player = event.getPlayer(); + if (player.getVehicle() == null) return; + BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player); + if (serverPlayer.seat() == null) return; + + serverPlayer.seat().event(serverPlayer, event); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/BukkitSeatEntity.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/BukkitSeatEntity.java index f11dd3469..df82a045f 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/BukkitSeatEntity.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/BukkitSeatEntity.java @@ -1,74 +1,68 @@ package net.momirealms.craftengine.bukkit.entity.furniture.seat; -import net.momirealms.craftengine.bukkit.world.BukkitWorld; +import net.momirealms.craftengine.bukkit.api.BukkitAdaptors; +import net.momirealms.craftengine.bukkit.entity.BukkitEntity; +import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurniture; +import net.momirealms.craftengine.core.entity.furniture.Furniture; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.entity.seat.SeatEntity; -import net.momirealms.craftengine.core.util.Direction; -import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.world.World; +import net.momirealms.craftengine.core.plugin.network.NetWorkUser; import org.bukkit.entity.Entity; +import org.joml.Vector3f; -import java.lang.ref.WeakReference; +public abstract class BukkitSeatEntity extends BukkitEntity implements SeatEntity { + private final BukkitFurniture furniture; + private final Vector3f vector3f; + private final int playerID; -public abstract class BukkitSeatEntity extends SeatEntity { - - private final WeakReference entity; - - public BukkitSeatEntity(Entity entity) { - this.entity = new WeakReference<>(entity); + public BukkitSeatEntity(Entity entity, Furniture furniture, Vector3f vector3f, int playerID) { + super(entity); + this.furniture = (BukkitFurniture) furniture; + this.vector3f = vector3f; + this.playerID = playerID; } @Override - public void dismount(Player from) { - from.setSeat(null); + public void add(NetWorkUser from, NetWorkUser to) {} + + @Override + public void dismount(Player player) { + player.setSeat(null); + destroy(); } @Override - public double x() { - return literalObject().getLocation().getX(); + public void event(Player player, Object event) {} + + @Override + public void destroy() { + org.bukkit.entity.Entity entity = this.literalObject(); + if (entity == null) return; + + for (org.bukkit.entity.Entity passenger : entity.getPassengers()) { + entity.removePassenger(passenger); + if (passenger instanceof org.bukkit.entity.Player p && p.getEntityId() == this.playerID) { + dismount(BukkitAdaptors.adapt(p)); + return; + } + } + furniture.removeSeatEntity(playerID()); + furniture.removeOccupiedSeat(vector3f()); + entity.remove(); } @Override - public double y() { - return literalObject().getLocation().getY(); + public BukkitFurniture furniture() { + return this.furniture; } @Override - public double z() { - return literalObject().getLocation().getZ(); + public Vector3f vector3f() { + return this.vector3f; } @Override - public void tick() { - } - - @Override - public int entityID() { - return literalObject().getEntityId(); - } - - @Override - public float getXRot() { - return literalObject().getLocation().getYaw(); - } - - @Override - public float getYRot() { - return literalObject().getLocation().getPitch(); - } - - @Override - public World world() { - return new BukkitWorld(literalObject().getWorld()); - } - - @Override - public Direction getDirection() { - return Direction.NORTH; - } - - @Override - public org.bukkit.entity.Entity literalObject() { - return this.entity.get(); + public int playerID() { + return this.playerID; } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/CrawlSeat.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/CrawlSeat.java index 99f74c7fd..28a44898d 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/CrawlSeat.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/CrawlSeat.java @@ -1,37 +1,37 @@ package net.momirealms.craftengine.bukkit.entity.furniture.seat; import net.momirealms.craftengine.bukkit.api.BukkitAdaptors; +import net.momirealms.craftengine.bukkit.entity.data.PlayerData; import net.momirealms.craftengine.bukkit.entity.data.ShulkerData; -import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager; -import net.momirealms.craftengine.bukkit.entity.furniture.LoadedFurniture; +import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurniture; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; +import net.momirealms.craftengine.bukkit.plugin.network.BukkitNetworkManager; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.bukkit.util.EntityUtils; -import net.momirealms.craftengine.bukkit.util.LegacyAttributeUtils; +import net.momirealms.craftengine.bukkit.util.PlayerUtils; import net.momirealms.craftengine.bukkit.util.Reflections; -import net.momirealms.craftengine.core.entity.Entity; import net.momirealms.craftengine.core.entity.furniture.AbstractSeat; import net.momirealms.craftengine.core.entity.furniture.Furniture; import net.momirealms.craftengine.core.entity.furniture.Seat; import net.momirealms.craftengine.core.entity.furniture.SeatFactory; import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.entity.seat.SeatEntity; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.network.NetWorkUser; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.VersionHelper; import org.bukkit.Location; -import org.bukkit.attribute.Attribute; -import org.bukkit.entity.ArmorStand; -import org.bukkit.entity.EntityType; -import org.bukkit.entity.ItemDisplay; +import org.bukkit.entity.Entity; import org.bukkit.entity.Pose; -import org.bukkit.persistence.PersistentDataType; import org.joml.Vector3f; import java.util.ArrayList; +import java.util.Collections; import java.util.List; -import java.util.Objects; import java.util.UUID; +import java.util.function.Consumer; public class CrawlSeat extends AbstractSeat { public static final SeatFactory FACTORY = new Factory(); @@ -51,106 +51,92 @@ public class CrawlSeat extends AbstractSeat { } @Override - public Entity spawn(Player player, Furniture furniture) { + public SeatEntity spawn(Player player, Furniture furniture) { return spawn((org.bukkit.entity.Player) player.platformPlayer(), furniture); } - public Entity spawn(org.bukkit.entity.Player player, Furniture furniture) { - Location location = ((LoadedFurniture)furniture).calculateSeatLocation(this); + public SeatEntity spawn(org.bukkit.entity.Player player, Furniture furniture) { + Location location = ((BukkitFurniture)furniture).calculateSeatLocation(this); + org.bukkit.entity.Entity seatEntity = EntityUtils.spawnSeatEntity(furniture, this, player.getWorld(), location, this.limitPlayerRotation, null); + seatEntity.addPassenger(player); + + // Fix Rider Pose int visualId = Reflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(); List packets = new ArrayList<>(); packets.add(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(visualId, UUID.randomUUID(), location.getX(), location.getY(), location.getZ(), location.getPitch(), location.getYaw(), Reflections.instance$EntityType$SHULKER, 0, Reflections.instance$Vec3$Zero, 0)); packets.add(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(visualId, List.copyOf(visualData))); + + try { + if (VersionHelper.isOrAbove1_20_5()) { + Object attributeInstance = Reflections.constructor$AttributeInstance.newInstance(Reflections.instance$Holder$Attribute$scale, (Consumer) (o) -> {}); + Reflections.method$AttributeInstance$setBaseValue.invoke(attributeInstance, 0.6); + packets.add( + Reflections.constructor$ClientboundUpdateAttributesPacket0 + .newInstance(visualId, Collections.singletonList(attributeInstance)) + ); + packets.add(FastNMS.INSTANCE.constructor$ClientboundSetPassengersPacket(seatEntity.getEntityId(), visualId)); + } + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to add crawl seat attributes", e); + } + Object bundle = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(packets); - BukkitAdaptors.adapt(player).sendPacket(bundle, true); + BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player); + serverPlayer.sendPacket(bundle, true); - org.bukkit.entity.Entity seatEntity = this.limitPlayerRotation ? - EntityUtils.spawnEntity(player.getWorld(), VersionHelper.isOrAbove1_20_2() ? location.subtract(0,0.9875,0) : location.subtract(0,0.990625,0), EntityType.ARMOR_STAND, entity -> { - ArmorStand armorStand = (ArmorStand) entity; - if (VersionHelper.isOrAbove1_21_3()) { - Objects.requireNonNull(armorStand.getAttribute(Attribute.MAX_HEALTH)).setBaseValue(0.01); - } else { - LegacyAttributeUtils.setMaxHealth(armorStand); - } - armorStand.setSmall(true); - armorStand.setInvisible(true); - armorStand.setSilent(true); - armorStand.setInvulnerable(true); - armorStand.setArms(false); - armorStand.setCanTick(false); - armorStand.setAI(false); - armorStand.setGravity(false); - armorStand.setPersistent(false); - armorStand.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_SEAT_BASE_ENTITY_KEY, PersistentDataType.INTEGER, furniture.baseEntityId()); - armorStand.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_SEAT_VECTOR_3F_KEY, PersistentDataType.STRING, this.offset().x + ", " + this.offset().y + ", " + this.offset().z); - }) : - EntityUtils.spawnEntity(player.getWorld(), VersionHelper.isOrAbove1_20_2() ? location : location.subtract(0,0.25,0), EntityType.ITEM_DISPLAY, entity -> { - ItemDisplay itemDisplay = (ItemDisplay) entity; - itemDisplay.setPersistent(false); - itemDisplay.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_SEAT_BASE_ENTITY_KEY, PersistentDataType.INTEGER, furniture.baseEntityId()); - itemDisplay.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_SEAT_VECTOR_3F_KEY, PersistentDataType.STRING, this.offset().x + ", " + this.offset().y + ", " + this.offset().z); - }); - seatEntity.addPassenger(player); - // Todo 调查一下,位置y的变化是为了什么,可能是为了让玩家做下去之后的位置是seatlocation的位置 - BukkitCraftEngine.instance().scheduler().sync().runLater(() -> player.setPose(Pose.SWIMMING, true), - 1, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4); + // Sync Pose + player.setPose(Pose.SWIMMING, true); + Object syncPosePacket = null; + try { + Object playerData = Reflections.method$Entity$getEntityData.invoke(serverPlayer.serverPlayer()); + Object dataItem = Reflections.method$SynchedEntityData$getItem.invoke(playerData, PlayerData.Pose.entityDataAccessor()); + Object dataValue = Reflections.method$SynchedEntityData$DataItem$value.invoke(dataItem); + syncPosePacket = FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(serverPlayer.entityID(), List.of(dataValue)); + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to construct sync pose packet", e); + } - return new CrawlEntity(seatEntity, visualId); - // Todo 检测版本实现潜影贝缩小 + 找到合适的位置保持玩家的姿势 + 潜影贝骑乘展示实体 + Object finalSyncPosePacket = syncPosePacket; + BukkitCraftEngine.instance().scheduler().sync().runLater(() -> { + serverPlayer.sendPacket(finalSyncPosePacket, true); + for (org.bukkit.entity.Player p : PlayerUtils.getTrackedBy(player)) { + BukkitNetworkManager.instance().sendPacket(BukkitAdaptors.adapt(p), finalSyncPosePacket, true); + } + }, 1, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4); + + return new CrawlEntity(seatEntity, furniture, offset(), player.getEntityId(), visualId, syncPosePacket); } private static class CrawlEntity extends BukkitSeatEntity { - private final int visual; + private final int visualId; + private final Object syncPosePacket; - public CrawlEntity(org.bukkit.entity.Entity entity, int visual) { - super(entity); - this.visual = visual; + + public CrawlEntity(Entity entity, Furniture furniture, Vector3f vector3f, int playerID, int visualId, Object fixPosePacket) { + super(entity, furniture, vector3f, playerID); + this.visualId = visualId; + this.syncPosePacket = fixPosePacket; } @Override - public void sync(Player to) { - org.bukkit.entity.Entity entity = this.literalObject(); - for (org.bukkit.entity.Entity passenger : entity.getPassengers()) { - if (passenger instanceof org.bukkit.entity.Player) { - try { - Object serverPlayer = FastNMS.INSTANCE.method$CraftEntity$getHandle(passenger); - Reflections.method$Entity$refreshEntityData.invoke(serverPlayer, to.serverPlayer()); - } catch (Exception e) { - BukkitCraftEngine.instance().logger().warn("Failed to sync player pose", e); - } - } - } + public void add(NetWorkUser from, NetWorkUser to) { + to.sendPacket(syncPosePacket, false); } @Override - public void dismount(Player from) { - super.dismount(from); - ((org.bukkit.entity.Player) from.platformPlayer()).setPose(Pose.STANDING, false); + public void dismount(Player player) { + super.dismount(player); + ((org.bukkit.entity.Player) player.platformPlayer()).setPose(Pose.STANDING, false); try { - @SuppressWarnings("Confusing primitive array argument to var-arg method PrimitiveArrayArgumentToVariableArgMethod") - Object packet = Reflections.constructor$ClientboundRemoveEntitiesPacket.newInstance(new int[]{visual}); - from.sendPacket(packet, true); + Object packet = Reflections.constructor$ClientboundRemoveEntitiesPacket.newInstance((Object) new int[]{visualId}); + player.sendPacket(packet, false); } catch (Exception e) { - BukkitCraftEngine.instance().logger().warn("Failed to send remove entity packet", e); + BukkitCraftEngine.instance().logger().warn("Failed to remove crawl entity", e); } } - @Override - public void remove() { - org.bukkit.entity.Entity entity = this.literalObject(); - if (entity == null) return; - - for (org.bukkit.entity.Entity passenger : entity.getPassengers()) { - entity.removePassenger(passenger); - if (passenger instanceof org.bukkit.entity.Player p) { - dismount(BukkitAdaptors.adapt(p)); - } - } - entity.remove(); - } - @Override public Key type() { return Key.of("craftengine", "crawl"); @@ -161,8 +147,8 @@ public class CrawlSeat extends AbstractSeat { @Override public Seat create(List args) { - if (args.size() == 1) return new CrawlSeat(MiscUtils.getAsVector3f(args.get(0), "seats"), 0, false); - return new CrawlSeat(MiscUtils.getAsVector3f(args.get(0), "seats"), Float.parseFloat(args.get(1)), true); + if (args.size() == 1) return new CrawlSeat(MiscUtils.getAsVector3f(args.getFirst(), "seats"), 0, false); + return new CrawlSeat(MiscUtils.getAsVector3f(args.getFirst(), "seats"), Float.parseFloat(args.get(1)), true); } } } \ No newline at end of file diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/LaySeat.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/LaySeat.java index f606a46dc..fee10a713 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/LaySeat.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/LaySeat.java @@ -1,102 +1,639 @@ package net.momirealms.craftengine.bukkit.entity.furniture.seat; -import com.google.common.collect.ForwardingMultimap; import com.google.common.collect.Multimap; +import com.mojang.datafixers.util.Pair; +import it.unimi.dsi.fastutil.ints.IntList; import net.momirealms.craftengine.bukkit.api.BukkitAdaptors; -import net.momirealms.craftengine.bukkit.entity.BukkitEntity; -import net.momirealms.craftengine.bukkit.entity.data.ShulkerData; -import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager; -import net.momirealms.craftengine.bukkit.entity.furniture.LoadedFurniture; +import net.momirealms.craftengine.bukkit.entity.data.LivingEntityData; +import net.momirealms.craftengine.bukkit.entity.data.PlayerData; +import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurniture; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; -import net.momirealms.craftengine.bukkit.util.EntityUtils; -import net.momirealms.craftengine.bukkit.util.LegacyAttributeUtils; -import net.momirealms.craftengine.bukkit.util.Reflections; -import net.momirealms.craftengine.core.entity.Entity; +import net.momirealms.craftengine.bukkit.plugin.network.BukkitNetworkManager; +import net.momirealms.craftengine.bukkit.plugin.scheduler.impl.FoliaTask; +import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; +import net.momirealms.craftengine.bukkit.util.*; +import net.momirealms.craftengine.core.entity.EquipmentSlot; import net.momirealms.craftengine.core.entity.furniture.AbstractSeat; import net.momirealms.craftengine.core.entity.furniture.Furniture; import net.momirealms.craftengine.core.entity.furniture.Seat; import net.momirealms.craftengine.core.entity.furniture.SeatFactory; import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.entity.seat.SeatEntity; import net.momirealms.craftengine.core.plugin.CraftEngine; -import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.MiscUtils; -import net.momirealms.craftengine.core.util.VersionHelper; -import org.bukkit.Bukkit; +import net.momirealms.craftengine.core.plugin.network.NMSPacketEvent; +import net.momirealms.craftengine.core.plugin.network.NetWorkUser; +import net.momirealms.craftengine.core.plugin.scheduler.SchedulerTask; +import net.momirealms.craftengine.core.util.*; +import net.momirealms.craftengine.core.world.BlockPos; import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Statistic; import org.bukkit.attribute.Attribute; -import org.bukkit.entity.ArmorStand; -import org.bukkit.entity.EntityType; -import org.bukkit.entity.ItemDisplay; -import org.bukkit.entity.Pose; -import org.bukkit.persistence.PersistentDataType; +import org.bukkit.attribute.AttributeInstance; +import org.bukkit.block.data.BlockData; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.event.player.PlayerAnimationEvent; +import org.bukkit.event.player.PlayerAnimationType; +import org.bukkit.event.player.PlayerItemHeldEvent; +import org.bukkit.inventory.EntityEquipment; +import org.bukkit.inventory.ItemStack; +import org.bukkit.potion.PotionEffectType; import org.joml.Vector3f; -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.UUID; +import java.lang.reflect.Method; +import java.util.*; +import java.util.concurrent.TimeUnit; public class LaySeat extends AbstractSeat { public static final SeatFactory FACTORY = new Factory(); + private final Direction facing; + private final boolean sleep; + private final boolean phantom; - public LaySeat(Vector3f offset, float yaw) { - super(offset, yaw); + public LaySeat(Vector3f offset, Direction facing, boolean sleep, boolean phantom) { + super(offset, 0); + this.facing = facing; + this.sleep = sleep; + this.phantom = phantom; } @Override - @SuppressWarnings("unchecked") - public Entity spawn(Player serverPlayer, Furniture furniture) { - Location location = ((LoadedFurniture)furniture).calculateSeatLocation(this); - org.bukkit.entity.Player player = (org.bukkit.entity.Player) serverPlayer.platformPlayer(); + @SuppressWarnings({"unchecked", "rawtypes"}) + public SeatEntity spawn(Player cePlayer, Furniture furniture) { + Location loc = ((BukkitFurniture)furniture).calculateSeatLocation(this); + + org.bukkit.entity.Player player = (org.bukkit.entity.Player) cePlayer.platformPlayer(); + Object serverPlayer = cePlayer.serverPlayer(); + + // Pose offset nearly same as vanilla + AttributeInstance attribute = VersionHelper.isOrAbove1_21_2() ? player.getAttribute(Attribute.SCALE) : null; + double scale = attribute == null ? 1 : attribute.getValue(); + loc.add(0, 0.08525 * scale, 0); - Object npc; try { + List packets = new ArrayList<>(); + // NPC Object server = Reflections.method$MinecraftServer$getServer.invoke(null); Object level = FastNMS.INSTANCE.field$CraftWorld$ServerLevel(player.getWorld()); - Object npcProfile = Reflections.constructor$GameProfile.newInstance(UUID.randomUUID(), player.getName()); - Object pProfile = Reflections.method$ServerPlayer$getGameProfile.invoke(serverPlayer.serverPlayer()); - Multimap properties = (Multimap) Reflections.method$GameProfile$getProperties.invoke(npcProfile); - properties.putAll((Multimap) Reflections.method$GameProfile$getProperties.invoke(pProfile)); - Object information = Reflections.method$ServerPlayer$clientInformation.invoke(serverPlayer.serverPlayer()); - npc = Reflections.constructor$ServerPlayer.newInstance(server, level, npcProfile, information); - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to create NPC", e); - } + UUID uuid = UUID.randomUUID(); + Object npcProfile = Reflections.constructor$GameProfile.newInstance(uuid, player.getName()); + Object playerProfile = Reflections.method$ServerPlayer$getGameProfile.invoke(serverPlayer); + Multimap properties = (Multimap) Reflections.method$GameProfile$getProperties.invoke(npcProfile); + properties.putAll((Multimap) Reflections.method$GameProfile$getProperties.invoke(playerProfile)); + + Object npc; + if (VersionHelper.isOrAbove1_20_2()) { + Object clientInfo = Reflections.method$ServerPlayer$clientInformation.invoke(serverPlayer); + npc = Reflections.constructor$ServerPlayer.newInstance(server, level, npcProfile, clientInfo); + } else { + npc = Reflections.constructor$ServerPlayer.newInstance(server, level, npcProfile); + } + int npcId = FastNMS.INSTANCE.method$Entity$getId(npc); + Reflections.method$Entity$absSnapTo.invoke(npc, loc.getX(), loc.getY(), loc.getZ(), 0, 0); + Object npcSpawnPacket = FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(npcId, uuid, + loc.getX(), loc.getY(), loc.getZ(), 0, 0, + Reflections.instance$EntityType$PLAYER, 0, Reflections.instance$Vec3$Zero, 0); + + // Info + EnumSet enumSet = EnumSet.noneOf((Class) Reflections.clazz$ClientboundPlayerInfoUpdatePacket$Action); + enumSet.add(Reflections.instance$ClientboundPlayerInfoUpdatePacket$Action$ADD_PLAYER); + Object entry; + if (VersionHelper.isOrAbove1_21_4()) { + entry = Reflections.constructor$ClientBoundPlayerInfoUpdatePacket$Entry.newInstance( + uuid, npcProfile, false, 0, Reflections.instance$GameType$SURVIVAL, null, true, 0, null); + } else if (VersionHelper.isOrAbove1_21_3()) { + entry = Reflections.constructor$ClientBoundPlayerInfoUpdatePacket$Entry.newInstance( + uuid, npcProfile, false, 0, Reflections.instance$GameType$SURVIVAL, null, 0, null); + } else { + entry = Reflections.constructor$ClientBoundPlayerInfoUpdatePacket$Entry.newInstance( + uuid, npcProfile, false, 0, Reflections.instance$GameType$SURVIVAL, null, null); + } + Object npcInfoPacket = FastNMS.INSTANCE.constructor$ClientboundPlayerInfoUpdatePacket(enumSet, Collections.singletonList(entry)); + + // Bed + Direction bedDir = Direction.fromYaw(loc.getYaw() + Direction.getYaw(facing)); + if (bedDir == Direction.EAST || bedDir == Direction.WEST) bedDir = bedDir.opposite(); + BlockData bedData = Material.WHITE_BED.createBlockData("[facing=" + bedDir.name().toLowerCase() + ",part=head]"); + Location bedLoc = loc.clone(); + bedLoc.setY(bedLoc.getWorld().getMinHeight()); + Object bedPos = LocationUtils.toBlockPos(new BlockPos(bedLoc.getBlockX(), bedLoc.getBlockY(), bedLoc.getBlockZ())); + Object blockState = BlockStateUtils.blockDataToBlockState(bedData); + Object bedPacket = Reflections.constructor$ClientboundBlockUpdatePacket.newInstance(bedPos, blockState); + + // Data + Object npcData = Reflections.method$Entity$getEntityData.invoke(npc); + Object playerData = Reflections.method$Entity$getEntityData.invoke(serverPlayer); + Reflections.method$Entity$setInvisible.invoke(serverPlayer, true); + Reflections.method$SynchedEntityData$set.invoke(npcData, PlayerData.Pose.entityDataAccessor(), Reflections.instance$Pose$SLEEPING); + Reflections.method$SynchedEntityData$set.invoke(npcData, LivingEntityData.SleepingPos.entityDataAccessor(), Optional.of(bedPos)); + Reflections.method$SynchedEntityData$set.invoke(npcData, PlayerData.Skin.entityDataAccessor(), Reflections.method$SynchedEntityData$get.invoke(playerData, PlayerData.Skin.entityDataAccessor())); + Reflections.method$SynchedEntityData$set.invoke(npcData, PlayerData.Hand.entityDataAccessor(), Reflections.method$SynchedEntityData$get.invoke(playerData, PlayerData.Hand.entityDataAccessor())); + Reflections.method$SynchedEntityData$set.invoke(npcData, PlayerData.LShoulder.entityDataAccessor(), Reflections.method$SynchedEntityData$get.invoke(playerData, PlayerData.LShoulder.entityDataAccessor())); + Reflections.method$SynchedEntityData$set.invoke(npcData, PlayerData.RShoulder.entityDataAccessor(), Reflections.method$SynchedEntityData$get.invoke(playerData, PlayerData.RShoulder.entityDataAccessor())); + Reflections.method$SynchedEntityData$set.invoke(playerData, PlayerData.LShoulder.entityDataAccessor(), Reflections.instance$CompoundTag$Empty); + Reflections.method$SynchedEntityData$set.invoke(playerData, PlayerData.RShoulder.entityDataAccessor(), Reflections.instance$CompoundTag$Empty); + + // SetData + Reflections.method$Entity$setInvisible.invoke(serverPlayer, true); + Object npcDataPacket = FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket( + npcId, (List) Reflections.method$SynchedEntityData$packDirty.invoke(npcData) + ); + + // Remove + Object npcRemovePacket = Reflections.constructor$ClientboundRemoveEntitiesPacket.newInstance((Object) new int[]{npcId}); + + // TP + Object npcTeleportPacket; + if (VersionHelper.isOrAbove1_21_3()) { + Object positionMoveRotation = Reflections.method$PositionMoveRotation$of.invoke(null, npc); + npcTeleportPacket = Reflections.constructor$ClientboundTeleportEntityPacket.newInstance(npcId, positionMoveRotation, Set.of(), false); + } else { + npcTeleportPacket = Reflections.constructor$ClientboundTeleportEntityPacket.newInstance(npc); + } + + + // Equipment + List> emptySlots = new ArrayList<>(); + + emptySlots.add(Pair.of(Reflections.instance$EquipmentSlot$MAINHAND, Reflections.instance$ItemStack$Air)); + emptySlots.add(Pair.of(Reflections.instance$EquipmentSlot$OFFHAND, Reflections.instance$ItemStack$Air)); + emptySlots.add(Pair.of(Reflections.instance$EquipmentSlot$HEAD, Reflections.instance$ItemStack$Air)); + emptySlots.add(Pair.of(Reflections.instance$EquipmentSlot$CHEST, Reflections.instance$ItemStack$Air)); + emptySlots.add(Pair.of(Reflections.instance$EquipmentSlot$LEGS, Reflections.instance$ItemStack$Air)); + emptySlots.add(Pair.of(Reflections.instance$EquipmentSlot$FEET, Reflections.instance$ItemStack$Air)); + Object emptyEquipPacket = Reflections.constructor$ClientboundSetEquipmentPacket.newInstance(player.getEntityId(), emptySlots); + + Map equipments = new HashMap<>(); + EntityEquipment equipment = player.getEquipment(); + for (org.bukkit.inventory.EquipmentSlot slot : org.bukkit.inventory.EquipmentSlot.values()) { + if ((!slot.isHand() && !slot.isArmor()) + || (VersionHelper.isOrAbove1_20_5() && slot == org.bukkit.inventory.EquipmentSlot.BODY)) { + continue; + } + EquipmentSlot slotId = EntityUtils.toCEEquipmentSlot(slot); + ItemStack item = equipment.getItem(slot); + equipments.put(slotId, item); + } + List> npcSlots = new ArrayList<>(); + equipments.forEach((slot, item) -> npcSlots.add(Pair.of(EntityUtils.fromEquipmentSlot(slot), FastNMS.INSTANCE.method$CraftItemStack$asNMSCopy(item)))); + Object fullEquipPacket = Reflections.constructor$ClientboundSetEquipmentPacket.newInstance(npcId, npcSlots); + + + packets.add(npcInfoPacket); + packets.add(npcSpawnPacket); + packets.add(bedPacket); + packets.add(npcDataPacket); + packets.add(npcTeleportPacket); + packets.add(emptyEquipPacket); + Object npcInitPackets = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(packets); + + // Spawn + org.bukkit.entity.Entity seatEntity = EntityUtils.spawnSeatEntity(furniture, this, player.getWorld(), loc, false, null); + seatEntity.addPassenger(player); // 0.5 higher + cePlayer.sendPacket(npcInitPackets, true); + cePlayer.sendPacket(fullEquipPacket, true); + if (player.getY() > 0) { + BukkitCraftEngine.instance().scheduler().asyncLater(() -> cePlayer.sendPacket(npcTeleportPacket, true), + 50, TimeUnit.MILLISECONDS); // over height 0 cost 2 npcTeleportPacket + } + + for (org.bukkit.entity.Player p : PlayerUtils.getTrackedBy(player)) { + BukkitNetworkManager.instance().getOnlineUser(p).sendPacket(npcInitPackets, false); + BukkitNetworkManager.instance().getOnlineUser(p).sendPacket(fullEquipPacket, false); + if (player.getY() > 0) { + BukkitCraftEngine.instance().scheduler().asyncLater(() -> BukkitNetworkManager.instance().getOnlineUser(p).sendPacket(npcTeleportPacket, false), + 50, TimeUnit.MILLISECONDS); + } + } + + // HeadRot + Direction npcDir = bedDir.opposite(); + float npcYawOffset = 0.0f; + if (player.getY() > 0.0874218749) { + if (VersionHelper.isOrAbove1_21_2()) { + if (npcDir == Direction.SOUTH) npcYawOffset = 27; + if (npcDir == Direction.NORTH) npcYawOffset = -26; + } + } + + if (sleep) { + player.setSleepingIgnored(true); + } + + if (phantom) { + player.setStatistic(Statistic.TIME_SINCE_REST, 0); + } + + return new LayEntity( + seatEntity, + furniture, + this.offset(), + npcInitPackets, + npcRemovePacket, + npcTeleportPacket, + (BukkitServerPlayer) cePlayer, + bedLoc, + npc, + npcId, + npcDir, + npcYawOffset, + equipments, + emptyEquipPacket, + fullEquipPacket, + sleep + ); + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to spawn LaySeat", e); + } return null; } private static class LayEntity extends BukkitSeatEntity { - private final WeakReference npc; + private final Object npcInitPackets; + private final Object npcRemovePacket; + private final Object npcTPPacket; + private final BukkitServerPlayer serverPlayer; + private final Object npc; + private final Location bedLoc; + private final int npcID; + private final Direction npcDir; + private final float npcYawOffset; - public LayEntity(org.bukkit.entity.Entity entity, org.bukkit.entity.Entity npc) { - super(entity); - this.npc = new WeakReference<>(npc); + // Equipment + private final PlayerMonitorTask task; + private final Map equipments; + private final Object emptyEquipPacket; + private Object updateEquipPacket; + private Object fullEquipPacket; + + private final boolean sleep; + private Object npcRotHeadPacket; + private Object npcDataPacket; + + public LayEntity( + org.bukkit.entity.Entity entity, + Furniture furniture, + Vector3f vector, + Object npcInitPackets, + Object npcRemovePacket, + Object npcTPPacket, + BukkitServerPlayer serverPlayer, + Location bedLoc, + Object npc, + int npcID, + Direction npcDir, + float npcYawOffset, + Map equipments, + Object emptyEquipPacket, + Object fullEquipPacket, + boolean sleep + ) { + super(entity, furniture, vector, serverPlayer.entityID()); + this.npcInitPackets = npcInitPackets; + this.npcRemovePacket = npcRemovePacket; + this.npcTPPacket = npcTPPacket; + this.serverPlayer = serverPlayer; + this.bedLoc = bedLoc; + this.npc = npc; + this.npcID = npcID; + this.npcDir = npcDir; + this.npcYawOffset = npcYawOffset; + + this.task = new PlayerMonitorTask(); + this.equipments = equipments; + this.emptyEquipPacket = emptyEquipPacket; + this.fullEquipPacket = fullEquipPacket; + + this.sleep = sleep; + updateNpcYaw(serverPlayer.xRot()); + updateNpcInvisible(); } @Override - public void sync(Player to) { - + public void add(NetWorkUser from, NetWorkUser to) { + to.sendPacket(this.npcInitPackets, false); + to.sendPacket(this.fullEquipPacket, false); + if (serverPlayer.y() > 0) { + BukkitCraftEngine.instance().scheduler().asyncLater(() -> { + to.sendPacket(this.npcTPPacket, false); + to.sendPacket(this.npcRotHeadPacket, false); + if (npcDataPacket != null) to.sendPacket(this.npcDataPacket, false); + }, 50, TimeUnit.MILLISECONDS); + } else { + to.sendPacket(this.npcRotHeadPacket, false); + if (npcDataPacket != null) to.sendPacket(this.npcDataPacket, false); + } } @Override - public void remove() { + public boolean handleEntitiesRemove(NetWorkUser user, IntList entityIds) { + entityIds.add(npcID); + return true; + } + @Override + public void dismount(Player from) { + super.dismount(from); + this.task.task.cancel(); + org.bukkit.entity.Player player = (org.bukkit.entity.Player) from.platformPlayer(); + Object blockPos = LocationUtils.toBlockPos(bedLoc.getBlockX(), bedLoc.getBlockY(), bedLoc.getBlockZ()); + Object blockState = BlockStateUtils.blockDataToBlockState(bedLoc.getBlock().getBlockData()); + try { + Object blockUpdatePacket = Reflections.constructor$ClientboundBlockUpdatePacket.newInstance(blockPos, blockState); + if (player.getPotionEffect(PotionEffectType.INVISIBILITY) == null) Reflections.method$Entity$setInvisible.invoke(serverPlayer.serverPlayer(), false); + from.sendPacket(this.npcRemovePacket, true); + from.sendPacket(blockUpdatePacket, true); + + Object npcData = Reflections.method$Entity$getEntityData.invoke(npc); + Object playerData = Reflections.method$Entity$getEntityData.invoke(serverPlayer.serverPlayer()); + Reflections.method$SynchedEntityData$set.invoke(playerData, PlayerData.LShoulder.entityDataAccessor(), Reflections.method$SynchedEntityData$get.invoke(npcData, PlayerData.LShoulder.entityDataAccessor())); + Reflections.method$SynchedEntityData$set.invoke(playerData, PlayerData.RShoulder.entityDataAccessor(), Reflections.method$SynchedEntityData$get.invoke(npcData, PlayerData.RShoulder.entityDataAccessor())); + if (player.getPotionEffect(PotionEffectType.INVISIBILITY) == null) Reflections.method$Entity$setInvisible.invoke(serverPlayer.serverPlayer(), false); + + player.updateInventory(); + + if (sleep) { + player.setSleepingIgnored(false); + } + + Object fullSlots = Reflections.method$ClientboundSetEquipmentPacket$getSlots.invoke(this.fullEquipPacket); + Object recoverEquip = Reflections.constructor$ClientboundSetEquipmentPacket.newInstance(player.getEntityId(), fullSlots); + + for (org.bukkit.entity.Player p : PlayerUtils.getTrackedBy(player)) { + BukkitServerPlayer sp = BukkitAdaptors.adapt(p); + sp.entityPacketHandlers().remove(playerID()); + sp.sendPacket(this.npcRemovePacket, false); + sp.sendPacket(blockUpdatePacket, false); + sp.sendPacket(recoverEquip, false); + } + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to dismount LayEntity", e); + } + } + + public void equipmentChange(Map equipmentChanges, int previousSlot) { + org.bukkit.entity.Player player = serverPlayer.platformPlayer(); + List> changedSlots = new ArrayList<>(); + + for (Map.Entry entry : equipmentChanges.entrySet()) { + Object slotId = EntityUtils.fromEquipmentSlot(entry.getKey()); + Object itemStack = FastNMS.INSTANCE.method$CraftItemStack$asNMSCopy(entry.getValue()); + changedSlots.add(Pair.of(slotId, itemStack)); + } + this.equipments.putAll(equipmentChanges); + + List> allSlots = new ArrayList<>(); + equipments.forEach((slot, item) -> + allSlots.add(Pair.of(EntityUtils.fromEquipmentSlot(slot), FastNMS.INSTANCE.method$CraftItemStack$asNMSCopy(item)))); + try { + this.updateEquipPacket = Reflections.constructor$ClientboundSetEquipmentPacket.newInstance(npcID, changedSlots); + this.fullEquipPacket = Reflections.constructor$ClientboundSetEquipmentPacket.newInstance(npcID, allSlots); + if (previousSlot != -1) { + player.updateInventory(); + } + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to handle equipmentChange", e); + } + + serverPlayer.sendPacket(this.emptyEquipPacket, false); + serverPlayer.sendPacket(this.updateEquipPacket, false); + + for (org.bukkit.entity.Player p : PlayerUtils.getTrackedBy(player)) { + BukkitNetworkManager.instance().getOnlineUser(p).sendPacket(this.updateEquipPacket, false); + } + } + + @Override + public void handleSetEquipment(NetWorkUser user, NMSPacketEvent event, Object packet) { + if (this.emptyEquipPacket == packet) return; + event.setCancelled(true); + } + + @Override + public void handleContainerSetSlot(NetWorkUser user, NMSPacketEvent event, Object packet) { + try { + int slot = (int) Reflections.method$ClientboundContainerSetSlotPacket$getSlot.invoke(packet); + org.bukkit.entity.Player player = (org.bukkit.entity.Player) user.platformPlayer(); + + int convertSlot; + boolean isPlayerInv; + + if (!VersionHelper.isOrAbove1_21_1()) { + Object openInventory = player.getClass().getMethod("getOpenInventory").invoke(player); + + Method convertSlotMethod = openInventory.getClass().getMethod("convertSlot", int.class); + convertSlot = (int) convertSlotMethod.invoke(openInventory, slot); + + Method getTopInventoryMethod = openInventory.getClass().getMethod("getTopInventory"); + Object topInventory = getTopInventoryMethod.invoke(openInventory); + Method getTypeMethod = topInventory.getClass().getMethod("getType"); + Object type = getTypeMethod.invoke(topInventory); + isPlayerInv = type == InventoryType.CRAFTING; + } else { + convertSlot = player.getOpenInventory().convertSlot(slot); + isPlayerInv = player.getOpenInventory().getTopInventory().getType() == InventoryType.CRAFTING; + } + + if (!(convertSlot == player.getInventory().getHeldItemSlot() || (isPlayerInv && (slot == 45 || (slot >= 5 && slot <= 8))))) return; + int containerId = (int) Reflections.method$ClientboundContainerSetSlotPacket$getContainerId.invoke(packet); + int stateId = (int) Reflections.method$ClientboundContainerSetSlotPacket$getStateId.invoke(packet); + Object replacePacket = Reflections.constructor$ClientboundContainerSetSlotPacket.newInstance(containerId, stateId, slot, Reflections.instance$ItemStack$Air); + event.replacePacket(replacePacket); + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to handleContainerSetSlotPacket", e); + } + } + + @Override + public void event(Player player, Object event) { + if (event instanceof PlayerAnimationEvent e) { + try { + Object animatePacket; + if (e.getAnimationType() == PlayerAnimationType.ARM_SWING) { + animatePacket = Reflections.constructor$ClientboundAnimatePacket.newInstance(npc, 0); + } else { + animatePacket = Reflections.constructor$ClientboundAnimatePacket.newInstance(npc, 3); + } + serverPlayer.sendPacket(animatePacket, true); + for (org.bukkit.entity.Player other : PlayerUtils.getTrackedBy(serverPlayer.platformPlayer())) { + BukkitNetworkManager.instance().getOnlineUser(other).sendPacket(animatePacket, true); + } + } catch (Exception exception) { + CraftEngine.instance().logger().warn("Failed to handle PlayerAnimationEvent", exception); + } + } else if (event instanceof PlayerItemHeldEvent e) { + ItemStack item = e.getPlayer().getInventory().getItem(e.getNewSlot()); + if (item == null) item = ItemUtils.AIR; + + equipmentChange(Map.of(EquipmentSlot.MAIN_HAND, item), e.getPreviousSlot()); + } } @Override public Key type() { return Key.of("craftengine", "lay"); } + + private class PlayerMonitorTask implements Runnable { + + private final SchedulerTask task; + private float lastYaw; + + private PlayerMonitorTask() { + org.bukkit.entity.Player p = serverPlayer.platformPlayer(); + BukkitCraftEngine plugin = BukkitCraftEngine.instance(); + if (VersionHelper.isFolia()) { + this.task = new FoliaTask(p.getScheduler().runAtFixedRate(plugin.javaPlugin(), (t) -> this.run(), () -> {}, 1, 1)); + } else { + this.task = plugin.scheduler().sync().runRepeating(this, 1, 1); + } + } + + @Override + public void run() { + org.bukkit.entity.Player player = serverPlayer.platformPlayer(); + if (player == null || !player.isValid()) { + this.task.cancel(); + return; + } + + // Invisible + updateNpcInvisible(); + try { + if (!player.isInvisible()) Reflections.method$Entity$setInvisible.invoke(serverPlayer.serverPlayer(), true); + } catch (Exception exception) { + CraftEngine.instance().logger().warn("Failed to set shared flag", exception); + } + + // Sync Rotation + float playerYaw = player.getYaw(); + if (lastYaw != playerYaw) { + updateNpcYaw(playerYaw); + serverPlayer.sendPacket(npcRotHeadPacket, false); + for (org.bukkit.entity.Player other : PlayerUtils.getTrackedBy(player)) { + BukkitNetworkManager.instance().getOnlineUser(other).sendPacket(npcRotHeadPacket, true); + } + this.lastYaw = playerYaw; + } + + // Sync Equipment + Map newEquipments = new HashMap<>(); + for (EquipmentSlot slot : EquipmentSlot.values()) { + if (!slot.isHand() && !slot.isPlayerArmor()) continue; + ItemStack newItem = player.getEquipment().getItem(EntityUtils.toBukkitEquipmentSlot(slot)); + try { + ItemStack item = equipments.get(slot); + boolean isChange = !newItem.equals(item); + if (isChange) { + newEquipments.put(slot, newItem); + } + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to monitor equipments change", e); + } + } + + if (!newEquipments.isEmpty()) { + equipmentChange(newEquipments, -1); + return; + } + serverPlayer.sendPacket(emptyEquipPacket, false); + } + } + + private void updateNpcYaw(float playerYaw) { + byte packYaw = getRot(playerYaw); + try { + this.npcRotHeadPacket = Reflections.constructor$ClientboundRotateHeadPacket.newInstance(npc, packYaw); + } catch (Exception exception) { + CraftEngine.instance().logger().warn("Failed to sync NPC yaw", exception); + } + } + + private byte getRot(float playerYaw) { + float npcYaw = Direction.getYaw(npcDir); + float centerYaw = normalizeYaw(npcYaw); + float playerYawNorm = normalizeYaw(playerYaw); + + float deltaYaw = normalizeYaw(playerYawNorm - centerYaw); + boolean isBehind = Math.abs(deltaYaw) > 90; + + float mappedYaw; + if (isBehind) { + float rel = Math.abs(deltaYaw) - 180; + mappedYaw = rel * (deltaYaw > 0 ? -1 : 1); + } else { + mappedYaw = deltaYaw; + } + + float limitedYaw = Math.max(-45, Math.min(45, mappedYaw)); + float finalYaw = limitedYaw + npcYawOffset; + return MCUtils.packDegrees(finalYaw); + } + + private float normalizeYaw(float yaw) { + yaw %= 360.0f; + if (yaw < -180.0f) yaw += 360.0f; + if (yaw >= 180.0f) yaw -= 360.0f; + return yaw; + } + + private void updateNpcInvisible() { + try { + org.bukkit.entity.Player player = serverPlayer.platformPlayer(); + if (player.getPotionEffect(PotionEffectType.INVISIBILITY) == null && npcDataPacket != null) { + npcDataPacket = null; + Reflections.method$Entity$setInvisible.invoke(npc, false); + Object npcData = Reflections.method$Entity$getEntityData.invoke(npc); + Object dataItem = Reflections.method$SynchedEntityData$getItem.invoke(npcData, PlayerData.SharedFlags.entityDataAccessor()); + Object dataValue = Reflections.method$SynchedEntityData$DataItem$value.invoke(dataItem); + Object packet = FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(npcID, List.of(dataValue)); + serverPlayer.sendPacket(packet, false); + for (org.bukkit.entity.Player p : PlayerUtils.getTrackedBy(player)) { + BukkitNetworkManager.instance().getOnlineUser(p).sendPacket(packet, false); + } + } else if (player.getPotionEffect(PotionEffectType.INVISIBILITY) != null && npcDataPacket == null) { + Reflections.method$Entity$setInvisible.invoke(npc, true); + Object npcData = Reflections.method$Entity$getEntityData.invoke(npc); + Object dataItem = Reflections.method$SynchedEntityData$getItem.invoke(npcData, PlayerData.SharedFlags.entityDataAccessor()); + Object dataValue = Reflections.method$SynchedEntityData$DataItem$value.invoke(dataItem); + npcDataPacket = FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(npcID, List.of(dataValue)); + serverPlayer.sendPacket(npcDataPacket, false); + for (org.bukkit.entity.Player p : PlayerUtils.getTrackedBy(player)) { + BukkitNetworkManager.instance().getOnlineUser(p).sendPacket(npcDataPacket, false); + } + } + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to updateNpcInvisible", e); + } + } } public static class Factory implements SeatFactory { @Override - public Seat create(List arguments) { - return null; + public Seat create(List args) { + Vector3f offset = MiscUtils.getAsVector3f(args.get(0), "seats"); + Direction facing = args.size() > 1 ? parseFacing(args.get(1)) : Direction.SOUTH; + boolean sleep = args.size() > 2 && Boolean.parseBoolean(args.get(2)); + boolean phantom = args.size() > 4 && Boolean.parseBoolean(args.get(3)); + + if (facing == Direction.NORTH || facing == Direction.SOUTH) { + float temp = offset.x; + offset.x = offset.z; + offset.z = temp; + } + return new LaySeat(offset, facing, sleep, phantom); + } + + private Direction parseFacing(String facing) { + return switch (facing.toLowerCase()) { + case "back" -> Direction.NORTH; + case "left" -> Direction.WEST; + case "right" -> Direction.EAST; + default -> Direction.SOUTH; + }; } } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/SitSeat.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/SitSeat.java index 0e1ef4994..7593d64be 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/SitSeat.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/SitSeat.java @@ -1,29 +1,20 @@ package net.momirealms.craftengine.bukkit.entity.furniture.seat; -import net.momirealms.craftengine.bukkit.api.BukkitAdaptors; -import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager; -import net.momirealms.craftengine.bukkit.entity.furniture.LoadedFurniture; +import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurniture; import net.momirealms.craftengine.bukkit.util.EntityUtils; -import net.momirealms.craftengine.bukkit.util.LegacyAttributeUtils; -import net.momirealms.craftengine.core.entity.Entity; import net.momirealms.craftengine.core.entity.furniture.AbstractSeat; import net.momirealms.craftengine.core.entity.furniture.Furniture; import net.momirealms.craftengine.core.entity.furniture.Seat; import net.momirealms.craftengine.core.entity.furniture.SeatFactory; import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.entity.seat.SeatEntity; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; -import net.momirealms.craftengine.core.util.VersionHelper; import org.bukkit.Location; -import org.bukkit.attribute.Attribute; -import org.bukkit.entity.ArmorStand; -import org.bukkit.entity.EntityType; -import org.bukkit.entity.ItemDisplay; -import org.bukkit.persistence.PersistentDataType; +import org.bukkit.entity.Entity; import org.joml.Vector3f; import java.util.List; -import java.util.Objects; public class SitSeat extends AbstractSeat { public static final SeatFactory FACTORY = new Factory(); @@ -35,62 +26,21 @@ public class SitSeat extends AbstractSeat { } @Override - public Entity spawn(Player player, Furniture furniture) { + public SeatEntity spawn(Player player, Furniture furniture) { return spawn((org.bukkit.entity.Player) player.platformPlayer(), furniture); } - public Entity spawn(org.bukkit.entity.Player player, Furniture furniture) { - Location location = ((LoadedFurniture)furniture).calculateSeatLocation(this); - org.bukkit.entity.Entity seatEntity = this.limitPlayerRotation ? - EntityUtils.spawnEntity(player.getWorld(), VersionHelper.isOrAbove1_20_2() ? location.subtract(0,0.9875,0) : location.subtract(0,0.990625,0), EntityType.ARMOR_STAND, entity -> { - ArmorStand armorStand = (ArmorStand) entity; - if (VersionHelper.isOrAbove1_21_3()) { - Objects.requireNonNull(armorStand.getAttribute(Attribute.MAX_HEALTH)).setBaseValue(0.01); - } else { - LegacyAttributeUtils.setMaxHealth(armorStand); - } - armorStand.setSmall(true); - armorStand.setInvisible(true); - armorStand.setSilent(true); - armorStand.setInvulnerable(true); - armorStand.setArms(false); - armorStand.setCanTick(false); - armorStand.setAI(false); - armorStand.setGravity(false); - armorStand.setPersistent(false); - armorStand.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_SEAT_BASE_ENTITY_KEY, PersistentDataType.INTEGER, furniture.baseEntityId()); - armorStand.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_SEAT_VECTOR_3F_KEY, PersistentDataType.STRING, this.offset().x + ", " + this.offset().y + ", " + this.offset().z); - }) : - EntityUtils.spawnEntity(player.getWorld(), VersionHelper.isOrAbove1_20_2() ? location : location.subtract(0,0.25,0), EntityType.ITEM_DISPLAY, entity -> { - ItemDisplay itemDisplay = (ItemDisplay) entity; - itemDisplay.setPersistent(false); - itemDisplay.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_SEAT_BASE_ENTITY_KEY, PersistentDataType.INTEGER, furniture.baseEntityId()); - itemDisplay.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_SEAT_VECTOR_3F_KEY, PersistentDataType.STRING, this.offset().x + ", " + this.offset().y + ", " + this.offset().z); - }); + public SeatEntity spawn(org.bukkit.entity.Player player, Furniture furniture) { + Location location = ((BukkitFurniture)furniture).calculateSeatLocation(this); + org.bukkit.entity.Entity seatEntity = EntityUtils.spawnSeatEntity(furniture, this, player.getWorld(), location, this.limitPlayerRotation, null); seatEntity.addPassenger(player); - return new SitEntity(seatEntity); + return new SitEntity(seatEntity, furniture, offset(), player.getEntityId()); } private static class SitEntity extends BukkitSeatEntity { - public SitEntity(org.bukkit.entity.Entity entity) { - super(entity); - } - - @Override - public void sync(Player to) {} - - @Override - public void remove() { - org.bukkit.entity.Entity entity = this.literalObject(); - if (entity == null) return; - for (org.bukkit.entity.Entity passenger : entity.getPassengers()) { - entity.removePassenger(passenger); - if (passenger instanceof org.bukkit.entity.Player p) { - BukkitAdaptors.adapt(p).setSeat(null); - } - } - entity.remove(); + public SitEntity(Entity entity, Furniture furniture, Vector3f vector3f, int playerID) { + super(entity, furniture, vector3f, playerID); } @Override @@ -103,8 +53,8 @@ public class SitSeat extends AbstractSeat { @Override public Seat create(List args) { - if (args.size() == 1) return new SitSeat(MiscUtils.getAsVector3f(args.get(0), "seats"), 0, false); - return new SitSeat(MiscUtils.getAsVector3f(args.get(0), "seats"), Float.parseFloat(args.get(1)), true); + if (args.size() == 1) return new SitSeat(MiscUtils.getAsVector3f(args.getFirst(), "seats"), 0, false); + return new SitSeat(MiscUtils.getAsVector3f(args.getFirst(), "seats"), Float.parseFloat(args.get(1)), true); } } } \ No newline at end of file diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugGetBlockInternalIdCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugGetBlockInternalIdCommand.java index 7e1a63eea..ac1e9ae7b 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugGetBlockInternalIdCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugGetBlockInternalIdCommand.java @@ -8,8 +8,6 @@ import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager; import net.momirealms.craftengine.core.plugin.command.FlagKeys; import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.bukkit.entity.Pose; import org.checkerframework.checker.nullness.qual.NonNull; import org.incendo.cloud.Command; import org.incendo.cloud.context.CommandContext; @@ -37,13 +35,10 @@ public class DebugGetBlockInternalIdCommand extends BukkitCommandFeature { - Player player = (Player) context.sender(); - if (player.hasFixedPose()) player.setPose(Pose.STANDING, false); - else player.setPose(Pose.SWIMMING, true); String data = context.get("id"); - //ImmutableBlockState state = BlockStateParser.deserialize(data); - //if (state == null) return; - //context.sender().sendMessage(BlockStateUtils.getBlockOwnerIdFromState(state.customBlockState().handle()).toString()); + ImmutableBlockState state = BlockStateParser.deserialize(data); + if (state == null) return; + context.sender().sendMessage(BlockStateUtils.getBlockOwnerIdFromState(state.customBlockState().handle()).toString()); }); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java index 98a7df2ac..eb6de3ea8 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java @@ -17,6 +17,7 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.LibraryRefl import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.bukkit.util.KeyUtils; +import net.momirealms.craftengine.bukkit.util.Reflections; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.context.CooldownData; import net.momirealms.craftengine.core.plugin.network.*; @@ -162,6 +163,8 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes registerNMSPacketConsumer(PacketConsumers.MOVE_POS_ENTITY, NetworkReflections.clazz$ClientboundMoveEntityPacket$Pos); registerNMSPacketConsumer(PacketConsumers.ROTATE_HEAD, NetworkReflections.clazz$ClientboundRotateHeadPacket); registerNMSPacketConsumer(PacketConsumers.SET_ENTITY_MOTION, NetworkReflections.clazz$ClientboundSetEntityMotionPacket); + registerNMSPacketConsumer(PacketConsumers.SET_EQUIPMENT_NMS, Reflections.clazz$ClientboundSetEquipmentPacket); + registerNMSPacketConsumer(PacketConsumers.SET_CONTAINER_SLOT, Reflections.clazz$ClientboundContainerSetSlotPacket); registerS2CByteBufPacketConsumer(PacketConsumers.LEVEL_CHUNK_WITH_LIGHT, this.packetIds.clientboundLevelChunkWithLightPacket()); registerS2CByteBufPacketConsumer(PacketConsumers.SECTION_BLOCK_UPDATE, this.packetIds.clientboundSectionBlocksUpdatePacket()); registerS2CByteBufPacketConsumer(PacketConsumers.BLOCK_UPDATE, this.packetIds.clientboundBlockUpdatePacket()); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java index cf6ef4dbc..9cefa7cfc 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java @@ -9,7 +9,6 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.IntList; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TranslationArgument; -import net.momirealms.craftengine.bukkit.api.BukkitAdaptors; import net.momirealms.craftengine.bukkit.api.CraftEngineFurniture; import net.momirealms.craftengine.bukkit.api.event.FurnitureAttemptBreakEvent; import net.momirealms.craftengine.bukkit.api.event.FurnitureBreakEvent; @@ -180,6 +179,17 @@ public class PacketConsumers { user.entityPacketHandlers().put(id, FurnitureCollisionPacketHandler.INSTANCE); } }; + ADD_ENTITY_HANDLERS[MEntityTypes.instance$EntityType$PLAYER$registryId] = (user, event) -> { + FriendlyByteBuf buf = event.getBuffer(); + buf.readVarInt(); + UUID uuid = buf.readUUID(); + BukkitServerPlayer player = (BukkitServerPlayer) BukkitCraftEngine.instance().networkManager().getOnlineUser(uuid); + if (player == null) return; + SeatEntity seat = player.seat(); + if (seat == null) return; + user.entityPacketHandlers().put(seat.playerID(), seat); + seat.add(player, user); + }; } private static BukkitNetworkManager.Handlers simpleAddEntityHandler(EntityPacketHandler handler) { @@ -1497,7 +1507,7 @@ public class PacketConsumers { for (int i = 0, size = intList.size(); i < size; i++) { int entityId = intList.getInt(i); EntityPacketHandler handler = user.entityPacketHandlers().remove(entityId); - if (handler != null && handler.handleEntitiesRemove(intList)) { + if (handler != null && handler.handleEntitiesRemove(user, intList)) { isChange = true; } } @@ -1646,6 +1656,9 @@ public class PacketConsumers { } else { furniture.findFirstAvailableSeat(entityId).ifPresent(seat -> { if (furniture.tryOccupySeat(seat)) { + SeatEntity currentSeat = serverPlayer.seat(); + if (currentSeat != null) currentSeat.dismount(serverPlayer); + furniture.spawnSeatEntityForPlayer(serverPlayer, seat); } }); @@ -1911,6 +1924,10 @@ public class PacketConsumers { @SuppressWarnings("unchecked") public static final BiConsumer SET_ENTITY_DATA = (user, event) -> { try { + SeatEntity seat = ((BukkitServerPlayer)user).seat(); + if (seat != null) { + seat.handleSetEntityData(user, event); + } FriendlyByteBuf buf = event.getBuffer(); int id = buf.readVarInt(); EntityPacketHandler handler = user.entityPacketHandlers().get(id); @@ -2412,4 +2429,24 @@ public class PacketConsumers { CraftEngine.instance().logger().warn("Failed to handle ClientboundSetEntityMotionPacket", e); } }; + public static final TriConsumer SET_EQUIPMENT_NMS = (user, event, packet) -> { + try { + int entityId = (int) Reflections.method$ClientboundSetEquipmentPacket$getEntity.invoke(packet); + EntityPacketHandler handler = user.entityPacketHandlers().get(entityId); + if (handler != null) { + handler.handleSetEquipment(user, event, packet); + } + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to handle ClientboundSetEquipmentPacket", e); + } + }; + + public static final TriConsumer SET_CONTAINER_SLOT = (user, event, packet) -> { + try { + SeatEntity seat = ((BukkitServerPlayer) user).seat(); + if (seat != null) seat.handleContainerSetSlot(user, event, packet); + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to handle ClientboundSetEquipmentPacket", e); + } + }; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/FurniturePacketHandler.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/FurniturePacketHandler.java index 0a60cfc3a..8e26a3d3a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/FurniturePacketHandler.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/FurniturePacketHandler.java @@ -15,7 +15,7 @@ public class FurniturePacketHandler implements EntityPacketHandler { } @Override - public boolean handleEntitiesRemove(IntList entityIds) { + public boolean handleEntitiesRemove(NetWorkUser user, IntList entityIds) { entityIds.addAll(this.fakeEntities); return true; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EntityUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EntityUtils.java index 30fd6fa4b..f002866ef 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EntityUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EntityUtils.java @@ -1,18 +1,26 @@ package net.momirealms.craftengine.bukkit.util; +import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; +import net.momirealms.craftengine.core.entity.EquipmentSlot; +import net.momirealms.craftengine.core.entity.furniture.Furniture; +import net.momirealms.craftengine.core.entity.furniture.Seat; import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.world.BlockPos; import org.bukkit.Location; import org.bukkit.World; -import org.bukkit.entity.Entity; -import org.bukkit.entity.EntityType; -import org.bukkit.entity.Player; +import org.bukkit.attribute.Attribute; +import org.bukkit.entity.*; import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.persistence.PersistentDataType; +import java.util.Objects; import java.util.function.Consumer; +import static net.momirealms.craftengine.core.entity.EquipmentSlot.BODY; +import static net.momirealms.craftengine.core.entity.EquipmentSlot.MAIN_HAND; + public class EntityUtils { private EntityUtils() {} @@ -34,4 +42,94 @@ public class EntityUtils { return LegacyEntityUtils.spawnEntity(world, loc, type, function); } } + + public static Entity spawnSeatEntity(Furniture furniture, Seat seat, World world, Location loc, boolean limitPlayerRotation, Consumer function) { + EntityType type; + if (limitPlayerRotation) { + type = EntityType.ARMOR_STAND; + loc = VersionHelper.isOrAbove1_20_2() ? loc.subtract(0,0.9875,0) : loc.subtract(0,0.990625,0); + if (function == null) { + function = entity -> { + ArmorStand armorStand = (ArmorStand) entity; + if (VersionHelper.isOrAbove1_21_3()) { + Objects.requireNonNull(armorStand.getAttribute(Attribute.MAX_HEALTH)).setBaseValue(0.01); + } else { + LegacyAttributeUtils.setMaxHealth(armorStand); + } + armorStand.setSmall(true); + armorStand.setInvisible(true); + armorStand.setSilent(true); + armorStand.setInvulnerable(true); + armorStand.setArms(false); + armorStand.setCanTick(false); + armorStand.setAI(false); + armorStand.setGravity(false); + armorStand.setPersistent(false); + armorStand.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_SEAT_BASE_ENTITY_KEY, PersistentDataType.INTEGER, furniture.baseEntityId()); + //armorStand.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_SEAT_VECTOR_3F_KEY, PersistentDataType.STRING, seat.offset().x + ", " + seat.offset().y + ", " + seat.offset().z); + }; + } + } else { + type = EntityType.ITEM_DISPLAY; + loc = VersionHelper.isOrAbove1_20_2() ? loc : loc.subtract(0,0.25,0); + if (function == null) { + function = entity -> { + ItemDisplay itemDisplay = (ItemDisplay) entity; + itemDisplay.setPersistent(false); + itemDisplay.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_SEAT_BASE_ENTITY_KEY, PersistentDataType.INTEGER, furniture.baseEntityId()); + //itemDisplay.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_SEAT_VECTOR_3F_KEY, PersistentDataType.STRING, seat.offset().x + ", " + seat.offset().y + ", " + seat.offset().z); + }; + } + } + return spawnEntity(world, loc, type, function); + } + + public static org.bukkit.inventory.EquipmentSlot toBukkitEquipmentSlot(EquipmentSlot slot) { + return switch (slot) { + case MAIN_HAND -> org.bukkit.inventory.EquipmentSlot.HAND; + case OFF_HAND -> org.bukkit.inventory.EquipmentSlot.OFF_HAND; + case HEAD -> org.bukkit.inventory.EquipmentSlot.HEAD; + case CHEST -> org.bukkit.inventory.EquipmentSlot.CHEST; + case LEGS -> org.bukkit.inventory.EquipmentSlot.LEGS; + case FEET -> org.bukkit.inventory.EquipmentSlot.FEET; + default -> org.bukkit.inventory.EquipmentSlot.BODY; + }; + } + + public static EquipmentSlot toCEEquipmentSlot(org.bukkit.inventory.EquipmentSlot slot) { + return switch (slot) { + case HAND -> MAIN_HAND; + case OFF_HAND -> EquipmentSlot.OFF_HAND; + case HEAD -> EquipmentSlot.HEAD; + case CHEST -> EquipmentSlot.CHEST; + case LEGS -> EquipmentSlot.LEGS; + case FEET -> EquipmentSlot.FEET; + case BODY -> EquipmentSlot.BODY; + default -> BODY; + }; + } + + public static Object fromEquipmentSlot(org.bukkit.inventory.EquipmentSlot slot) { + return switch (slot) { + case HAND -> Reflections.instance$EquipmentSlot$MAINHAND; + case OFF_HAND -> Reflections.instance$EquipmentSlot$OFFHAND; + case HEAD -> Reflections.instance$EquipmentSlot$HEAD; + case CHEST -> Reflections.instance$EquipmentSlot$CHEST; + case LEGS -> Reflections.instance$EquipmentSlot$LEGS; + case FEET -> Reflections.instance$EquipmentSlot$FEET; + default -> new Object(); + }; + }; + + public static Object fromEquipmentSlot(EquipmentSlot slot) { + return switch (slot) { + case MAIN_HAND -> Reflections.instance$EquipmentSlot$MAINHAND; + case OFF_HAND -> Reflections.instance$EquipmentSlot$OFFHAND; + case HEAD -> Reflections.instance$EquipmentSlot$HEAD; + case CHEST -> Reflections.instance$EquipmentSlot$CHEST; + case LEGS -> Reflections.instance$EquipmentSlot$LEGS; + case FEET -> Reflections.instance$EquipmentSlot$FEET; + default -> new Object(); + }; + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ItemUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ItemUtils.java index 1841169e8..46f8594e7 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ItemUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ItemUtils.java @@ -7,6 +7,8 @@ import org.jetbrains.annotations.Contract; public class ItemUtils { + public static final ItemStack AIR = new ItemStack(Material.AIR); + private ItemUtils() {} @Contract("null -> true") diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/PlayerUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/PlayerUtils.java index 9a0c33285..189d9bc16 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/PlayerUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/PlayerUtils.java @@ -8,6 +8,7 @@ import net.momirealms.craftengine.bukkit.plugin.network.BukkitNetworkManager; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections; import net.momirealms.craftengine.core.util.RandomUtils; +import net.momirealms.craftengine.core.util.VersionHelper; import org.bukkit.Location; import org.bukkit.entity.Item; import org.bukkit.entity.Player; @@ -20,6 +21,7 @@ import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.List; +import java.util.Set; import static java.util.Objects.requireNonNull; @@ -174,4 +176,8 @@ public final class PlayerUtils { BukkitCraftEngine.instance().logger().warn("Failed to send totem animation"); } } + + public static Set getTrackedBy(Player player) { + return VersionHelper.isOrAbove1_20_2() ? player.getTrackedBy() : player.getTrackedPlayers(); + } } diff --git a/common-files/src/main/resources/resources/default/configuration/furniture.yml b/common-files/src/main/resources/resources/default/configuration/furniture.yml index 5e3018767..35b568623 100644 --- a/common-files/src/main/resources/resources/default/configuration/furniture.yml +++ b/common-files/src/main/resources/resources/default/configuration/furniture.yml @@ -36,7 +36,7 @@ items: interactive: true interaction-entity: true seats: - - 0,0,-0.1 0 crawl + - 0,0,-0.1 0 - 1,0,-0.1 0 loot: template: default:loot_table/furniture diff --git a/common-files/src/main/resources/translations/en.yml b/common-files/src/main/resources/translations/en.yml index 57db5e240..2e5b798c2 100644 --- a/common-files/src/main/resources/translations/en.yml +++ b/common-files/src/main/resources/translations/en.yml @@ -151,6 +151,7 @@ warning.config.furniture.element.missing_item: "Issue found in file Issue found in file - The furniture '' is using an unknown setting type ''." warning.config.furniture.hitbox.invalid_type: "Issue found in file - The furniture '' is using an invalid hitbox type ''." warning.config.furniture.hitbox.custom.invalid_entity: "Issue found in file - The furniture '' is using a custom hitbox with invalid entity type ''." +warning.config.furniture.seat.invalid_type: "Issue found in file - The furniture '' is using an invalid seat type ''." warning.config.item.duplicate: "Issue found in file - Duplicated item ''. Please check if there is the same configuration in other files." warning.config.item.settings.unknown: "Issue found in file - The item '' is using an unknown setting type ''." warning.config.item.settings.invulnerable.invalid_damage_source: "Issue found in file - The item '' is using an unknown damage source ''. Allowed sources: []." diff --git a/common-files/src/main/resources/translations/zh_cn.yml b/common-files/src/main/resources/translations/zh_cn.yml index 62be30309..cb80b377d 100644 --- a/common-files/src/main/resources/translations/zh_cn.yml +++ b/common-files/src/main/resources/translations/zh_cn.yml @@ -151,6 +151,7 @@ warning.config.furniture.element.missing_item: "在文件 发现 warning.config.furniture.settings.unknown: "在文件 发现问题 - 家具 '' 使用了未知的设置类型 ''" warning.config.furniture.hitbox.invalid_type: "在文件 发现问题 - 家具 '' 使用了无效的碰撞箱类型 ''" warning.config.furniture.hitbox.custom.invalid_entity: "在文件 发现问题 - 家具 '' 的自定义碰撞箱使用了无效的实体类型 ''" +warning.config.furniture.seat.invalid_type: "在文件 发现问题 - 家具 '' 使用了无效的座椅类型 ''" warning.config.item.duplicate: "在文件 发现问题 - 重复的物品 '' 请检查其他文件中是否存在相同配置" warning.config.item.settings.unknown: "在文件 发现问题 - 物品 '' 使用了未知的设置类型 ''" warning.config.item.settings.invulnerable.invalid_damage_source: "在文件 发现问题 - 物品 '' 物品使用了未知的伤害来源类型 '' 允许的来源: []" diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/EquipmentSlot.java b/core/src/main/java/net/momirealms/craftengine/core/entity/EquipmentSlot.java index 55c343103..f3f9c279a 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/EquipmentSlot.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/EquipmentSlot.java @@ -8,5 +8,13 @@ public enum EquipmentSlot { BODY, MAIN_HAND, OFF_HAND, - SADDLE + SADDLE; + + public boolean isHand() { + return this == MAIN_HAND || this == OFF_HAND; + } + + public boolean isPlayerArmor() { + return this == HEAD || this == CHEST || this == LEGS || this == FEET; + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Seat.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Seat.java index d0f3eadd3..deb77a736 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Seat.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Seat.java @@ -1,12 +1,12 @@ package net.momirealms.craftengine.core.entity.furniture; -import net.momirealms.craftengine.core.entity.Entity; import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.entity.seat.SeatEntity; import org.joml.Vector3f; public interface Seat { - Entity spawn(Player player, Furniture furniture); + SeatEntity spawn(Player player, Furniture furniture); Vector3f offset(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/SeatType.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/SeatType.java index a049595d7..1a96248d3 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/SeatType.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/SeatType.java @@ -1,7 +1,7 @@ package net.momirealms.craftengine.core.entity.furniture; import com.google.common.collect.Lists; -import net.momirealms.craftengine.core.plugin.locale.LocalizedException; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.registry.Registries; @@ -35,7 +35,7 @@ public class SeatType { } factory = BuiltInRegistries.SEAT_FACTORY.getValue(type); if (factory == null) { - throw new LocalizedException("warning.config.furniture.hitbox.invalid_type"); + throw new LocalizedResourceConfigException("warning.config.furniture.seat.invalid_type", type.toString()); } return factory.create(split); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/seat/SeatEntity.java b/core/src/main/java/net/momirealms/craftengine/core/entity/seat/SeatEntity.java index ccb0f1694..dc621df4b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/seat/SeatEntity.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/seat/SeatEntity.java @@ -1,14 +1,24 @@ package net.momirealms.craftengine.core.entity.seat; -import net.momirealms.craftengine.core.entity.AbstractEntity; -import net.momirealms.craftengine.core.entity.Entity; +import net.momirealms.craftengine.core.entity.furniture.Furniture; import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler; +import net.momirealms.craftengine.core.plugin.network.NetWorkUser; +import org.joml.Vector3f; -public abstract class SeatEntity extends AbstractEntity { +public interface SeatEntity extends EntityPacketHandler { - public abstract void sync(Player to); + void add(NetWorkUser from, NetWorkUser to); - public abstract void dismount(Player from); + void dismount(Player player); - public abstract void remove(); + void event(Player player, Object event); + + void destroy(); + + Furniture furniture(); + + Vector3f vector3f(); + + int playerID(); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/EntityPacketHandler.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/EntityPacketHandler.java index 15e0aa919..163556067 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/EntityPacketHandler.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/EntityPacketHandler.java @@ -4,7 +4,7 @@ import it.unimi.dsi.fastutil.ints.IntList; public interface EntityPacketHandler { - default boolean handleEntitiesRemove(IntList entityIds) { + default boolean handleEntitiesRemove(NetWorkUser user, IntList entityIds) { return false; } @@ -19,4 +19,10 @@ public interface EntityPacketHandler { default void handleMove(NetWorkUser user, NMSPacketEvent event, Object packet) { } + + default void handleSetEquipment(NetWorkUser user, NMSPacketEvent event, Object packet) { + } + + default void handleContainerSetSlot(NetWorkUser user, NMSPacketEvent event, Object packet) { + } } From 32d9a942e4fde75b160f7b260d345b19e4dba980 Mon Sep 17 00:00:00 2001 From: iqtester Date: Mon, 2 Jun 2025 15:11:34 -0400 Subject: [PATCH 03/10] Move to CoreReflections --- .../bukkit/entity/data/PlayerData.java | 8 +- .../entity/furniture/seat/CrawlSeat.java | 23 +- .../bukkit/entity/furniture/seat/LaySeat.java | 146 ++++---- .../plugin/network/BukkitNetworkManager.java | 3 +- .../plugin/network/PacketConsumers.java | 2 +- .../reflection/minecraft/CoreReflections.java | 329 ++++++++++++++++++ .../craftengine/bukkit/util/EntityUtils.java | 24 +- 7 files changed, 436 insertions(+), 99 deletions(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/PlayerData.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/PlayerData.java index 23f822ae3..b1e6e1b4a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/PlayerData.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/PlayerData.java @@ -1,13 +1,13 @@ package net.momirealms.craftengine.bukkit.entity.data; -import net.momirealms.craftengine.bukkit.util.Reflections; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; public class PlayerData extends LivingEntityData { - public static final PlayerData Pose = new PlayerData<>(6, EntityDataValue.Serializers$POSE, Reflections.instance$Pose$STANDING); + public static final PlayerData Pose = new PlayerData<>(6, EntityDataValue.Serializers$POSE, CoreReflections.instance$Pose$STANDING); public static final PlayerData Skin = new PlayerData<>(17, EntityDataValue.Serializers$BYTE, (byte) 0); public static final PlayerData Hand = new PlayerData<>(18, EntityDataValue.Serializers$BYTE, (byte) 0); - public static final PlayerData LShoulder = new PlayerData<>(19, EntityDataValue.Serializers$COMPOUND_TAG, Reflections.instance$CompoundTag$Empty); - public static final PlayerData RShoulder = new PlayerData<>(20, EntityDataValue.Serializers$COMPOUND_TAG, Reflections.instance$CompoundTag$Empty); + public static final PlayerData LShoulder = new PlayerData<>(19, EntityDataValue.Serializers$COMPOUND_TAG, CoreReflections.instance$CompoundTag$Empty); + public static final PlayerData RShoulder = new PlayerData<>(20, EntityDataValue.Serializers$COMPOUND_TAG, CoreReflections.instance$CompoundTag$Empty); public PlayerData(int id, Object serializer, T defaultValue) { super(id, serializer, defaultValue); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/CrawlSeat.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/CrawlSeat.java index 28a44898d..588e1128a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/CrawlSeat.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/CrawlSeat.java @@ -7,10 +7,13 @@ import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurniture; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.plugin.network.BukkitNetworkManager; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MAttributeHolders; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.bukkit.util.EntityUtils; import net.momirealms.craftengine.bukkit.util.PlayerUtils; -import net.momirealms.craftengine.bukkit.util.Reflections; import net.momirealms.craftengine.core.entity.furniture.AbstractSeat; import net.momirealms.craftengine.core.entity.furniture.Furniture; import net.momirealms.craftengine.core.entity.furniture.Seat; @@ -62,18 +65,18 @@ public class CrawlSeat extends AbstractSeat { seatEntity.addPassenger(player); // Fix Rider Pose - int visualId = Reflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(); + int visualId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(); List packets = new ArrayList<>(); packets.add(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(visualId, UUID.randomUUID(), location.getX(), location.getY(), location.getZ(), location.getPitch(), location.getYaw(), - Reflections.instance$EntityType$SHULKER, 0, Reflections.instance$Vec3$Zero, 0)); + MEntityTypes.instance$EntityType$SHULKER, 0, CoreReflections.instance$Vec3$Zero, 0)); packets.add(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(visualId, List.copyOf(visualData))); try { if (VersionHelper.isOrAbove1_20_5()) { - Object attributeInstance = Reflections.constructor$AttributeInstance.newInstance(Reflections.instance$Holder$Attribute$scale, (Consumer) (o) -> {}); - Reflections.method$AttributeInstance$setBaseValue.invoke(attributeInstance, 0.6); + Object attributeInstance = CoreReflections.constructor$AttributeInstance.newInstance(MAttributeHolders.SCALE, (Consumer) (o) -> {}); + CoreReflections.method$AttributeInstance$setBaseValue.invoke(attributeInstance, 0.6); packets.add( - Reflections.constructor$ClientboundUpdateAttributesPacket0 + NetworkReflections.constructor$ClientboundUpdateAttributesPacket0 .newInstance(visualId, Collections.singletonList(attributeInstance)) ); packets.add(FastNMS.INSTANCE.constructor$ClientboundSetPassengersPacket(seatEntity.getEntityId(), visualId)); @@ -90,9 +93,9 @@ public class CrawlSeat extends AbstractSeat { player.setPose(Pose.SWIMMING, true); Object syncPosePacket = null; try { - Object playerData = Reflections.method$Entity$getEntityData.invoke(serverPlayer.serverPlayer()); - Object dataItem = Reflections.method$SynchedEntityData$getItem.invoke(playerData, PlayerData.Pose.entityDataAccessor()); - Object dataValue = Reflections.method$SynchedEntityData$DataItem$value.invoke(dataItem); + Object playerData = CoreReflections.method$Entity$getEntityData.invoke(serverPlayer.serverPlayer()); + Object dataItem = CoreReflections.method$SynchedEntityData$getItem.invoke(playerData, PlayerData.Pose.entityDataAccessor()); + Object dataValue = CoreReflections.method$SynchedEntityData$DataItem$value.invoke(dataItem); syncPosePacket = FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(serverPlayer.entityID(), List.of(dataValue)); } catch (Exception e) { CraftEngine.instance().logger().warn("Failed to construct sync pose packet", e); @@ -130,7 +133,7 @@ public class CrawlSeat extends AbstractSeat { super.dismount(player); ((org.bukkit.entity.Player) player.platformPlayer()).setPose(Pose.STANDING, false); try { - Object packet = Reflections.constructor$ClientboundRemoveEntitiesPacket.newInstance((Object) new int[]{visualId}); + Object packet = CoreReflections.constructor$ClientboundRemoveEntitiesPacket.newInstance((Object) new int[]{visualId}); player.sendPacket(packet, false); } catch (Exception e) { BukkitCraftEngine.instance().logger().warn("Failed to remove crawl entity", e); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/LaySeat.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/LaySeat.java index fee10a713..413609a0e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/LaySeat.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/LaySeat.java @@ -10,6 +10,10 @@ import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurniture; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.plugin.network.BukkitNetworkManager; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MItems; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections; import net.momirealms.craftengine.bukkit.plugin.scheduler.impl.FoliaTask; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.bukkit.util.*; @@ -74,41 +78,41 @@ public class LaySeat extends AbstractSeat { try { List packets = new ArrayList<>(); // NPC - Object server = Reflections.method$MinecraftServer$getServer.invoke(null); + Object server = CoreReflections.method$MinecraftServer$getServer.invoke(null); Object level = FastNMS.INSTANCE.field$CraftWorld$ServerLevel(player.getWorld()); UUID uuid = UUID.randomUUID(); - Object npcProfile = Reflections.constructor$GameProfile.newInstance(uuid, player.getName()); - Object playerProfile = Reflections.method$ServerPlayer$getGameProfile.invoke(serverPlayer); + Object npcProfile = CoreReflections.constructor$GameProfile.newInstance(uuid, player.getName()); + Object playerProfile = CoreReflections.method$ServerPlayer$getGameProfile.invoke(serverPlayer); - Multimap properties = (Multimap) Reflections.method$GameProfile$getProperties.invoke(npcProfile); - properties.putAll((Multimap) Reflections.method$GameProfile$getProperties.invoke(playerProfile)); + Multimap properties = (Multimap) CoreReflections.method$GameProfile$getProperties.invoke(npcProfile); + properties.putAll((Multimap) CoreReflections.method$GameProfile$getProperties.invoke(playerProfile)); Object npc; if (VersionHelper.isOrAbove1_20_2()) { - Object clientInfo = Reflections.method$ServerPlayer$clientInformation.invoke(serverPlayer); - npc = Reflections.constructor$ServerPlayer.newInstance(server, level, npcProfile, clientInfo); + Object clientInfo = CoreReflections.method$ServerPlayer$clientInformation.invoke(serverPlayer); + npc = CoreReflections.constructor$ServerPlayer.newInstance(server, level, npcProfile, clientInfo); } else { - npc = Reflections.constructor$ServerPlayer.newInstance(server, level, npcProfile); + npc = CoreReflections.constructor$ServerPlayer.newInstance(server, level, npcProfile); } int npcId = FastNMS.INSTANCE.method$Entity$getId(npc); - Reflections.method$Entity$absSnapTo.invoke(npc, loc.getX(), loc.getY(), loc.getZ(), 0, 0); + CoreReflections.method$Entity$absSnapTo.invoke(npc, loc.getX(), loc.getY(), loc.getZ(), 0, 0); Object npcSpawnPacket = FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(npcId, uuid, loc.getX(), loc.getY(), loc.getZ(), 0, 0, - Reflections.instance$EntityType$PLAYER, 0, Reflections.instance$Vec3$Zero, 0); + MEntityTypes.instance$EntityType$PLAYER, 0, CoreReflections.instance$Vec3$Zero, 0); // Info - EnumSet enumSet = EnumSet.noneOf((Class) Reflections.clazz$ClientboundPlayerInfoUpdatePacket$Action); - enumSet.add(Reflections.instance$ClientboundPlayerInfoUpdatePacket$Action$ADD_PLAYER); + EnumSet enumSet = EnumSet.noneOf((Class) CoreReflections.clazz$ClientboundPlayerInfoUpdatePacket$Action); + enumSet.add(CoreReflections.instance$ClientboundPlayerInfoUpdatePacket$Action$ADD_PLAYER); Object entry; if (VersionHelper.isOrAbove1_21_4()) { - entry = Reflections.constructor$ClientBoundPlayerInfoUpdatePacket$Entry.newInstance( - uuid, npcProfile, false, 0, Reflections.instance$GameType$SURVIVAL, null, true, 0, null); + entry = CoreReflections.constructor$ClientBoundPlayerInfoUpdatePacket$Entry.newInstance( + uuid, npcProfile, false, 0, CoreReflections.instance$GameType$SURVIVAL, null, true, 0, null); } else if (VersionHelper.isOrAbove1_21_3()) { - entry = Reflections.constructor$ClientBoundPlayerInfoUpdatePacket$Entry.newInstance( - uuid, npcProfile, false, 0, Reflections.instance$GameType$SURVIVAL, null, 0, null); + entry = CoreReflections.constructor$ClientBoundPlayerInfoUpdatePacket$Entry.newInstance( + uuid, npcProfile, false, 0, CoreReflections.instance$GameType$SURVIVAL, null, 0, null); } else { - entry = Reflections.constructor$ClientBoundPlayerInfoUpdatePacket$Entry.newInstance( - uuid, npcProfile, false, 0, Reflections.instance$GameType$SURVIVAL, null, null); + entry = CoreReflections.constructor$ClientBoundPlayerInfoUpdatePacket$Entry.newInstance( + uuid, npcProfile, false, 0, CoreReflections.instance$GameType$SURVIVAL, null, null); } Object npcInfoPacket = FastNMS.INSTANCE.constructor$ClientboundPlayerInfoUpdatePacket(enumSet, Collections.singletonList(entry)); @@ -120,50 +124,50 @@ public class LaySeat extends AbstractSeat { bedLoc.setY(bedLoc.getWorld().getMinHeight()); Object bedPos = LocationUtils.toBlockPos(new BlockPos(bedLoc.getBlockX(), bedLoc.getBlockY(), bedLoc.getBlockZ())); Object blockState = BlockStateUtils.blockDataToBlockState(bedData); - Object bedPacket = Reflections.constructor$ClientboundBlockUpdatePacket.newInstance(bedPos, blockState); + Object bedPacket = CoreReflections.constructor$ClientboundBlockUpdatePacket.newInstance(bedPos, blockState); // Data - Object npcData = Reflections.method$Entity$getEntityData.invoke(npc); - Object playerData = Reflections.method$Entity$getEntityData.invoke(serverPlayer); - Reflections.method$Entity$setInvisible.invoke(serverPlayer, true); - Reflections.method$SynchedEntityData$set.invoke(npcData, PlayerData.Pose.entityDataAccessor(), Reflections.instance$Pose$SLEEPING); - Reflections.method$SynchedEntityData$set.invoke(npcData, LivingEntityData.SleepingPos.entityDataAccessor(), Optional.of(bedPos)); - Reflections.method$SynchedEntityData$set.invoke(npcData, PlayerData.Skin.entityDataAccessor(), Reflections.method$SynchedEntityData$get.invoke(playerData, PlayerData.Skin.entityDataAccessor())); - Reflections.method$SynchedEntityData$set.invoke(npcData, PlayerData.Hand.entityDataAccessor(), Reflections.method$SynchedEntityData$get.invoke(playerData, PlayerData.Hand.entityDataAccessor())); - Reflections.method$SynchedEntityData$set.invoke(npcData, PlayerData.LShoulder.entityDataAccessor(), Reflections.method$SynchedEntityData$get.invoke(playerData, PlayerData.LShoulder.entityDataAccessor())); - Reflections.method$SynchedEntityData$set.invoke(npcData, PlayerData.RShoulder.entityDataAccessor(), Reflections.method$SynchedEntityData$get.invoke(playerData, PlayerData.RShoulder.entityDataAccessor())); - Reflections.method$SynchedEntityData$set.invoke(playerData, PlayerData.LShoulder.entityDataAccessor(), Reflections.instance$CompoundTag$Empty); - Reflections.method$SynchedEntityData$set.invoke(playerData, PlayerData.RShoulder.entityDataAccessor(), Reflections.instance$CompoundTag$Empty); + Object npcData = CoreReflections.method$Entity$getEntityData.invoke(npc); + Object playerData = CoreReflections.method$Entity$getEntityData.invoke(serverPlayer); + CoreReflections.method$Entity$setInvisible.invoke(serverPlayer, true); + CoreReflections.method$SynchedEntityData$set.invoke(npcData, PlayerData.Pose.entityDataAccessor(), CoreReflections.instance$Pose$SLEEPING); + CoreReflections.method$SynchedEntityData$set.invoke(npcData, LivingEntityData.SleepingPos.entityDataAccessor(), Optional.of(bedPos)); + CoreReflections.method$SynchedEntityData$set.invoke(npcData, PlayerData.Skin.entityDataAccessor(), CoreReflections.method$SynchedEntityData$get.invoke(playerData, PlayerData.Skin.entityDataAccessor())); + CoreReflections.method$SynchedEntityData$set.invoke(npcData, PlayerData.Hand.entityDataAccessor(), CoreReflections.method$SynchedEntityData$get.invoke(playerData, PlayerData.Hand.entityDataAccessor())); + CoreReflections.method$SynchedEntityData$set.invoke(npcData, PlayerData.LShoulder.entityDataAccessor(), CoreReflections.method$SynchedEntityData$get.invoke(playerData, PlayerData.LShoulder.entityDataAccessor())); + CoreReflections.method$SynchedEntityData$set.invoke(npcData, PlayerData.RShoulder.entityDataAccessor(), CoreReflections.method$SynchedEntityData$get.invoke(playerData, PlayerData.RShoulder.entityDataAccessor())); + CoreReflections.method$SynchedEntityData$set.invoke(playerData, PlayerData.LShoulder.entityDataAccessor(), CoreReflections.instance$CompoundTag$Empty); + CoreReflections.method$SynchedEntityData$set.invoke(playerData, PlayerData.RShoulder.entityDataAccessor(), CoreReflections.instance$CompoundTag$Empty); // SetData - Reflections.method$Entity$setInvisible.invoke(serverPlayer, true); + CoreReflections.method$Entity$setInvisible.invoke(serverPlayer, true); Object npcDataPacket = FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket( - npcId, (List) Reflections.method$SynchedEntityData$packDirty.invoke(npcData) + npcId, (List) CoreReflections.method$SynchedEntityData$packDirty.invoke(npcData) ); // Remove - Object npcRemovePacket = Reflections.constructor$ClientboundRemoveEntitiesPacket.newInstance((Object) new int[]{npcId}); + Object npcRemovePacket = CoreReflections.constructor$ClientboundRemoveEntitiesPacket.newInstance((Object) new int[]{npcId}); // TP Object npcTeleportPacket; if (VersionHelper.isOrAbove1_21_3()) { - Object positionMoveRotation = Reflections.method$PositionMoveRotation$of.invoke(null, npc); - npcTeleportPacket = Reflections.constructor$ClientboundTeleportEntityPacket.newInstance(npcId, positionMoveRotation, Set.of(), false); + Object positionMoveRotation = CoreReflections.method$PositionMoveRotation$of.invoke(null, npc); + npcTeleportPacket = CoreReflections.constructor$ClientboundTeleportEntityPacket.newInstance(npcId, positionMoveRotation, Set.of(), false); } else { - npcTeleportPacket = Reflections.constructor$ClientboundTeleportEntityPacket.newInstance(npc); + npcTeleportPacket = CoreReflections.constructor$ClientboundTeleportEntityPacket.newInstance(npc); } // Equipment List> emptySlots = new ArrayList<>(); - emptySlots.add(Pair.of(Reflections.instance$EquipmentSlot$MAINHAND, Reflections.instance$ItemStack$Air)); - emptySlots.add(Pair.of(Reflections.instance$EquipmentSlot$OFFHAND, Reflections.instance$ItemStack$Air)); - emptySlots.add(Pair.of(Reflections.instance$EquipmentSlot$HEAD, Reflections.instance$ItemStack$Air)); - emptySlots.add(Pair.of(Reflections.instance$EquipmentSlot$CHEST, Reflections.instance$ItemStack$Air)); - emptySlots.add(Pair.of(Reflections.instance$EquipmentSlot$LEGS, Reflections.instance$ItemStack$Air)); - emptySlots.add(Pair.of(Reflections.instance$EquipmentSlot$FEET, Reflections.instance$ItemStack$Air)); - Object emptyEquipPacket = Reflections.constructor$ClientboundSetEquipmentPacket.newInstance(player.getEntityId(), emptySlots); + emptySlots.add(Pair.of(CoreReflections.instance$EquipmentSlot$MAINHAND, MItems.Air$ItemStack)); + emptySlots.add(Pair.of(CoreReflections.instance$EquipmentSlot$OFFHAND, MItems.Air$ItemStack)); + emptySlots.add(Pair.of(CoreReflections.instance$EquipmentSlot$HEAD, MItems.Air$ItemStack)); + emptySlots.add(Pair.of(CoreReflections.instance$EquipmentSlot$CHEST, MItems.Air$ItemStack)); + emptySlots.add(Pair.of(CoreReflections.instance$EquipmentSlot$LEGS, MItems.Air$ItemStack)); + emptySlots.add(Pair.of(CoreReflections.instance$EquipmentSlot$FEET, MItems.Air$ItemStack)); + Object emptyEquipPacket = NetworkReflections.constructor$ClientboundSetEquipmentPacket.newInstance(player.getEntityId(), emptySlots); Map equipments = new HashMap<>(); EntityEquipment equipment = player.getEquipment(); @@ -178,7 +182,7 @@ public class LaySeat extends AbstractSeat { } List> npcSlots = new ArrayList<>(); equipments.forEach((slot, item) -> npcSlots.add(Pair.of(EntityUtils.fromEquipmentSlot(slot), FastNMS.INSTANCE.method$CraftItemStack$asNMSCopy(item)))); - Object fullEquipPacket = Reflections.constructor$ClientboundSetEquipmentPacket.newInstance(npcId, npcSlots); + Object fullEquipPacket = NetworkReflections.constructor$ClientboundSetEquipmentPacket.newInstance(npcId, npcSlots); packets.add(npcInfoPacket); @@ -341,16 +345,16 @@ public class LaySeat extends AbstractSeat { Object blockPos = LocationUtils.toBlockPos(bedLoc.getBlockX(), bedLoc.getBlockY(), bedLoc.getBlockZ()); Object blockState = BlockStateUtils.blockDataToBlockState(bedLoc.getBlock().getBlockData()); try { - Object blockUpdatePacket = Reflections.constructor$ClientboundBlockUpdatePacket.newInstance(blockPos, blockState); - if (player.getPotionEffect(PotionEffectType.INVISIBILITY) == null) Reflections.method$Entity$setInvisible.invoke(serverPlayer.serverPlayer(), false); + Object blockUpdatePacket = CoreReflections.constructor$ClientboundBlockUpdatePacket.newInstance(blockPos, blockState); + if (player.getPotionEffect(PotionEffectType.INVISIBILITY) == null) CoreReflections.method$Entity$setInvisible.invoke(serverPlayer.serverPlayer(), false); from.sendPacket(this.npcRemovePacket, true); from.sendPacket(blockUpdatePacket, true); - Object npcData = Reflections.method$Entity$getEntityData.invoke(npc); - Object playerData = Reflections.method$Entity$getEntityData.invoke(serverPlayer.serverPlayer()); - Reflections.method$SynchedEntityData$set.invoke(playerData, PlayerData.LShoulder.entityDataAccessor(), Reflections.method$SynchedEntityData$get.invoke(npcData, PlayerData.LShoulder.entityDataAccessor())); - Reflections.method$SynchedEntityData$set.invoke(playerData, PlayerData.RShoulder.entityDataAccessor(), Reflections.method$SynchedEntityData$get.invoke(npcData, PlayerData.RShoulder.entityDataAccessor())); - if (player.getPotionEffect(PotionEffectType.INVISIBILITY) == null) Reflections.method$Entity$setInvisible.invoke(serverPlayer.serverPlayer(), false); + Object npcData = CoreReflections.method$Entity$getEntityData.invoke(npc); + Object playerData = CoreReflections.method$Entity$getEntityData.invoke(serverPlayer.serverPlayer()); + CoreReflections.method$SynchedEntityData$set.invoke(playerData, PlayerData.LShoulder.entityDataAccessor(), CoreReflections.method$SynchedEntityData$get.invoke(npcData, PlayerData.LShoulder.entityDataAccessor())); + CoreReflections.method$SynchedEntityData$set.invoke(playerData, PlayerData.RShoulder.entityDataAccessor(), CoreReflections.method$SynchedEntityData$get.invoke(npcData, PlayerData.RShoulder.entityDataAccessor())); + if (player.getPotionEffect(PotionEffectType.INVISIBILITY) == null) CoreReflections.method$Entity$setInvisible.invoke(serverPlayer.serverPlayer(), false); player.updateInventory(); @@ -358,8 +362,8 @@ public class LaySeat extends AbstractSeat { player.setSleepingIgnored(false); } - Object fullSlots = Reflections.method$ClientboundSetEquipmentPacket$getSlots.invoke(this.fullEquipPacket); - Object recoverEquip = Reflections.constructor$ClientboundSetEquipmentPacket.newInstance(player.getEntityId(), fullSlots); + Object fullSlots = CoreReflections.method$ClientboundSetEquipmentPacket$getSlots.invoke(this.fullEquipPacket); + Object recoverEquip = NetworkReflections.constructor$ClientboundSetEquipmentPacket.newInstance(player.getEntityId(), fullSlots); for (org.bukkit.entity.Player p : PlayerUtils.getTrackedBy(player)) { BukkitServerPlayer sp = BukkitAdaptors.adapt(p); @@ -388,8 +392,8 @@ public class LaySeat extends AbstractSeat { equipments.forEach((slot, item) -> allSlots.add(Pair.of(EntityUtils.fromEquipmentSlot(slot), FastNMS.INSTANCE.method$CraftItemStack$asNMSCopy(item)))); try { - this.updateEquipPacket = Reflections.constructor$ClientboundSetEquipmentPacket.newInstance(npcID, changedSlots); - this.fullEquipPacket = Reflections.constructor$ClientboundSetEquipmentPacket.newInstance(npcID, allSlots); + this.updateEquipPacket = NetworkReflections.constructor$ClientboundSetEquipmentPacket.newInstance(npcID, changedSlots); + this.fullEquipPacket = NetworkReflections.constructor$ClientboundSetEquipmentPacket.newInstance(npcID, allSlots); if (previousSlot != -1) { player.updateInventory(); } @@ -414,7 +418,7 @@ public class LaySeat extends AbstractSeat { @Override public void handleContainerSetSlot(NetWorkUser user, NMSPacketEvent event, Object packet) { try { - int slot = (int) Reflections.method$ClientboundContainerSetSlotPacket$getSlot.invoke(packet); + int slot = (int) CoreReflections.method$ClientboundContainerSetSlotPacket$getSlot.invoke(packet); org.bukkit.entity.Player player = (org.bukkit.entity.Player) user.platformPlayer(); int convertSlot; @@ -437,9 +441,9 @@ public class LaySeat extends AbstractSeat { } if (!(convertSlot == player.getInventory().getHeldItemSlot() || (isPlayerInv && (slot == 45 || (slot >= 5 && slot <= 8))))) return; - int containerId = (int) Reflections.method$ClientboundContainerSetSlotPacket$getContainerId.invoke(packet); - int stateId = (int) Reflections.method$ClientboundContainerSetSlotPacket$getStateId.invoke(packet); - Object replacePacket = Reflections.constructor$ClientboundContainerSetSlotPacket.newInstance(containerId, stateId, slot, Reflections.instance$ItemStack$Air); + int containerId = (int) CoreReflections.method$ClientboundContainerSetSlotPacket$getContainerId.invoke(packet); + int stateId = (int) CoreReflections.method$ClientboundContainerSetSlotPacket$getStateId.invoke(packet); + Object replacePacket = CoreReflections.constructor$ClientboundContainerSetSlotPacket.newInstance(containerId, stateId, slot, MItems.Air$ItemStack); event.replacePacket(replacePacket); } catch (Exception e) { CraftEngine.instance().logger().warn("Failed to handleContainerSetSlotPacket", e); @@ -452,9 +456,9 @@ public class LaySeat extends AbstractSeat { try { Object animatePacket; if (e.getAnimationType() == PlayerAnimationType.ARM_SWING) { - animatePacket = Reflections.constructor$ClientboundAnimatePacket.newInstance(npc, 0); + animatePacket = CoreReflections.constructor$ClientboundAnimatePacket.newInstance(npc, 0); } else { - animatePacket = Reflections.constructor$ClientboundAnimatePacket.newInstance(npc, 3); + animatePacket = CoreReflections.constructor$ClientboundAnimatePacket.newInstance(npc, 3); } serverPlayer.sendPacket(animatePacket, true); for (org.bukkit.entity.Player other : PlayerUtils.getTrackedBy(serverPlayer.platformPlayer())) { @@ -502,7 +506,7 @@ public class LaySeat extends AbstractSeat { // Invisible updateNpcInvisible(); try { - if (!player.isInvisible()) Reflections.method$Entity$setInvisible.invoke(serverPlayer.serverPlayer(), true); + if (!player.isInvisible()) CoreReflections.method$Entity$setInvisible.invoke(serverPlayer.serverPlayer(), true); } catch (Exception exception) { CraftEngine.instance().logger().warn("Failed to set shared flag", exception); } @@ -545,7 +549,7 @@ public class LaySeat extends AbstractSeat { private void updateNpcYaw(float playerYaw) { byte packYaw = getRot(playerYaw); try { - this.npcRotHeadPacket = Reflections.constructor$ClientboundRotateHeadPacket.newInstance(npc, packYaw); + this.npcRotHeadPacket = CoreReflections.constructor$ClientboundRotateHeadPacket.newInstance(npc, packYaw); } catch (Exception exception) { CraftEngine.instance().logger().warn("Failed to sync NPC yaw", exception); } @@ -584,20 +588,20 @@ public class LaySeat extends AbstractSeat { org.bukkit.entity.Player player = serverPlayer.platformPlayer(); if (player.getPotionEffect(PotionEffectType.INVISIBILITY) == null && npcDataPacket != null) { npcDataPacket = null; - Reflections.method$Entity$setInvisible.invoke(npc, false); - Object npcData = Reflections.method$Entity$getEntityData.invoke(npc); - Object dataItem = Reflections.method$SynchedEntityData$getItem.invoke(npcData, PlayerData.SharedFlags.entityDataAccessor()); - Object dataValue = Reflections.method$SynchedEntityData$DataItem$value.invoke(dataItem); + CoreReflections.method$Entity$setInvisible.invoke(npc, false); + Object npcData = CoreReflections.method$Entity$getEntityData.invoke(npc); + Object dataItem = CoreReflections.method$SynchedEntityData$getItem.invoke(npcData, PlayerData.SharedFlags.entityDataAccessor()); + Object dataValue = CoreReflections.method$SynchedEntityData$DataItem$value.invoke(dataItem); Object packet = FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(npcID, List.of(dataValue)); serverPlayer.sendPacket(packet, false); for (org.bukkit.entity.Player p : PlayerUtils.getTrackedBy(player)) { BukkitNetworkManager.instance().getOnlineUser(p).sendPacket(packet, false); } } else if (player.getPotionEffect(PotionEffectType.INVISIBILITY) != null && npcDataPacket == null) { - Reflections.method$Entity$setInvisible.invoke(npc, true); - Object npcData = Reflections.method$Entity$getEntityData.invoke(npc); - Object dataItem = Reflections.method$SynchedEntityData$getItem.invoke(npcData, PlayerData.SharedFlags.entityDataAccessor()); - Object dataValue = Reflections.method$SynchedEntityData$DataItem$value.invoke(dataItem); + CoreReflections.method$Entity$setInvisible.invoke(npc, true); + Object npcData = CoreReflections.method$Entity$getEntityData.invoke(npc); + Object dataItem = CoreReflections.method$SynchedEntityData$getItem.invoke(npcData, PlayerData.SharedFlags.entityDataAccessor()); + Object dataValue = CoreReflections.method$SynchedEntityData$DataItem$value.invoke(dataItem); npcDataPacket = FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(npcID, List.of(dataValue)); serverPlayer.sendPacket(npcDataPacket, false); for (org.bukkit.entity.Player p : PlayerUtils.getTrackedBy(player)) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java index eb6de3ea8..7a93e53b2 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java @@ -17,7 +17,6 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.LibraryRefl import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.bukkit.util.KeyUtils; -import net.momirealms.craftengine.bukkit.util.Reflections; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.context.CooldownData; import net.momirealms.craftengine.core.plugin.network.*; @@ -165,6 +164,8 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes registerNMSPacketConsumer(PacketConsumers.SET_ENTITY_MOTION, NetworkReflections.clazz$ClientboundSetEntityMotionPacket); registerNMSPacketConsumer(PacketConsumers.SET_EQUIPMENT_NMS, Reflections.clazz$ClientboundSetEquipmentPacket); registerNMSPacketConsumer(PacketConsumers.SET_CONTAINER_SLOT, Reflections.clazz$ClientboundContainerSetSlotPacket); + registerNMSPacketConsumer(PacketConsumers.SET_EQUIPMENT_NMS, NetworkReflections.clazz$ClientboundSetEquipmentPacket); + registerNMSPacketConsumer(PacketConsumers.SET_CONTAINER_SLOT, NetworkReflections.clazz$ClientboundContainerSetSlotPacket); registerS2CByteBufPacketConsumer(PacketConsumers.LEVEL_CHUNK_WITH_LIGHT, this.packetIds.clientboundLevelChunkWithLightPacket()); registerS2CByteBufPacketConsumer(PacketConsumers.SECTION_BLOCK_UPDATE, this.packetIds.clientboundSectionBlocksUpdatePacket()); registerS2CByteBufPacketConsumer(PacketConsumers.BLOCK_UPDATE, this.packetIds.clientboundBlockUpdatePacket()); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java index 9cefa7cfc..e4948f54d 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java @@ -2431,7 +2431,7 @@ public class PacketConsumers { }; public static final TriConsumer SET_EQUIPMENT_NMS = (user, event, packet) -> { try { - int entityId = (int) Reflections.method$ClientboundSetEquipmentPacket$getEntity.invoke(packet); + int entityId = (int) CoreReflections.method$ClientboundSetEquipmentPacket$getEntity.invoke(packet); EntityPacketHandler handler = user.entityPacketHandlers().get(entityId); if (handler != null) { handler.handleSetEquipment(user, event, packet); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java index ed5a93376..bd7d1eab1 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.bukkit.plugin.reflection.minecraft; +import com.google.common.collect.ForwardingMultimap; import com.google.common.collect.ImmutableList; import com.google.gson.JsonElement; import com.mojang.serialization.Codec; @@ -3347,4 +3348,332 @@ public final class CoreReflections { throw new ReflectionInitException("Failed to init ItemStack$CODEC", e); } } + + public static final Method method$Entity$setInvisible = requireNonNull( + ReflectionUtils.getMethod( + clazz$Entity, new String[]{"j", "setInvisible"}, boolean.class + ) + ); + + public static final Class clazz$GameProfile = requireNonNull( + ReflectionUtils.getClazz("com.mojang.authlib.GameProfile") + ); + + public static final Constructor constructor$GameProfile = requireNonNull( + ReflectionUtils.getConstructor( + clazz$GameProfile, 0 + ) + ); + + public static final Method method$GameProfile$getProperties = requireNonNull( + ReflectionUtils.getMethod( + clazz$GameProfile, ForwardingMultimap.class + ) + ); + + public static final Constructor constructor$ServerPlayer = requireNonNull( + ReflectionUtils.getConstructor( + clazz$ServerPlayer, 0 + ) + ); + + public static final Method method$ServerPlayer$getGameProfile = requireNonNull( + ReflectionUtils.getMethod( + clazz$ServerPlayer, clazz$GameProfile, 0 + ) + ); + + // 1.20.2 + + public static final Method method$ServerPlayer$clientInformation = VersionHelper.isOrAbove1_20_2() ? requireNonNull(ReflectionUtils.getMethod( + clazz$ServerPlayer, clazz$ClientInformation, 0 + )) : null; + + public static final Class clazz$CompoundTag = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "nbt.NBTTagCompound", + "nbt.CompoundTag" + ) + ); + + public static final Object instance$CompoundTag$Empty; + + static { + try { + instance$CompoundTag$Empty = CoreReflections.clazz$CompoundTag.getConstructor().newInstance(); + } catch (ReflectiveOperationException e) { + throw new RuntimeException("Failed to instantiate empty CompoundTag", e); + } + } + + public static final Method method$Entity$getEntityData = requireNonNull( + ReflectionUtils.getMethod( + clazz$Entity, clazz$SynchedEntityData, 0 + ) + ); + + public static final Method method$SynchedEntityData$set = requireNonNull( + ReflectionUtils.getMethod( + clazz$SynchedEntityData, void.class, clazz$EntityDataAccessor, Object.class + ) + ); + + public static final Method method$SynchedEntityData$isDirty = requireNonNull( + ReflectionUtils.getMethod( + clazz$SynchedEntityData, boolean.class + ) + ); + + public static final Method method$SynchedEntityData$packDirty = requireNonNull( + ReflectionUtils.getMethod( + clazz$SynchedEntityData, List.class, new String[]{"b", "packDirty"} + ) + ); + + public static final Class clazz$SynchedEntityData$DataItem = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "network.syncher.DataWatcher$Item", + "network.syncher.SynchedEntityData$DataItem" + ) + ); + + public static final Method method$SynchedEntityData$getItem = requireNonNull( + ReflectionUtils.getDeclaredMethod( + clazz$SynchedEntityData, clazz$SynchedEntityData$DataItem, clazz$EntityDataAccessor) + ); + + public static final Method method$SynchedEntityData$DataItem$value = requireNonNull( + ReflectionUtils.getMethod( + clazz$SynchedEntityData$DataItem, clazz$SynchedEntityData$DataValue, 0 + ) + ); + + public static final Class clazz$ClientBoundPlayerInfoRemovePacket = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("network.protocol.game.ClientboundPlayerInfoRemovePacket") + ) + ); + + public static final Constructor constructor$ClientBoundPlayerInfoRemovePacket = requireNonNull( + ReflectionUtils.getConstructor(clazz$ClientBoundPlayerInfoRemovePacket, List.class) + ); + + public static final Class clazz$ClientBoundPlayerInfoUpdatePacket$Entry = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "network.protocol.game.ClientboundPlayerInfoUpdatePacket$b", + "network.protocol.game.ClientboundPlayerInfoUpdatePacket$Entry" + ) + ); + + public static final Class clazz$RemoteChatSession$Data = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "network.chat.RemoteChatSession$a", + "network.chat.RemoteChatSession$Data" + ) + ); + + public static final Constructor constructor$ClientBoundPlayerInfoUpdatePacket$Entry = requireNonNull( + VersionHelper.isOrAbove1_21_3() + ? VersionHelper.isOrAbove1_21_4() + ? ReflectionUtils.getConstructor(clazz$ClientBoundPlayerInfoUpdatePacket$Entry, UUID.class, clazz$GameProfile, boolean.class, int.class, clazz$GameType, clazz$Component, boolean.class, int.class, clazz$RemoteChatSession$Data) + : ReflectionUtils.getConstructor(clazz$ClientBoundPlayerInfoUpdatePacket$Entry, UUID.class, clazz$GameProfile, boolean.class, int.class, clazz$GameType, clazz$Component, int.class, clazz$RemoteChatSession$Data) + : ReflectionUtils.getConstructor(clazz$ClientBoundPlayerInfoUpdatePacket$Entry, UUID.class, clazz$GameProfile, boolean.class, int.class, clazz$GameType, clazz$Component, clazz$RemoteChatSession$Data) + ); + + public static final Method method$GameType$values = requireNonNull( + ReflectionUtils.getStaticMethod( + clazz$GameType, clazz$GameType.arrayType() + ) + ); + + public static final Object instance$GameType$SURVIVAL; + public static final Object instance$GameType$CREATIVE; + public static final Object instance$GameType$ADVENTURE; + public static final Object instance$GameType$SPECTATOR; + + static { + try { + Object[] values = (Object[]) method$GameType$values.invoke(null); + instance$GameType$SURVIVAL = values[0]; + instance$GameType$CREATIVE = values[1]; + instance$GameType$ADVENTURE = values[2]; + instance$GameType$SPECTATOR = values[3]; + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + // 1.21.3 + + public static final Class clazz$PositionMoveRotation = VersionHelper.isOrAbove1_21_3() ? ReflectionUtils.getClazz( + requireNonNull(BukkitReflectionUtils.assembleMCClass("world.entity.PositionMoveRotation"))) : null; + + public static final Method method$PositionMoveRotation$of = VersionHelper.isOrAbove1_21_3() ? requireNonNull( + ReflectionUtils.getStaticMethod(clazz$PositionMoveRotation, clazz$PositionMoveRotation, clazz$Entity)) : null; + + public static final Method method$Entity$absSnapTo = requireNonNull( + ReflectionUtils.getMethod( + clazz$Entity, void.class, double.class, double.class, double.class, float.class, float.class + ) + ); + + public static final Class clazz$ClientboundTeleportEntityPacket = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "network.protocol.game.PacketPlayOutEntityTeleport", + "network.protocol.game.ClientboundTeleportEntityPacket" + ) + ); + + public static final Constructor constructor$ClientboundTeleportEntityPacket = requireNonNull( + VersionHelper.isOrAbove1_21_3() + ? ReflectionUtils.getConstructor(clazz$ClientboundTeleportEntityPacket, int.class, clazz$PositionMoveRotation, Set.class, boolean.class) + : ReflectionUtils.getConstructor(clazz$ClientboundTeleportEntityPacket, clazz$Entity) + ); + + public static final Method method$Entity$setSharedFlag = requireNonNull( + ReflectionUtils.getDeclaredMethod( + clazz$Entity, void.class, int.class, boolean.class + ) + ); + + public static final Method method$ClientboundSetEquipmentPacket$getEntity = requireNonNull( + ReflectionUtils.getMethod( + NetworkReflections.clazz$ClientboundSetEquipmentPacket, int.class + ) + ); + + public static final Method method$ClientboundSetEquipmentPacket$getSlots = requireNonNull( + ReflectionUtils.getMethod( + NetworkReflections.clazz$ClientboundSetEquipmentPacket, List.class + ) + ); + + public static final Class clazz$ClientboundContainerSetSlotPacket = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "network.protocol.game.PacketPlayOutSetSlot", + "network.protocol.game.ClientboundContainerSetSlotPacket" + ) + ); + + public static final Constructor constructor$ClientboundContainerSetSlotPacket = requireNonNull( + ReflectionUtils.getConstructor( + clazz$ClientboundContainerSetSlotPacket, int.class, int.class, int.class, clazz$ItemStack + ) + ); + + public static final Method method$ClientboundContainerSetSlotPacket$getContainerId = requireNonNull( + ReflectionUtils.getMethod( + clazz$ClientboundContainerSetSlotPacket, int.class, new String[]{"a", "getContainerId"} + ) + ); + + public static final Method method$ClientboundContainerSetSlotPacket$getSlot = requireNonNull( + ReflectionUtils.getMethod( + clazz$ClientboundContainerSetSlotPacket, int.class, new String[]{"c", "d", "getSlot"} + ) + ); + + public static final Method method$ClientboundContainerSetSlotPacket$getStateId = requireNonNull( + ReflectionUtils.getMethod( + clazz$ClientboundContainerSetSlotPacket, int.class, new String[]{"e", "f", "getStateId"} + ) + ); + + public static final Class clazz$ClientboundAnimatePacket = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "network.protocol.game.PacketPlayOutAnimation", + "network.protocol.game.ClientboundAnimatePacket") + ); + + public static final Constructor constructor$ClientboundAnimatePacket = requireNonNull( + ReflectionUtils.getConstructor(clazz$ClientboundAnimatePacket, clazz$Entity, int.class) + ); + + public static final Class clazz$ClientboundRotateHeadPacket = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "network.protocol.game.PacketPlayOutEntityHeadRotation", + "network.protocol.game.ClientboundRotateHeadPacket" + ) + ); + + public static final Constructor constructor$ClientboundRotateHeadPacket = requireNonNull( + ReflectionUtils.getDeclaredConstructor( + clazz$ClientboundRotateHeadPacket, clazz$Entity, byte.class + ) + ); + + public static final Class clazz$ClientboundBlockUpdatePacket = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "network.protocol.game.PacketPlayOutBlockChange", + "network.protocol.game.ClientboundBlockUpdatePacket" + ) + ); + + public static final Constructor constructor$ClientboundBlockUpdatePacket = requireNonNull( + ReflectionUtils.getConstructor( + clazz$ClientboundBlockUpdatePacket, clazz$BlockPos, clazz$BlockState + ) + ); + + public static final Class clazz$ClientboundRemoveEntitiesPacket = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "network.protocol.game.PacketPlayOutEntityDestroy", + "network.protocol.game.ClientboundRemoveEntitiesPacket" + ) + ); + + public static final Constructor constructor$ClientboundRemoveEntitiesPacket = requireNonNull( + ReflectionUtils.getConstructor(clazz$ClientboundRemoveEntitiesPacket, int[].class) + ); + + public static final Class clazz$ClientboundPlayerInfoUpdatePacket = requireNonNull( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("network.protocol.game.ClientboundPlayerInfoUpdatePacket") + ) + ); + + public static final Field field$ClientboundPlayerInfoUpdatePacket$entries = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$ClientboundPlayerInfoUpdatePacket, List.class, 0 + ) + ); + + public static final Class clazz$ClientboundPlayerInfoUpdatePacket$Action = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "network.protocol.game.ClientboundPlayerInfoUpdatePacket$a", + "network.protocol.game.ClientboundPlayerInfoUpdatePacket$Action" + ) + ); + + public static final Method method$ClientboundPlayerInfoUpdatePacket$Action$values = requireNonNull( + ReflectionUtils.getStaticMethod( + clazz$ClientboundPlayerInfoUpdatePacket$Action, clazz$ClientboundPlayerInfoUpdatePacket$Action.arrayType() + ) + ); + + public static final Object instance$ClientboundPlayerInfoUpdatePacket$Action$ADD_PLAYER; + public static final Object instance$ClientboundPlayerInfoUpdatePacket$Action$INITIALIZE_CHAT; + public static final Object instance$ClientboundPlayerInfoUpdatePacket$Action$UPDATE_GAME_MODE; + public static final Object instance$ClientboundPlayerInfoUpdatePacket$Action$UPDATE_LISTED; + public static final Object instance$ClientboundPlayerInfoUpdatePacket$Action$UPDATE_LATENCY; + public static final Object instance$ClientboundPlayerInfoUpdatePacket$Action$UPDATE_DISPLAY_NAME; + //public static final Object instance$ClientboundPlayerInfoUpdatePacket$Action$UPDATE_LIST_ORDER; + //public static final Object instance$ClientboundPlayerInfoUpdatePacket$Action$UPDATE_HAT; + + + static { + try { + Object[] values = (Object[]) method$ClientboundPlayerInfoUpdatePacket$Action$values.invoke(null); + instance$ClientboundPlayerInfoUpdatePacket$Action$ADD_PLAYER = values[0]; + instance$ClientboundPlayerInfoUpdatePacket$Action$INITIALIZE_CHAT = values[1]; + instance$ClientboundPlayerInfoUpdatePacket$Action$UPDATE_GAME_MODE = values[2]; + instance$ClientboundPlayerInfoUpdatePacket$Action$UPDATE_LISTED = values[3]; + instance$ClientboundPlayerInfoUpdatePacket$Action$UPDATE_LATENCY = values[4]; + instance$ClientboundPlayerInfoUpdatePacket$Action$UPDATE_DISPLAY_NAME = values[5]; + //1.21.3 + //instance$ClientboundPlayerInfoUpdatePacket$Action$UPDATE_LIST_ORDER = values[6]; + //1.21.4 + //instance$ClientboundPlayerInfoUpdatePacket$Action$UPDATE_HAT = values[7]; + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EntityUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EntityUtils.java index f002866ef..9a71ef3c2 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EntityUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EntityUtils.java @@ -111,24 +111,24 @@ public class EntityUtils { public static Object fromEquipmentSlot(org.bukkit.inventory.EquipmentSlot slot) { return switch (slot) { - case HAND -> Reflections.instance$EquipmentSlot$MAINHAND; - case OFF_HAND -> Reflections.instance$EquipmentSlot$OFFHAND; - case HEAD -> Reflections.instance$EquipmentSlot$HEAD; - case CHEST -> Reflections.instance$EquipmentSlot$CHEST; - case LEGS -> Reflections.instance$EquipmentSlot$LEGS; - case FEET -> Reflections.instance$EquipmentSlot$FEET; + case HAND -> CoreReflections.instance$EquipmentSlot$MAINHAND; + case OFF_HAND -> CoreReflections.instance$EquipmentSlot$OFFHAND; + case HEAD -> CoreReflections.instance$EquipmentSlot$HEAD; + case CHEST -> CoreReflections.instance$EquipmentSlot$CHEST; + case LEGS -> CoreReflections.instance$EquipmentSlot$LEGS; + case FEET -> CoreReflections.instance$EquipmentSlot$FEET; default -> new Object(); }; }; public static Object fromEquipmentSlot(EquipmentSlot slot) { return switch (slot) { - case MAIN_HAND -> Reflections.instance$EquipmentSlot$MAINHAND; - case OFF_HAND -> Reflections.instance$EquipmentSlot$OFFHAND; - case HEAD -> Reflections.instance$EquipmentSlot$HEAD; - case CHEST -> Reflections.instance$EquipmentSlot$CHEST; - case LEGS -> Reflections.instance$EquipmentSlot$LEGS; - case FEET -> Reflections.instance$EquipmentSlot$FEET; + case MAIN_HAND -> CoreReflections.instance$EquipmentSlot$MAINHAND; + case OFF_HAND -> CoreReflections.instance$EquipmentSlot$OFFHAND; + case HEAD -> CoreReflections.instance$EquipmentSlot$HEAD; + case CHEST -> CoreReflections.instance$EquipmentSlot$CHEST; + case LEGS -> CoreReflections.instance$EquipmentSlot$LEGS; + case FEET -> CoreReflections.instance$EquipmentSlot$FEET; default -> new Object(); }; } From 0c22567c2cd8c956f9e00fec56f575175a8d9c7a Mon Sep 17 00:00:00 2001 From: iqtester Date: Tue, 3 Jun 2025 02:04:15 -0400 Subject: [PATCH 04/10] Fix Head Rotate and Move to NetworkReflections --- .../entity/furniture/seat/CrawlSeat.java | 2 +- .../bukkit/entity/furniture/seat/LaySeat.java | 43 +++-- .../plugin/network/PacketConsumers.java | 2 +- .../reflection/minecraft/CoreReflections.java | 181 ------------------ .../minecraft/NetworkReflections.java | 92 +++++++++ 5 files changed, 117 insertions(+), 203 deletions(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/CrawlSeat.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/CrawlSeat.java index 588e1128a..2f3947673 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/CrawlSeat.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/CrawlSeat.java @@ -133,7 +133,7 @@ public class CrawlSeat extends AbstractSeat { super.dismount(player); ((org.bukkit.entity.Player) player.platformPlayer()).setPose(Pose.STANDING, false); try { - Object packet = CoreReflections.constructor$ClientboundRemoveEntitiesPacket.newInstance((Object) new int[]{visualId}); + Object packet = NetworkReflections.constructor$ClientboundRemoveEntitiesPacket.newInstance((Object) new int[]{visualId}); player.sendPacket(packet, false); } catch (Exception e) { BukkitCraftEngine.instance().logger().warn("Failed to remove crawl entity", e); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/LaySeat.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/LaySeat.java index 413609a0e..f18bf1b91 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/LaySeat.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/LaySeat.java @@ -101,17 +101,17 @@ public class LaySeat extends AbstractSeat { MEntityTypes.instance$EntityType$PLAYER, 0, CoreReflections.instance$Vec3$Zero, 0); // Info - EnumSet enumSet = EnumSet.noneOf((Class) CoreReflections.clazz$ClientboundPlayerInfoUpdatePacket$Action); - enumSet.add(CoreReflections.instance$ClientboundPlayerInfoUpdatePacket$Action$ADD_PLAYER); + EnumSet enumSet = EnumSet.noneOf((Class) NetworkReflections.clazz$ClientboundPlayerInfoUpdatePacket$Action); + enumSet.add(NetworkReflections.instance$ClientboundPlayerInfoUpdatePacket$Action$ADD_PLAYER); Object entry; if (VersionHelper.isOrAbove1_21_4()) { - entry = CoreReflections.constructor$ClientBoundPlayerInfoUpdatePacket$Entry.newInstance( + entry = NetworkReflections.constructor$ClientBoundPlayerInfoUpdatePacket$Entry.newInstance( uuid, npcProfile, false, 0, CoreReflections.instance$GameType$SURVIVAL, null, true, 0, null); } else if (VersionHelper.isOrAbove1_21_3()) { - entry = CoreReflections.constructor$ClientBoundPlayerInfoUpdatePacket$Entry.newInstance( + entry = NetworkReflections.constructor$ClientBoundPlayerInfoUpdatePacket$Entry.newInstance( uuid, npcProfile, false, 0, CoreReflections.instance$GameType$SURVIVAL, null, 0, null); } else { - entry = CoreReflections.constructor$ClientBoundPlayerInfoUpdatePacket$Entry.newInstance( + entry = NetworkReflections.constructor$ClientBoundPlayerInfoUpdatePacket$Entry.newInstance( uuid, npcProfile, false, 0, CoreReflections.instance$GameType$SURVIVAL, null, null); } Object npcInfoPacket = FastNMS.INSTANCE.constructor$ClientboundPlayerInfoUpdatePacket(enumSet, Collections.singletonList(entry)); @@ -124,7 +124,7 @@ public class LaySeat extends AbstractSeat { bedLoc.setY(bedLoc.getWorld().getMinHeight()); Object bedPos = LocationUtils.toBlockPos(new BlockPos(bedLoc.getBlockX(), bedLoc.getBlockY(), bedLoc.getBlockZ())); Object blockState = BlockStateUtils.blockDataToBlockState(bedData); - Object bedPacket = CoreReflections.constructor$ClientboundBlockUpdatePacket.newInstance(bedPos, blockState); + Object bedPacket = NetworkReflections.constructor$ClientboundBlockUpdatePacket.newInstance(bedPos, blockState); // Data Object npcData = CoreReflections.method$Entity$getEntityData.invoke(npc); @@ -146,15 +146,15 @@ public class LaySeat extends AbstractSeat { ); // Remove - Object npcRemovePacket = CoreReflections.constructor$ClientboundRemoveEntitiesPacket.newInstance((Object) new int[]{npcId}); + Object npcRemovePacket = NetworkReflections.constructor$ClientboundRemoveEntitiesPacket.newInstance((Object) new int[]{npcId}); // TP Object npcTeleportPacket; if (VersionHelper.isOrAbove1_21_3()) { Object positionMoveRotation = CoreReflections.method$PositionMoveRotation$of.invoke(null, npc); - npcTeleportPacket = CoreReflections.constructor$ClientboundTeleportEntityPacket.newInstance(npcId, positionMoveRotation, Set.of(), false); + npcTeleportPacket = NetworkReflections.constructor$ClientboundTeleportEntityPacket.newInstance(npcId, positionMoveRotation, Set.of(), false); } else { - npcTeleportPacket = CoreReflections.constructor$ClientboundTeleportEntityPacket.newInstance(npc); + npcTeleportPacket = NetworkReflections.constructor$ClientboundTeleportEntityPacket.newInstance(npc); } @@ -217,8 +217,11 @@ public class LaySeat extends AbstractSeat { float npcYawOffset = 0.0f; if (player.getY() > 0.0874218749) { if (VersionHelper.isOrAbove1_21_2()) { - if (npcDir == Direction.SOUTH) npcYawOffset = 27; - if (npcDir == Direction.NORTH) npcYawOffset = -26; + double offset = loc.x() - Math.floor(loc.x()) - 0.5; + + if (Math.abs(offset) > 0.05005) { + npcYawOffset = offset > 0 ? +27f : -26f; + } } } @@ -345,7 +348,7 @@ public class LaySeat extends AbstractSeat { Object blockPos = LocationUtils.toBlockPos(bedLoc.getBlockX(), bedLoc.getBlockY(), bedLoc.getBlockZ()); Object blockState = BlockStateUtils.blockDataToBlockState(bedLoc.getBlock().getBlockData()); try { - Object blockUpdatePacket = CoreReflections.constructor$ClientboundBlockUpdatePacket.newInstance(blockPos, blockState); + Object blockUpdatePacket = NetworkReflections.constructor$ClientboundBlockUpdatePacket.newInstance(blockPos, blockState); if (player.getPotionEffect(PotionEffectType.INVISIBILITY) == null) CoreReflections.method$Entity$setInvisible.invoke(serverPlayer.serverPlayer(), false); from.sendPacket(this.npcRemovePacket, true); from.sendPacket(blockUpdatePacket, true); @@ -362,7 +365,7 @@ public class LaySeat extends AbstractSeat { player.setSleepingIgnored(false); } - Object fullSlots = CoreReflections.method$ClientboundSetEquipmentPacket$getSlots.invoke(this.fullEquipPacket); + Object fullSlots = NetworkReflections.method$ClientboundSetEquipmentPacket$getSlots.invoke(this.fullEquipPacket); Object recoverEquip = NetworkReflections.constructor$ClientboundSetEquipmentPacket.newInstance(player.getEntityId(), fullSlots); for (org.bukkit.entity.Player p : PlayerUtils.getTrackedBy(player)) { @@ -418,7 +421,7 @@ public class LaySeat extends AbstractSeat { @Override public void handleContainerSetSlot(NetWorkUser user, NMSPacketEvent event, Object packet) { try { - int slot = (int) CoreReflections.method$ClientboundContainerSetSlotPacket$getSlot.invoke(packet); + int slot = (int) NetworkReflections.method$ClientboundContainerSetSlotPacket$getSlot.invoke(packet); org.bukkit.entity.Player player = (org.bukkit.entity.Player) user.platformPlayer(); int convertSlot; @@ -441,9 +444,9 @@ public class LaySeat extends AbstractSeat { } if (!(convertSlot == player.getInventory().getHeldItemSlot() || (isPlayerInv && (slot == 45 || (slot >= 5 && slot <= 8))))) return; - int containerId = (int) CoreReflections.method$ClientboundContainerSetSlotPacket$getContainerId.invoke(packet); - int stateId = (int) CoreReflections.method$ClientboundContainerSetSlotPacket$getStateId.invoke(packet); - Object replacePacket = CoreReflections.constructor$ClientboundContainerSetSlotPacket.newInstance(containerId, stateId, slot, MItems.Air$ItemStack); + int containerId = (int) NetworkReflections.method$ClientboundContainerSetSlotPacket$getContainerId.invoke(packet); + int stateId = (int) NetworkReflections.method$ClientboundContainerSetSlotPacket$getStateId.invoke(packet); + Object replacePacket = NetworkReflections.constructor$ClientboundContainerSetSlotPacket.newInstance(containerId, stateId, slot, MItems.Air$ItemStack); event.replacePacket(replacePacket); } catch (Exception e) { CraftEngine.instance().logger().warn("Failed to handleContainerSetSlotPacket", e); @@ -456,9 +459,9 @@ public class LaySeat extends AbstractSeat { try { Object animatePacket; if (e.getAnimationType() == PlayerAnimationType.ARM_SWING) { - animatePacket = CoreReflections.constructor$ClientboundAnimatePacket.newInstance(npc, 0); + animatePacket = NetworkReflections.constructor$ClientboundAnimatePacket.newInstance(npc, 0); } else { - animatePacket = CoreReflections.constructor$ClientboundAnimatePacket.newInstance(npc, 3); + animatePacket = NetworkReflections.constructor$ClientboundAnimatePacket.newInstance(npc, 3); } serverPlayer.sendPacket(animatePacket, true); for (org.bukkit.entity.Player other : PlayerUtils.getTrackedBy(serverPlayer.platformPlayer())) { @@ -549,7 +552,7 @@ public class LaySeat extends AbstractSeat { private void updateNpcYaw(float playerYaw) { byte packYaw = getRot(playerYaw); try { - this.npcRotHeadPacket = CoreReflections.constructor$ClientboundRotateHeadPacket.newInstance(npc, packYaw); + this.npcRotHeadPacket = NetworkReflections.constructor$ClientboundRotateHeadPacket.newInstance(npc, packYaw); } catch (Exception exception) { CraftEngine.instance().logger().warn("Failed to sync NPC yaw", exception); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java index e4948f54d..31688d9d2 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java @@ -2431,7 +2431,7 @@ public class PacketConsumers { }; public static final TriConsumer SET_EQUIPMENT_NMS = (user, event, packet) -> { try { - int entityId = (int) CoreReflections.method$ClientboundSetEquipmentPacket$getEntity.invoke(packet); + int entityId = (int) NetworkReflections.method$ClientboundSetEquipmentPacket$getEntity.invoke(packet); EntityPacketHandler handler = user.entityPacketHandlers().get(entityId); if (handler != null) { handler.handleSetEquipment(user, event, packet); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java index bd7d1eab1..ee1b3ef7e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java @@ -3447,23 +3447,6 @@ public final class CoreReflections { ) ); - public static final Class clazz$ClientBoundPlayerInfoRemovePacket = requireNonNull( - ReflectionUtils.getClazz( - BukkitReflectionUtils.assembleMCClass("network.protocol.game.ClientboundPlayerInfoRemovePacket") - ) - ); - - public static final Constructor constructor$ClientBoundPlayerInfoRemovePacket = requireNonNull( - ReflectionUtils.getConstructor(clazz$ClientBoundPlayerInfoRemovePacket, List.class) - ); - - public static final Class clazz$ClientBoundPlayerInfoUpdatePacket$Entry = requireNonNull( - BukkitReflectionUtils.findReobfOrMojmapClass( - "network.protocol.game.ClientboundPlayerInfoUpdatePacket$b", - "network.protocol.game.ClientboundPlayerInfoUpdatePacket$Entry" - ) - ); - public static final Class clazz$RemoteChatSession$Data = requireNonNull( BukkitReflectionUtils.findReobfOrMojmapClass( "network.chat.RemoteChatSession$a", @@ -3471,14 +3454,6 @@ public final class CoreReflections { ) ); - public static final Constructor constructor$ClientBoundPlayerInfoUpdatePacket$Entry = requireNonNull( - VersionHelper.isOrAbove1_21_3() - ? VersionHelper.isOrAbove1_21_4() - ? ReflectionUtils.getConstructor(clazz$ClientBoundPlayerInfoUpdatePacket$Entry, UUID.class, clazz$GameProfile, boolean.class, int.class, clazz$GameType, clazz$Component, boolean.class, int.class, clazz$RemoteChatSession$Data) - : ReflectionUtils.getConstructor(clazz$ClientBoundPlayerInfoUpdatePacket$Entry, UUID.class, clazz$GameProfile, boolean.class, int.class, clazz$GameType, clazz$Component, int.class, clazz$RemoteChatSession$Data) - : ReflectionUtils.getConstructor(clazz$ClientBoundPlayerInfoUpdatePacket$Entry, UUID.class, clazz$GameProfile, boolean.class, int.class, clazz$GameType, clazz$Component, clazz$RemoteChatSession$Data) - ); - public static final Method method$GameType$values = requireNonNull( ReflectionUtils.getStaticMethod( clazz$GameType, clazz$GameType.arrayType() @@ -3515,165 +3490,9 @@ public final class CoreReflections { ) ); - public static final Class clazz$ClientboundTeleportEntityPacket = requireNonNull( - BukkitReflectionUtils.findReobfOrMojmapClass( - "network.protocol.game.PacketPlayOutEntityTeleport", - "network.protocol.game.ClientboundTeleportEntityPacket" - ) - ); - - public static final Constructor constructor$ClientboundTeleportEntityPacket = requireNonNull( - VersionHelper.isOrAbove1_21_3() - ? ReflectionUtils.getConstructor(clazz$ClientboundTeleportEntityPacket, int.class, clazz$PositionMoveRotation, Set.class, boolean.class) - : ReflectionUtils.getConstructor(clazz$ClientboundTeleportEntityPacket, clazz$Entity) - ); - public static final Method method$Entity$setSharedFlag = requireNonNull( ReflectionUtils.getDeclaredMethod( clazz$Entity, void.class, int.class, boolean.class ) ); - - public static final Method method$ClientboundSetEquipmentPacket$getEntity = requireNonNull( - ReflectionUtils.getMethod( - NetworkReflections.clazz$ClientboundSetEquipmentPacket, int.class - ) - ); - - public static final Method method$ClientboundSetEquipmentPacket$getSlots = requireNonNull( - ReflectionUtils.getMethod( - NetworkReflections.clazz$ClientboundSetEquipmentPacket, List.class - ) - ); - - public static final Class clazz$ClientboundContainerSetSlotPacket = requireNonNull( - BukkitReflectionUtils.findReobfOrMojmapClass( - "network.protocol.game.PacketPlayOutSetSlot", - "network.protocol.game.ClientboundContainerSetSlotPacket" - ) - ); - - public static final Constructor constructor$ClientboundContainerSetSlotPacket = requireNonNull( - ReflectionUtils.getConstructor( - clazz$ClientboundContainerSetSlotPacket, int.class, int.class, int.class, clazz$ItemStack - ) - ); - - public static final Method method$ClientboundContainerSetSlotPacket$getContainerId = requireNonNull( - ReflectionUtils.getMethod( - clazz$ClientboundContainerSetSlotPacket, int.class, new String[]{"a", "getContainerId"} - ) - ); - - public static final Method method$ClientboundContainerSetSlotPacket$getSlot = requireNonNull( - ReflectionUtils.getMethod( - clazz$ClientboundContainerSetSlotPacket, int.class, new String[]{"c", "d", "getSlot"} - ) - ); - - public static final Method method$ClientboundContainerSetSlotPacket$getStateId = requireNonNull( - ReflectionUtils.getMethod( - clazz$ClientboundContainerSetSlotPacket, int.class, new String[]{"e", "f", "getStateId"} - ) - ); - - public static final Class clazz$ClientboundAnimatePacket = requireNonNull( - BukkitReflectionUtils.findReobfOrMojmapClass( - "network.protocol.game.PacketPlayOutAnimation", - "network.protocol.game.ClientboundAnimatePacket") - ); - - public static final Constructor constructor$ClientboundAnimatePacket = requireNonNull( - ReflectionUtils.getConstructor(clazz$ClientboundAnimatePacket, clazz$Entity, int.class) - ); - - public static final Class clazz$ClientboundRotateHeadPacket = requireNonNull( - BukkitReflectionUtils.findReobfOrMojmapClass( - "network.protocol.game.PacketPlayOutEntityHeadRotation", - "network.protocol.game.ClientboundRotateHeadPacket" - ) - ); - - public static final Constructor constructor$ClientboundRotateHeadPacket = requireNonNull( - ReflectionUtils.getDeclaredConstructor( - clazz$ClientboundRotateHeadPacket, clazz$Entity, byte.class - ) - ); - - public static final Class clazz$ClientboundBlockUpdatePacket = requireNonNull( - BukkitReflectionUtils.findReobfOrMojmapClass( - "network.protocol.game.PacketPlayOutBlockChange", - "network.protocol.game.ClientboundBlockUpdatePacket" - ) - ); - - public static final Constructor constructor$ClientboundBlockUpdatePacket = requireNonNull( - ReflectionUtils.getConstructor( - clazz$ClientboundBlockUpdatePacket, clazz$BlockPos, clazz$BlockState - ) - ); - - public static final Class clazz$ClientboundRemoveEntitiesPacket = requireNonNull( - BukkitReflectionUtils.findReobfOrMojmapClass( - "network.protocol.game.PacketPlayOutEntityDestroy", - "network.protocol.game.ClientboundRemoveEntitiesPacket" - ) - ); - - public static final Constructor constructor$ClientboundRemoveEntitiesPacket = requireNonNull( - ReflectionUtils.getConstructor(clazz$ClientboundRemoveEntitiesPacket, int[].class) - ); - - public static final Class clazz$ClientboundPlayerInfoUpdatePacket = requireNonNull( - ReflectionUtils.getClazz( - BukkitReflectionUtils.assembleMCClass("network.protocol.game.ClientboundPlayerInfoUpdatePacket") - ) - ); - - public static final Field field$ClientboundPlayerInfoUpdatePacket$entries = requireNonNull( - ReflectionUtils.getDeclaredField( - clazz$ClientboundPlayerInfoUpdatePacket, List.class, 0 - ) - ); - - public static final Class clazz$ClientboundPlayerInfoUpdatePacket$Action = requireNonNull( - BukkitReflectionUtils.findReobfOrMojmapClass( - "network.protocol.game.ClientboundPlayerInfoUpdatePacket$a", - "network.protocol.game.ClientboundPlayerInfoUpdatePacket$Action" - ) - ); - - public static final Method method$ClientboundPlayerInfoUpdatePacket$Action$values = requireNonNull( - ReflectionUtils.getStaticMethod( - clazz$ClientboundPlayerInfoUpdatePacket$Action, clazz$ClientboundPlayerInfoUpdatePacket$Action.arrayType() - ) - ); - - public static final Object instance$ClientboundPlayerInfoUpdatePacket$Action$ADD_PLAYER; - public static final Object instance$ClientboundPlayerInfoUpdatePacket$Action$INITIALIZE_CHAT; - public static final Object instance$ClientboundPlayerInfoUpdatePacket$Action$UPDATE_GAME_MODE; - public static final Object instance$ClientboundPlayerInfoUpdatePacket$Action$UPDATE_LISTED; - public static final Object instance$ClientboundPlayerInfoUpdatePacket$Action$UPDATE_LATENCY; - public static final Object instance$ClientboundPlayerInfoUpdatePacket$Action$UPDATE_DISPLAY_NAME; - //public static final Object instance$ClientboundPlayerInfoUpdatePacket$Action$UPDATE_LIST_ORDER; - //public static final Object instance$ClientboundPlayerInfoUpdatePacket$Action$UPDATE_HAT; - - - static { - try { - Object[] values = (Object[]) method$ClientboundPlayerInfoUpdatePacket$Action$values.invoke(null); - instance$ClientboundPlayerInfoUpdatePacket$Action$ADD_PLAYER = values[0]; - instance$ClientboundPlayerInfoUpdatePacket$Action$INITIALIZE_CHAT = values[1]; - instance$ClientboundPlayerInfoUpdatePacket$Action$UPDATE_GAME_MODE = values[2]; - instance$ClientboundPlayerInfoUpdatePacket$Action$UPDATE_LISTED = values[3]; - instance$ClientboundPlayerInfoUpdatePacket$Action$UPDATE_LATENCY = values[4]; - instance$ClientboundPlayerInfoUpdatePacket$Action$UPDATE_DISPLAY_NAME = values[5]; - //1.21.3 - //instance$ClientboundPlayerInfoUpdatePacket$Action$UPDATE_LIST_ORDER = values[6]; - //1.21.4 - //instance$ClientboundPlayerInfoUpdatePacket$Action$UPDATE_HAT = values[7]; - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } - } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/NetworkReflections.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/NetworkReflections.java index 40ffdc977..617298bc3 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/NetworkReflections.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/NetworkReflections.java @@ -382,12 +382,30 @@ public final class NetworkReflections { ReflectionUtils.getStaticMethod(clazz$ClientboundPlayerInfoUpdatePacket$Action, clazz$ClientboundPlayerInfoUpdatePacket$Action.arrayType()) ); + + public static final Object instance$ClientboundPlayerInfoUpdatePacket$Action$ADD_PLAYER; + public static final Object instance$ClientboundPlayerInfoUpdatePacket$Action$INITIALIZE_CHAT; + public static final Object instance$ClientboundPlayerInfoUpdatePacket$Action$UPDATE_GAME_MODE; + public static final Object instance$ClientboundPlayerInfoUpdatePacket$Action$UPDATE_LISTED; + public static final Object instance$ClientboundPlayerInfoUpdatePacket$Action$UPDATE_LATENCY; public static final Object instance$ClientboundPlayerInfoUpdatePacket$Action$UPDATE_DISPLAY_NAME; + //public static final Object instance$ClientboundPlayerInfoUpdatePacket$Action$UPDATE_LIST_ORDER; + //public static final Object instance$ClientboundPlayerInfoUpdatePacket$Action$UPDATE_HAT; + static { try { Object[] values = (Object[]) method$ClientboundPlayerInfoUpdatePacket$Action$values.invoke(null); + instance$ClientboundPlayerInfoUpdatePacket$Action$ADD_PLAYER = values[0]; + instance$ClientboundPlayerInfoUpdatePacket$Action$INITIALIZE_CHAT = values[1]; + instance$ClientboundPlayerInfoUpdatePacket$Action$UPDATE_GAME_MODE = values[2]; + instance$ClientboundPlayerInfoUpdatePacket$Action$UPDATE_LISTED = values[3]; + instance$ClientboundPlayerInfoUpdatePacket$Action$UPDATE_LATENCY = values[4]; instance$ClientboundPlayerInfoUpdatePacket$Action$UPDATE_DISPLAY_NAME = values[5]; + //1.21.3 + //instance$ClientboundPlayerInfoUpdatePacket$Action$UPDATE_LIST_ORDER = values[6]; + //1.21.4 + //instance$ClientboundPlayerInfoUpdatePacket$Action$UPDATE_HAT = values[7]; } catch (ReflectiveOperationException e) { throw new RuntimeException(e); } @@ -1484,4 +1502,78 @@ public final class NetworkReflections { } } + + public static final Class clazz$ClientBoundPlayerInfoUpdatePacket$Entry = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "network.protocol.game.ClientboundPlayerInfoUpdatePacket$b", + "network.protocol.game.ClientboundPlayerInfoUpdatePacket$Entry" + ) + ); + + public static final Constructor constructor$ClientBoundPlayerInfoUpdatePacket$Entry = requireNonNull( + VersionHelper.isOrAbove1_21_3() + ? VersionHelper.isOrAbove1_21_4() + ? ReflectionUtils.getConstructor(clazz$ClientBoundPlayerInfoUpdatePacket$Entry, UUID.class, CoreReflections.clazz$GameProfile, boolean.class, int.class, CoreReflections.clazz$GameType, CoreReflections.clazz$Component, boolean.class, int.class, CoreReflections.clazz$RemoteChatSession$Data) + : ReflectionUtils.getConstructor(clazz$ClientBoundPlayerInfoUpdatePacket$Entry, UUID.class, CoreReflections.clazz$GameProfile, boolean.class, int.class, CoreReflections.clazz$GameType, CoreReflections.clazz$Component, int.class, CoreReflections.clazz$RemoteChatSession$Data) + : ReflectionUtils.getConstructor(clazz$ClientBoundPlayerInfoUpdatePacket$Entry, UUID.class, CoreReflections.clazz$GameProfile, boolean.class, int.class, CoreReflections.clazz$GameType, CoreReflections.clazz$Component, CoreReflections.clazz$RemoteChatSession$Data) + ); + + public static final Class clazz$ClientboundTeleportEntityPacket = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "network.protocol.game.PacketPlayOutEntityTeleport", + "network.protocol.game.ClientboundTeleportEntityPacket" + ) + ); + + public static final Constructor constructor$ClientboundTeleportEntityPacket = requireNonNull( + VersionHelper.isOrAbove1_21_3() + ? ReflectionUtils.getConstructor(clazz$ClientboundTeleportEntityPacket, int.class, CoreReflections.clazz$PositionMoveRotation, Set.class, boolean.class) + : ReflectionUtils.getConstructor(clazz$ClientboundTeleportEntityPacket, CoreReflections.clazz$Entity) + ); + + public static final Method method$ClientboundSetEquipmentPacket$getEntity = requireNonNull( + ReflectionUtils.getMethod( + NetworkReflections.clazz$ClientboundSetEquipmentPacket, int.class + ) + ); + + public static final Method method$ClientboundSetEquipmentPacket$getSlots = requireNonNull( + ReflectionUtils.getMethod( + NetworkReflections.clazz$ClientboundSetEquipmentPacket, List.class + ) + ); + + public static final Method method$ClientboundContainerSetSlotPacket$getContainerId = requireNonNull( + ReflectionUtils.getMethod( + clazz$ClientboundContainerSetSlotPacket, int.class, new String[]{"a", "getContainerId"} + ) + ); + + public static final Method method$ClientboundContainerSetSlotPacket$getSlot = requireNonNull( + ReflectionUtils.getMethod( + clazz$ClientboundContainerSetSlotPacket, int.class, new String[]{"c", "d", "getSlot"} + ) + ); + + public static final Method method$ClientboundContainerSetSlotPacket$getStateId = requireNonNull( + ReflectionUtils.getMethod( + clazz$ClientboundContainerSetSlotPacket, int.class, new String[]{"e", "f", "getStateId"} + ) + ); + + public static final Class clazz$ClientboundAnimatePacket = requireNonNull( + BukkitReflectionUtils.findReobfOrMojmapClass( + "network.protocol.game.PacketPlayOutAnimation", + "network.protocol.game.ClientboundAnimatePacket") + ); + + public static final Constructor constructor$ClientboundAnimatePacket = requireNonNull( + ReflectionUtils.getConstructor(clazz$ClientboundAnimatePacket, CoreReflections.clazz$Entity, int.class) + ); + + public static final Constructor constructor$ClientboundRotateHeadPacket = requireNonNull( + ReflectionUtils.getDeclaredConstructor( + clazz$ClientboundRotateHeadPacket, CoreReflections.clazz$Entity, byte.class + ) + ); } From 63efa3fd5e64076f571d2899b884210f206e2b52 Mon Sep 17 00:00:00 2001 From: iqtester Date: Tue, 24 Jun 2025 02:43:18 -0400 Subject: [PATCH 05/10] Improved Head Rotate & Fix 1.20.1 NPC Not Spawning & Minor Improvements --- .../bukkit/entity/data/PlayerData.java | 15 +- .../entity/furniture/BukkitFurniture.java | 85 ++++--- .../furniture/BukkitFurnitureManager.java | 16 ++ .../furniture/hitbox/BukkitHitBoxTypes.java | 11 +- .../furniture/seat/BukkitSeatEntity.java | 24 +- .../entity/furniture/seat/CrawlSeat.java | 45 ++-- .../bukkit/entity/furniture/seat/LaySeat.java | 240 ++++++++++-------- .../bukkit/entity/furniture/seat/SitSeat.java | 14 +- .../plugin/network/BukkitNetworkManager.java | 3 - .../plugin/network/PacketConsumers.java | 18 +- .../reflection/minecraft/CoreReflections.java | 16 +- .../plugin/reflection/minecraft/MItems.java | 3 + .../minecraft/NetworkReflections.java | 4 + .../craftengine/bukkit/util/EntityUtils.java | 63 +---- .../core/entity/furniture/AbstractSeat.java | 2 +- .../core/entity/furniture/SeatType.java | 34 ++- .../core/entity/seat/SeatEntity.java | 4 + .../plugin/network/EntityPacketHandler.java | 2 +- .../core/registry/BuiltInRegistries.java | 2 - 19 files changed, 318 insertions(+), 283 deletions(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/PlayerData.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/PlayerData.java index b1e6e1b4a..c72936b0c 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/PlayerData.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/PlayerData.java @@ -3,13 +3,14 @@ package net.momirealms.craftengine.bukkit.entity.data; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; public class PlayerData extends LivingEntityData { - public static final PlayerData Pose = new PlayerData<>(6, EntityDataValue.Serializers$POSE, CoreReflections.instance$Pose$STANDING); - public static final PlayerData Skin = new PlayerData<>(17, EntityDataValue.Serializers$BYTE, (byte) 0); - public static final PlayerData Hand = new PlayerData<>(18, EntityDataValue.Serializers$BYTE, (byte) 0); - public static final PlayerData LShoulder = new PlayerData<>(19, EntityDataValue.Serializers$COMPOUND_TAG, CoreReflections.instance$CompoundTag$Empty); - public static final PlayerData RShoulder = new PlayerData<>(20, EntityDataValue.Serializers$COMPOUND_TAG, CoreReflections.instance$CompoundTag$Empty); + public static final PlayerData PlayerAbsorption = new PlayerData<>(PlayerData.class, EntityDataValue.Serializers$FLOAT, 0.0f); + public static final PlayerData Score = new PlayerData<>(PlayerData.class, EntityDataValue.Serializers$INT, 0); + public static final PlayerData PlayerModeCustomisation = new PlayerData<>(PlayerData.class, EntityDataValue.Serializers$BYTE, (byte) 0); + public static final PlayerData PlayerMainHand = new PlayerData<>(PlayerData.class, EntityDataValue.Serializers$BYTE, (byte) 1); + public static final PlayerData ShoulderLeft = new PlayerData<>(PlayerData.class, EntityDataValue.Serializers$COMPOUND_TAG, CoreReflections.instance$CompoundTag$Empty); + public static final PlayerData ShoulderRight = new PlayerData<>(PlayerData.class, EntityDataValue.Serializers$COMPOUND_TAG, CoreReflections.instance$CompoundTag$Empty); - public PlayerData(int id, Object serializer, T defaultValue) { - super(id, serializer, defaultValue); + public PlayerData(Class clazz, Object serializer, T defaultValue) { + super(clazz, serializer, defaultValue); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java index 92ff4c1b4..97daff287 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurniture.java @@ -8,6 +8,8 @@ import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.network.BukkitNetworkManager; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; +import net.momirealms.craftengine.bukkit.util.EntityUtils; +import net.momirealms.craftengine.bukkit.util.LegacyAttributeUtils; import net.momirealms.craftengine.bukkit.util.LocationUtils; import net.momirealms.craftengine.bukkit.util.PlayerUtils; import net.momirealms.craftengine.core.entity.furniture.*; @@ -15,11 +17,13 @@ import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.util.ArrayUtils; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.QuaternionUtils; +import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.world.WorldPosition; import net.momirealms.craftengine.core.world.collision.AABB; import org.bukkit.Location; -import org.bukkit.entity.Entity; -import org.bukkit.entity.Player; +import org.bukkit.World; +import org.bukkit.attribute.Attribute; +import org.bukkit.entity.*; import org.bukkit.persistence.PersistentDataType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -29,6 +33,7 @@ import org.joml.Vector3f; import java.io.IOException; import java.lang.ref.WeakReference; import java.util.*; +import java.util.function.Consumer; public class BukkitFurniture implements Furniture { private final Key id; @@ -286,6 +291,10 @@ public class BukkitFurniture implements Furniture { @Override public void spawnSeatEntityForPlayer(net.momirealms.craftengine.core.entity.player.Player player, Seat seat) { BukkitSeatEntity seatEntity = (BukkitSeatEntity) seat.spawn(player, this); + if (seatEntity == null) { + this.removeOccupiedSeat(seat.offset()); + return; + } this.seats.put(seatEntity.playerID(), seatEntity); player.setSeat(seatEntity); BukkitServerPlayer serverPlayer = (BukkitServerPlayer) player; @@ -314,10 +323,31 @@ public class BukkitFurniture implements Furniture { } } - private void spawnSeatEntityForPlayer(org.bukkit.entity.Player player, Seat seat) { - Location location = this.calculateSeatLocation(seat); - Entity seatEntity = seat.limitPlayerRotation() ? - EntityUtils.spawnEntity(player.getWorld(), VersionHelper.isOrAbove1_20_2() ? location.subtract(0,0.9875,0) : location.subtract(0,0.990625,0), EntityType.ARMOR_STAND, entity -> { + public Location calculateSeatLocation(Seat seat) { + Vector3f offset = QuaternionUtils.toQuaternionf(0, Math.toRadians(180 - this.location.getYaw()), 0).conjugate().transform(new Vector3f(seat.offset())); + double yaw = seat.yaw() + this.location.getYaw(); + if (yaw < -180) yaw += 360; + Location newLocation = this.location.clone(); + newLocation.setYaw((float) yaw); + newLocation.add(offset.x, offset.y + 0.6, -offset.z); + return newLocation; + } + + public BukkitSeatEntity seatByPlayerId(int playerId) { + return this.seats.get(playerId); + } + + public void removeSeatEntity(int playerId) { + this.seats.remove(playerId); + } + + public static Entity spawnSeatEntity(Furniture furniture, World world, Location loc, boolean limitPlayerRotation, Consumer function) { + EntityType type; + if (limitPlayerRotation) { + type = EntityType.ARMOR_STAND; + loc = VersionHelper.isOrAbove1_20_2() ? loc.subtract(0,0.9875,0) : loc.subtract(0,0.990625,0); + if (function == null) { + function = entity -> { ArmorStand armorStand = (ArmorStand) entity; if (VersionHelper.isOrAbove1_21_3()) { Objects.requireNonNull(armorStand.getAttribute(Attribute.MAX_HEALTH)).setBaseValue(0.01); @@ -333,37 +363,22 @@ public class BukkitFurniture implements Furniture { armorStand.setAI(false); armorStand.setGravity(false); armorStand.setPersistent(false); - armorStand.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_SEAT_BASE_ENTITY_KEY, PersistentDataType.INTEGER, this.baseEntityId()); - armorStand.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_SEAT_VECTOR_3F_KEY, PersistentDataType.STRING, seat.offset().x + ", " + seat.offset().y + ", " + seat.offset().z); - }) : - EntityUtils.spawnEntity(player.getWorld(), VersionHelper.isOrAbove1_20_2() ? location : location.subtract(0,0.25,0), EntityType.ITEM_DISPLAY, entity -> { + armorStand.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_SEAT_BASE_ENTITY_KEY, PersistentDataType.INTEGER, furniture.baseEntityId()); + //armorStand.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_SEAT_VECTOR_3F_KEY, PersistentDataType.STRING, seat.offset().x + ", " + seat.offset().y + ", " + seat.offset().z); + }; + } + } else { + type = EntityType.ITEM_DISPLAY; + loc = VersionHelper.isOrAbove1_20_2() ? loc : loc.subtract(0,0.25,0); + if (function == null) { + function = entity -> { ItemDisplay itemDisplay = (ItemDisplay) entity; itemDisplay.setPersistent(false); - itemDisplay.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_SEAT_BASE_ENTITY_KEY, PersistentDataType.INTEGER, this.baseEntityId()); - itemDisplay.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_SEAT_VECTOR_3F_KEY, PersistentDataType.STRING, seat.offset().x + ", " + seat.offset().y + ", " + seat.offset().z); - }); - this.seats.add(new WeakReference<>(seatEntity)); - if (!seatEntity.addPassenger(player)) { - seatEntity.remove(); - this.removeOccupiedSeat(seat.offset()); + itemDisplay.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_SEAT_BASE_ENTITY_KEY, PersistentDataType.INTEGER, furniture.baseEntityId()); + //itemDisplay.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_SEAT_VECTOR_3F_KEY, PersistentDataType.STRING, seat.offset().x + ", " + seat.offset().y + ", " + seat.offset().z); + }; + } } - } - - public Location calculateSeatLocation(Seat seat) { - Vector3f offset = QuaternionUtils.toQuaternionf(0, Math.toRadians(180 - this.location.getYaw()), 0).conjugate().transform(new Vector3f(seat.offset())); - double yaw = seat.yaw() + this.location.getYaw(); - if (yaw < -180) yaw += 360; - Location newLocation = this.location.clone(); - newLocation.setYaw((float) yaw); - newLocation.add(offset.x, offset.y + 0.6, -offset.z); - return newLocation; - } - - public BukkitSeatEntity seatByEntityId(int id) { - return this.seats.get(id); - } - - public void removeSeatEntity(int id) { - this.seats.remove(id); + return EntityUtils.spawnEntity(world, loc, type, function); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java index 28e94e12f..db1e88b7c 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java @@ -12,6 +12,7 @@ import net.momirealms.craftengine.bukkit.util.EntityUtils; import net.momirealms.craftengine.bukkit.util.KeyUtils; import net.momirealms.craftengine.bukkit.util.LocationUtils; import net.momirealms.craftengine.core.entity.furniture.*; +import net.momirealms.craftengine.core.entity.seat.SeatEntity; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.sound.SoundData; import net.momirealms.craftengine.core.util.Key; @@ -336,6 +337,21 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager { protected void tryLeavingSeat(@NotNull Player player, @NotNull Entity vehicle) { net.momirealms.craftengine.core.entity.player.Player serverPlayer = BukkitAdaptors.adapt(player); + if (serverPlayer == null) { + Integer baseFurniture = vehicle.getPersistentDataContainer().get(FURNITURE_SEAT_BASE_ENTITY_KEY, PersistentDataType.INTEGER); + if (baseFurniture == null) return; + BukkitFurniture furniture = loadedFurnitureByRealEntityId(baseFurniture); + if (furniture == null) { + vehicle.remove(); + return; + } + SeatEntity seatEntity = furniture.seatByPlayerId(player.getEntityId()); + if (seatEntity != null && !seatEntity.destroyed()) { + seatEntity.destroy(); + } + return; + } + BukkitSeatEntity seatEntity = (BukkitSeatEntity) serverPlayer.seat(); if (seatEntity == null || seatEntity.literalObject() != vehicle) return; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitHitBoxTypes.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitHitBoxTypes.java index 05b0ea0cf..dda752638 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitHitBoxTypes.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/BukkitHitBoxTypes.java @@ -4,17 +4,12 @@ import net.momirealms.craftengine.core.entity.furniture.HitBoxTypes; public class BukkitHitBoxTypes extends HitBoxTypes { - public static void init() { + public static void init() {} + + static { register(INTERACTION, InteractionHitBox.FACTORY); register(SHULKER, ShulkerHitBox.FACTORY); register(HAPPY_GHAST, HappyGhastHitBox.FACTORY); register(CUSTOM, CustomHitBox.FACTORY); } - - static { - //register(INTERACTION, InteractionHitBox.FACTORY); - //register(SHULKER, ShulkerHitBox.FACTORY); - //register(HAPPY_GHAST, HappyGhastHitBox.FACTORY); - //register(CUSTOM, CustomHitBox.FACTORY); - } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/BukkitSeatEntity.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/BukkitSeatEntity.java index df82a045f..61eb80dd8 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/BukkitSeatEntity.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/BukkitSeatEntity.java @@ -14,6 +14,7 @@ public abstract class BukkitSeatEntity extends BukkitEntity implements SeatEntit private final BukkitFurniture furniture; private final Vector3f vector3f; private final int playerID; + private boolean destroyed = false; public BukkitSeatEntity(Entity entity, Furniture furniture, Vector3f vector3f, int playerID) { super(entity); @@ -27,28 +28,45 @@ public abstract class BukkitSeatEntity extends BukkitEntity implements SeatEntit @Override public void dismount(Player player) { + if (player == null || destroyed) return; player.setSeat(null); + onDismount(player); destroy(); } + @Override + public void onDismount(Player player) { + + } + @Override public void event(Player player, Object event) {} @Override public void destroy() { + if (destroyed) return; + destroyed = true; + org.bukkit.entity.Entity entity = this.literalObject(); if (entity == null) return; for (org.bukkit.entity.Entity passenger : entity.getPassengers()) { entity.removePassenger(passenger); if (passenger instanceof org.bukkit.entity.Player p && p.getEntityId() == this.playerID) { - dismount(BukkitAdaptors.adapt(p)); - return; + Player cePlayer = BukkitAdaptors.adapt(p); + if (cePlayer != null && cePlayer.entityID() == playerID()) { + cePlayer.setSeat(null); + onDismount(cePlayer); + } } } + entity.remove(); furniture.removeSeatEntity(playerID()); furniture.removeOccupiedSeat(vector3f()); - entity.remove(); + } + + public boolean destroyed() { + return destroyed; } @Override diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/CrawlSeat.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/CrawlSeat.java index 2f3947673..9ed148529 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/CrawlSeat.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/CrawlSeat.java @@ -12,12 +12,8 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MAttributeH import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; -import net.momirealms.craftengine.bukkit.util.EntityUtils; import net.momirealms.craftengine.bukkit.util.PlayerUtils; -import net.momirealms.craftengine.core.entity.furniture.AbstractSeat; -import net.momirealms.craftengine.core.entity.furniture.Furniture; -import net.momirealms.craftengine.core.entity.furniture.Seat; -import net.momirealms.craftengine.core.entity.furniture.SeatFactory; +import net.momirealms.craftengine.core.entity.furniture.*; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.entity.seat.SeatEntity; import net.momirealms.craftengine.core.plugin.CraftEngine; @@ -61,15 +57,17 @@ public class CrawlSeat extends AbstractSeat { public SeatEntity spawn(org.bukkit.entity.Player player, Furniture furniture) { Location location = ((BukkitFurniture)furniture).calculateSeatLocation(this); - org.bukkit.entity.Entity seatEntity = EntityUtils.spawnSeatEntity(furniture, this, player.getWorld(), location, this.limitPlayerRotation, null); - seatEntity.addPassenger(player); + org.bukkit.entity.Entity seatEntity = BukkitFurniture.spawnSeatEntity(furniture, player.getWorld(), location, this.limitPlayerRotation, null); + if (!seatEntity.addPassenger(player)) { + return null; + }; // Fix Rider Pose - int visualId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(); + int entityId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(); List packets = new ArrayList<>(); - packets.add(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(visualId, UUID.randomUUID(), location.getX(), location.getY(), location.getZ(), location.getPitch(), location.getYaw(), - MEntityTypes.instance$EntityType$SHULKER, 0, CoreReflections.instance$Vec3$Zero, 0)); - packets.add(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(visualId, List.copyOf(visualData))); + packets.add(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(entityId, UUID.randomUUID(), location.getX(), location.getY(), location.getZ(), location.getPitch(), location.getYaw(), + MEntityTypes.SHULKER, 0, CoreReflections.instance$Vec3$Zero, 0)); + packets.add(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityId, List.copyOf(visualData))); try { if (VersionHelper.isOrAbove1_20_5()) { @@ -77,9 +75,9 @@ public class CrawlSeat extends AbstractSeat { CoreReflections.method$AttributeInstance$setBaseValue.invoke(attributeInstance, 0.6); packets.add( NetworkReflections.constructor$ClientboundUpdateAttributesPacket0 - .newInstance(visualId, Collections.singletonList(attributeInstance)) + .newInstance(entityId, Collections.singletonList(attributeInstance)) ); - packets.add(FastNMS.INSTANCE.constructor$ClientboundSetPassengersPacket(seatEntity.getEntityId(), visualId)); + packets.add(FastNMS.INSTANCE.constructor$ClientboundSetPassengersPacket(seatEntity.getEntityId(), entityId)); } } catch (Exception e) { CraftEngine.instance().logger().warn("Failed to add crawl seat attributes", e); @@ -109,17 +107,17 @@ public class CrawlSeat extends AbstractSeat { } }, 1, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4); - return new CrawlEntity(seatEntity, furniture, offset(), player.getEntityId(), visualId, syncPosePacket); + return new CrawlEntity(seatEntity, furniture, offset(), player.getEntityId(), entityId, syncPosePacket); } private static class CrawlEntity extends BukkitSeatEntity { - private final int visualId; + private final int entityId; private final Object syncPosePacket; - public CrawlEntity(Entity entity, Furniture furniture, Vector3f vector3f, int playerID, int visualId, Object fixPosePacket) { + public CrawlEntity(Entity entity, Furniture furniture, Vector3f vector3f, int playerID, int entityId, Object fixPosePacket) { super(entity, furniture, vector3f, playerID); - this.visualId = visualId; + this.entityId = entityId; this.syncPosePacket = fixPosePacket; } @@ -129,20 +127,21 @@ public class CrawlSeat extends AbstractSeat { } @Override - public void dismount(Player player) { - super.dismount(player); - ((org.bukkit.entity.Player) player.platformPlayer()).setPose(Pose.STANDING, false); + public void onDismount(Player player) { + if (player == null) return; + org.bukkit.entity.Player bukkitPlayer = (org.bukkit.entity.Player) player.platformPlayer(); + bukkitPlayer.setPose(Pose.STANDING, false); try { - Object packet = NetworkReflections.constructor$ClientboundRemoveEntitiesPacket.newInstance((Object) new int[]{visualId}); + Object packet = NetworkReflections.constructor$ClientboundRemoveEntitiesPacket.newInstance((Object) new int[]{entityId}); player.sendPacket(packet, false); } catch (Exception e) { - BukkitCraftEngine.instance().logger().warn("Failed to remove crawl entity", e); + BukkitCraftEngine.instance().logger().warn("Failed to dismount from CrawlEntity", e); } } @Override public Key type() { - return Key.of("craftengine", "crawl"); + return SeatType.CRAWL; } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/LaySeat.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/LaySeat.java index f18bf1b91..5822f84d1 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/LaySeat.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/LaySeat.java @@ -3,7 +3,6 @@ package net.momirealms.craftengine.bukkit.entity.furniture.seat; import com.google.common.collect.Multimap; import com.mojang.datafixers.util.Pair; import it.unimi.dsi.fastutil.ints.IntList; -import net.momirealms.craftengine.bukkit.api.BukkitAdaptors; import net.momirealms.craftengine.bukkit.entity.data.LivingEntityData; import net.momirealms.craftengine.bukkit.entity.data.PlayerData; import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurniture; @@ -18,13 +17,11 @@ import net.momirealms.craftengine.bukkit.plugin.scheduler.impl.FoliaTask; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.bukkit.util.*; import net.momirealms.craftengine.core.entity.EquipmentSlot; -import net.momirealms.craftengine.core.entity.furniture.AbstractSeat; -import net.momirealms.craftengine.core.entity.furniture.Furniture; -import net.momirealms.craftengine.core.entity.furniture.Seat; -import net.momirealms.craftengine.core.entity.furniture.SeatFactory; +import net.momirealms.craftengine.core.entity.furniture.*; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.entity.seat.SeatEntity; import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.network.ByteBufPacketEvent; import net.momirealms.craftengine.core.plugin.network.NMSPacketEvent; import net.momirealms.craftengine.core.plugin.network.NetWorkUser; import net.momirealms.craftengine.core.plugin.scheduler.SchedulerTask; @@ -41,6 +38,8 @@ import org.bukkit.event.player.PlayerAnimationEvent; import org.bukkit.event.player.PlayerAnimationType; import org.bukkit.event.player.PlayerItemHeldEvent; import org.bukkit.inventory.EntityEquipment; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryView; import org.bukkit.inventory.ItemStack; import org.bukkit.potion.PotionEffectType; import org.joml.Vector3f; @@ -49,12 +48,43 @@ import java.lang.reflect.Method; import java.util.*; import java.util.concurrent.TimeUnit; +import static net.momirealms.craftengine.core.plugin.network.ProtocolVersion.V1_21_2; + public class LaySeat extends AbstractSeat { public static final SeatFactory FACTORY = new Factory(); + private static final List> emptyEquipments; + private static final List> emptyBukkitEquipments; + private static Method method$InventoryView$convertSlot; + private static Method method$InventoryView$getTopInventory; + private static Method method$InventoryView$getType; private final Direction facing; private final boolean sleep; private final boolean phantom; + static { + if (!VersionHelper.isOrAbove1_21_1()) { + method$InventoryView$convertSlot = ReflectionUtils.getMethod(InventoryView.class, new String[]{"convertSlot"}, int.class); + method$InventoryView$getTopInventory = ReflectionUtils.getMethod(InventoryView.class, new String[]{"getTopInventory"}); + method$InventoryView$getType = ReflectionUtils.getMethod(Inventory.class, new String[]{"getType"}); + } + emptyEquipments = List.of( + Pair.of(CoreReflections.instance$EquipmentSlot$MAINHAND, MItems.AIR$Item), + Pair.of(CoreReflections.instance$EquipmentSlot$OFFHAND, MItems.AIR$Item), + Pair.of(CoreReflections.instance$EquipmentSlot$HEAD, MItems.AIR$Item), + Pair.of(CoreReflections.instance$EquipmentSlot$CHEST, MItems.AIR$Item), + Pair.of(CoreReflections.instance$EquipmentSlot$LEGS, MItems.AIR$Item), + Pair.of(CoreReflections.instance$EquipmentSlot$FEET, MItems.AIR$Item) + ); + emptyBukkitEquipments = List.of( + Pair.of(CoreReflections.instance$EquipmentSlot$MAINHAND, ItemUtils.AIR), + Pair.of(CoreReflections.instance$EquipmentSlot$OFFHAND, ItemUtils.AIR), + Pair.of(CoreReflections.instance$EquipmentSlot$HEAD, ItemUtils.AIR), + Pair.of(CoreReflections.instance$EquipmentSlot$CHEST, ItemUtils.AIR), + Pair.of(CoreReflections.instance$EquipmentSlot$LEGS, ItemUtils.AIR), + Pair.of(CoreReflections.instance$EquipmentSlot$FEET, ItemUtils.AIR) + ); + } + public LaySeat(Vector3f offset, Direction facing, boolean sleep, boolean phantom) { super(offset, 0); this.facing = facing; @@ -96,9 +126,14 @@ public class LaySeat extends AbstractSeat { } int npcId = FastNMS.INSTANCE.method$Entity$getId(npc); CoreReflections.method$Entity$absSnapTo.invoke(npc, loc.getX(), loc.getY(), loc.getZ(), 0, 0); - Object npcSpawnPacket = FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(npcId, uuid, - loc.getX(), loc.getY(), loc.getZ(), 0, 0, - MEntityTypes.instance$EntityType$PLAYER, 0, CoreReflections.instance$Vec3$Zero, 0); + Object npcSpawnPacket; + if (!VersionHelper.isOrAbove1_20_2()) { + npcSpawnPacket = NetworkReflections.constructor$ClientboundAddPlayerPacket.newInstance(npc); + } else { + npcSpawnPacket = FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(npcId, uuid, + loc.getX(), loc.getY(), loc.getZ(), 0, 0, + MEntityTypes.PLAYER, 0, CoreReflections.instance$Vec3$Zero, 0); + } // Info EnumSet enumSet = EnumSet.noneOf((Class) NetworkReflections.clazz$ClientboundPlayerInfoUpdatePacket$Action); @@ -132,12 +167,12 @@ public class LaySeat extends AbstractSeat { CoreReflections.method$Entity$setInvisible.invoke(serverPlayer, true); CoreReflections.method$SynchedEntityData$set.invoke(npcData, PlayerData.Pose.entityDataAccessor(), CoreReflections.instance$Pose$SLEEPING); CoreReflections.method$SynchedEntityData$set.invoke(npcData, LivingEntityData.SleepingPos.entityDataAccessor(), Optional.of(bedPos)); - CoreReflections.method$SynchedEntityData$set.invoke(npcData, PlayerData.Skin.entityDataAccessor(), CoreReflections.method$SynchedEntityData$get.invoke(playerData, PlayerData.Skin.entityDataAccessor())); - CoreReflections.method$SynchedEntityData$set.invoke(npcData, PlayerData.Hand.entityDataAccessor(), CoreReflections.method$SynchedEntityData$get.invoke(playerData, PlayerData.Hand.entityDataAccessor())); - CoreReflections.method$SynchedEntityData$set.invoke(npcData, PlayerData.LShoulder.entityDataAccessor(), CoreReflections.method$SynchedEntityData$get.invoke(playerData, PlayerData.LShoulder.entityDataAccessor())); - CoreReflections.method$SynchedEntityData$set.invoke(npcData, PlayerData.RShoulder.entityDataAccessor(), CoreReflections.method$SynchedEntityData$get.invoke(playerData, PlayerData.RShoulder.entityDataAccessor())); - CoreReflections.method$SynchedEntityData$set.invoke(playerData, PlayerData.LShoulder.entityDataAccessor(), CoreReflections.instance$CompoundTag$Empty); - CoreReflections.method$SynchedEntityData$set.invoke(playerData, PlayerData.RShoulder.entityDataAccessor(), CoreReflections.instance$CompoundTag$Empty); + CoreReflections.method$SynchedEntityData$set.invoke(npcData, PlayerData.PlayerModeCustomisation.entityDataAccessor(), CoreReflections.method$SynchedEntityData$get.invoke(playerData, PlayerData.PlayerModeCustomisation.entityDataAccessor())); + CoreReflections.method$SynchedEntityData$set.invoke(npcData, PlayerData.PlayerMainHand.entityDataAccessor(), CoreReflections.method$SynchedEntityData$get.invoke(playerData, PlayerData.PlayerMainHand.entityDataAccessor())); + CoreReflections.method$SynchedEntityData$set.invoke(npcData, PlayerData.ShoulderLeft.entityDataAccessor(), CoreReflections.method$SynchedEntityData$get.invoke(playerData, PlayerData.ShoulderLeft.entityDataAccessor())); + CoreReflections.method$SynchedEntityData$set.invoke(npcData, PlayerData.ShoulderRight.entityDataAccessor(), CoreReflections.method$SynchedEntityData$get.invoke(playerData, PlayerData.ShoulderRight.entityDataAccessor())); + CoreReflections.method$SynchedEntityData$set.invoke(playerData, PlayerData.ShoulderLeft.entityDataAccessor(), CoreReflections.instance$CompoundTag$Empty); + CoreReflections.method$SynchedEntityData$set.invoke(playerData, PlayerData.ShoulderRight.entityDataAccessor(), CoreReflections.instance$CompoundTag$Empty); // SetData CoreReflections.method$Entity$setInvisible.invoke(serverPlayer, true); @@ -157,17 +192,8 @@ public class LaySeat extends AbstractSeat { npcTeleportPacket = NetworkReflections.constructor$ClientboundTeleportEntityPacket.newInstance(npc); } - // Equipment - List> emptySlots = new ArrayList<>(); - - emptySlots.add(Pair.of(CoreReflections.instance$EquipmentSlot$MAINHAND, MItems.Air$ItemStack)); - emptySlots.add(Pair.of(CoreReflections.instance$EquipmentSlot$OFFHAND, MItems.Air$ItemStack)); - emptySlots.add(Pair.of(CoreReflections.instance$EquipmentSlot$HEAD, MItems.Air$ItemStack)); - emptySlots.add(Pair.of(CoreReflections.instance$EquipmentSlot$CHEST, MItems.Air$ItemStack)); - emptySlots.add(Pair.of(CoreReflections.instance$EquipmentSlot$LEGS, MItems.Air$ItemStack)); - emptySlots.add(Pair.of(CoreReflections.instance$EquipmentSlot$FEET, MItems.Air$ItemStack)); - Object emptyEquipPacket = NetworkReflections.constructor$ClientboundSetEquipmentPacket.newInstance(player.getEntityId(), emptySlots); + Object emptyEquipPacket = NetworkReflections.constructor$ClientboundSetEquipmentPacket.newInstance(player.getEntityId(), emptyEquipments); Map equipments = new HashMap<>(); EntityEquipment equipment = player.getEquipment(); @@ -184,6 +210,9 @@ public class LaySeat extends AbstractSeat { equipments.forEach((slot, item) -> npcSlots.add(Pair.of(EntityUtils.fromEquipmentSlot(slot), FastNMS.INSTANCE.method$CraftItemStack$asNMSCopy(item)))); Object fullEquipPacket = NetworkReflections.constructor$ClientboundSetEquipmentPacket.newInstance(npcId, npcSlots); + // Animation + Object npcLeftAnimatePacket = NetworkReflections.constructor$ClientboundAnimatePacket.newInstance(npc, 0); + Object npcRightAnimatePacket = NetworkReflections.constructor$ClientboundAnimatePacket.newInstance(npc, 3); packets.add(npcInfoPacket); packets.add(npcSpawnPacket); @@ -191,39 +220,33 @@ public class LaySeat extends AbstractSeat { packets.add(npcDataPacket); packets.add(npcTeleportPacket); packets.add(emptyEquipPacket); + packets.add(npcLeftAnimatePacket); Object npcInitPackets = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(packets); // Spawn - org.bukkit.entity.Entity seatEntity = EntityUtils.spawnSeatEntity(furniture, this, player.getWorld(), loc, false, null); - seatEntity.addPassenger(player); // 0.5 higher + org.bukkit.entity.Entity seatEntity = BukkitFurniture.spawnSeatEntity(furniture, player.getWorld(), loc, false, null); + if (!seatEntity.addPassenger(player)) { // 0.5 higher + return null; + } cePlayer.sendPacket(npcInitPackets, true); cePlayer.sendPacket(fullEquipPacket, true); - if (player.getY() > 0) { + if (player.getY() > 0 && cePlayer.protocolVersion().isVersionNewerThan(V1_21_2)) { BukkitCraftEngine.instance().scheduler().asyncLater(() -> cePlayer.sendPacket(npcTeleportPacket, true), 50, TimeUnit.MILLISECONDS); // over height 0 cost 2 npcTeleportPacket } - for (org.bukkit.entity.Player p : PlayerUtils.getTrackedBy(player)) { - BukkitNetworkManager.instance().getOnlineUser(p).sendPacket(npcInitPackets, false); - BukkitNetworkManager.instance().getOnlineUser(p).sendPacket(fullEquipPacket, false); - if (player.getY() > 0) { - BukkitCraftEngine.instance().scheduler().asyncLater(() -> BukkitNetworkManager.instance().getOnlineUser(p).sendPacket(npcTeleportPacket, false), + for (org.bukkit.entity.Player o : PlayerUtils.getTrackedBy(player)) { + NetWorkUser tracker = BukkitNetworkManager.instance().getOnlineUser(o); + tracker.sendPacket(npcInitPackets, false); + tracker.sendPacket(fullEquipPacket, false); + if (player.getY() > 0 && tracker.protocolVersion().isVersionNewerThan(V1_21_2)) { + BukkitCraftEngine.instance().scheduler().asyncLater(() -> tracker.sendPacket(npcTeleportPacket, false), 50, TimeUnit.MILLISECONDS); } } // HeadRot Direction npcDir = bedDir.opposite(); - float npcYawOffset = 0.0f; - if (player.getY() > 0.0874218749) { - if (VersionHelper.isOrAbove1_21_2()) { - double offset = loc.x() - Math.floor(loc.x()) - 0.5; - - if (Math.abs(offset) > 0.05005) { - npcYawOffset = offset > 0 ? +27f : -26f; - } - } - } if (sleep) { player.setSleepingIgnored(true); @@ -240,12 +263,13 @@ public class LaySeat extends AbstractSeat { npcInitPackets, npcRemovePacket, npcTeleportPacket, + npcLeftAnimatePacket, + npcRightAnimatePacket, (BukkitServerPlayer) cePlayer, bedLoc, npc, npcId, npcDir, - npcYawOffset, equipments, emptyEquipPacket, fullEquipPacket, @@ -261,12 +285,13 @@ public class LaySeat extends AbstractSeat { private final Object npcInitPackets; private final Object npcRemovePacket; private final Object npcTPPacket; + private final Object npcLeftAnimatePacket; + private final Object npcRightAnimatePacket; private final BukkitServerPlayer serverPlayer; private final Object npc; private final Location bedLoc; private final int npcID; private final Direction npcDir; - private final float npcYawOffset; // Equipment private final PlayerMonitorTask task; @@ -286,12 +311,13 @@ public class LaySeat extends AbstractSeat { Object npcInitPackets, Object npcRemovePacket, Object npcTPPacket, + Object npcLeftAnimatePacket, + Object npcRightAnimatePacket, BukkitServerPlayer serverPlayer, Location bedLoc, Object npc, int npcID, Direction npcDir, - float npcYawOffset, Map equipments, Object emptyEquipPacket, Object fullEquipPacket, @@ -301,12 +327,13 @@ public class LaySeat extends AbstractSeat { this.npcInitPackets = npcInitPackets; this.npcRemovePacket = npcRemovePacket; this.npcTPPacket = npcTPPacket; + this.npcLeftAnimatePacket = npcLeftAnimatePacket; + this.npcRightAnimatePacket = npcRightAnimatePacket; this.serverPlayer = serverPlayer; this.bedLoc = bedLoc; this.npc = npc; this.npcID = npcID; this.npcDir = npcDir; - this.npcYawOffset = npcYawOffset; this.task = new PlayerMonitorTask(); this.equipments = equipments; @@ -314,23 +341,17 @@ public class LaySeat extends AbstractSeat { this.fullEquipPacket = fullEquipPacket; this.sleep = sleep; - updateNpcYaw(serverPlayer.xRot()); - updateNpcInvisible(); } @Override public void add(NetWorkUser from, NetWorkUser to) { to.sendPacket(this.npcInitPackets, false); to.sendPacket(this.fullEquipPacket, false); - if (serverPlayer.y() > 0) { - BukkitCraftEngine.instance().scheduler().asyncLater(() -> { - to.sendPacket(this.npcTPPacket, false); - to.sendPacket(this.npcRotHeadPacket, false); - if (npcDataPacket != null) to.sendPacket(this.npcDataPacket, false); - }, 50, TimeUnit.MILLISECONDS); - } else { - to.sendPacket(this.npcRotHeadPacket, false); - if (npcDataPacket != null) to.sendPacket(this.npcDataPacket, false); + to.sendPacket(this.npcRotHeadPacket, false); + if (npcDataPacket != null) to.sendPacket(this.npcDataPacket, false); + if (serverPlayer.y() > 0 && to.protocolVersion().isVersionNewerThan(V1_21_2)) { + BukkitCraftEngine.instance().scheduler().asyncLater(() -> + to.sendPacket(this.npcTPPacket, false), 50, TimeUnit.MILLISECONDS); } } @@ -341,42 +362,58 @@ public class LaySeat extends AbstractSeat { } @Override - public void dismount(Player from) { - super.dismount(from); + public void onDismount(Player player) { + if (player == null) return; + this.task.task.cancel(); - org.bukkit.entity.Player player = (org.bukkit.entity.Player) from.platformPlayer(); + + org.bukkit.entity.Player bukkitPlayer = (org.bukkit.entity.Player) player.platformPlayer(); Object blockPos = LocationUtils.toBlockPos(bedLoc.getBlockX(), bedLoc.getBlockY(), bedLoc.getBlockZ()); Object blockState = BlockStateUtils.blockDataToBlockState(bedLoc.getBlock().getBlockData()); + try { Object blockUpdatePacket = NetworkReflections.constructor$ClientboundBlockUpdatePacket.newInstance(blockPos, blockState); - if (player.getPotionEffect(PotionEffectType.INVISIBILITY) == null) CoreReflections.method$Entity$setInvisible.invoke(serverPlayer.serverPlayer(), false); - from.sendPacket(this.npcRemovePacket, true); - from.sendPacket(blockUpdatePacket, true); + player.sendPacket(this.npcRemovePacket, true); + player.sendPacket(blockUpdatePacket, true); + + if (bukkitPlayer.getPotionEffect(PotionEffectType.INVISIBILITY) == null) { + CoreReflections.method$Entity$setInvisible.invoke(serverPlayer.serverPlayer(), false); + } Object npcData = CoreReflections.method$Entity$getEntityData.invoke(npc); Object playerData = CoreReflections.method$Entity$getEntityData.invoke(serverPlayer.serverPlayer()); - CoreReflections.method$SynchedEntityData$set.invoke(playerData, PlayerData.LShoulder.entityDataAccessor(), CoreReflections.method$SynchedEntityData$get.invoke(npcData, PlayerData.LShoulder.entityDataAccessor())); - CoreReflections.method$SynchedEntityData$set.invoke(playerData, PlayerData.RShoulder.entityDataAccessor(), CoreReflections.method$SynchedEntityData$get.invoke(npcData, PlayerData.RShoulder.entityDataAccessor())); - if (player.getPotionEffect(PotionEffectType.INVISIBILITY) == null) CoreReflections.method$Entity$setInvisible.invoke(serverPlayer.serverPlayer(), false); + CoreReflections.method$SynchedEntityData$set.invoke( + playerData, + PlayerData.ShoulderLeft.entityDataAccessor(), + CoreReflections.method$SynchedEntityData$get.invoke(npcData, PlayerData.ShoulderLeft.entityDataAccessor()) + ); + CoreReflections.method$SynchedEntityData$set.invoke( + playerData, + PlayerData.ShoulderRight.entityDataAccessor(), + CoreReflections.method$SynchedEntityData$get.invoke(npcData, PlayerData.ShoulderRight.entityDataAccessor()) + ); + if (bukkitPlayer.getPotionEffect(PotionEffectType.INVISIBILITY) == null) { + CoreReflections.method$Entity$setInvisible.invoke(serverPlayer.serverPlayer(), false); + } - player.updateInventory(); + bukkitPlayer.updateInventory(); if (sleep) { - player.setSleepingIgnored(false); + bukkitPlayer.setSleepingIgnored(false); } Object fullSlots = NetworkReflections.method$ClientboundSetEquipmentPacket$getSlots.invoke(this.fullEquipPacket); - Object recoverEquip = NetworkReflections.constructor$ClientboundSetEquipmentPacket.newInstance(player.getEntityId(), fullSlots); + Object recoverEquip = NetworkReflections.constructor$ClientboundSetEquipmentPacket.newInstance(bukkitPlayer.getEntityId(), fullSlots); - for (org.bukkit.entity.Player p : PlayerUtils.getTrackedBy(player)) { - BukkitServerPlayer sp = BukkitAdaptors.adapt(p); - sp.entityPacketHandlers().remove(playerID()); - sp.sendPacket(this.npcRemovePacket, false); - sp.sendPacket(blockUpdatePacket, false); - sp.sendPacket(recoverEquip, false); + for (org.bukkit.entity.Player o : PlayerUtils.getTrackedBy(bukkitPlayer)) { + BukkitServerPlayer tracker = (BukkitServerPlayer) BukkitNetworkManager.instance().getOnlineUser(o); + tracker.entityPacketHandlers().remove(playerID()); + tracker.sendPacket(this.npcRemovePacket, false); + tracker.sendPacket(blockUpdatePacket, false); + tracker.sendPacket(recoverEquip, false); } } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to dismount LayEntity", e); + CraftEngine.instance().logger().warn("Failed to dismount from LayEntity", e); } } @@ -407,14 +444,14 @@ public class LaySeat extends AbstractSeat { serverPlayer.sendPacket(this.emptyEquipPacket, false); serverPlayer.sendPacket(this.updateEquipPacket, false); - for (org.bukkit.entity.Player p : PlayerUtils.getTrackedBy(player)) { - BukkitNetworkManager.instance().getOnlineUser(p).sendPacket(this.updateEquipPacket, false); + for (org.bukkit.entity.Player o : PlayerUtils.getTrackedBy(player)) { + BukkitNetworkManager.instance().getOnlineUser(o).sendPacket(this.updateEquipPacket, false); } } @Override - public void handleSetEquipment(NetWorkUser user, NMSPacketEvent event, Object packet) { - if (this.emptyEquipPacket == packet) return; + public void handleSetEquipment(NetWorkUser user, ByteBufPacketEvent event, Object slots) { + if (emptyBukkitEquipments.equals(slots)) return; event.setCancelled(true); } @@ -428,15 +465,10 @@ public class LaySeat extends AbstractSeat { boolean isPlayerInv; if (!VersionHelper.isOrAbove1_21_1()) { - Object openInventory = player.getClass().getMethod("getOpenInventory").invoke(player); - - Method convertSlotMethod = openInventory.getClass().getMethod("convertSlot", int.class); - convertSlot = (int) convertSlotMethod.invoke(openInventory, slot); - - Method getTopInventoryMethod = openInventory.getClass().getMethod("getTopInventory"); - Object topInventory = getTopInventoryMethod.invoke(openInventory); - Method getTypeMethod = topInventory.getClass().getMethod("getType"); - Object type = getTypeMethod.invoke(topInventory); + Object openInventory = player.getOpenInventory(); + convertSlot = (int) method$InventoryView$convertSlot.invoke(openInventory, slot); + Object topInventory = method$InventoryView$getTopInventory.invoke(openInventory); + Object type = method$InventoryView$getType.invoke(topInventory); isPlayerInv = type == InventoryType.CRAFTING; } else { convertSlot = player.getOpenInventory().convertSlot(slot); @@ -446,7 +478,7 @@ public class LaySeat extends AbstractSeat { if (!(convertSlot == player.getInventory().getHeldItemSlot() || (isPlayerInv && (slot == 45 || (slot >= 5 && slot <= 8))))) return; int containerId = (int) NetworkReflections.method$ClientboundContainerSetSlotPacket$getContainerId.invoke(packet); int stateId = (int) NetworkReflections.method$ClientboundContainerSetSlotPacket$getStateId.invoke(packet); - Object replacePacket = NetworkReflections.constructor$ClientboundContainerSetSlotPacket.newInstance(containerId, stateId, slot, MItems.Air$ItemStack); + Object replacePacket = NetworkReflections.constructor$ClientboundContainerSetSlotPacket.newInstance(containerId, stateId, slot, MItems.AIR$Item); event.replacePacket(replacePacket); } catch (Exception e) { CraftEngine.instance().logger().warn("Failed to handleContainerSetSlotPacket", e); @@ -459,13 +491,13 @@ public class LaySeat extends AbstractSeat { try { Object animatePacket; if (e.getAnimationType() == PlayerAnimationType.ARM_SWING) { - animatePacket = NetworkReflections.constructor$ClientboundAnimatePacket.newInstance(npc, 0); + animatePacket = npcLeftAnimatePacket; } else { - animatePacket = NetworkReflections.constructor$ClientboundAnimatePacket.newInstance(npc, 3); + animatePacket = npcRightAnimatePacket; } serverPlayer.sendPacket(animatePacket, true); - for (org.bukkit.entity.Player other : PlayerUtils.getTrackedBy(serverPlayer.platformPlayer())) { - BukkitNetworkManager.instance().getOnlineUser(other).sendPacket(animatePacket, true); + for (org.bukkit.entity.Player o : PlayerUtils.getTrackedBy(serverPlayer.platformPlayer())) { + BukkitNetworkManager.instance().getOnlineUser(o).sendPacket(animatePacket, true); } } catch (Exception exception) { CraftEngine.instance().logger().warn("Failed to handle PlayerAnimationEvent", exception); @@ -480,7 +512,7 @@ public class LaySeat extends AbstractSeat { @Override public Key type() { - return Key.of("craftengine", "lay"); + return SeatType.LAY; } private class PlayerMonitorTask implements Runnable { @@ -494,7 +526,7 @@ public class LaySeat extends AbstractSeat { if (VersionHelper.isFolia()) { this.task = new FoliaTask(p.getScheduler().runAtFixedRate(plugin.javaPlugin(), (t) -> this.run(), () -> {}, 1, 1)); } else { - this.task = plugin.scheduler().sync().runRepeating(this, 1, 1); + this.task = plugin.scheduler().sync().runRepeating(this, 0, 1); } } @@ -519,8 +551,9 @@ public class LaySeat extends AbstractSeat { if (lastYaw != playerYaw) { updateNpcYaw(playerYaw); serverPlayer.sendPacket(npcRotHeadPacket, false); - for (org.bukkit.entity.Player other : PlayerUtils.getTrackedBy(player)) { - BukkitNetworkManager.instance().getOnlineUser(other).sendPacket(npcRotHeadPacket, true); + for (org.bukkit.entity.Player o : PlayerUtils.getTrackedBy(player)) { + NetWorkUser tracker = BukkitNetworkManager.instance().getOnlineUser(o); + tracker.sendPacket(npcRotHeadPacket, true); } this.lastYaw = playerYaw; } @@ -574,8 +607,7 @@ public class LaySeat extends AbstractSeat { mappedYaw = deltaYaw; } - float limitedYaw = Math.max(-45, Math.min(45, mappedYaw)); - float finalYaw = limitedYaw + npcYawOffset; + float finalYaw = Math.max(-45, Math.min(45, mappedYaw)); return MCUtils.packDegrees(finalYaw); } @@ -597,8 +629,8 @@ public class LaySeat extends AbstractSeat { Object dataValue = CoreReflections.method$SynchedEntityData$DataItem$value.invoke(dataItem); Object packet = FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(npcID, List.of(dataValue)); serverPlayer.sendPacket(packet, false); - for (org.bukkit.entity.Player p : PlayerUtils.getTrackedBy(player)) { - BukkitNetworkManager.instance().getOnlineUser(p).sendPacket(packet, false); + for (org.bukkit.entity.Player o : PlayerUtils.getTrackedBy(player)) { + BukkitNetworkManager.instance().getOnlineUser(o).sendPacket(packet, false); } } else if (player.getPotionEffect(PotionEffectType.INVISIBILITY) != null && npcDataPacket == null) { CoreReflections.method$Entity$setInvisible.invoke(npc, true); @@ -607,8 +639,8 @@ public class LaySeat extends AbstractSeat { Object dataValue = CoreReflections.method$SynchedEntityData$DataItem$value.invoke(dataItem); npcDataPacket = FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(npcID, List.of(dataValue)); serverPlayer.sendPacket(npcDataPacket, false); - for (org.bukkit.entity.Player p : PlayerUtils.getTrackedBy(player)) { - BukkitNetworkManager.instance().getOnlineUser(p).sendPacket(npcDataPacket, false); + for (org.bukkit.entity.Player o : PlayerUtils.getTrackedBy(player)) { + BukkitNetworkManager.instance().getOnlineUser(o).sendPacket(npcDataPacket, false); } } } catch (Exception e) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/SitSeat.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/SitSeat.java index 7593d64be..4df4d23a4 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/SitSeat.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/SitSeat.java @@ -1,11 +1,7 @@ package net.momirealms.craftengine.bukkit.entity.furniture.seat; import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurniture; -import net.momirealms.craftengine.bukkit.util.EntityUtils; -import net.momirealms.craftengine.core.entity.furniture.AbstractSeat; -import net.momirealms.craftengine.core.entity.furniture.Furniture; -import net.momirealms.craftengine.core.entity.furniture.Seat; -import net.momirealms.craftengine.core.entity.furniture.SeatFactory; +import net.momirealms.craftengine.core.entity.furniture.*; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.entity.seat.SeatEntity; import net.momirealms.craftengine.core.util.Key; @@ -32,8 +28,10 @@ public class SitSeat extends AbstractSeat { public SeatEntity spawn(org.bukkit.entity.Player player, Furniture furniture) { Location location = ((BukkitFurniture)furniture).calculateSeatLocation(this); - org.bukkit.entity.Entity seatEntity = EntityUtils.spawnSeatEntity(furniture, this, player.getWorld(), location, this.limitPlayerRotation, null); - seatEntity.addPassenger(player); + org.bukkit.entity.Entity seatEntity = BukkitFurniture.spawnSeatEntity(furniture, player.getWorld(), location, this.limitPlayerRotation, null); + if (!seatEntity.addPassenger(player)) { + return null; + }; return new SitEntity(seatEntity, furniture, offset(), player.getEntityId()); } @@ -45,7 +43,7 @@ public class SitSeat extends AbstractSeat { @Override public Key type() { - return Key.of("craftengine", "sit") ; + return SeatType.SIT; } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java index 7a93e53b2..762f8b04b 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java @@ -162,9 +162,6 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes registerNMSPacketConsumer(PacketConsumers.MOVE_POS_ENTITY, NetworkReflections.clazz$ClientboundMoveEntityPacket$Pos); registerNMSPacketConsumer(PacketConsumers.ROTATE_HEAD, NetworkReflections.clazz$ClientboundRotateHeadPacket); registerNMSPacketConsumer(PacketConsumers.SET_ENTITY_MOTION, NetworkReflections.clazz$ClientboundSetEntityMotionPacket); - registerNMSPacketConsumer(PacketConsumers.SET_EQUIPMENT_NMS, Reflections.clazz$ClientboundSetEquipmentPacket); - registerNMSPacketConsumer(PacketConsumers.SET_CONTAINER_SLOT, Reflections.clazz$ClientboundContainerSetSlotPacket); - registerNMSPacketConsumer(PacketConsumers.SET_EQUIPMENT_NMS, NetworkReflections.clazz$ClientboundSetEquipmentPacket); registerNMSPacketConsumer(PacketConsumers.SET_CONTAINER_SLOT, NetworkReflections.clazz$ClientboundContainerSetSlotPacket); registerS2CByteBufPacketConsumer(PacketConsumers.LEVEL_CHUNK_WITH_LIGHT, this.packetIds.clientboundLevelChunkWithLightPacket()); registerS2CByteBufPacketConsumer(PacketConsumers.SECTION_BLOCK_UPDATE, this.packetIds.clientboundSectionBlocksUpdatePacket()); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java index 00690ce9b..7fe2b5d38 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java @@ -182,7 +182,7 @@ public class PacketConsumers { user.entityPacketHandlers().put(id, FurnitureCollisionPacketHandler.INSTANCE); } }; - ADD_ENTITY_HANDLERS[MEntityTypes.instance$EntityType$PLAYER$registryId] = (user, event) -> { + ADD_ENTITY_HANDLERS[MEntityTypes.PLAYER$registryId] = (user, event) -> { FriendlyByteBuf buf = event.getBuffer(); buf.readVarInt(); UUID uuid = buf.readUUID(); @@ -2198,6 +2198,10 @@ public class PacketConsumers { FastNMS.INSTANCE.method$FriendlyByteBuf$writeItem(newFriendlyBuf, pair.getSecond()); } } + EntityPacketHandler handler = user.entityPacketHandlers().get(entity); + if (handler != null) { + handler.handleSetEquipment(user, event, slots); + } } catch (Exception e) { CraftEngine.instance().logger().warn("Failed to handle ClientboundSetEquipmentPacket", e); } @@ -2436,18 +2440,6 @@ public class PacketConsumers { } }; - public static final TriConsumer SET_EQUIPMENT_NMS = (user, event, packet) -> { - try { - int entityId = (int) NetworkReflections.method$ClientboundSetEquipmentPacket$getEntity.invoke(packet); - EntityPacketHandler handler = user.entityPacketHandlers().get(entityId); - if (handler != null) { - handler.handleSetEquipment(user, event, packet); - } - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle ClientboundSetEquipmentPacket", e); - } - }; - public static final TriConsumer SET_CONTAINER_SLOT = (user, event, packet) -> { try { SeatEntity seat = ((BukkitServerPlayer) user).seat(); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java index a65e9926c..2c564c3e8 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java @@ -3540,9 +3540,9 @@ public final class CoreReflections { ); // 1.20.2 + - public static final Method method$ServerPlayer$clientInformation = VersionHelper.isOrAbove1_20_2() ? requireNonNull(ReflectionUtils.getMethod( - clazz$ServerPlayer, clazz$ClientInformation, 0 - )) : null; + public static final Method method$ServerPlayer$clientInformation = Optional.ofNullable(clazz$ClientInformation) + .map(it -> ReflectionUtils.getMethod(clazz$ServerPlayer, it, 0)) + .orElse(null); public static final Class clazz$CompoundTag = requireNonNull( BukkitReflectionUtils.findReobfOrMojmapClass( @@ -3634,11 +3634,13 @@ public final class CoreReflections { } // 1.21.3 + - public static final Class clazz$PositionMoveRotation = VersionHelper.isOrAbove1_21_3() ? ReflectionUtils.getClazz( - requireNonNull(BukkitReflectionUtils.assembleMCClass("world.entity.PositionMoveRotation"))) : null; + public static final Class clazz$PositionMoveRotation = ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("world.entity.PositionMoveRotation") + ); - public static final Method method$PositionMoveRotation$of = VersionHelper.isOrAbove1_21_3() ? requireNonNull( - ReflectionUtils.getStaticMethod(clazz$PositionMoveRotation, clazz$PositionMoveRotation, clazz$Entity)) : null; + public static final Method method$PositionMoveRotation$of = Optional.ofNullable(clazz$PositionMoveRotation) + .map(it -> ReflectionUtils.getStaticMethod(it, it, clazz$Entity)) + .orElse(null); public static final Method method$Entity$absSnapTo = requireNonNull( ReflectionUtils.getMethod( diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MItems.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MItems.java index 51fbc836b..cf2ee270b 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MItems.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MItems.java @@ -2,6 +2,7 @@ package net.momirealms.craftengine.bukkit.plugin.reflection.minecraft; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.reflection.ReflectionInitException; +import net.momirealms.craftengine.bukkit.util.ItemUtils; public final class MItems { private MItems() {} @@ -22,4 +23,6 @@ public final class MItems { throw new ReflectionInitException("Failed to init Items", e); } } + + public static final Object AIR$Item = FastNMS.INSTANCE.method$CraftItemStack$asNMSCopy(ItemUtils.AIR); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/NetworkReflections.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/NetworkReflections.java index 617298bc3..845c83c23 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/NetworkReflections.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/NetworkReflections.java @@ -178,6 +178,10 @@ public final class NetworkReflections { "network.protocol.game.ClientboundAddPlayerPacket" ); + public static final Constructor constructor$ClientboundAddPlayerPacket = Optional.ofNullable(clazz$ClientboundAddPlayerPacket) + .map(it -> ReflectionUtils.getConstructor(clazz$ClientboundAddPlayerPacket, CoreReflections.clazz$Player)) + .orElse(null); + public static final Class clazz$ClientboundRemoveEntitiesPacket = requireNonNull( BukkitReflectionUtils.findReobfOrMojmapClass( "network.protocol.game.PacketPlayOutEntityDestroy", diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EntityUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EntityUtils.java index 9a71ef3c2..3a23c5277 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EntityUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EntityUtils.java @@ -1,21 +1,17 @@ package net.momirealms.craftengine.bukkit.util; -import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.core.entity.EquipmentSlot; -import net.momirealms.craftengine.core.entity.furniture.Furniture; -import net.momirealms.craftengine.core.entity.furniture.Seat; import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.world.BlockPos; import org.bukkit.Location; import org.bukkit.World; -import org.bukkit.attribute.Attribute; -import org.bukkit.entity.*; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; import org.bukkit.event.entity.CreatureSpawnEvent; -import org.bukkit.persistence.PersistentDataType; -import java.util.Objects; import java.util.function.Consumer; import static net.momirealms.craftengine.core.entity.EquipmentSlot.BODY; @@ -43,47 +39,6 @@ public class EntityUtils { } } - public static Entity spawnSeatEntity(Furniture furniture, Seat seat, World world, Location loc, boolean limitPlayerRotation, Consumer function) { - EntityType type; - if (limitPlayerRotation) { - type = EntityType.ARMOR_STAND; - loc = VersionHelper.isOrAbove1_20_2() ? loc.subtract(0,0.9875,0) : loc.subtract(0,0.990625,0); - if (function == null) { - function = entity -> { - ArmorStand armorStand = (ArmorStand) entity; - if (VersionHelper.isOrAbove1_21_3()) { - Objects.requireNonNull(armorStand.getAttribute(Attribute.MAX_HEALTH)).setBaseValue(0.01); - } else { - LegacyAttributeUtils.setMaxHealth(armorStand); - } - armorStand.setSmall(true); - armorStand.setInvisible(true); - armorStand.setSilent(true); - armorStand.setInvulnerable(true); - armorStand.setArms(false); - armorStand.setCanTick(false); - armorStand.setAI(false); - armorStand.setGravity(false); - armorStand.setPersistent(false); - armorStand.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_SEAT_BASE_ENTITY_KEY, PersistentDataType.INTEGER, furniture.baseEntityId()); - //armorStand.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_SEAT_VECTOR_3F_KEY, PersistentDataType.STRING, seat.offset().x + ", " + seat.offset().y + ", " + seat.offset().z); - }; - } - } else { - type = EntityType.ITEM_DISPLAY; - loc = VersionHelper.isOrAbove1_20_2() ? loc : loc.subtract(0,0.25,0); - if (function == null) { - function = entity -> { - ItemDisplay itemDisplay = (ItemDisplay) entity; - itemDisplay.setPersistent(false); - itemDisplay.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_SEAT_BASE_ENTITY_KEY, PersistentDataType.INTEGER, furniture.baseEntityId()); - //itemDisplay.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_SEAT_VECTOR_3F_KEY, PersistentDataType.STRING, seat.offset().x + ", " + seat.offset().y + ", " + seat.offset().z); - }; - } - } - return spawnEntity(world, loc, type, function); - } - public static org.bukkit.inventory.EquipmentSlot toBukkitEquipmentSlot(EquipmentSlot slot) { return switch (slot) { case MAIN_HAND -> org.bukkit.inventory.EquipmentSlot.HAND; @@ -109,18 +64,6 @@ public class EntityUtils { }; } - public static Object fromEquipmentSlot(org.bukkit.inventory.EquipmentSlot slot) { - return switch (slot) { - case HAND -> CoreReflections.instance$EquipmentSlot$MAINHAND; - case OFF_HAND -> CoreReflections.instance$EquipmentSlot$OFFHAND; - case HEAD -> CoreReflections.instance$EquipmentSlot$HEAD; - case CHEST -> CoreReflections.instance$EquipmentSlot$CHEST; - case LEGS -> CoreReflections.instance$EquipmentSlot$LEGS; - case FEET -> CoreReflections.instance$EquipmentSlot$FEET; - default -> new Object(); - }; - }; - public static Object fromEquipmentSlot(EquipmentSlot slot) { return switch (slot) { case MAIN_HAND -> CoreReflections.instance$EquipmentSlot$MAINHAND; diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractSeat.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractSeat.java index f05a6ae4d..ba19c21f2 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractSeat.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractSeat.java @@ -4,7 +4,7 @@ import org.joml.Vector3f; import java.util.Objects; -public abstract class AbstractSeat implements Seat{ +public abstract class AbstractSeat implements Seat { protected final Vector3f offset; protected final float yaw; diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/SeatType.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/SeatType.java index 1a96248d3..054a57089 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/SeatType.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/SeatType.java @@ -1,6 +1,5 @@ package net.momirealms.craftengine.core.entity.furniture; -import com.google.common.collect.Lists; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.Holder; @@ -9,6 +8,7 @@ import net.momirealms.craftengine.core.registry.WritableRegistry; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceKey; +import java.util.ArrayList; import java.util.List; public class SeatType { @@ -23,16 +23,34 @@ public class SeatType { } public static Seat fromString(String s) { - List split = Lists.newArrayList(s.split(" ")); - int last = split.size() - 1; + int lastSpaceIndex = s.lastIndexOf(' '); + Key type = SIT; SeatFactory factory; - try { - Float.parseFloat(split.get(last)); - } catch (NullPointerException | NumberFormatException e) { - type = Key.withDefaultNamespace(split.get(last), "craftengine"); - split.remove(last); + String numericPart; + + if (lastSpaceIndex != -1) { + numericPart = s.substring(lastSpaceIndex + 1); + try { + Float.parseFloat(numericPart); + } catch (NumberFormatException e) { + type = Key.withDefaultNamespace(numericPart, "craftengine"); + s = s.substring(0, lastSpaceIndex); + lastSpaceIndex = s.lastIndexOf(' '); + } } + + List split = new ArrayList<>(); + int start = 0; + while (lastSpaceIndex != -1) { + split.add(s.substring(start, lastSpaceIndex)); + start = lastSpaceIndex + 1; + lastSpaceIndex = s.indexOf(' ', start); + } + if (start < s.length()) { + split.add(s.substring(start)); + } + factory = BuiltInRegistries.SEAT_FACTORY.getValue(type); if (factory == null) { throw new LocalizedResourceConfigException("warning.config.furniture.seat.invalid_type", type.toString()); diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/seat/SeatEntity.java b/core/src/main/java/net/momirealms/craftengine/core/entity/seat/SeatEntity.java index dc621df4b..f1ee6e3d9 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/seat/SeatEntity.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/seat/SeatEntity.java @@ -12,10 +12,14 @@ public interface SeatEntity extends EntityPacketHandler { void dismount(Player player); + void onDismount(Player player); + void event(Player player, Object event); void destroy(); + boolean destroyed(); + Furniture furniture(); Vector3f vector3f(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/EntityPacketHandler.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/EntityPacketHandler.java index 163556067..70113b8ca 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/EntityPacketHandler.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/EntityPacketHandler.java @@ -20,7 +20,7 @@ public interface EntityPacketHandler { default void handleMove(NetWorkUser user, NMSPacketEvent event, Object packet) { } - default void handleSetEquipment(NetWorkUser user, NMSPacketEvent event, Object packet) { + default void handleSetEquipment(NetWorkUser user, ByteBufPacketEvent event, Object slots) { } default void handleContainerSetSlot(NetWorkUser user, NMSPacketEvent event, Object packet) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java b/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java index e09b61f34..64757f0ab 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java +++ b/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java @@ -57,8 +57,6 @@ public class BuiltInRegistries { public static final Registry> EVENT_FUNCTION_FACTORY = createRegistry(Registries.EVENT_FUNCTION_FACTORY); public static final Registry> EVENT_CONDITION_FACTORY = createRegistry(Registries.EVENT_CONDITION_FACTORY); public static final Registry> PLAYER_SELECTOR_FACTORY = createRegistry(Registries.PLAYER_SELECTOR_FACTORY); - public static final Registry>> PLAYER_BLOCK_FUNCTION_FACTORY = createRegistry(Registries.PLAYER_BLOCK_FUNCTION_FACTORY); - public static final Registry>> PLAYER_BLOCK_CONDITION_FACTORY = createRegistry(Registries.PLAYER_BLOCK_CONDITION_FACTORY); public static final Registry SEAT_FACTORY = createRegistry(Registries.SEAT_FACTORY); private static Registry createRegistry(ResourceKey> key) { From 273bd5d29229c7586a51f810f2d6ec094b6a42f3 Mon Sep 17 00:00:00 2001 From: iqtester Date: Tue, 24 Jun 2025 11:39:39 -0400 Subject: [PATCH 06/10] Fix Indentation --- .../bukkit/entity/data/PlayerData.java | 18 +- .../furniture/seat/BukkitSeatEntity.java | 118 +- .../furniture/seat/BukkitSeatTypes.java | 10 +- .../entity/furniture/seat/CrawlSeat.java | 202 +-- .../bukkit/entity/furniture/seat/LaySeat.java | 1251 +++++++++-------- .../bukkit/entity/furniture/seat/SitSeat.java | 68 +- .../core/entity/furniture/AbstractSeat.java | 52 +- .../core/entity/furniture/SeatFactory.java | 2 +- .../core/entity/furniture/SeatType.java | 78 +- .../core/entity/seat/SeatEntity.java | 18 +- 10 files changed, 910 insertions(+), 907 deletions(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/PlayerData.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/PlayerData.java index c72936b0c..202f1b5a7 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/PlayerData.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/PlayerData.java @@ -3,14 +3,14 @@ package net.momirealms.craftengine.bukkit.entity.data; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; public class PlayerData extends LivingEntityData { - public static final PlayerData PlayerAbsorption = new PlayerData<>(PlayerData.class, EntityDataValue.Serializers$FLOAT, 0.0f); - public static final PlayerData Score = new PlayerData<>(PlayerData.class, EntityDataValue.Serializers$INT, 0); - public static final PlayerData PlayerModeCustomisation = new PlayerData<>(PlayerData.class, EntityDataValue.Serializers$BYTE, (byte) 0); - public static final PlayerData PlayerMainHand = new PlayerData<>(PlayerData.class, EntityDataValue.Serializers$BYTE, (byte) 1); - public static final PlayerData ShoulderLeft = new PlayerData<>(PlayerData.class, EntityDataValue.Serializers$COMPOUND_TAG, CoreReflections.instance$CompoundTag$Empty); - public static final PlayerData ShoulderRight = new PlayerData<>(PlayerData.class, EntityDataValue.Serializers$COMPOUND_TAG, CoreReflections.instance$CompoundTag$Empty); + public static final PlayerData PlayerAbsorption = new PlayerData<>(PlayerData.class, EntityDataValue.Serializers$FLOAT, 0.0f); + public static final PlayerData Score = new PlayerData<>(PlayerData.class, EntityDataValue.Serializers$INT, 0); + public static final PlayerData PlayerModeCustomisation = new PlayerData<>(PlayerData.class, EntityDataValue.Serializers$BYTE, (byte) 0); + public static final PlayerData PlayerMainHand = new PlayerData<>(PlayerData.class, EntityDataValue.Serializers$BYTE, (byte) 1); + public static final PlayerData ShoulderLeft = new PlayerData<>(PlayerData.class, EntityDataValue.Serializers$COMPOUND_TAG, CoreReflections.instance$CompoundTag$Empty); + public static final PlayerData ShoulderRight = new PlayerData<>(PlayerData.class, EntityDataValue.Serializers$COMPOUND_TAG, CoreReflections.instance$CompoundTag$Empty); - public PlayerData(Class clazz, Object serializer, T defaultValue) { - super(clazz, serializer, defaultValue); - } + public PlayerData(Class clazz, Object serializer, T defaultValue) { + super(clazz, serializer, defaultValue); + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/BukkitSeatEntity.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/BukkitSeatEntity.java index 61eb80dd8..cff139fe4 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/BukkitSeatEntity.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/BukkitSeatEntity.java @@ -11,76 +11,76 @@ import org.bukkit.entity.Entity; import org.joml.Vector3f; public abstract class BukkitSeatEntity extends BukkitEntity implements SeatEntity { - private final BukkitFurniture furniture; - private final Vector3f vector3f; - private final int playerID; - private boolean destroyed = false; + private final BukkitFurniture furniture; + private final Vector3f vector3f; + private final int playerID; + private boolean destroyed = false; - public BukkitSeatEntity(Entity entity, Furniture furniture, Vector3f vector3f, int playerID) { - super(entity); - this.furniture = (BukkitFurniture) furniture; - this.vector3f = vector3f; - this.playerID = playerID; - } + public BukkitSeatEntity(Entity entity, Furniture furniture, Vector3f vector3f, int playerID) { + super(entity); + this.furniture = (BukkitFurniture) furniture; + this.vector3f = vector3f; + this.playerID = playerID; + } - @Override - public void add(NetWorkUser from, NetWorkUser to) {} + @Override + public void add(NetWorkUser from, NetWorkUser to) {} - @Override - public void dismount(Player player) { - if (player == null || destroyed) return; - player.setSeat(null); - onDismount(player); - destroy(); - } + @Override + public void dismount(Player player) { + if (player == null || destroyed) return; + player.setSeat(null); + onDismount(player); + destroy(); + } - @Override - public void onDismount(Player player) { + @Override + public void onDismount(Player player) { - } + } - @Override - public void event(Player player, Object event) {} + @Override + public void event(Player player, Object event) {} - @Override - public void destroy() { - if (destroyed) return; - destroyed = true; + @Override + public void destroy() { + if (destroyed) return; + destroyed = true; - org.bukkit.entity.Entity entity = this.literalObject(); - if (entity == null) return; + org.bukkit.entity.Entity entity = this.literalObject(); + if (entity == null) return; - for (org.bukkit.entity.Entity passenger : entity.getPassengers()) { - entity.removePassenger(passenger); - if (passenger instanceof org.bukkit.entity.Player p && p.getEntityId() == this.playerID) { - Player cePlayer = BukkitAdaptors.adapt(p); - if (cePlayer != null && cePlayer.entityID() == playerID()) { - cePlayer.setSeat(null); - onDismount(cePlayer); - } - } - } - entity.remove(); - furniture.removeSeatEntity(playerID()); - furniture.removeOccupiedSeat(vector3f()); - } + for (org.bukkit.entity.Entity passenger : entity.getPassengers()) { + entity.removePassenger(passenger); + if (passenger instanceof org.bukkit.entity.Player p && p.getEntityId() == this.playerID) { + Player cePlayer = BukkitAdaptors.adapt(p); + if (cePlayer != null && cePlayer.entityID() == playerID()) { + cePlayer.setSeat(null); + onDismount(cePlayer); + } + } + } + entity.remove(); + furniture.removeSeatEntity(playerID()); + furniture.removeOccupiedSeat(vector3f()); + } - public boolean destroyed() { - return destroyed; - } + public boolean destroyed() { + return destroyed; + } - @Override - public BukkitFurniture furniture() { - return this.furniture; - } + @Override + public BukkitFurniture furniture() { + return this.furniture; + } - @Override - public Vector3f vector3f() { - return this.vector3f; - } + @Override + public Vector3f vector3f() { + return this.vector3f; + } - @Override - public int playerID() { - return this.playerID; - } + @Override + public int playerID() { + return this.playerID; + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/BukkitSeatTypes.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/BukkitSeatTypes.java index 31ccc875c..2e73c8707 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/BukkitSeatTypes.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/BukkitSeatTypes.java @@ -4,9 +4,9 @@ import net.momirealms.craftengine.core.entity.furniture.SeatType; public class BukkitSeatTypes extends SeatType { - public static void init() { - register(SIT, SitSeat.FACTORY); - register(LAY, LaySeat.FACTORY); - register(CRAWL, CrawlSeat.FACTORY); - } + public static void init() { + register(SIT, SitSeat.FACTORY); + register(LAY, LaySeat.FACTORY); + register(CRAWL, CrawlSeat.FACTORY); + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/CrawlSeat.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/CrawlSeat.java index 9ed148529..9692e578b 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/CrawlSeat.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/CrawlSeat.java @@ -33,124 +33,124 @@ import java.util.UUID; import java.util.function.Consumer; public class CrawlSeat extends AbstractSeat { - public static final SeatFactory FACTORY = new Factory(); - private static final List visualData = new ArrayList<>(); - private final boolean limitPlayerRotation; + public static final SeatFactory FACTORY = new Factory(); + private static final List visualData = new ArrayList<>(); + private final boolean limitPlayerRotation; - static { - ShulkerData.NoGravity.addEntityDataIfNotDefaultValue(true, visualData); - ShulkerData.Silent.addEntityDataIfNotDefaultValue(true, visualData); - ShulkerData.MobFlags.addEntityDataIfNotDefaultValue((byte) 0x01, visualData); - ShulkerData.SharedFlags.addEntityDataIfNotDefaultValue((byte) 0x20, visualData); - } + static { + ShulkerData.NoGravity.addEntityDataIfNotDefaultValue(true, visualData); + ShulkerData.Silent.addEntityDataIfNotDefaultValue(true, visualData); + ShulkerData.MobFlags.addEntityDataIfNotDefaultValue((byte) 0x01, visualData); + ShulkerData.SharedFlags.addEntityDataIfNotDefaultValue((byte) 0x20, visualData); + } - public CrawlSeat(Vector3f offset, float yaw, boolean limitPlayerRotation) { - super(offset, yaw); - this.limitPlayerRotation = limitPlayerRotation; - } + public CrawlSeat(Vector3f offset, float yaw, boolean limitPlayerRotation) { + super(offset, yaw); + this.limitPlayerRotation = limitPlayerRotation; + } - @Override - public SeatEntity spawn(Player player, Furniture furniture) { - return spawn((org.bukkit.entity.Player) player.platformPlayer(), furniture); - } + @Override + public SeatEntity spawn(Player player, Furniture furniture) { + return spawn((org.bukkit.entity.Player) player.platformPlayer(), furniture); + } - public SeatEntity spawn(org.bukkit.entity.Player player, Furniture furniture) { - Location location = ((BukkitFurniture)furniture).calculateSeatLocation(this); + public SeatEntity spawn(org.bukkit.entity.Player player, Furniture furniture) { + Location location = ((BukkitFurniture) furniture).calculateSeatLocation(this); - org.bukkit.entity.Entity seatEntity = BukkitFurniture.spawnSeatEntity(furniture, player.getWorld(), location, this.limitPlayerRotation, null); - if (!seatEntity.addPassenger(player)) { - return null; - }; + org.bukkit.entity.Entity seatEntity = BukkitFurniture.spawnSeatEntity(furniture, player.getWorld(), location, this.limitPlayerRotation, null); + if (!seatEntity.addPassenger(player)) { + return null; + } - // Fix Rider Pose - int entityId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(); - List packets = new ArrayList<>(); - packets.add(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(entityId, UUID.randomUUID(), location.getX(), location.getY(), location.getZ(), location.getPitch(), location.getYaw(), - MEntityTypes.SHULKER, 0, CoreReflections.instance$Vec3$Zero, 0)); - packets.add(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityId, List.copyOf(visualData))); + // Fix Rider Pose + int entityId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(); + List packets = new ArrayList<>(); + packets.add(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(entityId, UUID.randomUUID(), location.getX(), location.getY(), location.getZ(), location.getPitch(), location.getYaw(), + MEntityTypes.SHULKER, 0, CoreReflections.instance$Vec3$Zero, 0)); + packets.add(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityId, List.copyOf(visualData))); - try { - if (VersionHelper.isOrAbove1_20_5()) { - Object attributeInstance = CoreReflections.constructor$AttributeInstance.newInstance(MAttributeHolders.SCALE, (Consumer) (o) -> {}); - CoreReflections.method$AttributeInstance$setBaseValue.invoke(attributeInstance, 0.6); - packets.add( - NetworkReflections.constructor$ClientboundUpdateAttributesPacket0 - .newInstance(entityId, Collections.singletonList(attributeInstance)) - ); - packets.add(FastNMS.INSTANCE.constructor$ClientboundSetPassengersPacket(seatEntity.getEntityId(), entityId)); - } - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to add crawl seat attributes", e); - } + try { + if (VersionHelper.isOrAbove1_20_5()) { + Object attributeInstance = CoreReflections.constructor$AttributeInstance.newInstance(MAttributeHolders.SCALE, (Consumer) (o) -> {}); + CoreReflections.method$AttributeInstance$setBaseValue.invoke(attributeInstance, 0.6); + packets.add( + NetworkReflections.constructor$ClientboundUpdateAttributesPacket0 + .newInstance(entityId, Collections.singletonList(attributeInstance)) + ); + packets.add(FastNMS.INSTANCE.constructor$ClientboundSetPassengersPacket(seatEntity.getEntityId(), entityId)); + } + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to add crawl seat attributes", e); + } - Object bundle = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(packets); - BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player); - serverPlayer.sendPacket(bundle, true); + Object bundle = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(packets); + BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player); + serverPlayer.sendPacket(bundle, true); - // Sync Pose - player.setPose(Pose.SWIMMING, true); - Object syncPosePacket = null; - try { - Object playerData = CoreReflections.method$Entity$getEntityData.invoke(serverPlayer.serverPlayer()); - Object dataItem = CoreReflections.method$SynchedEntityData$getItem.invoke(playerData, PlayerData.Pose.entityDataAccessor()); - Object dataValue = CoreReflections.method$SynchedEntityData$DataItem$value.invoke(dataItem); - syncPosePacket = FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(serverPlayer.entityID(), List.of(dataValue)); - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to construct sync pose packet", e); - } + // Sync Pose + player.setPose(Pose.SWIMMING, true); + Object syncPosePacket = null; + try { + Object playerData = CoreReflections.method$Entity$getEntityData.invoke(serverPlayer.serverPlayer()); + Object dataItem = CoreReflections.method$SynchedEntityData$getItem.invoke(playerData, PlayerData.Pose.entityDataAccessor()); + Object dataValue = CoreReflections.method$SynchedEntityData$DataItem$value.invoke(dataItem); + syncPosePacket = FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(serverPlayer.entityID(), List.of(dataValue)); + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to construct sync pose packet", e); + } - Object finalSyncPosePacket = syncPosePacket; - BukkitCraftEngine.instance().scheduler().sync().runLater(() -> { - serverPlayer.sendPacket(finalSyncPosePacket, true); - for (org.bukkit.entity.Player p : PlayerUtils.getTrackedBy(player)) { - BukkitNetworkManager.instance().sendPacket(BukkitAdaptors.adapt(p), finalSyncPosePacket, true); - } - }, 1, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4); + Object finalSyncPosePacket = syncPosePacket; + BukkitCraftEngine.instance().scheduler().sync().runLater(() -> { + serverPlayer.sendPacket(finalSyncPosePacket, true); + for (org.bukkit.entity.Player p : PlayerUtils.getTrackedBy(player)) { + BukkitNetworkManager.instance().sendPacket(BukkitAdaptors.adapt(p), finalSyncPosePacket, true); + } + }, 1, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4); - return new CrawlEntity(seatEntity, furniture, offset(), player.getEntityId(), entityId, syncPosePacket); - } + return new CrawlEntity(seatEntity, furniture, offset(), player.getEntityId(), entityId, syncPosePacket); + } - private static class CrawlEntity extends BukkitSeatEntity { - private final int entityId; - private final Object syncPosePacket; + private static class CrawlEntity extends BukkitSeatEntity { + private final int entityId; + private final Object syncPosePacket; - public CrawlEntity(Entity entity, Furniture furniture, Vector3f vector3f, int playerID, int entityId, Object fixPosePacket) { - super(entity, furniture, vector3f, playerID); - this.entityId = entityId; - this.syncPosePacket = fixPosePacket; - } + public CrawlEntity(Entity entity, Furniture furniture, Vector3f vector3f, int playerID, int entityId, Object fixPosePacket) { + super(entity, furniture, vector3f, playerID); + this.entityId = entityId; + this.syncPosePacket = fixPosePacket; + } - @Override - public void add(NetWorkUser from, NetWorkUser to) { - to.sendPacket(syncPosePacket, false); - } + @Override + public void add(NetWorkUser from, NetWorkUser to) { + to.sendPacket(syncPosePacket, false); + } - @Override - public void onDismount(Player player) { - if (player == null) return; - org.bukkit.entity.Player bukkitPlayer = (org.bukkit.entity.Player) player.platformPlayer(); - bukkitPlayer.setPose(Pose.STANDING, false); - try { - Object packet = NetworkReflections.constructor$ClientboundRemoveEntitiesPacket.newInstance((Object) new int[]{entityId}); - player.sendPacket(packet, false); - } catch (Exception e) { - BukkitCraftEngine.instance().logger().warn("Failed to dismount from CrawlEntity", e); - } - } + @Override + public void onDismount(Player player) { + if (player == null) return; + org.bukkit.entity.Player bukkitPlayer = (org.bukkit.entity.Player) player.platformPlayer(); + bukkitPlayer.setPose(Pose.STANDING, false); + try { + Object packet = NetworkReflections.constructor$ClientboundRemoveEntitiesPacket.newInstance((Object) new int[]{entityId}); + player.sendPacket(packet, false); + } catch (Exception e) { + BukkitCraftEngine.instance().logger().warn("Failed to dismount from CrawlEntity", e); + } + } - @Override - public Key type() { - return SeatType.CRAWL; - } - } + @Override + public Key type() { + return SeatType.CRAWL; + } + } - public static class Factory implements SeatFactory { + public static class Factory implements SeatFactory { - @Override - public Seat create(List args) { - if (args.size() == 1) return new CrawlSeat(MiscUtils.getAsVector3f(args.getFirst(), "seats"), 0, false); - return new CrawlSeat(MiscUtils.getAsVector3f(args.getFirst(), "seats"), Float.parseFloat(args.get(1)), true); - } - } + @Override + public Seat create(List args) { + if (args.size() == 1) return new CrawlSeat(MiscUtils.getAsVector3f(args.getFirst(), "seats"), 0, false); + return new CrawlSeat(MiscUtils.getAsVector3f(args.getFirst(), "seats"), Float.parseFloat(args.get(1)), true); + } + } } \ No newline at end of file diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/LaySeat.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/LaySeat.java index 5822f84d1..b731ff96e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/LaySeat.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/LaySeat.java @@ -51,628 +51,631 @@ import java.util.concurrent.TimeUnit; import static net.momirealms.craftengine.core.plugin.network.ProtocolVersion.V1_21_2; public class LaySeat extends AbstractSeat { - public static final SeatFactory FACTORY = new Factory(); - private static final List> emptyEquipments; - private static final List> emptyBukkitEquipments; - private static Method method$InventoryView$convertSlot; - private static Method method$InventoryView$getTopInventory; - private static Method method$InventoryView$getType; - private final Direction facing; - private final boolean sleep; - private final boolean phantom; - - static { - if (!VersionHelper.isOrAbove1_21_1()) { - method$InventoryView$convertSlot = ReflectionUtils.getMethod(InventoryView.class, new String[]{"convertSlot"}, int.class); - method$InventoryView$getTopInventory = ReflectionUtils.getMethod(InventoryView.class, new String[]{"getTopInventory"}); - method$InventoryView$getType = ReflectionUtils.getMethod(Inventory.class, new String[]{"getType"}); - } - emptyEquipments = List.of( - Pair.of(CoreReflections.instance$EquipmentSlot$MAINHAND, MItems.AIR$Item), - Pair.of(CoreReflections.instance$EquipmentSlot$OFFHAND, MItems.AIR$Item), - Pair.of(CoreReflections.instance$EquipmentSlot$HEAD, MItems.AIR$Item), - Pair.of(CoreReflections.instance$EquipmentSlot$CHEST, MItems.AIR$Item), - Pair.of(CoreReflections.instance$EquipmentSlot$LEGS, MItems.AIR$Item), - Pair.of(CoreReflections.instance$EquipmentSlot$FEET, MItems.AIR$Item) - ); - emptyBukkitEquipments = List.of( - Pair.of(CoreReflections.instance$EquipmentSlot$MAINHAND, ItemUtils.AIR), - Pair.of(CoreReflections.instance$EquipmentSlot$OFFHAND, ItemUtils.AIR), - Pair.of(CoreReflections.instance$EquipmentSlot$HEAD, ItemUtils.AIR), - Pair.of(CoreReflections.instance$EquipmentSlot$CHEST, ItemUtils.AIR), - Pair.of(CoreReflections.instance$EquipmentSlot$LEGS, ItemUtils.AIR), - Pair.of(CoreReflections.instance$EquipmentSlot$FEET, ItemUtils.AIR) - ); - } - - public LaySeat(Vector3f offset, Direction facing, boolean sleep, boolean phantom) { - super(offset, 0); - this.facing = facing; - this.sleep = sleep; - this.phantom = phantom; - } - - @Override - @SuppressWarnings({"unchecked", "rawtypes"}) - public SeatEntity spawn(Player cePlayer, Furniture furniture) { - Location loc = ((BukkitFurniture)furniture).calculateSeatLocation(this); - - org.bukkit.entity.Player player = (org.bukkit.entity.Player) cePlayer.platformPlayer(); - Object serverPlayer = cePlayer.serverPlayer(); - - // Pose offset nearly same as vanilla - AttributeInstance attribute = VersionHelper.isOrAbove1_21_2() ? player.getAttribute(Attribute.SCALE) : null; - double scale = attribute == null ? 1 : attribute.getValue(); - loc.add(0, 0.08525 * scale, 0); - - try { - List packets = new ArrayList<>(); - // NPC - Object server = CoreReflections.method$MinecraftServer$getServer.invoke(null); - Object level = FastNMS.INSTANCE.field$CraftWorld$ServerLevel(player.getWorld()); - UUID uuid = UUID.randomUUID(); - Object npcProfile = CoreReflections.constructor$GameProfile.newInstance(uuid, player.getName()); - Object playerProfile = CoreReflections.method$ServerPlayer$getGameProfile.invoke(serverPlayer); - - Multimap properties = (Multimap) CoreReflections.method$GameProfile$getProperties.invoke(npcProfile); - properties.putAll((Multimap) CoreReflections.method$GameProfile$getProperties.invoke(playerProfile)); - - Object npc; - if (VersionHelper.isOrAbove1_20_2()) { - Object clientInfo = CoreReflections.method$ServerPlayer$clientInformation.invoke(serverPlayer); - npc = CoreReflections.constructor$ServerPlayer.newInstance(server, level, npcProfile, clientInfo); - } else { - npc = CoreReflections.constructor$ServerPlayer.newInstance(server, level, npcProfile); - } - int npcId = FastNMS.INSTANCE.method$Entity$getId(npc); - CoreReflections.method$Entity$absSnapTo.invoke(npc, loc.getX(), loc.getY(), loc.getZ(), 0, 0); - Object npcSpawnPacket; - if (!VersionHelper.isOrAbove1_20_2()) { - npcSpawnPacket = NetworkReflections.constructor$ClientboundAddPlayerPacket.newInstance(npc); - } else { - npcSpawnPacket = FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(npcId, uuid, - loc.getX(), loc.getY(), loc.getZ(), 0, 0, - MEntityTypes.PLAYER, 0, CoreReflections.instance$Vec3$Zero, 0); - } - - // Info - EnumSet enumSet = EnumSet.noneOf((Class) NetworkReflections.clazz$ClientboundPlayerInfoUpdatePacket$Action); - enumSet.add(NetworkReflections.instance$ClientboundPlayerInfoUpdatePacket$Action$ADD_PLAYER); - Object entry; - if (VersionHelper.isOrAbove1_21_4()) { - entry = NetworkReflections.constructor$ClientBoundPlayerInfoUpdatePacket$Entry.newInstance( - uuid, npcProfile, false, 0, CoreReflections.instance$GameType$SURVIVAL, null, true, 0, null); - } else if (VersionHelper.isOrAbove1_21_3()) { - entry = NetworkReflections.constructor$ClientBoundPlayerInfoUpdatePacket$Entry.newInstance( - uuid, npcProfile, false, 0, CoreReflections.instance$GameType$SURVIVAL, null, 0, null); - } else { - entry = NetworkReflections.constructor$ClientBoundPlayerInfoUpdatePacket$Entry.newInstance( - uuid, npcProfile, false, 0, CoreReflections.instance$GameType$SURVIVAL, null, null); - } - Object npcInfoPacket = FastNMS.INSTANCE.constructor$ClientboundPlayerInfoUpdatePacket(enumSet, Collections.singletonList(entry)); - - // Bed - Direction bedDir = Direction.fromYaw(loc.getYaw() + Direction.getYaw(facing)); - if (bedDir == Direction.EAST || bedDir == Direction.WEST) bedDir = bedDir.opposite(); - BlockData bedData = Material.WHITE_BED.createBlockData("[facing=" + bedDir.name().toLowerCase() + ",part=head]"); - Location bedLoc = loc.clone(); - bedLoc.setY(bedLoc.getWorld().getMinHeight()); - Object bedPos = LocationUtils.toBlockPos(new BlockPos(bedLoc.getBlockX(), bedLoc.getBlockY(), bedLoc.getBlockZ())); - Object blockState = BlockStateUtils.blockDataToBlockState(bedData); - Object bedPacket = NetworkReflections.constructor$ClientboundBlockUpdatePacket.newInstance(bedPos, blockState); - - // Data - Object npcData = CoreReflections.method$Entity$getEntityData.invoke(npc); - Object playerData = CoreReflections.method$Entity$getEntityData.invoke(serverPlayer); - CoreReflections.method$Entity$setInvisible.invoke(serverPlayer, true); - CoreReflections.method$SynchedEntityData$set.invoke(npcData, PlayerData.Pose.entityDataAccessor(), CoreReflections.instance$Pose$SLEEPING); - CoreReflections.method$SynchedEntityData$set.invoke(npcData, LivingEntityData.SleepingPos.entityDataAccessor(), Optional.of(bedPos)); - CoreReflections.method$SynchedEntityData$set.invoke(npcData, PlayerData.PlayerModeCustomisation.entityDataAccessor(), CoreReflections.method$SynchedEntityData$get.invoke(playerData, PlayerData.PlayerModeCustomisation.entityDataAccessor())); - CoreReflections.method$SynchedEntityData$set.invoke(npcData, PlayerData.PlayerMainHand.entityDataAccessor(), CoreReflections.method$SynchedEntityData$get.invoke(playerData, PlayerData.PlayerMainHand.entityDataAccessor())); - CoreReflections.method$SynchedEntityData$set.invoke(npcData, PlayerData.ShoulderLeft.entityDataAccessor(), CoreReflections.method$SynchedEntityData$get.invoke(playerData, PlayerData.ShoulderLeft.entityDataAccessor())); - CoreReflections.method$SynchedEntityData$set.invoke(npcData, PlayerData.ShoulderRight.entityDataAccessor(), CoreReflections.method$SynchedEntityData$get.invoke(playerData, PlayerData.ShoulderRight.entityDataAccessor())); - CoreReflections.method$SynchedEntityData$set.invoke(playerData, PlayerData.ShoulderLeft.entityDataAccessor(), CoreReflections.instance$CompoundTag$Empty); - CoreReflections.method$SynchedEntityData$set.invoke(playerData, PlayerData.ShoulderRight.entityDataAccessor(), CoreReflections.instance$CompoundTag$Empty); - - // SetData - CoreReflections.method$Entity$setInvisible.invoke(serverPlayer, true); - Object npcDataPacket = FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket( - npcId, (List) CoreReflections.method$SynchedEntityData$packDirty.invoke(npcData) - ); - - // Remove - Object npcRemovePacket = NetworkReflections.constructor$ClientboundRemoveEntitiesPacket.newInstance((Object) new int[]{npcId}); - - // TP - Object npcTeleportPacket; - if (VersionHelper.isOrAbove1_21_3()) { - Object positionMoveRotation = CoreReflections.method$PositionMoveRotation$of.invoke(null, npc); - npcTeleportPacket = NetworkReflections.constructor$ClientboundTeleportEntityPacket.newInstance(npcId, positionMoveRotation, Set.of(), false); - } else { - npcTeleportPacket = NetworkReflections.constructor$ClientboundTeleportEntityPacket.newInstance(npc); - } - - // Equipment - Object emptyEquipPacket = NetworkReflections.constructor$ClientboundSetEquipmentPacket.newInstance(player.getEntityId(), emptyEquipments); - - Map equipments = new HashMap<>(); - EntityEquipment equipment = player.getEquipment(); - for (org.bukkit.inventory.EquipmentSlot slot : org.bukkit.inventory.EquipmentSlot.values()) { - if ((!slot.isHand() && !slot.isArmor()) - || (VersionHelper.isOrAbove1_20_5() && slot == org.bukkit.inventory.EquipmentSlot.BODY)) { - continue; - } - EquipmentSlot slotId = EntityUtils.toCEEquipmentSlot(slot); - ItemStack item = equipment.getItem(slot); - equipments.put(slotId, item); - } - List> npcSlots = new ArrayList<>(); - equipments.forEach((slot, item) -> npcSlots.add(Pair.of(EntityUtils.fromEquipmentSlot(slot), FastNMS.INSTANCE.method$CraftItemStack$asNMSCopy(item)))); - Object fullEquipPacket = NetworkReflections.constructor$ClientboundSetEquipmentPacket.newInstance(npcId, npcSlots); - - // Animation - Object npcLeftAnimatePacket = NetworkReflections.constructor$ClientboundAnimatePacket.newInstance(npc, 0); - Object npcRightAnimatePacket = NetworkReflections.constructor$ClientboundAnimatePacket.newInstance(npc, 3); - - packets.add(npcInfoPacket); - packets.add(npcSpawnPacket); - packets.add(bedPacket); - packets.add(npcDataPacket); - packets.add(npcTeleportPacket); - packets.add(emptyEquipPacket); - packets.add(npcLeftAnimatePacket); - Object npcInitPackets = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(packets); - - // Spawn - org.bukkit.entity.Entity seatEntity = BukkitFurniture.spawnSeatEntity(furniture, player.getWorld(), loc, false, null); - if (!seatEntity.addPassenger(player)) { // 0.5 higher - return null; - } - cePlayer.sendPacket(npcInitPackets, true); - cePlayer.sendPacket(fullEquipPacket, true); - if (player.getY() > 0 && cePlayer.protocolVersion().isVersionNewerThan(V1_21_2)) { - BukkitCraftEngine.instance().scheduler().asyncLater(() -> cePlayer.sendPacket(npcTeleportPacket, true), - 50, TimeUnit.MILLISECONDS); // over height 0 cost 2 npcTeleportPacket - } - - for (org.bukkit.entity.Player o : PlayerUtils.getTrackedBy(player)) { - NetWorkUser tracker = BukkitNetworkManager.instance().getOnlineUser(o); - tracker.sendPacket(npcInitPackets, false); - tracker.sendPacket(fullEquipPacket, false); - if (player.getY() > 0 && tracker.protocolVersion().isVersionNewerThan(V1_21_2)) { - BukkitCraftEngine.instance().scheduler().asyncLater(() -> tracker.sendPacket(npcTeleportPacket, false), - 50, TimeUnit.MILLISECONDS); - } - } - - // HeadRot - Direction npcDir = bedDir.opposite(); - - if (sleep) { - player.setSleepingIgnored(true); - } - - if (phantom) { - player.setStatistic(Statistic.TIME_SINCE_REST, 0); - } - - return new LayEntity( - seatEntity, - furniture, - this.offset(), - npcInitPackets, - npcRemovePacket, - npcTeleportPacket, - npcLeftAnimatePacket, - npcRightAnimatePacket, - (BukkitServerPlayer) cePlayer, - bedLoc, - npc, - npcId, - npcDir, - equipments, - emptyEquipPacket, - fullEquipPacket, - sleep - ); - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to spawn LaySeat", e); - } - return null; - } - - private static class LayEntity extends BukkitSeatEntity { - private final Object npcInitPackets; - private final Object npcRemovePacket; - private final Object npcTPPacket; - private final Object npcLeftAnimatePacket; - private final Object npcRightAnimatePacket; - private final BukkitServerPlayer serverPlayer; - private final Object npc; - private final Location bedLoc; - private final int npcID; - private final Direction npcDir; - - // Equipment - private final PlayerMonitorTask task; - private final Map equipments; - private final Object emptyEquipPacket; - private Object updateEquipPacket; - private Object fullEquipPacket; - - private final boolean sleep; - private Object npcRotHeadPacket; - private Object npcDataPacket; - - public LayEntity( - org.bukkit.entity.Entity entity, - Furniture furniture, - Vector3f vector, - Object npcInitPackets, - Object npcRemovePacket, - Object npcTPPacket, - Object npcLeftAnimatePacket, - Object npcRightAnimatePacket, - BukkitServerPlayer serverPlayer, - Location bedLoc, - Object npc, - int npcID, - Direction npcDir, - Map equipments, - Object emptyEquipPacket, - Object fullEquipPacket, - boolean sleep - ) { - super(entity, furniture, vector, serverPlayer.entityID()); - this.npcInitPackets = npcInitPackets; - this.npcRemovePacket = npcRemovePacket; - this.npcTPPacket = npcTPPacket; - this.npcLeftAnimatePacket = npcLeftAnimatePacket; - this.npcRightAnimatePacket = npcRightAnimatePacket; - this.serverPlayer = serverPlayer; - this.bedLoc = bedLoc; - this.npc = npc; - this.npcID = npcID; - this.npcDir = npcDir; - - this.task = new PlayerMonitorTask(); - this.equipments = equipments; - this.emptyEquipPacket = emptyEquipPacket; - this.fullEquipPacket = fullEquipPacket; - - this.sleep = sleep; - } - - @Override - public void add(NetWorkUser from, NetWorkUser to) { - to.sendPacket(this.npcInitPackets, false); - to.sendPacket(this.fullEquipPacket, false); - to.sendPacket(this.npcRotHeadPacket, false); - if (npcDataPacket != null) to.sendPacket(this.npcDataPacket, false); - if (serverPlayer.y() > 0 && to.protocolVersion().isVersionNewerThan(V1_21_2)) { - BukkitCraftEngine.instance().scheduler().asyncLater(() -> - to.sendPacket(this.npcTPPacket, false), 50, TimeUnit.MILLISECONDS); - } - } - - @Override - public boolean handleEntitiesRemove(NetWorkUser user, IntList entityIds) { - entityIds.add(npcID); - return true; - } - - @Override - public void onDismount(Player player) { - if (player == null) return; - - this.task.task.cancel(); - - org.bukkit.entity.Player bukkitPlayer = (org.bukkit.entity.Player) player.platformPlayer(); - Object blockPos = LocationUtils.toBlockPos(bedLoc.getBlockX(), bedLoc.getBlockY(), bedLoc.getBlockZ()); - Object blockState = BlockStateUtils.blockDataToBlockState(bedLoc.getBlock().getBlockData()); - - try { - Object blockUpdatePacket = NetworkReflections.constructor$ClientboundBlockUpdatePacket.newInstance(blockPos, blockState); - player.sendPacket(this.npcRemovePacket, true); - player.sendPacket(blockUpdatePacket, true); - - if (bukkitPlayer.getPotionEffect(PotionEffectType.INVISIBILITY) == null) { - CoreReflections.method$Entity$setInvisible.invoke(serverPlayer.serverPlayer(), false); - } - - Object npcData = CoreReflections.method$Entity$getEntityData.invoke(npc); - Object playerData = CoreReflections.method$Entity$getEntityData.invoke(serverPlayer.serverPlayer()); - CoreReflections.method$SynchedEntityData$set.invoke( - playerData, - PlayerData.ShoulderLeft.entityDataAccessor(), - CoreReflections.method$SynchedEntityData$get.invoke(npcData, PlayerData.ShoulderLeft.entityDataAccessor()) - ); - CoreReflections.method$SynchedEntityData$set.invoke( - playerData, - PlayerData.ShoulderRight.entityDataAccessor(), - CoreReflections.method$SynchedEntityData$get.invoke(npcData, PlayerData.ShoulderRight.entityDataAccessor()) - ); - if (bukkitPlayer.getPotionEffect(PotionEffectType.INVISIBILITY) == null) { - CoreReflections.method$Entity$setInvisible.invoke(serverPlayer.serverPlayer(), false); - } - - bukkitPlayer.updateInventory(); - - if (sleep) { - bukkitPlayer.setSleepingIgnored(false); - } - - Object fullSlots = NetworkReflections.method$ClientboundSetEquipmentPacket$getSlots.invoke(this.fullEquipPacket); - Object recoverEquip = NetworkReflections.constructor$ClientboundSetEquipmentPacket.newInstance(bukkitPlayer.getEntityId(), fullSlots); - - for (org.bukkit.entity.Player o : PlayerUtils.getTrackedBy(bukkitPlayer)) { - BukkitServerPlayer tracker = (BukkitServerPlayer) BukkitNetworkManager.instance().getOnlineUser(o); - tracker.entityPacketHandlers().remove(playerID()); - tracker.sendPacket(this.npcRemovePacket, false); - tracker.sendPacket(blockUpdatePacket, false); - tracker.sendPacket(recoverEquip, false); - } - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to dismount from LayEntity", e); - } - } - - public void equipmentChange(Map equipmentChanges, int previousSlot) { - org.bukkit.entity.Player player = serverPlayer.platformPlayer(); - List> changedSlots = new ArrayList<>(); - - for (Map.Entry entry : equipmentChanges.entrySet()) { - Object slotId = EntityUtils.fromEquipmentSlot(entry.getKey()); - Object itemStack = FastNMS.INSTANCE.method$CraftItemStack$asNMSCopy(entry.getValue()); - changedSlots.add(Pair.of(slotId, itemStack)); - } - this.equipments.putAll(equipmentChanges); - - List> allSlots = new ArrayList<>(); - equipments.forEach((slot, item) -> - allSlots.add(Pair.of(EntityUtils.fromEquipmentSlot(slot), FastNMS.INSTANCE.method$CraftItemStack$asNMSCopy(item)))); - try { - this.updateEquipPacket = NetworkReflections.constructor$ClientboundSetEquipmentPacket.newInstance(npcID, changedSlots); - this.fullEquipPacket = NetworkReflections.constructor$ClientboundSetEquipmentPacket.newInstance(npcID, allSlots); - if (previousSlot != -1) { - player.updateInventory(); - } - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handle equipmentChange", e); - } - - serverPlayer.sendPacket(this.emptyEquipPacket, false); - serverPlayer.sendPacket(this.updateEquipPacket, false); - - for (org.bukkit.entity.Player o : PlayerUtils.getTrackedBy(player)) { - BukkitNetworkManager.instance().getOnlineUser(o).sendPacket(this.updateEquipPacket, false); - } - } - - @Override - public void handleSetEquipment(NetWorkUser user, ByteBufPacketEvent event, Object slots) { - if (emptyBukkitEquipments.equals(slots)) return; - event.setCancelled(true); - } - - @Override - public void handleContainerSetSlot(NetWorkUser user, NMSPacketEvent event, Object packet) { - try { - int slot = (int) NetworkReflections.method$ClientboundContainerSetSlotPacket$getSlot.invoke(packet); - org.bukkit.entity.Player player = (org.bukkit.entity.Player) user.platformPlayer(); - - int convertSlot; - boolean isPlayerInv; - - if (!VersionHelper.isOrAbove1_21_1()) { - Object openInventory = player.getOpenInventory(); - convertSlot = (int) method$InventoryView$convertSlot.invoke(openInventory, slot); - Object topInventory = method$InventoryView$getTopInventory.invoke(openInventory); - Object type = method$InventoryView$getType.invoke(topInventory); - isPlayerInv = type == InventoryType.CRAFTING; - } else { - convertSlot = player.getOpenInventory().convertSlot(slot); - isPlayerInv = player.getOpenInventory().getTopInventory().getType() == InventoryType.CRAFTING; - } - - if (!(convertSlot == player.getInventory().getHeldItemSlot() || (isPlayerInv && (slot == 45 || (slot >= 5 && slot <= 8))))) return; - int containerId = (int) NetworkReflections.method$ClientboundContainerSetSlotPacket$getContainerId.invoke(packet); - int stateId = (int) NetworkReflections.method$ClientboundContainerSetSlotPacket$getStateId.invoke(packet); - Object replacePacket = NetworkReflections.constructor$ClientboundContainerSetSlotPacket.newInstance(containerId, stateId, slot, MItems.AIR$Item); - event.replacePacket(replacePacket); - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to handleContainerSetSlotPacket", e); - } - } - - @Override - public void event(Player player, Object event) { - if (event instanceof PlayerAnimationEvent e) { - try { - Object animatePacket; - if (e.getAnimationType() == PlayerAnimationType.ARM_SWING) { - animatePacket = npcLeftAnimatePacket; - } else { - animatePacket = npcRightAnimatePacket; - } - serverPlayer.sendPacket(animatePacket, true); - for (org.bukkit.entity.Player o : PlayerUtils.getTrackedBy(serverPlayer.platformPlayer())) { - BukkitNetworkManager.instance().getOnlineUser(o).sendPacket(animatePacket, true); - } - } catch (Exception exception) { - CraftEngine.instance().logger().warn("Failed to handle PlayerAnimationEvent", exception); - } - } else if (event instanceof PlayerItemHeldEvent e) { - ItemStack item = e.getPlayer().getInventory().getItem(e.getNewSlot()); - if (item == null) item = ItemUtils.AIR; - - equipmentChange(Map.of(EquipmentSlot.MAIN_HAND, item), e.getPreviousSlot()); - } - } - - @Override - public Key type() { - return SeatType.LAY; - } - - private class PlayerMonitorTask implements Runnable { - - private final SchedulerTask task; - private float lastYaw; - - private PlayerMonitorTask() { - org.bukkit.entity.Player p = serverPlayer.platformPlayer(); - BukkitCraftEngine plugin = BukkitCraftEngine.instance(); - if (VersionHelper.isFolia()) { - this.task = new FoliaTask(p.getScheduler().runAtFixedRate(plugin.javaPlugin(), (t) -> this.run(), () -> {}, 1, 1)); - } else { - this.task = plugin.scheduler().sync().runRepeating(this, 0, 1); - } - } - - @Override - public void run() { - org.bukkit.entity.Player player = serverPlayer.platformPlayer(); - if (player == null || !player.isValid()) { - this.task.cancel(); - return; - } - - // Invisible - updateNpcInvisible(); - try { - if (!player.isInvisible()) CoreReflections.method$Entity$setInvisible.invoke(serverPlayer.serverPlayer(), true); - } catch (Exception exception) { - CraftEngine.instance().logger().warn("Failed to set shared flag", exception); - } - - // Sync Rotation - float playerYaw = player.getYaw(); - if (lastYaw != playerYaw) { - updateNpcYaw(playerYaw); - serverPlayer.sendPacket(npcRotHeadPacket, false); - for (org.bukkit.entity.Player o : PlayerUtils.getTrackedBy(player)) { - NetWorkUser tracker = BukkitNetworkManager.instance().getOnlineUser(o); - tracker.sendPacket(npcRotHeadPacket, true); - } - this.lastYaw = playerYaw; - } - - // Sync Equipment - Map newEquipments = new HashMap<>(); - for (EquipmentSlot slot : EquipmentSlot.values()) { - if (!slot.isHand() && !slot.isPlayerArmor()) continue; - ItemStack newItem = player.getEquipment().getItem(EntityUtils.toBukkitEquipmentSlot(slot)); - try { - ItemStack item = equipments.get(slot); - boolean isChange = !newItem.equals(item); - if (isChange) { - newEquipments.put(slot, newItem); - } - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to monitor equipments change", e); - } - } - - if (!newEquipments.isEmpty()) { - equipmentChange(newEquipments, -1); - return; - } - serverPlayer.sendPacket(emptyEquipPacket, false); - } - } - - private void updateNpcYaw(float playerYaw) { - byte packYaw = getRot(playerYaw); - try { - this.npcRotHeadPacket = NetworkReflections.constructor$ClientboundRotateHeadPacket.newInstance(npc, packYaw); - } catch (Exception exception) { - CraftEngine.instance().logger().warn("Failed to sync NPC yaw", exception); - } - } - - private byte getRot(float playerYaw) { - float npcYaw = Direction.getYaw(npcDir); - float centerYaw = normalizeYaw(npcYaw); - float playerYawNorm = normalizeYaw(playerYaw); - - float deltaYaw = normalizeYaw(playerYawNorm - centerYaw); - boolean isBehind = Math.abs(deltaYaw) > 90; - - float mappedYaw; - if (isBehind) { - float rel = Math.abs(deltaYaw) - 180; - mappedYaw = rel * (deltaYaw > 0 ? -1 : 1); - } else { - mappedYaw = deltaYaw; - } - - float finalYaw = Math.max(-45, Math.min(45, mappedYaw)); - return MCUtils.packDegrees(finalYaw); - } - - private float normalizeYaw(float yaw) { - yaw %= 360.0f; - if (yaw < -180.0f) yaw += 360.0f; - if (yaw >= 180.0f) yaw -= 360.0f; - return yaw; - } - - private void updateNpcInvisible() { - try { - org.bukkit.entity.Player player = serverPlayer.platformPlayer(); - if (player.getPotionEffect(PotionEffectType.INVISIBILITY) == null && npcDataPacket != null) { - npcDataPacket = null; - CoreReflections.method$Entity$setInvisible.invoke(npc, false); - Object npcData = CoreReflections.method$Entity$getEntityData.invoke(npc); - Object dataItem = CoreReflections.method$SynchedEntityData$getItem.invoke(npcData, PlayerData.SharedFlags.entityDataAccessor()); - Object dataValue = CoreReflections.method$SynchedEntityData$DataItem$value.invoke(dataItem); - Object packet = FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(npcID, List.of(dataValue)); - serverPlayer.sendPacket(packet, false); - for (org.bukkit.entity.Player o : PlayerUtils.getTrackedBy(player)) { - BukkitNetworkManager.instance().getOnlineUser(o).sendPacket(packet, false); - } - } else if (player.getPotionEffect(PotionEffectType.INVISIBILITY) != null && npcDataPacket == null) { - CoreReflections.method$Entity$setInvisible.invoke(npc, true); - Object npcData = CoreReflections.method$Entity$getEntityData.invoke(npc); - Object dataItem = CoreReflections.method$SynchedEntityData$getItem.invoke(npcData, PlayerData.SharedFlags.entityDataAccessor()); - Object dataValue = CoreReflections.method$SynchedEntityData$DataItem$value.invoke(dataItem); - npcDataPacket = FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(npcID, List.of(dataValue)); - serverPlayer.sendPacket(npcDataPacket, false); - for (org.bukkit.entity.Player o : PlayerUtils.getTrackedBy(player)) { - BukkitNetworkManager.instance().getOnlineUser(o).sendPacket(npcDataPacket, false); - } - } - } catch (Exception e) { - CraftEngine.instance().logger().warn("Failed to updateNpcInvisible", e); - } - } - } - - public static class Factory implements SeatFactory { - - @Override - public Seat create(List args) { - Vector3f offset = MiscUtils.getAsVector3f(args.get(0), "seats"); - Direction facing = args.size() > 1 ? parseFacing(args.get(1)) : Direction.SOUTH; - boolean sleep = args.size() > 2 && Boolean.parseBoolean(args.get(2)); - boolean phantom = args.size() > 4 && Boolean.parseBoolean(args.get(3)); - - if (facing == Direction.NORTH || facing == Direction.SOUTH) { - float temp = offset.x; - offset.x = offset.z; - offset.z = temp; - } - return new LaySeat(offset, facing, sleep, phantom); - } - - private Direction parseFacing(String facing) { - return switch (facing.toLowerCase()) { - case "back" -> Direction.NORTH; - case "left" -> Direction.WEST; - case "right" -> Direction.EAST; - default -> Direction.SOUTH; - }; - } - } + public static final SeatFactory FACTORY = new Factory(); + private static final List> emptyEquipments; + private static final List> emptyBukkitEquipments; + private static Method method$InventoryView$convertSlot; + private static Method method$InventoryView$getTopInventory; + private static Method method$InventoryView$getType; + private final Direction facing; + private final boolean sleep; + private final boolean phantom; + + static { + if (!VersionHelper.isOrAbove1_21_1()) { + method$InventoryView$convertSlot = ReflectionUtils.getMethod(InventoryView.class, new String[]{"convertSlot"}, int.class); + method$InventoryView$getTopInventory = ReflectionUtils.getMethod(InventoryView.class, new String[]{"getTopInventory"}); + method$InventoryView$getType = ReflectionUtils.getMethod(Inventory.class, new String[]{"getType"}); + } + emptyEquipments = List.of( + Pair.of(CoreReflections.instance$EquipmentSlot$MAINHAND, MItems.AIR$Item), + Pair.of(CoreReflections.instance$EquipmentSlot$OFFHAND, MItems.AIR$Item), + Pair.of(CoreReflections.instance$EquipmentSlot$HEAD, MItems.AIR$Item), + Pair.of(CoreReflections.instance$EquipmentSlot$CHEST, MItems.AIR$Item), + Pair.of(CoreReflections.instance$EquipmentSlot$LEGS, MItems.AIR$Item), + Pair.of(CoreReflections.instance$EquipmentSlot$FEET, MItems.AIR$Item) + ); + emptyBukkitEquipments = List.of( + Pair.of(CoreReflections.instance$EquipmentSlot$MAINHAND, ItemUtils.AIR), + Pair.of(CoreReflections.instance$EquipmentSlot$OFFHAND, ItemUtils.AIR), + Pair.of(CoreReflections.instance$EquipmentSlot$HEAD, ItemUtils.AIR), + Pair.of(CoreReflections.instance$EquipmentSlot$CHEST, ItemUtils.AIR), + Pair.of(CoreReflections.instance$EquipmentSlot$LEGS, ItemUtils.AIR), + Pair.of(CoreReflections.instance$EquipmentSlot$FEET, ItemUtils.AIR) + ); + } + + public LaySeat(Vector3f offset, Direction facing, boolean sleep, boolean phantom) { + super(offset, 0); + this.facing = facing; + this.sleep = sleep; + this.phantom = phantom; + } + + @Override + @SuppressWarnings({"unchecked", "rawtypes"}) + public SeatEntity spawn(Player cePlayer, Furniture furniture) { + Location loc = ((BukkitFurniture) furniture).calculateSeatLocation(this); + + org.bukkit.entity.Player player = (org.bukkit.entity.Player) cePlayer.platformPlayer(); + Object serverPlayer = cePlayer.serverPlayer(); + + // Pose offset nearly same as vanilla + AttributeInstance attribute = VersionHelper.isOrAbove1_21_2() ? player.getAttribute(Attribute.SCALE) : null; + double scale = attribute == null ? 1 : attribute.getValue(); + loc.add(0, 0.08525 * scale, 0); + + try { + List packets = new ArrayList<>(); + // NPC + Object server = CoreReflections.method$MinecraftServer$getServer.invoke(null); + Object level = FastNMS.INSTANCE.field$CraftWorld$ServerLevel(player.getWorld()); + UUID uuid = UUID.randomUUID(); + Object npcProfile = CoreReflections.constructor$GameProfile.newInstance(uuid, player.getName()); + Object playerProfile = CoreReflections.method$ServerPlayer$getGameProfile.invoke(serverPlayer); + + Multimap properties = (Multimap) CoreReflections.method$GameProfile$getProperties.invoke(npcProfile); + properties.putAll((Multimap) CoreReflections.method$GameProfile$getProperties.invoke(playerProfile)); + + Object npc; + if (VersionHelper.isOrAbove1_20_2()) { + Object clientInfo = CoreReflections.method$ServerPlayer$clientInformation.invoke(serverPlayer); + npc = CoreReflections.constructor$ServerPlayer.newInstance(server, level, npcProfile, clientInfo); + } else { + npc = CoreReflections.constructor$ServerPlayer.newInstance(server, level, npcProfile); + } + int npcId = FastNMS.INSTANCE.method$Entity$getId(npc); + CoreReflections.method$Entity$absSnapTo.invoke(npc, loc.getX(), loc.getY(), loc.getZ(), 0, 0); + Object npcSpawnPacket; + if (!VersionHelper.isOrAbove1_20_2()) { + npcSpawnPacket = NetworkReflections.constructor$ClientboundAddPlayerPacket.newInstance(npc); + } else { + npcSpawnPacket = FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(npcId, uuid, + loc.getX(), loc.getY(), loc.getZ(), 0, 0, + MEntityTypes.PLAYER, 0, CoreReflections.instance$Vec3$Zero, 0); + } + + // Info + EnumSet enumSet = EnumSet.noneOf((Class) NetworkReflections.clazz$ClientboundPlayerInfoUpdatePacket$Action); + enumSet.add(NetworkReflections.instance$ClientboundPlayerInfoUpdatePacket$Action$ADD_PLAYER); + Object entry; + if (VersionHelper.isOrAbove1_21_4()) { + entry = NetworkReflections.constructor$ClientBoundPlayerInfoUpdatePacket$Entry.newInstance( + uuid, npcProfile, false, 0, CoreReflections.instance$GameType$SURVIVAL, null, true, 0, null); + } else if (VersionHelper.isOrAbove1_21_3()) { + entry = NetworkReflections.constructor$ClientBoundPlayerInfoUpdatePacket$Entry.newInstance( + uuid, npcProfile, false, 0, CoreReflections.instance$GameType$SURVIVAL, null, 0, null); + } else { + entry = NetworkReflections.constructor$ClientBoundPlayerInfoUpdatePacket$Entry.newInstance( + uuid, npcProfile, false, 0, CoreReflections.instance$GameType$SURVIVAL, null, null); + } + Object npcInfoPacket = FastNMS.INSTANCE.constructor$ClientboundPlayerInfoUpdatePacket(enumSet, Collections.singletonList(entry)); + + // Bed + Direction bedDir = Direction.fromYaw(loc.getYaw() + Direction.getYaw(facing)); + if (bedDir == Direction.EAST || bedDir == Direction.WEST) bedDir = bedDir.opposite(); + BlockData bedData = Material.WHITE_BED.createBlockData("[facing=" + bedDir.name().toLowerCase() + ",part=head]"); + Location bedLoc = loc.clone(); + bedLoc.setY(bedLoc.getWorld().getMinHeight()); + Object bedPos = LocationUtils.toBlockPos(new BlockPos(bedLoc.getBlockX(), bedLoc.getBlockY(), bedLoc.getBlockZ())); + Object blockState = BlockStateUtils.blockDataToBlockState(bedData); + Object bedPacket = NetworkReflections.constructor$ClientboundBlockUpdatePacket.newInstance(bedPos, blockState); + + // Data + Object npcData = CoreReflections.method$Entity$getEntityData.invoke(npc); + Object playerData = CoreReflections.method$Entity$getEntityData.invoke(serverPlayer); + CoreReflections.method$Entity$setInvisible.invoke(serverPlayer, true); + CoreReflections.method$SynchedEntityData$set.invoke(npcData, PlayerData.Pose.entityDataAccessor(), CoreReflections.instance$Pose$SLEEPING); + CoreReflections.method$SynchedEntityData$set.invoke(npcData, LivingEntityData.SleepingPos.entityDataAccessor(), Optional.of(bedPos)); + CoreReflections.method$SynchedEntityData$set.invoke(npcData, PlayerData.PlayerModeCustomisation.entityDataAccessor(), CoreReflections.method$SynchedEntityData$get.invoke(playerData, PlayerData.PlayerModeCustomisation.entityDataAccessor())); + CoreReflections.method$SynchedEntityData$set.invoke(npcData, PlayerData.PlayerMainHand.entityDataAccessor(), CoreReflections.method$SynchedEntityData$get.invoke(playerData, PlayerData.PlayerMainHand.entityDataAccessor())); + CoreReflections.method$SynchedEntityData$set.invoke(npcData, PlayerData.ShoulderLeft.entityDataAccessor(), CoreReflections.method$SynchedEntityData$get.invoke(playerData, PlayerData.ShoulderLeft.entityDataAccessor())); + CoreReflections.method$SynchedEntityData$set.invoke(npcData, PlayerData.ShoulderRight.entityDataAccessor(), CoreReflections.method$SynchedEntityData$get.invoke(playerData, PlayerData.ShoulderRight.entityDataAccessor())); + CoreReflections.method$SynchedEntityData$set.invoke(playerData, PlayerData.ShoulderLeft.entityDataAccessor(), CoreReflections.instance$CompoundTag$Empty); + CoreReflections.method$SynchedEntityData$set.invoke(playerData, PlayerData.ShoulderRight.entityDataAccessor(), CoreReflections.instance$CompoundTag$Empty); + + // SetData + CoreReflections.method$Entity$setInvisible.invoke(serverPlayer, true); + Object npcDataPacket = FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket( + npcId, (List) CoreReflections.method$SynchedEntityData$packDirty.invoke(npcData) + ); + + // Remove + Object npcRemovePacket = NetworkReflections.constructor$ClientboundRemoveEntitiesPacket.newInstance((Object) new int[]{npcId}); + + // TP + Object npcTeleportPacket; + if (VersionHelper.isOrAbove1_21_3()) { + Object positionMoveRotation = CoreReflections.method$PositionMoveRotation$of.invoke(null, npc); + npcTeleportPacket = NetworkReflections.constructor$ClientboundTeleportEntityPacket.newInstance(npcId, positionMoveRotation, Set.of(), false); + } else { + npcTeleportPacket = NetworkReflections.constructor$ClientboundTeleportEntityPacket.newInstance(npc); + } + + // Equipment + Object emptyEquipPacket = NetworkReflections.constructor$ClientboundSetEquipmentPacket.newInstance(player.getEntityId(), emptyEquipments); + + Map equipments = new HashMap<>(); + EntityEquipment equipment = player.getEquipment(); + for (org.bukkit.inventory.EquipmentSlot slot : org.bukkit.inventory.EquipmentSlot.values()) { + if ((!slot.isHand() && !slot.isArmor()) + || (VersionHelper.isOrAbove1_20_5() && slot == org.bukkit.inventory.EquipmentSlot.BODY)) { + continue; + } + EquipmentSlot slotId = EntityUtils.toCEEquipmentSlot(slot); + ItemStack item = equipment.getItem(slot); + equipments.put(slotId, item); + } + List> npcSlots = new ArrayList<>(); + equipments.forEach((slot, item) -> npcSlots.add(Pair.of(EntityUtils.fromEquipmentSlot(slot), FastNMS.INSTANCE.method$CraftItemStack$asNMSCopy(item)))); + Object fullEquipPacket = NetworkReflections.constructor$ClientboundSetEquipmentPacket.newInstance(npcId, npcSlots); + + // Animation + Object npcLeftAnimatePacket = NetworkReflections.constructor$ClientboundAnimatePacket.newInstance(npc, 0); + Object npcRightAnimatePacket = NetworkReflections.constructor$ClientboundAnimatePacket.newInstance(npc, 3); + + packets.add(npcInfoPacket); + packets.add(npcSpawnPacket); + packets.add(bedPacket); + packets.add(npcDataPacket); + packets.add(npcTeleportPacket); + packets.add(emptyEquipPacket); + packets.add(npcLeftAnimatePacket); + Object npcInitPackets = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(packets); + + // Spawn + org.bukkit.entity.Entity seatEntity = BukkitFurniture.spawnSeatEntity(furniture, player.getWorld(), loc, false, null); + if (!seatEntity.addPassenger(player)) { // 0.5 higher + return null; + } + cePlayer.sendPacket(npcInitPackets, true); + cePlayer.sendPacket(fullEquipPacket, true); + if (player.getY() > 0 && cePlayer.protocolVersion().isVersionNewerThan(V1_21_2)) { + BukkitCraftEngine.instance().scheduler().asyncLater(() -> cePlayer.sendPacket(npcTeleportPacket, true), + 50, TimeUnit.MILLISECONDS); // over height 0 cost 2 npcTeleportPacket + } + + for (org.bukkit.entity.Player o : PlayerUtils.getTrackedBy(player)) { + NetWorkUser tracker = BukkitNetworkManager.instance().getOnlineUser(o); + tracker.sendPacket(npcInitPackets, false); + tracker.sendPacket(fullEquipPacket, false); + if (player.getY() > 0 && tracker.protocolVersion().isVersionNewerThan(V1_21_2)) { + BukkitCraftEngine.instance().scheduler().asyncLater(() -> tracker.sendPacket(npcTeleportPacket, false), + 50, TimeUnit.MILLISECONDS); + } + } + + // HeadRot + Direction npcDir = bedDir.opposite(); + + if (sleep) { + player.setSleepingIgnored(true); + } + + if (phantom) { + player.setStatistic(Statistic.TIME_SINCE_REST, 0); + } + + return new LayEntity( + seatEntity, + furniture, + this.offset(), + npcInitPackets, + npcRemovePacket, + npcTeleportPacket, + npcLeftAnimatePacket, + npcRightAnimatePacket, + (BukkitServerPlayer) cePlayer, + bedLoc, + npc, + npcId, + npcDir, + equipments, + emptyEquipPacket, + fullEquipPacket, + sleep + ); + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to spawn LaySeat", e); + } + return null; + } + + private static class LayEntity extends BukkitSeatEntity { + private final Object npcInitPackets; + private final Object npcRemovePacket; + private final Object npcTPPacket; + private final Object npcLeftAnimatePacket; + private final Object npcRightAnimatePacket; + private final BukkitServerPlayer serverPlayer; + private final Object npc; + private final Location bedLoc; + private final int npcID; + private final Direction npcDir; + + // Equipment + private final PlayerMonitorTask task; + private final Map equipments; + private final Object emptyEquipPacket; + private Object updateEquipPacket; + private Object fullEquipPacket; + + private final boolean sleep; + private Object npcRotHeadPacket; + private Object npcDataPacket; + + public LayEntity( + org.bukkit.entity.Entity entity, + Furniture furniture, + Vector3f vector, + Object npcInitPackets, + Object npcRemovePacket, + Object npcTPPacket, + Object npcLeftAnimatePacket, + Object npcRightAnimatePacket, + BukkitServerPlayer serverPlayer, + Location bedLoc, + Object npc, + int npcID, + Direction npcDir, + Map equipments, + Object emptyEquipPacket, + Object fullEquipPacket, + boolean sleep + ) { + super(entity, furniture, vector, serverPlayer.entityID()); + this.npcInitPackets = npcInitPackets; + this.npcRemovePacket = npcRemovePacket; + this.npcTPPacket = npcTPPacket; + this.npcLeftAnimatePacket = npcLeftAnimatePacket; + this.npcRightAnimatePacket = npcRightAnimatePacket; + this.serverPlayer = serverPlayer; + this.bedLoc = bedLoc; + this.npc = npc; + this.npcID = npcID; + this.npcDir = npcDir; + + this.task = new PlayerMonitorTask(); + this.equipments = equipments; + this.emptyEquipPacket = emptyEquipPacket; + this.fullEquipPacket = fullEquipPacket; + + this.sleep = sleep; + } + + @Override + public void add(NetWorkUser from, NetWorkUser to) { + to.sendPacket(this.npcInitPackets, false); + to.sendPacket(this.fullEquipPacket, false); + to.sendPacket(this.npcRotHeadPacket, false); + if (npcDataPacket != null) to.sendPacket(this.npcDataPacket, false); + if (serverPlayer.y() > 0 && to.protocolVersion().isVersionNewerThan(V1_21_2)) { + BukkitCraftEngine.instance().scheduler().asyncLater(() -> + to.sendPacket(this.npcTPPacket, false), 50, TimeUnit.MILLISECONDS); + } + } + + @Override + public boolean handleEntitiesRemove(NetWorkUser user, IntList entityIds) { + entityIds.add(npcID); + return true; + } + + @Override + public void onDismount(Player player) { + if (player == null) return; + + this.task.task.cancel(); + + org.bukkit.entity.Player bukkitPlayer = (org.bukkit.entity.Player) player.platformPlayer(); + Object blockPos = LocationUtils.toBlockPos(bedLoc.getBlockX(), bedLoc.getBlockY(), bedLoc.getBlockZ()); + Object blockState = BlockStateUtils.blockDataToBlockState(bedLoc.getBlock().getBlockData()); + + try { + Object blockUpdatePacket = NetworkReflections.constructor$ClientboundBlockUpdatePacket.newInstance(blockPos, blockState); + player.sendPacket(this.npcRemovePacket, true); + player.sendPacket(blockUpdatePacket, true); + + if (bukkitPlayer.getPotionEffect(PotionEffectType.INVISIBILITY) == null) { + CoreReflections.method$Entity$setInvisible.invoke(serverPlayer.serverPlayer(), false); + } + + Object npcData = CoreReflections.method$Entity$getEntityData.invoke(npc); + Object playerData = CoreReflections.method$Entity$getEntityData.invoke(serverPlayer.serverPlayer()); + CoreReflections.method$SynchedEntityData$set.invoke( + playerData, + PlayerData.ShoulderLeft.entityDataAccessor(), + CoreReflections.method$SynchedEntityData$get.invoke(npcData, PlayerData.ShoulderLeft.entityDataAccessor()) + ); + CoreReflections.method$SynchedEntityData$set.invoke( + playerData, + PlayerData.ShoulderRight.entityDataAccessor(), + CoreReflections.method$SynchedEntityData$get.invoke(npcData, PlayerData.ShoulderRight.entityDataAccessor()) + ); + if (bukkitPlayer.getPotionEffect(PotionEffectType.INVISIBILITY) == null) { + CoreReflections.method$Entity$setInvisible.invoke(serverPlayer.serverPlayer(), false); + } + + bukkitPlayer.updateInventory(); + + if (sleep) { + bukkitPlayer.setSleepingIgnored(false); + } + + Object fullSlots = NetworkReflections.method$ClientboundSetEquipmentPacket$getSlots.invoke(this.fullEquipPacket); + Object recoverEquip = NetworkReflections.constructor$ClientboundSetEquipmentPacket.newInstance(bukkitPlayer.getEntityId(), fullSlots); + + for (org.bukkit.entity.Player o : PlayerUtils.getTrackedBy(bukkitPlayer)) { + BukkitServerPlayer tracker = (BukkitServerPlayer) BukkitNetworkManager.instance().getOnlineUser(o); + tracker.entityPacketHandlers().remove(playerID()); + tracker.sendPacket(this.npcRemovePacket, false); + tracker.sendPacket(blockUpdatePacket, false); + tracker.sendPacket(recoverEquip, false); + } + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to dismount from LayEntity", e); + } + } + + public void equipmentChange(Map equipmentChanges, int previousSlot) { + org.bukkit.entity.Player player = serverPlayer.platformPlayer(); + List> changedSlots = new ArrayList<>(); + + for (Map.Entry entry : equipmentChanges.entrySet()) { + Object slotId = EntityUtils.fromEquipmentSlot(entry.getKey()); + Object itemStack = FastNMS.INSTANCE.method$CraftItemStack$asNMSCopy(entry.getValue()); + changedSlots.add(Pair.of(slotId, itemStack)); + } + this.equipments.putAll(equipmentChanges); + + List> allSlots = new ArrayList<>(); + equipments.forEach((slot, item) -> + allSlots.add(Pair.of(EntityUtils.fromEquipmentSlot(slot), FastNMS.INSTANCE.method$CraftItemStack$asNMSCopy(item)))); + try { + this.updateEquipPacket = NetworkReflections.constructor$ClientboundSetEquipmentPacket.newInstance(npcID, changedSlots); + this.fullEquipPacket = NetworkReflections.constructor$ClientboundSetEquipmentPacket.newInstance(npcID, allSlots); + if (previousSlot != -1) { + player.updateInventory(); + } + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to handle equipmentChange", e); + } + + serverPlayer.sendPacket(this.emptyEquipPacket, false); + serverPlayer.sendPacket(this.updateEquipPacket, false); + + for (org.bukkit.entity.Player o : PlayerUtils.getTrackedBy(player)) { + BukkitNetworkManager.instance().getOnlineUser(o).sendPacket(this.updateEquipPacket, false); + } + } + + @Override + public void handleSetEquipment(NetWorkUser user, ByteBufPacketEvent event, Object slots) { + if (emptyBukkitEquipments.equals(slots)) return; + event.setCancelled(true); + } + + @Override + public void handleContainerSetSlot(NetWorkUser user, NMSPacketEvent event, Object packet) { + try { + int slot = (int) NetworkReflections.method$ClientboundContainerSetSlotPacket$getSlot.invoke(packet); + org.bukkit.entity.Player player = (org.bukkit.entity.Player) user.platformPlayer(); + + int convertSlot; + boolean isPlayerInv; + + if (!VersionHelper.isOrAbove1_21_1()) { + Object openInventory = player.getOpenInventory(); + convertSlot = (int) method$InventoryView$convertSlot.invoke(openInventory, slot); + Object topInventory = method$InventoryView$getTopInventory.invoke(openInventory); + Object type = method$InventoryView$getType.invoke(topInventory); + isPlayerInv = type == InventoryType.CRAFTING; + } else { + convertSlot = player.getOpenInventory().convertSlot(slot); + isPlayerInv = player.getOpenInventory().getTopInventory().getType() == InventoryType.CRAFTING; + } + + if (!(convertSlot == player.getInventory().getHeldItemSlot() || (isPlayerInv && (slot == 45 || (slot >= 5 && slot <= 8))))) + return; + int containerId = (int) NetworkReflections.method$ClientboundContainerSetSlotPacket$getContainerId.invoke(packet); + int stateId = (int) NetworkReflections.method$ClientboundContainerSetSlotPacket$getStateId.invoke(packet); + Object replacePacket = NetworkReflections.constructor$ClientboundContainerSetSlotPacket.newInstance(containerId, stateId, slot, MItems.AIR$Item); + event.replacePacket(replacePacket); + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to handleContainerSetSlotPacket", e); + } + } + + @Override + public void event(Player player, Object event) { + if (event instanceof PlayerAnimationEvent e) { + try { + Object animatePacket; + if (e.getAnimationType() == PlayerAnimationType.ARM_SWING) { + animatePacket = npcLeftAnimatePacket; + } else { + animatePacket = npcRightAnimatePacket; + } + serverPlayer.sendPacket(animatePacket, true); + for (org.bukkit.entity.Player o : PlayerUtils.getTrackedBy(serverPlayer.platformPlayer())) { + BukkitNetworkManager.instance().getOnlineUser(o).sendPacket(animatePacket, true); + } + } catch (Exception exception) { + CraftEngine.instance().logger().warn("Failed to handle PlayerAnimationEvent", exception); + } + } else if (event instanceof PlayerItemHeldEvent e) { + ItemStack item = e.getPlayer().getInventory().getItem(e.getNewSlot()); + if (item == null) item = ItemUtils.AIR; + + equipmentChange(Map.of(EquipmentSlot.MAIN_HAND, item), e.getPreviousSlot()); + } + } + + @Override + public Key type() { + return SeatType.LAY; + } + + private class PlayerMonitorTask implements Runnable { + + private final SchedulerTask task; + private float lastYaw; + + private PlayerMonitorTask() { + org.bukkit.entity.Player p = serverPlayer.platformPlayer(); + BukkitCraftEngine plugin = BukkitCraftEngine.instance(); + if (VersionHelper.isFolia()) { + this.task = new FoliaTask(p.getScheduler().runAtFixedRate(plugin.javaPlugin(), (t) -> this.run(), () -> { + }, 1, 1)); + } else { + this.task = plugin.scheduler().sync().runRepeating(this, 0, 1); + } + } + + @Override + public void run() { + org.bukkit.entity.Player player = serverPlayer.platformPlayer(); + if (player == null || !player.isValid()) { + this.task.cancel(); + return; + } + + // Invisible + updateNpcInvisible(); + try { + if (!player.isInvisible()) + CoreReflections.method$Entity$setInvisible.invoke(serverPlayer.serverPlayer(), true); + } catch (Exception exception) { + CraftEngine.instance().logger().warn("Failed to set shared flag", exception); + } + + // Sync Rotation + float playerYaw = player.getYaw(); + if (lastYaw != playerYaw) { + updateNpcYaw(playerYaw); + serverPlayer.sendPacket(npcRotHeadPacket, false); + for (org.bukkit.entity.Player o : PlayerUtils.getTrackedBy(player)) { + NetWorkUser tracker = BukkitNetworkManager.instance().getOnlineUser(o); + tracker.sendPacket(npcRotHeadPacket, true); + } + this.lastYaw = playerYaw; + } + + // Sync Equipment + Map newEquipments = new HashMap<>(); + for (EquipmentSlot slot : EquipmentSlot.values()) { + if (!slot.isHand() && !slot.isPlayerArmor()) continue; + ItemStack newItem = player.getEquipment().getItem(EntityUtils.toBukkitEquipmentSlot(slot)); + try { + ItemStack item = equipments.get(slot); + boolean isChange = !newItem.equals(item); + if (isChange) { + newEquipments.put(slot, newItem); + } + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to monitor equipments change", e); + } + } + + if (!newEquipments.isEmpty()) { + equipmentChange(newEquipments, -1); + return; + } + serverPlayer.sendPacket(emptyEquipPacket, false); + } + } + + private void updateNpcYaw(float playerYaw) { + byte packYaw = getRot(playerYaw); + try { + this.npcRotHeadPacket = NetworkReflections.constructor$ClientboundRotateHeadPacket.newInstance(npc, packYaw); + } catch (Exception exception) { + CraftEngine.instance().logger().warn("Failed to sync NPC yaw", exception); + } + } + + private byte getRot(float playerYaw) { + float npcYaw = Direction.getYaw(npcDir); + float centerYaw = normalizeYaw(npcYaw); + float playerYawNorm = normalizeYaw(playerYaw); + + float deltaYaw = normalizeYaw(playerYawNorm - centerYaw); + boolean isBehind = Math.abs(deltaYaw) > 90; + + float mappedYaw; + if (isBehind) { + float rel = Math.abs(deltaYaw) - 180; + mappedYaw = rel * (deltaYaw > 0 ? -1 : 1); + } else { + mappedYaw = deltaYaw; + } + + float finalYaw = Math.max(-45, Math.min(45, mappedYaw)); + return MCUtils.packDegrees(finalYaw); + } + + private float normalizeYaw(float yaw) { + yaw %= 360.0f; + if (yaw < -180.0f) yaw += 360.0f; + if (yaw >= 180.0f) yaw -= 360.0f; + return yaw; + } + + private void updateNpcInvisible() { + try { + org.bukkit.entity.Player player = serverPlayer.platformPlayer(); + if (player.getPotionEffect(PotionEffectType.INVISIBILITY) == null && npcDataPacket != null) { + npcDataPacket = null; + CoreReflections.method$Entity$setInvisible.invoke(npc, false); + Object npcData = CoreReflections.method$Entity$getEntityData.invoke(npc); + Object dataItem = CoreReflections.method$SynchedEntityData$getItem.invoke(npcData, PlayerData.SharedFlags.entityDataAccessor()); + Object dataValue = CoreReflections.method$SynchedEntityData$DataItem$value.invoke(dataItem); + Object packet = FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(npcID, List.of(dataValue)); + serverPlayer.sendPacket(packet, false); + for (org.bukkit.entity.Player o : PlayerUtils.getTrackedBy(player)) { + BukkitNetworkManager.instance().getOnlineUser(o).sendPacket(packet, false); + } + } else if (player.getPotionEffect(PotionEffectType.INVISIBILITY) != null && npcDataPacket == null) { + CoreReflections.method$Entity$setInvisible.invoke(npc, true); + Object npcData = CoreReflections.method$Entity$getEntityData.invoke(npc); + Object dataItem = CoreReflections.method$SynchedEntityData$getItem.invoke(npcData, PlayerData.SharedFlags.entityDataAccessor()); + Object dataValue = CoreReflections.method$SynchedEntityData$DataItem$value.invoke(dataItem); + npcDataPacket = FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(npcID, List.of(dataValue)); + serverPlayer.sendPacket(npcDataPacket, false); + for (org.bukkit.entity.Player o : PlayerUtils.getTrackedBy(player)) { + BukkitNetworkManager.instance().getOnlineUser(o).sendPacket(npcDataPacket, false); + } + } + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to updateNpcInvisible", e); + } + } + } + + public static class Factory implements SeatFactory { + + @Override + public Seat create(List args) { + Vector3f offset = MiscUtils.getAsVector3f(args.get(0), "seats"); + Direction facing = args.size() > 1 ? parseFacing(args.get(1)) : Direction.SOUTH; + boolean sleep = args.size() > 2 && Boolean.parseBoolean(args.get(2)); + boolean phantom = args.size() > 4 && Boolean.parseBoolean(args.get(3)); + + if (facing == Direction.NORTH || facing == Direction.SOUTH) { + float temp = offset.x; + offset.x = offset.z; + offset.z = temp; + } + return new LaySeat(offset, facing, sleep, phantom); + } + + private Direction parseFacing(String facing) { + return switch (facing.toLowerCase()) { + case "back" -> Direction.NORTH; + case "left" -> Direction.WEST; + case "right" -> Direction.EAST; + default -> Direction.SOUTH; + }; + } + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/SitSeat.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/SitSeat.java index 4df4d23a4..1f423c0ae 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/SitSeat.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/SitSeat.java @@ -13,46 +13,46 @@ import org.joml.Vector3f; import java.util.List; public class SitSeat extends AbstractSeat { - public static final SeatFactory FACTORY = new Factory(); - private final boolean limitPlayerRotation; + public static final SeatFactory FACTORY = new Factory(); + private final boolean limitPlayerRotation; - public SitSeat(Vector3f offset, float yaw, boolean limitPlayerRotation) { - super(offset, yaw); - this.limitPlayerRotation = limitPlayerRotation; - } + public SitSeat(Vector3f offset, float yaw, boolean limitPlayerRotation) { + super(offset, yaw); + this.limitPlayerRotation = limitPlayerRotation; + } - @Override - public SeatEntity spawn(Player player, Furniture furniture) { - return spawn((org.bukkit.entity.Player) player.platformPlayer(), furniture); - } + @Override + public SeatEntity spawn(Player player, Furniture furniture) { + return spawn((org.bukkit.entity.Player) player.platformPlayer(), furniture); + } - public SeatEntity spawn(org.bukkit.entity.Player player, Furniture furniture) { - Location location = ((BukkitFurniture)furniture).calculateSeatLocation(this); - org.bukkit.entity.Entity seatEntity = BukkitFurniture.spawnSeatEntity(furniture, player.getWorld(), location, this.limitPlayerRotation, null); - if (!seatEntity.addPassenger(player)) { - return null; - }; - return new SitEntity(seatEntity, furniture, offset(), player.getEntityId()); - } + public SeatEntity spawn(org.bukkit.entity.Player player, Furniture furniture) { + Location location = ((BukkitFurniture) furniture).calculateSeatLocation(this); + org.bukkit.entity.Entity seatEntity = BukkitFurniture.spawnSeatEntity(furniture, player.getWorld(), location, this.limitPlayerRotation, null); + if (!seatEntity.addPassenger(player)) { + return null; + } + return new SitEntity(seatEntity, furniture, offset(), player.getEntityId()); + } - private static class SitEntity extends BukkitSeatEntity { + private static class SitEntity extends BukkitSeatEntity { - public SitEntity(Entity entity, Furniture furniture, Vector3f vector3f, int playerID) { - super(entity, furniture, vector3f, playerID); - } + public SitEntity(Entity entity, Furniture furniture, Vector3f vector3f, int playerID) { + super(entity, furniture, vector3f, playerID); + } - @Override - public Key type() { - return SeatType.SIT; - } - } + @Override + public Key type() { + return SeatType.SIT; + } + } - public static class Factory implements SeatFactory { + public static class Factory implements SeatFactory { - @Override - public Seat create(List args) { - if (args.size() == 1) return new SitSeat(MiscUtils.getAsVector3f(args.getFirst(), "seats"), 0, false); - return new SitSeat(MiscUtils.getAsVector3f(args.getFirst(), "seats"), Float.parseFloat(args.get(1)), true); - } - } + @Override + public Seat create(List args) { + if (args.size() == 1) return new SitSeat(MiscUtils.getAsVector3f(args.getFirst(), "seats"), 0, false); + return new SitSeat(MiscUtils.getAsVector3f(args.getFirst(), "seats"), Float.parseFloat(args.get(1)), true); + } + } } \ No newline at end of file diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractSeat.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractSeat.java index ba19c21f2..1f0624ba5 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractSeat.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractSeat.java @@ -5,35 +5,35 @@ import org.joml.Vector3f; import java.util.Objects; public abstract class AbstractSeat implements Seat { - protected final Vector3f offset; - protected final float yaw; + protected final Vector3f offset; + protected final float yaw; - public AbstractSeat(Vector3f offset, float yaw) { - this.offset = offset; - this.yaw = yaw; - } + public AbstractSeat(Vector3f offset, float yaw) { + this.offset = offset; + this.yaw = yaw; + } - @Override - public Vector3f offset() { - return offset; - } + @Override + public Vector3f offset() { + return offset; + } - @Override - public float yaw() { - return yaw; - } + @Override + public float yaw() { + return yaw; + } - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof AbstractSeat seat)) return false; - return Float.compare(yaw, seat.yaw()) == 0 && offset.equals(seat.offset()); - } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof AbstractSeat seat)) return false; + return Float.compare(yaw, seat.yaw()) == 0 && offset.equals(seat.offset()); + } - @Override - public int hashCode() { - int result = Objects.hash(offset); - result = 31 * result + Float.hashCode(yaw); - return result; - } + @Override + public int hashCode() { + int result = Objects.hash(offset); + result = 31 * result + Float.hashCode(yaw); + return result; + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/SeatFactory.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/SeatFactory.java index 34b41577d..e665eac51 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/SeatFactory.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/SeatFactory.java @@ -4,5 +4,5 @@ import java.util.List; public interface SeatFactory { - Seat create(List args); + Seat create(List args); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/SeatType.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/SeatType.java index 054a57089..40dcc83ce 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/SeatType.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/SeatType.java @@ -12,49 +12,49 @@ import java.util.ArrayList; import java.util.List; public class SeatType { - public static final Key SIT = Key.of("craftengine:sit"); - public static final Key LAY = Key.of("craftengine:lay"); - public static final Key CRAWL = Key.of("craftengine:crawl"); + public static final Key SIT = Key.of("craftengine:sit"); + public static final Key LAY = Key.of("craftengine:lay"); + public static final Key CRAWL = Key.of("craftengine:crawl"); - public static void register(Key key, SeatFactory factory) { - Holder.Reference holder =((WritableRegistry) BuiltInRegistries.SEAT_FACTORY) - .registerForHolder(new ResourceKey<>(Registries.SEAT_FACTORY.location(), key)); - holder.bindValue(factory); - } + public static void register(Key key, SeatFactory factory) { + Holder.Reference holder = ((WritableRegistry) BuiltInRegistries.SEAT_FACTORY) + .registerForHolder(new ResourceKey<>(Registries.SEAT_FACTORY.location(), key)); + holder.bindValue(factory); + } - public static Seat fromString(String s) { - int lastSpaceIndex = s.lastIndexOf(' '); + public static Seat fromString(String s) { + int lastSpaceIndex = s.lastIndexOf(' '); - Key type = SIT; - SeatFactory factory; - String numericPart; + Key type = SIT; + SeatFactory factory; + String numericPart; - if (lastSpaceIndex != -1) { - numericPart = s.substring(lastSpaceIndex + 1); - try { - Float.parseFloat(numericPart); - } catch (NumberFormatException e) { - type = Key.withDefaultNamespace(numericPart, "craftengine"); - s = s.substring(0, lastSpaceIndex); - lastSpaceIndex = s.lastIndexOf(' '); - } - } + if (lastSpaceIndex != -1) { + numericPart = s.substring(lastSpaceIndex + 1); + try { + Float.parseFloat(numericPart); + } catch (NumberFormatException e) { + type = Key.withDefaultNamespace(numericPart, "craftengine"); + s = s.substring(0, lastSpaceIndex); + lastSpaceIndex = s.lastIndexOf(' '); + } + } - List split = new ArrayList<>(); - int start = 0; - while (lastSpaceIndex != -1) { - split.add(s.substring(start, lastSpaceIndex)); - start = lastSpaceIndex + 1; - lastSpaceIndex = s.indexOf(' ', start); - } - if (start < s.length()) { - split.add(s.substring(start)); - } + List split = new ArrayList<>(); + int start = 0; + while (lastSpaceIndex != -1) { + split.add(s.substring(start, lastSpaceIndex)); + start = lastSpaceIndex + 1; + lastSpaceIndex = s.indexOf(' ', start); + } + if (start < s.length()) { + split.add(s.substring(start)); + } - factory = BuiltInRegistries.SEAT_FACTORY.getValue(type); - if (factory == null) { - throw new LocalizedResourceConfigException("warning.config.furniture.seat.invalid_type", type.toString()); - } - return factory.create(split); - } + factory = BuiltInRegistries.SEAT_FACTORY.getValue(type); + if (factory == null) { + throw new LocalizedResourceConfigException("warning.config.furniture.seat.invalid_type", type.toString()); + } + return factory.create(split); + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/seat/SeatEntity.java b/core/src/main/java/net/momirealms/craftengine/core/entity/seat/SeatEntity.java index f1ee6e3d9..cdc19c739 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/seat/SeatEntity.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/seat/SeatEntity.java @@ -8,21 +8,21 @@ import org.joml.Vector3f; public interface SeatEntity extends EntityPacketHandler { - void add(NetWorkUser from, NetWorkUser to); + void add(NetWorkUser from, NetWorkUser to); - void dismount(Player player); + void dismount(Player player); - void onDismount(Player player); + void onDismount(Player player); - void event(Player player, Object event); + void event(Player player, Object event); - void destroy(); + void destroy(); - boolean destroyed(); + boolean destroyed(); - Furniture furniture(); + Furniture furniture(); - Vector3f vector3f(); + Vector3f vector3f(); - int playerID(); + int playerID(); } From bff7c85a1e8c1e807be248f3310cc84fb15ccb76 Mon Sep 17 00:00:00 2001 From: iqtester Date: Wed, 25 Jun 2025 02:16:46 -0400 Subject: [PATCH 07/10] Fix Mount and Dismount --- .../furniture/seat/BukkitSeatEntity.java | 13 ++-- .../entity/furniture/seat/CrawlSeat.java | 3 +- .../bukkit/entity/furniture/seat/LaySeat.java | 61 ++++++++++++------- .../bukkit/entity/furniture/seat/SitSeat.java | 1 + .../plugin/network/PacketConsumers.java | 2 +- .../core/entity/seat/SeatEntity.java | 2 +- 6 files changed, 48 insertions(+), 34 deletions(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/BukkitSeatEntity.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/BukkitSeatEntity.java index cff139fe4..06567d660 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/BukkitSeatEntity.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/BukkitSeatEntity.java @@ -24,20 +24,18 @@ public abstract class BukkitSeatEntity extends BukkitEntity implements SeatEntit } @Override - public void add(NetWorkUser from, NetWorkUser to) {} + public void add(NetWorkUser to) {} @Override public void dismount(Player player) { - if (player == null || destroyed) return; + if (player == null) return; player.setSeat(null); onDismount(player); destroy(); } @Override - public void onDismount(Player player) { - - } + public void onDismount(Player player) {} @Override public void event(Player player, Object event) {} @@ -54,10 +52,7 @@ public abstract class BukkitSeatEntity extends BukkitEntity implements SeatEntit entity.removePassenger(passenger); if (passenger instanceof org.bukkit.entity.Player p && p.getEntityId() == this.playerID) { Player cePlayer = BukkitAdaptors.adapt(p); - if (cePlayer != null && cePlayer.entityID() == playerID()) { - cePlayer.setSeat(null); - onDismount(cePlayer); - } + dismount(cePlayer); } } entity.remove(); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/CrawlSeat.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/CrawlSeat.java index 9692e578b..ee34c2e9f 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/CrawlSeat.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/CrawlSeat.java @@ -59,6 +59,7 @@ public class CrawlSeat extends AbstractSeat { org.bukkit.entity.Entity seatEntity = BukkitFurniture.spawnSeatEntity(furniture, player.getWorld(), location, this.limitPlayerRotation, null); if (!seatEntity.addPassenger(player)) { + seatEntity.remove(); return null; } @@ -122,7 +123,7 @@ public class CrawlSeat extends AbstractSeat { } @Override - public void add(NetWorkUser from, NetWorkUser to) { + public void add(NetWorkUser to) { to.sendPacket(syncPosePacket, false); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/LaySeat.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/LaySeat.java index b731ff96e..b8cc1fb41 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/LaySeat.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/LaySeat.java @@ -226,6 +226,7 @@ public class LaySeat extends AbstractSeat { // Spawn org.bukkit.entity.Entity seatEntity = BukkitFurniture.spawnSeatEntity(furniture, player.getWorld(), loc, false, null); if (!seatEntity.addPassenger(player)) { // 0.5 higher + seatEntity.remove(); return null; } cePlayer.sendPacket(npcInitPackets, true); @@ -235,6 +236,7 @@ public class LaySeat extends AbstractSeat { 50, TimeUnit.MILLISECONDS); // over height 0 cost 2 npcTeleportPacket } + Set trackers = new HashSet<>(); for (org.bukkit.entity.Player o : PlayerUtils.getTrackedBy(player)) { NetWorkUser tracker = BukkitNetworkManager.instance().getOnlineUser(o); tracker.sendPacket(npcInitPackets, false); @@ -243,6 +245,7 @@ public class LaySeat extends AbstractSeat { BukkitCraftEngine.instance().scheduler().asyncLater(() -> tracker.sendPacket(npcTeleportPacket, false), 50, TimeUnit.MILLISECONDS); } + trackers.add(tracker); } // HeadRot @@ -266,6 +269,7 @@ public class LaySeat extends AbstractSeat { npcLeftAnimatePacket, npcRightAnimatePacket, (BukkitServerPlayer) cePlayer, + trackers, bedLoc, npc, npcId, @@ -292,6 +296,7 @@ public class LaySeat extends AbstractSeat { private final Location bedLoc; private final int npcID; private final Direction npcDir; + private final Set trackers; // Equipment private final PlayerMonitorTask task; @@ -314,6 +319,7 @@ public class LaySeat extends AbstractSeat { Object npcLeftAnimatePacket, Object npcRightAnimatePacket, BukkitServerPlayer serverPlayer, + Set trackers, Location bedLoc, Object npc, int npcID, @@ -330,6 +336,7 @@ public class LaySeat extends AbstractSeat { this.npcLeftAnimatePacket = npcLeftAnimatePacket; this.npcRightAnimatePacket = npcRightAnimatePacket; this.serverPlayer = serverPlayer; + this.trackers = trackers; this.bedLoc = bedLoc; this.npc = npc; this.npcID = npcID; @@ -344,7 +351,7 @@ public class LaySeat extends AbstractSeat { } @Override - public void add(NetWorkUser from, NetWorkUser to) { + public void add(NetWorkUser to) { to.sendPacket(this.npcInitPackets, false); to.sendPacket(this.fullEquipPacket, false); to.sendPacket(this.npcRotHeadPacket, false); @@ -353,18 +360,34 @@ public class LaySeat extends AbstractSeat { BukkitCraftEngine.instance().scheduler().asyncLater(() -> to.sendPacket(this.npcTPPacket, false), 50, TimeUnit.MILLISECONDS); } + trackers.add(to); } @Override public boolean handleEntitiesRemove(NetWorkUser user, IntList entityIds) { entityIds.add(npcID); + trackers.remove(user); return true; } @Override - public void onDismount(Player player) { - if (player == null) return; + public void dismount(Player player) { + super.dismount(player); + if (player != null) return; + try { // for disconnect recover + Object blockPos = LocationUtils.toBlockPos(bedLoc.getBlockX(), bedLoc.getBlockY(), bedLoc.getBlockZ()); + Object blockState = BlockStateUtils.blockDataToBlockState(bedLoc.getBlock().getBlockData()); + Object blockRecoverPacket = NetworkReflections.constructor$ClientboundBlockUpdatePacket.newInstance(blockPos, blockState); + for (NetWorkUser tracker : trackers) { + tracker.sendPacket(blockRecoverPacket, false); + } + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to dismount player", e); + } + } + @Override + public void onDismount(Player player) { this.task.task.cancel(); org.bukkit.entity.Player bukkitPlayer = (org.bukkit.entity.Player) player.platformPlayer(); @@ -372,9 +395,9 @@ public class LaySeat extends AbstractSeat { Object blockState = BlockStateUtils.blockDataToBlockState(bedLoc.getBlock().getBlockData()); try { - Object blockUpdatePacket = NetworkReflections.constructor$ClientboundBlockUpdatePacket.newInstance(blockPos, blockState); + Object blockRecoverPacket = NetworkReflections.constructor$ClientboundBlockUpdatePacket.newInstance(blockPos, blockState); player.sendPacket(this.npcRemovePacket, true); - player.sendPacket(blockUpdatePacket, true); + player.sendPacket(blockRecoverPacket, true); if (bukkitPlayer.getPotionEffect(PotionEffectType.INVISIBILITY) == null) { CoreReflections.method$Entity$setInvisible.invoke(serverPlayer.serverPlayer(), false); @@ -392,9 +415,6 @@ public class LaySeat extends AbstractSeat { PlayerData.ShoulderRight.entityDataAccessor(), CoreReflections.method$SynchedEntityData$get.invoke(npcData, PlayerData.ShoulderRight.entityDataAccessor()) ); - if (bukkitPlayer.getPotionEffect(PotionEffectType.INVISIBILITY) == null) { - CoreReflections.method$Entity$setInvisible.invoke(serverPlayer.serverPlayer(), false); - } bukkitPlayer.updateInventory(); @@ -405,11 +425,9 @@ public class LaySeat extends AbstractSeat { Object fullSlots = NetworkReflections.method$ClientboundSetEquipmentPacket$getSlots.invoke(this.fullEquipPacket); Object recoverEquip = NetworkReflections.constructor$ClientboundSetEquipmentPacket.newInstance(bukkitPlayer.getEntityId(), fullSlots); - for (org.bukkit.entity.Player o : PlayerUtils.getTrackedBy(bukkitPlayer)) { - BukkitServerPlayer tracker = (BukkitServerPlayer) BukkitNetworkManager.instance().getOnlineUser(o); - tracker.entityPacketHandlers().remove(playerID()); + for (NetWorkUser tracker : trackers) { tracker.sendPacket(this.npcRemovePacket, false); - tracker.sendPacket(blockUpdatePacket, false); + tracker.sendPacket(blockRecoverPacket, false); tracker.sendPacket(recoverEquip, false); } } catch (Exception e) { @@ -444,8 +462,8 @@ public class LaySeat extends AbstractSeat { serverPlayer.sendPacket(this.emptyEquipPacket, false); serverPlayer.sendPacket(this.updateEquipPacket, false); - for (org.bukkit.entity.Player o : PlayerUtils.getTrackedBy(player)) { - BukkitNetworkManager.instance().getOnlineUser(o).sendPacket(this.updateEquipPacket, false); + for (NetWorkUser tracker : trackers) { + tracker.sendPacket(this.updateEquipPacket, false); } } @@ -497,8 +515,8 @@ public class LaySeat extends AbstractSeat { animatePacket = npcRightAnimatePacket; } serverPlayer.sendPacket(animatePacket, true); - for (org.bukkit.entity.Player o : PlayerUtils.getTrackedBy(serverPlayer.platformPlayer())) { - BukkitNetworkManager.instance().getOnlineUser(o).sendPacket(animatePacket, true); + for (NetWorkUser tracker : trackers) { + tracker.sendPacket(animatePacket, true); } } catch (Exception exception) { CraftEngine.instance().logger().warn("Failed to handle PlayerAnimationEvent", exception); @@ -554,8 +572,7 @@ public class LaySeat extends AbstractSeat { if (lastYaw != playerYaw) { updateNpcYaw(playerYaw); serverPlayer.sendPacket(npcRotHeadPacket, false); - for (org.bukkit.entity.Player o : PlayerUtils.getTrackedBy(player)) { - NetWorkUser tracker = BukkitNetworkManager.instance().getOnlineUser(o); + for (NetWorkUser tracker : trackers) { tracker.sendPacket(npcRotHeadPacket, true); } this.lastYaw = playerYaw; @@ -632,8 +649,8 @@ public class LaySeat extends AbstractSeat { Object dataValue = CoreReflections.method$SynchedEntityData$DataItem$value.invoke(dataItem); Object packet = FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(npcID, List.of(dataValue)); serverPlayer.sendPacket(packet, false); - for (org.bukkit.entity.Player o : PlayerUtils.getTrackedBy(player)) { - BukkitNetworkManager.instance().getOnlineUser(o).sendPacket(packet, false); + for (NetWorkUser tracker : trackers) { + tracker.sendPacket(packet, false); } } else if (player.getPotionEffect(PotionEffectType.INVISIBILITY) != null && npcDataPacket == null) { CoreReflections.method$Entity$setInvisible.invoke(npc, true); @@ -642,8 +659,8 @@ public class LaySeat extends AbstractSeat { Object dataValue = CoreReflections.method$SynchedEntityData$DataItem$value.invoke(dataItem); npcDataPacket = FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(npcID, List.of(dataValue)); serverPlayer.sendPacket(npcDataPacket, false); - for (org.bukkit.entity.Player o : PlayerUtils.getTrackedBy(player)) { - BukkitNetworkManager.instance().getOnlineUser(o).sendPacket(npcDataPacket, false); + for (NetWorkUser tracker : trackers) { + tracker.sendPacket(npcDataPacket, false); } } } catch (Exception e) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/SitSeat.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/SitSeat.java index 1f423c0ae..a34f54391 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/SitSeat.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/seat/SitSeat.java @@ -30,6 +30,7 @@ public class SitSeat extends AbstractSeat { Location location = ((BukkitFurniture) furniture).calculateSeatLocation(this); org.bukkit.entity.Entity seatEntity = BukkitFurniture.spawnSeatEntity(furniture, player.getWorld(), location, this.limitPlayerRotation, null); if (!seatEntity.addPassenger(player)) { + seatEntity.remove(); return null; } return new SitEntity(seatEntity, furniture, offset(), player.getEntityId()); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java index 7fe2b5d38..d3c920db8 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java @@ -191,7 +191,7 @@ public class PacketConsumers { SeatEntity seat = player.seat(); if (seat == null) return; user.entityPacketHandlers().put(seat.playerID(), seat); - seat.add(player, user); + seat.add(user); }; } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/seat/SeatEntity.java b/core/src/main/java/net/momirealms/craftengine/core/entity/seat/SeatEntity.java index cdc19c739..033eab3cc 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/seat/SeatEntity.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/seat/SeatEntity.java @@ -8,7 +8,7 @@ import org.joml.Vector3f; public interface SeatEntity extends EntityPacketHandler { - void add(NetWorkUser from, NetWorkUser to); + void add(NetWorkUser to); void dismount(Player player); From b4aea52b503ad273f9f1a47c14ff7376de50b5d7 Mon Sep 17 00:00:00 2001 From: Catnies Date: Wed, 25 Jun 2025 21:58:10 +0800 Subject: [PATCH 08/10] =?UTF-8?q?=E6=84=9F=E8=A7=89=E7=BB=93=E6=9E=84?= =?UTF-8?q?=E4=B8=8D=E5=A4=AA=E5=AF=B9,=20=E5=BC=BA=E8=A1=8C=E6=8B=93?= =?UTF-8?q?=E5=B1=95=E4=B8=8D=E4=B8=8B=E5=8E=BB=E4=BA=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plugin/config/StringKeyConstructor.java | 123 +++++++++++++----- 1 file changed, 88 insertions(+), 35 deletions(-) diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/StringKeyConstructor.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/StringKeyConstructor.java index bcca9168c..d1bbcdc2a 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/StringKeyConstructor.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/StringKeyConstructor.java @@ -1,3 +1,4 @@ + package net.momirealms.craftengine.core.plugin.config; import net.momirealms.craftengine.core.plugin.locale.TranslationManager; @@ -74,59 +75,111 @@ public class StringKeyConstructor extends SafeConstructor { String key = constructScalar((ScalarNode) keyNode); Node valueNode = tuple.getValueNode(); + // 处理 版本化块 合并. if (key.startsWith(VERSION_PREFIX)) { - // 处理版本化块合并 String versionSpec = key.substring(VERSION_PREFIX.length()); if (isVersionMatch(versionSpec)) { if (valueNode instanceof MappingNode) { // 将版本匹配的map内容合并到当前map - map.putAll(constructMapping((MappingNode) valueNode)); + Map versionedMap = constructMapping((MappingNode) valueNode); + mergeMap(map, versionedMap); } else { logWarning("versioned_key_not_a_map", key, valueNode); } } - } else if (key.contains(DEEP_KEY_SEPARATOR)) { - // 处理 '::' 分隔的深层键 - String[] parts = key.split(DEEP_KEY_SEPARATOR); + } + // 处理::分隔的键 -> {a::b::c: value} 和 {a::b: {c: value}}. + else if (key.contains(DEEP_KEY_SEPARATOR)) { + processDeepKey(map, key, valueNode, keyNode); + } + // 处理正常的内容. + else { Object value = constructObject(valueNode); - Map currentMap = map; - // 遍历除最后一个部分外的所有路径,创建嵌套的map - for (int i = 0; i < parts.length - 1; i++) { - String part = parts[i]; - Object nextObject = currentMap.get(part); - if (nextObject instanceof Map) { - currentMap = (Map) nextObject; - } else { - // 如果路径中存在一个非map的值,发出警告并覆盖它 - if (nextObject != null) { - logWarning("key_path_conflict", part, keyNode); - } - Map newMap = new LinkedHashMap<>(); - currentMap.put(part, newMap); - currentMap = newMap; + // 检查是否需要与现有的Map合并 + Object existing = map.get(key); + if (existing instanceof Map && value instanceof Map) { + // 如果已存在同名的Map(可能是由深层键创建的),则合并它们 + mergeMap((Map) existing, (Map) value); + } else { + // 否则正常设置 + Object previous = map.put(key, value); + + // 如果之前有值, + if (previous != null && !(previous instanceof Map)) { + logWarning("duplicated_key", key, keyNode); } } - - // 在最深层的map中设置最终的键值对 - String finalKey = parts[parts.length - 1]; - Object previous = currentMap.put(finalKey, value); - if (previous != null) { - // 使用完整的原始键来报告重复键,更清晰 - logWarning("duplicated_key", key, keyNode); - } - } else { - // 原始逻辑:处理普通键 - Object value = constructObject(valueNode); - Object previous = map.put(key, value); - if (previous != null) { - logWarning("duplicated_key", key, keyNode); - } } } return map; } + /** + * 处理深层键的逻辑,支持完全深层键和部分深层键的合并 + */ + @SuppressWarnings("unchecked") + private void processDeepKey(Map rootMap, String key, Node valueNode, Node keyNode) { + String[] parts = key.split(DEEP_KEY_SEPARATOR); + Map currentMap = rootMap; + + // 遍历除最后一个部分外的所有路径,创建或导航到嵌套的map + for (int i = 0; i < parts.length - 1; i++) { + String part = parts[i]; + Object nextObject = currentMap.get(part); + if (nextObject instanceof Map) { + currentMap = (Map) nextObject; + } else { + // 如果路径中存在一个非map的值,发出警告并覆盖它 + if (nextObject != null) { + logWarning("key_path_conflict", part, keyNode); + } + Map newMap = new LinkedHashMap<>(); + currentMap.put(part, newMap); + currentMap = newMap; + } + } + + // 处理最后一个部分 + String finalKey = parts[parts.length - 1]; + Object value = constructObject(valueNode); + + // 检查最终键是否需要合并 + Object existing = currentMap.get(finalKey); + if (existing instanceof Map && value instanceof Map) { + // 如果目标位置已经有一个Map,且新值也是Map,则合并它们 + mergeMap((Map) existing, (Map) value); + } else { + // 否则直接设置值 + Object previous = currentMap.put(finalKey, value); + if (previous != null && !(previous instanceof Map)) { + // 只有当之前的值不是Map时才报告重复键警告 + logWarning("duplicated_key", key, keyNode); + } + } + } + + /** + * 合并两个Map + */ + @SuppressWarnings("unchecked") + private void mergeMap(Map target, Map source) { + for (Map.Entry entry : source.entrySet()) { + Object key = entry.getKey(); + Object sourceValue = entry.getValue(); + Object targetValue = target.get(key); + + // 如果都是Map,则递归合并. + if (targetValue instanceof Map && sourceValue instanceof Map) { + mergeMap((Map) targetValue, (Map) sourceValue); + return; + } + + // TODO 不能覆盖, 得想办法丢个异常. + target.put(key, sourceValue); + } + } + /** * 检查一个MappingNode是否是“值选择器”(即所有键都以 '$$' 开头)。 */ From d7e904d22ccc2452ae7aeb87233db62c0e7507f3 Mon Sep 17 00:00:00 2001 From: Catnies Date: Wed, 25 Jun 2025 22:54:32 +0800 Subject: [PATCH 09/10] =?UTF-8?q?=E9=87=8D=E6=96=B0=E6=8D=8B=E9=A1=BA?= =?UTF-8?q?=E4=BB=A3=E7=A0=81,=20=E5=88=86=E5=89=B2=E4=B8=8D=E5=90=8C?= =?UTF-8?q?=E6=83=85=E5=86=B5.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plugin/config/StringKeyConstructor.java | 167 +++++++++--------- 1 file changed, 83 insertions(+), 84 deletions(-) diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/StringKeyConstructor.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/StringKeyConstructor.java index d1bbcdc2a..c744d82cf 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/StringKeyConstructor.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/StringKeyConstructor.java @@ -50,17 +50,15 @@ public class StringKeyConstructor extends SafeConstructor { */ @Override public Object constructObject(Node node) { - if (node instanceof MappingNode mappingNode) { - if (isValueSelectorNode(mappingNode)) { - // 场景B: 这是一个值选择器,解析它以获得单个值 - return constructVersionedValue(mappingNode); - } + if (node instanceof MappingNode mappingNode && isValueSelectorNode(mappingNode)) { + return constructVersionedValue(mappingNode); } // 对于所有其他情况 (包括需要合并的Map),使用默认的构造逻辑 // super.constructObject 会最终调用我们重写的 constructMapping return super.constructObject(node); } + /** * 场景A (块合并与路径展开): 构造一个Map,同时处理其中的版本化块合并和 `::` 分隔的深层键。 */ @@ -68,118 +66,119 @@ public class StringKeyConstructor extends SafeConstructor { @Override protected Map constructMapping(MappingNode node) { Map map = new LinkedHashMap<>(); + for (NodeTuple tuple : node.getValue()) { Node keyNode = tuple.getKeyNode(); if (!(keyNode instanceof ScalarNode)) continue; - - String key = constructScalar((ScalarNode) keyNode); Node valueNode = tuple.getValueNode(); - // 处理 版本化块 合并. - if (key.startsWith(VERSION_PREFIX)) { - String versionSpec = key.substring(VERSION_PREFIX.length()); - if (isVersionMatch(versionSpec)) { - if (valueNode instanceof MappingNode) { - // 将版本匹配的map内容合并到当前map - Map versionedMap = constructMapping((MappingNode) valueNode); - mergeMap(map, versionedMap); - } else { - logWarning("versioned_key_not_a_map", key, valueNode); - } - } - } - // 处理::分隔的键 -> {a::b::c: value} 和 {a::b: {c: value}}. - else if (key.contains(DEEP_KEY_SEPARATOR)) { - processDeepKey(map, key, valueNode, keyNode); - } - // 处理正常的内容. - else { - Object value = constructObject(valueNode); + String key = constructScalar((ScalarNode) keyNode); - // 检查是否需要与现有的Map合并 - Object existing = map.get(key); - if (existing instanceof Map && value instanceof Map) { - // 如果已存在同名的Map(可能是由深层键创建的),则合并它们 - mergeMap((Map) existing, (Map) value); - } else { - // 否则正常设置 - Object previous = map.put(key, value); - - // 如果之前有值, - if (previous != null && !(previous instanceof Map)) { - logWarning("duplicated_key", key, keyNode); - } - } - } + // 处理 版本化块. + if (key.startsWith(VERSION_PREFIX)) processVersionedBlock(map, key, valueNode); + // 处理 深层键 -> {a::b::c: value} 和 {a::b: {c: value}}. + else if (key.contains(DEEP_KEY_SEPARATOR)) processDeepKey(map, key, valueNode, keyNode); + // 处理 正常键. + else processRegularKey(map, key, valueNode, keyNode); } + return map; } - /** - * 处理深层键的逻辑,支持完全深层键和部分深层键的合并 - */ - @SuppressWarnings("unchecked") - private void processDeepKey(Map rootMap, String key, Node valueNode, Node keyNode) { - String[] parts = key.split(DEEP_KEY_SEPARATOR); + + // 处理版本化块合并 + private void processVersionedBlock(Map targetMap, String key, Node valueNode) { + String versionSpec = key.substring(VERSION_PREFIX.length()); + + if (isVersionMatch(versionSpec)) { + if (valueNode instanceof MappingNode mappingNode) { + Map versionedMap = constructMapping(mappingNode); + mergeMap(targetMap, versionedMap, "", valueNode); + } else { + logWarning("versioned_key_not_a_map", key, valueNode); + } + } + } + + // 处理深层键 + private void processDeepKey(Map rootMap, String fullKey, Node valueNode, Node keyNode) { + String[] keyParts = fullKey.split(DEEP_KEY_SEPARATOR); Map currentMap = rootMap; - // 遍历除最后一个部分外的所有路径,创建或导航到嵌套的map - for (int i = 0; i < parts.length - 1; i++) { - String part = parts[i]; - Object nextObject = currentMap.get(part); - if (nextObject instanceof Map) { - currentMap = (Map) nextObject; + // 创建必要的的中间层级 + for (int i = 0; i < keyParts.length - 1; i++) { + String keyPart = keyParts[i]; + Object existingValue = currentMap.get(keyPart); + + if (existingValue instanceof Map) { + currentMap = (Map) existingValue; } else { - // 如果路径中存在一个非map的值,发出警告并覆盖它 - if (nextObject != null) { - logWarning("key_path_conflict", part, keyNode); + if (existingValue != null) { + logWarning("key_path_conflict", keyPart, keyNode); } Map newMap = new LinkedHashMap<>(); - currentMap.put(part, newMap); + currentMap.put(keyPart, newMap); currentMap = newMap; } } - // 处理最后一个部分 - String finalKey = parts[parts.length - 1]; - Object value = constructObject(valueNode); + // 处理最终键 + String finalKey = keyParts[keyParts.length - 1]; + Object newValue = constructObject(valueNode); + String keyPath = String.join(DEEP_KEY_SEPARATOR, keyParts); // 构建完整的键路径字符串 - // 检查最终键是否需要合并 - Object existing = currentMap.get(finalKey); - if (existing instanceof Map && value instanceof Map) { - // 如果目标位置已经有一个Map,且新值也是Map,则合并它们 - mergeMap((Map) existing, (Map) value); + setValueWithDuplicationCheck(currentMap, finalKey, newValue, keyPath, keyNode); + } + + // 处理普通键 + private void processRegularKey(Map targetMap, String key, Node valueNode, Node keyNode) { + Object newValue = constructObject(valueNode); + setValueWithDuplicationCheck(targetMap, key, newValue, key, keyNode); + } + + + // 设置值并检查重复键 + @SuppressWarnings("unchecked") + private void setValueWithDuplicationCheck(Map targetMap, String key, Object newValue, String fullKeyPath, Node keyNode) { + Object existingValue = targetMap.get(key); + + if (existingValue == null) { + // 键不存在,直接设置. + targetMap.put(key, newValue); + } else if (existingValue instanceof Map && newValue instanceof Map) { + // 两个都是Map,直接合并. + mergeMap((Map) existingValue, (Map) newValue, fullKeyPath, keyNode); } else { - // 否则直接设置值 - Object previous = currentMap.put(finalKey, value); - if (previous != null && !(previous instanceof Map)) { - // 只有当之前的值不是Map时才报告重复键警告 - logWarning("duplicated_key", key, keyNode); - } + // 存在重复键(至少一个不是Map) + logWarning("duplicated_key", fullKeyPath, keyNode); + targetMap.put(key, newValue); } } - /** - * 合并两个Map - */ + + // 合并两个Map并检查重复键 @SuppressWarnings("unchecked") - private void mergeMap(Map target, Map source) { + private void mergeMap(Map target, Map source, String parentPath, Node sourceNode) { for (Map.Entry entry : source.entrySet()) { - Object key = entry.getKey(); + String key = entry.getKey().toString(); Object sourceValue = entry.getValue(); Object targetValue = target.get(key); + String currentPath = parentPath.isEmpty() ? key : parentPath + DEEP_KEY_SEPARATOR + key; - // 如果都是Map,则递归合并. - if (targetValue instanceof Map && sourceValue instanceof Map) { - mergeMap((Map) targetValue, (Map) sourceValue); - return; + // Map不存在该键,直接添加喵. + if (targetValue == null) target.put(key, sourceValue); + // 两个值都是Map,还需继续合并. + else if (targetValue instanceof Map && sourceValue instanceof Map) + mergeMap((Map) targetValue, (Map) sourceValue, currentPath, sourceNode); + // 发现重复的键, 爆炸了喵. + else { + logWarning("duplicated_key", currentPath, sourceNode); + target.put(key, sourceValue); } - - // TODO 不能覆盖, 得想办法丢个异常. - target.put(key, sourceValue); } } + /** * 检查一个MappingNode是否是“值选择器”(即所有键都以 '$$' 开头)。 */ From e4c4b0e7cd691620ecd091c799044e5d474c3231 Mon Sep 17 00:00:00 2001 From: Catnies Date: Wed, 25 Jun 2025 23:19:02 +0800 Subject: [PATCH 10/10] =?UTF-8?q?=E8=A1=A5=E9=BD=90=E6=B3=A8=E9=87=8A,=20?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=96=B0=E7=9A=84=E8=AF=AD=E8=A8=80key.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/translations/zh_cn.yml | 1 + .../plugin/config/StringKeyConstructor.java | 23 +++++++++++-------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/common-files/src/main/resources/translations/zh_cn.yml b/common-files/src/main/resources/translations/zh_cn.yml index 2edc1009c..fcfa0a54f 100644 --- a/common-files/src/main/resources/translations/zh_cn.yml +++ b/common-files/src/main/resources/translations/zh_cn.yml @@ -65,6 +65,7 @@ command.send_resource_pack.success.single: "发送资源包给 发送资源包给 个玩家" warning.config.pack.duplicated_files: "发现重复文件 请通过 config.yml 的 'resource-pack.duplicated-files-handler' 部分解决" warning.config.yaml.duplicated_key: "在文件 发现问题 - 在第行发现重复的键 '', 这可能会导致一些意料之外的问题." +warning.config.yaml.key_path_conflict: "在文件 发现问题 - 在第行发现重复且值类型不同的键 '', 这可能会导致一些意料之外的问题." warning.config.type.int: "在文件 发现问题 - 无法加载 '': 无法将 '' 转换为整数类型 (选项 '')" warning.config.type.float: "在文件 发现问题 - 无法加载 '': 无法将 '' 转换为浮点数类型 (选项 '')" warning.config.type.boolean: "在文件 发现问题 - 无法加载 '': 无法将 '' 转换为布尔类型 (选项 '')" diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/StringKeyConstructor.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/StringKeyConstructor.java index c744d82cf..a54f63239 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/StringKeyConstructor.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/StringKeyConstructor.java @@ -102,27 +102,32 @@ public class StringKeyConstructor extends SafeConstructor { // 处理深层键 private void processDeepKey(Map rootMap, String fullKey, Node valueNode, Node keyNode) { + // 分割出不同的层级 String[] keyParts = fullKey.split(DEEP_KEY_SEPARATOR); Map currentMap = rootMap; - // 创建必要的的中间层级 + // 创建必要的的中间层级(最后一个key不应遍历, 如aa::bb::cc, 只应创建aa和bb.) for (int i = 0; i < keyParts.length - 1; i++) { String keyPart = keyParts[i]; Object existingValue = currentMap.get(keyPart); + // 路径中的值 if (existingValue instanceof Map) { currentMap = (Map) existingValue; - } else { - if (existingValue != null) { - logWarning("key_path_conflict", keyPart, keyNode); - } - Map newMap = new LinkedHashMap<>(); - currentMap.put(keyPart, newMap); - currentMap = newMap; + continue; } + + // 如果路径中存在一个非map的值, 这意味着 + // 当存在了 {aa: bb}, 又想要写入 {aa::bb::c: value} 时, 会触发这个警告, 然后会覆盖之前的. + if (existingValue != null) logWarning("key_path_conflict", keyPart, keyNode); + + // 创建层级 + Map newMap = new LinkedHashMap<>(); + currentMap.put(keyPart, newMap); + currentMap = newMap; } - // 处理最终键 + // 这里再处理最后的 cc key. String finalKey = keyParts[keyParts.length - 1]; Object newValue = constructObject(valueNode); String keyPath = String.join(DEEP_KEY_SEPARATOR, keyParts); // 构建完整的键路径字符串