9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2026-01-06 15:52:03 +00:00

添加custom碰撞箱

This commit is contained in:
XiaoMoMi
2025-12-03 21:25:54 +08:00
parent 462cea8d0a
commit 1d00df89c5
11 changed files with 234 additions and 15 deletions

View File

@@ -10,7 +10,7 @@ public class BukkitFurnitureHitboxTypes extends FurnitureHitBoxTypes {
static {
register(INTERACTION, InteractionFurnitureHitboxConfig.FACTORY);
register(SHULKER, ShulkerFurnitureHitboxConfig.FACTORY);
// register(CUSTOM, CustomFurnitureHitboxConfig.FACTORY);
register(CUSTOM, CustomFurnitureHitboxConfig.FACTORY);
if (VersionHelper.isOrAbove1_21_6()) {
register(HAPPY_GHAST, HappyGhastFurnitureHitboxConfig.FACTORY);
}

View File

@@ -0,0 +1,83 @@
package net.momirealms.craftengine.bukkit.entity.furniture.hitbox;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MAttributeHolders;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections;
import net.momirealms.craftengine.core.entity.furniture.Collider;
import net.momirealms.craftengine.core.entity.furniture.Furniture;
import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitboxPart;
import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.util.VersionHelper;
import net.momirealms.craftengine.core.world.Vec3d;
import net.momirealms.craftengine.core.world.WorldPosition;
import net.momirealms.craftengine.core.world.collision.AABB;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.function.Consumer;
public class CustomFurnitureHitbox extends AbstractFurnitureHitBox {
private final CustomFurnitureHitboxConfig config;
private final Collider collider;
private final Object spawnPacket;
private final Object despawnPacket;
private final FurnitureHitboxPart part;
public CustomFurnitureHitbox(Furniture furniture, CustomFurnitureHitboxConfig config) {
super(furniture, config);
this.config = config;
WorldPosition position = furniture.position();
Vec3d pos = Furniture.getRelativePosition(position, config.position());
AABB aabb = AABB.makeBoundingBox(pos, config.width(), config.height());
this.collider = createCollider(furniture.world(), pos, aabb, false, config.blocksBuilding(), config.canBeHitByProjectile());
int entityId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet();
List<Object> packets = new ArrayList<>(3);
packets.add(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
entityId, UUID.randomUUID(), position.x, position.y, position.z, 0, position.yRot,
config.entityType(), 0, CoreReflections.instance$Vec3$Zero, 0
));
packets.add(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityId, config.cachedValues()));
if (VersionHelper.isOrAbove1_20_5()) {
try {
Object attributeInstance = CoreReflections.constructor$AttributeInstance.newInstance(MAttributeHolders.SCALE, (Consumer<?>) (o) -> {});
CoreReflections.method$AttributeInstance$setBaseValue.invoke(attributeInstance, config.scale());
packets.add(NetworkReflections.constructor$ClientboundUpdateAttributesPacket0.newInstance(entityId, Collections.singletonList(attributeInstance)));
} catch (ReflectiveOperationException e) {
CraftEngine.instance().logger().warn("Failed to apply scale attribute", e);
}
}
this.spawnPacket = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(packets);
this.part = new FurnitureHitboxPart(entityId, aabb, pos, false);
this.despawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(new IntArrayList() {{ add(entityId); }});
}
@Override
public List<Collider> colliders() {
return List.of(this.collider);
}
@Override
public List<FurnitureHitboxPart> parts() {
return List.of(this.part);
}
@Override
public void show(Player player) {
player.sendPacket(this.spawnPacket, false);
}
@Override
public void hide(Player player) {
player.sendPacket(this.despawnPacket, false);
}
@Override
public CustomFurnitureHitboxConfig config() {
return this.config;
}
}

View File

@@ -0,0 +1,114 @@
package net.momirealms.craftengine.bukkit.entity.furniture.hitbox;
import net.momirealms.craftengine.bukkit.entity.data.BaseEntityData;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBuiltInRegistries;
import net.momirealms.craftengine.bukkit.util.KeyUtils;
import net.momirealms.craftengine.core.entity.furniture.Furniture;
import net.momirealms.craftengine.core.entity.furniture.hitbox.AbstractFurnitureHitBoxConfig;
import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfigFactory;
import net.momirealms.craftengine.core.entity.seat.SeatConfig;
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.world.Vec3d;
import net.momirealms.craftengine.core.world.WorldPosition;
import net.momirealms.craftengine.core.world.collision.AABB;
import org.joml.Vector3f;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
public class CustomFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig<CustomFurnitureHitbox> {
public static final Factory FACTORY = new Factory();
private final float scale;
private final Object entityType;
private final List<Object> cachedValues = new ArrayList<>();
private final float width;
private final float height;
public CustomFurnitureHitboxConfig(SeatConfig[] seats,
Vector3f position,
boolean canUseItemOn,
boolean blocksBuilding,
boolean canBeHitByProjectile,
float width,
float height,
boolean fixed,
float scale,
Object type) {
super(seats, position, canUseItemOn, blocksBuilding, canBeHitByProjectile);
this.scale = scale;
this.entityType = type;
this.width = fixed ? width : width * scale;
this.height = fixed ? height : height * scale;
BaseEntityData.NoGravity.addEntityDataIfNotDefaultValue(true, this.cachedValues);
BaseEntityData.Silent.addEntityDataIfNotDefaultValue(true, this.cachedValues);
BaseEntityData.SharedFlags.addEntityDataIfNotDefaultValue((byte) 0x20, this.cachedValues);
}
public float scale() {
return this.scale;
}
public Object entityType() {
return this.entityType;
}
public List<Object> cachedValues() {
return this.cachedValues;
}
public float width() {
return width;
}
public float height() {
return height;
}
@Override
public void prepareForPlacement(WorldPosition targetPos, Consumer<AABB> aabbConsumer) {
if (this.blocksBuilding) {
Vec3d relativePosition = Furniture.getRelativePosition(targetPos, this.position);
aabbConsumer.accept(AABB.makeBoundingBox(relativePosition, this.width, this.height));
}
}
@Override
public CustomFurnitureHitbox create(Furniture furniture) {
return new CustomFurnitureHitbox(furniture, this);
}
public static class Factory implements FurnitureHitBoxConfigFactory<CustomFurnitureHitbox> {
@Override
public CustomFurnitureHitboxConfig create(Map<String, Object> arguments) {
Vector3f position = ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position");
float scale = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("scale", 1), "scale");
String type = (String) arguments.getOrDefault("entity-type", "slime");
Object nmsEntityType = FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.ENTITY_TYPE, KeyUtils.toResourceLocation(Key.of(type)));
if (nmsEntityType == null) {
throw new LocalizedResourceConfigException("warning.config.furniture.hitbox.custom.invalid_entity", new IllegalArgumentException("EntityType not found: " + type), type);
}
float width;
float height;
boolean fixed;
try {
Object dimensions = CoreReflections.field$EntityType$dimensions.get(nmsEntityType);
width = CoreReflections.field$EntityDimensions$width.getFloat(dimensions);
height = CoreReflections.field$EntityDimensions$height.getFloat(dimensions);
fixed = CoreReflections.field$EntityDimensions$fixed.getBoolean(dimensions);
} catch (ReflectiveOperationException e) {
throw new RuntimeException("Failed to get dimensions for " + nmsEntityType, e);
}
boolean canUseItemOn = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-use-item-on", false), "can-use-item-on");
boolean canBeHitByProjectile = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-be-hit-by-projectile", false), "can-be-hit-by-projectile");
boolean blocksBuilding = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("blocks-building", true), "blocks-building");
return new CustomFurnitureHitboxConfig(SeatConfig.fromObj(arguments.get("seats")), position, canUseItemOn, blocksBuilding, canBeHitByProjectile, width, height, fixed, scale, nmsEntityType);
}
}
}

View File

@@ -37,7 +37,7 @@ public class HappyGhastFurnitureHitbox extends AbstractFurnitureHitBox {
WorldPosition position = furniture.position();
this.pos = Furniture.getRelativePosition(position, config.position());
double bbSize = 4 * config.scale();
AABB aabb = AABB.fromInteraction(this.pos, bbSize, bbSize);
AABB aabb = AABB.makeBoundingBox(this.pos, bbSize, bbSize);
this.yaw = position.yRot;
this.entityId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet();
this.packets = new ArrayList<>(3);

View File

@@ -59,7 +59,7 @@ public class HappyGhastFurnitureHitboxConfig extends AbstractFurnitureHitBoxConf
public void prepareForPlacement(WorldPosition targetPos, Consumer<AABB> aabbConsumer) {
if (this.blocksBuilding) {
Vec3d relativePosition = Furniture.getRelativePosition(targetPos, this.position);
aabbConsumer.accept(AABB.fromInteraction(relativePosition, 4 * this.scale, 4 * this.scale));
aabbConsumer.accept(AABB.makeBoundingBox(relativePosition, 4 * this.scale, 4 * this.scale));
}
}

View File

@@ -27,7 +27,7 @@ public class InteractionFurnitureHitbox extends AbstractFurnitureHitBox {
this.config = config;
WorldPosition position = furniture.position();
Vec3d pos = Furniture.getRelativePosition(position, config.position());
AABB aabb = AABB.fromInteraction(pos, config.size().x, config.size().y);
AABB aabb = AABB.makeBoundingBox(pos, config.size().x, config.size().y);
this.collider = createCollider(furniture.world(), pos, aabb, false, config.blocksBuilding(), config.canBeHitByProjectile());
int interactionId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet();
this.spawnPacket = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(List.of(

View File

@@ -73,7 +73,7 @@ public class InteractionFurnitureHitboxConfig extends AbstractFurnitureHitBoxCon
public void prepareForPlacement(WorldPosition targetPos, Consumer<AABB> aabbConsumer) {
if (this.blocksBuilding) {
Vec3d relativePosition = Furniture.getRelativePosition(targetPos, this.position);
aabbConsumer.accept(AABB.fromInteraction(relativePosition, size.x, size.y));
aabbConsumer.accept(AABB.makeBoundingBox(relativePosition, size.x, size.y));
}
}

View File

@@ -82,7 +82,7 @@ public class ShulkerFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig<
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[2], List.copyOf(cachedInteractionValues)));
if (canUseItemOn) {
Vec3d vec3d = new Vec3d(x + offset.x, y + offset.y, z - offset.z);
aabb.accept(new FurnitureHitboxPart(entityIds[2], AABB.fromInteraction(vec3d, scale, shulkerHeight), vec3d, interactive));
aabb.accept(new FurnitureHitboxPart(entityIds[2], AABB.makeBoundingBox(vec3d, scale, shulkerHeight), vec3d, interactive));
}
}
};
@@ -102,7 +102,7 @@ public class ShulkerFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig<
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[2], List.copyOf(cachedInteractionValues)));
if (canUseItemOn) {
Vec3d vec3d = new Vec3d(x + offset.x, y + offset.y - shulkerHeight + scale, z - offset.z);
aabb.accept(new FurnitureHitboxPart(entityIds[2], AABB.fromInteraction(vec3d, scale, shulkerHeight), vec3d, interactive));
aabb.accept(new FurnitureHitboxPart(entityIds[2], AABB.makeBoundingBox(vec3d, scale, shulkerHeight), vec3d, interactive));
}
}
};
@@ -133,8 +133,8 @@ public class ShulkerFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig<
if (canUseItemOn) {
Vec3d vec3d1 = new Vec3d(x + offset.x, y + offset.y, z - offset.z);
Vec3d vec3d2 = new Vec3d(x + offset.x + shulkerDirection.stepX() * distance, y + offset.y, z - offset.z + shulkerDirection.stepZ() * distance);
aabb.accept(new FurnitureHitboxPart(entityIds[2], AABB.fromInteraction(vec3d1, scale, scale), vec3d1, interactive));
aabb.accept(new FurnitureHitboxPart(entityIds[3], AABB.fromInteraction(vec3d2, scale, scale), vec3d2, interactive));
aabb.accept(new FurnitureHitboxPart(entityIds[2], AABB.makeBoundingBox(vec3d1, scale, scale), vec3d1, interactive));
aabb.accept(new FurnitureHitboxPart(entityIds[3], AABB.makeBoundingBox(vec3d2, scale, scale), vec3d2, interactive));
}
}
};

View File

@@ -4594,4 +4594,27 @@ public final class CoreReflections {
throw new ReflectionInitException("Failed to init EmptyBlockGetter$INSTANCE", e);
}
}
public static final Class<?> clazz$EntityDimensions = requireNonNull(
BukkitReflectionUtils.findReobfOrMojmapClass(
"world.entity.EntitySize",
"world.entity.EntityDimensions"
)
);
public static final Field field$EntityType$dimensions = requireNonNull(
ReflectionUtils.getDeclaredField(clazz$EntityType, clazz$EntityDimensions, 0)
);
public static final Field field$EntityDimensions$width = requireNonNull(
ReflectionUtils.getDeclaredField(clazz$EntityDimensions, float.class, 0)
);
public static final Field field$EntityDimensions$height = requireNonNull(
ReflectionUtils.getDeclaredField(clazz$EntityDimensions, float.class, 1)
);
public static final Field field$EntityDimensions$fixed = requireNonNull(
ReflectionUtils.getDeclaredField(clazz$EntityDimensions, boolean.class, 0)
);
}

View File

@@ -27,12 +27,11 @@ items:
translation: 0,0.5,0
hitboxes:
- position: 0,0,0
type: interaction
blocks-building: true
type: custom
entity-type: slime
invisible: true
width: 0.7
height: 1.2
interactive: true
can-use-item-on: true
blocks-building: true
seats:
- 0,0,-0.1 0
loot:

View File

@@ -57,7 +57,7 @@ public class AABB {
return x * x + y * y + z * z;
}
public static AABB fromInteraction(Vec3d pos, double width, double height) {
public static AABB makeBoundingBox(Vec3d pos, double width, double height) {
return new AABB(
pos.x - width / 2,
pos.y,