From 273bd5d29229c7586a51f810f2d6ec094b6a42f3 Mon Sep 17 00:00:00 2001 From: iqtester Date: Tue, 24 Jun 2025 11:39:39 -0400 Subject: [PATCH] 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(); }