diff --git a/README.md b/README.md index b77184c..a3b1a3d 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,27 @@ A plugin for [PumpkinMC](https://pumpkinmc.org/) that adds support for [PaperMC] 4. **Initialize**: Restart PumpkinMC. This creates a new `patchbukkit/` directory in your server root. 5. **Add Plugins**: Drop your .jar plugin files (Paper/Spigot/Bukkit) into the newly created `patchbukkit/patchbukkit-plugins/` folder and restart. +## GraalVM (Ecosystem Compatibility) + +PatchBukkit is a **native Rust plugin** that embeds a JVM for Bukkit/Paper/Spigot plugins. For +maximum ecosystem compatibility, the recommended GraalVM usage is **JVM mode**, not native-image. + +**Recommended approach** +- Use GraalVM JDK as a drop-in replacement for a standard JDK. +- Keep plugins running on the JVM (dynamic classloading, reflection, JNI/FFM). + +**Why not native-image yet** +- Bukkit/Paper plugins rely heavily on dynamic classloading, reflection, and Java agents. +- PatchBukkit embeds a JVM and uses JNI/FFM; native-image would require extensive config + and compatibility work across the plugin ecosystem. + +**Suggested JVM flags (starting point)** +- `-XX:+UseG1GC` +- `-XX:+UseStringDeduplication` +- `-Dpolyglot.engine.WarnInterpreterOnly=false` + +These are general JVM tuning hints; validate against your workload. + ## Development If you wish to contribute to PatchBukkit, follow the following steps: diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/PatchBukkitServer.java b/java/patchbukkit/src/main/java/org/patchbukkit/PatchBukkitServer.java index a982154..dea19e6 100644 --- a/java/patchbukkit/src/main/java/org/patchbukkit/PatchBukkitServer.java +++ b/java/patchbukkit/src/main/java/org/patchbukkit/PatchBukkitServer.java @@ -1238,10 +1238,7 @@ public void setMotd(@NotNull String motd) { @Override public @NotNull WarningState getWarningState() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getWarningState'" - ); + return WarningState.DEFAULT; } @Override diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/bridge/BridgeUtils.java b/java/patchbukkit/src/main/java/org/patchbukkit/bridge/BridgeUtils.java index 083a548..3389110 100644 --- a/java/patchbukkit/src/main/java/org/patchbukkit/bridge/BridgeUtils.java +++ b/java/patchbukkit/src/main/java/org/patchbukkit/bridge/BridgeUtils.java @@ -1,5 +1,8 @@ package org.patchbukkit.bridge; +import org.bukkit.Location; +import org.patchbukkit.world.PatchBukkitWorld; + import java.util.UUID; public class BridgeUtils { @@ -10,4 +13,60 @@ public static UUID convertUuid(patchbukkit.common.UUID uuid) { public static patchbukkit.common.UUID convertUuid(UUID uuid) { return patchbukkit.common.UUID.newBuilder().setValue(uuid.toString()).build(); } + + public static Location convertLocation(patchbukkit.common.Location location) { + if (location == null || location.getWorld() == null || location.getWorld().getUuid() == null) { + return null; + } + + var world = PatchBukkitWorld.getOrCreate(convertUuid(location.getWorld().getUuid())); + var position = location.getPosition(); + if (position == null) { + return null; + } + + return new Location( + world, + position.getX(), + position.getY(), + position.getZ(), + location.getYaw(), + location.getPitch() + ); + } + + public static patchbukkit.common.Location convertLocation(Location location) { + if (location == null || location.getWorld() == null) { + return null; + } + + return patchbukkit.common.Location.newBuilder() + .setWorld(patchbukkit.common.World.newBuilder() + .setUuid(convertUuid(location.getWorld().getUID())) + .build()) + .setPosition(patchbukkit.common.Vec3.newBuilder() + .setX(location.getX()) + .setY(location.getY()) + .setZ(location.getZ()) + .build()) + .setYaw(location.getYaw()) + .setPitch(location.getPitch()) + .build(); + } + + public static PatchBukkitWorld convertWorld(patchbukkit.common.World world) { + if (world == null || world.getUuid() == null) { + return null; + } + return PatchBukkitWorld.getOrCreate(convertUuid(world.getUuid())); + } + + public static patchbukkit.common.World convertWorld(org.bukkit.World world) { + if (world == null) { + return null; + } + return patchbukkit.common.World.newBuilder() + .setUuid(convertUuid(world.getUID())) + .build(); + } } diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitAbstractArrow.java b/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitAbstractArrow.java new file mode 100644 index 0000000..0328a34 --- /dev/null +++ b/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitAbstractArrow.java @@ -0,0 +1,158 @@ +package org.patchbukkit.entity; + +import java.util.UUID; + +import org.bukkit.Material; +import org.bukkit.Sound; +import org.bukkit.block.Block; +import org.bukkit.entity.AbstractArrow; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import java.util.Collections; +import java.util.List; + +public class PatchBukkitAbstractArrow extends PatchBukkitProjectile implements AbstractArrow { + private PickupStatus pickupStatus = PickupStatus.ALLOWED; + private double damage = 2.0; + private int knockbackStrength = 0; + private int pierceLevel = 0; + private boolean critical = false; + private boolean inBlock = false; + private boolean shotFromCrossbow = false; + private boolean noPhysics = false; + private int lifetimeTicks = 0; + private ItemStack item = new ItemStack(Material.ARROW, 1); + private ItemStack weapon = null; + private Sound hitSound = Sound.ENTITY_ARROW_HIT; + + public PatchBukkitAbstractArrow(UUID uuid, String name) { + super(uuid, name); + warnSnapshotLimitation( + "entity:abstract_arrow_snapshot", + "PatchBukkitAbstractArrow uses fallback local values; Pumpkin does not fully expose arrow/bow runtime state yet." + ); + } + + public Block getAttachedBlock() { + return null; + } + + @Override + public @NotNull List getAttachedBlocks() { + return Collections.emptyList(); + } + + public double getDamage() { + return damage; + } + + public void setDamage(double damage) { + this.damage = damage; + } + + public int getKnockbackStrength() { + return knockbackStrength; + } + + public void setKnockbackStrength(int knockbackStrength) { + this.knockbackStrength = knockbackStrength; + } + + public PickupStatus getPickupStatus() { + return pickupStatus; + } + + public void setPickupStatus(PickupStatus status) { + this.pickupStatus = status; + } + + public int getPierceLevel() { + return pierceLevel; + } + + public void setPierceLevel(int pierceLevel) { + this.pierceLevel = pierceLevel; + } + + public boolean isCritical() { + return critical; + } + + public void setCritical(boolean critical) { + this.critical = critical; + } + + public boolean isInBlock() { + return inBlock; + } + + public boolean isShotFromCrossbow() { + return shotFromCrossbow; + } + + public void setShotFromCrossbow(boolean shotFromCrossbow) { + this.shotFromCrossbow = shotFromCrossbow; + } + + public ItemStack getItem() { + return item; + } + + public void setItem(ItemStack item) { + if (item != null) { + this.item = item; + } + } + + @Override + public @NotNull ItemStack getItemStack() { + return getItem(); + } + + @Override + public void setItemStack(@NotNull ItemStack stack) { + setItem(stack); + } + + public ItemStack getWeapon() { + return weapon != null ? weapon : item; + } + + public void setWeapon(ItemStack weapon) { + this.weapon = weapon; + } + + public Sound getHitSound() { + return hitSound; + } + + public void setHitSound(Sound sound) { + if (sound != null) { + this.hitSound = sound; + } + } + + public boolean hasNoPhysics() { + return noPhysics; + } + + public void setNoPhysics(boolean noPhysics) { + this.noPhysics = noPhysics; + } + + public int getLifetimeTicks() { + return lifetimeTicks; + } + + public void setLifetimeTicks(int ticks) { + this.lifetimeTicks = ticks; + } + + @Override + public void setShooter(org.bukkit.projectiles.ProjectileSource shooter, boolean update) { + setShooter(shooter); + if (update) { + setHasBeenShot(true); + } + } +} diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitArmorStand.java b/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitArmorStand.java new file mode 100644 index 0000000..86cf828 --- /dev/null +++ b/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitArmorStand.java @@ -0,0 +1,322 @@ +package org.patchbukkit.entity; + +import java.util.Collections; +import java.util.Set; +import java.util.UUID; + +import org.bukkit.Material; +import org.bukkit.entity.ArmorStand; +import org.bukkit.inventory.EntityEquipment; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; +import org.bukkit.util.EulerAngle; + +import io.papermc.paper.math.Rotations; +import org.jetbrains.annotations.NotNull; + +public class PatchBukkitArmorStand extends PatchBukkitLivingEntity implements ArmorStand { + + public PatchBukkitArmorStand(UUID uuid, String name) { + super(uuid, name); + warnSnapshotLimitation( + "entity:armor_stand_snapshot", + "PatchBukkitArmorStand currently mirrors a limited local snapshot; unsupported fields may not reflect Pumpkin state." + ); + } + + @Override + public @NotNull ItemStack getItemInHand() { + return new ItemStack(Material.AIR); + } + + @Override + public void setItemInHand(ItemStack item) { + // TODO: integrate with equipment + throw new UnsupportedOperationException("Unimplemented method 'setItemInHand'"); + } + + @Override + public @NotNull ItemStack getBoots() { + return new ItemStack(Material.AIR); + } + + @Override + public void setBoots(ItemStack item) { + throw new UnsupportedOperationException("Unimplemented method 'setBoots'"); + } + + @Override + public @NotNull ItemStack getLeggings() { + return new ItemStack(Material.AIR); + } + + @Override + public void setLeggings(ItemStack item) { + throw new UnsupportedOperationException("Unimplemented method 'setLeggings'"); + } + + @Override + public @NotNull ItemStack getChestplate() { + return new ItemStack(Material.AIR); + } + + @Override + public void setChestplate(ItemStack item) { + throw new UnsupportedOperationException("Unimplemented method 'setChestplate'"); + } + + @Override + public @NotNull ItemStack getHelmet() { + return new ItemStack(Material.AIR); + } + + @Override + public void setHelmet(ItemStack item) { + throw new UnsupportedOperationException("Unimplemented method 'setHelmet'"); + } + + @Override + public @NotNull EulerAngle getBodyPose() { + return EulerAngle.ZERO; + } + + @Override + public void setBodyPose(EulerAngle pose) { + throw new UnsupportedOperationException("Unimplemented method 'setBodyPose'"); + } + + @Override + public @NotNull EulerAngle getLeftArmPose() { + return EulerAngle.ZERO; + } + + @Override + public void setLeftArmPose(EulerAngle pose) { + throw new UnsupportedOperationException("Unimplemented method 'setLeftArmPose'"); + } + + @Override + public @NotNull EulerAngle getRightArmPose() { + return EulerAngle.ZERO; + } + + @Override + public void setRightArmPose(EulerAngle pose) { + throw new UnsupportedOperationException("Unimplemented method 'setRightArmPose'"); + } + + @Override + public @NotNull EulerAngle getLeftLegPose() { + return EulerAngle.ZERO; + } + + @Override + public void setLeftLegPose(EulerAngle pose) { + throw new UnsupportedOperationException("Unimplemented method 'setLeftLegPose'"); + } + + @Override + public @NotNull EulerAngle getRightLegPose() { + return EulerAngle.ZERO; + } + + @Override + public void setRightLegPose(EulerAngle pose) { + throw new UnsupportedOperationException("Unimplemented method 'setRightLegPose'"); + } + + @Override + public @NotNull EulerAngle getHeadPose() { + return EulerAngle.ZERO; + } + + @Override + public void setHeadPose(EulerAngle pose) { + throw new UnsupportedOperationException("Unimplemented method 'setHeadPose'"); + } + + @Override + public boolean hasBasePlate() { + return true; + } + + @Override + public void setBasePlate(boolean basePlate) { + throw new UnsupportedOperationException("Unimplemented method 'setBasePlate'"); + } + + @Override + public boolean isVisible() { + return true; + } + + @Override + public void setVisible(boolean visible) { + throw new UnsupportedOperationException("Unimplemented method 'setVisible'"); + } + + @Override + public boolean hasArms() { + return true; + } + + @Override + public void setArms(boolean arms) { + throw new UnsupportedOperationException("Unimplemented method 'setArms'"); + } + + @Override + public boolean isSmall() { + return false; + } + + @Override + public void setSmall(boolean small) { + throw new UnsupportedOperationException("Unimplemented method 'setSmall'"); + } + + @Override + public boolean isMarker() { + return false; + } + + @Override + public void setMarker(boolean marker) { + throw new UnsupportedOperationException("Unimplemented method 'setMarker'"); + } + + @Override + public void addEquipmentLock(@NotNull EquipmentSlot slot, @NotNull LockType lockType) { + throw new UnsupportedOperationException("Unimplemented method 'addEquipmentLock'"); + } + + @Override + public void removeEquipmentLock(@NotNull EquipmentSlot slot, @NotNull LockType lockType) { + throw new UnsupportedOperationException("Unimplemented method 'removeEquipmentLock'"); + } + + @Override + public boolean hasEquipmentLock(@NotNull EquipmentSlot slot, @NotNull LockType lockType) { + return false; + } + + @Override + public boolean canMove() { + return true; + } + + @Override + public void setCanMove(boolean move) { + throw new UnsupportedOperationException("Unimplemented method 'setCanMove'"); + } + + @Override + public EntityEquipment getEquipment() { + return null; + } + + @Override + public boolean canTick() { + return true; + } + + @Override + public void setCanTick(boolean tick) { + throw new UnsupportedOperationException("Unimplemented method 'setCanTick'"); + } + + @Override + public @NotNull ItemStack getItem(@NotNull EquipmentSlot slot) { + return new ItemStack(Material.AIR); + } + + @Override + public void setItem(@NotNull EquipmentSlot slot, ItemStack item) { + throw new UnsupportedOperationException("Unimplemented method 'setItem'"); + } + + @Override + public @NotNull Set getDisabledSlots() { + return Collections.emptySet(); + } + + @Override + public void setDisabledSlots(@NotNull EquipmentSlot @NotNull... slots) { + throw new UnsupportedOperationException("Unimplemented method 'setDisabledSlots'"); + } + + @Override + public void addDisabledSlots(@NotNull EquipmentSlot @NotNull... slots) { + throw new UnsupportedOperationException("Unimplemented method 'addDisabledSlots'"); + } + + @Override + public void removeDisabledSlots(@NotNull EquipmentSlot @NotNull... slots) { + throw new UnsupportedOperationException("Unimplemented method 'removeDisabledSlots'"); + } + + @Override + public boolean isSlotDisabled(@NotNull EquipmentSlot slot) { + return false; + } + + @Override + public @NotNull Rotations getBodyRotations() { + return Rotations.ZERO; + } + + @Override + public void setBodyRotations(@NotNull Rotations rotations) { + throw new UnsupportedOperationException("Unimplemented method 'setBodyRotations'"); + } + + @Override + public @NotNull Rotations getLeftArmRotations() { + return Rotations.ZERO; + } + + @Override + public void setLeftArmRotations(@NotNull Rotations rotations) { + throw new UnsupportedOperationException("Unimplemented method 'setLeftArmRotations'"); + } + + @Override + public @NotNull Rotations getRightArmRotations() { + return Rotations.ZERO; + } + + @Override + public void setRightArmRotations(@NotNull Rotations rotations) { + throw new UnsupportedOperationException("Unimplemented method 'setRightArmRotations'"); + } + + @Override + public @NotNull Rotations getLeftLegRotations() { + return Rotations.ZERO; + } + + @Override + public void setLeftLegRotations(@NotNull Rotations rotations) { + throw new UnsupportedOperationException("Unimplemented method 'setLeftLegRotations'"); + } + + @Override + public @NotNull Rotations getRightLegRotations() { + return Rotations.ZERO; + } + + @Override + public void setRightLegRotations(@NotNull Rotations rotations) { + throw new UnsupportedOperationException("Unimplemented method 'setRightLegRotations'"); + } + + @Override + public @NotNull Rotations getHeadRotations() { + return Rotations.ZERO; + } + + @Override + public void setHeadRotations(@NotNull Rotations rotations) { + throw new UnsupportedOperationException("Unimplemented method 'setHeadRotations'"); + } +} diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitEgg.java b/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitEgg.java new file mode 100644 index 0000000..d108c28 --- /dev/null +++ b/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitEgg.java @@ -0,0 +1,31 @@ +package org.patchbukkit.entity; + +import java.util.UUID; + +import org.bukkit.Material; +import org.bukkit.entity.Egg; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +public class PatchBukkitEgg extends PatchBukkitProjectile implements Egg { + private ItemStack itemStack; + + public PatchBukkitEgg(@NotNull UUID uuid) { + this(uuid, new ItemStack(Material.EGG)); + } + + public PatchBukkitEgg(@NotNull UUID uuid, @NotNull ItemStack itemStack) { + super(uuid, "EGG"); + this.itemStack = itemStack; + } + + @Override + public @NotNull ItemStack getItem() { + return itemStack; + } + + @Override + public void setItem(@NotNull ItemStack itemStack) { + this.itemStack = itemStack; + } +} diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitEntity.java b/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitEntity.java index c950b98..46e73e5 100644 --- a/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitEntity.java +++ b/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitEntity.java @@ -6,6 +6,8 @@ import java.util.Set; import java.util.UUID; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Logger; import org.bukkit.Bukkit; import org.bukkit.EntityEffect; @@ -51,9 +53,11 @@ public class PatchBukkitEntity implements Entity { + private static final Logger LOGGER = Logger.getLogger(PatchBukkitEntity.class.getName()); protected final UUID uuid; private final String name; private static PermissibleBase perm; + private static final Set WARNED_LIMITATIONS = ConcurrentHashMap.newKeySet(); private boolean visibleByDefault = true; public PatchBukkitEntity( @@ -64,6 +68,12 @@ public PatchBukkitEntity( this.name = name; } + protected final void warnSnapshotLimitation(String key, String message) { + if (WARNED_LIMITATIONS.add(key)) { + LOGGER.warning(message); + } + } + private static PermissibleBase getPermissibleBase() { if (PatchBukkitEntity.perm == null) { PatchBukkitEntity.perm = new PermissibleBase(new ServerOperator() { diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitExperienceOrb.java b/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitExperienceOrb.java new file mode 100644 index 0000000..e434d97 --- /dev/null +++ b/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitExperienceOrb.java @@ -0,0 +1,69 @@ +package org.patchbukkit.entity; + +import java.util.UUID; + +import org.bukkit.entity.ExperienceOrb; +import org.jetbrains.annotations.Nullable; + +public class PatchBukkitExperienceOrb extends PatchBukkitEntity implements ExperienceOrb { + private int experience = 0; + private int count = 1; + private UUID triggerEntityId; + private UUID sourceEntityId; + private SpawnReason spawnReason = SpawnReason.UNKNOWN; + + public PatchBukkitExperienceOrb(UUID uuid) { + super(uuid, "EXPERIENCE_ORB"); + warnSnapshotLimitation( + "entity:experience_orb_snapshot", + "PatchBukkitExperienceOrb values are local snapshots and are not continuously synchronized from Pumpkin." + ); + } + + @Override + public int getExperience() { + return experience; + } + + @Override + public void setExperience(int experience) { + this.experience = experience; + } + + @Override + public int getCount() { + return count; + } + + @Override + public void setCount(int count) { + this.count = count; + } + + @Override + public @Nullable UUID getTriggerEntityId() { + return triggerEntityId; + } + + @Override + public @Nullable UUID getSourceEntityId() { + return sourceEntityId; + } + + @Override + public SpawnReason getSpawnReason() { + return spawnReason; + } + + public void setTriggerEntityId(@Nullable UUID triggerEntityId) { + this.triggerEntityId = triggerEntityId; + } + + public void setSourceEntityId(@Nullable UUID sourceEntityId) { + this.sourceEntityId = sourceEntityId; + } + + public void setSpawnReason(SpawnReason spawnReason) { + this.spawnReason = spawnReason != null ? spawnReason : SpawnReason.UNKNOWN; + } +} diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitFishHook.java b/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitFishHook.java new file mode 100644 index 0000000..5d9b434 --- /dev/null +++ b/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitFishHook.java @@ -0,0 +1,214 @@ +package org.patchbukkit.entity; + +import java.util.UUID; + +import org.bukkit.entity.Entity; +import org.bukkit.entity.FishHook; +import org.bukkit.inventory.EquipmentSlot; +import org.jetbrains.annotations.Nullable; + +public class PatchBukkitFishHook extends PatchBukkitProjectile implements FishHook { + private int minWaitTime = 100; + private int maxWaitTime = 600; + private int minLureTime = 20; + private int maxLureTime = 80; + private float minLureAngle = 0.0f; + private float maxLureAngle = 360.0f; + private boolean applyLure = true; + private double biteChance = 0.0; + private boolean inOpenWater = false; + private Entity hookedEntity; + private boolean skyInfluenced = true; + private boolean rainInfluenced = true; + private HookState hookState = HookState.UNHOOKED; + private int waitTime = 0; + private int timeUntilBite = 0; + + public PatchBukkitFishHook(UUID uuid) { + super(uuid, "FISHING_BOBBER"); + warnSnapshotLimitation( + "entity:fish_hook_snapshot", + "PatchBukkitFishHook currently relies on local fallback state; values may differ from Pumpkin runtime state." + ); + } + + @Override + public int getMinWaitTime() { + return minWaitTime; + } + + @Override + public void setMinWaitTime(int minWaitTime) { + this.minWaitTime = minWaitTime; + } + + @Override + public int getMaxWaitTime() { + return maxWaitTime; + } + + @Override + public void setMaxWaitTime(int maxWaitTime) { + this.maxWaitTime = maxWaitTime; + } + + @Override + public void setWaitTime(int minWaitTime, int maxWaitTime) { + this.minWaitTime = minWaitTime; + this.maxWaitTime = maxWaitTime; + } + + @Override + public int getMinLureTime() { + return minLureTime; + } + + @Override + public void setMinLureTime(int minLureTime) { + this.minLureTime = minLureTime; + } + + @Override + public int getMaxLureTime() { + return maxLureTime; + } + + @Override + public void setMaxLureTime(int maxLureTime) { + this.maxLureTime = maxLureTime; + } + + @Override + public void setLureTime(int minLureTime, int maxLureTime) { + this.minLureTime = minLureTime; + this.maxLureTime = maxLureTime; + } + + @Override + public float getMinLureAngle() { + return minLureAngle; + } + + @Override + public void setMinLureAngle(float minLureAngle) { + this.minLureAngle = minLureAngle; + } + + @Override + public float getMaxLureAngle() { + return maxLureAngle; + } + + @Override + public void setMaxLureAngle(float maxLureAngle) { + this.maxLureAngle = maxLureAngle; + } + + @Override + public void setLureAngle(float minLureAngle, float maxLureAngle) { + this.minLureAngle = minLureAngle; + this.maxLureAngle = maxLureAngle; + } + + @Override + public boolean getApplyLure() { + return applyLure; + } + + @Override + public void setApplyLure(boolean applyLure) { + this.applyLure = applyLure; + } + + @Override + public double getBiteChance() { + return biteChance; + } + + @Override + public void setBiteChance(double biteChance) throws IllegalArgumentException { + if (biteChance < 0.0 || biteChance > 1.0) { + throw new IllegalArgumentException("Bite chance must be between 0.0 and 1.0"); + } + this.biteChance = biteChance; + } + + @Override + public boolean isInOpenWater() { + return inOpenWater; + } + + @Override + public @Nullable Entity getHookedEntity() { + return hookedEntity; + } + + @Override + public void setHookedEntity(@Nullable Entity entity) { + this.hookedEntity = entity; + } + + @Override + public boolean pullHookedEntity() { + return hookedEntity != null; + } + + @Override + public boolean isSkyInfluenced() { + return skyInfluenced; + } + + @Override + public void setSkyInfluenced(boolean skyInfluenced) { + this.skyInfluenced = skyInfluenced; + } + + @Override + public boolean isRainInfluenced() { + return rainInfluenced; + } + + @Override + public void setRainInfluenced(boolean rainInfluenced) { + this.rainInfluenced = rainInfluenced; + } + + @Override + public HookState getState() { + return hookState; + } + + @Override + public int getWaitTime() { + return waitTime; + } + + @Override + public void setWaitTime(int waitTime) { + this.waitTime = waitTime; + } + + @Override + public int getTimeUntilBite() { + return timeUntilBite; + } + + @Override + public void setTimeUntilBite(int timeUntilBite) throws IllegalArgumentException { + if (timeUntilBite < 0) { + throw new IllegalArgumentException("Time until bite cannot be negative"); + } + this.timeUntilBite = timeUntilBite; + } + + @Override + public void resetFishingState() { + hookState = HookState.UNHOOKED; + timeUntilBite = 0; + } + + @Override + public int retrieve(EquipmentSlot hand) { + return 0; + } +} diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitItem.java b/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitItem.java new file mode 100644 index 0000000..ed7ed4e --- /dev/null +++ b/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitItem.java @@ -0,0 +1,131 @@ +package org.patchbukkit.entity; + +import java.util.UUID; + +import org.bukkit.entity.Item; +import org.bukkit.inventory.ItemStack; +import net.kyori.adventure.util.TriState; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class PatchBukkitItem extends PatchBukkitEntity implements Item { + private ItemStack itemStack; + private int pickupDelay = 10; + private boolean unlimitedLifetime = false; + private boolean canMobPickup = true; + private boolean canPlayerPickup = true; + private UUID owner; + private UUID thrower; + private int health = 5; + private boolean willAge = true; + private TriState frictionState = TriState.NOT_SET; + + public PatchBukkitItem(@NotNull UUID uuid, @NotNull ItemStack itemStack) { + super(uuid, "ITEM"); + this.itemStack = itemStack; + warnSnapshotLimitation( + "entity:item_snapshot", + "PatchBukkitItem currently exposes local snapshot values and is not fully synchronized with Pumpkin item entity internals." + ); + } + + @Override + public @NotNull ItemStack getItemStack() { + return itemStack; + } + + @Override + public void setItemStack(@NotNull ItemStack stack) { + this.itemStack = stack; + } + + @Override + public int getPickupDelay() { + return pickupDelay; + } + + @Override + public void setPickupDelay(int delay) { + this.pickupDelay = delay; + } + + @Override + public void setUnlimitedLifetime(boolean value) { + this.unlimitedLifetime = value; + } + + @Override + public boolean isUnlimitedLifetime() { + return unlimitedLifetime; + } + + @Override + public void setCanMobPickup(boolean value) { + this.canMobPickup = value; + } + + @Override + public boolean canMobPickup() { + return canMobPickup; + } + + @Override + public void setCanPlayerPickup(boolean value) { + this.canPlayerPickup = value; + } + + @Override + public boolean canPlayerPickup() { + return canPlayerPickup; + } + + @Override + public void setOwner(@Nullable UUID owner) { + this.owner = owner; + } + + @Override + public @Nullable UUID getOwner() { + return owner; + } + + @Override + public void setThrower(@Nullable UUID thrower) { + this.thrower = thrower; + } + + @Override + public @Nullable UUID getThrower() { + return thrower; + } + + @Override + public int getHealth() { + return health; + } + + @Override + public void setHealth(int health) { + this.health = health; + } + + @Override + public boolean willAge() { + return willAge; + } + + @Override + public void setWillAge(boolean value) { + this.willAge = value; + } + + @Override + public @NotNull TriState getFrictionState() { + return frictionState; + } + + @Override + public void setFrictionState(@NotNull TriState state) { + this.frictionState = state; + } +} diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitProjectile.java b/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitProjectile.java new file mode 100644 index 0000000..ba83139 --- /dev/null +++ b/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitProjectile.java @@ -0,0 +1,88 @@ +package org.patchbukkit.entity; + +import java.util.UUID; + +import org.bukkit.entity.Entity; +import org.bukkit.entity.Projectile; +import org.bukkit.projectiles.ProjectileSource; +import org.bukkit.util.Vector; +import org.jetbrains.annotations.Nullable; + +public class PatchBukkitProjectile extends PatchBukkitEntity implements Projectile { + private ProjectileSource shooter; + private boolean bounce; + private boolean hasLeftShooter; + private boolean hasBeenShot; + private UUID ownerUuid; + + public PatchBukkitProjectile(UUID uuid, String name) { + super(uuid, name); + warnSnapshotLimitation( + "entity:projectile_snapshot", + "PatchBukkit projectile wrappers currently keep local snapshot state and may diverge from Pumpkin runtime state." + ); + } + + @Override + public @Nullable ProjectileSource getShooter() { + return shooter; + } + + @Override + public void setShooter(@Nullable ProjectileSource shooter) { + this.shooter = shooter; + if (shooter instanceof Entity entity) { + this.ownerUuid = entity.getUniqueId(); + } + } + + @Override + public boolean doesBounce() { + return bounce; + } + + @Override + public void setBounce(boolean bounce) { + this.bounce = bounce; + } + + @Override + public boolean hasLeftShooter() { + return hasLeftShooter; + } + + @Override + public void setHasLeftShooter(boolean hasLeftShooter) { + this.hasLeftShooter = hasLeftShooter; + } + + @Override + public boolean hasBeenShot() { + return hasBeenShot; + } + + @Override + public void setHasBeenShot(boolean hasBeenShot) { + this.hasBeenShot = hasBeenShot; + } + + @Override + public boolean canHitEntity(Entity entity) { + return true; + } + + @Override + public void hitEntity(Entity entity) { + // No-op + } + + @Override + public void hitEntity(Entity entity, Vector vector) { + // No-op + } + + @Override + public UUID getOwnerUniqueId() { + return ownerUuid; + } +} diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/events/PatchBukkitEventFactory.java b/java/patchbukkit/src/main/java/org/patchbukkit/events/PatchBukkitEventFactory.java index 14c24f1..2739ac6 100644 --- a/java/patchbukkit/src/main/java/org/patchbukkit/events/PatchBukkitEventFactory.java +++ b/java/patchbukkit/src/main/java/org/patchbukkit/events/PatchBukkitEventFactory.java @@ -5,14 +5,141 @@ import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; import org.bukkit.Bukkit; +import org.bukkit.Location; import org.bukkit.entity.Player; +import org.bukkit.entity.Entity; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockDamageAbortEvent; +import org.bukkit.event.block.BlockDamageEvent; +import org.bukkit.event.block.BlockDispenseEvent; +import org.bukkit.event.block.BlockDropItemEvent; +import org.bukkit.event.block.BlockExplodeEvent; +import org.bukkit.event.block.BlockFadeEvent; +import org.bukkit.event.block.BlockFertilizeEvent; +import org.bukkit.event.block.BlockFormEvent; +import org.bukkit.event.block.BlockFromToEvent; +import org.bukkit.event.block.BlockGrowEvent; +import org.bukkit.event.block.BlockPistonExtendEvent; +import org.bukkit.event.block.BlockPistonRetractEvent; +import org.bukkit.event.block.BlockRedstoneEvent; +import org.bukkit.event.block.BlockMultiPlaceEvent; +import org.bukkit.event.block.BlockPhysicsEvent; +import org.bukkit.event.block.BlockCanBuildEvent; +import org.bukkit.event.block.BlockBurnEvent; +import org.bukkit.event.block.BlockIgniteEvent; +import org.bukkit.event.block.BlockSpreadEvent; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.block.NotePlayEvent; +import org.bukkit.event.block.SignChangeEvent; +import org.bukkit.event.block.TNTPrimeEvent; +import org.bukkit.event.block.MoistureChangeEvent; +import org.bukkit.event.block.SpongeAbsorbEvent; +import org.bukkit.event.block.FluidLevelChangeEvent; +import org.bukkit.Instrument; +import org.bukkit.Note; +import org.bukkit.event.world.SpawnChangeEvent; +import org.bukkit.event.server.ServerListPingEvent; +import org.bukkit.event.server.PluginEnableEvent; +import org.bukkit.event.server.PluginDisableEvent; +import org.bukkit.event.server.ServiceRegisterEvent; +import org.bukkit.event.server.ServiceUnregisterEvent; +import org.bukkit.plugin.RegisteredServiceProvider; +import org.bukkit.plugin.ServicePriority; +import org.bukkit.event.player.AsyncPlayerChatEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerCommandPreprocessEvent; +import org.bukkit.event.player.PlayerCommandSendEvent; +import org.bukkit.event.player.PlayerLoginEvent; +import org.bukkit.event.player.AsyncPlayerPreLoginEvent; +import org.bukkit.event.player.PlayerTeleportEvent; +import org.bukkit.event.player.PlayerChangedWorldEvent; +import org.bukkit.event.player.PlayerGameModeChangeEvent; +import org.bukkit.event.player.PlayerAdvancementDoneEvent; +import org.bukkit.event.player.PlayerAnimationEvent; +import org.bukkit.event.player.PlayerAnimationType; +import org.bukkit.event.player.PlayerArmorStandManipulateEvent; +import org.bukkit.event.player.PlayerBedEnterEvent; +import org.bukkit.event.player.PlayerBedLeaveEvent; +import org.bukkit.event.player.PlayerBucketEmptyEvent; +import org.bukkit.event.player.PlayerBucketFillEvent; +import org.bukkit.event.player.PlayerBucketEntityEvent; +import org.bukkit.event.player.PlayerChangedMainHandEvent; +import org.bukkit.event.player.PlayerRegisterChannelEvent; +import org.bukkit.event.player.PlayerUnregisterChannelEvent; +import org.bukkit.event.player.PlayerDropItemEvent; +import org.bukkit.event.player.PlayerEditBookEvent; +import org.bukkit.event.player.PlayerEggThrowEvent; +import org.bukkit.event.player.PlayerExpChangeEvent; +import org.bukkit.event.player.PlayerFishEvent; +import org.bukkit.event.player.PlayerInteractEntityEvent; +import org.bukkit.event.player.PlayerInteractAtEntityEvent; +import org.bukkit.event.player.PlayerItemHeldEvent; +import org.bukkit.event.player.PlayerItemDamageEvent; +import org.bukkit.event.player.PlayerItemBreakEvent; +import org.bukkit.event.player.PlayerItemConsumeEvent; +import org.bukkit.event.player.PlayerItemMendEvent; +import org.bukkit.event.player.PlayerKickEvent; +import org.bukkit.event.player.PlayerLevelChangeEvent; +import org.bukkit.event.player.PlayerToggleSneakEvent; +import org.bukkit.event.player.PlayerToggleSprintEvent; +import org.bukkit.event.player.PlayerToggleFlightEvent; +import org.bukkit.event.player.PlayerSwapHandItemsEvent; +import org.bukkit.event.player.PlayerResourcePackStatusEvent; +import org.bukkit.event.player.PlayerRespawnEvent; +import org.bukkit.event.player.PlayerPickupArrowEvent; +import org.bukkit.event.player.PlayerPortalEvent; +import org.bukkit.event.player.PlayerRecipeDiscoverEvent; +import org.bukkit.event.player.PlayerRiptideEvent; +import org.bukkit.event.player.PlayerShearEntityEvent; +import org.bukkit.event.player.PlayerStatisticIncrementEvent; +import org.bukkit.event.player.PlayerVelocityEvent; +import org.spigotmc.event.player.PlayerSpawnLocationEvent; +import org.bukkit.event.player.PlayerHarvestBlockEvent; +import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.event.server.BroadcastMessageEvent; +import org.bukkit.event.server.ServerCommandEvent; +import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.event.entity.EntityDeathEvent; +import org.bukkit.event.entity.EntitySpawnEvent; +import org.bukkit.event.entity.EntityDamageEvent.DamageCause; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.patchbukkit.bridge.BridgeUtils; +import org.patchbukkit.command.PatchBukkitConsoleCommandSender; +import org.patchbukkit.world.PatchBukkitBlock; +import org.patchbukkit.world.PatchBukkitWorld; +import org.patchbukkit.entity.PatchBukkitEntity; +import org.patchbukkit.entity.PatchBukkitLivingEntity; +import org.patchbukkit.entity.PatchBukkitArmorStand; +import org.patchbukkit.entity.PatchBukkitItem; +import org.patchbukkit.entity.PatchBukkitEgg; +import org.patchbukkit.entity.PatchBukkitFishHook; +import org.patchbukkit.entity.PatchBukkitExperienceOrb; +import org.patchbukkit.entity.PatchBukkitAbstractArrow; +import org.bukkit.block.BlockFace; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.MainHand; +import org.bukkit.inventory.meta.BookMeta; +import org.bukkit.util.Vector; +import org.bukkit.block.Block; +import org.bukkit.Material; +import org.bukkit.GameMode; +import org.bukkit.NamespacedKey; +import org.bukkit.Statistic; +import org.bukkit.entity.EntityType; +import java.net.InetAddress; +import java.net.InetSocketAddress; import patchbukkit.common.UUID; import patchbukkit.events.Event; import patchbukkit.events.FireEventResponse; +import patchbukkit.events.PlayerChatEvent; +import patchbukkit.events.PlayerCommandEvent; import patchbukkit.events.PlayerJoinEvent; +import patchbukkit.events.PlayerLeaveEvent; +import java.util.HashSet; import java.util.logging.Logger; public class PatchBukkitEventFactory { @@ -42,6 +169,1814 @@ public static org.bukkit.event.Event createEvent(@NotNull Event event) { Component joinMessage = GsonComponentSerializer.gson().deserialize(joinEvent.getJoinMessage()); yield new org.bukkit.event.player.PlayerJoinEvent(player, joinMessage); } + case PLAYER_LOGIN -> { + patchbukkit.events.PlayerLoginEvent loginEvent = event.getPlayerLogin(); + Player player = getPlayer(loginEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + Component kickMessage = GsonComponentSerializer.gson().deserialize(loginEvent.getKickMessage()); + PlayerLoginEvent login = createLoginEvent(player); + if (login != null) { + String plainKickMessage = PlainTextComponentSerializer.plainText().serialize(kickMessage); + applyLoginResult(login, plainKickMessage); + yield login; + } + yield null; + } + case ASYNC_PLAYER_PRE_LOGIN -> { + patchbukkit.events.AsyncPlayerPreLoginEvent preLoginEvent = event.getAsyncPlayerPreLogin(); + String name = preLoginEvent.getName(); + String uuidStr = preLoginEvent.getPlayerUuid().getValue(); + java.util.UUID uuid; + try { + uuid = java.util.UUID.fromString(uuidStr); + } catch (IllegalArgumentException e) { + yield null; + } + InetAddress address = null; + if (!preLoginEvent.getAddress().isEmpty()) { + try { + address = InetAddress.getByName(preLoginEvent.getAddress()); + } catch (Exception ignored) { + address = null; + } + } + AsyncPlayerPreLoginEvent.Result result = AsyncPlayerPreLoginEvent.Result.ALLOWED; + if (!preLoginEvent.getResult().isEmpty()) { + try { + result = AsyncPlayerPreLoginEvent.Result.valueOf(preLoginEvent.getResult()); + } catch (IllegalArgumentException ignored) { + result = AsyncPlayerPreLoginEvent.Result.ALLOWED; + } + } + AsyncPlayerPreLoginEvent eventObj = new AsyncPlayerPreLoginEvent(name, address, uuid); + if (result != AsyncPlayerPreLoginEvent.Result.ALLOWED) { + eventObj.disallow(result, preLoginEvent.getKickMessage()); + } + yield eventObj; + } + case PLAYER_LEAVE -> { + PlayerLeaveEvent leaveEvent = event.getPlayerLeave(); + Player player = getPlayer(leaveEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + + Component leaveMessage = GsonComponentSerializer.gson().deserialize(leaveEvent.getLeaveMessage()); + yield new PlayerQuitEvent(player, leaveMessage); + } + case PLAYER_MOVE -> { + patchbukkit.events.PlayerMoveEvent moveEvent = event.getPlayerMove(); + Player player = getPlayer(moveEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + + Location from = BridgeUtils.convertLocation(moveEvent.getFrom()); + Location to = BridgeUtils.convertLocation(moveEvent.getTo()); + if (from == null || to == null) yield null; + + yield new PlayerMoveEvent(player, from, to); + } + case PLAYER_TELEPORT -> { + patchbukkit.events.PlayerTeleportEvent teleportEvent = event.getPlayerTeleport(); + Player player = getPlayer(teleportEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + Location from = BridgeUtils.convertLocation(teleportEvent.getFrom()); + Location to = BridgeUtils.convertLocation(teleportEvent.getTo()); + if (from == null || to == null) yield null; + PlayerTeleportEvent.TeleportCause cause = PlayerTeleportEvent.TeleportCause.UNKNOWN; + if (!teleportEvent.getCause().isEmpty()) { + try { + cause = PlayerTeleportEvent.TeleportCause.valueOf(teleportEvent.getCause()); + } catch (IllegalArgumentException ignored) { + cause = PlayerTeleportEvent.TeleportCause.UNKNOWN; + } + } + yield new PlayerTeleportEvent(player, from, to, cause); + } + case PLAYER_CHANGE_WORLD -> { + patchbukkit.events.PlayerChangeWorldEvent changeEvent = event.getPlayerChangeWorld(); + Player player = getPlayer(changeEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + if (changeEvent.getPreviousWorld() == null || changeEvent.getPreviousWorld().getUuid() == null) { + yield null; + } + java.util.UUID prevUuid = java.util.UUID.fromString(changeEvent.getPreviousWorld().getUuid().getValue()); + yield new PlayerChangedWorldEvent(player, PatchBukkitWorld.getOrCreate(prevUuid)); + } + case PLAYER_GAMEMODE_CHANGE -> { + patchbukkit.events.PlayerGamemodeChangeEvent gmEvent = event.getPlayerGamemodeChange(); + Player player = getPlayer(gmEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + GameMode mode = GameMode.SURVIVAL; + if (!gmEvent.getNewGamemode().isEmpty()) { + try { + mode = GameMode.valueOf(gmEvent.getNewGamemode()); + } catch (IllegalArgumentException ignored) { + mode = GameMode.SURVIVAL; + } + } + yield new PlayerGameModeChangeEvent(player, mode); + } + case PLAYER_ADVANCEMENT_DONE -> { + patchbukkit.events.PlayerAdvancementDoneEvent advancementEvent = event.getPlayerAdvancementDone(); + Player player = getPlayer(advancementEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + if (advancementEvent.getAdvancementKey().isEmpty()) yield null; + NamespacedKey key = NamespacedKey.fromString(advancementEvent.getAdvancementKey()); + if (key == null) yield null; + org.bukkit.advancement.Advancement advancement = Bukkit.getAdvancement(key); + if (advancement == null) yield null; + yield new PlayerAdvancementDoneEvent(player, advancement); + } + case PLAYER_ANIMATION -> { + patchbukkit.events.PlayerAnimationEvent animationEvent = event.getPlayerAnimation(); + Player player = getPlayer(animationEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + PlayerAnimationType type = PlayerAnimationType.ARM_SWING; + if (!animationEvent.getAnimationType().isEmpty()) { + try { + type = PlayerAnimationType.valueOf(animationEvent.getAnimationType()); + } catch (IllegalArgumentException ignored) { + type = PlayerAnimationType.ARM_SWING; + } + } + yield new PlayerAnimationEvent(player, type); + } + case PLAYER_ARMOR_STAND_MANIPULATE -> { + patchbukkit.events.PlayerArmorStandManipulateEvent armorEvent = + event.getPlayerArmorStandManipulate(); + Player player = getPlayer(armorEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + java.util.UUID standUuid; + try { + standUuid = java.util.UUID.fromString(armorEvent.getArmorStandUuid().getValue()); + } catch (IllegalArgumentException e) { + yield null; + } + PatchBukkitArmorStand armorStand = new PatchBukkitArmorStand(standUuid, "ArmorStand"); + + ItemStack playerItem = materialToItem(armorEvent.getItemKey()); + ItemStack standItem = materialToItem(armorEvent.getArmorStandItemKey()); + + EquipmentSlot slot = EquipmentSlot.HAND; + if (!armorEvent.getSlot().isEmpty()) { + try { + slot = EquipmentSlot.valueOf(armorEvent.getSlot()); + } catch (IllegalArgumentException ignored) { + slot = EquipmentSlot.HAND; + } + } + yield new PlayerArmorStandManipulateEvent(player, armorStand, playerItem, standItem, slot); + } + case PLAYER_BED_ENTER -> { + patchbukkit.events.PlayerBedEnterEvent bedEvent = event.getPlayerBedEnter(); + Player player = getPlayer(bedEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + Location bedLocation = BridgeUtils.convertLocation(bedEvent.getBedLocation()); + if (bedLocation == null) yield null; + if (bedLocation.getWorld() == null) yield null; + org.bukkit.event.Event created = createPlayerBedEnterEvent(player, bedLocation.getBlock()); + if (created instanceof PlayerBedEnterEvent) { + yield created; + } + yield null; + } + case PLAYER_BED_LEAVE -> { + patchbukkit.events.PlayerBedLeaveEvent bedEvent = event.getPlayerBedLeave(); + Player player = getPlayer(bedEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + Location bedLocation = BridgeUtils.convertLocation(bedEvent.getBedLocation()); + if (bedLocation == null) yield null; + if (bedLocation.getWorld() == null) yield null; + org.bukkit.event.Event created = createPlayerBedLeaveEvent(player, bedLocation.getBlock()); + if (created instanceof PlayerBedLeaveEvent) { + yield created; + } + yield null; + } + case PLAYER_BUCKET_EMPTY -> { + patchbukkit.events.PlayerBucketEmptyEvent bucketEvent = event.getPlayerBucketEmpty(); + Player player = getPlayer(bucketEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + Location location = BridgeUtils.convertLocation(bucketEvent.getLocation()); + if (location == null || !(location.getWorld() instanceof PatchBukkitWorld world)) { + yield null; + } + Block block = PatchBukkitBlock.create( + world, + location.getBlockX(), + location.getBlockY(), + location.getBlockZ(), + bucketEvent.getBlockKey() + ); + BlockFace face = BlockFace.SELF; + if (!bucketEvent.getBlockFace().isEmpty()) { + try { + face = BlockFace.valueOf(bucketEvent.getBlockFace()); + } catch (IllegalArgumentException ignored) { + face = BlockFace.SELF; + } + } + ItemStack item = materialToItem(bucketEvent.getBucketItemKey()); + Material bucket = item.getType(); + EquipmentSlot slot = EquipmentSlot.HAND; + if (!bucketEvent.getHand().isEmpty()) { + try { + slot = EquipmentSlot.valueOf(bucketEvent.getHand()); + } catch (IllegalArgumentException ignored) { + slot = EquipmentSlot.HAND; + } + } + yield new PlayerBucketEmptyEvent(player, block, block, face, bucket, item, slot); + } + case PLAYER_BUCKET_FILL -> { + patchbukkit.events.PlayerBucketFillEvent bucketEvent = event.getPlayerBucketFill(); + Player player = getPlayer(bucketEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + Location location = BridgeUtils.convertLocation(bucketEvent.getLocation()); + if (location == null || !(location.getWorld() instanceof PatchBukkitWorld world)) { + yield null; + } + Block block = PatchBukkitBlock.create( + world, + location.getBlockX(), + location.getBlockY(), + location.getBlockZ(), + bucketEvent.getBlockKey() + ); + BlockFace face = BlockFace.SELF; + if (!bucketEvent.getBlockFace().isEmpty()) { + try { + face = BlockFace.valueOf(bucketEvent.getBlockFace()); + } catch (IllegalArgumentException ignored) { + face = BlockFace.SELF; + } + } + ItemStack item = materialToItem(bucketEvent.getBucketItemKey()); + Material bucket = item.getType(); + EquipmentSlot slot = EquipmentSlot.HAND; + if (!bucketEvent.getHand().isEmpty()) { + try { + slot = EquipmentSlot.valueOf(bucketEvent.getHand()); + } catch (IllegalArgumentException ignored) { + slot = EquipmentSlot.HAND; + } + } + yield new PlayerBucketFillEvent(player, block, block, face, bucket, item, slot); + } + case PLAYER_BUCKET_ENTITY -> { + patchbukkit.events.PlayerBucketEntityEvent bucketEvent = event.getPlayerBucketEntity(); + Player player = getPlayer(bucketEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + java.util.UUID entityUuid; + try { + entityUuid = java.util.UUID.fromString(bucketEvent.getEntityUuid().getValue()); + } catch (IllegalArgumentException e) { + yield null; + } + PatchBukkitEntity entity = new PatchBukkitEntity(entityUuid, bucketEvent.getEntityType()); + ItemStack originalBucket = materialToItem(bucketEvent.getOriginalBucketKey()); + ItemStack entityBucket = materialToItem(bucketEvent.getEntityBucketKey()); + EquipmentSlot slot = EquipmentSlot.HAND; + if (!bucketEvent.getHand().isEmpty()) { + try { + slot = EquipmentSlot.valueOf(bucketEvent.getHand()); + } catch (IllegalArgumentException ignored) { + slot = EquipmentSlot.HAND; + } + } + yield new PlayerBucketEntityEvent(player, entity, originalBucket, entityBucket, slot); + } + case PLAYER_CHANGED_MAIN_HAND -> { + patchbukkit.events.PlayerChangedMainHandEvent mainHandEvent = event.getPlayerChangedMainHand(); + Player player = getPlayer(mainHandEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + MainHand mainHand = MainHand.RIGHT; + if (!mainHandEvent.getMainHand().isEmpty()) { + try { + mainHand = MainHand.valueOf(mainHandEvent.getMainHand()); + } catch (IllegalArgumentException ignored) { + mainHand = MainHand.RIGHT; + } + } + yield new PlayerChangedMainHandEvent(player, mainHand); + } + case PLAYER_REGISTER_CHANNEL -> { + patchbukkit.events.PlayerRegisterChannelEvent channelEvent = event.getPlayerRegisterChannel(); + Player player = getPlayer(channelEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + yield new PlayerRegisterChannelEvent(player, channelEvent.getChannel()); + } + case PLAYER_UNREGISTER_CHANNEL -> { + patchbukkit.events.PlayerUnregisterChannelEvent channelEvent = event.getPlayerUnregisterChannel(); + Player player = getPlayer(channelEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + yield new PlayerUnregisterChannelEvent(player, channelEvent.getChannel()); + } + case PLAYER_CHAT -> { + PlayerChatEvent chatEvent = event.getPlayerChat(); + Player player = getPlayer(chatEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + + var recipients = new HashSet(); + if (chatEvent.getRecipientsList().isEmpty()) { + recipients.addAll(Bukkit.getOnlinePlayers()); + } else { + for (UUID recipient : chatEvent.getRecipientsList()) { + Player recipientPlayer = getPlayer(recipient.getValue()); + if (recipientPlayer != null) { + recipients.add(recipientPlayer); + } + } + } + + yield new AsyncPlayerChatEvent(false, player, chatEvent.getMessage(), recipients); + } + case PLAYER_COMMAND -> { + PlayerCommandEvent commandEvent = event.getPlayerCommand(); + Player player = getPlayer(commandEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + + yield new PlayerCommandPreprocessEvent(player, commandEvent.getCommand()); + } + case PLAYER_COMMAND_SEND -> { + patchbukkit.events.PlayerCommandSendEvent commandEvent = event.getPlayerCommandSend(); + Player player = getPlayer(commandEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + yield new PlayerCommandSendEvent(player, new HashSet<>(commandEvent.getCommandsList())); + } + case PLAYER_DROP_ITEM -> { + patchbukkit.events.PlayerDropItemEvent dropEvent = event.getPlayerDropItem(); + Player player = getPlayer(dropEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + java.util.UUID itemUuid; + try { + itemUuid = java.util.UUID.fromString(dropEvent.getItemUuid().getValue()); + } catch (IllegalArgumentException e) { + yield null; + } + ItemStack stack = materialToItem(dropEvent.getItemKey(), dropEvent.getItemAmount()); + PatchBukkitItem item = new PatchBukkitItem(itemUuid, stack); + yield new PlayerDropItemEvent(player, item); + } + case PLAYER_EDIT_BOOK -> { + patchbukkit.events.PlayerEditBookEvent editEvent = event.getPlayerEditBook(); + Player player = getPlayer(editEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + boolean isSigning = editEvent.getIsSigning(); + ItemStack item = new ItemStack(isSigning ? Material.WRITTEN_BOOK : Material.WRITABLE_BOOK); + BookMeta previousMeta = (BookMeta) item.getItemMeta(); + BookMeta newMeta = (BookMeta) item.getItemMeta(); + if (newMeta != null) { + newMeta.setPages(editEvent.getPagesList()); + if (!editEvent.getTitle().isEmpty()) { + newMeta.setTitle(editEvent.getTitle()); + } + } + yield new PlayerEditBookEvent(player, editEvent.getSlot(), previousMeta, newMeta, isSigning); + } + case PLAYER_EGG_THROW -> { + patchbukkit.events.PlayerEggThrowEvent eggEvent = event.getPlayerEggThrow(); + Player player = getPlayer(eggEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + java.util.UUID eggUuid; + try { + eggUuid = java.util.UUID.fromString(eggEvent.getEggUuid().getValue()); + } catch (IllegalArgumentException e) { + yield null; + } + PatchBukkitEgg egg = new PatchBukkitEgg(eggUuid, new ItemStack(Material.EGG)); + org.bukkit.entity.EntityType hatchingType = org.bukkit.entity.EntityType.CHICKEN; + if (!eggEvent.getHatchingType().isEmpty()) { + try { + hatchingType = org.bukkit.entity.EntityType.valueOf(eggEvent.getHatchingType()); + } catch (IllegalArgumentException ignored) { + hatchingType = org.bukkit.entity.EntityType.CHICKEN; + } + } + yield new PlayerEggThrowEvent( + player, + egg, + eggEvent.getHatching(), + (byte) Math.max(0, Math.min(eggEvent.getNumHatches(), 127)), + hatchingType + ); + } + case PLAYER_EXP_CHANGE -> { + patchbukkit.events.PlayerExpChangeEvent expEvent = event.getPlayerExpChange(); + Player player = getPlayer(expEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + yield new PlayerExpChangeEvent(player, expEvent.getAmount()); + } + case PLAYER_FISH -> { + patchbukkit.events.PlayerFishEvent fishEvent = event.getPlayerFish(); + Player player = getPlayer(fishEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + java.util.UUID hookUuid; + try { + hookUuid = java.util.UUID.fromString(fishEvent.getHookUuid().getValue()); + } catch (IllegalArgumentException e) { + yield null; + } + PatchBukkitFishHook hook = new PatchBukkitFishHook(hookUuid); + org.bukkit.entity.Entity caught = null; + if (fishEvent.hasCaughtUuid()) { + try { + java.util.UUID caughtUuid = java.util.UUID.fromString(fishEvent.getCaughtUuid().getValue()); + caught = new PatchBukkitEntity(caughtUuid, fishEvent.getCaughtType()); + } catch (IllegalArgumentException ignored) { + caught = null; + } + } + PlayerFishEvent.State state = PlayerFishEvent.State.FISHING; + if (!fishEvent.getState().isEmpty()) { + try { + state = PlayerFishEvent.State.valueOf(fishEvent.getState()); + } catch (IllegalArgumentException ignored) { + state = PlayerFishEvent.State.FISHING; + } + } + EquipmentSlot hand = EquipmentSlot.HAND; + if (!fishEvent.getHand().isEmpty()) { + try { + hand = EquipmentSlot.valueOf(fishEvent.getHand()); + } catch (IllegalArgumentException ignored) { + hand = EquipmentSlot.HAND; + } + } + PlayerFishEvent playerFishEvent = new PlayerFishEvent(player, caught, hook, hand, state); + playerFishEvent.setExpToDrop(fishEvent.getExpToDrop()); + yield playerFishEvent; + } + case PLAYER_INTERACT_ENTITY -> { + patchbukkit.events.PlayerInteractEntityEvent interactEvent = event.getPlayerInteractEntity(); + Player player = getPlayer(interactEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + java.util.UUID entityUuid; + try { + entityUuid = java.util.UUID.fromString(interactEvent.getEntityUuid().getValue()); + } catch (IllegalArgumentException e) { + yield null; + } + PatchBukkitEntity entity = new PatchBukkitEntity(entityUuid, interactEvent.getEntityType()); + EquipmentSlot slot = EquipmentSlot.HAND; + if (!interactEvent.getHand().isEmpty()) { + try { + slot = EquipmentSlot.valueOf(interactEvent.getHand()); + } catch (IllegalArgumentException ignored) { + slot = EquipmentSlot.HAND; + } + } + yield new PlayerInteractEntityEvent(player, entity, slot); + } + case PLAYER_INTERACT_AT_ENTITY -> { + patchbukkit.events.PlayerInteractAtEntityEvent interactEvent = event.getPlayerInteractAtEntity(); + Player player = getPlayer(interactEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + java.util.UUID entityUuid; + try { + entityUuid = java.util.UUID.fromString(interactEvent.getEntityUuid().getValue()); + } catch (IllegalArgumentException e) { + yield null; + } + PatchBukkitEntity entity = new PatchBukkitEntity(entityUuid, interactEvent.getEntityType()); + EquipmentSlot slot = EquipmentSlot.HAND; + if (!interactEvent.getHand().isEmpty()) { + try { + slot = EquipmentSlot.valueOf(interactEvent.getHand()); + } catch (IllegalArgumentException ignored) { + slot = EquipmentSlot.HAND; + } + } + var pos = interactEvent.getClickedPosition(); + Vector clickedPos = pos != null + ? new Vector(pos.getX(), pos.getY(), pos.getZ()) + : new Vector(); + yield new PlayerInteractAtEntityEvent(player, entity, clickedPos, slot); + } + case PLAYER_ITEM_HELD -> { + patchbukkit.events.PlayerItemHeldEvent heldEvent = event.getPlayerItemHeld(); + Player player = getPlayer(heldEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + yield new PlayerItemHeldEvent(player, heldEvent.getPreviousSlot(), heldEvent.getNewSlot()); + } + case PLAYER_ITEM_DAMAGE -> { + patchbukkit.events.PlayerItemDamageEvent damageEvent = event.getPlayerItemDamage(); + Player player = getPlayer(damageEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + ItemStack item = materialToItem(damageEvent.getItemKey(), damageEvent.getItemAmount()); + yield new PlayerItemDamageEvent(player, item, damageEvent.getDamage()); + } + case PLAYER_ITEM_BREAK -> { + patchbukkit.events.PlayerItemBreakEvent breakEvent = event.getPlayerItemBreak(); + Player player = getPlayer(breakEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + ItemStack item = materialToItem(breakEvent.getItemKey(), breakEvent.getItemAmount()); + yield new PlayerItemBreakEvent(player, item); + } + case PLAYER_ITEM_CONSUME -> { + patchbukkit.events.PlayerItemConsumeEvent consumeEvent = event.getPlayerItemConsume(); + Player player = getPlayer(consumeEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + ItemStack item = materialToItem(consumeEvent.getItemKey(), consumeEvent.getItemAmount()); + EquipmentSlot hand = EquipmentSlot.HAND; + if (!consumeEvent.getHand().isEmpty()) { + try { + hand = EquipmentSlot.valueOf(consumeEvent.getHand()); + } catch (IllegalArgumentException ignored) { + hand = EquipmentSlot.HAND; + } + } + yield new PlayerItemConsumeEvent(player, item, hand); + } + case PLAYER_ITEM_MEND -> { + patchbukkit.events.PlayerItemMendEvent mendEvent = event.getPlayerItemMend(); + Player player = getPlayer(mendEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + ItemStack item = materialToItem(mendEvent.getItemKey(), mendEvent.getItemAmount()); + EquipmentSlot slot = EquipmentSlot.HAND; + if (!mendEvent.getSlot().isEmpty()) { + try { + slot = EquipmentSlot.valueOf(mendEvent.getSlot()); + } catch (IllegalArgumentException ignored) { + slot = EquipmentSlot.HAND; + } + } + java.util.UUID orbUuid = java.util.UUID.randomUUID(); + if (mendEvent.hasOrbUuid()) { + try { + orbUuid = java.util.UUID.fromString(mendEvent.getOrbUuid().getValue()); + } catch (IllegalArgumentException ignored) { + orbUuid = java.util.UUID.randomUUID(); + } + } + PatchBukkitExperienceOrb orb = new PatchBukkitExperienceOrb(orbUuid); + yield new PlayerItemMendEvent(player, item, slot, orb, mendEvent.getRepairAmount()); + } + case PLAYER_LEVEL_CHANGE -> { + patchbukkit.events.PlayerLevelChangeEvent levelEvent = event.getPlayerLevelChange(); + Player player = getPlayer(levelEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + yield new PlayerLevelChangeEvent(player, levelEvent.getOldLevel(), levelEvent.getNewLevel()); + } + case PLAYER_KICK -> { + patchbukkit.events.PlayerKickEvent kickEvent = event.getPlayerKick(); + Player player = getPlayer(kickEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + Component reason = kickEvent.getReason().isEmpty() + ? Component.text("") + : GsonComponentSerializer.gson().deserialize(kickEvent.getReason()); + Component leaveMessage = kickEvent.getLeaveMessage().isEmpty() + ? Component.text("") + : GsonComponentSerializer.gson().deserialize(kickEvent.getLeaveMessage()); + PlayerKickEvent.Cause cause = PlayerKickEvent.Cause.UNKNOWN; + if (!kickEvent.getCause().isEmpty()) { + try { + cause = PlayerKickEvent.Cause.valueOf(kickEvent.getCause()); + } catch (IllegalArgumentException ignored) { + cause = PlayerKickEvent.Cause.UNKNOWN; + } + } + yield new PlayerKickEvent(player, reason, leaveMessage, cause); + } + case PLAYER_TOGGLE_SNEAK -> { + patchbukkit.events.PlayerToggleSneakEvent toggleEvent = event.getPlayerToggleSneak(); + Player player = getPlayer(toggleEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + yield new PlayerToggleSneakEvent(player, toggleEvent.getIsSneaking()); + } + case PLAYER_TOGGLE_SPRINT -> { + patchbukkit.events.PlayerToggleSprintEvent toggleEvent = event.getPlayerToggleSprint(); + Player player = getPlayer(toggleEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + yield new PlayerToggleSprintEvent(player, toggleEvent.getIsSprinting()); + } + case PLAYER_TOGGLE_FLIGHT -> { + patchbukkit.events.PlayerToggleFlightEvent toggleEvent = event.getPlayerToggleFlight(); + Player player = getPlayer(toggleEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + yield new PlayerToggleFlightEvent(player, toggleEvent.getIsFlying()); + } + case PLAYER_SWAP_HAND_ITEMS -> { + patchbukkit.events.PlayerSwapHandItemsEvent swapEvent = event.getPlayerSwapHandItems(); + Player player = getPlayer(swapEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + ItemStack mainHand = materialToItem(swapEvent.getMainHandItemKey(), swapEvent.getMainHandItemAmount()); + ItemStack offHand = materialToItem(swapEvent.getOffHandItemKey(), swapEvent.getOffHandItemAmount()); + yield new PlayerSwapHandItemsEvent(player, mainHand, offHand); + } + case PLAYER_RESOURCE_PACK_STATUS -> { + patchbukkit.events.PlayerResourcePackStatusEvent packEvent = event.getPlayerResourcePackStatus(); + Player player = getPlayer(packEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + java.util.UUID packUuid; + try { + packUuid = java.util.UUID.fromString(packEvent.getPackUuid().getValue()); + } catch (IllegalArgumentException e) { + packUuid = java.util.UUID.randomUUID(); + } + PlayerResourcePackStatusEvent.Status status = PlayerResourcePackStatusEvent.Status.FAILED_DOWNLOAD; + if (!packEvent.getStatus().isEmpty()) { + try { + status = PlayerResourcePackStatusEvent.Status.valueOf(packEvent.getStatus()); + } catch (IllegalArgumentException ignored) { + status = PlayerResourcePackStatusEvent.Status.FAILED_DOWNLOAD; + } + } + PlayerResourcePackStatusEvent statusEvent = + new PlayerResourcePackStatusEvent(player, packUuid, status); + if (!packEvent.getHash().isEmpty()) { + try { + java.lang.reflect.Method method = statusEvent.getClass().getMethod("setHash", String.class); + method.invoke(statusEvent, packEvent.getHash()); + } catch (ReflectiveOperationException ignored) { + // ignore + } + } + yield statusEvent; + } + case PLAYER_RESPAWN -> { + patchbukkit.events.PlayerRespawnEvent respawnEvent = event.getPlayerRespawn(); + Player player = getPlayer(respawnEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + Location location = BridgeUtils.convertLocation(respawnEvent.getRespawnLocation()); + if (location == null) yield null; + PlayerRespawnEvent.RespawnReason reason = PlayerRespawnEvent.RespawnReason.DEATH; + if (!respawnEvent.getRespawnReason().isEmpty()) { + try { + reason = PlayerRespawnEvent.RespawnReason.valueOf(respawnEvent.getRespawnReason()); + } catch (IllegalArgumentException ignored) { + reason = PlayerRespawnEvent.RespawnReason.DEATH; + } + } + yield new PlayerRespawnEvent( + player, + location, + respawnEvent.getIsBedSpawn(), + respawnEvent.getIsAnchorSpawn(), + respawnEvent.getIsMissingRespawnBlock(), + reason + ); + } + case PLAYER_PICKUP_ARROW -> { + patchbukkit.events.PlayerPickupArrowEvent pickupEvent = event.getPlayerPickupArrow(); + Player player = getPlayer(pickupEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + java.util.UUID arrowUuid; + try { + arrowUuid = java.util.UUID.fromString(pickupEvent.getArrowUuid().getValue()); + } catch (IllegalArgumentException e) { + arrowUuid = java.util.UUID.randomUUID(); + } + java.util.UUID itemUuid; + try { + itemUuid = java.util.UUID.fromString(pickupEvent.getItemUuid().getValue()); + } catch (IllegalArgumentException e) { + itemUuid = java.util.UUID.randomUUID(); + } + ItemStack stack = materialToItem(pickupEvent.getItemKey(), pickupEvent.getItemAmount()); + PatchBukkitItem item = new PatchBukkitItem(itemUuid, stack); + PatchBukkitAbstractArrow arrow = new PatchBukkitAbstractArrow(arrowUuid, "Arrow"); + yield new PlayerPickupArrowEvent(player, item, arrow); + } + case PLAYER_PORTAL -> { + patchbukkit.events.PlayerPortalEvent portalEvent = event.getPlayerPortal(); + Player player = getPlayer(portalEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + Location from = BridgeUtils.convertLocation(portalEvent.getFrom()); + Location to = BridgeUtils.convertLocation(portalEvent.getTo()); + if (from == null || to == null) yield null; + PlayerTeleportEvent.TeleportCause cause = PlayerTeleportEvent.TeleportCause.UNKNOWN; + if (!portalEvent.getCause().isEmpty()) { + try { + cause = PlayerTeleportEvent.TeleportCause.valueOf(portalEvent.getCause()); + } catch (IllegalArgumentException ignored) { + cause = PlayerTeleportEvent.TeleportCause.UNKNOWN; + } + } + PlayerPortalEvent eventObj = new PlayerPortalEvent(player, from, to, cause); + try { + var method = PlayerPortalEvent.class.getMethod("setSearchRadius", int.class); + method.invoke(eventObj, portalEvent.getSearchRadius()); + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + var method = PlayerPortalEvent.class.getMethod("setCanCreatePortal", boolean.class); + method.invoke(eventObj, portalEvent.getCanCreatePortal()); + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + var method = PlayerPortalEvent.class.getMethod("setCreationRadius", int.class); + method.invoke(eventObj, portalEvent.getCreationRadius()); + } catch (ReflectiveOperationException ignored) { + // ignore + } + yield eventObj; + } + case PLAYER_RECIPE_DISCOVER -> { + patchbukkit.events.PlayerRecipeDiscoverEvent recipeEvent = event.getPlayerRecipeDiscover(); + Player player = getPlayer(recipeEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + if (recipeEvent.getRecipeKey().isEmpty()) yield null; + NamespacedKey key = NamespacedKey.fromString(recipeEvent.getRecipeKey()); + if (key == null) yield null; + org.bukkit.event.Event created = createPlayerRecipeDiscoverEvent(player, key); + if (created instanceof PlayerRecipeDiscoverEvent) { + yield created; + } + yield null; + } + case PLAYER_RIPTIDE -> { + patchbukkit.events.PlayerRiptideEvent riptideEvent = event.getPlayerRiptide(); + Player player = getPlayer(riptideEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + ItemStack item = materialToItem(riptideEvent.getItemKey(), riptideEvent.getItemAmount()); + Vector velocity = new Vector(); + if (riptideEvent.getVelocity() != null) { + velocity = new Vector( + riptideEvent.getVelocity().getX(), + riptideEvent.getVelocity().getY(), + riptideEvent.getVelocity().getZ() + ); + } + yield new PlayerRiptideEvent(player, item, velocity); + } + case PLAYER_SHEAR_ENTITY -> { + patchbukkit.events.PlayerShearEntityEvent shearEvent = event.getPlayerShearEntity(); + Player player = getPlayer(shearEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + java.util.UUID entityUuid; + try { + entityUuid = java.util.UUID.fromString(shearEvent.getEntityUuid().getValue()); + } catch (IllegalArgumentException e) { + entityUuid = java.util.UUID.randomUUID(); + } + PatchBukkitEntity entity = new PatchBukkitEntity(entityUuid, shearEvent.getEntityType()); + ItemStack item = materialToItem(shearEvent.getItemKey(), shearEvent.getItemAmount()); + EquipmentSlot slot = EquipmentSlot.HAND; + if (!shearEvent.getHand().isEmpty()) { + try { + slot = EquipmentSlot.valueOf(shearEvent.getHand()); + } catch (IllegalArgumentException ignored) { + slot = EquipmentSlot.HAND; + } + } + org.bukkit.event.Event created = createPlayerShearEntityEvent(player, entity, item, slot); + if (created instanceof PlayerShearEntityEvent) { + yield created; + } + yield null; + } + case PLAYER_SPAWN_LOCATION -> { + patchbukkit.events.PlayerSpawnLocationEvent spawnEvent = event.getPlayerSpawnLocation(); + Player player = getPlayer(spawnEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + Location location = BridgeUtils.convertLocation(spawnEvent.getSpawnLocation()); + if (location == null) yield null; + yield new PlayerSpawnLocationEvent(player, location); + } + case PLAYER_STATISTIC_INCREMENT -> { + patchbukkit.events.PlayerStatisticIncrementEvent statEvent = event.getPlayerStatisticIncrement(); + Player player = getPlayer(statEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + Statistic statistic; + try { + statistic = Statistic.valueOf(statEvent.getStatistic()); + } catch (IllegalArgumentException e) { + yield null; + } + if (!statEvent.getEntityType().isEmpty()) { + try { + EntityType entityType = EntityType.valueOf(statEvent.getEntityType()); + yield new PlayerStatisticIncrementEvent( + player, + statistic, + statEvent.getInitialValue(), + statEvent.getNewValue(), + entityType + ); + } catch (IllegalArgumentException ignored) { + yield null; + } + } + if (!statEvent.getMaterialKey().isEmpty()) { + Material material = Material.matchMaterial(statEvent.getMaterialKey()); + if (material == null) { + material = Material.matchMaterial("minecraft:" + statEvent.getMaterialKey()); + } + if (material == null) yield null; + yield new PlayerStatisticIncrementEvent( + player, + statistic, + statEvent.getInitialValue(), + statEvent.getNewValue(), + material + ); + } + yield new PlayerStatisticIncrementEvent( + player, + statistic, + statEvent.getInitialValue(), + statEvent.getNewValue() + ); + } + case PLAYER_VELOCITY -> { + patchbukkit.events.PlayerVelocityEvent velocityEvent = event.getPlayerVelocity(); + Player player = getPlayer(velocityEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + Vector velocity = new Vector(); + if (velocityEvent.getVelocity() != null) { + velocity = new Vector( + velocityEvent.getVelocity().getX(), + velocityEvent.getVelocity().getY(), + velocityEvent.getVelocity().getZ() + ); + } + yield new PlayerVelocityEvent(player, velocity); + } + case PLAYER_HARVEST_BLOCK -> { + patchbukkit.events.PlayerHarvestBlockEvent harvestEvent = event.getPlayerHarvestBlock(); + Player player = getPlayer(harvestEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + Location location = BridgeUtils.convertLocation(harvestEvent.getBlockLocation()); + if (location == null || !(location.getWorld() instanceof PatchBukkitWorld world)) { + yield null; + } + Block block = PatchBukkitBlock.create( + world, + location.getBlockX(), + location.getBlockY(), + location.getBlockZ(), + harvestEvent.getBlockKey() + ); + ItemStack tool = materialToItem(harvestEvent.getItemKey(), harvestEvent.getItemAmount()); + org.bukkit.event.Event constructed = createHarvestBlockEvent(player, block, tool); + if (constructed instanceof PlayerHarvestBlockEvent harvest) { + yield harvest; + } + yield null; + } + case PLAYER_INTERACT -> { + patchbukkit.events.PlayerInteractEvent interactEvent = event.getPlayerInteract(); + Player player = getPlayer(interactEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + + Location clicked = BridgeUtils.convertLocation(interactEvent.getClicked()); + org.bukkit.block.Block clickedBlock = null; + if (clicked != null && clicked.getWorld() instanceof PatchBukkitWorld world) { + clickedBlock = PatchBukkitBlock.create( + world, + clicked.getBlockX(), + clicked.getBlockY(), + clicked.getBlockZ(), + interactEvent.getBlockKey() + ); + } + + org.bukkit.event.block.Action action = + org.bukkit.event.block.Action.valueOf(interactEvent.getAction()); + ItemStack item = null; + if (!interactEvent.getItemKey().isEmpty()) { + Material material = Material.matchMaterial(interactEvent.getItemKey()); + if (material == null) { + material = Material.matchMaterial("minecraft:" + interactEvent.getItemKey()); + } + if (material != null) { + item = new ItemStack(material); + } + } + BlockFace face = null; + if (!interactEvent.getBlockFace().isEmpty()) { + try { + face = BlockFace.valueOf(interactEvent.getBlockFace()); + } catch (IllegalArgumentException ignored) { + face = null; + } + } + yield new PlayerInteractEvent(player, action, item, clickedBlock, face); + } + case BLOCK_BREAK -> { + patchbukkit.events.BlockBreakEvent breakEvent = event.getBlockBreak(); + Player player = getPlayer(breakEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + + Location location = BridgeUtils.convertLocation(breakEvent.getLocation()); + if (location == null || !(location.getWorld() instanceof PatchBukkitWorld world)) { + yield null; + } + + org.bukkit.block.Block block = PatchBukkitBlock.create( + world, + location.getBlockX(), + location.getBlockY(), + location.getBlockZ(), + breakEvent.getBlockKey() + ); + + BlockBreakEvent eventObj = new BlockBreakEvent(block, player); + eventObj.setExpToDrop(breakEvent.getExp()); + eventObj.setDropItems(breakEvent.getDrop()); + yield eventObj; + } + case BLOCK_DAMAGE -> { + patchbukkit.events.BlockDamageEvent damageEvent = event.getBlockDamage(); + Player player = getPlayer(damageEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + + Location location = BridgeUtils.convertLocation(damageEvent.getLocation()); + if (location == null || !(location.getWorld() instanceof PatchBukkitWorld world)) { + yield null; + } + + org.bukkit.block.Block block = PatchBukkitBlock.create( + world, + location.getBlockX(), + location.getBlockY(), + location.getBlockZ(), + damageEvent.getBlockKey() + ); + + ItemStack item = null; + if (!damageEvent.getItemKey().isEmpty()) { + Material material = Material.matchMaterial(damageEvent.getItemKey()); + if (material == null) { + material = Material.matchMaterial("minecraft:" + damageEvent.getItemKey()); + } + if (material != null) { + item = new ItemStack(material); + } + } + + org.bukkit.event.Event created = createBlockDamageEvent(block, player, item, damageEvent.getInstaBreak()); + if (created instanceof BlockDamageEvent) { + yield created; + } + yield null; + } + case BLOCK_DAMAGE_ABORT -> { + patchbukkit.events.BlockDamageAbortEvent damageAbortEvent = event.getBlockDamageAbort(); + Player player = getPlayer(damageAbortEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + + Location location = BridgeUtils.convertLocation(damageAbortEvent.getLocation()); + if (location == null || !(location.getWorld() instanceof PatchBukkitWorld world)) { + yield null; + } + + org.bukkit.block.Block block = PatchBukkitBlock.create( + world, + location.getBlockX(), + location.getBlockY(), + location.getBlockZ(), + damageAbortEvent.getBlockKey() + ); + + ItemStack item = null; + if (!damageAbortEvent.getItemKey().isEmpty()) { + Material material = Material.matchMaterial(damageAbortEvent.getItemKey()); + if (material == null) { + material = Material.matchMaterial("minecraft:" + damageAbortEvent.getItemKey()); + } + if (material != null) { + item = new ItemStack(material); + } + } + + org.bukkit.event.Event created = createBlockDamageAbortEvent(block, player, item); + if (created instanceof BlockDamageAbortEvent) { + yield created; + } + yield null; + } + case BLOCK_DISPENSE -> { + patchbukkit.events.BlockDispenseEvent dispenseEvent = event.getBlockDispense(); + Location location = BridgeUtils.convertLocation(dispenseEvent.getLocation()); + if (location == null || !(location.getWorld() instanceof PatchBukkitWorld world)) { + yield null; + } + + org.bukkit.block.Block block = PatchBukkitBlock.create( + world, + location.getBlockX(), + location.getBlockY(), + location.getBlockZ(), + dispenseEvent.getBlockKey() + ); + + ItemStack item = null; + if (!dispenseEvent.getItemKey().isEmpty()) { + Material material = Material.matchMaterial(dispenseEvent.getItemKey()); + if (material == null) { + material = Material.matchMaterial("minecraft:" + dispenseEvent.getItemKey()); + } + if (material != null) { + int amount = Math.max(1, dispenseEvent.getItemAmount()); + item = new ItemStack(material, amount); + } + } + + Vector velocity = new Vector(); + if (dispenseEvent.hasVelocity()) { + velocity.setX(dispenseEvent.getVelocity().getX()); + velocity.setY(dispenseEvent.getVelocity().getY()); + velocity.setZ(dispenseEvent.getVelocity().getZ()); + } + + yield new BlockDispenseEvent(block, item, velocity); + } + case BLOCK_DROP_ITEM -> { + patchbukkit.events.BlockDropItemEvent dropItemEvent = event.getBlockDropItem(); + Player player = getPlayer(dropItemEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + + Location location = BridgeUtils.convertLocation(dropItemEvent.getLocation()); + if (location == null || !(location.getWorld() instanceof PatchBukkitWorld world)) { + yield null; + } + + org.bukkit.block.Block block = PatchBukkitBlock.create( + world, + location.getBlockX(), + location.getBlockY(), + location.getBlockZ(), + dropItemEvent.getBlockKey() + ); + + java.util.List items = new java.util.ArrayList<>(); + for (patchbukkit.events.BlockDropItemEntry entry : dropItemEvent.getItemsList()) { + if (entry.getItemKey().isEmpty() || entry.getItemAmount() <= 0) continue; + Material material = Material.matchMaterial(entry.getItemKey()); + if (material == null) { + material = Material.matchMaterial("minecraft:" + entry.getItemKey()); + } + if (material == null) continue; + ItemStack stack = new ItemStack(material, entry.getItemAmount()); + org.patchbukkit.entity.PatchBukkitItem itemEntity = + new org.patchbukkit.entity.PatchBukkitItem(java.util.UUID.randomUUID(), stack); + items.add(itemEntity); + } + + org.bukkit.event.Event created = createBlockDropItemEvent(block, player, items); + if (created instanceof BlockDropItemEvent) { + yield created; + } + yield null; + } + case BLOCK_EXPLODE -> { + patchbukkit.events.BlockExplodeEvent explodeEvent = event.getBlockExplode(); + Location location = BridgeUtils.convertLocation(explodeEvent.getLocation()); + if (location == null || !(location.getWorld() instanceof PatchBukkitWorld world)) { + yield null; + } + + org.bukkit.block.Block block = PatchBukkitBlock.create( + world, + location.getBlockX(), + location.getBlockY(), + location.getBlockZ(), + explodeEvent.getBlockKey() + ); + + java.util.List blockList = new java.util.ArrayList<>(); + for (patchbukkit.events.BlockExplodeBlockEntry entry : explodeEvent.getBlocksList()) { + Location entryLoc = BridgeUtils.convertLocation(entry.getLocation()); + if (entryLoc == null || !(entryLoc.getWorld() instanceof PatchBukkitWorld entryWorld)) { + continue; + } + String key = entry.getBlockKey().isEmpty() + ? "minecraft:air" + : entry.getBlockKey(); + org.bukkit.block.Block entryBlock = PatchBukkitBlock.create( + entryWorld, + entryLoc.getBlockX(), + entryLoc.getBlockY(), + entryLoc.getBlockZ(), + key + ); + blockList.add(entryBlock); + } + + org.bukkit.event.Event created = createBlockExplodeEvent(block, blockList, explodeEvent.getYield()); + if (created instanceof BlockExplodeEvent) { + yield created; + } + yield null; + } + case BLOCK_FADE -> { + patchbukkit.events.BlockFadeEvent fadeEvent = event.getBlockFade(); + Location location = BridgeUtils.convertLocation(fadeEvent.getLocation()); + if (location == null || !(location.getWorld() instanceof PatchBukkitWorld world)) { + yield null; + } + + org.bukkit.block.Block block = PatchBukkitBlock.create( + world, + location.getBlockX(), + location.getBlockY(), + location.getBlockZ(), + fadeEvent.getBlockKey() + ); + String newKey = fadeEvent.getNewBlockKey().isEmpty() + ? "minecraft:air" + : fadeEvent.getNewBlockKey(); + org.bukkit.block.Block newBlock = PatchBukkitBlock.create( + world, + location.getBlockX(), + location.getBlockY(), + location.getBlockZ(), + newKey + ); + org.bukkit.block.BlockState newState = newBlock.getState(); + yield new BlockFadeEvent(block, newState); + } + case BLOCK_FERTILIZE -> { + patchbukkit.events.BlockFertilizeEvent fertilizeEvent = event.getBlockFertilize(); + Player player = getPlayer(fertilizeEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + + Location location = BridgeUtils.convertLocation(fertilizeEvent.getLocation()); + if (location == null || !(location.getWorld() instanceof PatchBukkitWorld world)) { + yield null; + } + + org.bukkit.block.Block block = PatchBukkitBlock.create( + world, + location.getBlockX(), + location.getBlockY(), + location.getBlockZ(), + fertilizeEvent.getBlockKey() + ); + + java.util.List states = new java.util.ArrayList<>(); + for (patchbukkit.events.BlockFertilizeBlockEntry entry : fertilizeEvent.getBlocksList()) { + Location entryLoc = BridgeUtils.convertLocation(entry.getLocation()); + if (entryLoc == null || !(entryLoc.getWorld() instanceof PatchBukkitWorld entryWorld)) { + continue; + } + String key = entry.getBlockKey().isEmpty() + ? "minecraft:air" + : entry.getBlockKey(); + org.bukkit.block.Block entryBlock = PatchBukkitBlock.create( + entryWorld, + entryLoc.getBlockX(), + entryLoc.getBlockY(), + entryLoc.getBlockZ(), + key + ); + states.add(entryBlock.getState()); + } + + yield new BlockFertilizeEvent(block, player, states); + } + case BLOCK_FORM -> { + patchbukkit.events.BlockFormEvent formEvent = event.getBlockForm(); + Location location = BridgeUtils.convertLocation(formEvent.getLocation()); + if (location == null || !(location.getWorld() instanceof PatchBukkitWorld world)) { + yield null; + } + + org.bukkit.block.Block block = PatchBukkitBlock.create( + world, + location.getBlockX(), + location.getBlockY(), + location.getBlockZ(), + formEvent.getBlockKey() + ); + String newKey = formEvent.getNewBlockKey().isEmpty() + ? "minecraft:air" + : formEvent.getNewBlockKey(); + org.bukkit.block.Block newBlock = PatchBukkitBlock.create( + world, + location.getBlockX(), + location.getBlockY(), + location.getBlockZ(), + newKey + ); + org.bukkit.block.BlockState newState = newBlock.getState(); + yield new BlockFormEvent(block, newState); + } + case BLOCK_FROM_TO -> { + patchbukkit.events.BlockFromToEvent fromToEvent = event.getBlockFromTo(); + Location location = BridgeUtils.convertLocation(fromToEvent.getLocation()); + Location toLocation = BridgeUtils.convertLocation(fromToEvent.getToLocation()); + if (location == null || toLocation == null) yield null; + if (!(location.getWorld() instanceof PatchBukkitWorld world)) yield null; + if (!(toLocation.getWorld() instanceof PatchBukkitWorld toWorld)) yield null; + + org.bukkit.block.Block block = PatchBukkitBlock.create( + world, + location.getBlockX(), + location.getBlockY(), + location.getBlockZ(), + fromToEvent.getBlockKey() + ); + String toKey = fromToEvent.getToBlockKey().isEmpty() + ? "minecraft:air" + : fromToEvent.getToBlockKey(); + org.bukkit.block.Block toBlock = PatchBukkitBlock.create( + toWorld, + toLocation.getBlockX(), + toLocation.getBlockY(), + toLocation.getBlockZ(), + toKey + ); + yield new BlockFromToEvent(block, toBlock); + } + case BLOCK_GROW -> { + patchbukkit.events.BlockGrowEvent growEvent = event.getBlockGrow(); + Location location = BridgeUtils.convertLocation(growEvent.getLocation()); + if (location == null || !(location.getWorld() instanceof PatchBukkitWorld world)) { + yield null; + } + + org.bukkit.block.Block block = PatchBukkitBlock.create( + world, + location.getBlockX(), + location.getBlockY(), + location.getBlockZ(), + growEvent.getBlockKey() + ); + String newKey = growEvent.getNewBlockKey().isEmpty() + ? "minecraft:air" + : growEvent.getNewBlockKey(); + org.bukkit.block.Block newBlock = PatchBukkitBlock.create( + world, + location.getBlockX(), + location.getBlockY(), + location.getBlockZ(), + newKey + ); + org.bukkit.block.BlockState newState = newBlock.getState(); + yield new BlockGrowEvent(block, newState); + } + case BLOCK_PISTON_EXTEND -> { + patchbukkit.events.BlockPistonExtendEvent extendEvent = event.getBlockPistonExtend(); + Location location = BridgeUtils.convertLocation(extendEvent.getLocation()); + if (location == null || !(location.getWorld() instanceof PatchBukkitWorld world)) { + yield null; + } + org.bukkit.block.Block block = PatchBukkitBlock.create( + world, + location.getBlockX(), + location.getBlockY(), + location.getBlockZ(), + extendEvent.getBlockKey() + ); + java.util.List blocks = new java.util.ArrayList<>(); + for (patchbukkit.events.BlockPistonBlockEntry entry : extendEvent.getBlocksList()) { + Location entryLoc = BridgeUtils.convertLocation(entry.getLocation()); + if (entryLoc == null || !(entryLoc.getWorld() instanceof PatchBukkitWorld entryWorld)) { + continue; + } + String key = entry.getBlockKey().isEmpty() + ? "minecraft:air" + : entry.getBlockKey(); + org.bukkit.block.Block entryBlock = PatchBukkitBlock.create( + entryWorld, + entryLoc.getBlockX(), + entryLoc.getBlockY(), + entryLoc.getBlockZ(), + key + ); + blocks.add(entryBlock); + } + BlockFace face = BlockFace.SELF; + if (!extendEvent.getDirection().isEmpty()) { + try { + face = BlockFace.valueOf(extendEvent.getDirection()); + } catch (IllegalArgumentException ignored) { + face = BlockFace.SELF; + } + } + org.bukkit.event.Event created = createBlockPistonExtendEvent( + block, + blocks, + extendEvent.getLength(), + face + ); + if (created instanceof BlockPistonExtendEvent) { + yield created; + } + yield null; + } + case BLOCK_PISTON_RETRACT -> { + patchbukkit.events.BlockPistonRetractEvent retractEvent = event.getBlockPistonRetract(); + Location location = BridgeUtils.convertLocation(retractEvent.getLocation()); + if (location == null || !(location.getWorld() instanceof PatchBukkitWorld world)) { + yield null; + } + org.bukkit.block.Block block = PatchBukkitBlock.create( + world, + location.getBlockX(), + location.getBlockY(), + location.getBlockZ(), + retractEvent.getBlockKey() + ); + java.util.List blocks = new java.util.ArrayList<>(); + for (patchbukkit.events.BlockPistonBlockEntry entry : retractEvent.getBlocksList()) { + Location entryLoc = BridgeUtils.convertLocation(entry.getLocation()); + if (entryLoc == null || !(entryLoc.getWorld() instanceof PatchBukkitWorld entryWorld)) { + continue; + } + String key = entry.getBlockKey().isEmpty() + ? "minecraft:air" + : entry.getBlockKey(); + org.bukkit.block.Block entryBlock = PatchBukkitBlock.create( + entryWorld, + entryLoc.getBlockX(), + entryLoc.getBlockY(), + entryLoc.getBlockZ(), + key + ); + blocks.add(entryBlock); + } + BlockFace face = BlockFace.SELF; + if (!retractEvent.getDirection().isEmpty()) { + try { + face = BlockFace.valueOf(retractEvent.getDirection()); + } catch (IllegalArgumentException ignored) { + face = BlockFace.SELF; + } + } + org.bukkit.event.Event created = createBlockPistonRetractEvent( + block, + blocks, + retractEvent.getLength(), + face + ); + if (created instanceof BlockPistonRetractEvent) { + yield created; + } + yield null; + } + case BLOCK_REDSTONE -> { + patchbukkit.events.BlockRedstoneEvent redstoneEvent = event.getBlockRedstone(); + Location location = BridgeUtils.convertLocation(redstoneEvent.getLocation()); + if (location == null || !(location.getWorld() instanceof PatchBukkitWorld world)) { + yield null; + } + org.bukkit.block.Block block = PatchBukkitBlock.create( + world, + location.getBlockX(), + location.getBlockY(), + location.getBlockZ(), + redstoneEvent.getBlockKey() + ); + yield new BlockRedstoneEvent(block, redstoneEvent.getOldCurrent(), redstoneEvent.getNewCurrent()); + } + case BLOCK_MULTI_PLACE -> { + patchbukkit.events.BlockMultiPlaceEvent multiEvent = event.getBlockMultiPlace(); + Player player = getPlayer(multiEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + Location location = BridgeUtils.convertLocation(multiEvent.getLocation()); + if (location == null || !(location.getWorld() instanceof PatchBukkitWorld world)) { + yield null; + } + org.bukkit.block.Block block = PatchBukkitBlock.create( + world, + location.getBlockX(), + location.getBlockY(), + location.getBlockZ(), + multiEvent.getBlockKey() + ); + java.util.List states = new java.util.ArrayList<>(); + for (patchbukkit.events.BlockMultiPlaceBlockEntry entry : multiEvent.getBlocksList()) { + Location entryLoc = BridgeUtils.convertLocation(entry.getLocation()); + if (entryLoc == null || !(entryLoc.getWorld() instanceof PatchBukkitWorld entryWorld)) { + continue; + } + String key = entry.getBlockKey().isEmpty() + ? "minecraft:air" + : entry.getBlockKey(); + org.bukkit.block.Block entryBlock = PatchBukkitBlock.create( + entryWorld, + entryLoc.getBlockX(), + entryLoc.getBlockY(), + entryLoc.getBlockZ(), + key + ); + states.add(entryBlock.getState()); + } + yield new BlockMultiPlaceEvent(states, block, null, player, true); + } + case BLOCK_PHYSICS -> { + patchbukkit.events.BlockPhysicsEvent physicsEvent = event.getBlockPhysics(); + Location location = BridgeUtils.convertLocation(physicsEvent.getLocation()); + if (location == null || !(location.getWorld() instanceof PatchBukkitWorld world)) { + yield null; + } + org.bukkit.block.Block block = PatchBukkitBlock.create( + world, + location.getBlockX(), + location.getBlockY(), + location.getBlockZ(), + physicsEvent.getBlockKey() + ); + Material material = Material.matchMaterial(physicsEvent.getSourceBlockKey()); + if (material == null) { + material = Material.matchMaterial("minecraft:" + physicsEvent.getSourceBlockKey()); + } + org.bukkit.block.data.BlockData data = material != null + ? material.createBlockData() + : block.getBlockData(); + yield new BlockPhysicsEvent(block, data); + } + case NOTE_PLAY -> { + patchbukkit.events.NotePlayEvent noteEvent = event.getNotePlay(); + Location location = BridgeUtils.convertLocation(noteEvent.getLocation()); + if (location == null || !(location.getWorld() instanceof PatchBukkitWorld world)) { + yield null; + } + Block block = PatchBukkitBlock.create( + world, + location.getBlockX(), + location.getBlockY(), + location.getBlockZ(), + noteEvent.getBlockKey() + ); + Instrument instrument = Instrument.PIANO; + if (!noteEvent.getInstrument().isEmpty()) { + try { + instrument = Instrument.valueOf(noteEvent.getInstrument()); + } catch (IllegalArgumentException ignored) { + instrument = Instrument.PIANO; + } + } + int rawNote = Math.max(0, Math.min(24, noteEvent.getNote())); + Note note = new Note((byte) rawNote); + yield new NotePlayEvent(block, instrument, note); + } + case SIGN_CHANGE -> { + patchbukkit.events.SignChangeEvent signEvent = event.getSignChange(); + Player player = getPlayer(signEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + + Location location = BridgeUtils.convertLocation(signEvent.getLocation()); + if (location == null || !(location.getWorld() instanceof PatchBukkitWorld world)) { + yield null; + } + Block block = PatchBukkitBlock.create( + world, + location.getBlockX(), + location.getBlockY(), + location.getBlockZ(), + signEvent.getBlockKey() + ); + String[] lines = new String[4]; + java.util.List linesList = signEvent.getLinesList(); + for (int i = 0; i < lines.length; i++) { + lines[i] = i < linesList.size() ? linesList.get(i) : ""; + } + org.bukkit.event.Event constructed = + createSignChangeEvent(block, player, lines, signEvent.getIsFrontText()); + if (constructed instanceof SignChangeEvent signChange) { + yield signChange; + } + yield null; + } + case TNT_PRIME -> { + patchbukkit.events.TNTPrimeEvent tntEvent = event.getTntPrime(); + Location location = BridgeUtils.convertLocation(tntEvent.getLocation()); + if (location == null || !(location.getWorld() instanceof PatchBukkitWorld world)) { + yield null; + } + Block block = PatchBukkitBlock.create( + world, + location.getBlockX(), + location.getBlockY(), + location.getBlockZ(), + tntEvent.getBlockKey() + ); + TNTPrimeEvent.PrimeCause cause = TNTPrimeEvent.PrimeCause.PLAYER; + if (!tntEvent.getCause().isEmpty()) { + try { + cause = TNTPrimeEvent.PrimeCause.valueOf(tntEvent.getCause()); + } catch (IllegalArgumentException ignored) { + cause = TNTPrimeEvent.PrimeCause.PLAYER; + } + } + Player player = null; + if (tntEvent.hasPlayerUuid()) { + player = getPlayer(tntEvent.getPlayerUuid().getValue()); + } + org.bukkit.event.Event constructed = createTntPrimeEvent(block, cause, player); + if (constructed instanceof TNTPrimeEvent tntPrime) { + yield tntPrime; + } + yield null; + } + case MOISTURE_CHANGE -> { + patchbukkit.events.MoistureChangeEvent moistureEvent = event.getMoistureChange(); + Location location = BridgeUtils.convertLocation(moistureEvent.getLocation()); + if (location == null || !(location.getWorld() instanceof PatchBukkitWorld world)) { + yield null; + } + Block block = PatchBukkitBlock.create( + world, + location.getBlockX(), + location.getBlockY(), + location.getBlockZ(), + moistureEvent.getBlockKey() + ); + String newKey = moistureEvent.getNewBlockKey().isEmpty() + ? "minecraft:air" + : moistureEvent.getNewBlockKey(); + Block newBlock = PatchBukkitBlock.create( + world, + location.getBlockX(), + location.getBlockY(), + location.getBlockZ(), + newKey + ); + yield new MoistureChangeEvent(block, newBlock.getState()); + } + case SPONGE_ABSORB -> { + patchbukkit.events.SpongeAbsorbEvent spongeEvent = event.getSpongeAbsorb(); + Location location = BridgeUtils.convertLocation(spongeEvent.getLocation()); + if (location == null || !(location.getWorld() instanceof PatchBukkitWorld world)) { + yield null; + } + Block block = PatchBukkitBlock.create( + world, + location.getBlockX(), + location.getBlockY(), + location.getBlockZ(), + spongeEvent.getBlockKey() + ); + java.util.List states = new java.util.ArrayList<>(); + for (patchbukkit.events.SpongeAbsorbBlockEntry entry : spongeEvent.getBlocksList()) { + Location entryLoc = BridgeUtils.convertLocation(entry.getLocation()); + if (entryLoc == null || !(entryLoc.getWorld() instanceof PatchBukkitWorld entryWorld)) { + continue; + } + String key = entry.getBlockKey().isEmpty() + ? "minecraft:air" + : entry.getBlockKey(); + Block entryBlock = PatchBukkitBlock.create( + entryWorld, + entryLoc.getBlockX(), + entryLoc.getBlockY(), + entryLoc.getBlockZ(), + key + ); + states.add(entryBlock.getState()); + } + yield new SpongeAbsorbEvent(block, states); + } + case FLUID_LEVEL_CHANGE -> { + patchbukkit.events.FluidLevelChangeEvent fluidEvent = event.getFluidLevelChange(); + Location location = BridgeUtils.convertLocation(fluidEvent.getLocation()); + if (location == null || !(location.getWorld() instanceof PatchBukkitWorld world)) { + yield null; + } + Block block = PatchBukkitBlock.create( + world, + location.getBlockX(), + location.getBlockY(), + location.getBlockZ(), + fluidEvent.getBlockKey() + ); + String newKey = fluidEvent.getNewBlockKey().isEmpty() + ? "minecraft:air" + : fluidEvent.getNewBlockKey(); + Block newBlock = PatchBukkitBlock.create( + world, + location.getBlockX(), + location.getBlockY(), + location.getBlockZ(), + newKey + ); + org.bukkit.event.Event created = createFluidLevelChangeEvent(block, newBlock); + if (created instanceof FluidLevelChangeEvent) { + yield created; + } + yield null; + } + case SPAWN_CHANGE -> { + patchbukkit.events.SpawnChangeEvent spawnEvent = event.getSpawnChange(); + Location location = BridgeUtils.convertLocation(spawnEvent.getLocation()); + if (location == null || !(location.getWorld() instanceof PatchBukkitWorld world)) { + yield null; + } + Location previous = BridgeUtils.convertLocation(spawnEvent.getPreviousLocation()); + if (previous == null) { + previous = location; + } + org.bukkit.event.Event constructed = + createSpawnChangeEvent(world, location, previous); + if (constructed instanceof SpawnChangeEvent spawnChange) { + yield spawnChange; + } + yield null; + } + case BLOCK_CAN_BUILD -> { + patchbukkit.events.BlockCanBuildEvent canBuildEvent = event.getBlockCanBuild(); + Player player = getPlayer(canBuildEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + + Location location = BridgeUtils.convertLocation(canBuildEvent.getLocation()); + if (location == null || !(location.getWorld() instanceof PatchBukkitWorld world)) { + yield null; + } + + org.bukkit.block.Block block = PatchBukkitBlock.create( + world, + location.getBlockX(), + location.getBlockY(), + location.getBlockZ(), + canBuildEvent.getBlockKey() + ); + org.bukkit.block.data.BlockData data = block.getBlockData(); + yield createBlockCanBuildEvent(block, data, canBuildEvent.getCanBuild()); + } + case BLOCK_BURN -> { + patchbukkit.events.BlockBurnEvent burnEvent = event.getBlockBurn(); + Location location = BridgeUtils.convertLocation(burnEvent.getLocation()); + if (location == null || !(location.getWorld() instanceof PatchBukkitWorld world)) { + yield null; + } + + org.bukkit.block.Block block = PatchBukkitBlock.create( + world, + location.getBlockX(), + location.getBlockY(), + location.getBlockZ(), + burnEvent.getBlockKey() + ); + BlockBurnEvent eventObj = new BlockBurnEvent(block); + setIgnitingBlock(eventObj, burnEvent.getIgnitingBlockKey()); + yield eventObj; + } + case BLOCK_IGNITE -> { + patchbukkit.events.BlockIgniteEvent igniteEvent = event.getBlockIgnite(); + Player player = null; + if (igniteEvent.hasPlayerUuid() + && igniteEvent.getPlayerUuid() != null + && !igniteEvent.getPlayerUuid().getValue().isEmpty()) { + player = getPlayer(igniteEvent.getPlayerUuid().getValue()); + } + Location location = BridgeUtils.convertLocation(igniteEvent.getLocation()); + if (location == null || !(location.getWorld() instanceof PatchBukkitWorld world)) { + yield null; + } + Block block = PatchBukkitBlock.create( + world, + location.getBlockX(), + location.getBlockY(), + location.getBlockZ(), + igniteEvent.getBlockKey() + ); + BlockIgniteEvent.IgniteCause cause = BlockIgniteEvent.IgniteCause.FLINT_AND_STEEL; + if (!igniteEvent.getCause().isEmpty()) { + try { + cause = BlockIgniteEvent.IgniteCause.valueOf(igniteEvent.getCause()); + } catch (IllegalArgumentException ignored) { + cause = BlockIgniteEvent.IgniteCause.FLINT_AND_STEEL; + } + } + BlockIgniteEvent eventObj = new BlockIgniteEvent(block, cause, player); + yield eventObj; + } + case BLOCK_SPREAD -> { + patchbukkit.events.BlockSpreadEvent spreadEvent = event.getBlockSpread(); + Location sourceLocation = BridgeUtils.convertLocation(spreadEvent.getSourceLocation()); + Location location = BridgeUtils.convertLocation(spreadEvent.getLocation()); + if (sourceLocation == null || location == null) yield null; + if (!(sourceLocation.getWorld() instanceof PatchBukkitWorld sourceWorld)) yield null; + if (!(location.getWorld() instanceof PatchBukkitWorld world)) yield null; + Block sourceBlock = PatchBukkitBlock.create( + sourceWorld, + sourceLocation.getBlockX(), + sourceLocation.getBlockY(), + sourceLocation.getBlockZ(), + spreadEvent.getSourceBlockKey() + ); + Block block = PatchBukkitBlock.create( + world, + location.getBlockX(), + location.getBlockY(), + location.getBlockZ(), + spreadEvent.getBlockKey() + ); + org.bukkit.block.BlockState newState = block.getState(); + yield new BlockSpreadEvent(block, sourceBlock, newState); + } + case BLOCK_PLACE -> { + patchbukkit.events.BlockPlaceEvent placeEvent = event.getBlockPlace(); + Player player = getPlayer(placeEvent.getPlayerUuid().getValue()); + if (player == null) yield null; + + Location location = BridgeUtils.convertLocation(placeEvent.getLocation()); + if (location == null || !(location.getWorld() instanceof PatchBukkitWorld world)) { + yield null; + } + + org.bukkit.block.Block placed = PatchBukkitBlock.create( + world, + location.getBlockX(), + location.getBlockY(), + location.getBlockZ(), + placeEvent.getBlockKey() + ); + org.bukkit.block.Block against = PatchBukkitBlock.create( + world, + location.getBlockX(), + location.getBlockY(), + location.getBlockZ(), + placeEvent.getBlockAgainstKey() + ); + + BlockPlaceEvent eventObj = new BlockPlaceEvent( + placed, + placed.getState(), + against, + null, + player, + placeEvent.getCanBuild(), + null + ); + yield eventObj; + } + case ENTITY_SPAWN -> { + patchbukkit.events.EntitySpawnEvent spawnEvent = event.getEntitySpawn(); + java.util.UUID entityUuid = java.util.UUID.fromString(spawnEvent.getEntityUuid().getValue()); + PatchBukkitEntity entity = new PatchBukkitEntity(entityUuid, spawnEvent.getEntityType()); + yield new EntitySpawnEvent(entity); + } + case ENTITY_DAMAGE -> { + patchbukkit.events.EntityDamageEvent damageEvent = event.getEntityDamage(); + java.util.UUID entityUuid = java.util.UUID.fromString(damageEvent.getEntityUuid().getValue()); + PatchBukkitEntity entity = new PatchBukkitEntity(entityUuid, "Entity"); + yield new EntityDamageEvent(entity, DamageCause.CUSTOM, damageEvent.getDamage()); + } + case ENTITY_DEATH -> { + patchbukkit.events.EntityDeathEvent deathEvent = event.getEntityDeath(); + java.util.UUID entityUuid = java.util.UUID.fromString(deathEvent.getEntityUuid().getValue()); + PatchBukkitLivingEntity entity = new PatchBukkitLivingEntity(entityUuid, "Entity"); + org.bukkit.event.Event created = createEntityDeathEvent(entity); + if (created instanceof EntityDeathEvent) { + yield created; + } + yield null; + } + case SERVER_COMMAND -> { + patchbukkit.events.ServerCommandEvent commandEvent = event.getServerCommand(); + yield new ServerCommandEvent(new PatchBukkitConsoleCommandSender(), commandEvent.getCommand()); + } + case SERVER_BROADCAST -> { + patchbukkit.events.ServerBroadcastEvent broadcastEvent = event.getServerBroadcast(); + Component messageComponent = GsonComponentSerializer.gson().deserialize(broadcastEvent.getMessage()); + String message = PlainTextComponentSerializer.plainText().serialize(messageComponent); + org.bukkit.event.Event created = createBroadcastMessageEvent(messageComponent, message); + if (created instanceof BroadcastMessageEvent) { + yield created; + } + yield null; + } + case SERVER_LIST_PING -> { + patchbukkit.events.ServerListPingEvent pingEvent = event.getServerListPing(); + String motd = pingEvent.getMotd(); + int maxPlayers = pingEvent.getMaxPlayers(); + int onlinePlayers = pingEvent.getOnlinePlayers(); + String favicon = pingEvent.getFavicon(); + org.bukkit.event.Event constructed = + createServerListPingEvent(motd, maxPlayers, onlinePlayers, favicon); + if (constructed instanceof ServerListPingEvent serverListPing) { + yield serverListPing; + } + yield null; + } + case PLUGIN_ENABLE -> { + patchbukkit.events.PluginEnableEvent enableEvent = event.getPluginEnable(); + String pluginName = enableEvent.getPluginName(); + org.bukkit.plugin.Plugin plugin = Bukkit.getPluginManager().getPlugin(pluginName); + if (plugin == null) yield null; + yield new PluginEnableEvent(plugin); + } + case PLUGIN_DISABLE -> { + patchbukkit.events.PluginDisableEvent disableEvent = event.getPluginDisable(); + String pluginName = disableEvent.getPluginName(); + org.bukkit.plugin.Plugin plugin = Bukkit.getPluginManager().getPlugin(pluginName); + if (plugin == null) yield null; + yield new PluginDisableEvent(plugin); + } + case SERVICE_REGISTER -> { + patchbukkit.events.ServiceRegisterEvent registerEvent = event.getServiceRegister(); + RegisteredServiceProvider provider = resolveServiceProvider( + registerEvent.getServiceName(), + registerEvent.getPluginName() + ); + if (provider == null) yield null; + yield new ServiceRegisterEvent(provider); + } + case SERVICE_UNREGISTER -> { + patchbukkit.events.ServiceUnregisterEvent unregisterEvent = event.getServiceUnregister(); + RegisteredServiceProvider provider = resolveServiceProvider( + unregisterEvent.getServiceName(), + unregisterEvent.getPluginName() + ); + if (provider == null) yield null; + yield new ServiceUnregisterEvent(provider); + } case DATA_NOT_SET -> { LOGGER.warning("EventFactory: Received Event with no data"); yield null; @@ -69,29 +2004,2443 @@ public static byte[] toFireEventResponse(@NotNull org.bukkit.event.Event event) .setJoinMessage(joinMessage) .build() ); - } - - builder.setData(eventBuilder.build()); - - return builder.build().toByteArray(); - } - - public static boolean isCancellable(@NotNull org.bukkit.event.Event event) { - return event instanceof org.bukkit.event.Cancellable; - } + } else if (event instanceof PlayerQuitEvent quitEvent) { + String quitMessage = GsonComponentSerializer.gson().serialize(quitEvent.quitMessage()); - @Nullable - private static Player getPlayer(@NotNull String uuidStr) { - try { - java.util.UUID uuid = java.util.UUID.fromString(uuidStr); - Player player = Bukkit.getServer().getPlayer(uuid); - if (player == null) { - LOGGER.warning("EventFactory: Player not found for UUID " + uuidStr); + eventBuilder.setPlayerLeave( + PlayerLeaveEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(quitEvent.getPlayer().getUniqueId().toString()) + .build()) + .setLeaveMessage(quitMessage) + .build() + ); + } else if (event instanceof PlayerLoginEvent loginEvent) { + String kickMessage = loginEvent.getKickMessage(); + if (kickMessage == null) { + kickMessage = ""; } - return player; - } catch (IllegalArgumentException e) { - LOGGER.severe("EventFactory: Invalid UUID string: " + uuidStr); - return null; + eventBuilder.setPlayerLogin( + patchbukkit.events.PlayerLoginEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(loginEvent.getPlayer().getUniqueId().toString()) + .build()) + .setKickMessage(GsonComponentSerializer.gson().serialize(Component.text(kickMessage))) + .build() + ); + } else if (event instanceof AsyncPlayerPreLoginEvent preLoginEvent) { + String address = preLoginEvent.getAddress() != null + ? preLoginEvent.getAddress().getHostAddress() + : ""; + String result = preLoginEvent.getLoginResult() != null + ? preLoginEvent.getLoginResult().name() + : "ALLOWED"; + String kickMessage = preLoginEvent.getKickMessage() != null + ? preLoginEvent.getKickMessage() + : ""; + eventBuilder.setAsyncPlayerPreLogin( + patchbukkit.events.AsyncPlayerPreLoginEvent.newBuilder() + .setName(preLoginEvent.getName()) + .setPlayerUuid(UUID.newBuilder() + .setValue(preLoginEvent.getUniqueId().toString()) + .build()) + .setAddress(address) + .setResult(result) + .setKickMessage(kickMessage) + .build() + ); + } else if (event instanceof PlayerMoveEvent moveEvent) { + eventBuilder.setPlayerMove( + patchbukkit.events.PlayerMoveEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(moveEvent.getPlayer().getUniqueId().toString()) + .build()) + .setFrom(BridgeUtils.convertLocation(moveEvent.getFrom())) + .setTo(BridgeUtils.convertLocation(moveEvent.getTo())) + .build() + ); + } else if (event instanceof PlayerTeleportEvent teleportEvent) { + String cause = teleportEvent.getCause() != null + ? teleportEvent.getCause().name() + : ""; + eventBuilder.setPlayerTeleport( + patchbukkit.events.PlayerTeleportEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(teleportEvent.getPlayer().getUniqueId().toString()) + .build()) + .setFrom(BridgeUtils.convertLocation(teleportEvent.getFrom())) + .setTo(BridgeUtils.convertLocation(teleportEvent.getTo())) + .setCause(cause) + .build() + ); + } else if (event instanceof PlayerChangedWorldEvent changeEvent) { + var previousWorld = changeEvent.getFrom(); + var currentWorld = changeEvent.getPlayer().getWorld(); + var location = changeEvent.getPlayer().getLocation(); + eventBuilder.setPlayerChangeWorld( + patchbukkit.events.PlayerChangeWorldEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(changeEvent.getPlayer().getUniqueId().toString()) + .build()) + .setPreviousWorld(BridgeUtils.convertWorld(previousWorld)) + .setNewWorld(BridgeUtils.convertWorld(currentWorld)) + .setPosition(BridgeUtils.convertLocation(location)) + .setYaw(location.getYaw()) + .setPitch(location.getPitch()) + .build() + ); + } else if (event instanceof PlayerGameModeChangeEvent gameModeChangeEvent) { + GameMode previous = gameModeChangeEvent.getPlayer().getGameMode(); + GameMode next = gameModeChangeEvent.getNewGameMode(); + eventBuilder.setPlayerGamemodeChange( + patchbukkit.events.PlayerGamemodeChangeEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(gameModeChangeEvent.getPlayer().getUniqueId().toString()) + .build()) + .setPreviousGamemode(previous != null ? previous.name() : "") + .setNewGamemode(next != null ? next.name() : "") + .build() + ); + } else if (event instanceof PlayerAdvancementDoneEvent advancementEvent) { + String key = advancementEvent.getAdvancement().getKey().toString(); + eventBuilder.setPlayerAdvancementDone( + patchbukkit.events.PlayerAdvancementDoneEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(advancementEvent.getPlayer().getUniqueId().toString()) + .build()) + .setAdvancementKey(key) + .build() + ); + } else if (event instanceof PlayerAnimationEvent animationEvent) { + eventBuilder.setPlayerAnimation( + patchbukkit.events.PlayerAnimationEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(animationEvent.getPlayer().getUniqueId().toString()) + .build()) + .setAnimationType(animationEvent.getAnimationType().name()) + .build() + ); + } else if (event instanceof PlayerArmorStandManipulateEvent armorEvent) { + ItemStack playerItem = armorEvent.getPlayerItem(); + ItemStack standItem = armorEvent.getArmorStandItem(); + String playerKey = playerItem != null ? playerItem.getType().getKey().toString() : "minecraft:air"; + String standKey = standItem != null ? standItem.getType().getKey().toString() : "minecraft:air"; + String slot = armorEvent.getSlot() != null ? armorEvent.getSlot().name() : "HAND"; + eventBuilder.setPlayerArmorStandManipulate( + patchbukkit.events.PlayerArmorStandManipulateEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(armorEvent.getPlayer().getUniqueId().toString()) + .build()) + .setArmorStandUuid(UUID.newBuilder() + .setValue(armorEvent.getRightClicked().getUniqueId().toString()) + .build()) + .setItemKey(playerKey) + .setArmorStandItemKey(standKey) + .setSlot(slot) + .build() + ); + } else if (event instanceof PlayerBedEnterEvent bedEvent) { + Location location = bedEvent.getBed().getLocation(); + eventBuilder.setPlayerBedEnter( + patchbukkit.events.PlayerBedEnterEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(bedEvent.getPlayer().getUniqueId().toString()) + .build()) + .setBedLocation(BridgeUtils.convertLocation(location)) + .build() + ); + } else if (event instanceof PlayerBedLeaveEvent bedEvent) { + Location location = bedEvent.getBed().getLocation(); + eventBuilder.setPlayerBedLeave( + patchbukkit.events.PlayerBedLeaveEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(bedEvent.getPlayer().getUniqueId().toString()) + .build()) + .setBedLocation(BridgeUtils.convertLocation(location)) + .build() + ); + } else if (event instanceof PlayerBucketEmptyEvent bucketEvent) { + Location location = bucketEvent.getBlock().getLocation(); + String blockKey = bucketEvent.getBlock().getType().getKey().toString(); + String face = bucketEvent.getBlockFace() != null ? bucketEvent.getBlockFace().name() : ""; + String bucketKey = bucketEvent.getItemStack() != null + ? bucketEvent.getItemStack().getType().getKey().toString() + : "minecraft:air"; + String hand = bucketEvent.getHand() != null ? bucketEvent.getHand().name() : "HAND"; + eventBuilder.setPlayerBucketEmpty( + patchbukkit.events.PlayerBucketEmptyEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(bucketEvent.getPlayer().getUniqueId().toString()) + .build()) + .setLocation(BridgeUtils.convertLocation(location)) + .setBlockKey(blockKey) + .setBlockFace(face) + .setBucketItemKey(bucketKey) + .setHand(hand) + .build() + ); + } else if (event instanceof PlayerBucketFillEvent bucketEvent) { + Location location = bucketEvent.getBlock().getLocation(); + String blockKey = bucketEvent.getBlock().getType().getKey().toString(); + String face = bucketEvent.getBlockFace() != null ? bucketEvent.getBlockFace().name() : ""; + String bucketKey = bucketEvent.getItemStack() != null + ? bucketEvent.getItemStack().getType().getKey().toString() + : "minecraft:air"; + String hand = bucketEvent.getHand() != null ? bucketEvent.getHand().name() : "HAND"; + eventBuilder.setPlayerBucketFill( + patchbukkit.events.PlayerBucketFillEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(bucketEvent.getPlayer().getUniqueId().toString()) + .build()) + .setLocation(BridgeUtils.convertLocation(location)) + .setBlockKey(blockKey) + .setBlockFace(face) + .setBucketItemKey(bucketKey) + .setHand(hand) + .build() + ); + } else if (event instanceof PlayerBucketEntityEvent bucketEvent) { + String originalBucket = bucketEvent.getOriginalBucket() != null + ? bucketEvent.getOriginalBucket().getType().getKey().toString() + : "minecraft:air"; + String entityBucket = bucketEvent.getEntityBucket() != null + ? bucketEvent.getEntityBucket().getType().getKey().toString() + : "minecraft:air"; + String hand = bucketEvent.getHand() != null ? bucketEvent.getHand().name() : "HAND"; + eventBuilder.setPlayerBucketEntity( + patchbukkit.events.PlayerBucketEntityEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(bucketEvent.getPlayer().getUniqueId().toString()) + .build()) + .setEntityUuid(UUID.newBuilder() + .setValue(bucketEvent.getEntity().getUniqueId().toString()) + .build()) + .setEntityType(bucketEvent.getEntity().getType().name()) + .setOriginalBucketKey(originalBucket) + .setEntityBucketKey(entityBucket) + .setHand(hand) + .build() + ); + } else if (event instanceof PlayerChangedMainHandEvent mainHandEvent) { + String mainHand = mainHandEvent.getMainHand() != null + ? mainHandEvent.getMainHand().name() + : "RIGHT"; + eventBuilder.setPlayerChangedMainHand( + patchbukkit.events.PlayerChangedMainHandEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(mainHandEvent.getPlayer().getUniqueId().toString()) + .build()) + .setMainHand(mainHand) + .build() + ); + } else if (event instanceof PlayerRegisterChannelEvent registerChannelEvent) { + eventBuilder.setPlayerRegisterChannel( + patchbukkit.events.PlayerRegisterChannelEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(registerChannelEvent.getPlayer().getUniqueId().toString()) + .build()) + .setChannel(registerChannelEvent.getChannel()) + .build() + ); + } else if (event instanceof PlayerUnregisterChannelEvent unregisterChannelEvent) { + eventBuilder.setPlayerUnregisterChannel( + patchbukkit.events.PlayerUnregisterChannelEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(unregisterChannelEvent.getPlayer().getUniqueId().toString()) + .build()) + .setChannel(unregisterChannelEvent.getChannel()) + .build() + ); + } else if (event instanceof PlayerItemHeldEvent heldEvent) { + eventBuilder.setPlayerItemHeld( + patchbukkit.events.PlayerItemHeldEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(heldEvent.getPlayer().getUniqueId().toString()) + .build()) + .setPreviousSlot(heldEvent.getPreviousSlot()) + .setNewSlot(heldEvent.getNewSlot()) + .build() + ); + } else if (event instanceof PlayerItemDamageEvent damageEvent) { + ItemStack item = damageEvent.getItem(); + String itemKey = item != null ? item.getType().getKey().toString() : "minecraft:air"; + int itemAmount = item != null ? item.getAmount() : 0; + eventBuilder.setPlayerItemDamage( + patchbukkit.events.PlayerItemDamageEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(damageEvent.getPlayer().getUniqueId().toString()) + .build()) + .setItemKey(itemKey) + .setItemAmount(itemAmount) + .setDamage(damageEvent.getDamage()) + .build() + ); + } else if (event instanceof PlayerItemBreakEvent breakEvent) { + ItemStack item = breakEvent.getBrokenItem(); + String itemKey = item != null ? item.getType().getKey().toString() : "minecraft:air"; + int itemAmount = item != null ? item.getAmount() : 0; + eventBuilder.setPlayerItemBreak( + patchbukkit.events.PlayerItemBreakEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(breakEvent.getPlayer().getUniqueId().toString()) + .build()) + .setItemKey(itemKey) + .setItemAmount(itemAmount) + .build() + ); + } else if (event instanceof PlayerItemConsumeEvent consumeEvent) { + ItemStack item = consumeEvent.getItem(); + String itemKey = item != null ? item.getType().getKey().toString() : "minecraft:air"; + int itemAmount = item != null ? item.getAmount() : 0; + String hand = consumeEvent.getHand() != null ? consumeEvent.getHand().name() : "HAND"; + eventBuilder.setPlayerItemConsume( + patchbukkit.events.PlayerItemConsumeEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(consumeEvent.getPlayer().getUniqueId().toString()) + .build()) + .setItemKey(itemKey) + .setItemAmount(itemAmount) + .setHand(hand) + .build() + ); + } else if (event instanceof PlayerItemMendEvent mendEvent) { + ItemStack item = mendEvent.getItem(); + String itemKey = item != null ? item.getType().getKey().toString() : "minecraft:air"; + int itemAmount = item != null ? item.getAmount() : 0; + String slot = mendEvent.getSlot() != null ? mendEvent.getSlot().name() : "HAND"; + var eventBuilderLocal = patchbukkit.events.PlayerItemMendEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(mendEvent.getPlayer().getUniqueId().toString()) + .build()) + .setItemKey(itemKey) + .setItemAmount(itemAmount) + .setSlot(slot) + .setRepairAmount(mendEvent.getRepairAmount()); + if (mendEvent.getExperienceOrb() != null) { + eventBuilderLocal.setOrbUuid(UUID.newBuilder() + .setValue(mendEvent.getExperienceOrb().getUniqueId().toString()) + .build()); + } + eventBuilder.setPlayerItemMend(eventBuilderLocal.build()); + } else if (event instanceof PlayerLevelChangeEvent levelEvent) { + eventBuilder.setPlayerLevelChange( + patchbukkit.events.PlayerLevelChangeEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(levelEvent.getPlayer().getUniqueId().toString()) + .build()) + .setOldLevel(levelEvent.getOldLevel()) + .setNewLevel(levelEvent.getNewLevel()) + .build() + ); + } else if (event instanceof PlayerKickEvent kickEvent) { + String reason = GsonComponentSerializer.gson().serialize(kickEvent.reason()); + String leaveMessage = GsonComponentSerializer.gson().serialize(kickEvent.leaveMessage()); + String cause = kickEvent.getCause() != null ? kickEvent.getCause().name() : "UNKNOWN"; + eventBuilder.setPlayerKick( + patchbukkit.events.PlayerKickEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(kickEvent.getPlayer().getUniqueId().toString()) + .build()) + .setReason(reason) + .setLeaveMessage(leaveMessage) + .setCause(cause) + .build() + ); + } else if (event instanceof PlayerToggleSneakEvent toggleEvent) { + eventBuilder.setPlayerToggleSneak( + patchbukkit.events.PlayerToggleSneakEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(toggleEvent.getPlayer().getUniqueId().toString()) + .build()) + .setIsSneaking(toggleEvent.isSneaking()) + .build() + ); + } else if (event instanceof PlayerToggleSprintEvent toggleEvent) { + eventBuilder.setPlayerToggleSprint( + patchbukkit.events.PlayerToggleSprintEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(toggleEvent.getPlayer().getUniqueId().toString()) + .build()) + .setIsSprinting(toggleEvent.isSprinting()) + .build() + ); + } else if (event instanceof PlayerToggleFlightEvent toggleEvent) { + eventBuilder.setPlayerToggleFlight( + patchbukkit.events.PlayerToggleFlightEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(toggleEvent.getPlayer().getUniqueId().toString()) + .build()) + .setIsFlying(toggleEvent.isFlying()) + .build() + ); + } else if (event instanceof PlayerSwapHandItemsEvent swapEvent) { + ItemStack mainHand = swapEvent.getMainHandItem(); + ItemStack offHand = swapEvent.getOffHandItem(); + String mainKey = mainHand != null ? mainHand.getType().getKey().toString() : "minecraft:air"; + int mainAmount = mainHand != null ? mainHand.getAmount() : 0; + String offKey = offHand != null ? offHand.getType().getKey().toString() : "minecraft:air"; + int offAmount = offHand != null ? offHand.getAmount() : 0; + eventBuilder.setPlayerSwapHandItems( + patchbukkit.events.PlayerSwapHandItemsEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(swapEvent.getPlayer().getUniqueId().toString()) + .build()) + .setMainHandItemKey(mainKey) + .setMainHandItemAmount(mainAmount) + .setOffHandItemKey(offKey) + .setOffHandItemAmount(offAmount) + .build() + ); + } else if (event instanceof PlayerResourcePackStatusEvent packEvent) { + java.util.UUID packUuid = resolveResourcePackId(packEvent); + String status = packEvent.getStatus() != null ? packEvent.getStatus().name() : "FAILED_DOWNLOAD"; + String hash = packEvent.getHash() != null ? packEvent.getHash() : ""; + eventBuilder.setPlayerResourcePackStatus( + patchbukkit.events.PlayerResourcePackStatusEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(packEvent.getPlayer().getUniqueId().toString()) + .build()) + .setPackUuid(UUID.newBuilder() + .setValue(packUuid.toString()) + .build()) + .setStatus(status) + .setHash(hash) + .build() + ); + } else if (event instanceof PlayerRespawnEvent respawnEvent) { + Location location = respawnEvent.getRespawnLocation(); + String reason = respawnEvent.getRespawnReason() != null + ? respawnEvent.getRespawnReason().name() + : "DEATH"; + eventBuilder.setPlayerRespawn( + patchbukkit.events.PlayerRespawnEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(respawnEvent.getPlayer().getUniqueId().toString()) + .build()) + .setRespawnLocation(BridgeUtils.convertLocation(location)) + .setIsBedSpawn(respawnEvent.isBedSpawn()) + .setIsAnchorSpawn(respawnEvent.isAnchorSpawn()) + .setIsMissingRespawnBlock(respawnEvent.isMissingRespawnBlock()) + .setRespawnReason(reason) + .build() + ); + } else if (event instanceof PlayerPickupArrowEvent pickupEvent) { + var item = pickupEvent.getItem(); + String itemKey = "minecraft:air"; + int itemAmount = 0; + if (item != null && item.getItemStack() != null) { + itemKey = item.getItemStack().getType().getKey().toString(); + itemAmount = item.getItemStack().getAmount(); + } + eventBuilder.setPlayerPickupArrow( + patchbukkit.events.PlayerPickupArrowEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(pickupEvent.getPlayer().getUniqueId().toString()) + .build()) + .setArrowUuid(UUID.newBuilder() + .setValue(pickupEvent.getArrow().getUniqueId().toString()) + .build()) + .setItemUuid(UUID.newBuilder() + .setValue(item != null ? item.getUniqueId().toString() : java.util.UUID.randomUUID().toString()) + .build()) + .setItemKey(itemKey) + .setItemAmount(itemAmount) + .setRemaining(pickupEvent.getRemaining()) + .build() + ); + } else if (event instanceof PlayerPortalEvent portalEvent) { + String cause = portalEvent.getCause() != null ? portalEvent.getCause().name() : ""; + int searchRadius = 0; + boolean canCreatePortal = false; + int creationRadius = 0; + try { + var method = PlayerPortalEvent.class.getMethod("getSearchRadius"); + Object value = method.invoke(portalEvent); + if (value instanceof Integer i) { + searchRadius = i; + } + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + var method = PlayerPortalEvent.class.getMethod("getCanCreatePortal"); + Object value = method.invoke(portalEvent); + if (value instanceof Boolean b) { + canCreatePortal = b; + } + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + var method = PlayerPortalEvent.class.getMethod("getCreationRadius"); + Object value = method.invoke(portalEvent); + if (value instanceof Integer i) { + creationRadius = i; + } + } catch (ReflectiveOperationException ignored) { + // ignore + } + eventBuilder.setPlayerPortal( + patchbukkit.events.PlayerPortalEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(portalEvent.getPlayer().getUniqueId().toString()) + .build()) + .setFrom(BridgeUtils.convertLocation(portalEvent.getFrom())) + .setTo(BridgeUtils.convertLocation(portalEvent.getTo())) + .setCause(cause) + .setSearchRadius(searchRadius) + .setCanCreatePortal(canCreatePortal) + .setCreationRadius(creationRadius) + .build() + ); + } else if (event instanceof PlayerRecipeDiscoverEvent recipeEvent) { + String key = recipeEvent.getRecipe() != null + ? recipeEvent.getRecipe().toString() + : ""; + eventBuilder.setPlayerRecipeDiscover( + patchbukkit.events.PlayerRecipeDiscoverEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(recipeEvent.getPlayer().getUniqueId().toString()) + .build()) + .setRecipeKey(key) + .build() + ); + } else if (event instanceof PlayerRiptideEvent riptideEvent) { + ItemStack item = riptideEvent.getItem(); + String itemKey = item != null ? item.getType().getKey().toString() : "minecraft:air"; + int itemAmount = item != null ? item.getAmount() : 0; + Vector velocity = riptideEvent.getVelocity(); + patchbukkit.common.Vec3 vec = patchbukkit.common.Vec3.newBuilder() + .setX(velocity.getX()) + .setY(velocity.getY()) + .setZ(velocity.getZ()) + .build(); + eventBuilder.setPlayerRiptide( + patchbukkit.events.PlayerRiptideEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(riptideEvent.getPlayer().getUniqueId().toString()) + .build()) + .setItemKey(itemKey) + .setItemAmount(itemAmount) + .setVelocity(vec) + .build() + ); + } else if (event instanceof PlayerShearEntityEvent shearEvent) { + ItemStack item = shearEvent.getItem(); + String itemKey = item != null ? item.getType().getKey().toString() : "minecraft:air"; + int itemAmount = item != null ? item.getAmount() : 0; + String hand = shearEvent.getHand() != null ? shearEvent.getHand().name() : "HAND"; + eventBuilder.setPlayerShearEntity( + patchbukkit.events.PlayerShearEntityEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(shearEvent.getPlayer().getUniqueId().toString()) + .build()) + .setEntityUuid(UUID.newBuilder() + .setValue(shearEvent.getEntity().getUniqueId().toString()) + .build()) + .setEntityType(shearEvent.getEntity().getType().name()) + .setItemKey(itemKey) + .setItemAmount(itemAmount) + .setHand(hand) + .build() + ); + } else if (event instanceof PlayerSpawnLocationEvent spawnEvent) { + eventBuilder.setPlayerSpawnLocation( + patchbukkit.events.PlayerSpawnLocationEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(spawnEvent.getPlayer().getUniqueId().toString()) + .build()) + .setSpawnLocation(BridgeUtils.convertLocation(spawnEvent.getSpawnLocation())) + .build() + ); + } else if (event instanceof PlayerStatisticIncrementEvent statEvent) { + String statistic = statEvent.getStatistic().name(); + String entityType = statEvent.getEntityType() != null ? statEvent.getEntityType().name() : ""; + String materialKey = statEvent.getMaterial() != null ? statEvent.getMaterial().getKey().toString() : ""; + eventBuilder.setPlayerStatisticIncrement( + patchbukkit.events.PlayerStatisticIncrementEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(statEvent.getPlayer().getUniqueId().toString()) + .build()) + .setStatistic(statistic) + .setInitialValue(statEvent.getPreviousValue()) + .setNewValue(statEvent.getNewValue()) + .setEntityType(entityType) + .setMaterialKey(materialKey) + .build() + ); + } else if (event instanceof PlayerVelocityEvent velocityEvent) { + Vector velocity = velocityEvent.getVelocity(); + patchbukkit.common.Vec3 vec = patchbukkit.common.Vec3.newBuilder() + .setX(velocity.getX()) + .setY(velocity.getY()) + .setZ(velocity.getZ()) + .build(); + eventBuilder.setPlayerVelocity( + patchbukkit.events.PlayerVelocityEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(velocityEvent.getPlayer().getUniqueId().toString()) + .build()) + .setVelocity(vec) + .build() + ); + } else if (event instanceof PlayerHarvestBlockEvent harvestEvent) { + Block harvestedBlock = resolveHarvestedBlock(harvestEvent); + ItemStack tool = resolveHarvestTool(harvestEvent); + Location location = harvestedBlock != null ? harvestedBlock.getLocation() : null; + String blockKey = harvestedBlock != null + ? harvestedBlock.getType().getKey().toString() + : "minecraft:air"; + String itemKey = tool != null ? tool.getType().getKey().toString() : "minecraft:air"; + int itemAmount = tool != null ? tool.getAmount() : 0; + eventBuilder.setPlayerHarvestBlock( + patchbukkit.events.PlayerHarvestBlockEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(harvestEvent.getPlayer().getUniqueId().toString()) + .build()) + .setBlockLocation(BridgeUtils.convertLocation(location)) + .setBlockKey(blockKey) + .setItemKey(itemKey) + .setItemAmount(itemAmount) + .build() + ); + } else if (event instanceof AsyncPlayerChatEvent chatEvent) { + var playerEventBuilder = patchbukkit.events.PlayerChatEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(chatEvent.getPlayer().getUniqueId().toString()) + .build()) + .setMessage(chatEvent.getMessage()); + + for (Player recipient : chatEvent.getRecipients()) { + playerEventBuilder.addRecipients( + UUID.newBuilder() + .setValue(recipient.getUniqueId().toString()) + .build() + ); + } + + eventBuilder.setPlayerChat(playerEventBuilder.build()); + } else if (event instanceof PlayerCommandPreprocessEvent commandEvent) { + eventBuilder.setPlayerCommand( + PlayerCommandEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(commandEvent.getPlayer().getUniqueId().toString()) + .build()) + .setCommand(commandEvent.getMessage()) + .build() + ); + } else if (event instanceof PlayerCommandSendEvent commandSendEvent) { + var eventBuilderLocal = patchbukkit.events.PlayerCommandSendEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(commandSendEvent.getPlayer().getUniqueId().toString()) + .build()); + eventBuilderLocal.addAllCommands(commandSendEvent.getCommands()); + eventBuilder.setPlayerCommandSend(eventBuilderLocal.build()); + } else if (event instanceof PlayerDropItemEvent dropEvent) { + var item = dropEvent.getItemDrop(); + String itemKey = "minecraft:air"; + int itemAmount = 0; + if (item != null && item.getItemStack() != null) { + itemKey = item.getItemStack().getType().getKey().toString(); + itemAmount = item.getItemStack().getAmount(); + } + eventBuilder.setPlayerDropItem( + patchbukkit.events.PlayerDropItemEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(dropEvent.getPlayer().getUniqueId().toString()) + .build()) + .setItemUuid(UUID.newBuilder() + .setValue(item != null ? item.getUniqueId().toString() : java.util.UUID.randomUUID().toString()) + .build()) + .setItemKey(itemKey) + .setItemAmount(itemAmount) + .build() + ); + } else if (event instanceof PlayerEditBookEvent editEvent) { + BookMeta newMeta = editEvent.getNewBookMeta(); + String title = ""; + java.util.List pages = java.util.Collections.emptyList(); + if (newMeta != null) { + pages = newMeta.getPages(); + if (newMeta.hasTitle()) { + title = newMeta.getTitle(); + } + } + eventBuilder.setPlayerEditBook( + patchbukkit.events.PlayerEditBookEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(editEvent.getPlayer().getUniqueId().toString()) + .build()) + .setSlot(editEvent.getSlot()) + .addAllPages(pages) + .setTitle(title) + .setIsSigning(editEvent.isSigning()) + .build() + ); + } else if (event instanceof PlayerEggThrowEvent eggEvent) { + var hatchingType = eggEvent.getHatchingType() != null + ? eggEvent.getHatchingType().name() + : "CHICKEN"; + var egg = eggEvent.getEgg(); + var eggUuid = egg != null ? egg.getUniqueId().toString() : java.util.UUID.randomUUID().toString(); + eventBuilder.setPlayerEggThrow( + patchbukkit.events.PlayerEggThrowEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(eggEvent.getPlayer().getUniqueId().toString()) + .build()) + .setEggUuid(UUID.newBuilder() + .setValue(eggUuid) + .build()) + .setHatching(eggEvent.isHatching()) + .setNumHatches(eggEvent.getNumHatches()) + .setHatchingType(hatchingType) + .build() + ); + } else if (event instanceof PlayerExpChangeEvent expEvent) { + eventBuilder.setPlayerExpChange( + patchbukkit.events.PlayerExpChangeEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(expEvent.getPlayer().getUniqueId().toString()) + .build()) + .setAmount(expEvent.getAmount()) + .build() + ); + } else if (event instanceof PlayerFishEvent fishEvent) { + var hook = fishEvent.getHook(); + var hookUuid = hook != null ? hook.getUniqueId().toString() : java.util.UUID.randomUUID().toString(); + var caught = fishEvent.getCaught(); + var fishBuilder = patchbukkit.events.PlayerFishEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(fishEvent.getPlayer().getUniqueId().toString()) + .build()) + .setHookUuid(UUID.newBuilder() + .setValue(hookUuid) + .build()) + .setState(fishEvent.getState().name()) + .setExpToDrop(fishEvent.getExpToDrop()); + if (fishEvent.getHand() != null) { + fishBuilder.setHand(fishEvent.getHand().name()); + } + if (caught != null) { + fishBuilder.setCaughtUuid(UUID.newBuilder() + .setValue(caught.getUniqueId().toString()) + .build()); + fishBuilder.setCaughtType(caught.getType().name()); + } + eventBuilder.setPlayerFish(fishBuilder.build()); + } else if (event instanceof PlayerInteractAtEntityEvent interactEvent) { + var entity = interactEvent.getRightClicked(); + var pos = interactEvent.getClickedPosition(); + var eventBuilderLocal = patchbukkit.events.PlayerInteractAtEntityEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(interactEvent.getPlayer().getUniqueId().toString()) + .build()) + .setEntityUuid(UUID.newBuilder() + .setValue(entity.getUniqueId().toString()) + .build()) + .setEntityType(entity.getType().name()); + if (interactEvent.getHand() != null) { + eventBuilderLocal.setHand(interactEvent.getHand().name()); + } + if (pos != null) { + eventBuilderLocal.setClickedPosition(patchbukkit.common.Vec3.newBuilder() + .setX(pos.getX()) + .setY(pos.getY()) + .setZ(pos.getZ()) + .build()); + } + eventBuilder.setPlayerInteractAtEntity(eventBuilderLocal.build()); + } else if (event instanceof PlayerInteractEntityEvent interactEvent) { + var entity = interactEvent.getRightClicked(); + var eventBuilderLocal = patchbukkit.events.PlayerInteractEntityEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(interactEvent.getPlayer().getUniqueId().toString()) + .build()) + .setEntityUuid(UUID.newBuilder() + .setValue(entity.getUniqueId().toString()) + .build()) + .setEntityType(entity.getType().name()); + if (interactEvent.getHand() != null) { + eventBuilderLocal.setHand(interactEvent.getHand().name()); + } + eventBuilder.setPlayerInteractEntity(eventBuilderLocal.build()); + } else if (event instanceof PlayerInteractEvent interactEvent) { + var block = interactEvent.getClickedBlock(); + var location = block != null ? block.getLocation() : null; + String blockKey = block != null ? block.getType().getKey().toString() : "minecraft:air"; + String itemKey = interactEvent.getItem() != null + ? interactEvent.getItem().getType().getKey().toString() + : "minecraft:air"; + String face = interactEvent.getBlockFace() != null + ? interactEvent.getBlockFace().name() + : ""; + eventBuilder.setPlayerInteract( + patchbukkit.events.PlayerInteractEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(interactEvent.getPlayer().getUniqueId().toString()) + .build()) + .setAction(interactEvent.getAction().name()) + .setBlockKey(blockKey) + .setItemKey(itemKey) + .setBlockFace(face) + .setClicked(BridgeUtils.convertLocation(location)) + .build() + ); + } else if (event instanceof BlockBreakEvent breakEvent) { + var block = breakEvent.getBlock(); + eventBuilder.setBlockBreak( + patchbukkit.events.BlockBreakEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(breakEvent.getPlayer().getUniqueId().toString()) + .build()) + .setBlockKey(block.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(block.getLocation())) + .setExp(breakEvent.getExpToDrop()) + .setDrop(breakEvent.isDropItems()) + .build() + ); + } else if (event instanceof BlockDamageEvent damageEvent) { + var block = damageEvent.getBlock(); + ItemStack item = damageEvent.getItemInHand(); + String itemKey = item != null ? item.getType().getKey().toString() : "minecraft:air"; + eventBuilder.setBlockDamage( + patchbukkit.events.BlockDamageEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(damageEvent.getPlayer().getUniqueId().toString()) + .build()) + .setBlockKey(block.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(block.getLocation())) + .setItemKey(itemKey) + .setInstaBreak(resolveInstaBreak(damageEvent)) + .build() + ); + } else if (event instanceof BlockDamageAbortEvent damageAbortEvent) { + var block = damageAbortEvent.getBlock(); + ItemStack item = damageAbortEvent.getItemInHand(); + String itemKey = item != null ? item.getType().getKey().toString() : "minecraft:air"; + eventBuilder.setBlockDamageAbort( + patchbukkit.events.BlockDamageAbortEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(damageAbortEvent.getPlayer().getUniqueId().toString()) + .build()) + .setBlockKey(block.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(block.getLocation())) + .setItemKey(itemKey) + .build() + ); + } else if (event instanceof BlockDispenseEvent dispenseEvent) { + var block = dispenseEvent.getBlock(); + ItemStack item = dispenseEvent.getItem(); + String itemKey = item != null ? item.getType().getKey().toString() : "minecraft:air"; + int amount = item != null ? item.getAmount() : 0; + Vector velocity = dispenseEvent.getVelocity(); + eventBuilder.setBlockDispense( + patchbukkit.events.BlockDispenseEvent.newBuilder() + .setBlockKey(block.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(block.getLocation())) + .setItemKey(itemKey) + .setItemAmount(amount) + .setVelocity(patchbukkit.common.Vec3.newBuilder() + .setX(velocity.getX()) + .setY(velocity.getY()) + .setZ(velocity.getZ()) + .build()) + .build() + ); + } else if (event instanceof BlockDropItemEvent dropItemEvent) { + var block = dropItemEvent.getBlock(); + var eventBuilderLocal = patchbukkit.events.BlockDropItemEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(dropItemEvent.getPlayer().getUniqueId().toString()) + .build()) + .setBlockKey(block.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(block.getLocation())); + + for (org.bukkit.entity.Item item : dropItemEvent.getItems()) { + ItemStack stack = item.getItemStack(); + if (stack == null) continue; + eventBuilderLocal.addItems( + patchbukkit.events.BlockDropItemEntry.newBuilder() + .setItemKey(stack.getType().getKey().toString()) + .setItemAmount(stack.getAmount()) + .build() + ); + } + + eventBuilder.setBlockDropItem(eventBuilderLocal.build()); + } else if (event instanceof BlockExplodeEvent explodeEvent) { + var block = explodeEvent.getBlock(); + var eventBuilderLocal = patchbukkit.events.BlockExplodeEvent.newBuilder() + .setBlockKey(block.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(block.getLocation())) + .setYield(explodeEvent.getYield()); + for (Block b : explodeEvent.blockList()) { + eventBuilderLocal.addBlocks( + patchbukkit.events.BlockExplodeBlockEntry.newBuilder() + .setBlockKey(b.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(b.getLocation())) + .build() + ); + } + eventBuilder.setBlockExplode(eventBuilderLocal.build()); + } else if (event instanceof BlockFadeEvent fadeEvent) { + var block = fadeEvent.getBlock(); + var newState = fadeEvent.getNewState(); + eventBuilder.setBlockFade( + patchbukkit.events.BlockFadeEvent.newBuilder() + .setBlockKey(block.getType().getKey().toString()) + .setNewBlockKey(newState.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(block.getLocation())) + .build() + ); + } else if (event instanceof BlockFertilizeEvent fertilizeEvent) { + var block = fertilizeEvent.getBlock(); + var eventBuilderLocal = patchbukkit.events.BlockFertilizeEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(fertilizeEvent.getPlayer().getUniqueId().toString()) + .build()) + .setBlockKey(block.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(block.getLocation())); + for (org.bukkit.block.BlockState state : fertilizeEvent.getBlocks()) { + eventBuilderLocal.addBlocks( + patchbukkit.events.BlockFertilizeBlockEntry.newBuilder() + .setBlockKey(state.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(state.getLocation())) + .build() + ); + } + eventBuilder.setBlockFertilize(eventBuilderLocal.build()); + } else if (event instanceof BlockFormEvent formEvent) { + var block = formEvent.getBlock(); + var newState = formEvent.getNewState(); + eventBuilder.setBlockForm( + patchbukkit.events.BlockFormEvent.newBuilder() + .setBlockKey(block.getType().getKey().toString()) + .setNewBlockKey(newState.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(block.getLocation())) + .build() + ); + } else if (event instanceof BlockFromToEvent fromToEvent) { + var block = fromToEvent.getBlock(); + var toBlock = fromToEvent.getToBlock(); + String face = fromToEvent.getFace() != null ? fromToEvent.getFace().name() : ""; + eventBuilder.setBlockFromTo( + patchbukkit.events.BlockFromToEvent.newBuilder() + .setBlockKey(block.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(block.getLocation())) + .setToBlockKey(toBlock.getType().getKey().toString()) + .setToLocation(BridgeUtils.convertLocation(toBlock.getLocation())) + .setFace(face) + .build() + ); + } else if (event instanceof BlockGrowEvent growEvent) { + var block = growEvent.getBlock(); + var newState = growEvent.getNewState(); + eventBuilder.setBlockGrow( + patchbukkit.events.BlockGrowEvent.newBuilder() + .setBlockKey(block.getType().getKey().toString()) + .setNewBlockKey(newState.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(block.getLocation())) + .build() + ); + } else if (event instanceof BlockPistonExtendEvent extendEvent) { + var block = extendEvent.getBlock(); + String direction = extendEvent.getDirection() != null ? extendEvent.getDirection().name() : ""; + var eventBuilderLocal = patchbukkit.events.BlockPistonExtendEvent.newBuilder() + .setBlockKey(block.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(block.getLocation())) + .setDirection(direction) + .setLength(resolvePistonLength(extendEvent)); + for (Block b : extendEvent.getBlocks()) { + eventBuilderLocal.addBlocks( + patchbukkit.events.BlockPistonBlockEntry.newBuilder() + .setBlockKey(b.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(b.getLocation())) + .build() + ); + } + eventBuilder.setBlockPistonExtend(eventBuilderLocal.build()); + } else if (event instanceof BlockPistonRetractEvent retractEvent) { + var block = retractEvent.getBlock(); + String direction = retractEvent.getDirection() != null ? retractEvent.getDirection().name() : ""; + var eventBuilderLocal = patchbukkit.events.BlockPistonRetractEvent.newBuilder() + .setBlockKey(block.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(block.getLocation())) + .setDirection(direction) + .setLength(resolvePistonLength(retractEvent)); + for (Block b : retractEvent.getBlocks()) { + eventBuilderLocal.addBlocks( + patchbukkit.events.BlockPistonBlockEntry.newBuilder() + .setBlockKey(b.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(b.getLocation())) + .build() + ); + } + eventBuilder.setBlockPistonRetract(eventBuilderLocal.build()); + } else if (event instanceof BlockRedstoneEvent redstoneEvent) { + var block = redstoneEvent.getBlock(); + eventBuilder.setBlockRedstone( + patchbukkit.events.BlockRedstoneEvent.newBuilder() + .setBlockKey(block.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(block.getLocation())) + .setOldCurrent(redstoneEvent.getOldCurrent()) + .setNewCurrent(redstoneEvent.getNewCurrent()) + .build() + ); + } else if (event instanceof BlockMultiPlaceEvent multiEvent) { + var block = multiEvent.getBlock(); + var eventBuilderLocal = patchbukkit.events.BlockMultiPlaceEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(multiEvent.getPlayer().getUniqueId().toString()) + .build()) + .setBlockKey(block.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(block.getLocation())); + for (org.bukkit.block.BlockState state : resolveMultiPlaceStates(multiEvent)) { + eventBuilderLocal.addBlocks( + patchbukkit.events.BlockMultiPlaceBlockEntry.newBuilder() + .setBlockKey(state.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(state.getLocation())) + .build() + ); + } + eventBuilder.setBlockMultiPlace(eventBuilderLocal.build()); + } else if (event instanceof BlockPhysicsEvent physicsEvent) { + var block = physicsEvent.getBlock(); + Material sourceMaterial = resolvePhysicsChangedMaterial(physicsEvent); + eventBuilder.setBlockPhysics( + patchbukkit.events.BlockPhysicsEvent.newBuilder() + .setBlockKey(block.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(block.getLocation())) + .setSourceBlockKey(sourceMaterial.getKey().toString()) + .setSourceLocation(BridgeUtils.convertLocation(block.getLocation())) + .build() + ); + } else if (event instanceof NotePlayEvent noteEvent) { + Block block = noteEvent.getBlock(); + eventBuilder.setNotePlay( + patchbukkit.events.NotePlayEvent.newBuilder() + .setBlockKey(block.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(block.getLocation())) + .setInstrument(noteEvent.getInstrument().name()) + .setNote(noteEvent.getNote().getId()) + .build() + ); + } else if (event instanceof SignChangeEvent signEvent) { + Block block = signEvent.getBlock(); + String[] lines = signEvent.getLines(); + var eventBuilderLocal = patchbukkit.events.SignChangeEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(signEvent.getPlayer().getUniqueId().toString()) + .build()) + .setBlockKey(block.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(block.getLocation())); + for (String line : lines) { + eventBuilderLocal.addLines(line != null ? line : ""); + } + try { + java.lang.reflect.Method method = signEvent.getClass().getMethod("isFrontText"); + Object value = method.invoke(signEvent); + if (value instanceof Boolean b) { + eventBuilderLocal.setIsFrontText(b); + } + } catch (ReflectiveOperationException ignored) { + eventBuilderLocal.setIsFrontText(true); + } + eventBuilder.setSignChange(eventBuilderLocal.build()); + } else if (event instanceof TNTPrimeEvent tntEvent) { + Block block = tntEvent.getBlock(); + var eventBuilderLocal = patchbukkit.events.TNTPrimeEvent.newBuilder() + .setBlockKey(block.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(block.getLocation())) + .setCause(tntEvent.getCause().name()); + if (tntEvent.getPrimingEntity() instanceof Player player) { + eventBuilderLocal.setPlayerUuid(UUID.newBuilder() + .setValue(player.getUniqueId().toString()) + .build()); + } + eventBuilder.setTntPrime(eventBuilderLocal.build()); + } else if (event instanceof MoistureChangeEvent moistureEvent) { + Block block = moistureEvent.getBlock(); + org.bukkit.block.BlockState newState = moistureEvent.getNewState(); + eventBuilder.setMoistureChange( + patchbukkit.events.MoistureChangeEvent.newBuilder() + .setBlockKey(block.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(block.getLocation())) + .setNewBlockKey(newState.getType().getKey().toString()) + .build() + ); + } else if (event instanceof SpongeAbsorbEvent spongeEvent) { + Block block = spongeEvent.getBlock(); + var eventBuilderLocal = patchbukkit.events.SpongeAbsorbEvent.newBuilder() + .setBlockKey(block.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(block.getLocation())); + for (org.bukkit.block.BlockState state : spongeEvent.getBlocks()) { + eventBuilderLocal.addBlocks( + patchbukkit.events.SpongeAbsorbBlockEntry.newBuilder() + .setBlockKey(state.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(state.getLocation())) + .build() + ); + } + eventBuilder.setSpongeAbsorb(eventBuilderLocal.build()); + } else if (event instanceof FluidLevelChangeEvent fluidEvent) { + Block block = fluidEvent.getBlock(); + String newBlockKey = resolveFluidNewBlockKey(fluidEvent); + eventBuilder.setFluidLevelChange( + patchbukkit.events.FluidLevelChangeEvent.newBuilder() + .setBlockKey(block.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(block.getLocation())) + .setNewBlockKey(newBlockKey) + .build() + ); + } else if (event instanceof SpawnChangeEvent spawnEvent) { + Location location = resolveSpawnChangeLocation(spawnEvent); + Location previous = resolveSpawnChangePreviousLocation(spawnEvent, location); + if (previous == null) { + previous = location; + } + eventBuilder.setSpawnChange( + patchbukkit.events.SpawnChangeEvent.newBuilder() + .setPreviousLocation(BridgeUtils.convertLocation(previous)) + .setLocation(BridgeUtils.convertLocation(location)) + .build() + ); + } else if (event instanceof BlockCanBuildEvent canBuildEvent) { + Block block = canBuildEvent.getBlock(); + boolean canBuild = canBuildEvent.isBuildable(); + java.util.UUID playerUuid = resolveEventPlayerUuid(canBuildEvent); + eventBuilder.setBlockCanBuild( + patchbukkit.events.BlockCanBuildEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(playerUuid.toString()) + .build()) + .setBlockKey(block.getType().getKey().toString()) + .setBlockAgainstKey(block.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(block.getLocation())) + .setCanBuild(canBuild) + .build() + ); + } else if (event instanceof BlockBurnEvent burnEvent) { + Block block = burnEvent.getBlock(); + Block ignitingBlock = resolveIgnitingBlock(burnEvent); + String ignitingKey = ignitingBlock != null + ? ignitingBlock.getType().getKey().toString() + : "minecraft:fire"; + eventBuilder.setBlockBurn( + patchbukkit.events.BlockBurnEvent.newBuilder() + .setBlockKey(block.getType().getKey().toString()) + .setIgnitingBlockKey(ignitingKey) + .setLocation(BridgeUtils.convertLocation(block.getLocation())) + .build() + ); + } else if (event instanceof BlockIgniteEvent igniteEvent) { + Block block = igniteEvent.getBlock(); + String cause = igniteEvent.getCause() != null ? igniteEvent.getCause().name() : ""; + Block ignitingBlock = resolveIgnitingBlock(igniteEvent); + String ignitingKey = ignitingBlock != null + ? ignitingBlock.getType().getKey().toString() + : "minecraft:fire"; + var igniteBuilder = patchbukkit.events.BlockIgniteEvent.newBuilder() + .setBlockKey(block.getType().getKey().toString()) + .setIgnitingBlockKey(ignitingKey) + .setLocation(BridgeUtils.convertLocation(block.getLocation())) + .setCause(cause); + if (igniteEvent.getPlayer() != null) { + igniteBuilder.setPlayerUuid( + UUID.newBuilder() + .setValue(igniteEvent.getPlayer().getUniqueId().toString()) + .build() + ); + } + eventBuilder.setBlockIgnite( + igniteBuilder.build() + ); + } else if (event instanceof BlockSpreadEvent spreadEvent) { + Block block = spreadEvent.getBlock(); + Block sourceBlock = spreadEvent.getSource(); + eventBuilder.setBlockSpread( + patchbukkit.events.BlockSpreadEvent.newBuilder() + .setSourceBlockKey(sourceBlock.getType().getKey().toString()) + .setSourceLocation(BridgeUtils.convertLocation(sourceBlock.getLocation())) + .setBlockKey(block.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(block.getLocation())) + .build() + ); + } else if (event instanceof BlockPlaceEvent placeEvent) { + var block = placeEvent.getBlockPlaced(); + var against = placeEvent.getBlockAgainst(); + eventBuilder.setBlockPlace( + patchbukkit.events.BlockPlaceEvent.newBuilder() + .setPlayerUuid(UUID.newBuilder() + .setValue(placeEvent.getPlayer().getUniqueId().toString()) + .build()) + .setBlockKey(block.getType().getKey().toString()) + .setBlockAgainstKey(against.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(block.getLocation())) + .setCanBuild(placeEvent.canBuild()) + .build() + ); + } else if (event instanceof EntitySpawnEvent spawnEvent) { + eventBuilder.setEntitySpawn( + patchbukkit.events.EntitySpawnEvent.newBuilder() + .setEntityUuid(UUID.newBuilder() + .setValue(spawnEvent.getEntity().getUniqueId().toString()) + .build()) + .setEntityType(spawnEvent.getEntity().getType().name()) + .build() + ); + } else if (event instanceof EntityDamageEvent damageEvent) { + eventBuilder.setEntityDamage( + patchbukkit.events.EntityDamageEvent.newBuilder() + .setEntityUuid(UUID.newBuilder() + .setValue(damageEvent.getEntity().getUniqueId().toString()) + .build()) + .setDamage((float) damageEvent.getFinalDamage()) + .setDamageType(damageEvent.getCause().name()) + .build() + ); + } else if (event instanceof EntityDeathEvent deathEvent) { + eventBuilder.setEntityDeath( + patchbukkit.events.EntityDeathEvent.newBuilder() + .setEntityUuid(UUID.newBuilder() + .setValue(deathEvent.getEntity().getUniqueId().toString()) + .build()) + .setDamageType("CUSTOM") + .build() + ); + } else if (event instanceof ServerCommandEvent commandEvent) { + eventBuilder.setServerCommand( + patchbukkit.events.ServerCommandEvent.newBuilder() + .setCommand(commandEvent.getCommand()) + .build() + ); + } else if (event instanceof BroadcastMessageEvent broadcastEvent) { + String messageJson = GsonComponentSerializer.gson().serialize( + net.kyori.adventure.text.Component.text(broadcastEvent.getMessage()) + ); + String senderJson = GsonComponentSerializer.gson().serialize( + net.kyori.adventure.text.Component.text("") + ); + eventBuilder.setServerBroadcast( + patchbukkit.events.ServerBroadcastEvent.newBuilder() + .setMessage(messageJson) + .setSender(senderJson) + .build() + ); + } else if (event instanceof ServerListPingEvent pingEvent) { + String motd = pingEvent.getMotd(); + int maxPlayers = pingEvent.getMaxPlayers(); + int onlinePlayers = 0; + try { + java.lang.reflect.Method method = pingEvent.getClass().getMethod("getNumPlayers"); + Object value = method.invoke(pingEvent); + if (value instanceof Integer count) { + onlinePlayers = count; + } + } catch (ReflectiveOperationException ignored) { + onlinePlayers = Bukkit.getOnlinePlayers().size(); + } + String favicon = ""; + try { + java.lang.reflect.Method method = pingEvent.getClass().getMethod("getServerIcon"); + Object icon = method.invoke(pingEvent); + if (icon != null) { + java.lang.reflect.Method dataMethod = icon.getClass().getMethod("getData"); + Object data = dataMethod.invoke(icon); + if (data instanceof String dataString) { + favicon = dataString; + } + } + } catch (ReflectiveOperationException ignored) { + // ignore + } + + eventBuilder.setServerListPing( + patchbukkit.events.ServerListPingEvent.newBuilder() + .setMotd(motd) + .setMaxPlayers(maxPlayers) + .setOnlinePlayers(onlinePlayers) + .setFavicon(favicon) + .build() + ); + } else if (event instanceof PluginEnableEvent enableEvent) { + eventBuilder.setPluginEnable( + patchbukkit.events.PluginEnableEvent.newBuilder() + .setPluginName(enableEvent.getPlugin().getName()) + .build() + ); + } else if (event instanceof PluginDisableEvent disableEvent) { + eventBuilder.setPluginDisable( + patchbukkit.events.PluginDisableEvent.newBuilder() + .setPluginName(disableEvent.getPlugin().getName()) + .build() + ); + } else if (event instanceof ServiceRegisterEvent registerEvent) { + RegisteredServiceProvider provider = registerEvent.getProvider(); + String pluginName = provider != null && provider.getPlugin() != null + ? provider.getPlugin().getName() + : ""; + String serviceName = provider != null && provider.getService() != null + ? provider.getService().getName() + : ""; + eventBuilder.setServiceRegister( + patchbukkit.events.ServiceRegisterEvent.newBuilder() + .setPluginName(pluginName) + .setServiceName(serviceName) + .build() + ); + } else if (event instanceof ServiceUnregisterEvent unregisterEvent) { + RegisteredServiceProvider provider = unregisterEvent.getProvider(); + String pluginName = provider != null && provider.getPlugin() != null + ? provider.getPlugin().getName() + : ""; + String serviceName = provider != null && provider.getService() != null + ? provider.getService().getName() + : ""; + eventBuilder.setServiceUnregister( + patchbukkit.events.ServiceUnregisterEvent.newBuilder() + .setPluginName(pluginName) + .setServiceName(serviceName) + .build() + ); + } + + builder.setData(eventBuilder.build()); + + return builder.build().toByteArray(); + } + + public static boolean isCancellable(@NotNull org.bukkit.event.Event event) { + return event instanceof org.bukkit.event.Cancellable; + } + + @Nullable + private static Player getPlayer(@NotNull String uuidStr) { + try { + java.util.UUID uuid = java.util.UUID.fromString(uuidStr); + Player player = Bukkit.getServer().getPlayer(uuid); + if (player == null) { + LOGGER.warning("EventFactory: Player not found for UUID " + uuidStr); + } + return player; + } catch (IllegalArgumentException e) { + LOGGER.severe("EventFactory: Invalid UUID string: " + uuidStr); + return null; + } + } + + @NotNull + static java.util.UUID resolveResourcePackId(@NotNull PlayerResourcePackStatusEvent event) { + java.util.UUID packUuid = null; + try { + java.lang.reflect.Method method = event.getClass().getMethod("getPackId"); + Object value = method.invoke(event); + if (value instanceof java.util.UUID uuid) { + packUuid = uuid; + } + } catch (ReflectiveOperationException ignored) { + // Fallback to alternative method names + } + if (packUuid == null) { + try { + java.lang.reflect.Method method = event.getClass().getMethod("getID"); + Object value = method.invoke(event); + if (value instanceof java.util.UUID uuid) { + packUuid = uuid; + } + } catch (ReflectiveOperationException ignored) { + // ignore + } + } + if (packUuid == null) { + try { + java.lang.reflect.Method method = event.getClass().getMethod("getId"); + Object value = method.invoke(event); + if (value instanceof java.util.UUID uuid) { + packUuid = uuid; + } + } catch (ReflectiveOperationException ignored) { + // ignore + } + } + return packUuid != null ? packUuid : java.util.UUID.randomUUID(); + } + + @Nullable + private static Block resolveHarvestedBlock(@NotNull PlayerHarvestBlockEvent event) { + try { + java.lang.reflect.Method method = event.getClass().getMethod("getHarvestedBlock"); + Object value = method.invoke(event); + if (value instanceof Block block) { + return block; + } + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + java.lang.reflect.Method method = event.getClass().getMethod("getBlock"); + Object value = method.invoke(event); + if (value instanceof Block block) { + return block; + } + } catch (ReflectiveOperationException ignored) { + // ignore + } + return null; + } + + @Nullable + private static ItemStack resolveHarvestTool(@NotNull PlayerHarvestBlockEvent event) { + try { + java.lang.reflect.Method method = event.getClass().getMethod("getHarvestingTool"); + Object value = method.invoke(event); + if (value instanceof ItemStack stack) { + return stack; + } + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + java.lang.reflect.Method method = event.getClass().getMethod("getTool"); + Object value = method.invoke(event); + if (value instanceof ItemStack stack) { + return stack; + } + } catch (ReflectiveOperationException ignored) { + // ignore + } + return null; + } + + @NotNull + static java.util.UUID resolveEventPlayerUuid(@NotNull org.bukkit.event.Event event) { + try { + java.lang.reflect.Method method = event.getClass().getMethod("getPlayer"); + Object value = method.invoke(event); + if (value instanceof Player player) { + return player.getUniqueId(); + } + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + java.lang.reflect.Method method = event.getClass().getMethod("getPlayerUUID"); + Object value = method.invoke(event); + if (value instanceof java.util.UUID uuid) { + return uuid; + } + } catch (ReflectiveOperationException ignored) { + // ignore + } + return java.util.UUID.randomUUID(); + } + + @Nullable + static Block resolveIgnitingBlock(@NotNull BlockBurnEvent event) { + try { + java.lang.reflect.Method method = event.getClass().getMethod("getIgnitingBlock"); + Object value = method.invoke(event); + if (value instanceof Block block) { + return block; + } + } catch (ReflectiveOperationException ignored) { + // ignore + } + return null; + } + + @Nullable + static Block resolveIgnitingBlock(@NotNull BlockIgniteEvent event) { + try { + java.lang.reflect.Method method = event.getClass().getMethod("getIgnitingBlock"); + Object value = method.invoke(event); + if (value instanceof Block block) { + return block; + } + } catch (ReflectiveOperationException ignored) { + // ignore + } + return null; + } + + private static boolean resolveInstaBreak(@NotNull BlockDamageEvent event) { + try { + java.lang.reflect.Method method = event.getClass().getMethod("isInstaBreak"); + Object value = method.invoke(event); + if (value instanceof Boolean bool) { + return bool; + } + } catch (ReflectiveOperationException ignored) { + // ignore + } + return false; + } + + private static int resolvePistonLength(@NotNull Object pistonEvent) { + try { + java.lang.reflect.Method method = pistonEvent.getClass().getMethod("getLength"); + Object value = method.invoke(pistonEvent); + if (value instanceof Integer len) { + return len; + } + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + java.lang.reflect.Method method = pistonEvent.getClass().getMethod("getBlocks"); + Object value = method.invoke(pistonEvent); + if (value instanceof java.util.List list) { + return list.size(); + } + } catch (ReflectiveOperationException ignored) { + // ignore + } + return 0; + } + + @NotNull + private static java.util.List resolveMultiPlaceStates(@NotNull BlockMultiPlaceEvent event) { + try { + java.lang.reflect.Method method = event.getClass().getMethod("getBlockStates"); + Object value = method.invoke(event); + if (value instanceof java.util.List list) { + java.util.List states = new java.util.ArrayList<>(); + for (Object entry : list) { + if (entry instanceof org.bukkit.block.BlockState state) { + states.add(state); + } + } + return states; + } + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + java.lang.reflect.Method method = event.getClass().getMethod("getReplacedBlockStates"); + Object value = method.invoke(event); + if (value instanceof java.util.List list) { + java.util.List states = new java.util.ArrayList<>(); + for (Object entry : list) { + if (entry instanceof org.bukkit.block.BlockState state) { + states.add(state); + } + } + return states; + } + } catch (ReflectiveOperationException ignored) { + // ignore + } + return java.util.Collections.emptyList(); + } + + @NotNull + private static Material resolvePhysicsChangedMaterial(@NotNull BlockPhysicsEvent event) { + try { + Object changed = event.getChangedType(); + if (changed instanceof Material material) { + return material; + } + if (changed != null) { + java.lang.reflect.Method method = changed.getClass().getMethod("getMaterial"); + Object value = method.invoke(changed); + if (value instanceof Material material) { + return material; + } + } + } catch (ReflectiveOperationException ignored) { + // ignore + } + return event.getBlock().getType(); + } + + @NotNull + private static String resolveFluidNewBlockKey(@NotNull FluidLevelChangeEvent event) { + try { + java.lang.reflect.Method method = event.getClass().getMethod("getNewState"); + Object value = method.invoke(event); + if (value instanceof org.bukkit.block.BlockState state) { + return state.getType().getKey().toString(); + } + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + java.lang.reflect.Method method = event.getClass().getMethod("getNewData"); + Object value = method.invoke(event); + if (value instanceof org.bukkit.block.data.BlockData data) { + return data.getMaterial().getKey().toString(); + } + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + java.lang.reflect.Method method = event.getClass().getMethod("getNewBlockData"); + Object value = method.invoke(event); + if (value instanceof org.bukkit.block.data.BlockData data) { + return data.getMaterial().getKey().toString(); + } + } catch (ReflectiveOperationException ignored) { + // ignore + } + return event.getBlock().getType().getKey().toString(); + } + + @Nullable + private static Location resolveSpawnChangeLocation(@NotNull SpawnChangeEvent event) { + try { + java.lang.reflect.Method method = event.getClass().getMethod("getLocation"); + Object value = method.invoke(event); + if (value instanceof Location location) { + return location; + } + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + java.lang.reflect.Method method = event.getClass().getMethod("getNewLocation"); + Object value = method.invoke(event); + if (value instanceof Location location) { + return location; + } + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + java.lang.reflect.Method method = event.getClass().getMethod("getNewSpawn"); + Object value = method.invoke(event); + if (value instanceof Location location) { + return location; + } + } catch (ReflectiveOperationException ignored) { + // ignore + } + return event.getWorld() != null ? event.getWorld().getSpawnLocation() : null; + } + + @Nullable + private static Location resolveSpawnChangePreviousLocation( + @NotNull SpawnChangeEvent event, + @Nullable Location fallback + ) { + try { + java.lang.reflect.Method method = event.getClass().getMethod("getPreviousLocation"); + Object value = method.invoke(event); + if (value instanceof Location location) { + return location; + } + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + java.lang.reflect.Method method = event.getClass().getMethod("getPreviousSpawn"); + Object value = method.invoke(event); + if (value instanceof Location location) { + return location; + } + } catch (ReflectiveOperationException ignored) { + // ignore + } + return fallback; + } + + @Nullable + private static org.bukkit.event.Event createHarvestBlockEvent( + @NotNull Player player, + @NotNull Block block, + @NotNull ItemStack tool + ) { + try { + java.lang.reflect.Constructor ctor = PlayerHarvestBlockEvent.class.getConstructor( + Player.class, + Block.class, + ItemStack.class, + java.util.List.class + ); + return (org.bukkit.event.Event) ctor.newInstance( + player, + block, + tool, + java.util.Collections.emptyList() + ); + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + java.lang.reflect.Constructor ctor = PlayerHarvestBlockEvent.class.getConstructor( + Player.class, + Block.class, + ItemStack.class + ); + return (org.bukkit.event.Event) ctor.newInstance(player, block, tool); + } catch (ReflectiveOperationException ignored) { + // ignore + } + return null; + } + + @Nullable + private static BlockCanBuildEvent createBlockCanBuildEvent( + @NotNull Block block, + @NotNull org.bukkit.block.data.BlockData data, + boolean canBuild + ) { + try { + java.lang.reflect.Constructor ctor = + BlockCanBuildEvent.class.getConstructor(Block.class, org.bukkit.block.data.BlockData.class, boolean.class); + return ctor.newInstance(block, data, canBuild); + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + java.lang.reflect.Constructor ctor = + BlockCanBuildEvent.class.getConstructor(Block.class, Material.class, boolean.class); + return ctor.newInstance(block, block.getType(), canBuild); + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + java.lang.reflect.Constructor ctor = + BlockCanBuildEvent.class.getConstructor(Block.class, boolean.class); + return ctor.newInstance(block, canBuild); + } catch (ReflectiveOperationException ignored) { + // ignore + } + return null; + } + + @Nullable + private static org.bukkit.event.Event createSignChangeEvent( + @NotNull Block block, + @NotNull Player player, + @NotNull String[] lines, + boolean isFrontText + ) { + try { + java.lang.reflect.Constructor ctor = + SignChangeEvent.class.getConstructor(Block.class, Player.class, String[].class); + return ctor.newInstance(block, player, lines); + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + java.lang.reflect.Constructor ctor = + SignChangeEvent.class.getConstructor(Block.class, Player.class, String[].class, boolean.class); + return ctor.newInstance(block, player, lines, isFrontText); + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + Class sideClass = Class.forName("org.bukkit.block.sign.SignSide"); + @SuppressWarnings("unchecked") + Object side = Enum.valueOf((Class) sideClass, isFrontText ? "FRONT" : "BACK"); + java.lang.reflect.Constructor ctor = + SignChangeEvent.class.getConstructor(Block.class, Player.class, String[].class, sideClass); + return (org.bukkit.event.Event) ctor.newInstance(block, player, lines, side); + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + Class sideClass = Class.forName("org.bukkit.block.sign.Side"); + @SuppressWarnings("unchecked") + Object side = Enum.valueOf((Class) sideClass, isFrontText ? "FRONT" : "BACK"); + java.lang.reflect.Constructor ctor = + SignChangeEvent.class.getConstructor(Block.class, Player.class, String[].class, sideClass); + return (org.bukkit.event.Event) ctor.newInstance(block, player, lines, side); + } catch (ReflectiveOperationException ignored) { + // ignore + } + return null; + } + + @Nullable + private static org.bukkit.event.Event createTntPrimeEvent( + @NotNull Block block, + @NotNull TNTPrimeEvent.PrimeCause cause, + @Nullable Entity primer + ) { + try { + java.lang.reflect.Constructor ctor = + TNTPrimeEvent.class.getConstructor(Block.class, TNTPrimeEvent.PrimeCause.class, Entity.class); + return ctor.newInstance(block, cause, primer); + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + java.lang.reflect.Constructor ctor = + TNTPrimeEvent.class.getConstructor(Block.class, TNTPrimeEvent.PrimeCause.class); + return ctor.newInstance(block, cause); + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + java.lang.reflect.Constructor ctor = + TNTPrimeEvent.class.getConstructor(Block.class, TNTPrimeEvent.PrimeCause.class, Entity.class, Block.class); + return ctor.newInstance(block, cause, primer, block); + } catch (ReflectiveOperationException ignored) { + // ignore + } + return null; + } + + @Nullable + private static org.bukkit.event.Event createSpawnChangeEvent( + @NotNull org.bukkit.World world, + @NotNull Location location, + @NotNull Location previous + ) { + try { + java.lang.reflect.Constructor ctor = + SpawnChangeEvent.class.getConstructor(org.bukkit.World.class, Location.class, Location.class); + return ctor.newInstance(world, location, previous); + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + java.lang.reflect.Constructor ctor = + SpawnChangeEvent.class.getConstructor(org.bukkit.World.class, Location.class); + return ctor.newInstance(world, location); + } catch (ReflectiveOperationException ignored) { + // ignore + } + return null; + } + + @Nullable + private static org.bukkit.event.Event createServerListPingEvent( + @NotNull String motd, + int maxPlayers, + int onlinePlayers, + @NotNull String favicon + ) { + java.net.InetAddress address = java.net.InetAddress.getLoopbackAddress(); + try { + java.lang.reflect.Constructor ctor = + ServerListPingEvent.class.getConstructor(java.net.InetAddress.class, String.class, int.class, int.class); + ServerListPingEvent event = ctor.newInstance(address, motd, maxPlayers, onlinePlayers); + return event; + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + java.lang.reflect.Constructor ctor = + ServerListPingEvent.class.getConstructor(java.net.InetAddress.class, String.class, int.class); + ServerListPingEvent event = ctor.newInstance(address, motd, maxPlayers); + try { + java.lang.reflect.Method method = event.getClass().getMethod("setNumPlayers", int.class); + method.invoke(event, onlinePlayers); + } catch (ReflectiveOperationException ignored) { + // ignore + } + return event; + } catch (ReflectiveOperationException ignored) { + // ignore + } + return null; + } + + @Nullable + private static org.bukkit.event.Event createPlayerBedEnterEvent( + @NotNull Player player, + @NotNull Block bed + ) { + try { + java.lang.reflect.Constructor ctor = + PlayerBedEnterEvent.class.getConstructor(Player.class, Block.class); + return ctor.newInstance(player, bed); + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + Class resultClass = Class.forName("org.bukkit.event.player.PlayerBedEnterEvent$BedEnterResult"); + Class actionClass = Class.forName("org.bukkit.event.player.PlayerBedEnterEvent$BedEnterAction"); + Object result = firstEnumConstant(resultClass, "OK"); + Object action = firstEnumConstant(actionClass, "RIGHT_CLICK"); + java.lang.reflect.Constructor ctor = + PlayerBedEnterEvent.class.getConstructor(Player.class, Block.class, resultClass, actionClass); + return ctor.newInstance(player, bed, result, action); + } catch (ReflectiveOperationException ignored) { + // ignore + } + return null; + } + + @Nullable + private static org.bukkit.event.Event createPlayerBedLeaveEvent( + @NotNull Player player, + @NotNull Block bed + ) { + try { + java.lang.reflect.Constructor ctor = + PlayerBedLeaveEvent.class.getConstructor(Player.class, Block.class, boolean.class); + return ctor.newInstance(player, bed, false); + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + java.lang.reflect.Constructor ctor = + PlayerBedLeaveEvent.class.getConstructor(Player.class, Block.class); + return ctor.newInstance(player, bed); + } catch (ReflectiveOperationException ignored) { + // ignore + } + return null; + } + + @Nullable + private static org.bukkit.event.Event createPlayerRecipeDiscoverEvent( + @NotNull Player player, + @NotNull NamespacedKey key + ) { + try { + java.lang.reflect.Constructor ctor = + PlayerRecipeDiscoverEvent.class.getConstructor(Player.class, NamespacedKey.class, boolean.class); + return ctor.newInstance(player, key, false); + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + java.lang.reflect.Constructor ctor = + PlayerRecipeDiscoverEvent.class.getConstructor(Player.class, NamespacedKey.class); + return ctor.newInstance(player, key); + } catch (ReflectiveOperationException ignored) { + // ignore + } + return null; + } + + @Nullable + private static org.bukkit.event.Event createPlayerShearEntityEvent( + @NotNull Player player, + @NotNull Entity entity, + @NotNull ItemStack item, + @NotNull EquipmentSlot slot + ) { + try { + java.lang.reflect.Constructor ctor = + PlayerShearEntityEvent.class.getConstructor( + Player.class, + Entity.class, + ItemStack.class, + EquipmentSlot.class, + java.util.List.class + ); + return ctor.newInstance(player, entity, item, slot, java.util.Collections.emptyList()); + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + java.lang.reflect.Constructor ctor = + PlayerShearEntityEvent.class.getConstructor(Player.class, Entity.class, ItemStack.class, EquipmentSlot.class); + return ctor.newInstance(player, entity, item, slot); + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + java.lang.reflect.Constructor ctor = + PlayerShearEntityEvent.class.getConstructor(Player.class, Entity.class); + return ctor.newInstance(player, entity); + } catch (ReflectiveOperationException ignored) { + // ignore + } + return null; + } + + @Nullable + private static org.bukkit.event.Event createBlockDamageEvent( + @NotNull Block block, + @NotNull Player player, + @Nullable ItemStack item, + boolean instaBreak + ) { + try { + java.lang.reflect.Constructor ctor = + BlockDamageEvent.class.getConstructor(Player.class, Block.class, ItemStack.class, boolean.class); + return ctor.newInstance(player, block, item, instaBreak); + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + java.lang.reflect.Constructor ctor = + BlockDamageEvent.class.getConstructor(Block.class, Player.class, ItemStack.class, boolean.class); + return ctor.newInstance(block, player, item, instaBreak); + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + java.lang.reflect.Constructor ctor = + BlockDamageEvent.class.getConstructor(Block.class, Player.class, ItemStack.class); + return ctor.newInstance(block, player, item); + } catch (ReflectiveOperationException ignored) { + // ignore + } + return null; + } + + @Nullable + private static org.bukkit.event.Event createBlockDamageAbortEvent( + @NotNull Block block, + @NotNull Player player, + @Nullable ItemStack item + ) { + try { + java.lang.reflect.Constructor ctor = + BlockDamageAbortEvent.class.getConstructor(Player.class, Block.class, ItemStack.class); + return ctor.newInstance(player, block, item); + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + java.lang.reflect.Constructor ctor = + BlockDamageAbortEvent.class.getConstructor(Block.class, Player.class, ItemStack.class); + return ctor.newInstance(block, player, item); + } catch (ReflectiveOperationException ignored) { + // ignore + } + return null; + } + + @Nullable + private static org.bukkit.event.Event createBlockDropItemEvent( + @NotNull Block block, + @NotNull Player player, + @NotNull java.util.List items + ) { + try { + java.lang.reflect.Constructor ctor = + BlockDropItemEvent.class.getConstructor(Block.class, org.bukkit.block.BlockState.class, Player.class, java.util.List.class); + return ctor.newInstance(block, block.getState(), player, items); + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + java.lang.reflect.Constructor ctor = + BlockDropItemEvent.class.getConstructor(Block.class, Player.class, java.util.List.class); + return ctor.newInstance(block, player, items); + } catch (ReflectiveOperationException ignored) { + // ignore + } + return null; + } + + @Nullable + private static org.bukkit.event.Event createBlockExplodeEvent( + @NotNull Block block, + @NotNull java.util.List blocks, + float yield + ) { + try { + java.lang.reflect.Constructor ctor = + BlockExplodeEvent.class.getConstructor(Block.class, org.bukkit.block.BlockState.class, java.util.List.class, float.class); + return ctor.newInstance(block, block.getState(), blocks, yield); + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + Class resultClass = Class.forName("org.bukkit.event.block.BlockExplodeEvent$ExplosionResult"); + Object result = firstEnumConstant(resultClass, "DESTROY"); + java.lang.reflect.Constructor ctor = + BlockExplodeEvent.class.getConstructor(Block.class, org.bukkit.block.BlockState.class, java.util.List.class, float.class, resultClass); + return ctor.newInstance(block, block.getState(), blocks, yield, result); + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + java.lang.reflect.Constructor ctor = + BlockExplodeEvent.class.getConstructor(Block.class, java.util.List.class, float.class); + return ctor.newInstance(block, blocks, yield); + } catch (ReflectiveOperationException ignored) { + // ignore + } + return null; + } + + @Nullable + private static org.bukkit.event.Event createBlockPistonExtendEvent( + @NotNull Block block, + @NotNull java.util.List blocks, + int length, + @NotNull BlockFace face + ) { + try { + java.lang.reflect.Constructor ctor = + BlockPistonExtendEvent.class.getConstructor(Block.class, java.util.List.class, BlockFace.class); + return ctor.newInstance(block, blocks, face); + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + java.lang.reflect.Constructor ctor = + BlockPistonExtendEvent.class.getConstructor(Block.class, int.class, BlockFace.class); + return ctor.newInstance(block, length, face); + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + java.lang.reflect.Constructor ctor = + BlockPistonExtendEvent.class.getConstructor(Block.class, java.util.List.class, int.class); + return ctor.newInstance(block, blocks, length); + } catch (ReflectiveOperationException ignored) { + // ignore + } + return null; + } + + @Nullable + private static org.bukkit.event.Event createBlockPistonRetractEvent( + @NotNull Block block, + @NotNull java.util.List blocks, + int length, + @NotNull BlockFace face + ) { + try { + java.lang.reflect.Constructor ctor = + BlockPistonRetractEvent.class.getConstructor(Block.class, java.util.List.class, BlockFace.class); + return ctor.newInstance(block, blocks, face); + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + java.lang.reflect.Constructor ctor = + BlockPistonRetractEvent.class.getConstructor(Block.class, int.class, BlockFace.class); + return ctor.newInstance(block, length, face); + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + java.lang.reflect.Constructor ctor = + BlockPistonRetractEvent.class.getConstructor(Block.class, java.util.List.class, int.class); + return ctor.newInstance(block, blocks, length); + } catch (ReflectiveOperationException ignored) { + // ignore + } + return null; + } + + @Nullable + private static org.bukkit.event.Event createFluidLevelChangeEvent( + @NotNull Block block, + @NotNull Block newBlock + ) { + try { + java.lang.reflect.Constructor ctor = + FluidLevelChangeEvent.class.getConstructor(Block.class, org.bukkit.block.data.BlockData.class); + return ctor.newInstance(block, newBlock.getBlockData()); + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + java.lang.reflect.Constructor ctor = + FluidLevelChangeEvent.class.getConstructor(Block.class, org.bukkit.block.BlockState.class); + return ctor.newInstance(block, newBlock.getState()); + } catch (ReflectiveOperationException ignored) { + // ignore + } + return null; + } + + @Nullable + private static org.bukkit.event.Event createEntityDeathEvent(@NotNull org.bukkit.entity.LivingEntity entity) { + java.util.List drops = java.util.Collections.emptyList(); + int exp = 0; + try { + java.lang.reflect.Constructor ctor = + EntityDeathEvent.class.getConstructor(org.bukkit.entity.LivingEntity.class, java.util.List.class, int.class); + return ctor.newInstance(entity, drops, exp); + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + Class damageSourceClass = Class.forName("org.bukkit.damage.DamageSource"); + java.lang.reflect.Constructor ctor = + EntityDeathEvent.class.getConstructor( + org.bukkit.entity.LivingEntity.class, + java.util.List.class, + int.class, + damageSourceClass + ); + return ctor.newInstance(entity, drops, exp, null); + } catch (ReflectiveOperationException ignored) { + // ignore + } + return null; + } + + @Nullable + private static org.bukkit.event.Event createBroadcastMessageEvent( + @NotNull Component messageComponent, + @NotNull String message + ) { + java.util.Set recipients = java.util.Collections.emptySet(); + try { + java.lang.reflect.Constructor ctor = + BroadcastMessageEvent.class.getConstructor(String.class, java.util.Set.class); + return ctor.newInstance(message, recipients); + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + java.lang.reflect.Constructor ctor = + BroadcastMessageEvent.class.getConstructor(boolean.class, String.class, java.util.Set.class); + return ctor.newInstance(false, message, recipients); + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + java.lang.reflect.Constructor ctor = + BroadcastMessageEvent.class.getConstructor(Component.class, java.util.Set.class); + return ctor.newInstance(messageComponent, recipients); + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + java.lang.reflect.Constructor ctor = + BroadcastMessageEvent.class.getConstructor(boolean.class, Component.class, java.util.Set.class); + return ctor.newInstance(false, messageComponent, recipients); + } catch (ReflectiveOperationException ignored) { + // ignore + } + return null; + } + + @Nullable + private static Object firstEnumConstant(@NotNull Class enumClass, @Nullable String preferredName) { + if (!enumClass.isEnum()) { + return null; + } + Object[] values = enumClass.getEnumConstants(); + if (values == null || values.length == 0) { + return null; + } + if (preferredName != null) { + try { + @SuppressWarnings("unchecked") + Object value = Enum.valueOf((Class) enumClass, preferredName); + return value; + } catch (IllegalArgumentException ignored) { + // ignore + } + } + return values[0]; + } + + @Nullable + private static RegisteredServiceProvider resolveServiceProvider( + @NotNull String serviceName, + @NotNull String pluginName + ) { + Class serviceClass = resolveServiceClass(serviceName); + if (serviceClass == null) { + return null; + } + + RegisteredServiceProvider provider = Bukkit.getServicesManager().getRegistration(serviceClass); + if (provider != null) { + if (pluginName.isEmpty() || provider.getPlugin().getName().equals(pluginName)) { + return provider; + } + } + + org.bukkit.plugin.Plugin plugin = Bukkit.getPluginManager().getPlugin(pluginName); + if (plugin == null) { + return provider; + } + return createFallbackServiceProvider(serviceClass, plugin); + } + + @Nullable + private static Class resolveServiceClass(@NotNull String serviceName) { + if (serviceName.isEmpty()) { + return null; + } + try { + return Class.forName(serviceName); + } catch (ClassNotFoundException ignored) { + // ignore + } + for (Class known : Bukkit.getServicesManager().getKnownServices()) { + if (known.getName().equals(serviceName) || known.getSimpleName().equals(serviceName)) { + return known; + } + } + return null; + } + + @Nullable + private static RegisteredServiceProvider createFallbackServiceProvider( + @NotNull Class serviceClass, + @NotNull org.bukkit.plugin.Plugin plugin + ) { + Object instance = createServiceInstance(serviceClass); + if (instance == null) { + return null; + } + try { + java.lang.reflect.Constructor[] ctors = RegisteredServiceProvider.class.getConstructors(); + for (java.lang.reflect.Constructor ctor : ctors) { + Class[] params = ctor.getParameterTypes(); + if (params.length != 4) { + continue; + } + if (!Class.class.isAssignableFrom(params[0])) { + continue; + } + if (!ServicePriority.class.isAssignableFrom(params[2])) { + continue; + } + if (!org.bukkit.plugin.Plugin.class.isAssignableFrom(params[3])) { + continue; + } + try { + ServicePriority priority = ServicePriority.values().length > 0 + ? ServicePriority.values()[0] + : null; + Object created = ctor.newInstance(serviceClass, instance, priority, plugin); + if (created instanceof RegisteredServiceProvider provider) { + return provider; + } + } catch (ReflectiveOperationException ignored) { + // try next constructor + } + } + } catch (SecurityException ignored) { + // ignore + } + return null; + } + + @Nullable + private static Object createServiceInstance(@NotNull Class serviceClass) { + if (serviceClass.isInterface()) { + return java.lang.reflect.Proxy.newProxyInstance( + serviceClass.getClassLoader(), + new Class[] { serviceClass }, + (proxy, method, args) -> defaultValue(method.getReturnType()) + ); + } + try { + return serviceClass.getDeclaredConstructor().newInstance(); + } catch (ReflectiveOperationException ignored) { + return null; + } + } + + @Nullable + private static Object defaultValue(@NotNull Class returnType) { + if (!returnType.isPrimitive()) { + return null; + } + if (returnType == boolean.class) { + return false; + } + if (returnType == byte.class) { + return (byte) 0; + } + if (returnType == short.class) { + return (short) 0; + } + if (returnType == int.class) { + return 0; + } + if (returnType == long.class) { + return 0L; + } + if (returnType == float.class) { + return 0.0f; + } + if (returnType == double.class) { + return 0.0d; + } + if (returnType == char.class) { + return '\0'; + } + return null; + } + + private static void setIgnitingBlock(@NotNull BlockBurnEvent event, @Nullable String ignitingKey) { + if (ignitingKey == null || ignitingKey.isEmpty()) { + return; + } + Material material = Material.matchMaterial(ignitingKey); + if (material == null) { + material = Material.matchMaterial("minecraft:" + ignitingKey); + } + if (material == null) { + return; + } + try { + java.lang.reflect.Method method = event.getClass().getMethod("setIgnitingBlock", Block.class); + Block block = event.getBlock().getWorld().getBlockAt(event.getBlock().getLocation()); + if (block != null) { + method.invoke(event, block); + } + } catch (ReflectiveOperationException ignored) { + // ignore + } + } + + @Nullable + private static PlayerLoginEvent createLoginEvent(@NotNull Player player) { + InetAddress playerAddress = resolvePlayerAddress(player); + try { + return PlayerLoginEvent.class.getConstructor(Player.class, String.class, InetAddress.class) + .newInstance(player, "", playerAddress); + } catch (ReflectiveOperationException ignored) { + try { + return PlayerLoginEvent.class.getConstructor(Player.class, String.class, java.net.InetAddress.class, + PlayerLoginEvent.Result.class, String.class) + .newInstance(player, "", playerAddress, PlayerLoginEvent.Result.ALLOWED, ""); + } catch (ReflectiveOperationException ignoredAgain) { + return null; + } + } + } + + @Nullable + private static InetAddress resolvePlayerAddress(@NotNull Player player) { + try { + InetSocketAddress address = player.getAddress(); + return address != null ? address.getAddress() : null; + } catch (UnsupportedOperationException ignored) { + return null; + } + } + + private static void applyLoginResult(@NotNull PlayerLoginEvent event, @NotNull String kickMessage) { + if (kickMessage.isEmpty()) { + try { + event.allow(); + } catch (UnsupportedOperationException ignored) { + // Older API variants may not expose allow/disallow in the same way. + } + return; + } + + try { + event.disallow(PlayerLoginEvent.Result.KICK_OTHER, kickMessage); + } catch (UnsupportedOperationException ignored) { + event.setKickMessage(kickMessage); + } + } + + @NotNull + private static ItemStack materialToItem(@NotNull String key, int amount) { + ItemStack stack = materialToItem(key); + stack.setAmount(Math.max(amount, 0)); + return stack; + } + + @NotNull + private static ItemStack materialToItem(@NotNull String key) { + if (key.isEmpty()) { + return new ItemStack(Material.AIR); + } + Material material = Material.matchMaterial(key); + if (material == null) { + material = Material.matchMaterial("minecraft:" + key); + } + if (material == null) { + material = Material.AIR; } + return new ItemStack(material); } } diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/events/PatchBukkitEventManager.java b/java/patchbukkit/src/main/java/org/patchbukkit/events/PatchBukkitEventManager.java index ba94bfa..2edda3a 100644 --- a/java/patchbukkit/src/main/java/org/patchbukkit/events/PatchBukkitEventManager.java +++ b/java/patchbukkit/src/main/java/org/patchbukkit/events/PatchBukkitEventManager.java @@ -1,6 +1,8 @@ package org.patchbukkit.events; import org.bukkit.Server; +import org.bukkit.Bukkit; +import org.bukkit.Material; import org.bukkit.Warning; import org.bukkit.event.Event; import org.bukkit.event.EventHandler; @@ -12,16 +14,68 @@ import org.bukkit.plugin.IllegalPluginAccessException; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.RegisteredListener; +import org.bukkit.inventory.ItemStack; +import org.bukkit.util.Vector; +import org.bukkit.block.Block; +import org.bukkit.Location; import org.patchbukkit.bridge.BridgeUtils; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import com.google.common.collect.Sets; import co.aikar.timings.TimedEventExecutor; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import patchbukkit.bridge.NativeBridgeFfi; import patchbukkit.events.CallEventRequest; +import patchbukkit.events.PlayerChatEvent; +import patchbukkit.events.PlayerCommandEvent; import patchbukkit.events.PlayerJoinEvent; +import patchbukkit.events.PlayerLeaveEvent; +import patchbukkit.events.PlayerMoveEvent; +import patchbukkit.events.ServerBroadcastEvent; +import patchbukkit.events.ServerCommandEvent; +import patchbukkit.events.BlockBreakEvent; +import patchbukkit.events.BlockDamageAbortEvent; +import patchbukkit.events.BlockDamageEvent; +import patchbukkit.events.BlockDispenseEvent; +import patchbukkit.events.BlockDropItemEntry; +import patchbukkit.events.BlockDropItemEvent; +import patchbukkit.events.BlockExplodeBlockEntry; +import patchbukkit.events.BlockExplodeEvent; +import patchbukkit.events.BlockFadeEvent; +import patchbukkit.events.BlockFertilizeBlockEntry; +import patchbukkit.events.BlockFertilizeEvent; +import patchbukkit.events.BlockFormEvent; +import patchbukkit.events.BlockFromToEvent; +import patchbukkit.events.BlockGrowEvent; +import patchbukkit.events.BlockPistonBlockEntry; +import patchbukkit.events.BlockPistonExtendEvent; +import patchbukkit.events.BlockPistonRetractEvent; +import patchbukkit.events.BlockRedstoneEvent; +import patchbukkit.events.BlockMultiPlaceBlockEntry; +import patchbukkit.events.BlockMultiPlaceEvent; +import patchbukkit.events.BlockPhysicsEvent; +import patchbukkit.events.BlockPlaceEvent; +import patchbukkit.events.NotePlayEvent; +import patchbukkit.events.SignChangeEvent; +import patchbukkit.events.TNTPrimeEvent; +import patchbukkit.events.MoistureChangeEvent; +import patchbukkit.events.SpongeAbsorbEvent; +import patchbukkit.events.SpongeAbsorbBlockEntry; +import patchbukkit.events.FluidLevelChangeEvent; +import patchbukkit.events.SpawnChangeEvent; +import patchbukkit.events.ServerListPingEvent; +import patchbukkit.events.PluginEnableEvent; +import patchbukkit.events.PluginDisableEvent; +import patchbukkit.events.ServiceRegisterEvent; +import patchbukkit.events.ServiceUnregisterEvent; +import patchbukkit.events.PlayerInteractEvent; +import patchbukkit.events.EntitySpawnEvent; +import patchbukkit.events.EntityDamageEvent; +import patchbukkit.events.EntityDeathEvent; import patchbukkit.events.RegisterEventRequest; import java.lang.reflect.Method; @@ -59,6 +113,1539 @@ public void callEvent(@NotNull Event event) throws IllegalStateException { ).build() ); break; + case "org.bukkit.event.player.PlayerLoginEvent": + var loginEvent = (org.bukkit.event.player.PlayerLoginEvent) event; + String loginKick = loginEvent.getKickMessage(); + if (loginKick == null) { + loginKick = ""; + } + request.setEvent( + patchbukkit.events.Event.newBuilder().setPlayerLogin( + patchbukkit.events.PlayerLoginEvent.newBuilder() + .setKickMessage(loginKick) + .setPlayerUuid(BridgeUtils.convertUuid(loginEvent.getPlayer().getUniqueId())) + .build() + ).build() + ); + break; + case "org.bukkit.event.player.AsyncPlayerPreLoginEvent": + var preLoginEvent = (org.bukkit.event.player.AsyncPlayerPreLoginEvent) event; + String address = preLoginEvent.getAddress() != null + ? preLoginEvent.getAddress().getHostAddress() + : ""; + String result = preLoginEvent.getLoginResult() != null + ? preLoginEvent.getLoginResult().name() + : "ALLOWED"; + String kickMessage = preLoginEvent.getKickMessage() != null + ? preLoginEvent.getKickMessage() + : ""; + request.setEvent( + patchbukkit.events.Event.newBuilder().setAsyncPlayerPreLogin( + patchbukkit.events.AsyncPlayerPreLoginEvent.newBuilder() + .setName(preLoginEvent.getName()) + .setPlayerUuid(BridgeUtils.convertUuid(preLoginEvent.getUniqueId())) + .setAddress(address) + .setResult(result) + .setKickMessage(kickMessage) + .build() + ).build() + ); + break; + case "org.bukkit.event.player.PlayerQuitEvent": + var quitEvent = (org.bukkit.event.player.PlayerQuitEvent) event; + request.setEvent( + patchbukkit.events.Event.newBuilder().setPlayerLeave( + PlayerLeaveEvent.newBuilder() + .setLeaveMessage(quitEvent.quitMessage().toString()) + .setPlayerUuid(BridgeUtils.convertUuid(quitEvent.getPlayer().getUniqueId())).build() + ).build() + ); + break; + case "org.bukkit.event.player.PlayerMoveEvent": + var moveEvent = (org.bukkit.event.player.PlayerMoveEvent) event; + request.setEvent( + patchbukkit.events.Event.newBuilder().setPlayerMove( + PlayerMoveEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(moveEvent.getPlayer().getUniqueId())) + .setFrom(BridgeUtils.convertLocation(moveEvent.getFrom())) + .setTo(BridgeUtils.convertLocation(moveEvent.getTo())) + .build() + ).build() + ); + break; + case "org.bukkit.event.player.PlayerTeleportEvent": + var teleportEvent = (org.bukkit.event.player.PlayerTeleportEvent) event; + String cause = teleportEvent.getCause() != null ? teleportEvent.getCause().name() : ""; + request.setEvent( + patchbukkit.events.Event.newBuilder().setPlayerTeleport( + patchbukkit.events.PlayerTeleportEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(teleportEvent.getPlayer().getUniqueId())) + .setFrom(BridgeUtils.convertLocation(teleportEvent.getFrom())) + .setTo(BridgeUtils.convertLocation(teleportEvent.getTo())) + .setCause(cause) + .build() + ).build() + ); + break; + case "org.bukkit.event.player.PlayerChangedWorldEvent": + var changeEvent = (org.bukkit.event.player.PlayerChangedWorldEvent) event; + var previousWorld = changeEvent.getFrom(); + var currentWorld = changeEvent.getPlayer().getWorld(); + var location = changeEvent.getPlayer().getLocation(); + request.setEvent( + patchbukkit.events.Event.newBuilder().setPlayerChangeWorld( + patchbukkit.events.PlayerChangeWorldEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(changeEvent.getPlayer().getUniqueId())) + .setPreviousWorld(BridgeUtils.convertWorld(previousWorld)) + .setNewWorld(BridgeUtils.convertWorld(currentWorld)) + .setPosition(BridgeUtils.convertLocation(location)) + .setYaw(location.getYaw()) + .setPitch(location.getPitch()) + .build() + ).build() + ); + break; + case "org.bukkit.event.player.PlayerGameModeChangeEvent": + var gameModeChangeEvent = (org.bukkit.event.player.PlayerGameModeChangeEvent) event; + var previousMode = gameModeChangeEvent.getPlayer().getGameMode(); + var nextMode = gameModeChangeEvent.getNewGameMode(); + request.setEvent( + patchbukkit.events.Event.newBuilder().setPlayerGamemodeChange( + patchbukkit.events.PlayerGamemodeChangeEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(gameModeChangeEvent.getPlayer().getUniqueId())) + .setPreviousGamemode(previousMode != null ? previousMode.name() : "") + .setNewGamemode(nextMode != null ? nextMode.name() : "") + .build() + ).build() + ); + break; + case "org.bukkit.event.player.PlayerAdvancementDoneEvent": + var advancementEvent = (org.bukkit.event.player.PlayerAdvancementDoneEvent) event; + request.setEvent( + patchbukkit.events.Event.newBuilder().setPlayerAdvancementDone( + patchbukkit.events.PlayerAdvancementDoneEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(advancementEvent.getPlayer().getUniqueId())) + .setAdvancementKey(advancementEvent.getAdvancement().getKey().toString()) + .build() + ).build() + ); + break; + case "org.bukkit.event.player.PlayerAnimationEvent": + var animationEvent = (org.bukkit.event.player.PlayerAnimationEvent) event; + request.setEvent( + patchbukkit.events.Event.newBuilder().setPlayerAnimation( + patchbukkit.events.PlayerAnimationEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(animationEvent.getPlayer().getUniqueId())) + .setAnimationType(animationEvent.getAnimationType().name()) + .build() + ).build() + ); + break; + case "org.bukkit.event.player.PlayerArmorStandManipulateEvent": + var armorEvent = (org.bukkit.event.player.PlayerArmorStandManipulateEvent) event; + String playerKey = armorEvent.getPlayerItem() != null + ? armorEvent.getPlayerItem().getType().getKey().toString() + : "minecraft:air"; + String standKey = armorEvent.getArmorStandItem() != null + ? armorEvent.getArmorStandItem().getType().getKey().toString() + : "minecraft:air"; + String slot = armorEvent.getSlot() != null ? armorEvent.getSlot().name() : "HAND"; + request.setEvent( + patchbukkit.events.Event.newBuilder().setPlayerArmorStandManipulate( + patchbukkit.events.PlayerArmorStandManipulateEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(armorEvent.getPlayer().getUniqueId())) + .setArmorStandUuid(BridgeUtils.convertUuid(armorEvent.getRightClicked().getUniqueId())) + .setItemKey(playerKey) + .setArmorStandItemKey(standKey) + .setSlot(slot) + .build() + ).build() + ); + break; + case "org.bukkit.event.player.PlayerBedEnterEvent": + var bedEnterEvent = (org.bukkit.event.player.PlayerBedEnterEvent) event; + request.setEvent( + patchbukkit.events.Event.newBuilder().setPlayerBedEnter( + patchbukkit.events.PlayerBedEnterEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(bedEnterEvent.getPlayer().getUniqueId())) + .setBedLocation(BridgeUtils.convertLocation(bedEnterEvent.getBed().getLocation())) + .build() + ).build() + ); + break; + case "org.bukkit.event.player.PlayerBedLeaveEvent": + var bedLeaveEvent = (org.bukkit.event.player.PlayerBedLeaveEvent) event; + request.setEvent( + patchbukkit.events.Event.newBuilder().setPlayerBedLeave( + patchbukkit.events.PlayerBedLeaveEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(bedLeaveEvent.getPlayer().getUniqueId())) + .setBedLocation(BridgeUtils.convertLocation(bedLeaveEvent.getBed().getLocation())) + .build() + ).build() + ); + break; + case "org.bukkit.event.player.PlayerBucketEmptyEvent": + var bucketEmptyEvent = (org.bukkit.event.player.PlayerBucketEmptyEvent) event; + String emptyFace = bucketEmptyEvent.getBlockFace() != null + ? bucketEmptyEvent.getBlockFace().name() + : ""; + String emptyBucket = bucketEmptyEvent.getItemStack() != null + ? bucketEmptyEvent.getItemStack().getType().getKey().toString() + : "minecraft:air"; + String emptyHand = bucketEmptyEvent.getHand() != null + ? bucketEmptyEvent.getHand().name() + : "HAND"; + request.setEvent( + patchbukkit.events.Event.newBuilder().setPlayerBucketEmpty( + patchbukkit.events.PlayerBucketEmptyEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(bucketEmptyEvent.getPlayer().getUniqueId())) + .setLocation(BridgeUtils.convertLocation(bucketEmptyEvent.getBlock().getLocation())) + .setBlockKey(bucketEmptyEvent.getBlock().getType().getKey().toString()) + .setBlockFace(emptyFace) + .setBucketItemKey(emptyBucket) + .setHand(emptyHand) + .build() + ).build() + ); + break; + case "org.bukkit.event.player.PlayerBucketFillEvent": + var bucketFillEvent = (org.bukkit.event.player.PlayerBucketFillEvent) event; + String fillFace = bucketFillEvent.getBlockFace() != null + ? bucketFillEvent.getBlockFace().name() + : ""; + String fillBucket = bucketFillEvent.getItemStack() != null + ? bucketFillEvent.getItemStack().getType().getKey().toString() + : "minecraft:air"; + String fillHand = bucketFillEvent.getHand() != null + ? bucketFillEvent.getHand().name() + : "HAND"; + request.setEvent( + patchbukkit.events.Event.newBuilder().setPlayerBucketFill( + patchbukkit.events.PlayerBucketFillEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(bucketFillEvent.getPlayer().getUniqueId())) + .setLocation(BridgeUtils.convertLocation(bucketFillEvent.getBlock().getLocation())) + .setBlockKey(bucketFillEvent.getBlock().getType().getKey().toString()) + .setBlockFace(fillFace) + .setBucketItemKey(fillBucket) + .setHand(fillHand) + .build() + ).build() + ); + break; + case "org.bukkit.event.player.PlayerBucketEntityEvent": + var bucketEntityEvent = (org.bukkit.event.player.PlayerBucketEntityEvent) event; + String originalBucket = bucketEntityEvent.getOriginalBucket() != null + ? bucketEntityEvent.getOriginalBucket().getType().getKey().toString() + : "minecraft:air"; + String entityBucket = bucketEntityEvent.getEntityBucket() != null + ? bucketEntityEvent.getEntityBucket().getType().getKey().toString() + : "minecraft:air"; + String entityHand = bucketEntityEvent.getHand() != null + ? bucketEntityEvent.getHand().name() + : "HAND"; + request.setEvent( + patchbukkit.events.Event.newBuilder().setPlayerBucketEntity( + patchbukkit.events.PlayerBucketEntityEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(bucketEntityEvent.getPlayer().getUniqueId())) + .setEntityUuid(BridgeUtils.convertUuid(bucketEntityEvent.getEntity().getUniqueId())) + .setEntityType(bucketEntityEvent.getEntity().getType().name()) + .setOriginalBucketKey(originalBucket) + .setEntityBucketKey(entityBucket) + .setHand(entityHand) + .build() + ).build() + ); + break; + case "org.bukkit.event.player.PlayerChangedMainHandEvent": + var mainHandEvent = (org.bukkit.event.player.PlayerChangedMainHandEvent) event; + String mainHand = mainHandEvent.getMainHand() != null + ? mainHandEvent.getMainHand().name() + : "RIGHT"; + request.setEvent( + patchbukkit.events.Event.newBuilder().setPlayerChangedMainHand( + patchbukkit.events.PlayerChangedMainHandEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(mainHandEvent.getPlayer().getUniqueId())) + .setMainHand(mainHand) + .build() + ).build() + ); + break; + case "org.bukkit.event.player.PlayerRegisterChannelEvent": + var registerChannelEvent = (org.bukkit.event.player.PlayerRegisterChannelEvent) event; + request.setEvent( + patchbukkit.events.Event.newBuilder().setPlayerRegisterChannel( + patchbukkit.events.PlayerRegisterChannelEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(registerChannelEvent.getPlayer().getUniqueId())) + .setChannel(registerChannelEvent.getChannel()) + .build() + ).build() + ); + break; + case "org.bukkit.event.player.PlayerUnregisterChannelEvent": + var unregisterChannelEvent = (org.bukkit.event.player.PlayerUnregisterChannelEvent) event; + request.setEvent( + patchbukkit.events.Event.newBuilder().setPlayerUnregisterChannel( + patchbukkit.events.PlayerUnregisterChannelEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(unregisterChannelEvent.getPlayer().getUniqueId())) + .setChannel(unregisterChannelEvent.getChannel()) + .build() + ).build() + ); + break; + case "org.bukkit.event.player.AsyncPlayerChatEvent": + var chatEvent = (org.bukkit.event.player.AsyncPlayerChatEvent) event; + var chatBuilder = PlayerChatEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(chatEvent.getPlayer().getUniqueId())) + .setMessage(chatEvent.getMessage()); + for (var recipient : chatEvent.getRecipients()) { + chatBuilder.addRecipients(BridgeUtils.convertUuid(recipient.getUniqueId())); + } + request.setEvent( + patchbukkit.events.Event.newBuilder().setPlayerChat(chatBuilder.build()).build() + ); + break; + case "org.bukkit.event.player.PlayerCommandPreprocessEvent": + var commandEvent = (org.bukkit.event.player.PlayerCommandPreprocessEvent) event; + request.setEvent( + patchbukkit.events.Event.newBuilder().setPlayerCommand( + PlayerCommandEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(commandEvent.getPlayer().getUniqueId())) + .setCommand(commandEvent.getMessage()) + .build() + ).build() + ); + break; + case "org.bukkit.event.player.PlayerCommandSendEvent": + var commandSendEvent = (org.bukkit.event.player.PlayerCommandSendEvent) event; + var commandSendBuilder = patchbukkit.events.PlayerCommandSendEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(commandSendEvent.getPlayer().getUniqueId())); + commandSendBuilder.addAllCommands(commandSendEvent.getCommands()); + request.setEvent( + patchbukkit.events.Event.newBuilder().setPlayerCommandSend( + commandSendBuilder.build() + ).build() + ); + break; + case "org.bukkit.event.player.PlayerDropItemEvent": + var dropEvent = (org.bukkit.event.player.PlayerDropItemEvent) event; + var item = dropEvent.getItemDrop(); + String dropKey = "minecraft:air"; + int dropAmount = 0; + java.util.UUID itemUuid = java.util.UUID.randomUUID(); + if (item != null && item.getItemStack() != null) { + dropKey = item.getItemStack().getType().getKey().toString(); + dropAmount = item.getItemStack().getAmount(); + itemUuid = item.getUniqueId(); + } + request.setEvent( + patchbukkit.events.Event.newBuilder().setPlayerDropItem( + patchbukkit.events.PlayerDropItemEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(dropEvent.getPlayer().getUniqueId())) + .setItemUuid(BridgeUtils.convertUuid(itemUuid)) + .setItemKey(dropKey) + .setItemAmount(dropAmount) + .build() + ).build() + ); + break; + case "org.bukkit.event.player.PlayerEditBookEvent": + var editEvent = (org.bukkit.event.player.PlayerEditBookEvent) event; + var editBuilder = patchbukkit.events.PlayerEditBookEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(editEvent.getPlayer().getUniqueId())) + .setSlot(editEvent.getSlot()) + .setIsSigning(editEvent.isSigning()); + var newMeta = editEvent.getNewBookMeta(); + if (newMeta != null) { + editBuilder.addAllPages(newMeta.getPages()); + if (newMeta.hasTitle()) { + editBuilder.setTitle(newMeta.getTitle()); + } + } + request.setEvent( + patchbukkit.events.Event.newBuilder().setPlayerEditBook( + editBuilder.build() + ).build() + ); + break; + case "org.bukkit.event.player.PlayerEggThrowEvent": + var eggEvent = (org.bukkit.event.player.PlayerEggThrowEvent) event; + var egg = eggEvent.getEgg(); + var eggUuid = egg != null ? egg.getUniqueId() : java.util.UUID.randomUUID(); + var hatchingType = eggEvent.getHatchingType() != null + ? eggEvent.getHatchingType().name() + : "CHICKEN"; + request.setEvent( + patchbukkit.events.Event.newBuilder().setPlayerEggThrow( + patchbukkit.events.PlayerEggThrowEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(eggEvent.getPlayer().getUniqueId())) + .setEggUuid(BridgeUtils.convertUuid(eggUuid)) + .setHatching(eggEvent.isHatching()) + .setNumHatches(eggEvent.getNumHatches()) + .setHatchingType(hatchingType) + .build() + ).build() + ); + break; + case "org.bukkit.event.player.PlayerExpChangeEvent": + var expEvent = (org.bukkit.event.player.PlayerExpChangeEvent) event; + request.setEvent( + patchbukkit.events.Event.newBuilder().setPlayerExpChange( + patchbukkit.events.PlayerExpChangeEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(expEvent.getPlayer().getUniqueId())) + .setAmount(expEvent.getAmount()) + .build() + ).build() + ); + break; + case "org.bukkit.event.player.PlayerFishEvent": + var fishEvent = (org.bukkit.event.player.PlayerFishEvent) event; + var fishBuilder = patchbukkit.events.PlayerFishEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(fishEvent.getPlayer().getUniqueId())) + .setState(fishEvent.getState().name()) + .setExpToDrop(fishEvent.getExpToDrop()); + if (fishEvent.getHand() != null) { + fishBuilder.setHand(fishEvent.getHand().name()); + } + if (fishEvent.getHook() != null) { + fishBuilder.setHookUuid(BridgeUtils.convertUuid(fishEvent.getHook().getUniqueId())); + } + if (fishEvent.getCaught() != null) { + fishBuilder.setCaughtUuid(BridgeUtils.convertUuid(fishEvent.getCaught().getUniqueId())); + fishBuilder.setCaughtType(fishEvent.getCaught().getType().name()); + } + request.setEvent( + patchbukkit.events.Event.newBuilder().setPlayerFish( + fishBuilder.build() + ).build() + ); + break; + case "org.bukkit.event.player.PlayerInteractAtEntityEvent": + var interactAtEvent = (org.bukkit.event.player.PlayerInteractAtEntityEvent) event; + var clickedEntity = interactAtEvent.getRightClicked(); + var pos = interactAtEvent.getClickedPosition(); + var interactAtBuilder = patchbukkit.events.PlayerInteractAtEntityEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(interactAtEvent.getPlayer().getUniqueId())) + .setEntityUuid(BridgeUtils.convertUuid(clickedEntity.getUniqueId())) + .setEntityType(clickedEntity.getType().name()); + if (interactAtEvent.getHand() != null) { + interactAtBuilder.setHand(interactAtEvent.getHand().name()); + } + if (pos != null) { + interactAtBuilder.setClickedPosition(patchbukkit.common.Vec3.newBuilder() + .setX(pos.getX()) + .setY(pos.getY()) + .setZ(pos.getZ()) + .build()); + } + request.setEvent( + patchbukkit.events.Event.newBuilder().setPlayerInteractAtEntity( + interactAtBuilder.build() + ).build() + ); + break; + case "org.bukkit.event.player.PlayerInteractEntityEvent": + var interactEntityEvent = (org.bukkit.event.player.PlayerInteractEntityEvent) event; + var rightClicked = interactEntityEvent.getRightClicked(); + var interactEntityBuilder = patchbukkit.events.PlayerInteractEntityEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(interactEntityEvent.getPlayer().getUniqueId())) + .setEntityUuid(BridgeUtils.convertUuid(rightClicked.getUniqueId())) + .setEntityType(rightClicked.getType().name()); + if (interactEntityEvent.getHand() != null) { + interactEntityBuilder.setHand(interactEntityEvent.getHand().name()); + } + request.setEvent( + patchbukkit.events.Event.newBuilder().setPlayerInteractEntity( + interactEntityBuilder.build() + ).build() + ); + break; + case "org.bukkit.event.player.PlayerItemHeldEvent": + var heldEvent = (org.bukkit.event.player.PlayerItemHeldEvent) event; + request.setEvent( + patchbukkit.events.Event.newBuilder().setPlayerItemHeld( + patchbukkit.events.PlayerItemHeldEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(heldEvent.getPlayer().getUniqueId())) + .setPreviousSlot(heldEvent.getPreviousSlot()) + .setNewSlot(heldEvent.getNewSlot()) + .build() + ).build() + ); + break; + case "org.bukkit.event.player.PlayerItemDamageEvent": + var playerItemDamageEvent = (org.bukkit.event.player.PlayerItemDamageEvent) event; + var damageItem = playerItemDamageEvent.getItem(); + String damageKey = damageItem != null + ? damageItem.getType().getKey().toString() + : "minecraft:air"; + int damageAmount = damageItem != null ? damageItem.getAmount() : 0; + request.setEvent( + patchbukkit.events.Event.newBuilder().setPlayerItemDamage( + patchbukkit.events.PlayerItemDamageEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(playerItemDamageEvent.getPlayer().getUniqueId())) + .setItemKey(damageKey) + .setItemAmount(damageAmount) + .setDamage(playerItemDamageEvent.getDamage()) + .build() + ).build() + ); + break; + case "org.bukkit.event.player.PlayerItemBreakEvent": + var playerItemBreakEvent = (org.bukkit.event.player.PlayerItemBreakEvent) event; + var brokenItem = playerItemBreakEvent.getBrokenItem(); + String breakKey = brokenItem != null + ? brokenItem.getType().getKey().toString() + : "minecraft:air"; + int breakAmount = brokenItem != null ? brokenItem.getAmount() : 0; + request.setEvent( + patchbukkit.events.Event.newBuilder().setPlayerItemBreak( + patchbukkit.events.PlayerItemBreakEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(playerItemBreakEvent.getPlayer().getUniqueId())) + .setItemKey(breakKey) + .setItemAmount(breakAmount) + .build() + ).build() + ); + break; + case "org.bukkit.event.player.PlayerItemConsumeEvent": + var consumeEvent = (org.bukkit.event.player.PlayerItemConsumeEvent) event; + var consumeItem = consumeEvent.getItem(); + String consumeKey = consumeItem != null + ? consumeItem.getType().getKey().toString() + : "minecraft:air"; + int consumeAmount = consumeItem != null ? consumeItem.getAmount() : 0; + String consumeHand = consumeEvent.getHand() != null + ? consumeEvent.getHand().name() + : "HAND"; + request.setEvent( + patchbukkit.events.Event.newBuilder().setPlayerItemConsume( + patchbukkit.events.PlayerItemConsumeEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(consumeEvent.getPlayer().getUniqueId())) + .setItemKey(consumeKey) + .setItemAmount(consumeAmount) + .setHand(consumeHand) + .build() + ).build() + ); + break; + case "org.bukkit.event.player.PlayerItemMendEvent": + var mendEvent = (org.bukkit.event.player.PlayerItemMendEvent) event; + var mendItem = mendEvent.getItem(); + String mendKey = mendItem != null + ? mendItem.getType().getKey().toString() + : "minecraft:air"; + int mendAmount = mendItem != null ? mendItem.getAmount() : 0; + String mendSlot = mendEvent.getSlot() != null + ? mendEvent.getSlot().name() + : "HAND"; + var mendBuilder = patchbukkit.events.PlayerItemMendEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(mendEvent.getPlayer().getUniqueId())) + .setItemKey(mendKey) + .setItemAmount(mendAmount) + .setSlot(mendSlot) + .setRepairAmount(mendEvent.getRepairAmount()); + if (mendEvent.getExperienceOrb() != null) { + mendBuilder.setOrbUuid( + BridgeUtils.convertUuid(mendEvent.getExperienceOrb().getUniqueId()) + ); + } + request.setEvent( + patchbukkit.events.Event.newBuilder().setPlayerItemMend( + mendBuilder.build() + ).build() + ); + break; + case "org.bukkit.event.player.PlayerLevelChangeEvent": + var levelEvent = (org.bukkit.event.player.PlayerLevelChangeEvent) event; + request.setEvent( + patchbukkit.events.Event.newBuilder().setPlayerLevelChange( + patchbukkit.events.PlayerLevelChangeEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(levelEvent.getPlayer().getUniqueId())) + .setOldLevel(levelEvent.getOldLevel()) + .setNewLevel(levelEvent.getNewLevel()) + .build() + ).build() + ); + break; + case "org.bukkit.event.player.PlayerKickEvent": + var kickEvent = (org.bukkit.event.player.PlayerKickEvent) event; + String kickReason = GsonComponentSerializer.gson().serialize(kickEvent.reason()); + String leaveMessage = GsonComponentSerializer.gson().serialize(kickEvent.leaveMessage()); + String kickCause = kickEvent.getCause() != null ? kickEvent.getCause().name() : "UNKNOWN"; + request.setEvent( + patchbukkit.events.Event.newBuilder().setPlayerKick( + patchbukkit.events.PlayerKickEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(kickEvent.getPlayer().getUniqueId())) + .setReason(kickReason) + .setLeaveMessage(leaveMessage) + .setCause(kickCause) + .build() + ).build() + ); + break; + case "org.bukkit.event.player.PlayerToggleSneakEvent": + var toggleSneakEvent = (org.bukkit.event.player.PlayerToggleSneakEvent) event; + request.setEvent( + patchbukkit.events.Event.newBuilder().setPlayerToggleSneak( + patchbukkit.events.PlayerToggleSneakEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(toggleSneakEvent.getPlayer().getUniqueId())) + .setIsSneaking(toggleSneakEvent.isSneaking()) + .build() + ).build() + ); + break; + case "org.bukkit.event.player.PlayerToggleSprintEvent": + var toggleSprintEvent = (org.bukkit.event.player.PlayerToggleSprintEvent) event; + request.setEvent( + patchbukkit.events.Event.newBuilder().setPlayerToggleSprint( + patchbukkit.events.PlayerToggleSprintEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(toggleSprintEvent.getPlayer().getUniqueId())) + .setIsSprinting(toggleSprintEvent.isSprinting()) + .build() + ).build() + ); + break; + case "org.bukkit.event.player.PlayerToggleFlightEvent": + var toggleFlightEvent = (org.bukkit.event.player.PlayerToggleFlightEvent) event; + request.setEvent( + patchbukkit.events.Event.newBuilder().setPlayerToggleFlight( + patchbukkit.events.PlayerToggleFlightEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(toggleFlightEvent.getPlayer().getUniqueId())) + .setIsFlying(toggleFlightEvent.isFlying()) + .build() + ).build() + ); + break; + case "org.bukkit.event.player.PlayerSwapHandItemsEvent": + var swapEvent = (org.bukkit.event.player.PlayerSwapHandItemsEvent) event; + var swapMainHandItem = swapEvent.getMainHandItem(); + var offHand = swapEvent.getOffHandItem(); + String mainKey = swapMainHandItem != null ? swapMainHandItem.getType().getKey().toString() : "minecraft:air"; + int mainAmount = swapMainHandItem != null ? swapMainHandItem.getAmount() : 0; + String offKey = offHand != null ? offHand.getType().getKey().toString() : "minecraft:air"; + int offAmount = offHand != null ? offHand.getAmount() : 0; + request.setEvent( + patchbukkit.events.Event.newBuilder().setPlayerSwapHandItems( + patchbukkit.events.PlayerSwapHandItemsEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(swapEvent.getPlayer().getUniqueId())) + .setMainHandItemKey(mainKey) + .setMainHandItemAmount(mainAmount) + .setOffHandItemKey(offKey) + .setOffHandItemAmount(offAmount) + .build() + ).build() + ); + break; + case "org.bukkit.event.player.PlayerResourcePackStatusEvent": + var packEvent = (org.bukkit.event.player.PlayerResourcePackStatusEvent) event; + var packUuid = PatchBukkitEventFactory.resolveResourcePackId(packEvent); + String status = packEvent.getStatus() != null ? packEvent.getStatus().name() : "FAILED_DOWNLOAD"; + String hash = packEvent.getHash() != null ? packEvent.getHash() : ""; + request.setEvent( + patchbukkit.events.Event.newBuilder().setPlayerResourcePackStatus( + patchbukkit.events.PlayerResourcePackStatusEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(packEvent.getPlayer().getUniqueId())) + .setPackUuid(BridgeUtils.convertUuid(packUuid)) + .setStatus(status) + .setHash(hash) + .build() + ).build() + ); + break; + case "org.bukkit.event.player.PlayerRespawnEvent": + var respawnEvent = (org.bukkit.event.player.PlayerRespawnEvent) event; + String respawnReason = respawnEvent.getRespawnReason() != null + ? respawnEvent.getRespawnReason().name() + : "DEATH"; + request.setEvent( + patchbukkit.events.Event.newBuilder().setPlayerRespawn( + patchbukkit.events.PlayerRespawnEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(respawnEvent.getPlayer().getUniqueId())) + .setRespawnLocation(BridgeUtils.convertLocation(respawnEvent.getRespawnLocation())) + .setIsBedSpawn(respawnEvent.isBedSpawn()) + .setIsAnchorSpawn(respawnEvent.isAnchorSpawn()) + .setIsMissingRespawnBlock(respawnEvent.isMissingRespawnBlock()) + .setRespawnReason(respawnReason) + .build() + ).build() + ); + break; + case "org.bukkit.event.player.PlayerPickupArrowEvent": + var pickupEvent = (org.bukkit.event.player.PlayerPickupArrowEvent) event; + var pickupItem = pickupEvent.getItem(); + String pickupItemKey = "minecraft:air"; + int itemAmount = 0; + if (pickupItem != null && pickupItem.getItemStack() != null) { + pickupItemKey = pickupItem.getItemStack().getType().getKey().toString(); + itemAmount = pickupItem.getItemStack().getAmount(); + } + request.setEvent( + patchbukkit.events.Event.newBuilder().setPlayerPickupArrow( + patchbukkit.events.PlayerPickupArrowEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(pickupEvent.getPlayer().getUniqueId())) + .setArrowUuid(BridgeUtils.convertUuid(pickupEvent.getArrow().getUniqueId())) + .setItemUuid(BridgeUtils.convertUuid(pickupItem != null ? pickupItem.getUniqueId() : java.util.UUID.randomUUID())) + .setItemKey(pickupItemKey) + .setItemAmount(itemAmount) + .setRemaining(pickupEvent.getRemaining()) + .build() + ).build() + ); + break; + case "org.bukkit.event.player.PlayerPortalEvent": + var portalEvent = (org.bukkit.event.player.PlayerPortalEvent) event; + String portalCause = portalEvent.getCause() != null ? portalEvent.getCause().name() : ""; + int searchRadius = 0; + boolean canCreatePortal = false; + int creationRadius = 0; + try { + var method = org.bukkit.event.player.PlayerPortalEvent.class.getMethod("getSearchRadius"); + Object value = method.invoke(portalEvent); + if (value instanceof Integer i) { + searchRadius = i; + } + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + var method = org.bukkit.event.player.PlayerPortalEvent.class.getMethod("getCanCreatePortal"); + Object value = method.invoke(portalEvent); + if (value instanceof Boolean b) { + canCreatePortal = b; + } + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + var method = org.bukkit.event.player.PlayerPortalEvent.class.getMethod("getCreationRadius"); + Object value = method.invoke(portalEvent); + if (value instanceof Integer i) { + creationRadius = i; + } + } catch (ReflectiveOperationException ignored) { + // ignore + } + request.setEvent( + patchbukkit.events.Event.newBuilder().setPlayerPortal( + patchbukkit.events.PlayerPortalEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(portalEvent.getPlayer().getUniqueId())) + .setFrom(BridgeUtils.convertLocation(portalEvent.getFrom())) + .setTo(BridgeUtils.convertLocation(portalEvent.getTo())) + .setCause(portalCause) + .setSearchRadius(searchRadius) + .setCanCreatePortal(canCreatePortal) + .setCreationRadius(creationRadius) + .build() + ).build() + ); + break; + case "org.bukkit.event.player.PlayerRecipeDiscoverEvent": + var recipeEvent = (org.bukkit.event.player.PlayerRecipeDiscoverEvent) event; + String recipeKey = recipeEvent.getRecipe() != null + ? recipeEvent.getRecipe().toString() + : ""; + request.setEvent( + patchbukkit.events.Event.newBuilder().setPlayerRecipeDiscover( + patchbukkit.events.PlayerRecipeDiscoverEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(recipeEvent.getPlayer().getUniqueId())) + .setRecipeKey(recipeKey) + .build() + ).build() + ); + break; + case "org.bukkit.event.player.PlayerRiptideEvent": + var riptideEvent = (org.bukkit.event.player.PlayerRiptideEvent) event; + var riptideItem = riptideEvent.getItem(); + String riptideKey = riptideItem != null ? riptideItem.getType().getKey().toString() : "minecraft:air"; + int riptideAmount = riptideItem != null ? riptideItem.getAmount() : 0; + Vector riptideVelocity = riptideEvent.getVelocity(); + request.setEvent( + patchbukkit.events.Event.newBuilder().setPlayerRiptide( + patchbukkit.events.PlayerRiptideEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(riptideEvent.getPlayer().getUniqueId())) + .setItemKey(riptideKey) + .setItemAmount(riptideAmount) + .setVelocity(patchbukkit.common.Vec3.newBuilder() + .setX(riptideVelocity.getX()) + .setY(riptideVelocity.getY()) + .setZ(riptideVelocity.getZ()) + .build()) + .build() + ).build() + ); + break; + case "org.bukkit.event.player.PlayerShearEntityEvent": + var shearEvent = (org.bukkit.event.player.PlayerShearEntityEvent) event; + ItemStack shearItem = shearEvent.getItem(); + String shearKey = shearItem != null ? shearItem.getType().getKey().toString() : "minecraft:air"; + int shearAmount = shearItem != null ? shearItem.getAmount() : 0; + String shearHand = shearEvent.getHand() != null ? shearEvent.getHand().name() : "HAND"; + request.setEvent( + patchbukkit.events.Event.newBuilder().setPlayerShearEntity( + patchbukkit.events.PlayerShearEntityEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(shearEvent.getPlayer().getUniqueId())) + .setEntityUuid(BridgeUtils.convertUuid(shearEvent.getEntity().getUniqueId())) + .setEntityType(shearEvent.getEntity().getType().name()) + .setItemKey(shearKey) + .setItemAmount(shearAmount) + .setHand(shearHand) + .build() + ).build() + ); + break; + case "org.spigotmc.event.player.PlayerSpawnLocationEvent": + var spawnEvent = (org.spigotmc.event.player.PlayerSpawnLocationEvent) event; + request.setEvent( + patchbukkit.events.Event.newBuilder().setPlayerSpawnLocation( + patchbukkit.events.PlayerSpawnLocationEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(spawnEvent.getPlayer().getUniqueId())) + .setSpawnLocation(BridgeUtils.convertLocation(spawnEvent.getSpawnLocation())) + .build() + ).build() + ); + break; + case "org.bukkit.event.player.PlayerStatisticIncrementEvent": + var statEvent = (org.bukkit.event.player.PlayerStatisticIncrementEvent) event; + String statEntityType = statEvent.getEntityType() != null ? statEvent.getEntityType().name() : ""; + String statMaterial = statEvent.getMaterial() != null ? statEvent.getMaterial().getKey().toString() : ""; + request.setEvent( + patchbukkit.events.Event.newBuilder().setPlayerStatisticIncrement( + patchbukkit.events.PlayerStatisticIncrementEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(statEvent.getPlayer().getUniqueId())) + .setStatistic(statEvent.getStatistic().name()) + .setInitialValue(statEvent.getPreviousValue()) + .setNewValue(statEvent.getNewValue()) + .setEntityType(statEntityType) + .setMaterialKey(statMaterial) + .build() + ).build() + ); + break; + case "org.bukkit.event.player.PlayerVelocityEvent": + var velocityEvent = (org.bukkit.event.player.PlayerVelocityEvent) event; + Vector velocity = velocityEvent.getVelocity(); + request.setEvent( + patchbukkit.events.Event.newBuilder().setPlayerVelocity( + patchbukkit.events.PlayerVelocityEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(velocityEvent.getPlayer().getUniqueId())) + .setVelocity(patchbukkit.common.Vec3.newBuilder() + .setX(velocity.getX()) + .setY(velocity.getY()) + .setZ(velocity.getZ()) + .build()) + .build() + ).build() + ); + break; + case "org.bukkit.event.player.PlayerHarvestBlockEvent": + var harvestEvent = (org.bukkit.event.player.PlayerHarvestBlockEvent) event; + Block harvestedBlock = null; + ItemStack harvestTool = null; + try { + var method = harvestEvent.getClass().getMethod("getHarvestedBlock"); + Object value = method.invoke(harvestEvent); + if (value instanceof Block block) { + harvestedBlock = block; + } + } catch (ReflectiveOperationException ignored) { + // ignore + } + if (harvestedBlock == null) { + try { + var method = harvestEvent.getClass().getMethod("getBlock"); + Object value = method.invoke(harvestEvent); + if (value instanceof Block block) { + harvestedBlock = block; + } + } catch (ReflectiveOperationException ignored) { + // ignore + } + } + try { + var method = harvestEvent.getClass().getMethod("getHarvestingTool"); + Object value = method.invoke(harvestEvent); + if (value instanceof ItemStack stack) { + harvestTool = stack; + } + } catch (ReflectiveOperationException ignored) { + // ignore + } + if (harvestTool == null) { + try { + var method = harvestEvent.getClass().getMethod("getTool"); + Object value = method.invoke(harvestEvent); + if (value instanceof ItemStack stack) { + harvestTool = stack; + } + } catch (ReflectiveOperationException ignored) { + // ignore + } + } + String harvestBlockKey = harvestedBlock != null + ? harvestedBlock.getType().getKey().toString() + : "minecraft:air"; + String harvestItemKey = harvestTool != null + ? harvestTool.getType().getKey().toString() + : "minecraft:air"; + int harvestItemAmount = harvestTool != null ? harvestTool.getAmount() : 0; + Location harvestLocation = harvestedBlock != null ? harvestedBlock.getLocation() : null; + request.setEvent( + patchbukkit.events.Event.newBuilder().setPlayerHarvestBlock( + patchbukkit.events.PlayerHarvestBlockEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(harvestEvent.getPlayer().getUniqueId())) + .setBlockLocation(BridgeUtils.convertLocation(harvestLocation)) + .setBlockKey(harvestBlockKey) + .setItemKey(harvestItemKey) + .setItemAmount(harvestItemAmount) + .build() + ).build() + ); + break; + case "org.bukkit.event.player.PlayerInteractEvent": + var interactEvent = (org.bukkit.event.player.PlayerInteractEvent) event; + var clicked = interactEvent.getClickedBlock(); + String interactItemKey = interactEvent.getItem() != null + ? interactEvent.getItem().getType().getKey().toString() + : "minecraft:air"; + String interactFace = interactEvent.getBlockFace() != null + ? interactEvent.getBlockFace().name() + : ""; + request.setEvent( + patchbukkit.events.Event.newBuilder().setPlayerInteract( + PlayerInteractEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(interactEvent.getPlayer().getUniqueId())) + .setAction(interactEvent.getAction().name()) + .setBlockKey(clicked != null ? clicked.getType().getKey().toString() : "minecraft:air") + .setClicked(BridgeUtils.convertLocation(clicked != null ? clicked.getLocation() : null)) + .setItemKey(interactItemKey) + .setBlockFace(interactFace) + .build() + ).build() + ); + break; + case "org.bukkit.event.block.BlockBreakEvent": + var blockBreakEvent = (org.bukkit.event.block.BlockBreakEvent) event; + request.setEvent( + patchbukkit.events.Event.newBuilder().setBlockBreak( + BlockBreakEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(blockBreakEvent.getPlayer().getUniqueId())) + .setBlockKey(blockBreakEvent.getBlock().getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(blockBreakEvent.getBlock().getLocation())) + .setExp(blockBreakEvent.getExpToDrop()) + .setDrop(blockBreakEvent.isDropItems()) + .build() + ).build() + ); + break; + case "org.bukkit.event.block.BlockDamageEvent": + var blockDamageEvent = (org.bukkit.event.block.BlockDamageEvent) event; + String damageItemKey = blockDamageEvent.getItemInHand() != null + ? blockDamageEvent.getItemInHand().getType().getKey().toString() + : "minecraft:air"; + boolean instaBreak = resolveInstaBreak(blockDamageEvent); + request.setEvent( + patchbukkit.events.Event.newBuilder().setBlockDamage( + BlockDamageEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(blockDamageEvent.getPlayer().getUniqueId())) + .setBlockKey(blockDamageEvent.getBlock().getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(blockDamageEvent.getBlock().getLocation())) + .setItemKey(damageItemKey) + .setInstaBreak(instaBreak) + .build() + ).build() + ); + break; + case "org.bukkit.event.block.BlockDamageAbortEvent": + var damageAbortEvent = (org.bukkit.event.block.BlockDamageAbortEvent) event; + String abortItemKey = damageAbortEvent.getItemInHand() != null + ? damageAbortEvent.getItemInHand().getType().getKey().toString() + : "minecraft:air"; + request.setEvent( + patchbukkit.events.Event.newBuilder().setBlockDamageAbort( + BlockDamageAbortEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(damageAbortEvent.getPlayer().getUniqueId())) + .setBlockKey(damageAbortEvent.getBlock().getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(damageAbortEvent.getBlock().getLocation())) + .setItemKey(abortItemKey) + .build() + ).build() + ); + break; + case "org.bukkit.event.block.BlockDispenseEvent": + var dispenseEvent = (org.bukkit.event.block.BlockDispenseEvent) event; + String dispenseItemKey = dispenseEvent.getItem() != null + ? dispenseEvent.getItem().getType().getKey().toString() + : "minecraft:air"; + int dispenseAmount = dispenseEvent.getItem() != null + ? dispenseEvent.getItem().getAmount() + : 0; + var dispenseVelocity = dispenseEvent.getVelocity(); + request.setEvent( + patchbukkit.events.Event.newBuilder().setBlockDispense( + BlockDispenseEvent.newBuilder() + .setBlockKey(dispenseEvent.getBlock().getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(dispenseEvent.getBlock().getLocation())) + .setItemKey(dispenseItemKey) + .setItemAmount(dispenseAmount) + .setVelocity(patchbukkit.common.Vec3.newBuilder() + .setX(dispenseVelocity.getX()) + .setY(dispenseVelocity.getY()) + .setZ(dispenseVelocity.getZ()) + .build()) + .build() + ).build() + ); + break; + case "org.bukkit.event.block.BlockDropItemEvent": + var dropItemEvent = (org.bukkit.event.block.BlockDropItemEvent) event; + Block dropBlock = dropItemEvent.getBlock(); + var dropBuilder = BlockDropItemEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(dropItemEvent.getPlayer().getUniqueId())) + .setBlockKey(dropBlock.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(dropBlock.getLocation())); + for (org.bukkit.entity.Item dropItem : dropItemEvent.getItems()) { + if (dropItem.getItemStack() == null) continue; + dropBuilder.addItems( + BlockDropItemEntry.newBuilder() + .setItemKey(dropItem.getItemStack().getType().getKey().toString()) + .setItemAmount(dropItem.getItemStack().getAmount()) + .build() + ); + } + request.setEvent( + patchbukkit.events.Event.newBuilder().setBlockDropItem(dropBuilder.build()).build() + ); + break; + case "org.bukkit.event.block.BlockExplodeEvent": + var explodeEvent = (org.bukkit.event.block.BlockExplodeEvent) event; + Block explodeBlock = explodeEvent.getBlock(); + var explodeBuilder = BlockExplodeEvent.newBuilder() + .setBlockKey(explodeBlock.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(explodeBlock.getLocation())) + .setYield(explodeEvent.getYield()); + for (Block b : explodeEvent.blockList()) { + explodeBuilder.addBlocks( + BlockExplodeBlockEntry.newBuilder() + .setBlockKey(b.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(b.getLocation())) + .build() + ); + } + request.setEvent( + patchbukkit.events.Event.newBuilder().setBlockExplode(explodeBuilder.build()).build() + ); + break; + case "org.bukkit.event.block.BlockFadeEvent": + var fadeEvent = (org.bukkit.event.block.BlockFadeEvent) event; + Block fadeBlock = fadeEvent.getBlock(); + request.setEvent( + patchbukkit.events.Event.newBuilder().setBlockFade( + BlockFadeEvent.newBuilder() + .setBlockKey(fadeBlock.getType().getKey().toString()) + .setNewBlockKey(fadeEvent.getNewState().getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(fadeBlock.getLocation())) + .build() + ).build() + ); + break; + case "org.bukkit.event.block.BlockFertilizeEvent": + var fertilizeEvent = (org.bukkit.event.block.BlockFertilizeEvent) event; + Block fertilizeBlock = fertilizeEvent.getBlock(); + var fertilizeBuilder = BlockFertilizeEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(fertilizeEvent.getPlayer().getUniqueId())) + .setBlockKey(fertilizeBlock.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(fertilizeBlock.getLocation())); + for (org.bukkit.block.BlockState state : fertilizeEvent.getBlocks()) { + fertilizeBuilder.addBlocks( + BlockFertilizeBlockEntry.newBuilder() + .setBlockKey(state.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(state.getLocation())) + .build() + ); + } + request.setEvent( + patchbukkit.events.Event.newBuilder().setBlockFertilize(fertilizeBuilder.build()).build() + ); + break; + case "org.bukkit.event.block.BlockFormEvent": + var formEvent = (org.bukkit.event.block.BlockFormEvent) event; + Block formBlock = formEvent.getBlock(); + request.setEvent( + patchbukkit.events.Event.newBuilder().setBlockForm( + BlockFormEvent.newBuilder() + .setBlockKey(formBlock.getType().getKey().toString()) + .setNewBlockKey(formEvent.getNewState().getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(formBlock.getLocation())) + .build() + ).build() + ); + break; + case "org.bukkit.event.block.BlockFromToEvent": + var fromToEvent = (org.bukkit.event.block.BlockFromToEvent) event; + Block fromBlock = fromToEvent.getBlock(); + Block toBlock = fromToEvent.getToBlock(); + String fromToFace = fromToEvent.getFace() != null ? fromToEvent.getFace().name() : ""; + request.setEvent( + patchbukkit.events.Event.newBuilder().setBlockFromTo( + BlockFromToEvent.newBuilder() + .setBlockKey(fromBlock.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(fromBlock.getLocation())) + .setToBlockKey(toBlock.getType().getKey().toString()) + .setToLocation(BridgeUtils.convertLocation(toBlock.getLocation())) + .setFace(fromToFace) + .build() + ).build() + ); + break; + case "org.bukkit.event.block.BlockGrowEvent": + var growEvent = (org.bukkit.event.block.BlockGrowEvent) event; + Block growBlock = growEvent.getBlock(); + request.setEvent( + patchbukkit.events.Event.newBuilder().setBlockGrow( + BlockGrowEvent.newBuilder() + .setBlockKey(growBlock.getType().getKey().toString()) + .setNewBlockKey(growEvent.getNewState().getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(growBlock.getLocation())) + .build() + ).build() + ); + break; + case "org.bukkit.event.block.BlockPistonExtendEvent": + var extendEvent = (org.bukkit.event.block.BlockPistonExtendEvent) event; + Block extendBlock = extendEvent.getBlock(); + String extendDir = extendEvent.getDirection() != null ? extendEvent.getDirection().name() : ""; + var extendBuilder = BlockPistonExtendEvent.newBuilder() + .setBlockKey(extendBlock.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(extendBlock.getLocation())) + .setDirection(extendDir) + .setLength(resolvePistonLength(extendEvent)); + for (Block b : extendEvent.getBlocks()) { + extendBuilder.addBlocks( + BlockPistonBlockEntry.newBuilder() + .setBlockKey(b.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(b.getLocation())) + .build() + ); + } + request.setEvent( + patchbukkit.events.Event.newBuilder().setBlockPistonExtend(extendBuilder.build()).build() + ); + break; + case "org.bukkit.event.block.BlockPistonRetractEvent": + var retractEvent = (org.bukkit.event.block.BlockPistonRetractEvent) event; + Block retractBlock = retractEvent.getBlock(); + String retractDir = retractEvent.getDirection() != null ? retractEvent.getDirection().name() : ""; + var retractBuilder = BlockPistonRetractEvent.newBuilder() + .setBlockKey(retractBlock.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(retractBlock.getLocation())) + .setDirection(retractDir) + .setLength(resolvePistonLength(retractEvent)); + for (Block b : retractEvent.getBlocks()) { + retractBuilder.addBlocks( + BlockPistonBlockEntry.newBuilder() + .setBlockKey(b.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(b.getLocation())) + .build() + ); + } + request.setEvent( + patchbukkit.events.Event.newBuilder().setBlockPistonRetract(retractBuilder.build()).build() + ); + break; + case "org.bukkit.event.block.BlockRedstoneEvent": + var redstoneEvent = (org.bukkit.event.block.BlockRedstoneEvent) event; + Block redstoneBlock = redstoneEvent.getBlock(); + request.setEvent( + patchbukkit.events.Event.newBuilder().setBlockRedstone( + BlockRedstoneEvent.newBuilder() + .setBlockKey(redstoneBlock.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(redstoneBlock.getLocation())) + .setOldCurrent(redstoneEvent.getOldCurrent()) + .setNewCurrent(redstoneEvent.getNewCurrent()) + .build() + ).build() + ); + break; + case "org.bukkit.event.block.BlockMultiPlaceEvent": + var multiEvent = (org.bukkit.event.block.BlockMultiPlaceEvent) event; + Block multiBlock = multiEvent.getBlock(); + var multiBuilder = BlockMultiPlaceEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(multiEvent.getPlayer().getUniqueId())) + .setBlockKey(multiBlock.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(multiBlock.getLocation())); + for (org.bukkit.block.BlockState state : resolveMultiPlaceStates(multiEvent)) { + multiBuilder.addBlocks( + BlockMultiPlaceBlockEntry.newBuilder() + .setBlockKey(state.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(state.getLocation())) + .build() + ); + } + request.setEvent( + patchbukkit.events.Event.newBuilder().setBlockMultiPlace(multiBuilder.build()).build() + ); + break; + case "org.bukkit.event.block.BlockPhysicsEvent": + var physicsEvent = (org.bukkit.event.block.BlockPhysicsEvent) event; + Block physicsBlock = physicsEvent.getBlock(); + Material physicsSourceMaterial = resolvePhysicsChangedMaterial(physicsEvent); + request.setEvent( + patchbukkit.events.Event.newBuilder().setBlockPhysics( + BlockPhysicsEvent.newBuilder() + .setBlockKey(physicsBlock.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(physicsBlock.getLocation())) + .setSourceBlockKey(physicsSourceMaterial.getKey().toString()) + .setSourceLocation(BridgeUtils.convertLocation(physicsBlock.getLocation())) + .build() + ).build() + ); + break; + case "org.bukkit.event.block.NotePlayEvent": + var noteEvent = (org.bukkit.event.block.NotePlayEvent) event; + Block noteBlock = noteEvent.getBlock(); + request.setEvent( + patchbukkit.events.Event.newBuilder().setNotePlay( + NotePlayEvent.newBuilder() + .setBlockKey(noteBlock.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(noteBlock.getLocation())) + .setInstrument(noteEvent.getInstrument().name()) + .setNote(noteEvent.getNote().getId()) + .build() + ).build() + ); + break; + case "org.bukkit.event.block.SignChangeEvent": + var signEvent = (org.bukkit.event.block.SignChangeEvent) event; + Block signBlock = signEvent.getBlock(); + var signBuilder = SignChangeEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(signEvent.getPlayer().getUniqueId())) + .setBlockKey(signBlock.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(signBlock.getLocation())); + for (String line : signEvent.getLines()) { + signBuilder.addLines(line != null ? line : ""); + } + try { + Method method = signEvent.getClass().getMethod("isFrontText"); + Object value = method.invoke(signEvent); + if (value instanceof Boolean b) { + signBuilder.setIsFrontText(b); + } + } catch (ReflectiveOperationException ignored) { + signBuilder.setIsFrontText(true); + } + request.setEvent( + patchbukkit.events.Event.newBuilder().setSignChange(signBuilder.build()).build() + ); + break; + case "org.bukkit.event.block.TNTPrimeEvent": + var tntEvent = (org.bukkit.event.block.TNTPrimeEvent) event; + Block tntBlock = tntEvent.getBlock(); + var tntBuilder = TNTPrimeEvent.newBuilder() + .setBlockKey(tntBlock.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(tntBlock.getLocation())) + .setCause(tntEvent.getCause().name()); + if (tntEvent.getPrimingEntity() instanceof org.bukkit.entity.Player player) { + tntBuilder.setPlayerUuid(BridgeUtils.convertUuid(player.getUniqueId())); + } + request.setEvent( + patchbukkit.events.Event.newBuilder().setTntPrime(tntBuilder.build()).build() + ); + break; + case "org.bukkit.event.block.MoistureChangeEvent": + var moistureEvent = (org.bukkit.event.block.MoistureChangeEvent) event; + Block moistureBlock = moistureEvent.getBlock(); + request.setEvent( + patchbukkit.events.Event.newBuilder().setMoistureChange( + MoistureChangeEvent.newBuilder() + .setBlockKey(moistureBlock.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(moistureBlock.getLocation())) + .setNewBlockKey(moistureEvent.getNewState().getType().getKey().toString()) + .build() + ).build() + ); + break; + case "org.bukkit.event.block.SpongeAbsorbEvent": + var spongeEvent = (org.bukkit.event.block.SpongeAbsorbEvent) event; + Block spongeBlock = spongeEvent.getBlock(); + var spongeBuilder = SpongeAbsorbEvent.newBuilder() + .setBlockKey(spongeBlock.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(spongeBlock.getLocation())); + for (org.bukkit.block.BlockState state : spongeEvent.getBlocks()) { + spongeBuilder.addBlocks( + SpongeAbsorbBlockEntry.newBuilder() + .setBlockKey(state.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(state.getLocation())) + .build() + ); + } + request.setEvent( + patchbukkit.events.Event.newBuilder().setSpongeAbsorb(spongeBuilder.build()).build() + ); + break; + case "org.bukkit.event.block.FluidLevelChangeEvent": + var fluidEvent = (org.bukkit.event.block.FluidLevelChangeEvent) event; + Block fluidBlock = fluidEvent.getBlock(); + String newBlockKey = resolveFluidNewBlockKey(fluidEvent); + request.setEvent( + patchbukkit.events.Event.newBuilder().setFluidLevelChange( + FluidLevelChangeEvent.newBuilder() + .setBlockKey(fluidBlock.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(fluidBlock.getLocation())) + .setNewBlockKey(newBlockKey) + .build() + ).build() + ); + break; + case "org.bukkit.event.world.SpawnChangeEvent": + var worldSpawnChangeEvent = (org.bukkit.event.world.SpawnChangeEvent) event; + Location spawnLocation = resolveSpawnChangeLocation(worldSpawnChangeEvent); + Location previous = resolveSpawnChangePreviousLocation(worldSpawnChangeEvent, spawnLocation); + if (previous == null) { + previous = spawnLocation; + } + request.setEvent( + patchbukkit.events.Event.newBuilder().setSpawnChange( + SpawnChangeEvent.newBuilder() + .setPreviousLocation(BridgeUtils.convertLocation(previous)) + .setLocation(BridgeUtils.convertLocation(spawnLocation)) + .build() + ).build() + ); + break; + case "org.bukkit.event.server.ServerListPingEvent": + var pingEvent = (org.bukkit.event.server.ServerListPingEvent) event; + int onlinePlayers = 0; + try { + Method method = pingEvent.getClass().getMethod("getNumPlayers"); + Object value = method.invoke(pingEvent); + if (value instanceof Integer count) { + onlinePlayers = count; + } + } catch (ReflectiveOperationException ignored) { + onlinePlayers = Bukkit.getOnlinePlayers().size(); + } + String favicon = ""; + try { + Method method = pingEvent.getClass().getMethod("getServerIcon"); + Object icon = method.invoke(pingEvent); + if (icon != null) { + Method dataMethod = icon.getClass().getMethod("getData"); + Object data = dataMethod.invoke(icon); + if (data instanceof String dataString) { + favicon = dataString; + } + } + } catch (ReflectiveOperationException ignored) { + // ignore + } + request.setEvent( + patchbukkit.events.Event.newBuilder().setServerListPing( + ServerListPingEvent.newBuilder() + .setMotd(pingEvent.getMotd()) + .setMaxPlayers(pingEvent.getMaxPlayers()) + .setOnlinePlayers(onlinePlayers) + .setFavicon(favicon) + .build() + ).build() + ); + break; + case "org.bukkit.event.block.BlockCanBuildEvent": + var canBuildEvent = (org.bukkit.event.block.BlockCanBuildEvent) event; + Block canBuildBlock = canBuildEvent.getBlock(); + request.setEvent( + patchbukkit.events.Event.newBuilder().setBlockCanBuild( + patchbukkit.events.BlockCanBuildEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(PatchBukkitEventFactory.resolveEventPlayerUuid(canBuildEvent))) + .setBlockKey(canBuildBlock.getType().getKey().toString()) + .setBlockAgainstKey(canBuildBlock.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(canBuildBlock.getLocation())) + .setCanBuild(canBuildEvent.isBuildable()) + .build() + ).build() + ); + break; + case "org.bukkit.event.block.BlockBurnEvent": + var burnEvent = (org.bukkit.event.block.BlockBurnEvent) event; + Block burnedBlock = burnEvent.getBlock(); + Block ignitingBlock = PatchBukkitEventFactory.resolveIgnitingBlock(burnEvent); + String ignitingKey = ignitingBlock != null + ? ignitingBlock.getType().getKey().toString() + : "minecraft:fire"; + request.setEvent( + patchbukkit.events.Event.newBuilder().setBlockBurn( + patchbukkit.events.BlockBurnEvent.newBuilder() + .setBlockKey(burnedBlock.getType().getKey().toString()) + .setIgnitingBlockKey(ignitingKey) + .setLocation(BridgeUtils.convertLocation(burnedBlock.getLocation())) + .build() + ).build() + ); + break; + case "org.bukkit.event.block.BlockIgniteEvent": + var igniteEvent = (org.bukkit.event.block.BlockIgniteEvent) event; + Block igniteBlock = igniteEvent.getBlock(); + String igniteCause = igniteEvent.getCause() != null ? igniteEvent.getCause().name() : ""; + Block igniteSource = PatchBukkitEventFactory.resolveIgnitingBlock(igniteEvent); + String igniteKey = igniteSource != null + ? igniteSource.getType().getKey().toString() + : "minecraft:fire"; + var igniteBuilder = patchbukkit.events.BlockIgniteEvent.newBuilder() + .setBlockKey(igniteBlock.getType().getKey().toString()) + .setIgnitingBlockKey(igniteKey) + .setLocation(BridgeUtils.convertLocation(igniteBlock.getLocation())) + .setCause(igniteCause); + if (igniteEvent.getPlayer() != null) { + igniteBuilder.setPlayerUuid( + BridgeUtils.convertUuid(igniteEvent.getPlayer().getUniqueId()) + ); + } + request.setEvent( + patchbukkit.events.Event.newBuilder().setBlockIgnite( + igniteBuilder.build() + ).build() + ); + break; + case "org.bukkit.event.block.BlockSpreadEvent": + var spreadEvent = (org.bukkit.event.block.BlockSpreadEvent) event; + Block spreadBlock = spreadEvent.getBlock(); + Block spreadSource = spreadEvent.getSource(); + request.setEvent( + patchbukkit.events.Event.newBuilder().setBlockSpread( + patchbukkit.events.BlockSpreadEvent.newBuilder() + .setSourceBlockKey(spreadSource.getType().getKey().toString()) + .setSourceLocation(BridgeUtils.convertLocation(spreadSource.getLocation())) + .setBlockKey(spreadBlock.getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(spreadBlock.getLocation())) + .build() + ).build() + ); + break; + case "org.bukkit.event.block.BlockPlaceEvent": + var placeEvent = (org.bukkit.event.block.BlockPlaceEvent) event; + request.setEvent( + patchbukkit.events.Event.newBuilder().setBlockPlace( + BlockPlaceEvent.newBuilder() + .setPlayerUuid(BridgeUtils.convertUuid(placeEvent.getPlayer().getUniqueId())) + .setBlockKey(placeEvent.getBlockPlaced().getType().getKey().toString()) + .setBlockAgainstKey(placeEvent.getBlockAgainst().getType().getKey().toString()) + .setLocation(BridgeUtils.convertLocation(placeEvent.getBlockPlaced().getLocation())) + .setCanBuild(placeEvent.canBuild()) + .build() + ).build() + ); + break; + case "org.bukkit.event.entity.EntitySpawnEvent": + var entitySpawnEvent = (org.bukkit.event.entity.EntitySpawnEvent) event; + request.setEvent( + patchbukkit.events.Event.newBuilder().setEntitySpawn( + EntitySpawnEvent.newBuilder() + .setEntityUuid(BridgeUtils.convertUuid(entitySpawnEvent.getEntity().getUniqueId())) + .setEntityType(entitySpawnEvent.getEntity().getType().name()) + .build() + ).build() + ); + break; + case "org.bukkit.event.entity.EntityDamageEvent": + var entityDamageEvent = (org.bukkit.event.entity.EntityDamageEvent) event; + request.setEvent( + patchbukkit.events.Event.newBuilder().setEntityDamage( + EntityDamageEvent.newBuilder() + .setEntityUuid(BridgeUtils.convertUuid(entityDamageEvent.getEntity().getUniqueId())) + .setDamage((float) entityDamageEvent.getFinalDamage()) + .setDamageType(entityDamageEvent.getCause().name()) + .build() + ).build() + ); + break; + case "org.bukkit.event.entity.EntityDeathEvent": + var deathEvent = (org.bukkit.event.entity.EntityDeathEvent) event; + request.setEvent( + patchbukkit.events.Event.newBuilder().setEntityDeath( + EntityDeathEvent.newBuilder() + .setEntityUuid(BridgeUtils.convertUuid(deathEvent.getEntity().getUniqueId())) + .setDamageType("CUSTOM") + .build() + ).build() + ); + break; + case "org.bukkit.event.server.ServerCommandEvent": + var serverCommandEvent = (org.bukkit.event.server.ServerCommandEvent) event; + request.setEvent( + patchbukkit.events.Event.newBuilder().setServerCommand( + ServerCommandEvent.newBuilder() + .setCommand(serverCommandEvent.getCommand()) + .build() + ).build() + ); + break; + case "org.bukkit.event.server.BroadcastMessageEvent": + var broadcastEvent = (org.bukkit.event.server.BroadcastMessageEvent) event; + String messageJson = GsonComponentSerializer.gson().serialize(Component.text(broadcastEvent.getMessage())); + String senderJson = GsonComponentSerializer.gson().serialize(Component.text("")); + request.setEvent( + patchbukkit.events.Event.newBuilder().setServerBroadcast( + ServerBroadcastEvent.newBuilder() + .setMessage(messageJson) + .setSender(senderJson) + .build() + ).build() + ); + break; + case "org.bukkit.event.server.PluginEnableEvent": + var pluginEnableEvent = (org.bukkit.event.server.PluginEnableEvent) event; + request.setEvent( + patchbukkit.events.Event.newBuilder().setPluginEnable( + PluginEnableEvent.newBuilder() + .setPluginName(pluginEnableEvent.getPlugin().getName()) + .build() + ).build() + ); + break; + case "org.bukkit.event.server.PluginDisableEvent": + var pluginDisableEvent = (org.bukkit.event.server.PluginDisableEvent) event; + request.setEvent( + patchbukkit.events.Event.newBuilder().setPluginDisable( + PluginDisableEvent.newBuilder() + .setPluginName(pluginDisableEvent.getPlugin().getName()) + .build() + ).build() + ); + break; + case "org.bukkit.event.server.ServiceRegisterEvent": + var serviceRegisterEvent = (org.bukkit.event.server.ServiceRegisterEvent) event; + var registerProvider = serviceRegisterEvent.getProvider(); + String registerPluginName = registerProvider != null && registerProvider.getPlugin() != null + ? registerProvider.getPlugin().getName() + : ""; + String registerServiceName = registerProvider != null && registerProvider.getService() != null + ? registerProvider.getService().getName() + : ""; + request.setEvent( + patchbukkit.events.Event.newBuilder().setServiceRegister( + ServiceRegisterEvent.newBuilder() + .setPluginName(registerPluginName) + .setServiceName(registerServiceName) + .build() + ).build() + ); + break; + case "org.bukkit.event.server.ServiceUnregisterEvent": + var serviceUnregisterEvent = (org.bukkit.event.server.ServiceUnregisterEvent) event; + var unregisterProvider = serviceUnregisterEvent.getProvider(); + String unregisterPluginName = unregisterProvider != null && unregisterProvider.getPlugin() != null + ? unregisterProvider.getPlugin().getName() + : ""; + String unregisterServiceName = unregisterProvider != null && unregisterProvider.getService() != null + ? unregisterProvider.getService().getName() + : ""; + request.setEvent( + patchbukkit.events.Event.newBuilder().setServiceUnregister( + ServiceUnregisterEvent.newBuilder() + .setPluginName(unregisterPluginName) + .setServiceName(unregisterServiceName) + .build() + ).build() + ); + break; } var response = NativeBridgeFfi.callEvent(request.build()); @@ -264,4 +1851,186 @@ public Map, Set> createRegisteredList public void clearEvents() { HandlerList.unregisterAll(); } + + private static boolean resolveInstaBreak(@NotNull org.bukkit.event.block.BlockDamageEvent event) { + try { + Method method = event.getClass().getMethod("isInstaBreak"); + Object value = method.invoke(event); + if (value instanceof Boolean b) { + return b; + } + } catch (ReflectiveOperationException ignored) { + // ignore + } + return false; + } + + private static int resolvePistonLength(@NotNull Object pistonEvent) { + try { + Method method = pistonEvent.getClass().getMethod("getLength"); + Object value = method.invoke(pistonEvent); + if (value instanceof Integer len) { + return len; + } + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + Method method = pistonEvent.getClass().getMethod("getBlocks"); + Object value = method.invoke(pistonEvent); + if (value instanceof java.util.List list) { + return list.size(); + } + } catch (ReflectiveOperationException ignored) { + // ignore + } + return 0; + } + + @NotNull + private static java.util.List resolveMultiPlaceStates( + @NotNull org.bukkit.event.block.BlockMultiPlaceEvent event + ) { + try { + Method method = event.getClass().getMethod("getBlockStates"); + Object value = method.invoke(event); + if (value instanceof java.util.List list) { + java.util.List states = new java.util.ArrayList<>(); + for (Object entry : list) { + if (entry instanceof org.bukkit.block.BlockState state) { + states.add(state); + } + } + return states; + } + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + Method method = event.getClass().getMethod("getReplacedBlockStates"); + Object value = method.invoke(event); + if (value instanceof java.util.List list) { + java.util.List states = new java.util.ArrayList<>(); + for (Object entry : list) { + if (entry instanceof org.bukkit.block.BlockState state) { + states.add(state); + } + } + return states; + } + } catch (ReflectiveOperationException ignored) { + // ignore + } + return java.util.Collections.emptyList(); + } + + @NotNull + private static Material resolvePhysicsChangedMaterial(@NotNull org.bukkit.event.block.BlockPhysicsEvent event) { + try { + Object changed = event.getChangedType(); + if (changed instanceof Material material) { + return material; + } + if (changed != null) { + Method method = changed.getClass().getMethod("getMaterial"); + Object value = method.invoke(changed); + if (value instanceof Material material) { + return material; + } + } + } catch (ReflectiveOperationException ignored) { + // ignore + } + return event.getBlock().getType(); + } + + @NotNull + private static String resolveFluidNewBlockKey(@NotNull org.bukkit.event.block.FluidLevelChangeEvent event) { + try { + Method method = event.getClass().getMethod("getNewState"); + Object value = method.invoke(event); + if (value instanceof org.bukkit.block.BlockState state) { + return state.getType().getKey().toString(); + } + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + Method method = event.getClass().getMethod("getNewData"); + Object value = method.invoke(event); + if (value instanceof org.bukkit.block.data.BlockData data) { + return data.getMaterial().getKey().toString(); + } + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + Method method = event.getClass().getMethod("getNewBlockData"); + Object value = method.invoke(event); + if (value instanceof org.bukkit.block.data.BlockData data) { + return data.getMaterial().getKey().toString(); + } + } catch (ReflectiveOperationException ignored) { + // ignore + } + return event.getBlock().getType().getKey().toString(); + } + + @Nullable + private static Location resolveSpawnChangeLocation(@NotNull org.bukkit.event.world.SpawnChangeEvent event) { + try { + Method method = event.getClass().getMethod("getLocation"); + Object value = method.invoke(event); + if (value instanceof Location location) { + return location; + } + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + Method method = event.getClass().getMethod("getNewLocation"); + Object value = method.invoke(event); + if (value instanceof Location location) { + return location; + } + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + Method method = event.getClass().getMethod("getNewSpawn"); + Object value = method.invoke(event); + if (value instanceof Location location) { + return location; + } + } catch (ReflectiveOperationException ignored) { + // ignore + } + return event.getWorld() != null ? event.getWorld().getSpawnLocation() : null; + } + + @Nullable + private static Location resolveSpawnChangePreviousLocation( + @NotNull org.bukkit.event.world.SpawnChangeEvent event, + @Nullable Location fallback + ) { + try { + Method method = event.getClass().getMethod("getPreviousLocation"); + Object value = method.invoke(event); + if (value instanceof Location location) { + return location; + } + } catch (ReflectiveOperationException ignored) { + // ignore + } + try { + Method method = event.getClass().getMethod("getPreviousSpawn"); + Object value = method.invoke(event); + if (value instanceof Location location) { + return location; + } + } catch (ReflectiveOperationException ignored) { + // ignore + } + return fallback; + } } diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/world/PatchBukkitBlock.java b/java/patchbukkit/src/main/java/org/patchbukkit/world/PatchBukkitBlock.java new file mode 100644 index 0000000..25805d7 --- /dev/null +++ b/java/patchbukkit/src/main/java/org/patchbukkit/world/PatchBukkitBlock.java @@ -0,0 +1,149 @@ +package org.patchbukkit.world; + +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.patchbukkit.bridge.BridgeUtils; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.Locale; + +import patchbukkit.bridge.NativeBridgeFfi; + +public final class PatchBukkitBlock { + private PatchBukkitBlock() { + } + + public static Block create(PatchBukkitWorld world, int x, int y, int z, String blockKey) { + return (Block) Proxy.newProxyInstance( + PatchBukkitBlock.class.getClassLoader(), + new Class[]{Block.class}, + new Handler(world, x, y, z, blockKey) + ); + } + + static Material resolveMaterial(String blockKey) { + if (blockKey == null || blockKey.isBlank()) { + return Material.AIR; + } + + Material material = Material.matchMaterial(blockKey); + if (material != null) { + return material; + } + + String key = blockKey.contains(":") ? blockKey : "minecraft:" + blockKey; + material = Material.matchMaterial(key); + if (material != null) { + return material; + } + + String legacy = blockKey.replace("minecraft:", "").toUpperCase(Locale.ROOT); + material = Material.getMaterial(legacy); + return material != null ? material : Material.AIR; + } + + static String queryBlockKey(PatchBukkitWorld world, int x, int y, int z, String fallbackBlockKey) { + var request = patchbukkit.block.GetBlockRequest.newBuilder() + .setWorld(patchbukkit.common.World.newBuilder() + .setUuid(BridgeUtils.convertUuid(world.getUID())) + .build()) + .setX(x) + .setY(y) + .setZ(z) + .build(); + var response = NativeBridgeFfi.getBlock(request); + if (response == null || response.getBlockKey().isBlank()) { + return fallbackBlockKey; + } + return response.getBlockKey(); + } + + static Material queryMaterial(PatchBukkitWorld world, int x, int y, int z, String fallbackBlockKey) { + return resolveMaterial(queryBlockKey(world, x, y, z, fallbackBlockKey)); + } + + private static final class Handler implements InvocationHandler { + private final PatchBukkitWorld world; + private final int x; + private final int y; + private final int z; + private final String fallbackBlockKey; + + private Handler(PatchBukkitWorld world, int x, int y, int z, String blockKey) { + this.world = world; + this.x = x; + this.y = y; + this.z = z; + this.fallbackBlockKey = blockKey; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) { + String name = method.getName(); + switch (name) { + case "getWorld": + return world; + case "getX": + return x; + case "getY": + return y; + case "getZ": + return z; + case "getLocation": + return new Location(world, x, y, z); + case "getType": + return queryMaterial(world, x, y, z, fallbackBlockKey); + case "getBlockData": + return queryMaterial(world, x, y, z, fallbackBlockKey).createBlockData(); + case "getState": + String key = queryBlockKey(world, x, y, z, fallbackBlockKey); + return PatchBukkitBlockState.create(world, x, y, z, key); + case "isEmpty": + return queryMaterial(world, x, y, z, fallbackBlockKey).isAir(); + case "isLiquid": + Material material = queryMaterial(world, x, y, z, fallbackBlockKey); + return material == Material.WATER || material == Material.LAVA; + case "getRelative": + return getRelative(args); + case "getChunk": + throw new UnsupportedOperationException("Unimplemented method 'getChunk'"); + case "equals": + return proxy == args[0]; + case "hashCode": + return System.identityHashCode(proxy); + case "toString": + String blockKey = queryBlockKey(world, x, y, z, fallbackBlockKey); + return "PatchBukkitBlock{" + blockKey + " @ " + x + "," + y + "," + z + "}"; + default: + throw new UnsupportedOperationException("Unimplemented method '" + name + "'"); + } + } + + private Block getRelative(Object[] args) { + if (args == null || args.length == 0) { + return world.getBlockAt(x, y, z); + } + if (args.length == 1 && args[0] instanceof BlockFace face) { + return world.getBlockAt(x + face.getModX(), y + face.getModY(), z + face.getModZ()); + } + if (args.length == 2 && args[0] instanceof BlockFace face && args[1] instanceof Integer distance) { + return world.getBlockAt( + x + face.getModX() * distance, + y + face.getModY() * distance, + z + face.getModZ() * distance + ); + } + if (args.length == 3 + && args[0] instanceof Integer dx + && args[1] instanceof Integer dy + && args[2] instanceof Integer dz) { + return world.getBlockAt(x + dx, y + dy, z + dz); + } + throw new UnsupportedOperationException("Unimplemented method 'getRelative'"); + } + } +} diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/world/PatchBukkitBlockState.java b/java/patchbukkit/src/main/java/org/patchbukkit/world/PatchBukkitBlockState.java new file mode 100644 index 0000000..3f04254 --- /dev/null +++ b/java/patchbukkit/src/main/java/org/patchbukkit/world/PatchBukkitBlockState.java @@ -0,0 +1,70 @@ +package org.patchbukkit.world; + +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.BlockState; +import org.bukkit.block.data.BlockData; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +public final class PatchBukkitBlockState { + private PatchBukkitBlockState() { + } + + public static BlockState create(PatchBukkitWorld world, int x, int y, int z, String blockKey) { + return (BlockState) Proxy.newProxyInstance( + PatchBukkitBlockState.class.getClassLoader(), + new Class[]{BlockState.class}, + new Handler(world, x, y, z, blockKey) + ); + } + + private static final class Handler implements InvocationHandler { + private final PatchBukkitWorld world; + private final int x; + private final int y; + private final int z; + private final String blockKey; + + private Handler(PatchBukkitWorld world, int x, int y, int z, String blockKey) { + this.world = world; + this.x = x; + this.y = y; + this.z = z; + this.blockKey = blockKey; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) { + String name = method.getName(); + switch (name) { + case "getBlock": + return world.getBlockAt(x, y, z); + case "getWorld": + return world; + case "getLocation": + return new Location(world, x, y, z); + case "getX": + return x; + case "getY": + return y; + case "getZ": + return z; + case "getType": + return PatchBukkitBlock.queryMaterial(world, x, y, z, blockKey); + case "getBlockData": + return PatchBukkitBlock.queryMaterial(world, x, y, z, blockKey).createBlockData(); + case "update": + return false; + case "toString": + String resolvedKey = PatchBukkitBlock.queryBlockKey(world, x, y, z, blockKey); + return "PatchBukkitBlockState{" + resolvedKey + " @ " + x + "," + y + "," + z + "}"; + default: + throw new UnsupportedOperationException("Unimplemented method '" + name + "'"); + } + } + } +} diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/world/PatchBukkitWorld.java b/java/patchbukkit/src/main/java/org/patchbukkit/world/PatchBukkitWorld.java index e8c2c36..fec1bd0 100644 --- a/java/patchbukkit/src/main/java/org/patchbukkit/world/PatchBukkitWorld.java +++ b/java/patchbukkit/src/main/java/org/patchbukkit/world/PatchBukkitWorld.java @@ -149,10 +149,17 @@ public boolean hasStructureAt( @Override public @NotNull Block getBlockAt(int x, int y, int z) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getBlockAt'" - ); + var request = patchbukkit.block.GetBlockRequest.newBuilder() + .setWorld(patchbukkit.common.World.newBuilder() + .setUuid(org.patchbukkit.bridge.BridgeUtils.convertUuid(this.uuid)) + .build()) + .setX(x) + .setY(y) + .setZ(z) + .build(); + var response = patchbukkit.bridge.NativeBridgeFfi.getBlock(request); + String blockKey = response != null ? response.getBlockKey() : "minecraft:air"; + return PatchBukkitBlock.create(this, x, y, z, blockKey); } @Override diff --git a/proto/google/protobuf/empty.proto b/proto/google/protobuf/empty.proto new file mode 100644 index 0000000..47eb5aa --- /dev/null +++ b/proto/google/protobuf/empty.proto @@ -0,0 +1,9 @@ +syntax = "proto3"; + +package google.protobuf; + +option java_package = "com.google.protobuf"; +option java_outer_classname = "EmptyProto"; +option java_multiple_files = true; + +message Empty {} diff --git a/proto/patchbukkit/block.proto b/proto/patchbukkit/block.proto new file mode 100644 index 0000000..f3ab319 --- /dev/null +++ b/proto/patchbukkit/block.proto @@ -0,0 +1,18 @@ +syntax = "proto3"; +package patchbukkit.block; + +import "patchbukkit/common/types.proto"; + +option java_multiple_files = true; +option java_package = "patchbukkit.block"; + +message GetBlockRequest { + patchbukkit.common.World world = 1; + int32 x = 2; + int32 y = 3; + int32 z = 4; +} + +message GetBlockResponse { + string block_key = 1; +} diff --git a/proto/patchbukkit/bridge.proto b/proto/patchbukkit/bridge.proto index 5338248..ad5f8ab 100644 --- a/proto/patchbukkit/bridge.proto +++ b/proto/patchbukkit/bridge.proto @@ -2,8 +2,8 @@ syntax = "proto3"; package patchbukkit.bridge; import "google/protobuf/empty.proto"; -import "google/protobuf/wrappers.proto"; import "patchbukkit/abilities.proto"; +import "patchbukkit/block.proto"; import "patchbukkit/common/types.proto"; import "patchbukkit/events/event.proto"; import "patchbukkit/message.proto"; @@ -21,6 +21,8 @@ service NativeBridge { rpc GetLocation(patchbukkit.common.UUID) returns (patchbukkit.common.Location); + rpc GetBlock(patchbukkit.block.GetBlockRequest) returns (patchbukkit.block.GetBlockResponse); + rpc GetRegistryData(patchbukkit.registry.GetRegistryDataRequest) returns (patchbukkit.registry.GetRegistryDataResponse); rpc PlayerEntityPlaySound(patchbukkit.sound.PlayerEntityPlaySoundRequest) returns (google.protobuf.Empty); diff --git a/proto/patchbukkit/events/block_events.proto b/proto/patchbukkit/events/block_events.proto new file mode 100644 index 0000000..13ef896 --- /dev/null +++ b/proto/patchbukkit/events/block_events.proto @@ -0,0 +1,238 @@ +syntax = "proto3"; +package patchbukkit.events; + +import "patchbukkit/common/types.proto"; + +option java_multiple_files = true; +option java_package = "patchbukkit.events"; + +message BlockBreakEvent { + patchbukkit.common.UUID player_uuid = 1; + string block_key = 2; + patchbukkit.common.Location location = 3; + uint32 exp = 4; + bool drop = 5; +} + +message BlockDamageEvent { + patchbukkit.common.UUID player_uuid = 1; + string block_key = 2; + patchbukkit.common.Location location = 3; + string item_key = 4; + bool insta_break = 5; +} + +message BlockDamageAbortEvent { + patchbukkit.common.UUID player_uuid = 1; + string block_key = 2; + patchbukkit.common.Location location = 3; + string item_key = 4; +} + +message BlockDispenseEvent { + string block_key = 1; + patchbukkit.common.Location location = 2; + string item_key = 3; + int32 item_amount = 4; + patchbukkit.common.Vec3 velocity = 5; +} + +message BlockDropItemEntry { + string item_key = 1; + int32 item_amount = 2; +} + +message BlockDropItemEvent { + patchbukkit.common.UUID player_uuid = 1; + string block_key = 2; + patchbukkit.common.Location location = 3; + repeated BlockDropItemEntry items = 4; +} + +message BlockExplodeBlockEntry { + string block_key = 1; + patchbukkit.common.Location location = 2; +} + +message BlockExplodeEvent { + string block_key = 1; + patchbukkit.common.Location location = 2; + float yield = 3; + repeated BlockExplodeBlockEntry blocks = 4; +} + +message BlockFadeEvent { + string block_key = 1; + string new_block_key = 2; + patchbukkit.common.Location location = 3; +} + +message BlockFertilizeBlockEntry { + string block_key = 1; + patchbukkit.common.Location location = 2; +} + +message BlockFertilizeEvent { + patchbukkit.common.UUID player_uuid = 1; + string block_key = 2; + patchbukkit.common.Location location = 3; + repeated BlockFertilizeBlockEntry blocks = 4; +} + +message BlockFormEvent { + string block_key = 1; + string new_block_key = 2; + patchbukkit.common.Location location = 3; +} + +message BlockFromToEvent { + string block_key = 1; + patchbukkit.common.Location location = 2; + string to_block_key = 3; + patchbukkit.common.Location to_location = 4; + string face = 5; +} + +message BlockGrowEvent { + string block_key = 1; + string new_block_key = 2; + patchbukkit.common.Location location = 3; +} + +message BlockPistonBlockEntry { + string block_key = 1; + patchbukkit.common.Location location = 2; +} + +message BlockPistonExtendEvent { + string block_key = 1; + patchbukkit.common.Location location = 2; + string direction = 3; + int32 length = 4; + repeated BlockPistonBlockEntry blocks = 5; +} + +message BlockPistonRetractEvent { + string block_key = 1; + patchbukkit.common.Location location = 2; + string direction = 3; + int32 length = 4; + repeated BlockPistonBlockEntry blocks = 5; +} + +message BlockRedstoneEvent { + string block_key = 1; + patchbukkit.common.Location location = 2; + int32 old_current = 3; + int32 new_current = 4; +} + +message BlockMultiPlaceBlockEntry { + string block_key = 1; + patchbukkit.common.Location location = 2; +} + +message BlockMultiPlaceEvent { + patchbukkit.common.UUID player_uuid = 1; + string block_key = 2; + patchbukkit.common.Location location = 3; + repeated BlockMultiPlaceBlockEntry blocks = 4; +} + +message BlockPhysicsEvent { + string block_key = 1; + patchbukkit.common.Location location = 2; + string source_block_key = 3; + patchbukkit.common.Location source_location = 4; +} + +message BlockPlaceEvent { + patchbukkit.common.UUID player_uuid = 1; + string block_key = 2; + string block_against_key = 3; + patchbukkit.common.Location location = 4; + bool can_build = 5; +} + +message BlockCanBuildEvent { + patchbukkit.common.UUID player_uuid = 1; + string block_key = 2; + string block_against_key = 3; + patchbukkit.common.Location location = 4; + bool can_build = 5; +} + +message BlockBurnEvent { + string block_key = 1; + string igniting_block_key = 2; + patchbukkit.common.Location location = 3; +} + +message BlockIgniteEvent { + patchbukkit.common.UUID player_uuid = 1; + string block_key = 2; + string igniting_block_key = 3; + patchbukkit.common.Location location = 4; + string cause = 5; +} + +message BlockSpreadEvent { + string source_block_key = 1; + patchbukkit.common.Location source_location = 2; + string block_key = 3; + patchbukkit.common.Location location = 4; +} + +message PlayerInteractEvent { + patchbukkit.common.UUID player_uuid = 1; + string action = 2; + string block_key = 3; + patchbukkit.common.Location clicked = 4; + string item_key = 5; + string block_face = 6; +} + +message NotePlayEvent { + string block_key = 1; + patchbukkit.common.Location location = 2; + string instrument = 3; + int32 note = 4; +} + +message SignChangeEvent { + patchbukkit.common.UUID player_uuid = 1; + string block_key = 2; + patchbukkit.common.Location location = 3; + repeated string lines = 4; + bool is_front_text = 5; +} + +message TNTPrimeEvent { + patchbukkit.common.UUID player_uuid = 1; + string block_key = 2; + patchbukkit.common.Location location = 3; + string cause = 4; +} + +message MoistureChangeEvent { + string block_key = 1; + patchbukkit.common.Location location = 2; + string new_block_key = 3; +} + +message SpongeAbsorbBlockEntry { + string block_key = 1; + patchbukkit.common.Location location = 2; +} + +message SpongeAbsorbEvent { + string block_key = 1; + patchbukkit.common.Location location = 2; + repeated SpongeAbsorbBlockEntry blocks = 3; +} + +message FluidLevelChangeEvent { + string block_key = 1; + patchbukkit.common.Location location = 2; + string new_block_key = 3; +} diff --git a/proto/patchbukkit/events/entity_events.proto b/proto/patchbukkit/events/entity_events.proto new file mode 100644 index 0000000..85dd61c --- /dev/null +++ b/proto/patchbukkit/events/entity_events.proto @@ -0,0 +1,23 @@ +syntax = "proto3"; +package patchbukkit.events; + +import "patchbukkit/common/types.proto"; + +option java_multiple_files = true; +option java_package = "patchbukkit.events"; + +message EntitySpawnEvent { + patchbukkit.common.UUID entity_uuid = 1; + string entity_type = 2; +} + +message EntityDamageEvent { + patchbukkit.common.UUID entity_uuid = 1; + float damage = 2; + string damage_type = 3; +} + +message EntityDeathEvent { + patchbukkit.common.UUID entity_uuid = 1; + string damage_type = 2; +} diff --git a/proto/patchbukkit/events/event.proto b/proto/patchbukkit/events/event.proto index f678892..0536dd6 100644 --- a/proto/patchbukkit/events/event.proto +++ b/proto/patchbukkit/events/event.proto @@ -4,6 +4,10 @@ package patchbukkit.events; import "patchbukkit/common/types.proto"; import "patchbukkit/events/player_events.proto"; +import "patchbukkit/events/server_events.proto"; +import "patchbukkit/events/block_events.proto"; +import "patchbukkit/events/entity_events.proto"; +import "patchbukkit/events/world_events.proto"; option java_multiple_files = true; option java_package = "patchbukkit.events"; @@ -11,6 +15,95 @@ option java_package = "patchbukkit.events"; message Event { oneof data { PlayerJoinEvent player_join = 1; + PlayerLoginEvent player_login = 2; + PlayerLeaveEvent player_leave = 3; + PlayerMoveEvent player_move = 4; + PlayerChatEvent player_chat = 5; + PlayerCommandEvent player_command = 6; + PlayerTeleportEvent player_teleport = 7; + PlayerChangeWorldEvent player_change_world = 8; + PlayerGamemodeChangeEvent player_gamemode_change = 9; + ServerCommandEvent server_command = 10; + ServerBroadcastEvent server_broadcast = 11; + BlockBreakEvent block_break = 12; + BlockPlaceEvent block_place = 13; + PlayerInteractEvent player_interact = 14; + EntitySpawnEvent entity_spawn = 15; + EntityDamageEvent entity_damage = 16; + EntityDeathEvent entity_death = 17; + AsyncPlayerPreLoginEvent async_player_pre_login = 18; + PlayerAdvancementDoneEvent player_advancement_done = 19; + PlayerAnimationEvent player_animation = 20; + PlayerArmorStandManipulateEvent player_armor_stand_manipulate = 21; + PlayerBedEnterEvent player_bed_enter = 22; + PlayerBedLeaveEvent player_bed_leave = 23; + PlayerBucketEmptyEvent player_bucket_empty = 24; + PlayerBucketFillEvent player_bucket_fill = 25; + PlayerBucketEntityEvent player_bucket_entity = 26; + PlayerChangedMainHandEvent player_changed_main_hand = 27; + PlayerRegisterChannelEvent player_register_channel = 28; + PlayerUnregisterChannelEvent player_unregister_channel = 29; + PlayerCommandSendEvent player_command_send = 30; + PlayerDropItemEvent player_drop_item = 31; + PlayerEditBookEvent player_edit_book = 32; + PlayerEggThrowEvent player_egg_throw = 33; + PlayerExpChangeEvent player_exp_change = 34; + PlayerFishEvent player_fish = 35; + PlayerInteractEntityEvent player_interact_entity = 36; + PlayerInteractAtEntityEvent player_interact_at_entity = 37; + PlayerItemHeldEvent player_item_held = 38; + PlayerItemDamageEvent player_item_damage = 39; + PlayerItemBreakEvent player_item_break = 40; + PlayerItemConsumeEvent player_item_consume = 41; + PlayerItemMendEvent player_item_mend = 42; + PlayerLevelChangeEvent player_level_change = 43; + PlayerKickEvent player_kick = 44; + PlayerToggleSneakEvent player_toggle_sneak = 45; + PlayerToggleSprintEvent player_toggle_sprint = 46; + PlayerToggleFlightEvent player_toggle_flight = 47; + PlayerSwapHandItemsEvent player_swap_hand_items = 48; + PlayerResourcePackStatusEvent player_resource_pack_status = 49; + PlayerRespawnEvent player_respawn = 50; + PlayerPickupArrowEvent player_pickup_arrow = 51; + PlayerPortalEvent player_portal = 52; + PlayerRecipeDiscoverEvent player_recipe_discover = 53; + PlayerRiptideEvent player_riptide = 54; + PlayerShearEntityEvent player_shear_entity = 55; + PlayerSpawnLocationEvent player_spawn_location = 56; + PlayerStatisticIncrementEvent player_statistic_increment = 57; + PlayerVelocityEvent player_velocity = 58; + PlayerHarvestBlockEvent player_harvest_block = 59; + BlockCanBuildEvent block_can_build = 60; + BlockBurnEvent block_burn = 61; + BlockIgniteEvent block_ignite = 62; + BlockSpreadEvent block_spread = 63; + BlockDamageEvent block_damage = 64; + BlockDamageAbortEvent block_damage_abort = 65; + BlockDispenseEvent block_dispense = 66; + BlockDropItemEvent block_drop_item = 67; + BlockExplodeEvent block_explode = 68; + BlockFadeEvent block_fade = 69; + BlockFertilizeEvent block_fertilize = 70; + BlockFormEvent block_form = 71; + BlockFromToEvent block_from_to = 72; + BlockGrowEvent block_grow = 73; + BlockPistonExtendEvent block_piston_extend = 74; + BlockPistonRetractEvent block_piston_retract = 75; + BlockRedstoneEvent block_redstone = 76; + BlockMultiPlaceEvent block_multi_place = 77; + BlockPhysicsEvent block_physics = 78; + NotePlayEvent note_play = 79; + SignChangeEvent sign_change = 80; + TNTPrimeEvent tnt_prime = 81; + MoistureChangeEvent moisture_change = 82; + SpongeAbsorbEvent sponge_absorb = 83; + FluidLevelChangeEvent fluid_level_change = 84; + SpawnChangeEvent spawn_change = 85; + ServerListPingEvent server_list_ping = 86; + PluginEnableEvent plugin_enable = 87; + PluginDisableEvent plugin_disable = 88; + ServiceRegisterEvent service_register = 89; + ServiceUnregisterEvent service_unregister = 90; } } diff --git a/proto/patchbukkit/events/player_events.proto b/proto/patchbukkit/events/player_events.proto index cd92e96..612f57d 100644 --- a/proto/patchbukkit/events/player_events.proto +++ b/proto/patchbukkit/events/player_events.proto @@ -10,3 +10,342 @@ message PlayerJoinEvent { patchbukkit.common.UUID player_uuid = 1; string join_message = 2; } + +message PlayerLoginEvent { + patchbukkit.common.UUID player_uuid = 1; + string kick_message = 2; +} + +message PlayerLeaveEvent { + patchbukkit.common.UUID player_uuid = 1; + string leave_message = 2; +} + +message PlayerMoveEvent { + patchbukkit.common.UUID player_uuid = 1; + patchbukkit.common.Location from = 2; + patchbukkit.common.Location to = 3; +} + +message PlayerTeleportEvent { + patchbukkit.common.UUID player_uuid = 1; + patchbukkit.common.Location from = 2; + patchbukkit.common.Location to = 3; + string cause = 4; +} + +message PlayerChangeWorldEvent { + patchbukkit.common.UUID player_uuid = 1; + patchbukkit.common.World previous_world = 2; + patchbukkit.common.World new_world = 3; + patchbukkit.common.Location position = 4; + float yaw = 5; + float pitch = 6; +} + +message PlayerGamemodeChangeEvent { + patchbukkit.common.UUID player_uuid = 1; + string previous_gamemode = 2; + string new_gamemode = 3; +} + +message PlayerChatEvent { + patchbukkit.common.UUID player_uuid = 1; + string message = 2; + repeated patchbukkit.common.UUID recipients = 3; +} + +message PlayerCommandEvent { + patchbukkit.common.UUID player_uuid = 1; + string command = 2; +} + +message PlayerCommandSendEvent { + patchbukkit.common.UUID player_uuid = 1; + repeated string commands = 2; +} + +message PlayerDropItemEvent { + patchbukkit.common.UUID player_uuid = 1; + patchbukkit.common.UUID item_uuid = 2; + string item_key = 3; + int32 item_amount = 4; +} + +message PlayerEditBookEvent { + patchbukkit.common.UUID player_uuid = 1; + int32 slot = 2; + repeated string pages = 3; + string title = 4; + bool is_signing = 5; +} + +message PlayerEggThrowEvent { + patchbukkit.common.UUID player_uuid = 1; + patchbukkit.common.UUID egg_uuid = 2; + bool hatching = 3; + int32 num_hatches = 4; + string hatching_type = 5; +} + +message PlayerExpChangeEvent { + patchbukkit.common.UUID player_uuid = 1; + int32 amount = 2; +} + +message PlayerFishEvent { + patchbukkit.common.UUID player_uuid = 1; + patchbukkit.common.UUID caught_uuid = 2; + string caught_type = 3; + patchbukkit.common.UUID hook_uuid = 4; + string state = 5; + string hand = 6; + int32 exp_to_drop = 7; +} + +message PlayerInteractEntityEvent { + patchbukkit.common.UUID player_uuid = 1; + patchbukkit.common.UUID entity_uuid = 2; + string entity_type = 3; + string hand = 4; +} + +message PlayerInteractAtEntityEvent { + patchbukkit.common.UUID player_uuid = 1; + patchbukkit.common.UUID entity_uuid = 2; + string entity_type = 3; + string hand = 4; + patchbukkit.common.Vec3 clicked_position = 5; +} + +message PlayerItemHeldEvent { + patchbukkit.common.UUID player_uuid = 1; + int32 previous_slot = 2; + int32 new_slot = 3; +} + +message PlayerItemDamageEvent { + patchbukkit.common.UUID player_uuid = 1; + string item_key = 2; + int32 item_amount = 3; + int32 damage = 4; +} + +message PlayerItemBreakEvent { + patchbukkit.common.UUID player_uuid = 1; + string item_key = 2; + int32 item_amount = 3; +} + +message PlayerItemConsumeEvent { + patchbukkit.common.UUID player_uuid = 1; + string item_key = 2; + int32 item_amount = 3; + string hand = 4; +} + +message PlayerItemMendEvent { + patchbukkit.common.UUID player_uuid = 1; + string item_key = 2; + int32 item_amount = 3; + string slot = 4; + int32 repair_amount = 5; + patchbukkit.common.UUID orb_uuid = 6; +} + +message PlayerLevelChangeEvent { + patchbukkit.common.UUID player_uuid = 1; + int32 old_level = 2; + int32 new_level = 3; +} + +message PlayerKickEvent { + patchbukkit.common.UUID player_uuid = 1; + string reason = 2; + string leave_message = 3; + string cause = 4; +} + +message PlayerToggleSneakEvent { + patchbukkit.common.UUID player_uuid = 1; + bool is_sneaking = 2; +} + +message PlayerToggleSprintEvent { + patchbukkit.common.UUID player_uuid = 1; + bool is_sprinting = 2; +} + +message PlayerToggleFlightEvent { + patchbukkit.common.UUID player_uuid = 1; + bool is_flying = 2; +} + +message PlayerSwapHandItemsEvent { + patchbukkit.common.UUID player_uuid = 1; + string main_hand_item_key = 2; + int32 main_hand_item_amount = 3; + string off_hand_item_key = 4; + int32 off_hand_item_amount = 5; +} + +message PlayerResourcePackStatusEvent { + patchbukkit.common.UUID player_uuid = 1; + patchbukkit.common.UUID pack_uuid = 2; + string status = 3; + string hash = 4; +} + +message PlayerRespawnEvent { + patchbukkit.common.UUID player_uuid = 1; + patchbukkit.common.Location respawn_location = 2; + bool is_bed_spawn = 3; + bool is_anchor_spawn = 4; + bool is_missing_respawn_block = 5; + string respawn_reason = 6; +} + +message PlayerPickupArrowEvent { + patchbukkit.common.UUID player_uuid = 1; + patchbukkit.common.UUID arrow_uuid = 2; + patchbukkit.common.UUID item_uuid = 3; + string item_key = 4; + int32 item_amount = 5; + int32 remaining = 6; +} + +message PlayerPortalEvent { + patchbukkit.common.UUID player_uuid = 1; + patchbukkit.common.Location from = 2; + patchbukkit.common.Location to = 3; + string cause = 4; + int32 search_radius = 5; + bool can_create_portal = 6; + int32 creation_radius = 7; +} + +message PlayerRecipeDiscoverEvent { + patchbukkit.common.UUID player_uuid = 1; + string recipe_key = 2; +} + +message PlayerRiptideEvent { + patchbukkit.common.UUID player_uuid = 1; + string item_key = 2; + int32 item_amount = 3; + patchbukkit.common.Vec3 velocity = 4; +} + +message PlayerShearEntityEvent { + patchbukkit.common.UUID player_uuid = 1; + patchbukkit.common.UUID entity_uuid = 2; + string entity_type = 3; + string item_key = 4; + int32 item_amount = 5; + string hand = 6; +} + +message PlayerSpawnLocationEvent { + patchbukkit.common.UUID player_uuid = 1; + patchbukkit.common.Location spawn_location = 2; +} + +message PlayerStatisticIncrementEvent { + patchbukkit.common.UUID player_uuid = 1; + string statistic = 2; + int32 initial_value = 3; + int32 new_value = 4; + string entity_type = 5; + string material_key = 6; +} + +message PlayerVelocityEvent { + patchbukkit.common.UUID player_uuid = 1; + patchbukkit.common.Vec3 velocity = 2; +} + +message PlayerHarvestBlockEvent { + patchbukkit.common.UUID player_uuid = 1; + patchbukkit.common.Location block_location = 2; + string block_key = 3; + string item_key = 4; + int32 item_amount = 5; +} + +message AsyncPlayerPreLoginEvent { + string name = 1; + patchbukkit.common.UUID player_uuid = 2; + string address = 3; + string result = 4; + string kick_message = 5; +} + +message PlayerAdvancementDoneEvent { + patchbukkit.common.UUID player_uuid = 1; + string advancement_key = 2; +} + +message PlayerAnimationEvent { + patchbukkit.common.UUID player_uuid = 1; + string animation_type = 2; +} + +message PlayerArmorStandManipulateEvent { + patchbukkit.common.UUID player_uuid = 1; + patchbukkit.common.UUID armor_stand_uuid = 2; + string item_key = 3; + string armor_stand_item_key = 4; + string slot = 5; +} + +message PlayerBedEnterEvent { + patchbukkit.common.UUID player_uuid = 1; + patchbukkit.common.Location bed_location = 2; +} + +message PlayerBedLeaveEvent { + patchbukkit.common.UUID player_uuid = 1; + patchbukkit.common.Location bed_location = 2; +} + +message PlayerBucketEmptyEvent { + patchbukkit.common.UUID player_uuid = 1; + patchbukkit.common.Location location = 2; + string block_key = 3; + string block_face = 4; + string bucket_item_key = 5; + string hand = 6; +} + +message PlayerBucketFillEvent { + patchbukkit.common.UUID player_uuid = 1; + patchbukkit.common.Location location = 2; + string block_key = 3; + string block_face = 4; + string bucket_item_key = 5; + string hand = 6; +} + +message PlayerBucketEntityEvent { + patchbukkit.common.UUID player_uuid = 1; + patchbukkit.common.UUID entity_uuid = 2; + string entity_type = 3; + string original_bucket_key = 4; + string entity_bucket_key = 5; + string hand = 6; +} + +message PlayerChangedMainHandEvent { + patchbukkit.common.UUID player_uuid = 1; + string main_hand = 2; +} + +message PlayerRegisterChannelEvent { + patchbukkit.common.UUID player_uuid = 1; + string channel = 2; +} + +message PlayerUnregisterChannelEvent { + patchbukkit.common.UUID player_uuid = 1; + string channel = 2; +} diff --git a/proto/patchbukkit/events/server_events.proto b/proto/patchbukkit/events/server_events.proto new file mode 100644 index 0000000..5c0d783 --- /dev/null +++ b/proto/patchbukkit/events/server_events.proto @@ -0,0 +1,41 @@ +syntax = "proto3"; +package patchbukkit.events; + +import "patchbukkit/common/types.proto"; + +option java_multiple_files = true; +option java_package = "patchbukkit.events"; + +message ServerCommandEvent { + string command = 1; +} + +message ServerBroadcastEvent { + string message = 1; + string sender = 2; +} + +message ServerListPingEvent { + string motd = 1; + int32 max_players = 2; + int32 online_players = 3; + string favicon = 4; +} + +message PluginEnableEvent { + string plugin_name = 1; +} + +message PluginDisableEvent { + string plugin_name = 1; +} + +message ServiceRegisterEvent { + string plugin_name = 1; + string service_name = 2; +} + +message ServiceUnregisterEvent { + string plugin_name = 1; + string service_name = 2; +} diff --git a/proto/patchbukkit/events/world_events.proto b/proto/patchbukkit/events/world_events.proto new file mode 100644 index 0000000..4c59dda --- /dev/null +++ b/proto/patchbukkit/events/world_events.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; +package patchbukkit.events; + +import "patchbukkit/common/types.proto"; + +option java_multiple_files = true; +option java_package = "patchbukkit.events"; + +message SpawnChangeEvent { + patchbukkit.common.Location previous_location = 1; + patchbukkit.common.Location location = 2; +} diff --git a/rust/Cargo.lock b/rust/Cargo.lock index af1e654..b14a5b5 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -50,12 +50,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "allocator-api2" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" - [[package]] name = "annotate-snippets" version = "0.12.11" @@ -322,13 +316,13 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chacha20" -version = "0.10.0-rc.9" +version = "0.10.0-rc.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c81d916c6ae06736ec667b51f95ee5ff660a75f4ea6ce1bd932c942365c0ea43" +checksum = "14f3c04cdd19f5ed3271e460267fb1d94b7899ae1f7bb10ac3dfc3afb749d659" dependencies = [ "cfg-if", "cpufeatures", - "rand_core 0.10.0-rc-6", + "rand_core 0.10.0", ] [[package]] @@ -549,9 +543,9 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crossfire" -version = "3.0.3" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9c756fd351d7bc1f95698a33a90ce39c8f7458335cc7e95132d3d36124902a" +checksum = "86db11309bffee9435040d648c13c3efbdbcdcd3ead9872e65ce9dff4c3f79ce" dependencies = [ "crossbeam-utils", "futures-core", @@ -579,7 +573,7 @@ checksum = "053c3561863ce55e3226ecc48b08679f4b66cb1b92b9afb42c2c402dfe8b9b51" dependencies = [ "ctutils", "num-traits", - "rand_core 0.10.0-rc-6", + "rand_core 0.10.0", "serdect", "zeroize", ] @@ -611,7 +605,7 @@ checksum = "334a79c97c0b7fa536716dc132fd417d0afbf471440a41fc25a5d9f66d8771cd" dependencies = [ "crypto-bigint 0.7.0-rc.22", "libm", - "rand_core 0.10.0-rc-6", + "rand_core 0.10.0", ] [[package]] @@ -955,9 +949,9 @@ checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" [[package]] name = "flate2" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b375d6465b98090a5f25b1c7703f3859783755aa9a80433b36e0379a3ec2f369" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" dependencies = [ "crc32fast", "miniz_oxide", @@ -976,12 +970,6 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" -[[package]] -name = "foldhash" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" - [[package]] name = "form_urlencoded" version = "1.2.2" @@ -1153,7 +1141,7 @@ dependencies = [ "cfg-if", "libc", "r-efi", - "rand_core 0.10.0-rc-6", + "rand_core 0.10.0", "wasip2", "wasip3", ] @@ -1193,7 +1181,7 @@ version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ - "foldhash 0.1.5", + "foldhash", ] [[package]] @@ -1201,11 +1189,6 @@ name = "hashbrown" version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" -dependencies = [ - "allocator-api2", - "equivalent", - "foldhash 0.2.0", -] [[package]] name = "heck" @@ -1631,15 +1614,6 @@ version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" -[[package]] -name = "lru" -version = "0.16.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1dc47f592c06f33f8e3aea9591776ec7c9f9e4124778ff8a3c3b87159f7e593" -dependencies = [ - "hashbrown 0.16.1", -] - [[package]] name = "lz4-java-wrc" version = "0.2.0" @@ -1921,6 +1895,7 @@ dependencies = [ "pumpkin-data", "pumpkin-protocol", "pumpkin-util", + "pumpkin-world", "rand", "rust-embed", "serde", @@ -2251,13 +2226,12 @@ dependencies = [ [[package]] name = "pumpkin" version = "0.1.0-dev+1.21.11" -source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#441040105bb8ceb8b118bf84039727e6423046aa" +source = "git+https://github.com/yunuservices/Pumpkin.git?branch=feat%2Fevents#ba0410c324819ba5cfb693ac722e3a0bb3bab378" dependencies = [ "arc-swap", "base64", "bytes", "crossbeam", - "dashmap", "flate2", "futures", "hmac 0.13.0-rc.4", @@ -2281,7 +2255,6 @@ dependencies = [ "serde_json", "sha1 0.11.0-rc.4", "sha2 0.11.0-rc.4", - "signature 2.2.0", "simplelog", "thiserror", "time", @@ -2294,7 +2267,7 @@ dependencies = [ [[package]] name = "pumpkin-api-macros" version = "0.1.0-dev+1.21.11" -source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#441040105bb8ceb8b118bf84039727e6423046aa" +source = "git+https://github.com/yunuservices/Pumpkin.git?branch=feat%2Fevents#ba0410c324819ba5cfb693ac722e3a0bb3bab378" dependencies = [ "proc-macro-error2", "proc-macro2", @@ -2305,7 +2278,7 @@ dependencies = [ [[package]] name = "pumpkin-config" version = "0.1.0-dev+1.21.11" -source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#441040105bb8ceb8b118bf84039727e6423046aa" +source = "git+https://github.com/yunuservices/Pumpkin.git?branch=feat%2Fevents#ba0410c324819ba5cfb693ac722e3a0bb3bab378" dependencies = [ "log", "pumpkin-util", @@ -2317,7 +2290,7 @@ dependencies = [ [[package]] name = "pumpkin-data" version = "0.1.0-dev+1.21.11" -source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#441040105bb8ceb8b118bf84039727e6423046aa" +source = "git+https://github.com/yunuservices/Pumpkin.git?branch=feat%2Fevents#ba0410c324819ba5cfb693ac722e3a0bb3bab378" dependencies = [ "crc-fast", "heck", @@ -2337,7 +2310,7 @@ dependencies = [ [[package]] name = "pumpkin-inventory" version = "0.1.0-dev+1.21.11" -source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#441040105bb8ceb8b118bf84039727e6423046aa" +source = "git+https://github.com/yunuservices/Pumpkin.git?branch=feat%2Fevents#ba0410c324819ba5cfb693ac722e3a0bb3bab378" dependencies = [ "crossbeam-utils", "log", @@ -2352,10 +2325,9 @@ dependencies = [ [[package]] name = "pumpkin-macros" version = "0.1.0-dev+1.21.11" -source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#441040105bb8ceb8b118bf84039727e6423046aa" +source = "git+https://github.com/yunuservices/Pumpkin.git?branch=feat%2Fevents#ba0410c324819ba5cfb693ac722e3a0bb3bab378" dependencies = [ "proc-macro-error2", - "proc-macro2", "pumpkin-data", "quote", "syn", @@ -2364,7 +2336,7 @@ dependencies = [ [[package]] name = "pumpkin-nbt" version = "0.1.0-dev+1.21.11" -source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#441040105bb8ceb8b118bf84039727e6423046aa" +source = "git+https://github.com/yunuservices/Pumpkin.git?branch=feat%2Fevents#ba0410c324819ba5cfb693ac722e3a0bb3bab378" dependencies = [ "bytes", "cesu8", @@ -2376,7 +2348,7 @@ dependencies = [ [[package]] name = "pumpkin-protocol" version = "0.1.0-dev+1.21.11" -source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#441040105bb8ceb8b118bf84039727e6423046aa" +source = "git+https://github.com/yunuservices/Pumpkin.git?branch=feat%2Fevents#ba0410c324819ba5cfb693ac722e3a0bb3bab378" dependencies = [ "aes", "async-compression", @@ -2398,7 +2370,7 @@ dependencies = [ [[package]] name = "pumpkin-util" version = "0.1.0-dev+1.21.11" -source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#441040105bb8ceb8b118bf84039727e6423046aa" +source = "git+https://github.com/yunuservices/Pumpkin.git?branch=feat%2Fevents#ba0410c324819ba5cfb693ac722e3a0bb3bab378" dependencies = [ "base64", "bytes", @@ -2423,7 +2395,7 @@ dependencies = [ [[package]] name = "pumpkin-world" version = "0.1.0-dev+1.21.11" -source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#441040105bb8ceb8b118bf84039727e6423046aa" +source = "git+https://github.com/yunuservices/Pumpkin.git?branch=feat%2Fevents#ba0410c324819ba5cfb693ac722e3a0bb3bab378" dependencies = [ "bitflags", "bytes", @@ -2435,7 +2407,6 @@ dependencies = [ "futures", "itertools", "log", - "lru", "lz4-java-wrc", "num-derive", "num-traits", @@ -2452,7 +2423,6 @@ dependencies = [ "sha2 0.11.0-rc.4", "slotmap", "thiserror", - "thread_local", "tokio", "tokio-util", "uuid", @@ -2485,12 +2455,12 @@ dependencies = [ [[package]] name = "rand" -version = "0.10.0-rc.8" -source = "git+https://github.com/rust-random/rand#a4c9a8e565fa310de423e3ba89593b647b721e8c" +version = "0.10.0-rc.9" +source = "git+https://github.com/rust-random/rand#72afe1e973fcd83d840cf597888223072bbdb04c" dependencies = [ "chacha20", "getrandom 0.4.0-rc.1", - "rand_core 0.10.0-rc-6", + "rand_core 0.10.0", ] [[package]] @@ -2504,9 +2474,9 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.10.0-rc-6" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70765ff7112b0fb2d272d24d9a2f907fc206211304328fe58b2db15a5649ef28" +checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba" [[package]] name = "rayon" @@ -2613,7 +2583,7 @@ dependencies = [ "digest 0.11.0-rc.9", "pkcs1", "pkcs8 0.11.0-rc.10", - "rand_core 0.10.0-rc-6", + "rand_core 0.10.0", "sha1 0.11.0-rc.4", "signature 3.0.0-rc.9", "spki 0.8.0-rc.4", @@ -2989,7 +2959,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ad0ce3b3f8efd7406f22e2ca5d02be21cdf3b3d1d53ab141f784de8965c7c7e" dependencies = [ "digest 0.11.0-rc.9", - "rand_core 0.10.0-rc-6", + "rand_core 0.10.0", ] [[package]] @@ -3214,15 +3184,6 @@ dependencies = [ "syn", ] -[[package]] -name = "thread_local" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" -dependencies = [ - "cfg-if", -] - [[package]] name = "time" version = "0.3.46" @@ -3434,9 +3395,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "ureq" -version = "3.1.4" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d39cb1dbab692d82a977c0392ffac19e188bd9186a9f32806f0aaa859d75585a" +checksum = "fdc97a28575b85cfedf2a7e7d3cc64b3e11bd8ac766666318003abbacc7a21fc" dependencies = [ "base64", "cookie_store", @@ -4120,9 +4081,9 @@ dependencies = [ [[package]] name = "zlib-rs" -version = "0.5.5" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40990edd51aae2c2b6907af74ffb635029d5788228222c4bb811e9351c0caad3" +checksum = "a7948af682ccbc3342b6e9420e8c51c1fe5d7bf7756002b4a3c6cabfe96a7e3c" [[package]] name = "zmij" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 65178bf..ea94e55 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -9,11 +9,12 @@ build = "build/main.rs" crate-type = ["cdylib"] [dependencies] -pumpkin = { git = "https://github.com/Pumpkin-MC/Pumpkin.git", branch = "master", package = "pumpkin" } -pumpkin-data = { git = "https://github.com/Pumpkin-MC/Pumpkin.git", branch = "master", package = "pumpkin-data" } -pumpkin-protocol = { git = "https://github.com/Pumpkin-MC/Pumpkin.git", branch = "master", package = "pumpkin-protocol" } -pumpkin-util = { git = "https://github.com/Pumpkin-MC/Pumpkin.git", branch = "master", package = "pumpkin-util" } -pumpkin-api-macros = { git = "https://github.com/Pumpkin-MC/Pumpkin.git", branch = "master", package = "pumpkin-api-macros" } +pumpkin = { git = "https://github.com/yunuservices/Pumpkin.git", branch = "feat/events", package = "pumpkin" } +pumpkin-data = { git = "https://github.com/yunuservices/Pumpkin.git", branch = "feat/events", package = "pumpkin-data" } +pumpkin-protocol = { git = "https://github.com/yunuservices/Pumpkin.git", branch = "feat/events", package = "pumpkin-protocol" } +pumpkin-util = { git = "https://github.com/yunuservices/Pumpkin.git", branch = "feat/events", package = "pumpkin-util" } +pumpkin-world = { git = "https://github.com/yunuservices/Pumpkin.git", branch = "feat/events", package = "pumpkin-world" } +pumpkin-api-macros = { git = "https://github.com/yunuservices/Pumpkin.git", branch = "feat/events", package = "pumpkin-api-macros" } tokio = "1" log = "0.4" diff --git a/rust/build/java.rs b/rust/build/java.rs index 4cd8795..f1f17f9 100644 --- a/rust/build/java.rs +++ b/rust/build/java.rs @@ -91,6 +91,13 @@ pub fn setup_java(base: PathBuf) { // These are deps of patchbukkit itself ("io.papermc.paper", "paper-api", "1.21.11-R0.1-SNAPSHOT"), ("com.google.protobuf", "protobuf-java", "4.33.5"), + // Runtime plugin classpath deps (resolved into jassets), including Netty for ViaVersion/ViaBackwards. + ("io.netty", "netty-common", "4.1.108.Final"), + ("io.netty", "netty-buffer", "4.1.108.Final"), + ("io.netty", "netty-transport", "4.1.108.Final"), + ("io.netty", "netty-codec", "4.1.108.Final"), + ("io.netty", "netty-handler", "4.1.108.Final"), + ("io.netty", "netty-resolver", "4.1.108.Final"), ]; let jvm = JvmBuilder::new() diff --git a/rust/src/commands/mod.rs b/rust/src/commands/mod.rs index 13598b1..5a1c34a 100644 --- a/rust/src/commands/mod.rs +++ b/rust/src/commands/mod.rs @@ -133,7 +133,7 @@ impl CommandExecutor for JavaCommandExecutor { _ => format!("/{}", self.cmd_name), }; - let (tx, _rx) = oneshot::channel(); + let (tx, rx) = oneshot::channel(); self.command_tx .send(JvmCommand::TriggerCommand { full_command, @@ -142,7 +142,9 @@ impl CommandExecutor for JavaCommandExecutor { }) .await .unwrap(); - Ok(()) + + let handled = rx.await.ok().and_then(Result::ok).unwrap_or(false); + Ok(i32::from(handled)) }) } } diff --git a/rust/src/events/handler.rs b/rust/src/events/handler.rs index b41e374..0732863 100644 --- a/rust/src/events/handler.rs +++ b/rust/src/events/handler.rs @@ -5,13 +5,33 @@ use std::sync::Arc; use pumpkin::entity::player::Player; use pumpkin::plugin::{BoxFuture, Cancellable, EventHandler, Payload}; use pumpkin::server::Server; +use pumpkin::world::World as PumpkinWorld; use pumpkin_api_macros::with_runtime; +use pumpkin_data::block_properties::Instrument; +use pumpkin_data::data_component_impl::EquipmentSlot; +use pumpkin_data::item::Item; +use pumpkin_data::{Block, BlockDirection}; +use pumpkin_util::Hand; +use pumpkin_util::math::vector3::Vector3; +use pumpkin_util::text::TextComponent; +use pumpkin_world::item::ItemStack; use tokio::sync::{mpsc, oneshot}; use crate::java::jvm::commands::JvmCommand; -use crate::proto::patchbukkit::common::Uuid; +use crate::proto::patchbukkit::common::{Location, Uuid, Vec3, World as ProtoWorld}; use crate::proto::patchbukkit::events::event::Data; -use crate::proto::patchbukkit::events::{Event, PlayerJoinEvent}; +use crate::proto::patchbukkit::events::{ + BlockBreakEvent, BlockBurnEvent, BlockCanBuildEvent, BlockDispenseEvent, BlockFormEvent, + BlockIgniteEvent, BlockMultiPlaceBlockEntry, BlockMultiPlaceEvent, BlockPlaceEvent, + BlockRedstoneEvent, Event, MoistureChangeEvent, NotePlayEvent, PlayerBedEnterEvent, + PlayerBucketEmptyEvent, PlayerBucketFillEvent, PlayerChangeWorldEvent, PlayerChatEvent, + PlayerCommandSendEvent, PlayerDropItemEvent, PlayerExpChangeEvent, PlayerGamemodeChangeEvent, + PlayerInteractEvent, PlayerItemBreakEvent, PlayerItemConsumeEvent, PlayerItemDamageEvent, + PlayerItemHeldEvent, PlayerItemMendEvent, PlayerJoinEvent, PlayerKickEvent, PlayerLeaveEvent, + PlayerLevelChangeEvent, PlayerLoginEvent, PlayerMoveEvent, PlayerTeleportEvent, + PlayerToggleFlightEvent, ServerBroadcastEvent, ServerCommandEvent, SignChangeEvent, + TntPrimeEvent, +}; pub struct EventContext { pub server: Arc, @@ -28,7 +48,7 @@ pub trait PatchBukkitEvent { fn apply_modifications(&mut self, server: &Arc, data: Data) -> Option<()>; } -impl PatchBukkitEvent for pumpkin::plugin::player::player_join::PlayerJoinEvent { +impl PatchBukkitEvent for pumpkin::plugin::api::events::player::player_join::PlayerJoinEvent { fn to_payload(&self, server: Arc) -> JvmEventPayload { JvmEventPayload { event: Event { @@ -47,23 +67,1986 @@ impl PatchBukkitEvent for pumpkin::plugin::player::player_join::PlayerJoinEvent } fn apply_modifications(&mut self, server: &Arc, data: Data) -> Option<()> { - match data { - Data::PlayerJoin(event) => { - self.join_message = serde_json::from_str(&event.join_message).ok()?; - server.get_player_by_uuid(uuid::Uuid::from_str(&event.player_uuid?.value).ok()?)?; + if let Data::PlayerJoin(event) = data { + self.join_message = serde_json::from_str(&event.join_message).ok()?; + server.get_player_by_uuid(uuid::Uuid::from_str(&event.player_uuid?.value).ok()?)?; + } + + Some(()) + } +} + +impl PatchBukkitEvent for pumpkin::plugin::api::events::player::player_login::PlayerLoginEvent { + fn to_payload(&self, server: Arc) -> JvmEventPayload { + JvmEventPayload { + event: Event { + data: Some(Data::PlayerLogin(PlayerLoginEvent { + player_uuid: Some(Uuid { + value: self.player.gameprofile.id.to_string(), + }), + kick_message: serde_json::to_string(&self.kick_message).unwrap(), + })), + }, + context: EventContext { + server, + player: Some(self.player.clone()), + }, + } + } + + fn apply_modifications(&mut self, server: &Arc, data: Data) -> Option<()> { + if let Data::PlayerLogin(event) = data { + self.kick_message = serde_json::from_str(&event.kick_message).ok()?; + server.get_player_by_uuid(uuid::Uuid::from_str(&event.player_uuid?.value).ok()?)?; + } + + Some(()) + } +} + +impl PatchBukkitEvent for pumpkin::plugin::api::events::player::player_leave::PlayerLeaveEvent { + fn to_payload(&self, server: Arc) -> JvmEventPayload { + JvmEventPayload { + event: Event { + data: Some(Data::PlayerLeave(PlayerLeaveEvent { + player_uuid: Some(Uuid { + value: self.player.gameprofile.id.to_string(), + }), + leave_message: serde_json::to_string(&self.leave_message).unwrap(), + })), + }, + context: EventContext { + server, + player: Some(self.player.clone()), + }, + } + } + + fn apply_modifications(&mut self, server: &Arc, data: Data) -> Option<()> { + if let Data::PlayerLeave(event) = data { + self.leave_message = serde_json::from_str(&event.leave_message).ok()?; + server.get_player_by_uuid(uuid::Uuid::from_str(&event.player_uuid?.value).ok()?)?; + } + + Some(()) + } +} + +impl PatchBukkitEvent for pumpkin::plugin::api::events::player::player_move::PlayerMoveEvent { + fn to_payload(&self, server: Arc) -> JvmEventPayload { + let world_uuid = self.player.world().uuid; + JvmEventPayload { + event: Event { + data: Some(Data::PlayerMove(PlayerMoveEvent { + player_uuid: Some(Uuid { + value: self.player.gameprofile.id.to_string(), + }), + from: Some(build_location(world_uuid, &self.from, 0.0, 0.0)), + to: Some(build_location(world_uuid, &self.to, 0.0, 0.0)), + })), + }, + context: EventContext { + server, + player: Some(self.player.clone()), + }, + } + } + + fn apply_modifications(&mut self, _server: &Arc, data: Data) -> Option<()> { + if let Data::PlayerMove(event) = data { + if let Some(from) = event.from.and_then(location_to_vec3) { + self.from = from; + } + if let Some(to) = event.to.and_then(location_to_vec3) { + self.to = to; + } + } + + Some(()) + } +} + +impl PatchBukkitEvent + for pumpkin::plugin::api::events::player::player_teleport::PlayerTeleportEvent +{ + fn to_payload(&self, server: Arc) -> JvmEventPayload { + let world_uuid = self.player.world().uuid; + JvmEventPayload { + event: Event { + data: Some(Data::PlayerTeleport(PlayerTeleportEvent { + player_uuid: Some(Uuid { + value: self.player.gameprofile.id.to_string(), + }), + from: Some(build_location(world_uuid, &self.from, 0.0, 0.0)), + to: Some(build_location(world_uuid, &self.to, 0.0, 0.0)), + cause: "UNKNOWN".to_string(), + })), + }, + context: EventContext { + server, + player: Some(self.player.clone()), + }, + } + } + + fn apply_modifications(&mut self, _server: &Arc, data: Data) -> Option<()> { + if let Data::PlayerTeleport(event) = data { + if let Some(from) = event.from.and_then(location_to_vec3) { + self.from = from; + } + if let Some(to) = event.to.and_then(location_to_vec3) { + self.to = to; + } + } + + Some(()) + } +} + +impl PatchBukkitEvent + for pumpkin::plugin::api::events::player::player_change_world::PlayerChangeWorldEvent +{ + fn to_payload(&self, server: Arc) -> JvmEventPayload { + JvmEventPayload { + event: Event { + data: Some(Data::PlayerChangeWorld(PlayerChangeWorldEvent { + player_uuid: Some(Uuid { + value: self.player.gameprofile.id.to_string(), + }), + previous_world: Some(ProtoWorld { + uuid: Some(Uuid { + value: self.previous_world.uuid.to_string(), + }), + }), + new_world: Some(ProtoWorld { + uuid: Some(Uuid { + value: self.new_world.uuid.to_string(), + }), + }), + position: Some(build_location( + self.new_world.uuid, + &self.position, + self.yaw, + self.pitch, + )), + yaw: self.yaw, + pitch: self.pitch, + })), + }, + context: EventContext { + server, + player: Some(self.player.clone()), + }, + } + } + + fn apply_modifications(&mut self, server: &Arc, data: Data) -> Option<()> { + if let Data::PlayerChangeWorld(event) = data { + if let Some(world) = event.previous_world.and_then(|w| w.uuid) { + self.previous_world = + find_world_by_uuid(server, uuid::Uuid::from_str(&world.value).ok()?)?; + } + if let Some(world) = event.new_world.and_then(|w| w.uuid) { + self.new_world = + find_world_by_uuid(server, uuid::Uuid::from_str(&world.value).ok()?)?; + } + if let Some(position) = event.position.and_then(location_to_vec3) { + self.position = position; + } + if event.yaw != 0.0 { + self.yaw = event.yaw; + } + if event.pitch != 0.0 { + self.pitch = event.pitch; + } + } + + Some(()) + } +} + +impl PatchBukkitEvent + for pumpkin::plugin::api::events::player::player_gamemode_change::PlayerGamemodeChangeEvent +{ + fn to_payload(&self, server: Arc) -> JvmEventPayload { + JvmEventPayload { + event: Event { + data: Some(Data::PlayerGamemodeChange(PlayerGamemodeChangeEvent { + player_uuid: Some(Uuid { + value: self.player.gameprofile.id.to_string(), + }), + previous_gamemode: gamemode_to_bukkit(self.previous_gamemode), + new_gamemode: gamemode_to_bukkit(self.new_gamemode), + })), + }, + context: EventContext { + server, + player: Some(self.player.clone()), + }, + } + } + + fn apply_modifications(&mut self, _server: &Arc, data: Data) -> Option<()> { + if let Data::PlayerGamemodeChange(event) = data { + if let Some(mode) = gamemode_from_bukkit(&event.previous_gamemode) { + self.previous_gamemode = mode; + } + if let Some(mode) = gamemode_from_bukkit(&event.new_gamemode) { + self.new_gamemode = mode; + } + } + Some(()) + } +} + +impl PatchBukkitEvent for pumpkin::plugin::api::events::player::player_chat::PlayerChatEvent { + fn to_payload(&self, server: Arc) -> JvmEventPayload { + let recipients = self + .recipients + .iter() + .map(|player| Uuid { + value: player.gameprofile.id.to_string(), + }) + .collect(); + JvmEventPayload { + event: Event { + data: Some(Data::PlayerChat(PlayerChatEvent { + player_uuid: Some(Uuid { + value: self.player.gameprofile.id.to_string(), + }), + message: self.message.clone(), + recipients, + })), + }, + context: EventContext { + server, + player: Some(self.player.clone()), + }, + } + } + + fn apply_modifications(&mut self, server: &Arc, data: Data) -> Option<()> { + if let Data::PlayerChat(event) = data { + if !event.message.is_empty() { + self.message = event.message; + } + if !event.recipients.is_empty() { + let mut recipients = Vec::new(); + for uuid in event.recipients { + let player_uuid = uuid::Uuid::from_str(&uuid.value).ok()?; + if let Some(player) = server.get_player_by_uuid(player_uuid) { + recipients.push(player); + } + } + self.recipients = recipients; + } + } + Some(()) + } +} + +impl PatchBukkitEvent + for pumpkin::plugin::api::events::player::player_command_send::PlayerCommandSendEvent +{ + fn to_payload(&self, server: Arc) -> JvmEventPayload { + JvmEventPayload { + event: Event { + data: Some(Data::PlayerCommandSend(PlayerCommandSendEvent { + player_uuid: Some(Uuid { + value: self.player.gameprofile.id.to_string(), + }), + commands: self.commands.clone(), + })), + }, + context: EventContext { + server, + player: Some(self.player.clone()), + }, + } + } + + fn apply_modifications(&mut self, _server: &Arc, data: Data) -> Option<()> { + if let Data::PlayerCommandSend(event) = data + && !event.commands.is_empty() + { + self.commands = event.commands; + } + Some(()) + } +} + +impl PatchBukkitEvent + for pumpkin::plugin::api::events::player::player_interact_event::PlayerInteractEvent +{ + fn to_payload(&self, server: Arc) -> JvmEventPayload { + let world_uuid = self.player.world().uuid; + let clicked = self.clicked_pos.as_ref().map(|pos| { + build_location( + world_uuid, + &Vector3::new(f64::from(pos.0.x), f64::from(pos.0.y), f64::from(pos.0.z)), + 0.0, + 0.0, + ) + }); + let item_key = self + .item + .try_lock() + .ok() + .map(|item| item_to_key(item.get_item())) + .unwrap_or_default(); + + JvmEventPayload { + event: Event { + data: Some(Data::PlayerInteract(PlayerInteractEvent { + player_uuid: Some(Uuid { + value: self.player.gameprofile.id.to_string(), + }), + action: interact_action_to_bukkit(&self.action), + block_key: block_to_key(self.block), + clicked, + item_key, + block_face: String::new(), + })), + }, + context: EventContext { + server, + player: Some(self.player.clone()), + }, + } + } + + fn apply_modifications(&mut self, _server: &Arc, data: Data) -> Option<()> { + if let Data::PlayerInteract(event) = data { + if let Some(action) = interact_action_from_bukkit(&event.action) { + self.action = action; + } + if let Some(loc) = event.clicked.and_then(location_to_vec3) { + self.clicked_pos = Some(pumpkin_util::math::position::BlockPos::new( + loc.x.floor() as i32, + loc.y.floor() as i32, + loc.z.floor() as i32, + )); + } + if !event.block_key.is_empty() + && let Some(block) = Block::from_name(&event.block_key) + { + self.block = block; + } + } + Some(()) + } +} + +impl PatchBukkitEvent for pumpkin::plugin::api::events::block::block_break::BlockBreakEvent { + fn to_payload(&self, server: Arc) -> JvmEventPayload { + let world_uuid = self + .player + .as_ref() + .map(|p| p.world().uuid) + .or_else(|| server.worlds.load().first().map(|w| w.uuid)) + .unwrap_or_else(uuid::Uuid::new_v4); + let location = build_location( + world_uuid, + &Vector3::new( + f64::from(self.block_position.0.x), + f64::from(self.block_position.0.y), + f64::from(self.block_position.0.z), + ), + 0.0, + 0.0, + ); + + JvmEventPayload { + event: Event { + data: Some(Data::BlockBreak(BlockBreakEvent { + player_uuid: self.player.as_ref().map(|p| Uuid { + value: p.gameprofile.id.to_string(), + }), + block_key: block_to_key(self.block), + location: Some(location), + exp: self.exp, + drop: self.drop, + })), + }, + context: EventContext { + server, + player: self.player.clone(), + }, + } + } + + fn apply_modifications(&mut self, _server: &Arc, data: Data) -> Option<()> { + if let Data::BlockBreak(event) = data { + if !event.block_key.is_empty() + && let Some(block) = Block::from_name(&event.block_key) + { + self.block = block; + } + self.exp = event.exp; + self.drop = event.drop; + } + Some(()) + } +} + +impl PatchBukkitEvent for pumpkin::plugin::api::events::block::block_burn::BlockBurnEvent { + fn to_payload(&self, server: Arc) -> JvmEventPayload { + JvmEventPayload { + event: Event { + data: Some(Data::BlockBurn(BlockBurnEvent { + block_key: block_to_key(self.block), + igniting_block_key: block_to_key(self.igniting_block), + location: None, + })), + }, + context: EventContext { + server, + player: None, + }, + } + } + + fn apply_modifications(&mut self, _server: &Arc, data: Data) -> Option<()> { + if let Data::BlockBurn(event) = data { + if !event.block_key.is_empty() + && let Some(block) = Block::from_name(&event.block_key) + { + self.block = block; + } + if !event.igniting_block_key.is_empty() + && let Some(block) = Block::from_name(&event.igniting_block_key) + { + self.igniting_block = block; + } + } + Some(()) + } +} + +impl PatchBukkitEvent for pumpkin::plugin::api::events::block::block_can_build::BlockCanBuildEvent { + fn to_payload(&self, server: Arc) -> JvmEventPayload { + let world_uuid = self.player.world().uuid; + let position = self.player.position(); + let location = build_location( + world_uuid, + &Vector3::new(position.x, position.y, position.z), + 0.0, + 0.0, + ); + JvmEventPayload { + event: Event { + data: Some(Data::BlockCanBuild(BlockCanBuildEvent { + player_uuid: Some(Uuid { + value: self.player.gameprofile.id.to_string(), + }), + block_key: block_to_key(self.block_to_build), + block_against_key: block_to_key(self.block), + location: Some(location), + can_build: self.buildable, + })), + }, + context: EventContext { + server, + player: Some(self.player.clone()), + }, + } + } + + fn apply_modifications(&mut self, _server: &Arc, data: Data) -> Option<()> { + if let Data::BlockCanBuild(event) = data { + if !event.block_key.is_empty() + && let Some(block) = Block::from_name(&event.block_key) + { + self.block_to_build = block; + } + if !event.block_against_key.is_empty() + && let Some(block) = Block::from_name(&event.block_against_key) + { + self.block = block; + } + self.buildable = event.can_build; + } + Some(()) + } +} + +impl PatchBukkitEvent for pumpkin::plugin::api::events::block::block_place::BlockPlaceEvent { + fn to_payload(&self, server: Arc) -> JvmEventPayload { + let world_uuid = self.player.world().uuid; + let position = self.player.position(); + let location = build_location( + world_uuid, + &Vector3::new(position.x, position.y, position.z), + 0.0, + 0.0, + ); + JvmEventPayload { + event: Event { + data: Some(Data::BlockPlace(BlockPlaceEvent { + player_uuid: Some(Uuid { + value: self.player.gameprofile.id.to_string(), + }), + block_key: block_to_key(self.block_placed), + block_against_key: block_to_key(self.block_placed_against), + location: Some(location), + can_build: self.can_build, + })), + }, + context: EventContext { + server, + player: Some(self.player.clone()), + }, + } + } + + fn apply_modifications(&mut self, _server: &Arc, data: Data) -> Option<()> { + if let Data::BlockPlace(event) = data { + if !event.block_key.is_empty() + && let Some(block) = Block::from_name(&event.block_key) + { + self.block_placed = block; + } + if !event.block_against_key.is_empty() + && let Some(block) = Block::from_name(&event.block_against_key) + { + self.block_placed_against = block; + } + self.can_build = event.can_build; + } + Some(()) + } +} + +impl PatchBukkitEvent for pumpkin::plugin::api::events::server::server_command::ServerCommandEvent { + fn to_payload(&self, server: Arc) -> JvmEventPayload { + JvmEventPayload { + event: Event { + data: Some(Data::ServerCommand(ServerCommandEvent { + command: self.command.clone(), + })), + }, + context: EventContext { + server, + player: None, + }, + } + } + + fn apply_modifications(&mut self, _server: &Arc, data: Data) -> Option<()> { + if let Data::ServerCommand(event) = data { + self.command = event.command; + } + Some(()) + } +} + +impl PatchBukkitEvent + for pumpkin::plugin::api::events::server::server_broadcast::ServerBroadcastEvent +{ + fn to_payload(&self, server: Arc) -> JvmEventPayload { + JvmEventPayload { + event: Event { + data: Some(Data::ServerBroadcast(ServerBroadcastEvent { + message: serde_json::to_string(&self.message).unwrap(), + sender: serde_json::to_string(&self.sender).unwrap(), + })), + }, + context: EventContext { + server, + player: None, + }, + } + } + + fn apply_modifications(&mut self, _server: &Arc, data: Data) -> Option<()> { + if let Data::ServerBroadcast(event) = data { + self.message = serde_json::from_str(&event.message) + .unwrap_or_else(|_| TextComponent::from_legacy_string(&event.message)); + self.sender = serde_json::from_str(&event.sender) + .unwrap_or_else(|_| TextComponent::from_legacy_string(&event.sender)); + } + Some(()) + } +} + +impl PatchBukkitEvent + for pumpkin::plugin::api::events::player::player_bed_enter::PlayerBedEnterEvent +{ + fn to_payload(&self, server: Arc) -> JvmEventPayload { + let world_uuid = self.player.world().uuid; + + JvmEventPayload { + event: Event { + data: Some(Data::PlayerBedEnter(PlayerBedEnterEvent { + player_uuid: Some(Uuid { + value: self.player.gameprofile.id.to_string(), + }), + bed_location: Some(build_location(world_uuid, &self.bed_position, 0.0, 0.0)), + })), + }, + context: EventContext { + server, + player: Some(self.player.clone()), + }, + } + } + + fn apply_modifications(&mut self, _server: &Arc, data: Data) -> Option<()> { + if let Data::PlayerBedEnter(event) = data + && let Some(location) = event.bed_location + && let Some(pos) = location_to_vec3(location) + { + self.bed_position = pos; + } + + Some(()) + } +} + +impl PatchBukkitEvent + for pumpkin::plugin::api::events::player::player_bucket_empty::PlayerBucketEmptyEvent +{ + fn to_payload(&self, server: Arc) -> JvmEventPayload { + let world_uuid = self.player.world().uuid; + JvmEventPayload { + event: Event { + data: Some(Data::PlayerBucketEmpty(PlayerBucketEmptyEvent { + player_uuid: Some(Uuid { + value: self.player.gameprofile.id.to_string(), + }), + location: Some(build_location(world_uuid, &self.position, 0.0, 0.0)), + block_key: self.block_key.clone(), + block_face: block_face_to_bukkit(self.face), + bucket_item_key: self.bucket_item_key.clone(), + hand: self.hand.clone(), + })), + }, + context: EventContext { + server, + player: Some(self.player.clone()), + }, + } + } + + fn apply_modifications(&mut self, _server: &Arc, data: Data) -> Option<()> { + if let Data::PlayerBucketEmpty(event) = data { + if let Some(location) = event.location + && let Some(pos) = location_to_vec3(location) + { + self.position = pos; + } + if !event.block_key.is_empty() { + self.block_key = event.block_key; + } + if !event.bucket_item_key.is_empty() { + self.bucket_item_key = event.bucket_item_key; + } + if !event.hand.is_empty() { + self.hand = event.hand; + } + self.face = bukkit_block_face_from_string(&event.block_face); + } + Some(()) + } +} + +impl PatchBukkitEvent + for pumpkin::plugin::api::events::player::player_bucket_fill::PlayerBucketFillEvent +{ + fn to_payload(&self, server: Arc) -> JvmEventPayload { + let world_uuid = self.player.world().uuid; + JvmEventPayload { + event: Event { + data: Some(Data::PlayerBucketFill(PlayerBucketFillEvent { + player_uuid: Some(Uuid { + value: self.player.gameprofile.id.to_string(), + }), + location: Some(build_location(world_uuid, &self.position, 0.0, 0.0)), + block_key: self.block_key.clone(), + block_face: block_face_to_bukkit(self.face), + bucket_item_key: self.bucket_item_key.clone(), + hand: self.hand.clone(), + })), + }, + context: EventContext { + server, + player: Some(self.player.clone()), + }, + } + } + + fn apply_modifications(&mut self, _server: &Arc, data: Data) -> Option<()> { + if let Data::PlayerBucketFill(event) = data { + if let Some(location) = event.location + && let Some(pos) = location_to_vec3(location) + { + self.position = pos; + } + if !event.block_key.is_empty() { + self.block_key = event.block_key; + } + if !event.bucket_item_key.is_empty() { + self.bucket_item_key = event.bucket_item_key; + } + if !event.hand.is_empty() { + self.hand = event.hand; + } + self.face = bukkit_block_face_from_string(&event.block_face); + } + Some(()) + } +} + +impl PatchBukkitEvent + for pumpkin::plugin::api::events::player::player_drop_item::PlayerDropItemEvent +{ + fn to_payload(&self, server: Arc) -> JvmEventPayload { + JvmEventPayload { + event: Event { + data: Some(Data::PlayerDropItem(PlayerDropItemEvent { + player_uuid: Some(Uuid { + value: self.player.gameprofile.id.to_string(), + }), + item_uuid: Some(Uuid { + value: self.item_uuid.to_string(), + }), + item_key: item_to_key(self.item_stack.item), + item_amount: i32::from(self.item_stack.item_count), + })), + }, + context: EventContext { + server, + player: Some(self.player.clone()), + }, + } + } + + fn apply_modifications(&mut self, _server: &Arc, data: Data) -> Option<()> { + if let Data::PlayerDropItem(event) = data { + if let Some(uuid) = event.item_uuid + && let Ok(item_uuid) = uuid::Uuid::from_str(&uuid.value) + { + self.item_uuid = item_uuid; + } + let mut key = if event.item_key.is_empty() { + None + } else { + Some(event.item_key) + }; + let mut amount = if event.item_amount > 0 { + Some(event.item_amount as u8) + } else { + None + }; + + if key.is_some() || amount.is_some() { + let fallback_key = item_to_key(self.item_stack.item); + let key = key.take().unwrap_or(fallback_key); + let count = amount.take().unwrap_or(self.item_stack.item_count); + self.item_stack = item_stack_from_key(&key, count); + } + } + Some(()) + } +} + +impl PatchBukkitEvent + for pumpkin::plugin::api::events::player::player_exp_change::PlayerExpChangeEvent +{ + fn to_payload(&self, server: Arc) -> JvmEventPayload { + JvmEventPayload { + event: Event { + data: Some(Data::PlayerExpChange(PlayerExpChangeEvent { + player_uuid: Some(Uuid { + value: self.player.gameprofile.id.to_string(), + }), + amount: self.amount, + })), + }, + context: EventContext { + server, + player: Some(self.player.clone()), + }, + } + } + + fn apply_modifications(&mut self, _server: &Arc, data: Data) -> Option<()> { + if let Data::PlayerExpChange(event) = data { + self.amount = event.amount; + } + Some(()) + } +} + +impl PatchBukkitEvent + for pumpkin::plugin::api::events::player::player_item_break::PlayerItemBreakEvent +{ + fn to_payload(&self, server: Arc) -> JvmEventPayload { + JvmEventPayload { + event: Event { + data: Some(Data::PlayerItemBreak(PlayerItemBreakEvent { + player_uuid: Some(Uuid { + value: self.player.gameprofile.id.to_string(), + }), + item_key: item_to_key(self.item_stack.item), + item_amount: i32::from(self.item_stack.item_count), + })), + }, + context: EventContext { + server, + player: Some(self.player.clone()), + }, + } + } + + fn apply_modifications(&mut self, _server: &Arc, data: Data) -> Option<()> { + if let Data::PlayerItemBreak(event) = data { + let mut key = if event.item_key.is_empty() { + None + } else { + Some(event.item_key) + }; + let mut amount = if event.item_amount > 0 { + Some(event.item_amount as u8) + } else { + None + }; + + if key.is_some() || amount.is_some() { + let fallback_key = item_to_key(self.item_stack.item); + let key = key.take().unwrap_or(fallback_key); + let count = amount.take().unwrap_or(self.item_stack.item_count); + self.item_stack = item_stack_from_key(&key, count); + } + } + Some(()) + } +} + +impl PatchBukkitEvent + for pumpkin::plugin::api::events::player::player_item_consume::PlayerItemConsumeEvent +{ + fn to_payload(&self, server: Arc) -> JvmEventPayload { + let hand = match self.hand { + Hand::Left => "OFF_HAND", + Hand::Right => "HAND", + }; + JvmEventPayload { + event: Event { + data: Some(Data::PlayerItemConsume(PlayerItemConsumeEvent { + player_uuid: Some(Uuid { + value: self.player.gameprofile.id.to_string(), + }), + item_key: item_to_key(self.item_stack.item), + item_amount: i32::from(self.item_stack.item_count), + hand: hand.to_string(), + })), + }, + context: EventContext { + server, + player: Some(self.player.clone()), + }, + } + } + + fn apply_modifications(&mut self, _server: &Arc, data: Data) -> Option<()> { + if let Data::PlayerItemConsume(event) = data { + if !event.hand.is_empty() { + self.hand = if event.hand == "OFF_HAND" { + Hand::Left + } else { + Hand::Right + }; + } + + let mut key = if event.item_key.is_empty() { + None + } else { + Some(event.item_key) + }; + let mut amount = if event.item_amount > 0 { + Some(event.item_amount as u8) + } else { + None + }; + + if key.is_some() || amount.is_some() { + let fallback_key = item_to_key(self.item_stack.item); + let key = key.take().unwrap_or(fallback_key); + let count = amount.take().unwrap_or(self.item_stack.item_count); + self.item_stack = item_stack_from_key(&key, count); + } + } + Some(()) + } +} + +impl PatchBukkitEvent + for pumpkin::plugin::api::events::player::player_item_damage::PlayerItemDamageEvent +{ + fn to_payload(&self, server: Arc) -> JvmEventPayload { + JvmEventPayload { + event: Event { + data: Some(Data::PlayerItemDamage(PlayerItemDamageEvent { + player_uuid: Some(Uuid { + value: self.player.gameprofile.id.to_string(), + }), + item_key: item_to_key(self.item_stack.item), + item_amount: i32::from(self.item_stack.item_count), + damage: self.damage, + })), + }, + context: EventContext { + server, + player: Some(self.player.clone()), + }, + } + } + + fn apply_modifications(&mut self, _server: &Arc, data: Data) -> Option<()> { + if let Data::PlayerItemDamage(event) = data { + self.damage = event.damage; + let mut key = if event.item_key.is_empty() { + None + } else { + Some(event.item_key) + }; + let mut amount = if event.item_amount > 0 { + Some(event.item_amount as u8) + } else { + None + }; + + if key.is_some() || amount.is_some() { + let fallback_key = item_to_key(self.item_stack.item); + let key = key.take().unwrap_or(fallback_key); + let count = amount.take().unwrap_or(self.item_stack.item_count); + self.item_stack = item_stack_from_key(&key, count); + } + } + Some(()) + } +} + +impl PatchBukkitEvent + for pumpkin::plugin::api::events::player::player_item_held::PlayerItemHeldEvent +{ + fn to_payload(&self, server: Arc) -> JvmEventPayload { + JvmEventPayload { + event: Event { + data: Some(Data::PlayerItemHeld(PlayerItemHeldEvent { + player_uuid: Some(Uuid { + value: self.player.gameprofile.id.to_string(), + }), + previous_slot: self.previous_slot, + new_slot: self.new_slot, + })), + }, + context: EventContext { + server, + player: Some(self.player.clone()), + }, + } + } + + fn apply_modifications(&mut self, _server: &Arc, data: Data) -> Option<()> { + if let Data::PlayerItemHeld(event) = data { + self.previous_slot = event.previous_slot; + self.new_slot = event.new_slot; + } + Some(()) + } +} + +impl PatchBukkitEvent + for pumpkin::plugin::api::events::player::player_item_mend::PlayerItemMendEvent +{ + fn to_payload(&self, server: Arc) -> JvmEventPayload { + JvmEventPayload { + event: Event { + data: Some(Data::PlayerItemMend(PlayerItemMendEvent { + player_uuid: Some(Uuid { + value: self.player.gameprofile.id.to_string(), + }), + item_key: item_to_key(self.item_stack.item), + item_amount: i32::from(self.item_stack.item_count), + slot: equipment_slot_to_bukkit(&self.slot), + repair_amount: self.repair_amount, + orb_uuid: self.orb_uuid.map(|id| Uuid { + value: id.to_string(), + }), + })), + }, + context: EventContext { + server, + player: Some(self.player.clone()), + }, + } + } + + fn apply_modifications(&mut self, _server: &Arc, data: Data) -> Option<()> { + if let Data::PlayerItemMend(event) = data { + self.repair_amount = event.repair_amount; + if !event.slot.is_empty() + && let Some(slot) = equipment_slot_from_bukkit(&event.slot) + { + self.slot = slot; + } + if let Some(uuid) = event.orb_uuid + && let Ok(orb_uuid) = uuid::Uuid::from_str(&uuid.value) + { + self.orb_uuid = Some(orb_uuid); + } + let mut key = if event.item_key.is_empty() { + None + } else { + Some(event.item_key) + }; + let mut amount = if event.item_amount > 0 { + Some(event.item_amount as u8) + } else { + None + }; + + if key.is_some() || amount.is_some() { + let fallback_key = item_to_key(self.item_stack.item); + let key = key.take().unwrap_or(fallback_key); + let count = amount.take().unwrap_or(self.item_stack.item_count); + self.item_stack = item_stack_from_key(&key, count); + } + } + Some(()) + } +} + +impl PatchBukkitEvent for pumpkin::plugin::api::events::player::player_kick::PlayerKickEvent { + fn to_payload(&self, server: Arc) -> JvmEventPayload { + JvmEventPayload { + event: Event { + data: Some(Data::PlayerKick(PlayerKickEvent { + player_uuid: Some(Uuid { + value: self.player.gameprofile.id.to_string(), + }), + reason: serde_json::to_string(&self.reason).unwrap_or_default(), + leave_message: serde_json::to_string(&self.leave_message).unwrap_or_default(), + cause: self.cause.clone(), + })), + }, + context: EventContext { + server, + player: Some(self.player.clone()), + }, + } + } + + fn apply_modifications(&mut self, _server: &Arc, data: Data) -> Option<()> { + if let Data::PlayerKick(event) = data { + if !event.reason.is_empty() + && let Ok(reason) = serde_json::from_str(&event.reason) + { + self.reason = reason; + } + if !event.leave_message.is_empty() + && let Ok(msg) = serde_json::from_str(&event.leave_message) + { + self.leave_message = msg; + } + if !event.cause.is_empty() { + self.cause = event.cause; + } + } + Some(()) + } +} + +impl PatchBukkitEvent + for pumpkin::plugin::api::events::player::player_level_change::PlayerLevelChangeEvent +{ + fn to_payload(&self, server: Arc) -> JvmEventPayload { + JvmEventPayload { + event: Event { + data: Some(Data::PlayerLevelChange(PlayerLevelChangeEvent { + player_uuid: Some(Uuid { + value: self.player.gameprofile.id.to_string(), + }), + old_level: self.old_level, + new_level: self.new_level, + })), + }, + context: EventContext { + server, + player: Some(self.player.clone()), + }, + } + } + + fn apply_modifications(&mut self, _server: &Arc, data: Data) -> Option<()> { + if let Data::PlayerLevelChange(event) = data { + self.old_level = event.old_level; + self.new_level = event.new_level; + } + Some(()) + } +} + +impl PatchBukkitEvent + for pumpkin::plugin::api::events::player::player_toggle_flight::PlayerToggleFlightEvent +{ + fn to_payload(&self, server: Arc) -> JvmEventPayload { + JvmEventPayload { + event: Event { + data: Some(Data::PlayerToggleFlight(PlayerToggleFlightEvent { + player_uuid: Some(Uuid { + value: self.player.gameprofile.id.to_string(), + }), + is_flying: self.is_flying, + })), + }, + context: EventContext { + server, + player: Some(self.player.clone()), + }, + } + } + + fn apply_modifications(&mut self, _server: &Arc, data: Data) -> Option<()> { + if let Data::PlayerToggleFlight(event) = data { + self.is_flying = event.is_flying; + } + Some(()) + } +} + +impl PatchBukkitEvent for pumpkin::plugin::api::events::block::block_dispense::BlockDispenseEvent { + fn to_payload(&self, server: Arc) -> JvmEventPayload { + let location = build_location( + self.world_uuid, + &Vector3::new( + f64::from(self.block_position.0.x), + f64::from(self.block_position.0.y), + f64::from(self.block_position.0.z), + ), + 0.0, + 0.0, + ); + + JvmEventPayload { + event: Event { + data: Some(Data::BlockDispense(BlockDispenseEvent { + block_key: block_to_key(self.block), + location: Some(location), + item_key: item_to_key(self.item_stack.item), + item_amount: i32::from(self.item_stack.item_count), + velocity: Some(Vec3 { + x: self.velocity.x, + y: self.velocity.y, + z: self.velocity.z, + }), + })), + }, + context: EventContext { + server, + player: None, + }, + } + } + + fn apply_modifications(&mut self, _server: &Arc, data: Data) -> Option<()> { + if let Data::BlockDispense(event) = data { + if !event.item_key.is_empty() || event.item_amount > 0 { + let key = if event.item_key.is_empty() { + item_to_key(self.item_stack.item) + } else { + event.item_key + }; + let count = if event.item_amount > 0 { + event.item_amount as u8 + } else { + self.item_stack.item_count + }; + self.item_stack = item_stack_from_key(&key, count); + } + if let Some(vel) = event.velocity { + self.velocity = Vector3::new(vel.x, vel.y, vel.z); + } + } + + Some(()) + } +} + +impl PatchBukkitEvent for pumpkin::plugin::api::events::block::block_form::BlockFormEvent { + fn to_payload(&self, server: Arc) -> JvmEventPayload { + let location = build_location( + self.world_uuid, + &Vector3::new( + f64::from(self.block_pos.0.x), + f64::from(self.block_pos.0.y), + f64::from(self.block_pos.0.z), + ), + 0.0, + 0.0, + ); + + JvmEventPayload { + event: Event { + data: Some(Data::BlockForm(BlockFormEvent { + block_key: block_to_key(self.old_block), + new_block_key: block_to_key(self.new_block), + location: Some(location), + })), + }, + context: EventContext { + server, + player: None, + }, + } + } + + fn apply_modifications(&mut self, _server: &Arc, data: Data) -> Option<()> { + if let Data::BlockForm(event) = data { + if !event.new_block_key.is_empty() + && let Some(block) = Block::from_name(&event.new_block_key) + { + self.new_block = block; + } + if let Some(loc) = event.location + && let Some(pos) = location_to_vec3(loc.clone()) + { + self.block_pos = pumpkin_util::math::position::BlockPos::new( + pos.x.floor() as i32, + pos.y.floor() as i32, + pos.z.floor() as i32, + ); + } + if !event.block_key.is_empty() + && let Some(block) = Block::from_name(&event.block_key) + { + self.old_block = block; + } + } + + Some(()) + } +} + +impl PatchBukkitEvent for pumpkin::plugin::api::events::block::block_ignite::BlockIgniteEvent { + fn to_payload(&self, server: Arc) -> JvmEventPayload { + let location = build_location( + self.world_uuid, + &Vector3::new( + f64::from(self.block_pos.0.x), + f64::from(self.block_pos.0.y), + f64::from(self.block_pos.0.z), + ), + 0.0, + 0.0, + ); + + JvmEventPayload { + event: Event { + data: Some(Data::BlockIgnite(BlockIgniteEvent { + player_uuid: Some(Uuid { + value: self.player.gameprofile.id.to_string(), + }), + block_key: block_to_key(self.block), + igniting_block_key: block_to_key(self.igniting_block), + location: Some(location), + cause: self.cause.clone(), + })), + }, + context: EventContext { + server, + player: Some(self.player.clone()), + }, + } + } + + fn apply_modifications(&mut self, _server: &Arc, data: Data) -> Option<()> { + if let Data::BlockIgnite(event) = data { + if !event.block_key.is_empty() + && let Some(block) = Block::from_name(&event.block_key) + { + self.block = block; + } + if !event.igniting_block_key.is_empty() + && let Some(block) = Block::from_name(&event.igniting_block_key) + { + self.igniting_block = block; + } + if let Some(loc) = event.location { + if let Some(pos) = location_to_vec3(loc.clone()) { + self.block_pos = pumpkin_util::math::position::BlockPos::new( + pos.x.floor() as i32, + pos.y.floor() as i32, + pos.z.floor() as i32, + ); + } + if let Some(world) = loc.world.and_then(|w| w.uuid) + && let Ok(uuid) = uuid::Uuid::from_str(&world.value) + { + self.world_uuid = uuid; + } + } + if !event.cause.is_empty() { + self.cause = event.cause; + } + } + Some(()) + } +} + +impl PatchBukkitEvent + for pumpkin::plugin::api::events::block::block_multi_place::BlockMultiPlaceEvent +{ + fn to_payload(&self, server: Arc) -> JvmEventPayload { + let world_uuid = self.player.world().uuid; + let origin = self + .positions + .first() + .copied() + .unwrap_or_else(|| self.player.position().to_block_pos()); + let location = build_location( + world_uuid, + &Vector3::new( + f64::from(origin.0.x), + f64::from(origin.0.y), + f64::from(origin.0.z), + ), + 0.0, + 0.0, + ); + let blocks = self + .positions + .iter() + .map(|pos| { + let loc = build_location( + world_uuid, + &Vector3::new(f64::from(pos.0.x), f64::from(pos.0.y), f64::from(pos.0.z)), + 0.0, + 0.0, + ); + BlockMultiPlaceBlockEntry { + block_key: block_to_key(self.block_placed), + location: Some(loc), + } + }) + .collect(); + + JvmEventPayload { + event: Event { + data: Some(Data::BlockMultiPlace(BlockMultiPlaceEvent { + player_uuid: Some(Uuid { + value: self.player.gameprofile.id.to_string(), + }), + block_key: block_to_key(self.block_placed), + location: Some(location), + blocks, + })), + }, + context: EventContext { + server, + player: Some(self.player.clone()), + }, + } + } + + fn apply_modifications(&mut self, _server: &Arc, _data: Data) -> Option<()> { + Some(()) + } +} + +impl PatchBukkitEvent for pumpkin::plugin::api::events::block::block_redstone::BlockRedstoneEvent { + fn to_payload(&self, server: Arc) -> JvmEventPayload { + let location = build_location( + self.world_uuid, + &Vector3::new( + f64::from(self.block_pos.0.x), + f64::from(self.block_pos.0.y), + f64::from(self.block_pos.0.z), + ), + 0.0, + 0.0, + ); + + JvmEventPayload { + event: Event { + data: Some(Data::BlockRedstone(BlockRedstoneEvent { + block_key: block_to_key(self.block), + location: Some(location), + old_current: self.old_current, + new_current: self.new_current, + })), + }, + context: EventContext { + server, + player: None, + }, + } + } + + fn apply_modifications(&mut self, _server: &Arc, data: Data) -> Option<()> { + if let Data::BlockRedstone(event) = data { + self.new_current = event.new_current; + } + Some(()) + } +} + +impl PatchBukkitEvent + for pumpkin::plugin::api::events::block::moisture_change::MoistureChangeEvent +{ + fn to_payload(&self, server: Arc) -> JvmEventPayload { + let location = build_location( + self.world_uuid, + &Vector3::new( + f64::from(self.block_pos.0.x), + f64::from(self.block_pos.0.y), + f64::from(self.block_pos.0.z), + ), + 0.0, + 0.0, + ); + let new_block_key = block_to_key(Block::from_state_id(self.new_state_id)); + + JvmEventPayload { + event: Event { + data: Some(Data::MoistureChange(MoistureChangeEvent { + block_key: block_to_key(self.block), + location: Some(location), + new_block_key, + })), + }, + context: EventContext { + server, + player: None, + }, + } + } + + fn apply_modifications(&mut self, _server: &Arc, data: Data) -> Option<()> { + if let Data::MoistureChange(event) = data { + if !event.block_key.is_empty() + && let Some(block) = Block::from_name(&event.block_key) + { + self.block = block; + } + if let Some(loc) = event.location { + if let Some(pos) = location_to_vec3(loc.clone()) { + self.block_pos = pumpkin_util::math::position::BlockPos::new( + pos.x.floor() as i32, + pos.y.floor() as i32, + pos.z.floor() as i32, + ); + } + if let Some(world) = loc.world.and_then(|w| w.uuid) + && let Ok(uuid) = uuid::Uuid::from_str(&world.value) + { + self.world_uuid = uuid; + } + } + if !event.new_block_key.is_empty() + && let Some(block) = Block::from_name(&event.new_block_key) + { + self.new_state_id = block.default_state.id; + } + } + Some(()) + } +} + +impl PatchBukkitEvent for pumpkin::plugin::api::events::block::note_play::NotePlayEvent { + fn to_payload(&self, server: Arc) -> JvmEventPayload { + let location = build_location( + self.world_uuid, + &Vector3::new( + f64::from(self.block_pos.0.x), + f64::from(self.block_pos.0.y), + f64::from(self.block_pos.0.z), + ), + 0.0, + 0.0, + ); + + JvmEventPayload { + event: Event { + data: Some(Data::NotePlay(NotePlayEvent { + block_key: block_to_key(self.block), + location: Some(location), + instrument: instrument_to_bukkit(self.instrument), + note: i32::from(self.note), + })), + }, + context: EventContext { + server, + player: None, + }, + } + } + + fn apply_modifications(&mut self, _server: &Arc, data: Data) -> Option<()> { + if let Data::NotePlay(event) = data { + if !event.block_key.is_empty() + && let Some(block) = Block::from_name(&event.block_key) + { + self.block = block; + } + if let Some(loc) = event.location { + if let Some(pos) = location_to_vec3(loc.clone()) { + self.block_pos = pumpkin_util::math::position::BlockPos::new( + pos.x.floor() as i32, + pos.y.floor() as i32, + pos.z.floor() as i32, + ); + } + if let Some(world) = loc.world.and_then(|w| w.uuid) + && let Ok(uuid) = uuid::Uuid::from_str(&world.value) + { + self.world_uuid = uuid; + } + } + if !event.instrument.is_empty() + && let Some(instrument) = instrument_from_bukkit(&event.instrument) + { + self.instrument = instrument; + } + self.note = event.note.clamp(0, 24) as u8; + } + Some(()) + } +} + +impl PatchBukkitEvent for pumpkin::plugin::api::events::block::sign_change::SignChangeEvent { + fn to_payload(&self, server: Arc) -> JvmEventPayload { + let world_uuid = self.player.world().uuid; + let location = build_location( + world_uuid, + &Vector3::new( + f64::from(self.block_pos.0.x), + f64::from(self.block_pos.0.y), + f64::from(self.block_pos.0.z), + ), + 0.0, + 0.0, + ); + + JvmEventPayload { + event: Event { + data: Some(Data::SignChange(SignChangeEvent { + player_uuid: Some(Uuid { + value: self.player.gameprofile.id.to_string(), + }), + block_key: block_to_key(self.block), + location: Some(location), + lines: self.lines.clone(), + is_front_text: self.is_front_text, + })), + }, + context: EventContext { + server, + player: Some(self.player.clone()), + }, + } + } + + fn apply_modifications(&mut self, server: &Arc, data: Data) -> Option<()> { + if let Data::SignChange(event) = data { + if let Some(uuid) = event.player_uuid + && let Ok(uuid) = uuid::Uuid::from_str(&uuid.value) + && let Some(player) = server.get_player_by_uuid(uuid) + { + self.player = player; } + if !event.block_key.is_empty() + && let Some(block) = Block::from_name(&event.block_key) + { + self.block = block; + } + if let Some(loc) = event.location + && let Some(pos) = location_to_vec3(loc.clone()) + { + self.block_pos = pumpkin_util::math::position::BlockPos::new( + pos.x.floor() as i32, + pos.y.floor() as i32, + pos.z.floor() as i32, + ); + } + if !event.lines.is_empty() { + self.lines = event.lines; + } + self.is_front_text = event.is_front_text; + } + Some(()) + } +} + +impl PatchBukkitEvent for pumpkin::plugin::api::events::block::tnt_prime::TNTPrimeEvent { + fn to_payload(&self, server: Arc) -> JvmEventPayload { + let location = build_location( + self.world_uuid, + &Vector3::new( + f64::from(self.block_pos.0.x), + f64::from(self.block_pos.0.y), + f64::from(self.block_pos.0.z), + ), + 0.0, + 0.0, + ); + let player_uuid = self.player.as_ref().map(|player| Uuid { + value: player.gameprofile.id.to_string(), + }); + + JvmEventPayload { + event: Event { + data: Some(Data::TntPrime(TntPrimeEvent { + player_uuid, + block_key: block_to_key(self.block), + location: Some(location), + cause: self.cause.clone(), + })), + }, + context: EventContext { + server, + player: self.player.clone(), + }, } + } + fn apply_modifications(&mut self, server: &Arc, data: Data) -> Option<()> { + if let Data::TntPrime(event) = data { + if let Some(uuid) = event.player_uuid { + if let Ok(uuid) = uuid::Uuid::from_str(&uuid.value) { + self.player = server.get_player_by_uuid(uuid); + } + } else { + self.player = None; + } + if !event.block_key.is_empty() + && let Some(block) = Block::from_name(&event.block_key) + { + self.block = block; + } + if let Some(loc) = event.location { + if let Some(pos) = location_to_vec3(loc.clone()) { + self.block_pos = pumpkin_util::math::position::BlockPos::new( + pos.x.floor() as i32, + pos.y.floor() as i32, + pos.z.floor() as i32, + ); + } + if let Some(world) = loc.world.and_then(|w| w.uuid) + && let Ok(uuid) = uuid::Uuid::from_str(&world.value) + { + self.world_uuid = uuid; + } + } + if !event.cause.is_empty() { + self.cause = event.cause; + } + } Some(()) } } +fn build_location( + world_uuid: uuid::Uuid, + position: &Vector3, + yaw: f32, + pitch: f32, +) -> Location { + Location { + world: Some(ProtoWorld { + uuid: Some(Uuid { + value: world_uuid.to_string(), + }), + }), + position: Some(Vec3 { + x: position.x, + y: position.y, + z: position.z, + }), + yaw, + pitch, + } +} + +fn location_to_vec3(location: Location) -> Option> { + let pos = location.position?; + Some(Vector3::new(pos.x, pos.y, pos.z)) +} + +fn block_to_key(block: &Block) -> String { + format!("minecraft:{}", block.name) +} + +fn item_to_key(item: &Item) -> String { + format!("minecraft:{}", item.registry_key) +} + +fn item_stack_from_key(key: &str, amount: u8) -> ItemStack { + let trimmed = key.strip_prefix("minecraft:").unwrap_or(key); + let item = Item::from_registry_key(trimmed).unwrap_or(&Item::AIR); + ItemStack::new(amount, item) +} + +fn equipment_slot_to_bukkit(slot: &EquipmentSlot) -> String { + match slot { + EquipmentSlot::MainHand(_) => "HAND".to_string(), + EquipmentSlot::OffHand(_) => "OFF_HAND".to_string(), + EquipmentSlot::Feet(_) => "FEET".to_string(), + EquipmentSlot::Legs(_) => "LEGS".to_string(), + EquipmentSlot::Chest(_) => "CHEST".to_string(), + EquipmentSlot::Head(_) => "HEAD".to_string(), + EquipmentSlot::Body(_) => "BODY".to_string(), + EquipmentSlot::Saddle(_) => "SADDLE".to_string(), + } +} + +fn equipment_slot_from_bukkit(slot: &str) -> Option { + match slot { + "HAND" => Some(EquipmentSlot::MAIN_HAND), + "OFF_HAND" => Some(EquipmentSlot::OFF_HAND), + "FEET" => Some(EquipmentSlot::FEET), + "LEGS" => Some(EquipmentSlot::LEGS), + "CHEST" => Some(EquipmentSlot::CHEST), + "HEAD" => Some(EquipmentSlot::HEAD), + "BODY" => Some(EquipmentSlot::BODY), + "SADDLE" => Some(EquipmentSlot::SADDLE), + _ => None, + } +} + +fn find_world_by_uuid(server: &Arc, world_uuid: uuid::Uuid) -> Option> { + server + .worlds + .load() + .iter() + .find(|world| world.uuid == world_uuid) + .cloned() +} + +fn gamemode_to_bukkit(mode: pumpkin_util::GameMode) -> String { + match mode { + pumpkin_util::GameMode::Survival => "SURVIVAL".to_string(), + pumpkin_util::GameMode::Creative => "CREATIVE".to_string(), + pumpkin_util::GameMode::Adventure => "ADVENTURE".to_string(), + pumpkin_util::GameMode::Spectator => "SPECTATOR".to_string(), + } +} + +fn gamemode_from_bukkit(mode: &str) -> Option { + match mode { + "SURVIVAL" => Some(pumpkin_util::GameMode::Survival), + "CREATIVE" => Some(pumpkin_util::GameMode::Creative), + "ADVENTURE" => Some(pumpkin_util::GameMode::Adventure), + "SPECTATOR" => Some(pumpkin_util::GameMode::Spectator), + _ => None, + } +} + +fn block_face_to_bukkit(face: Option) -> String { + match face { + Some(BlockDirection::Up) => "UP".to_string(), + Some(BlockDirection::Down) => "DOWN".to_string(), + Some(BlockDirection::North) => "NORTH".to_string(), + Some(BlockDirection::South) => "SOUTH".to_string(), + Some(BlockDirection::West) => "WEST".to_string(), + Some(BlockDirection::East) => "EAST".to_string(), + None => String::new(), + } +} + +fn bukkit_block_face_from_string(face: &str) -> Option { + match face { + "UP" => Some(BlockDirection::Up), + "DOWN" => Some(BlockDirection::Down), + "NORTH" => Some(BlockDirection::North), + "SOUTH" => Some(BlockDirection::South), + "WEST" => Some(BlockDirection::West), + "EAST" => Some(BlockDirection::East), + _ => None, + } +} + +fn instrument_to_bukkit(instrument: Instrument) -> String { + match instrument { + Instrument::Harp => "PIANO".to_string(), + Instrument::Basedrum => "BASS_DRUM".to_string(), + Instrument::Snare => "SNARE_DRUM".to_string(), + Instrument::Hat => "STICKS".to_string(), + Instrument::Bass => "BASS_GUITAR".to_string(), + Instrument::Flute => "FLUTE".to_string(), + Instrument::Bell => "BELL".to_string(), + Instrument::Guitar => "GUITAR".to_string(), + Instrument::Chime => "CHIME".to_string(), + Instrument::Xylophone => "XYLOPHONE".to_string(), + Instrument::IronXylophone => "IRON_XYLOPHONE".to_string(), + Instrument::CowBell => "COW_BELL".to_string(), + Instrument::Didgeridoo => "DIDGERIDOO".to_string(), + Instrument::Bit => "BIT".to_string(), + Instrument::Banjo => "BANJO".to_string(), + Instrument::Pling => "PLING".to_string(), + Instrument::Zombie => "ZOMBIE".to_string(), + Instrument::Skeleton => "SKELETON".to_string(), + Instrument::Creeper => "CREEPER".to_string(), + Instrument::Dragon => "DRAGON".to_string(), + Instrument::WitherSkeleton => "WITHER_SKELETON".to_string(), + Instrument::Piglin => "PIGLIN".to_string(), + Instrument::CustomHead => "CUSTOM_HEAD".to_string(), + } +} + +fn instrument_from_bukkit(name: &str) -> Option { + match name { + "PIANO" => Some(Instrument::Harp), + "BASS_DRUM" => Some(Instrument::Basedrum), + "SNARE_DRUM" => Some(Instrument::Snare), + "STICKS" => Some(Instrument::Hat), + "BASS_GUITAR" => Some(Instrument::Bass), + "FLUTE" => Some(Instrument::Flute), + "BELL" => Some(Instrument::Bell), + "GUITAR" => Some(Instrument::Guitar), + "CHIME" => Some(Instrument::Chime), + "XYLOPHONE" => Some(Instrument::Xylophone), + "IRON_XYLOPHONE" => Some(Instrument::IronXylophone), + "COW_BELL" => Some(Instrument::CowBell), + "DIDGERIDOO" => Some(Instrument::Didgeridoo), + "BIT" => Some(Instrument::Bit), + "BANJO" => Some(Instrument::Banjo), + "PLING" => Some(Instrument::Pling), + "ZOMBIE" => Some(Instrument::Zombie), + "SKELETON" => Some(Instrument::Skeleton), + "CREEPER" => Some(Instrument::Creeper), + "DRAGON" => Some(Instrument::Dragon), + "WITHER_SKELETON" => Some(Instrument::WitherSkeleton), + "PIGLIN" => Some(Instrument::Piglin), + "CUSTOM_HEAD" => Some(Instrument::CustomHead), + _ => None, + } +} + +fn interact_action_to_bukkit( + action: &pumpkin::plugin::api::events::player::player_interact_event::InteractAction, +) -> String { + match action { + pumpkin::plugin::api::events::player::player_interact_event::InteractAction::LeftClickBlock => { + "LEFT_CLICK_BLOCK".to_string() + } + pumpkin::plugin::api::events::player::player_interact_event::InteractAction::LeftClickAir => { + "LEFT_CLICK_AIR".to_string() + } + pumpkin::plugin::api::events::player::player_interact_event::InteractAction::RightClickAir => { + "RIGHT_CLICK_AIR".to_string() + } + pumpkin::plugin::api::events::player::player_interact_event::InteractAction::RightClickBlock => { + "RIGHT_CLICK_BLOCK".to_string() + } + } +} + +fn interact_action_from_bukkit( + action: &str, +) -> Option { + match action { + "LEFT_CLICK_BLOCK" => Some( + pumpkin::plugin::api::events::player::player_interact_event::InteractAction::LeftClickBlock, + ), + "LEFT_CLICK_AIR" => Some( + pumpkin::plugin::api::events::player::player_interact_event::InteractAction::LeftClickAir, + ), + "RIGHT_CLICK_AIR" => Some( + pumpkin::plugin::api::events::player::player_interact_event::InteractAction::RightClickAir, + ), + "RIGHT_CLICK_BLOCK" => Some( + pumpkin::plugin::api::events::player::player_interact_event::InteractAction::RightClickBlock, + ), + _ => None, + } +} + pub struct PatchBukkitEventHandler { plugin_name: String, command_tx: mpsc::Sender, _phantom: PhantomData, } +trait CancellationBridge { + fn apply_cancelled(&mut self, cancelled: bool); +} + +macro_rules! impl_cancellation_bridge { + ($($ty:path),+ $(,)?) => { + $( + impl CancellationBridge for $ty { + fn apply_cancelled(&mut self, cancelled: bool) { + self.set_cancelled(cancelled); + } + } + )+ + }; +} + +impl_cancellation_bridge!( + pumpkin::plugin::api::events::player::player_join::PlayerJoinEvent, + pumpkin::plugin::api::events::player::player_login::PlayerLoginEvent, + pumpkin::plugin::api::events::player::player_leave::PlayerLeaveEvent, + pumpkin::plugin::api::events::player::player_move::PlayerMoveEvent, + pumpkin::plugin::api::events::player::player_teleport::PlayerTeleportEvent, + pumpkin::plugin::api::events::player::player_change_world::PlayerChangeWorldEvent, + pumpkin::plugin::api::events::player::player_gamemode_change::PlayerGamemodeChangeEvent, + pumpkin::plugin::api::events::player::player_chat::PlayerChatEvent, + pumpkin::plugin::api::events::player::player_interact_event::PlayerInteractEvent, + pumpkin::plugin::api::events::player::player_bed_enter::PlayerBedEnterEvent, + pumpkin::plugin::api::events::player::player_bucket_empty::PlayerBucketEmptyEvent, + pumpkin::plugin::api::events::player::player_bucket_fill::PlayerBucketFillEvent, + pumpkin::plugin::api::events::player::player_drop_item::PlayerDropItemEvent, + pumpkin::plugin::api::events::player::player_item_consume::PlayerItemConsumeEvent, + pumpkin::plugin::api::events::player::player_item_damage::PlayerItemDamageEvent, + pumpkin::plugin::api::events::player::player_item_held::PlayerItemHeldEvent, + pumpkin::plugin::api::events::player::player_item_mend::PlayerItemMendEvent, + pumpkin::plugin::api::events::player::player_kick::PlayerKickEvent, + pumpkin::plugin::api::events::player::player_toggle_flight::PlayerToggleFlightEvent, + pumpkin::plugin::api::events::block::block_break::BlockBreakEvent, + pumpkin::plugin::api::events::block::block_burn::BlockBurnEvent, + pumpkin::plugin::api::events::block::block_can_build::BlockCanBuildEvent, + pumpkin::plugin::api::events::block::block_place::BlockPlaceEvent, + pumpkin::plugin::api::events::block::block_dispense::BlockDispenseEvent, + pumpkin::plugin::api::events::block::block_form::BlockFormEvent, + pumpkin::plugin::api::events::block::block_ignite::BlockIgniteEvent, + pumpkin::plugin::api::events::block::block_multi_place::BlockMultiPlaceEvent, + pumpkin::plugin::api::events::block::block_redstone::BlockRedstoneEvent, + pumpkin::plugin::api::events::block::moisture_change::MoistureChangeEvent, + pumpkin::plugin::api::events::block::note_play::NotePlayEvent, + pumpkin::plugin::api::events::block::sign_change::SignChangeEvent, + pumpkin::plugin::api::events::block::tnt_prime::TNTPrimeEvent, + pumpkin::plugin::api::events::server::server_command::ServerCommandEvent, + pumpkin::plugin::api::events::server::server_broadcast::ServerBroadcastEvent, +); + +impl CancellationBridge + for pumpkin::plugin::api::events::player::player_command_send::PlayerCommandSendEvent +{ + fn apply_cancelled(&mut self, _cancelled: bool) {} +} + +impl CancellationBridge + for pumpkin::plugin::api::events::player::player_exp_change::PlayerExpChangeEvent +{ + fn apply_cancelled(&mut self, _cancelled: bool) {} +} + +impl CancellationBridge + for pumpkin::plugin::api::events::player::player_item_break::PlayerItemBreakEvent +{ + fn apply_cancelled(&mut self, _cancelled: bool) {} +} + +impl CancellationBridge + for pumpkin::plugin::api::events::player::player_level_change::PlayerLevelChangeEvent +{ + fn apply_cancelled(&mut self, _cancelled: bool) {} +} + impl PatchBukkitEventHandler { #[must_use] pub const fn new(plugin_name: String, command_tx: mpsc::Sender) -> Self { @@ -78,7 +2061,7 @@ impl PatchBukkitEventHandler { #[with_runtime(global)] impl EventHandler for PatchBukkitEventHandler where - E: PatchBukkitEvent + Payload + Cancellable + 'static, + E: PatchBukkitEvent + Payload + CancellationBridge + 'static, { fn handle_blocking<'a>( &'a self, @@ -103,8 +2086,10 @@ where match rx.await { Ok(response) => { - event.set_cancelled(response.cancelled); - event.apply_modifications(server, response.data.unwrap().data.unwrap()); + event.apply_cancelled(response.cancelled); + if let Some(data) = response.data.and_then(|d| d.data) { + event.apply_modifications(server, data); + } } Err(_) => { log::warn!("JVM worker dropped response channel for event"); diff --git a/rust/src/java/jvm/commands.rs b/rust/src/java/jvm/commands.rs index a8301f6..d0c32cf 100644 --- a/rust/src/java/jvm/commands.rs +++ b/rust/src/java/jvm/commands.rs @@ -53,7 +53,7 @@ pub enum JvmCommand { TriggerCommand { full_command: String, command_sender: SimpleCommandSender, - respond_to: oneshot::Sender>, + respond_to: oneshot::Sender>, }, GetCommandTabComplete { command_sender: SimpleCommandSender, diff --git a/rust/src/java/native_callbacks/block.rs b/rust/src/java/native_callbacks/block.rs new file mode 100644 index 0000000..ea89540 --- /dev/null +++ b/rust/src/java/native_callbacks/block.rs @@ -0,0 +1,32 @@ +use pumpkin_util::math::position::BlockPos; + +use crate::{ + java::native_callbacks::CALLBACK_CONTEXT, + proto::patchbukkit::block::{GetBlockRequest, GetBlockResponse}, +}; + +pub fn ffi_native_bridge_get_block_impl(request: GetBlockRequest) -> Option { + let ctx = CALLBACK_CONTEXT.get()?; + let world = request.world?; + let world_uuid = world.uuid?.value; + let world_uuid = uuid::Uuid::parse_str(&world_uuid).ok()?; + + let server = ctx.plugin_context.server.clone(); + let block_key = tokio::task::block_in_place(|| { + ctx.runtime.block_on(async move { + let world = { + let worlds = server.worlds.load(); + // Clone the Arc here so the worlds read-guard is dropped before the await below. + worlds + .iter() + .find(|world| world.uuid == world_uuid)? + .clone() + }; + let pos = BlockPos::new(request.x, request.y, request.z); + let block = world.get_block(&pos).await; + Some(format!("minecraft:{}", block.name)) + }) + })?; + + Some(GetBlockResponse { block_key }) +} diff --git a/rust/src/java/native_callbacks/events.rs b/rust/src/java/native_callbacks/events.rs index 3b5fa28..3459cb0 100644 --- a/rust/src/java/native_callbacks/events.rs +++ b/rust/src/java/native_callbacks/events.rs @@ -1,8 +1,13 @@ use std::sync::Arc; use pumpkin::plugin::EventPriority; -use pumpkin::plugin::player::player_join::PlayerJoinEvent; +use pumpkin_data::item::Item; +use pumpkin_data::{Block, BlockDirection}; +use pumpkin_util::math::position::BlockPos; +use pumpkin_util::math::vector3::Vector3; use pumpkin_util::text::TextComponent; +use pumpkin_world::item::ItemStack; +use tokio::sync::Mutex; use crate::events::handler::PatchBukkitEventHandler; use crate::java::native_callbacks::CALLBACK_CONTEXT; @@ -34,13 +39,10 @@ pub fn ffi_native_bridge_register_event_impl(request: RegisterEventRequest) -> O tokio::task::block_in_place(|| { ctx.runtime.block_on(async { - match request.event_type.as_str() { - "org.bukkit.event.player.PlayerJoinEvent" => { + macro_rules! register_bridge_event { + ($event_ty:path) => { context - .register_event::< - pumpkin::plugin::player::player_join::PlayerJoinEvent, - PatchBukkitEventHandler, - >( + .register_event::<$event_ty, PatchBukkitEventHandler<$event_ty>>( Arc::new(PatchBukkitEventHandler::new( request.plugin_name.clone(), command_tx.clone(), @@ -49,15 +51,130 @@ pub fn ffi_native_bridge_register_event_impl(request: RegisterEventRequest) -> O request.blocking, ) .await; + }; + } + + match request.event_type.as_str() { + "org.bukkit.event.player.PlayerJoinEvent" => { + register_bridge_event!(pumpkin::plugin::api::events::player::player_join::PlayerJoinEvent); + } + "org.bukkit.event.player.PlayerLoginEvent" => { + register_bridge_event!(pumpkin::plugin::api::events::player::player_login::PlayerLoginEvent); + } + "org.bukkit.event.player.PlayerQuitEvent" => { + register_bridge_event!(pumpkin::plugin::api::events::player::player_leave::PlayerLeaveEvent); + } + "org.bukkit.event.player.PlayerMoveEvent" => { + register_bridge_event!(pumpkin::plugin::api::events::player::player_move::PlayerMoveEvent); + } + "org.bukkit.event.player.PlayerTeleportEvent" => { + register_bridge_event!(pumpkin::plugin::api::events::player::player_teleport::PlayerTeleportEvent); + } + "org.bukkit.event.player.PlayerChangedWorldEvent" => { + register_bridge_event!(pumpkin::plugin::api::events::player::player_change_world::PlayerChangeWorldEvent); + } + "org.bukkit.event.player.PlayerGameModeChangeEvent" => { + register_bridge_event!(pumpkin::plugin::api::events::player::player_gamemode_change::PlayerGamemodeChangeEvent); + } + "org.bukkit.event.player.AsyncPlayerChatEvent" + | "org.bukkit.event.player.PlayerChatEvent" => { + register_bridge_event!(pumpkin::plugin::api::events::player::player_chat::PlayerChatEvent); + } + "org.bukkit.event.player.PlayerCommandSendEvent" => { + register_bridge_event!(pumpkin::plugin::api::events::player::player_command_send::PlayerCommandSendEvent); + } + "org.bukkit.event.player.PlayerInteractEvent" => { + register_bridge_event!(pumpkin::plugin::api::events::player::player_interact_event::PlayerInteractEvent); + } + "org.bukkit.event.block.BlockBreakEvent" => { + register_bridge_event!(pumpkin::plugin::api::events::block::block_break::BlockBreakEvent); + } + "org.bukkit.event.block.BlockBurnEvent" => { + register_bridge_event!(pumpkin::plugin::api::events::block::block_burn::BlockBurnEvent); + } + "org.bukkit.event.block.BlockCanBuildEvent" => { + register_bridge_event!(pumpkin::plugin::api::events::block::block_can_build::BlockCanBuildEvent); + } + "org.bukkit.event.block.BlockPlaceEvent" => { + register_bridge_event!(pumpkin::plugin::api::events::block::block_place::BlockPlaceEvent); + } + "org.bukkit.event.server.ServerCommandEvent" => { + register_bridge_event!(pumpkin::plugin::api::events::server::server_command::ServerCommandEvent); + } + "org.bukkit.event.server.BroadcastMessageEvent" => { + register_bridge_event!(pumpkin::plugin::api::events::server::server_broadcast::ServerBroadcastEvent); + } + "org.bukkit.event.player.PlayerBedEnterEvent" => { + register_bridge_event!(pumpkin::plugin::api::events::player::player_bed_enter::PlayerBedEnterEvent); + } + "org.bukkit.event.player.PlayerBucketEmptyEvent" => { + register_bridge_event!(pumpkin::plugin::api::events::player::player_bucket_empty::PlayerBucketEmptyEvent); + } + "org.bukkit.event.player.PlayerBucketFillEvent" => { + register_bridge_event!(pumpkin::plugin::api::events::player::player_bucket_fill::PlayerBucketFillEvent); + } + "org.bukkit.event.player.PlayerDropItemEvent" => { + register_bridge_event!(pumpkin::plugin::api::events::player::player_drop_item::PlayerDropItemEvent); + } + "org.bukkit.event.player.PlayerExpChangeEvent" => { + register_bridge_event!(pumpkin::plugin::api::events::player::player_exp_change::PlayerExpChangeEvent); + } + "org.bukkit.event.player.PlayerItemBreakEvent" => { + register_bridge_event!(pumpkin::plugin::api::events::player::player_item_break::PlayerItemBreakEvent); + } + "org.bukkit.event.player.PlayerItemConsumeEvent" => { + register_bridge_event!(pumpkin::plugin::api::events::player::player_item_consume::PlayerItemConsumeEvent); + } + "org.bukkit.event.player.PlayerItemDamageEvent" => { + register_bridge_event!(pumpkin::plugin::api::events::player::player_item_damage::PlayerItemDamageEvent); + } + "org.bukkit.event.player.PlayerItemHeldEvent" => { + register_bridge_event!(pumpkin::plugin::api::events::player::player_item_held::PlayerItemHeldEvent); + } + "org.bukkit.event.player.PlayerItemMendEvent" => { + register_bridge_event!(pumpkin::plugin::api::events::player::player_item_mend::PlayerItemMendEvent); + } + "org.bukkit.event.player.PlayerKickEvent" => { + register_bridge_event!(pumpkin::plugin::api::events::player::player_kick::PlayerKickEvent); + } + "org.bukkit.event.player.PlayerLevelChangeEvent" => { + register_bridge_event!(pumpkin::plugin::api::events::player::player_level_change::PlayerLevelChangeEvent); + } + "org.bukkit.event.player.PlayerToggleFlightEvent" => { + register_bridge_event!(pumpkin::plugin::api::events::player::player_toggle_flight::PlayerToggleFlightEvent); + } + "org.bukkit.event.block.BlockDispenseEvent" => { + register_bridge_event!(pumpkin::plugin::api::events::block::block_dispense::BlockDispenseEvent); + } + "org.bukkit.event.block.BlockFormEvent" => { + register_bridge_event!(pumpkin::plugin::api::events::block::block_form::BlockFormEvent); + } + "org.bukkit.event.block.BlockIgniteEvent" => { + register_bridge_event!(pumpkin::plugin::api::events::block::block_ignite::BlockIgniteEvent); + } + "org.bukkit.event.block.BlockMultiPlaceEvent" => { + register_bridge_event!(pumpkin::plugin::api::events::block::block_multi_place::BlockMultiPlaceEvent); + } + "org.bukkit.event.block.BlockRedstoneEvent" => { + register_bridge_event!(pumpkin::plugin::api::events::block::block_redstone::BlockRedstoneEvent); + } + "org.bukkit.event.block.MoistureChangeEvent" => { + register_bridge_event!(pumpkin::plugin::api::events::block::moisture_change::MoistureChangeEvent); + } + "org.bukkit.event.block.NotePlayEvent" => { + register_bridge_event!(pumpkin::plugin::api::events::block::note_play::NotePlayEvent); + } + "org.bukkit.event.block.SignChangeEvent" => { + register_bridge_event!(pumpkin::plugin::api::events::block::sign_change::SignChangeEvent); + } + "org.bukkit.event.block.TNTPrimeEvent" => { + register_bridge_event!(pumpkin::plugin::api::events::block::tnt_prime::TNTPrimeEvent); } _ => { - log::warn!( - "Unsupported Bukkit event type '{}' from plugin '{}'", - request.event_type, request.plugin_name - ); + log::warn!("Unsupported Bukkit event registration: {}", request.event_type); } } - }); + }) }); Some(()) @@ -65,28 +182,342 @@ pub fn ffi_native_bridge_register_event_impl(request: RegisterEventRequest) -> O pub fn ffi_native_bridge_call_event_impl(request: CallEventRequest) -> Option { let ctx = CALLBACK_CONTEXT.get()?; - let event = request.event?; - log::debug!("Java calling event {:?}", event); - - let context = ctx.plugin_context.clone(); let handled = tokio::task::block_in_place(|| { ctx.runtime.block_on(async { - match event.data? { - Data::PlayerJoin(player_join_event_data) => { - let uuid = - uuid::Uuid::parse_str(&player_join_event_data.player_uuid?.value).ok()?; - let player = context.server.get_player_by_uuid(uuid)?; - let pumpkin_event = PlayerJoinEvent::new( - player, - TextComponent::from_legacy_string(&player_join_event_data.join_message), - ); - context.server.plugin_manager.fire(pumpkin_event).await; + let Some(data) = request.event.and_then(|event| event.data) else { + return Some(false); + }; + + match data { + Data::PlayerChat(event) => { + let uuid = uuid::Uuid::parse_str(&event.player_uuid?.value).ok()?; + let player = ctx.plugin_context.server.get_player_by_uuid(uuid)?; + let mut recipients = Vec::new(); + for recipient in event.recipients { + let rid = uuid::Uuid::parse_str(&recipient.value).ok()?; + if let Some(recipient_player) = ctx.plugin_context.server.get_player_by_uuid(rid) { + recipients.push(recipient_player); + } + } + let pumpkin_event = + pumpkin::plugin::api::events::player::player_chat::PlayerChatEvent::new( + player, + event.message, + recipients, + ); + ctx.plugin_context.server.plugin_manager.fire(pumpkin_event).await; + Some(true) + } + Data::PlayerCommandSend(event) => { + let uuid = uuid::Uuid::parse_str(&event.player_uuid?.value).ok()?; + let player = ctx.plugin_context.server.get_player_by_uuid(uuid)?; + let commands = if event.commands.is_empty() { + vec![String::new()] + } else { + event.commands + }; + let pumpkin_event = + pumpkin::plugin::api::events::player::player_command_send::PlayerCommandSendEvent::new( + player, + commands, + ); + ctx.plugin_context.server.plugin_manager.fire(pumpkin_event).await; + Some(true) + } + Data::PlayerInteract(event) => { + let uuid = uuid::Uuid::parse_str(&event.player_uuid?.value).ok()?; + let player = ctx.plugin_context.server.get_player_by_uuid(uuid)?; + let action = match event.action.as_str() { + "LEFT_CLICK_BLOCK" => pumpkin::plugin::api::events::player::player_interact_event::InteractAction::LeftClickBlock, + "LEFT_CLICK_AIR" => pumpkin::plugin::api::events::player::player_interact_event::InteractAction::LeftClickAir, + "RIGHT_CLICK_AIR" => pumpkin::plugin::api::events::player::player_interact_event::InteractAction::RightClickAir, + "RIGHT_CLICK_BLOCK" => pumpkin::plugin::api::events::player::player_interact_event::InteractAction::RightClickBlock, + _ => pumpkin::plugin::api::events::player::player_interact_event::InteractAction::RightClickAir, + }; + let block = block_from_key(&event.block_key); + let clicked_pos = event + .clicked + .and_then(location_to_vec3) + .map(|pos| pumpkin_util::math::position::BlockPos::new( + pos.x.floor() as i32, + pos.y.floor() as i32, + pos.z.floor() as i32, + )); + let item = Arc::new(Mutex::new(item_stack_from_key(&event.item_key, 1))); + let item_key = event.item_key; + let block_face = block_face_from_bukkit(&event.block_face); + let pumpkin_event = + pumpkin::plugin::api::events::player::player_interact_event::PlayerInteractEvent::new( + &player, + action, + &item, + item_key, + block, + clicked_pos, + block_face, + ); + ctx.plugin_context.server.plugin_manager.fire(pumpkin_event).await; + Some(true) + } + Data::PlayerMove(event) => { + let uuid = uuid::Uuid::parse_str(&event.player_uuid?.value).ok()?; + let player = ctx.plugin_context.server.get_player_by_uuid(uuid)?; + let from = event.from.and_then(location_to_vec3).unwrap_or_else(Vector3::default); + let to = event.to.and_then(location_to_vec3).unwrap_or_else(Vector3::default); + let pumpkin_event = + pumpkin::plugin::api::events::player::player_move::PlayerMoveEvent::new( + player, + from, + to, + ); + ctx.plugin_context.server.plugin_manager.fire(pumpkin_event).await; + Some(true) + } + Data::PlayerTeleport(event) => { + let uuid = uuid::Uuid::parse_str(&event.player_uuid?.value).ok()?; + let player = ctx.plugin_context.server.get_player_by_uuid(uuid)?; + let from = event.from.and_then(location_to_vec3).unwrap_or_else(Vector3::default); + let to = event.to.and_then(location_to_vec3).unwrap_or_else(Vector3::default); + let pumpkin_event = + pumpkin::plugin::api::events::player::player_teleport::PlayerTeleportEvent::new( + player, + from, + to, + ); + ctx.plugin_context.server.plugin_manager.fire(pumpkin_event).await; Some(true) } + Data::PlayerChangeWorld(event) => { + let uuid = uuid::Uuid::parse_str(&event.player_uuid?.value).ok()?; + let player = ctx.plugin_context.server.get_player_by_uuid(uuid)?; + let previous_world = event + .previous_world + .and_then(|w| w.uuid) + .and_then(|id| uuid::Uuid::parse_str(&id.value).ok()) + .and_then(|id| find_world_by_uuid(&ctx.plugin_context.server, id)) + .unwrap_or_else(|| ctx.plugin_context.server.worlds.load()[0].clone()); + let new_world = event + .new_world + .and_then(|w| w.uuid) + .and_then(|id| uuid::Uuid::parse_str(&id.value).ok()) + .and_then(|id| find_world_by_uuid(&ctx.plugin_context.server, id)) + .unwrap_or_else(|| ctx.plugin_context.server.worlds.load()[0].clone()); + let position = event + .position + .and_then(location_to_vec3) + .unwrap_or_else(Vector3::default); + let pumpkin_event = + pumpkin::plugin::api::events::player::player_change_world::PlayerChangeWorldEvent::new( + player, + previous_world, + new_world, + position, + event.yaw, + event.pitch, + ); + ctx.plugin_context.server.plugin_manager.fire(pumpkin_event).await; + Some(true) + } + Data::PlayerGamemodeChange(event) => { + let uuid = uuid::Uuid::parse_str(&event.player_uuid?.value).ok()?; + let player = ctx.plugin_context.server.get_player_by_uuid(uuid)?; + let previous = gamemode_from_bukkit(&event.previous_gamemode) + .unwrap_or(pumpkin_util::GameMode::Survival); + let new_mode = gamemode_from_bukkit(&event.new_gamemode) + .unwrap_or(pumpkin_util::GameMode::Survival); + let pumpkin_event = + pumpkin::plugin::api::events::player::player_gamemode_change::PlayerGamemodeChangeEvent::new( + player, + previous, + new_mode, + ); + ctx.plugin_context.server.plugin_manager.fire(pumpkin_event).await; + Some(true) + } + Data::BlockBreak(event) => { + let player = event + .player_uuid + .and_then(|id| uuid::Uuid::parse_str(&id.value).ok()) + .and_then(|id| ctx.plugin_context.server.get_player_by_uuid(id)); + let block = block_from_key(&event.block_key); + let position = event + .location + .and_then(|loc| loc.position) + .map(|pos| { + pumpkin_util::math::position::BlockPos::new( + pos.x as i32, + pos.y as i32, + pos.z as i32, + ) + }) + .unwrap_or_else(|| pumpkin_util::math::position::BlockPos::new(0, 0, 0)); + let pumpkin_event = + pumpkin::plugin::api::events::block::block_break::BlockBreakEvent::new( + player, + block, + position, + event.exp, + event.drop, + ); + ctx.plugin_context.server.plugin_manager.fire(pumpkin_event).await; + Some(true) + } + Data::BlockBurn(event) => { + let block = block_from_key(&event.block_key); + let igniting_block = block_from_key(&event.igniting_block_key); + let block_pos = location_to_block_pos(event.location.as_ref()); + let world_uuid = + location_world_uuid(event.location.as_ref(), &ctx.plugin_context.server); + let pumpkin_event = + pumpkin::plugin::api::events::block::block_burn::BlockBurnEvent { + igniting_block, + block, + block_pos, + world_uuid, + cancelled: false, + }; + ctx.plugin_context.server.plugin_manager.fire(pumpkin_event).await; + Some(true) + } + Data::BlockCanBuild(event) => { + let uuid = uuid::Uuid::parse_str(&event.player_uuid?.value).ok()?; + let player = ctx.plugin_context.server.get_player_by_uuid(uuid)?; + let block_to_build = block_from_key(&event.block_key); + let block_against = block_from_key(&event.block_against_key); + let block_pos = location_to_block_pos(event.location.as_ref()); + let pumpkin_event = + pumpkin::plugin::api::events::block::block_can_build::BlockCanBuildEvent { + block_to_build, + buildable: event.can_build, + player, + block: block_against, + block_pos, + cancelled: false, + }; + ctx.plugin_context.server.plugin_manager.fire(pumpkin_event).await; + Some(true) + } + Data::BlockPlace(event) => { + let uuid = uuid::Uuid::parse_str(&event.player_uuid?.value).ok()?; + let player = ctx.plugin_context.server.get_player_by_uuid(uuid)?; + let block = block_from_key(&event.block_key); + let against = block_from_key(&event.block_against_key); + let position = location_to_block_pos(event.location.as_ref()); + let pumpkin_event = + pumpkin::plugin::api::events::block::block_place::BlockPlaceEvent { + player, + block_placed: block, + block_placed_against: against, + position, + can_build: event.can_build, + cancelled: false, + }; + ctx.plugin_context.server.plugin_manager.fire(pumpkin_event).await; + Some(true) + } + Data::ServerCommand(event) => { + let pumpkin_event = + pumpkin::plugin::api::events::server::server_command::ServerCommandEvent::new( + event.command, + ); + ctx.plugin_context.server.plugin_manager.fire(pumpkin_event).await; + Some(true) + } + Data::ServerBroadcast(event) => { + let message = serde_json::from_str(&event.message) + .unwrap_or_else(|_| TextComponent::from_legacy_string(&event.message)); + let sender = serde_json::from_str(&event.sender) + .unwrap_or_else(|_| TextComponent::from_legacy_string(&event.sender)); + let pumpkin_event = + pumpkin::plugin::api::events::server::server_broadcast::ServerBroadcastEvent::new( + message, + sender, + ); + ctx.plugin_context.server.plugin_manager.fire(pumpkin_event).await; + Some(true) + } + _ => Some(false), } }) })?; Some(CallEventResponse { handled }) } + +fn block_from_key(key: &str) -> &'static Block { + let trimmed = key.strip_prefix("minecraft:").unwrap_or(key); + Block::from_registry_key(trimmed).unwrap_or(&Block::AIR) +} + +fn item_stack_from_key(key: &str, amount: i32) -> ItemStack { + let trimmed = key.strip_prefix("minecraft:").unwrap_or(key); + let item = Item::from_registry_key(trimmed).unwrap_or(&Item::AIR); + let count = amount.clamp(0, u8::MAX as i32) as u8; + ItemStack::new(count, item) +} + +fn location_to_vec3(location: crate::proto::patchbukkit::common::Location) -> Option> { + let pos = location.position?; + Some(Vector3::new(pos.x, pos.y, pos.z)) +} + +fn gamemode_from_bukkit(mode: &str) -> Option { + match mode { + "SURVIVAL" => Some(pumpkin_util::GameMode::Survival), + "CREATIVE" => Some(pumpkin_util::GameMode::Creative), + "ADVENTURE" => Some(pumpkin_util::GameMode::Adventure), + "SPECTATOR" => Some(pumpkin_util::GameMode::Spectator), + _ => None, + } +} + +fn block_face_from_bukkit(face: &str) -> Option { + match face { + "DOWN" => Some(BlockDirection::Down), + "UP" => Some(BlockDirection::Up), + "NORTH" => Some(BlockDirection::North), + "SOUTH" => Some(BlockDirection::South), + "WEST" => Some(BlockDirection::West), + "EAST" => Some(BlockDirection::East), + _ => None, + } +} + +fn location_to_block_pos( + location: Option<&crate::proto::patchbukkit::common::Location>, +) -> BlockPos { + let pos = location.and_then(|loc| loc.position); + match pos { + Some(pos) => BlockPos::new( + pos.x.floor() as i32, + pos.y.floor() as i32, + pos.z.floor() as i32, + ), + None => BlockPos::new(0, 0, 0), + } +} + +fn location_world_uuid( + location: Option<&crate::proto::patchbukkit::common::Location>, + server: &std::sync::Arc, +) -> uuid::Uuid { + location + .and_then(|loc| loc.world.as_ref()) + .and_then(|world| world.uuid.as_ref()) + .and_then(|id| uuid::Uuid::parse_str(&id.value).ok()) + .or_else(|| server.worlds.load().first().map(|world| world.uuid)) + .unwrap_or_else(uuid::Uuid::new_v4) +} + +fn find_world_by_uuid( + server: &std::sync::Arc, + world_uuid: uuid::Uuid, +) -> Option> { + server + .worlds + .load() + .iter() + .find(|world| world.uuid == world_uuid) + .cloned() +} diff --git a/rust/src/java/native_callbacks/mod.rs b/rust/src/java/native_callbacks/mod.rs index dc65f60..f889df9 100644 --- a/rust/src/java/native_callbacks/mod.rs +++ b/rust/src/java/native_callbacks/mod.rs @@ -22,6 +22,9 @@ pub use message::*; pub mod registry; pub use registry::*; +pub mod block; +pub use block::*; + pub mod sound; pub use sound::*; diff --git a/rust/src/java/plugin/command_manager.rs b/rust/src/java/plugin/command_manager.rs index d96a8c5..bfa0100 100644 --- a/rust/src/java/plugin/command_manager.rs +++ b/rust/src/java/plugin/command_manager.rs @@ -217,7 +217,7 @@ impl CommandManager { jvm: &Jvm, full_command: String, sender: SimpleCommandSender, - ) -> Result<()> { + ) -> Result { let command_map = match self.command_map { Some(ref command_map) => command_map, None => match self.init(jvm) { @@ -239,11 +239,7 @@ impl CommandManager { let handled: bool = jvm.to_rust(dispatch_result)?; - if !handled { - //log::warn!("Command was not handled by any Java plugin: {}", cmd_name); - } - - Ok(()) + Ok(handled) } pub fn sender_to_jsender(jvm: &Jvm, sender: SimpleCommandSender) -> Result { diff --git a/rust/src/java/plugin/event_manager.rs b/rust/src/java/plugin/event_manager.rs index f295f98..e24e549 100644 --- a/rust/src/java/plugin/event_manager.rs +++ b/rust/src/java/plugin/event_manager.rs @@ -35,7 +35,7 @@ impl EventManager { let event_manager = jvm.invoke(&patch_server, "getEventManager", InvocationArg::empty())?; if let Some(ref event) = payload.event.data - && matches!(event, Data::PlayerJoin(_)) + && matches!(event, Data::PlayerJoin(_) | Data::PlayerLogin(_)) && let Some(ref player) = payload.context.player { Self::register_player(jvm, &patch_server, player, &payload.context.server)?; diff --git a/rust/src/proto/mod.rs b/rust/src/proto/mod.rs index 64a6435..abbd684 100644 --- a/rust/src/proto/mod.rs +++ b/rust/src/proto/mod.rs @@ -23,6 +23,10 @@ pub mod patchbukkit { include!(concat!(env!("OUT_DIR"), "/patchbukkit.registry.rs")); } + pub mod block { + include!(concat!(env!("OUT_DIR"), "/patchbukkit.block.rs")); + } + pub mod sound { include!(concat!(env!("OUT_DIR"), "/patchbukkit.sound.rs")); }