From 5539f5cd8b54d0789f340b88d2f1f4a156b56777 Mon Sep 17 00:00:00 2001
From: Tofaa <82680183+Tofaa2@users.noreply.github.com>
Date: Tue, 5 Dec 2023 17:36:01 +0300
Subject: [PATCH] add living entity

---
 .../java/me/tofaa/entitylib/EntityLib.java    |  18 ++-
 .../tofaa/entitylib/entity/WrapperEntity.java |  14 +--
 .../entity/WrapperEntityEquipment.java        | 116 ++++++++++++++++++
 .../entitylib/entity/WrapperLivingEntity.java |  22 ++++
 .../me/tofaa/entitylib/TestEntityCommand.java |  13 +-
 5 files changed, 172 insertions(+), 11 deletions(-)
 create mode 100644 src/main/java/me/tofaa/entitylib/entity/WrapperEntityEquipment.java
 create mode 100644 src/main/java/me/tofaa/entitylib/entity/WrapperLivingEntity.java

diff --git a/src/main/java/me/tofaa/entitylib/EntityLib.java b/src/main/java/me/tofaa/entitylib/EntityLib.java
index 8cab681..01237f4 100644
--- a/src/main/java/me/tofaa/entitylib/EntityLib.java
+++ b/src/main/java/me/tofaa/entitylib/EntityLib.java
@@ -3,6 +3,7 @@ package me.tofaa.entitylib;
 import com.github.retrooper.packetevents.PacketEventsAPI;
 import com.github.retrooper.packetevents.event.PacketListenerAbstract;
 import com.github.retrooper.packetevents.event.PacketReceiveEvent;
+import com.github.retrooper.packetevents.manager.server.ServerVersion;
 import com.github.retrooper.packetevents.protocol.entity.type.EntityType;
 import com.github.retrooper.packetevents.protocol.packettype.PacketType;
 import com.github.retrooper.packetevents.protocol.packettype.PacketTypeCommon;
@@ -10,8 +11,11 @@ import com.github.retrooper.packetevents.wrapper.PacketWrapper;
 import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientInteractEntity;
 import me.tofaa.entitylib.entity.EntityInteractionProcessor;
 import me.tofaa.entitylib.entity.WrapperEntity;
+import me.tofaa.entitylib.entity.WrapperLivingEntity;
+import me.tofaa.entitylib.exception.InvalidVersionException;
 import me.tofaa.entitylib.meta.EntityMeta;
 import me.tofaa.entitylib.meta.Metadata;
+import me.tofaa.entitylib.meta.types.LivingEntityMeta;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
@@ -87,7 +91,13 @@ public final class EntityLib {
         int id = WrapperEntity.ID_PROVIDER.provide();
         EntityMeta meta = createMeta(id, entityType);
         if (meta == null) return null;
-        WrapperEntity entity = new WrapperEntity(uuid, entityType, meta);
+        WrapperEntity entity;
+        if (meta instanceof LivingEntityMeta) {
+            entity = new WrapperLivingEntity(uuid, entityType, meta);
+        }
+        else {
+            entity = new WrapperEntity(uuid, entityType, meta);
+        }
         entities.put(uuid, entity);
         entitiesById.put(id, entity);
         return entity;
@@ -145,6 +155,12 @@ public final class EntityLib {
         EntityLib.interactionProcessor = interactionProcessor;
     }
 
+    public static void verifyVersion(ServerVersion supported, String msg) {
+        if (packetEvents.getServerManager().getVersion().isOlderThan(supported)) {
+            throw new InvalidVersionException(msg);
+        }
+    }
+
     private static void checkInit() {
         if (!initialized) {
             throw new IllegalStateException("EntityLib is not initialized");
diff --git a/src/main/java/me/tofaa/entitylib/entity/WrapperEntity.java b/src/main/java/me/tofaa/entitylib/entity/WrapperEntity.java
index 6d677ba..bcaa565 100644
--- a/src/main/java/me/tofaa/entitylib/entity/WrapperEntity.java
+++ b/src/main/java/me/tofaa/entitylib/entity/WrapperEntity.java
@@ -4,10 +4,7 @@ import com.github.retrooper.packetevents.protocol.entity.type.EntityType;
 import com.github.retrooper.packetevents.protocol.player.User;
 import com.github.retrooper.packetevents.protocol.world.Location;
 import com.github.retrooper.packetevents.wrapper.PacketWrapper;
-import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerDestroyEntities;
-import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityRotation;
-import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityTeleport;
-import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerSpawnEntity;
+import com.github.retrooper.packetevents.wrapper.play.server.*;
 import me.tofaa.entitylib.EntityLib;
 import me.tofaa.entitylib.meta.EntityMeta;
 import org.jetbrains.annotations.NotNull;
@@ -23,7 +20,6 @@ public class WrapperEntity {
     private final Optional<UUID> uuid;
     private final EntityMeta meta;
     private final Set<UUID> viewers = new HashSet<>();
-
     private Location location;
     private boolean onGround;
     private boolean spawned;
@@ -61,8 +57,8 @@ public class WrapperEntity {
         );
     }
 
-    public @NotNull Collection<UUID> getViewers() {
-        return Collections.unmodifiableCollection(viewers);
+    public void rotateHead(Location location) {
+        rotateHead(location.getYaw(), location.getPitch());
     }
 
 
@@ -80,6 +76,10 @@ public class WrapperEntity {
         );
     }
 
+    public @NotNull Collection<UUID> getViewers() {
+        return Collections.unmodifiableCollection(viewers);
+    }
+
     public void teleport(Location location) {
         teleport(location, true);
     }
diff --git a/src/main/java/me/tofaa/entitylib/entity/WrapperEntityEquipment.java b/src/main/java/me/tofaa/entitylib/entity/WrapperEntityEquipment.java
new file mode 100644
index 0000000..a834d1c
--- /dev/null
+++ b/src/main/java/me/tofaa/entitylib/entity/WrapperEntityEquipment.java
@@ -0,0 +1,116 @@
+package me.tofaa.entitylib.entity;
+
+import com.github.retrooper.packetevents.manager.server.ServerVersion;
+import com.github.retrooper.packetevents.protocol.item.ItemStack;
+import com.github.retrooper.packetevents.protocol.player.Equipment;
+import com.github.retrooper.packetevents.protocol.player.EquipmentSlot;
+import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityEquipment;
+import me.tofaa.entitylib.EntityLib;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class WrapperEntityEquipment {
+
+    private static final EquipmentSlot[] EQUIPMENT_SLOTS = EquipmentSlot.values();
+
+    private final WrapperLivingEntity entity;
+
+
+    // 0 = main hand, 1 = offhand, 2 = boots, 3 = leggings, 4 = chestplate, 5 = helmet
+    private final ItemStack[] equipment = new ItemStack[6];
+
+    public WrapperEntityEquipment(WrapperLivingEntity entity) {
+        this.entity = entity;
+        Arrays.fill(equipment, ItemStack.EMPTY);
+    }
+
+    public void setHelmet(@NotNull ItemStack itemStack) {
+        equipment[5] = itemStack;
+        refresh();
+    }
+
+    public void setChestplate(@NotNull ItemStack itemStack) {
+        equipment[4] = itemStack;
+        refresh();
+    }
+
+    public void setLeggings(@NotNull ItemStack itemStack) {
+        equipment[3] = itemStack;
+        refresh();
+    }
+
+    public void setBoots(@NotNull ItemStack itemStack) {
+        equipment[2] = itemStack;
+        refresh();
+    }
+
+    public void setMainHand(@NotNull ItemStack itemStack) {
+        equipment[0] = itemStack;
+        refresh();
+    }
+
+    public void setOffhand(@NotNull ItemStack itemStack) {
+        EntityLib.verifyVersion(ServerVersion.V_1_9, "Offhand is only supported on 1.9+");
+        equipment[1] = itemStack;
+        refresh();
+    }
+
+    public void setItem(@NotNull EquipmentSlot slot, @NotNull ItemStack itemStack) {
+        equipment[slot.ordinal()]  = itemStack;
+        refresh();
+    }
+
+    public @NotNull ItemStack getItem(@NotNull EquipmentSlot slot) {
+        ItemStack itemStack = equipment[slot.ordinal()];
+        if (itemStack == null) {
+            return ItemStack.EMPTY;
+        }
+        return itemStack;
+    }
+
+    public @NotNull ItemStack getHelmet() {
+        return getItem(EquipmentSlot.HELMET);
+    }
+
+    public @NotNull ItemStack getChestplate() {
+        return getItem(EquipmentSlot.CHEST_PLATE);
+    }
+
+    public @NotNull ItemStack getLeggings() {
+        return getItem(EquipmentSlot.LEGGINGS);
+    }
+
+    public @NotNull ItemStack getBoots() {
+        return getItem(EquipmentSlot.BOOTS);
+    }
+
+    public @NotNull ItemStack getMainHand() {
+        return getItem(EquipmentSlot.MAIN_HAND);
+    }
+
+    public @NotNull ItemStack getOffhand() {
+        EntityLib.verifyVersion(ServerVersion.V_1_9, "Offhand is only supported on 1.9+");
+        return getItem(EquipmentSlot.OFF_HAND);
+    }
+
+    public WrapperPlayServerEntityEquipment createPacket() {
+        List<Equipment> equipment = new ArrayList<>();
+        for (int i = 0; i < this.equipment.length; i++) {
+            ItemStack itemStack = this.equipment[i];
+            if (itemStack == null || itemStack.equals(ItemStack.EMPTY)) continue;
+            equipment.add(new Equipment(EQUIPMENT_SLOTS[i], itemStack));
+        }
+        return new WrapperPlayServerEntityEquipment(
+                entity.getEntityId(),
+                equipment
+        );
+    }
+
+    public void refresh() {
+        this.entity.sendPacketToViewers(createPacket());
+    }
+
+}
diff --git a/src/main/java/me/tofaa/entitylib/entity/WrapperLivingEntity.java b/src/main/java/me/tofaa/entitylib/entity/WrapperLivingEntity.java
new file mode 100644
index 0000000..9f4d02c
--- /dev/null
+++ b/src/main/java/me/tofaa/entitylib/entity/WrapperLivingEntity.java
@@ -0,0 +1,22 @@
+package me.tofaa.entitylib.entity;
+
+import com.github.retrooper.packetevents.protocol.entity.type.EntityType;
+import me.tofaa.entitylib.meta.EntityMeta;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.UUID;
+
+public class WrapperLivingEntity extends WrapperEntity{
+
+    private final WrapperEntityEquipment equipment;
+
+    public WrapperLivingEntity(@NotNull UUID uuid, EntityType entityType, EntityMeta meta) {
+        super(uuid, entityType, meta);
+        this.equipment = new WrapperEntityEquipment(this);
+    }
+
+    public WrapperEntityEquipment getEquipment() {
+        return equipment;
+    }
+
+}
diff --git a/test-plugin/src/main/java/me/tofaa/entitylib/TestEntityCommand.java b/test-plugin/src/main/java/me/tofaa/entitylib/TestEntityCommand.java
index d6fee53..7d44661 100644
--- a/test-plugin/src/main/java/me/tofaa/entitylib/TestEntityCommand.java
+++ b/test-plugin/src/main/java/me/tofaa/entitylib/TestEntityCommand.java
@@ -2,12 +2,16 @@ package me.tofaa.entitylib;
 
 import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
 import com.github.retrooper.packetevents.protocol.world.Location;
+import io.github.retrooper.packetevents.util.SpigotConversionUtil;
 import me.tofaa.entitylib.entity.WrapperEntity;
+import me.tofaa.entitylib.entity.WrapperLivingEntity;
 import me.tofaa.entitylib.meta.EntityMeta;
+import org.bukkit.Material;
 import org.bukkit.command.Command;
 import org.bukkit.command.CommandExecutor;
 import org.bukkit.command.CommandSender;
 import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
 import org.jetbrains.annotations.NotNull;
 
 import java.util.Collections;
@@ -15,7 +19,7 @@ import java.util.UUID;
 
 public class TestEntityCommand implements CommandExecutor {
 
-    private WrapperEntity entity;
+    private WrapperLivingEntity entity;
 
     @Override
     public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
@@ -23,7 +27,7 @@ public class TestEntityCommand implements CommandExecutor {
         Player player = (Player) sender;
 
         if (entity == null) {
-            entity = EntityLib.createEntity(UUID.randomUUID(), EntityTypes.PIG);
+            entity = (WrapperLivingEntity) EntityLib.createEntity(UUID.randomUUID(), EntityTypes.ZOMBIE);
             if (entity == null) {
                 player.sendMessage("idk");
                 return false;
@@ -31,7 +35,10 @@ public class TestEntityCommand implements CommandExecutor {
             entity.addViewer(player.getUniqueId());
             entity.spawn(fromBukkit(player.getLocation()));
         }
-
+        ItemStack held = player.getInventory().getItemInMainHand();
+        if (held != null && held.getType() != Material.AIR) {
+            entity.getEquipment().setBoots(SpigotConversionUtil.fromBukkitItemStack(held));
+        }
         EntityMeta meta = entity.getMeta();
         meta.setOnFire(!meta.isOnFire());
         meta.setHasGlowingEffect(!meta.hasGlowingEffect());