diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java index 33adbc8d8..9a685a32f 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java @@ -130,16 +130,6 @@ public class BukkitFurnitureManager implements FurnitureManager { elements.add(furnitureElement); } - // add hitboxes - List> hitboxConfigs = (List>) placementArguments.getOrDefault("hitboxes", List.of()); - List hitboxes = new ArrayList<>(); - for (Map config : hitboxConfigs) { - hitboxes.add(HitBoxTypes.fromMap(config)); - } - if (hitboxes.isEmpty()) { - hitboxes.add(InteractionHitBox.DEFAULT); - } - // add colliders List> colliderConfigs = (List>) placementArguments.getOrDefault("colliders", List.of()); List colliders = new ArrayList<>(); @@ -147,8 +137,8 @@ public class BukkitFurnitureManager implements FurnitureManager { if (!config.containsKey("position")) { colliders.add(new Collider( (boolean) config.getOrDefault("can-be-hit-by-projectile", false), - MiscUtils.getVector3f(config.getOrDefault("point-1", "0")), - MiscUtils.getVector3f(config.getOrDefault("point-2", "0")) + MiscUtils.getVector3d(config.getOrDefault("point-1", "0")), + MiscUtils.getVector3d(config.getOrDefault("point-2", "0")) )); } else { colliders.add(new Collider( @@ -160,6 +150,18 @@ public class BukkitFurnitureManager implements FurnitureManager { } } + // add hitboxes + List> hitboxConfigs = (List>) placementArguments.getOrDefault("hitboxes", List.of()); + List hitboxes = new ArrayList<>(); + for (Map config : hitboxConfigs) { + HitBox hitBox = HitBoxTypes.fromMap(config); + hitboxes.add(hitBox); + hitBox.optionCollider().ifPresent(colliders::add); + } + if (hitboxes.isEmpty()) { + hitboxes.add(InteractionHitBox.DEFAULT); + } + // rules Map ruleSection = MiscUtils.castToMap(placementArguments.get("rules"), true); if (ruleSection != null) { @@ -276,6 +278,10 @@ public class BukkitFurnitureManager implements FurnitureManager { CustomFurniture customFurniture = optionalFurniture.get(); LoadedFurniture previous = this.furnitureByBaseEntityId.get(display.getEntityId()); if (previous != null) return; + Location location = entity.getLocation(); + if (FastNMS.INSTANCE.isPreventingStatusUpdates(location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4)) { + return; + } LoadedFurniture furniture = addNewFurniture(display, customFurniture, getAnchorType(entity, customFurniture)); for (Player player : display.getTrackedPlayers()) { this.plugin.adapt(player).furnitureView().computeIfAbsent(furniture.baseEntityId(), k -> new ArrayList<>()).addAll(furniture.subEntityIds()); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/LoadedFurniture.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/LoadedFurniture.java index 25faaa9b7..04c8255f7 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/LoadedFurniture.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/LoadedFurniture.java @@ -20,6 +20,7 @@ import org.bukkit.entity.ItemDisplay; import org.bukkit.persistence.PersistentDataType; import org.jetbrains.annotations.NotNull; import org.joml.Quaternionf; +import org.joml.Vector3d; import org.joml.Vector3f; import java.lang.ref.WeakReference; @@ -34,6 +35,8 @@ public class LoadedFurniture { // base entity private final WeakReference baseEntity; private final int baseEntityId; + // colliders + private final CollisionEntity[] collisionEntities; // cache private final List fakeEntityIds; private final List hitBoxEntityIds; @@ -88,12 +91,14 @@ public class LoadedFurniture { } this.fakeEntityIds = fakeEntityIds; this.hitBoxEntityIds = hitBoxEntityIds; - - if (placement.colliders().length != 0) { + int colliderSize = placement.colliders().length; + this.collisionEntities = new CollisionEntity[colliderSize]; + if (colliderSize != 0) { Object world = FastNMS.INSTANCE.field$CraftWorld$ServerLevel(this.location.getWorld()); - for (Collider collider : placement.colliders()) { - Vector3f offset1 = conjugated.transform(new Vector3f(collider.point1())); - Vector3f offset2 = conjugated.transform(new Vector3f(collider.point2())); + for (int i = 0; i < colliderSize; i++) { + Collider collider = placement.colliders()[i]; + Vector3d offset1 = conjugated.transform(new Vector3d(collider.point1())); + Vector3d offset2 = conjugated.transform(new Vector3d(collider.point2())); double x1 = x + offset1.x(); double x2 = x + offset2.x(); double y1 = y + offset1.y(); @@ -103,6 +108,7 @@ public class LoadedFurniture { Object aabb = FastNMS.INSTANCE.constructor$AABB(x1, y1, z1, x2, y2, z2); CollisionEntity entity = FastNMS.INSTANCE.createCollisionEntity(world, aabb, x, y, z, true, collider.canBeHitByProjectile()); FastNMS.INSTANCE.method$LevelWriter$addFreshEntity(world, entity); + this.collisionEntities[i] = entity; } } } @@ -135,6 +141,10 @@ public class LoadedFurniture { return; } this.baseEntity().remove(); + for (CollisionEntity entity : this.collisionEntities) { + if (entity != null) + entity.destroy(); + } for (Entity entity : this.seats) { for (Entity passenger : entity.getPassengers()) { entity.removePassenger(passenger); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerHitBox.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerHitBox.java index bc8ab0803..4eca2774b 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerHitBox.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/hitbox/ShulkerHitBox.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.bukkit.entity.furniture.hitbox; +import net.momirealms.craftengine.bukkit.entity.data.InteractionEntityData; import net.momirealms.craftengine.bukkit.entity.data.ShulkerData; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.util.DirectionUtils; @@ -21,15 +22,54 @@ public class ShulkerHitBox extends AbstractHitBox { // 1.20.6+ private final double scale; private final byte peek; + private final boolean interactive; + private final boolean interactionEntity; // todo或许还能做个方向,但是会麻烦点,和 yaw 有关 - private final Direction direction; - private List cachedValues; + private final Direction direction = Direction.UP; + private final List cachedShulkerValues = new ArrayList<>(); + private final List cachedInteractionValues = new ArrayList<>(); - public ShulkerHitBox(Seat[] seats, Vector3f position, double scale, byte peek, Direction direction) { + public ShulkerHitBox(Seat[] seats, Vector3f position, double scale, byte peek, boolean interactionEntity, boolean interactive) { super(seats, position); this.scale = scale; this.peek = peek; - this.direction = direction; + this.interactive = interactive; + this.interactionEntity = interactionEntity; + + ShulkerData.Peek.addEntityDataIfNotDefaultValue(peek, this.cachedShulkerValues); +// ShulkerData.AttachFace.addEntityDataIfNotDefaultValue(DirectionUtils.toNMSDirection(direction), this.cachedShulkerValues); + ShulkerData.NoGravity.addEntityDataIfNotDefaultValue(true, this.cachedShulkerValues); + ShulkerData.Silent.addEntityDataIfNotDefaultValue(true, this.cachedShulkerValues); + ShulkerData.MobFlags.addEntityDataIfNotDefaultValue((byte) 0x01, this.cachedShulkerValues); // 无ai + ShulkerData.SharedFlags.addEntityDataIfNotDefaultValue((byte) 0x20, this.cachedShulkerValues); // 不可见 + + if (this.interactionEntity) { + InteractionEntityData.Height.addEntityDataIfNotDefaultValue((float) ((1 + getPeekHeight(peek)) * scale) + 0.001f, cachedInteractionValues); + InteractionEntityData.Width.addEntityDataIfNotDefaultValue((float) scale + 0.001f, cachedInteractionValues); + InteractionEntityData.Responsive.addEntityDataIfNotDefaultValue(interactive, cachedInteractionValues); + } + } + + @Override + public Optional optionCollider() { + return Optional.of(new Collider( + true, + position(), + (float) scale(), + 1 + getPeekHeight(peek()) + )); + } + + private static float getPeekHeight(byte peek) { + return (float) (0.5F - Math.sin((0.5F + peek * 0.01F) * 3.1415927F) * 0.5F); + } + + public boolean interactionEntity() { + return interactionEntity; + } + + public boolean interactive() { + return interactive; } public Direction direction() { @@ -61,35 +101,34 @@ public class ShulkerHitBox extends AbstractHitBox { entityIds[1], UUID.randomUUID(), x + offset.x, y + offset.y, z - offset.z, 0, yaw, Reflections.instance$EntityType$SHULKER, 0, Reflections.instance$Vec3$Zero, 0 )); - packets.accept(Reflections.constructor$ClientboundSetEntityDataPacket.newInstance(entityIds[1], getCachedValues())); + packets.accept(Reflections.constructor$ClientboundSetEntityDataPacket.newInstance(entityIds[1], List.copyOf(this.cachedShulkerValues))); packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetPassengersPacket(entityIds[0], entityIds[1])); if (VersionHelper.isVersionNewerThan1_20_5()) { Object attributeInstance = Reflections.constructor$AttributeInstance.newInstance(Reflections.instance$Holder$Attribute$scale, (Consumer) (o) -> {}); Reflections.method$AttributeInstance$setBaseValue.invoke(attributeInstance, scale); packets.accept(Reflections.constructor$ClientboundUpdateAttributesPacket0.newInstance(entityIds[1], Collections.singletonList(attributeInstance))); } + if (this.interactionEntity) { + packets.accept(Reflections.constructor$ClientboundAddEntityPacket.newInstance( + entityIds[2], UUID.randomUUID(), x + offset.x, y + offset.y - 0.0005f, z - offset.z, 0, yaw, + Reflections.instance$EntityType$INTERACTION, 0, Reflections.instance$Vec3$Zero, 0 + )); + packets.accept(Reflections.constructor$ClientboundSetEntityDataPacket.newInstance(entityIds[2], List.copyOf(this.cachedInteractionValues))); + } } catch (ReflectiveOperationException e) { throw new RuntimeException("Failed to construct shulker hitbox spawn packet", e); } } - private synchronized List getCachedValues() { - if (this.cachedValues == null) { - this.cachedValues = new ArrayList<>(); - ShulkerData.Peek.addEntityDataIfNotDefaultValue(peek, this.cachedValues); - ShulkerData.AttachFace.addEntityDataIfNotDefaultValue(DirectionUtils.toNMSDirection(direction), this.cachedValues); - ShulkerData.NoGravity.addEntityDataIfNotDefaultValue(true, this.cachedValues); - ShulkerData.Silent.addEntityDataIfNotDefaultValue(true, this.cachedValues); - ShulkerData.MobFlags.addEntityDataIfNotDefaultValue((byte) 0x01, this.cachedValues); // 无ai - ShulkerData.SharedFlags.addEntityDataIfNotDefaultValue((byte) 0x20, this.cachedValues); // 不可见 - } - return this.cachedValues; - } - @Override public int[] acquireEntityIds(Supplier entityIdSupplier) { - // 展示实体 // 潜影贝 - return new int[] {entityIdSupplier.get(), entityIdSupplier.get()}; + if (this.interactionEntity) { + // 展示实体 // 潜影贝 // 交互实体 + return new int[] {entityIdSupplier.get(), entityIdSupplier.get(), entityIdSupplier.get()}; + } else { + // 展示实体 // 潜影贝 + return new int[] {entityIdSupplier.get(), entityIdSupplier.get()}; + } } public static class Factory implements HitBoxFactory { @@ -100,10 +139,12 @@ public class ShulkerHitBox extends AbstractHitBox { double scale = MiscUtils.getAsDouble(arguments.getOrDefault("scale", "1")); byte peek = (byte) MiscUtils.getAsInt(arguments.getOrDefault("peek", 0)); Direction directionEnum = Optional.ofNullable(arguments.get("direction")).map(it -> Direction.valueOf(it.toString().toUpperCase(Locale.ENGLISH))).orElse(Direction.UP); + boolean interactive = (boolean) arguments.getOrDefault("interactive", true); + boolean interactionEntity = (boolean) arguments.getOrDefault("interaction-entity", true); return new ShulkerHitBox( HitBoxFactory.getSeats(arguments), position, - scale, peek, directionEnum + scale, peek, interactionEntity, interactive ); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Collider.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Collider.java index a32b4b213..efa419157 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Collider.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Collider.java @@ -1,13 +1,14 @@ package net.momirealms.craftengine.core.entity.furniture; +import org.joml.Vector3d; import org.joml.Vector3f; public class Collider { - private final Vector3f point1; - private final Vector3f point2; + private final Vector3d point1; + private final Vector3d point2; private final boolean canBeHitByProjectile; - public Collider(boolean canBeHitByProjectile, Vector3f point1, Vector3f point2) { + public Collider(boolean canBeHitByProjectile, Vector3d point1, Vector3d point2) { this.canBeHitByProjectile = canBeHitByProjectile; this.point1 = point1; this.point2 = point2; @@ -15,19 +16,19 @@ public class Collider { public Collider(boolean canBeHitByProjectile, Vector3f position, float width, float height) { this.canBeHitByProjectile = canBeHitByProjectile; - this.point1 = new Vector3f(position.x - width / 2, position.y, position.z - width / 2); - this.point2 = new Vector3f(position.x + width / 2, position.y + height, position.z + width / 2); + this.point1 = new Vector3d(position.x - width / 2, position.y, position.z - width / 2); + this.point2 = new Vector3d(position.x + width / 2, position.y + height, position.z + width / 2); } public boolean canBeHitByProjectile() { return canBeHitByProjectile; } - public Vector3f point1() { + public Vector3d point1() { return point1; } - public Vector3f point2() { + public Vector3d point2() { return point2; } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBox.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBox.java index dcbaca37e..0eddfd069 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBox.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/HitBox.java @@ -4,6 +4,7 @@ import net.momirealms.craftengine.core.util.Key; import org.joml.Quaternionf; import org.joml.Vector3f; +import java.util.Optional; import java.util.function.Consumer; import java.util.function.Supplier; @@ -18,4 +19,8 @@ public interface HitBox { Seat[] seats(); Vector3f position(); + + default Optional optionCollider() { + return Optional.empty(); + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/MiscUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/MiscUtils.java index dfb84b83e..d6fda7904 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/MiscUtils.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/MiscUtils.java @@ -1,6 +1,7 @@ package net.momirealms.craftengine.core.util; import org.joml.Quaternionf; +import org.joml.Vector3d; import org.joml.Vector3f; import java.util.ArrayList; @@ -148,6 +149,18 @@ public class MiscUtils { } } + public static Vector3d getVector3d(Object o) { + String stringFormat = o.toString(); + String[] split = stringFormat.split(","); + if (split.length == 3) { + return new Vector3d(Double.parseDouble(split[0]), Double.parseDouble(split[1]), Double.parseDouble(split[2])); + } else if (split.length == 1) { + return new Vector3d(Double.parseDouble(split[0])); + } else { + throw new RuntimeException("Cannot convert " + o + " to Vector3d"); + } + } + public static Quaternionf getQuaternionf(Object o) { String stringFormat = o.toString(); String[] split = stringFormat.split(",");