9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-22 16:39:28 +00:00

初步完成自定义弹射物

This commit is contained in:
XiaoMoMi
2025-05-11 00:49:32 +08:00
parent 3483275cdf
commit 593f3021a7
8 changed files with 109 additions and 60 deletions

View File

@@ -4,9 +4,9 @@ import net.momirealms.craftengine.bukkit.entity.data.ItemDisplayEntityData;
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.util.Reflections;
import net.momirealms.craftengine.core.entity.furniture.AbstractFurnitureElement;
import net.momirealms.craftengine.core.entity.Billboard;
import net.momirealms.craftengine.core.entity.ItemDisplayContext;
import net.momirealms.craftengine.core.entity.furniture.AbstractFurnitureElement;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.util.Key;

View File

@@ -1,25 +1,31 @@
package net.momirealms.craftengine.bukkit.entity.projectile;
import com.destroystokyo.paper.event.entity.EntityRemoveFromWorldEvent;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.util.Reflections;
import net.momirealms.craftengine.core.entity.projectile.CustomProjectile;
import net.momirealms.craftengine.core.entity.projectile.ProjectileManager;
import net.momirealms.craftengine.core.entity.projectile.ProjectileMeta;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.util.VersionHelper;
import org.bukkit.Bukkit;
import org.bukkit.entity.Arrow;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Projectile;
import org.bukkit.entity.ThrowableProjectile;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityRemoveEvent;
import org.bukkit.event.entity.ProjectileLaunchEvent;
import org.bukkit.event.world.EntitiesLoadEvent;
import org.bukkit.inventory.ItemStack;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
public class BukkitProjectileManager implements Listener, ProjectileManager {
private static BukkitProjectileManager instance;
@@ -50,6 +56,24 @@ public class BukkitProjectileManager implements Listener, ProjectileManager {
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
public void onProjectileLaunch(ProjectileLaunchEvent event) {
Projectile projectile = event.getEntity();
handleProjectileLoad(projectile, true);
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
public void onEntitiesLoad(EntitiesLoadEvent event) {
for (Entity entity : event.getEntities()) {
if (entity instanceof Projectile projectile) {
handleProjectileLoad(projectile, false);
}
}
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGH)
public void onEntityRemove(EntityRemoveFromWorldEvent event) {
this.projectiles.remove(event.getEntity().getEntityId());
}
private void handleProjectileLoad(Projectile projectile, boolean delay) {
ItemStack projectileItem;
if (projectile instanceof ThrowableProjectile throwableProjectile) {
projectileItem = throwableProjectile.getItem();
@@ -58,21 +82,50 @@ public class BukkitProjectileManager implements Listener, ProjectileManager {
} else {
return;
}
System.out.println("发射");
Item<ItemStack> wrapped = this.plugin.itemManager().wrap(projectileItem);
if (wrapped == null) return;
wrapped.getCustomItem().ifPresent(it -> {
ProjectileMeta meta = it.settings().projectileMeta();
if (meta != null) {
System.out.println("来啦");
this.projectiles.put(projectile.getEntityId(), new BukkitCustomProjectile(meta, projectile, wrapped));
ProjectileInjectTask task = new ProjectileInjectTask(projectile);
if (!delay) {
task.run();
} else if (VersionHelper.isFolia()) {
projectile.getScheduler().run(plugin.bootstrap(), (t) -> task.run(), () -> {});
} else {
plugin.scheduler().sync().runDelayed(task);
}
}
});
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGH)
public void onEntityRemove(EntityRemoveEvent event) {
this.projectiles.remove(event.getEntity().getEntityId());
public class ProjectileInjectTask implements Runnable {
private final Projectile projectile;
public ProjectileInjectTask(Projectile projectile) {
this.projectile = projectile;
}
@Override
public void run() {
Object nmsEntity = FastNMS.INSTANCE.method$CraftEntity$getHandle(this.projectile);
Object trackedEntity = FastNMS.INSTANCE.field$Entity$trackedEntity(nmsEntity);
if (trackedEntity == null) {
plugin.logger().warn("Failed to update server entity tracking interval due to missing tracked entity");
return;
}
Object serverEntity = FastNMS.INSTANCE.filed$ChunkMap$TrackedEntity$serverEntity(trackedEntity);
if (serverEntity == null) {
plugin.logger().warn("Failed to update server entity tracking interval due to missing server entity");
return;
}
try {
Reflections.field$ServerEntity$updateInterval.set(serverEntity, 1);
} catch (ReflectiveOperationException e) {
plugin.logger().warn("Failed to update server entity tracking interval", e);
}
}
}
public static BukkitProjectileManager instance() {

View File

@@ -23,16 +23,12 @@ import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.block.Block;
import org.bukkit.block.data.BlockData;
import org.bukkit.entity.Arrow;
import org.bukkit.entity.Player;
import org.bukkit.entity.Projectile;
import org.bukkit.entity.ThrowableProjectile;
import org.bukkit.event.Event;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.entity.ProjectileLaunchEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;

View File

@@ -139,7 +139,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
registerNMSPacketConsumer(PacketConsumers.RESPAWN, Reflections.clazz$ClientboundRespawnPacket);
registerNMSPacketConsumer(PacketConsumers.INTERACT_ENTITY, Reflections.clazz$ServerboundInteractPacket);
registerNMSPacketConsumer(PacketConsumers.SYNC_ENTITY_POSITION, Reflections.clazz$ClientboundEntityPositionSyncPacket);
registerNMSPacketConsumer(PacketConsumers.MOVE_ENTITY, Reflections.clazz$ClientboundMoveEntityPacket$Pos);
registerNMSPacketConsumer(PacketConsumers.MOVE_POS_ENTITY, Reflections.clazz$ClientboundMoveEntityPacket$Pos);
registerNMSPacketConsumer(PacketConsumers.PICK_ITEM_FROM_ENTITY, Reflections.clazz$ServerboundPickItemFromEntityPacket);
registerNMSPacketConsumer(PacketConsumers.RENAME_ITEM, Reflections.clazz$ServerboundRenameItemPacket);
registerNMSPacketConsumer(PacketConsumers.SIGN_UPDATE, Reflections.clazz$ServerboundSignUpdatePacket);
@@ -150,7 +150,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
registerNMSPacketConsumer(PacketConsumers.LOGIN_ACKNOWLEDGED, Reflections.clazz$ServerboundLoginAcknowledgedPacket);
registerNMSPacketConsumer(PacketConsumers.RESOURCE_PACK_RESPONSE, Reflections.clazz$ServerboundResourcePackPacket);
registerNMSPacketConsumer(PacketConsumers.ENTITY_EVENT, Reflections.clazz$ClientboundEntityEventPacket);
registerNMSPacketConsumer(PacketConsumers.MOVE_AND_ROTATE_ENTITY, Reflections.clazz$ClientboundMoveEntityPacket$PosRot);
registerNMSPacketConsumer(PacketConsumers.MOVE_POS_AND_ROTATE_ENTITY, Reflections.clazz$ClientboundMoveEntityPacket$PosRot);
registerByteBufPacketConsumer(PacketConsumers.LEVEL_CHUNK_WITH_LIGHT, this.packetIds.clientboundLevelChunkWithLightPacket());
registerByteBufPacketConsumer(PacketConsumers.SECTION_BLOCK_UPDATE, this.packetIds.clientboundSectionBlocksUpdatePacket());
registerByteBufPacketConsumer(PacketConsumers.BLOCK_UPDATE, this.packetIds.clientboundBlockUpdatePacket());

View File

@@ -1611,8 +1611,9 @@ public class PacketConsumers {
}
} else {
BukkitProjectileManager.instance().projectileByEntityId(entityId).ifPresent(customProjectile -> {
event.replacePacket(ProjectilePacketHandler.convertAddCustomProjectPacket(packet));
user.entityPacketHandlers().put(entityId, new ProjectilePacketHandler(customProjectile));
ProjectilePacketHandler handler = new ProjectilePacketHandler(customProjectile, entityId);
event.replacePacket(handler.convertAddCustomProjectilePacket(packet));
user.entityPacketHandlers().put(entityId, handler);
});
}
} catch (Exception e) {
@@ -1633,17 +1634,6 @@ public class PacketConsumers {
}
};
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> MOVE_ENTITY = (user, event, packet) -> {
try {
int entityId = BukkitInjector.internalFieldAccessor().field$ClientboundMoveEntityPacket$entityId(packet);
if (BukkitFurnitureManager.instance().isFurnitureRealEntity(entityId)) {
event.setCancelled(true);
}
} catch (Exception e) {
CraftEngine.instance().logger().warn("Failed to handle ClientboundMoveEntityPacket$Pos", e);
}
};
public static final BiConsumer<NetWorkUser, ByteBufPacketEvent> REMOVE_ENTITY = (user, event) -> {
try {
FriendlyByteBuf buf = event.getBuffer();
@@ -2215,7 +2205,18 @@ public class PacketConsumers {
}
};
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> MOVE_AND_ROTATE_ENTITY = (user, event, packet) -> {
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> MOVE_POS_ENTITY = (user, event, packet) -> {
try {
int entityId = BukkitInjector.internalFieldAccessor().field$ClientboundMoveEntityPacket$entityId(packet);
if (BukkitFurnitureManager.instance().isFurnitureRealEntity(entityId)) {
event.setCancelled(true);
}
} catch (Exception e) {
CraftEngine.instance().logger().warn("Failed to handle ClientboundMoveEntityPacket", e);
}
};
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> MOVE_POS_AND_ROTATE_ENTITY = (user, event, packet) -> {
try {
int entityId = BukkitInjector.internalFieldAccessor().field$ClientboundMoveEntityPacket$entityId(packet);
EntityPacketHandler handler = user.entityPacketHandlers().get(entityId);

View File

@@ -26,22 +26,24 @@ import java.util.UUID;
public class ProjectilePacketHandler implements EntityPacketHandler {
private final CustomProjectile projectile;
private final Object cachedPacket;
private final List<Object> cachedData;
public ProjectilePacketHandler(CustomProjectile projectile) {
public ProjectilePacketHandler(CustomProjectile projectile, int entityId) {
this.projectile = projectile;
this.cachedData = createCustomProjectileEntityDataValues();
this.cachedPacket = FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityId, this.cachedData);
}
@Override
public void handleSetEntityData(NetWorkUser user, ByteBufPacketEvent event) {
FriendlyByteBuf buf = event.getBuffer();
int id = buf.readVarInt();
List<Object> packedItems = FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$unpack(buf);
List<Object> newPackedItems = convertCustomProjectileSetEntityDataPacket(packedItems);
event.setChanged(true);
buf.clear();
buf.writeVarInt(event.packetID());
buf.writeVarInt(id);
FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$pack(newPackedItems, buf);
FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$pack(this.cachedData, buf);
}
@Override
@@ -52,11 +54,15 @@ public class ProjectilePacketHandler implements EntityPacketHandler {
@Override
public void handleMoveAndRotate(NetWorkUser user, NMSPacketEvent event, Object packet) {
Object converted = convertCustomProjectileMovePacket(packet);
event.replacePacket(converted);
int entityId = BukkitInjector.internalFieldAccessor().field$ClientboundMoveEntityPacket$entityId(packet);
Object converted = convertCustomProjectileMovePacket(packet, entityId);
event.replacePacket(FastNMS.INSTANCE.constructor$ClientboundBundlePacket(List.of(
this.cachedPacket,
converted
)));
}
public static Object convertAddCustomProjectPacket(Object packet) {
public Object convertAddCustomProjectilePacket(Object packet) {
int entityId = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$entityId(packet);
UUID uuid = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$uuid(packet);
double x = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$x(packet);
@@ -70,11 +76,7 @@ public class ProjectilePacketHandler implements EntityPacketHandler {
double ya = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$ya(packet);
double za = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$za(packet);
double yHeadRot = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$yHeadRot(packet);
return FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
entityId, uuid, x, y, z,
MCUtils.clamp(-xRot, -90.0F, 90.0F), -yRot,
type, data, FastNMS.INSTANCE.constructor$Vec3(xa, ya, za), yHeadRot
);
return FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(entityId, uuid, x, y, z, MCUtils.clamp(-xRot, -90.0F, 90.0F), -yRot, type, data, FastNMS.INSTANCE.constructor$Vec3(xa, ya, za), yHeadRot);
}
private Object convertCustomProjectilePositionSyncPacket(Object packet) {
@@ -89,24 +91,11 @@ public class ProjectilePacketHandler implements EntityPacketHandler {
return FastNMS.INSTANCE.constructor$ClientboundEntityPositionSyncPacket(entityId, newPositionMoveRotation, onGround);
}
// 将原有的投掷物的entity data转化为展示实体的数据包
public List<Object> convertCustomProjectileSetEntityDataPacket(List<Object> packedItems) {
List<Object> newPackedItems = new ArrayList<>();
for (Object packedItem : packedItems) {
int entityDataId = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$id(packedItem);
if (entityDataId < 8) {
newPackedItems.add(packedItem);
}
}
newPackedItems.addAll(createCustomProjectileEntityDataValues());
return newPackedItems;
}
private List<Object> createCustomProjectileEntityDataValues() {
public List<Object> createCustomProjectileEntityDataValues() {
List<Object> itemDisplayValues = new ArrayList<>();
Optional<CustomItem<ItemStack>> customItem = BukkitItemManager.instance().getCustomItem(this.projectile.metadata().item());
if (customItem.isEmpty()) return itemDisplayValues;
ProjectileMeta meta = projectile.metadata();
ProjectileMeta meta = this.projectile.metadata();
Item<?> displayedItem = customItem.get().buildItem(ItemBuildContext.EMPTY);
// 我们应当使用新的展示物品的组件覆盖原物品的组件,以完成附魔,附魔光效等组件的继承
displayedItem = this.projectile.item().mergeCopy(displayedItem);
@@ -114,18 +103,17 @@ public class ProjectilePacketHandler implements EntityPacketHandler {
ItemDisplayEntityData.Translation.addEntityDataIfNotDefaultValue(meta.translation(), itemDisplayValues);
ItemDisplayEntityData.RotationLeft.addEntityDataIfNotDefaultValue(meta.rotation(), itemDisplayValues);
if (VersionHelper.isOrAbove1_20_2()) {
ItemDisplayEntityData.TransformationInterpolationDuration.addEntityDataIfNotDefaultValue(2, itemDisplayValues);
ItemDisplayEntityData.PositionRotationInterpolationDuration.addEntityDataIfNotDefaultValue(2, itemDisplayValues);
ItemDisplayEntityData.TransformationInterpolationDuration.addEntityDataIfNotDefaultValue(1, itemDisplayValues);
ItemDisplayEntityData.PositionRotationInterpolationDuration.addEntityDataIfNotDefaultValue(1, itemDisplayValues);
} else {
ItemDisplayEntityData.InterpolationDuration.addEntityDataIfNotDefaultValue(2, itemDisplayValues);
ItemDisplayEntityData.InterpolationDuration.addEntityDataIfNotDefaultValue(1, itemDisplayValues);
}
ItemDisplayEntityData.DisplayedItem.addEntityDataIfNotDefaultValue(displayedItem.getLiteralObject(), itemDisplayValues);
ItemDisplayEntityData.DisplayType.addEntityDataIfNotDefaultValue(meta.displayType().id(), itemDisplayValues);
return itemDisplayValues;
}
private static Object convertCustomProjectileMovePacket(Object packet) {
int entityId = BukkitInjector.internalFieldAccessor().field$ClientboundMoveEntityPacket$entityId(packet);
private Object convertCustomProjectileMovePacket(Object packet, int entityId) {
short xa = FastNMS.INSTANCE.field$ClientboundMoveEntityPacket$xa(packet);
short ya = FastNMS.INSTANCE.field$ClientboundMoveEntityPacket$ya(packet);
short za = FastNMS.INSTANCE.field$ClientboundMoveEntityPacket$za(packet);

View File

@@ -6634,4 +6634,15 @@ public class Reflections {
)
);
public static final Class<?> clazz$ServerEntity = requireNonNull(
BukkitReflectionUtils.findReobfOrMojmapClass(
"server.level.EntityTrackerEntry",
"server.level.ServerEntity")
);
public static final Field field$ServerEntity$updateInterval = requireNonNull(
ReflectionUtils.getInstanceDeclaredField(
clazz$ServerEntity, int.class, 0
)
);
}

View File

@@ -1,7 +1,7 @@
package net.momirealms.craftengine.core.item;
import net.momirealms.craftengine.core.entity.projectile.ProjectileMeta;
import net.momirealms.craftengine.core.entity.ItemDisplayContext;
import net.momirealms.craftengine.core.entity.projectile.ProjectileMeta;
import net.momirealms.craftengine.core.item.modifier.EquippableModifier;
import net.momirealms.craftengine.core.item.modifier.ItemDataModifier;
import net.momirealms.craftengine.core.pack.misc.EquipmentGeneration;