9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-30 04:19:27 +00:00

Crawl and Lay

This commit is contained in:
iqtester
2025-06-02 14:25:29 -04:00
parent 4c6a073a96
commit 65f0e968cc
22 changed files with 953 additions and 312 deletions

View File

@@ -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

View File

@@ -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();

View File

@@ -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);
}
}

View File

@@ -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> 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;
}
}

View File

@@ -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<Object> 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<String> 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);
}
}
}

View File

@@ -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<Object> 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<String, Object> properties = (Multimap<String, Object>) Reflections.method$GameProfile$getProperties.invoke(npcProfile);
properties.putAll((Multimap<String, Object>) 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<String, Object> properties = (Multimap<String, Object>) Reflections.method$GameProfile$getProperties.invoke(npcProfile);
properties.putAll((Multimap<String, Object>) 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<? extends Enum>) 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<Pair<Object, Object>> 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<EquipmentSlot, ItemStack> 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<Pair<Object, Object>> 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<Object> 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<EquipmentSlot, ItemStack> 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<EquipmentSlot, ItemStack> 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<EquipmentSlot, ItemStack> equipmentChanges, int previousSlot) {
org.bukkit.entity.Player player = serverPlayer.platformPlayer();
List<Pair<Object,Object>> changedSlots = new ArrayList<>();
for (Map.Entry<EquipmentSlot, ItemStack> 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<Pair<Object,Object>> 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<EquipmentSlot, ItemStack> 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<String> arguments) {
return null;
public Seat create(List<String> 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;
};
}
}
}

View File

@@ -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<String> 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);
}
}
}

View File

@@ -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<Command
}
}))
.handler(context -> {
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());
});
}

View File

@@ -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());

View File

@@ -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<NetWorkUser, ByteBufPacketEvent> 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<NetWorkUser, NMSPacketEvent, Object> 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<NetWorkUser, NMSPacketEvent, Object> 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);
}
};
}

View File

@@ -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;
}

View File

@@ -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<Entity> 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();
};
}
}

View File

@@ -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")

View File

@@ -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<Player> getTrackedBy(Player player) {
return VersionHelper.isOrAbove1_20_2() ? player.getTrackedBy() : player.getTrackedPlayers();
}
}