From ab3acf1076d14b222083959ab6dcb5962769b06a Mon Sep 17 00:00:00 2001
From: Pyrbu <pyrmcserver@gmail.com>
Date: Mon, 24 Apr 2023 20:58:14 +0100
Subject: [PATCH] properties woo

---
 .../znpcsplus/entity/EntityIDProvider.java    | 15 ------
 .../pyr/znpcsplus/entity/PacketEntity.java    | 17 +++++--
 .../pyr/znpcsplus/entity/PacketPlayer.java    | 24 +++++++++
 src/main/java/lol/pyr/znpcsplus/npc/NPC.java  | 51 +++++++++++++------
 .../lol/pyr/znpcsplus/npc/NPCPropertyKey.java | 35 +++++++++++++
 .../pyr/znpcsplus/packets/PacketFactory.java  | 11 ++--
 .../pyr/znpcsplus/packets/V1_19Factory.java   |  9 ++--
 .../pyr/znpcsplus/packets/V1_8Factory.java    | 23 ++++-----
 .../pyr/znpcsplus/properties/NPCProperty.java |  4 --
 .../znpcsplus/properties/NPCPropertyKey.java  |  7 ---
 10 files changed, 128 insertions(+), 68 deletions(-)
 delete mode 100644 src/main/java/lol/pyr/znpcsplus/entity/EntityIDProvider.java
 create mode 100644 src/main/java/lol/pyr/znpcsplus/entity/PacketPlayer.java
 create mode 100644 src/main/java/lol/pyr/znpcsplus/npc/NPCPropertyKey.java
 delete mode 100644 src/main/java/lol/pyr/znpcsplus/properties/NPCProperty.java
 delete mode 100644 src/main/java/lol/pyr/znpcsplus/properties/NPCPropertyKey.java

diff --git a/src/main/java/lol/pyr/znpcsplus/entity/EntityIDProvider.java b/src/main/java/lol/pyr/znpcsplus/entity/EntityIDProvider.java
deleted file mode 100644
index dcf8b78..0000000
--- a/src/main/java/lol/pyr/znpcsplus/entity/EntityIDProvider.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package lol.pyr.znpcsplus.entity;
-
-import io.github.znetworkw.znpcservers.reflection.Reflections;
-import io.github.znetworkw.znpcservers.utility.Utils;
-
-public class EntityIDProvider {
-    public static int reserve() {
-        if (Utils.versionNewer(14)) return Reflections.ATOMIC_ENTITY_ID_FIELD.get().incrementAndGet();
-        else {
-            int id = Reflections.ENTITY_ID_MODIFIER.get();
-            Reflections.ENTITY_ID_MODIFIER.set(id + 1);
-            return id;
-        }
-    }
-}
diff --git a/src/main/java/lol/pyr/znpcsplus/entity/PacketEntity.java b/src/main/java/lol/pyr/znpcsplus/entity/PacketEntity.java
index 93d9dcb..a77aab9 100644
--- a/src/main/java/lol/pyr/znpcsplus/entity/PacketEntity.java
+++ b/src/main/java/lol/pyr/znpcsplus/entity/PacketEntity.java
@@ -2,6 +2,8 @@ package lol.pyr.znpcsplus.entity;
 
 import com.github.retrooper.packetevents.protocol.entity.type.EntityType;
 import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
+import io.github.znetworkw.znpcservers.reflection.Reflections;
+import io.github.znetworkw.znpcservers.utility.Utils;
 import lol.pyr.znpcsplus.packets.PacketFactory;
 import org.bukkit.entity.Player;
 
@@ -16,7 +18,8 @@ public class PacketEntity {
     private PacketLocation location;
 
     public PacketEntity(EntityType type, PacketLocation location) {
-        this.entityId = EntityIDProvider.reserve();
+        if (type == EntityTypes.PLAYER) throw new RuntimeException("Wrong class used for player");
+        this.entityId = reserveEntityID();
         this.uuid = UUID.randomUUID();
         this.type = type;
         this.location = location;
@@ -44,11 +47,19 @@ public class PacketEntity {
     }
 
     public void spawn(Player player) {
-        if (type == EntityTypes.PLAYER) PacketFactory.get().spawnPlayer(player, this);
-        else PacketFactory.get().spawnEntity(player, this);
+        PacketFactory.get().spawnEntity(player, this);
     }
 
     public void despawn(Player player) {
         PacketFactory.get().destroyEntity(player, this);
     }
+
+    private static int reserveEntityID() {
+        if (Utils.versionNewer(14)) return Reflections.ATOMIC_ENTITY_ID_FIELD.get().incrementAndGet();
+        else {
+            int id = Reflections.ENTITY_ID_MODIFIER.get();
+            Reflections.ENTITY_ID_MODIFIER.set(id + 1);
+            return id;
+        }
+    }
 }
diff --git a/src/main/java/lol/pyr/znpcsplus/entity/PacketPlayer.java b/src/main/java/lol/pyr/znpcsplus/entity/PacketPlayer.java
new file mode 100644
index 0000000..373944f
--- /dev/null
+++ b/src/main/java/lol/pyr/znpcsplus/entity/PacketPlayer.java
@@ -0,0 +1,24 @@
+package lol.pyr.znpcsplus.entity;
+
+import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
+import com.github.retrooper.packetevents.protocol.player.UserProfile;
+import lol.pyr.znpcsplus.packets.PacketFactory;
+import org.bukkit.entity.Player;
+
+public class PacketPlayer extends PacketEntity {
+    private final UserProfile gameProfile;
+
+    public PacketPlayer(PacketLocation location) {
+        super(EntityTypes.PLAYER, location);
+        this.gameProfile = new UserProfile(getUuid(), Integer.toString(getEntityId()));
+    }
+
+    @Override
+    public void spawn(Player player) {
+        PacketFactory.get().spawnPlayer(player,  this);
+    }
+
+    public UserProfile getGameProfile() {
+        return gameProfile;
+    }
+}
diff --git a/src/main/java/lol/pyr/znpcsplus/npc/NPC.java b/src/main/java/lol/pyr/znpcsplus/npc/NPC.java
index 9283a5e..9dee875 100644
--- a/src/main/java/lol/pyr/znpcsplus/npc/NPC.java
+++ b/src/main/java/lol/pyr/znpcsplus/npc/NPC.java
@@ -1,9 +1,9 @@
 package lol.pyr.znpcsplus.npc;
 
+import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
 import lol.pyr.znpcsplus.entity.PacketEntity;
 import lol.pyr.znpcsplus.entity.PacketLocation;
-import lol.pyr.znpcsplus.properties.NPCProperty;
-import lol.pyr.znpcsplus.properties.NPCPropertyKey;
+import lol.pyr.znpcsplus.entity.PacketPlayer;
 import org.bukkit.Bukkit;
 import org.bukkit.World;
 import org.bukkit.entity.Player;
@@ -20,19 +20,19 @@ public class NPC {
     private PacketLocation location;
     private NPCType type;
 
-    private final Map<NPCPropertyKey, NPCProperty> propertyMap = new HashMap<>();
+    private final Map<NPCPropertyKey<?>, Object> propertyMap = new HashMap<>();
 
     public NPC(World world, NPCType type, PacketLocation location) {
         this.worldName = world.getName();
         this.type = type;
         this.location = location;
-        entity = new PacketEntity(type.getType(), location); // TODO: Entity ID Provider
+        entity = new PacketEntity(type.getType(), location);
     }
 
     public void setType(NPCType type) {
         _hideAll();
         this.type = type;
-        entity = new PacketEntity(type.getType(), entity.getLocation());
+        entity = type.getType() == EntityTypes.PLAYER ? new PacketPlayer(entity.getLocation()) : new PacketEntity(type.getType(), entity.getLocation());
         _showAll();
     }
 
@@ -40,6 +40,10 @@ public class NPC {
         return type;
     }
 
+    public PacketEntity getEntity() {
+        return entity;
+    }
+
     public PacketLocation getLocation() {
         return location;
     }
@@ -58,22 +62,31 @@ public class NPC {
         viewers.clear();
     }
 
+    public void respawn() {
+        _hideAll();
+        _showAll();
+    }
+
     public void show(Player player) {
         if (viewers.contains(player)) return;
         _show(player);
         viewers.add(player);
     }
 
-    private void _show(Player player) {
-        entity.spawn(player);
-    }
-
     public void hide(Player player) {
         if (!viewers.contains(player)) return;
         _hide(player);
         viewers.remove(player);
     }
 
+    public boolean isShown(Player player) {
+        return viewers.contains(player);
+    }
+
+    private void _show(Player player) {
+        entity.spawn(player);
+    }
+
     private void _hide(Player player) {
         entity.despawn(player);
     }
@@ -86,15 +99,21 @@ public class NPC {
         for (Player viewer : viewers) _show(viewer);
     }
 
-    public boolean isShown(Player player) {
-        return viewers.contains(player);
+    @SuppressWarnings("unchecked")
+    public <T> T getProperty(NPCPropertyKey<T> key) {
+        return (T) propertyMap.get(key);
     }
 
-    public NPCProperty getProperty(NPCPropertyKey key) {
-        return propertyMap.get(key);
-    }
-
-    public boolean hasProperty(NPCPropertyKey key) {
+    public boolean hasProperty(NPCPropertyKey<?> key) {
         return propertyMap.containsKey(key);
     }
+
+    public <T> void setProperty(NPCPropertyKey<T> key, T value) {
+        propertyMap.put(key, value);
+        key.update(this, value);
+    }
+
+    public void removeProperty(NPCPropertyKey<?> key) {
+        propertyMap.remove(key);
+    }
 }
diff --git a/src/main/java/lol/pyr/znpcsplus/npc/NPCPropertyKey.java b/src/main/java/lol/pyr/znpcsplus/npc/NPCPropertyKey.java
new file mode 100644
index 0000000..4cc09ca
--- /dev/null
+++ b/src/main/java/lol/pyr/znpcsplus/npc/NPCPropertyKey.java
@@ -0,0 +1,35 @@
+package lol.pyr.znpcsplus.npc;
+
+import com.github.retrooper.packetevents.protocol.player.TextureProperty;
+import io.github.znetworkw.znpcservers.npc.NPCSkin;
+import lol.pyr.znpcsplus.entity.PacketPlayer;
+
+import java.util.List;
+
+public class NPCPropertyKey<T> {
+    private final UpdateCallback<T> updateCallback;
+
+    public NPCPropertyKey() {
+        this(null);
+    }
+
+    public NPCPropertyKey(UpdateCallback<T> updateCallback) {
+        this.updateCallback = updateCallback;
+    }
+
+    public void update(NPC npc, T value) {
+        if (updateCallback != null) updateCallback.onUpdate(npc, value);
+    }
+
+    @FunctionalInterface
+    public interface UpdateCallback<T> {
+        void onUpdate(NPC npc, T value);
+    }
+
+    public static NPCPropertyKey<NPCSkin> NPC_SKIN = new NPCPropertyKey<>((npc, skin) -> {
+        if (!(npc.getEntity() instanceof PacketPlayer entity))
+            throw new RuntimeException("Tried to set a skin on an entity that isn't a player");
+        entity.getGameProfile().setTextureProperties(List.of(new TextureProperty("textures", skin.getTexture(), skin.getSignature())));
+        npc.respawn();
+    });
+}
diff --git a/src/main/java/lol/pyr/znpcsplus/packets/PacketFactory.java b/src/main/java/lol/pyr/znpcsplus/packets/PacketFactory.java
index 3baf573..e79702b 100644
--- a/src/main/java/lol/pyr/znpcsplus/packets/PacketFactory.java
+++ b/src/main/java/lol/pyr/znpcsplus/packets/PacketFactory.java
@@ -3,6 +3,7 @@ package lol.pyr.znpcsplus.packets;
 import com.github.retrooper.packetevents.PacketEvents;
 import com.github.retrooper.packetevents.manager.server.ServerVersion;
 import lol.pyr.znpcsplus.entity.PacketEntity;
+import lol.pyr.znpcsplus.entity.PacketPlayer;
 import lol.pyr.znpcsplus.util.LazyLoader;
 import org.bukkit.entity.Player;
 
@@ -10,14 +11,14 @@ import java.util.HashMap;
 import java.util.Map;
 
 public interface PacketFactory {
-    void spawnPlayer(Player player, PacketEntity entity);
+    void spawnPlayer(Player player, PacketPlayer entity);
     void spawnEntity(Player player, PacketEntity entity);
     void destroyEntity(Player player, PacketEntity entity);
     void teleportEntity(Player player, PacketEntity entity);
-    void addTabPlayer(Player player, PacketEntity entity);
-    void removeTabPlayer(Player player, PacketEntity entity);
-    void createTeam(Player player, PacketEntity entity);
-    void removeTeam(Player player, PacketEntity entity);
+    void addTabPlayer(Player player, PacketPlayer entity);
+    void removeTabPlayer(Player player, PacketPlayer entity);
+    void createTeam(Player player, PacketPlayer entity);
+    void removeTeam(Player player, PacketPlayer entity);
 
     PacketFactory factory = get();
 
diff --git a/src/main/java/lol/pyr/znpcsplus/packets/V1_19Factory.java b/src/main/java/lol/pyr/znpcsplus/packets/V1_19Factory.java
index 98aaee1..230b3bc 100644
--- a/src/main/java/lol/pyr/znpcsplus/packets/V1_19Factory.java
+++ b/src/main/java/lol/pyr/znpcsplus/packets/V1_19Factory.java
@@ -2,10 +2,9 @@ package lol.pyr.znpcsplus.packets;
 
 import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
 import com.github.retrooper.packetevents.protocol.player.GameMode;
-import com.github.retrooper.packetevents.protocol.player.UserProfile;
 import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerPlayerInfoRemove;
 import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerPlayerInfoUpdate;
-import lol.pyr.znpcsplus.entity.PacketEntity;
+import lol.pyr.znpcsplus.entity.PacketPlayer;
 import net.kyori.adventure.text.Component;
 import org.bukkit.entity.Player;
 
@@ -13,17 +12,17 @@ import java.util.EnumSet;
 
 public class V1_19Factory extends V1_14Factory {
     @Override
-    public void addTabPlayer(Player player, PacketEntity entity) {
+    public void addTabPlayer(Player player, PacketPlayer entity) {
         if (entity.getType() != EntityTypes.PLAYER) return;
         WrapperPlayServerPlayerInfoUpdate.PlayerInfo info = new WrapperPlayServerPlayerInfoUpdate.PlayerInfo(
-                new UserProfile(entity.getUuid(), Integer.toString(entity.getEntityId())), false, 1, GameMode.CREATIVE,
+                entity.getGameProfile(), false, 1, GameMode.CREATIVE,
                 Component.empty(), null);
         sendPacket(player, new WrapperPlayServerPlayerInfoUpdate(EnumSet.of(WrapperPlayServerPlayerInfoUpdate.Action.ADD_PLAYER,
                 WrapperPlayServerPlayerInfoUpdate.Action.UPDATE_LISTED), info, info));
     }
 
     @Override
-    public void removeTabPlayer(Player player, PacketEntity entity) {
+    public void removeTabPlayer(Player player, PacketPlayer entity) {
         if (entity.getType() != EntityTypes.PLAYER) return;
         sendPacket(player, new WrapperPlayServerPlayerInfoRemove(entity.getUuid()));
     }
diff --git a/src/main/java/lol/pyr/znpcsplus/packets/V1_8Factory.java b/src/main/java/lol/pyr/znpcsplus/packets/V1_8Factory.java
index 26e7d4b..2951d1d 100644
--- a/src/main/java/lol/pyr/znpcsplus/packets/V1_8Factory.java
+++ b/src/main/java/lol/pyr/znpcsplus/packets/V1_8Factory.java
@@ -5,13 +5,13 @@ import com.github.retrooper.packetevents.protocol.entity.type.EntityType;
 import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
 import com.github.retrooper.packetevents.protocol.player.ClientVersion;
 import com.github.retrooper.packetevents.protocol.player.GameMode;
-import com.github.retrooper.packetevents.protocol.player.UserProfile;
 import com.github.retrooper.packetevents.util.Vector3d;
 import com.github.retrooper.packetevents.wrapper.PacketWrapper;
 import com.github.retrooper.packetevents.wrapper.play.server.*;
 import lol.pyr.znpcsplus.ZNPCsPlus;
 import lol.pyr.znpcsplus.entity.PacketEntity;
 import lol.pyr.znpcsplus.entity.PacketLocation;
+import lol.pyr.znpcsplus.entity.PacketPlayer;
 import net.kyori.adventure.text.Component;
 import net.kyori.adventure.text.format.NamedTextColor;
 import org.bukkit.entity.Player;
@@ -21,7 +21,7 @@ import java.util.Optional;
 
 public class V1_8Factory implements PacketFactory {
     @Override
-    public void spawnPlayer(Player player, PacketEntity entity) {
+    public void spawnPlayer(Player player, PacketPlayer entity) {
         addTabPlayer(player, entity);
         createTeam(player, entity);
         PacketLocation location = entity.getLocation();
@@ -45,7 +45,7 @@ public class V1_8Factory implements PacketFactory {
     @Override
     public void destroyEntity(Player player, PacketEntity entity) {
         sendPacket(player, new WrapperPlayServerDestroyEntities(entity.getEntityId()));
-        if (entity.getType() == EntityTypes.PLAYER) removeTeam(player, entity);
+        if (entity.getType() == EntityTypes.PLAYER) removeTeam(player, (PacketPlayer) entity);
     }
 
     @Override
@@ -56,27 +56,24 @@ public class V1_8Factory implements PacketFactory {
     }
 
     @Override
-    public void addTabPlayer(Player player, PacketEntity entity) {
+    public void addTabPlayer(Player player, PacketPlayer entity) {
         if (entity.getType() != EntityTypes.PLAYER) return;
         sendPacket(player, new WrapperPlayServerPlayerInfo(
-                WrapperPlayServerPlayerInfo.Action.ADD_PLAYER, new WrapperPlayServerPlayerInfo.PlayerData(Component.text(""),
-                new UserProfile(entity.getUuid(), Integer.toString(entity.getEntityId())), GameMode.CREATIVE, 1)));
+                WrapperPlayServerPlayerInfo.Action.ADD_PLAYER, new WrapperPlayServerPlayerInfo.PlayerData(Component.text(""), entity.getGameProfile(), GameMode.CREATIVE, 1)));
     }
 
     @Override
-    public void removeTabPlayer(Player player, PacketEntity entity) {
+    public void removeTabPlayer(Player player, PacketPlayer entity) {
         if (entity.getType() != EntityTypes.PLAYER) return;
         sendPacket(player, new WrapperPlayServerPlayerInfo(
                 WrapperPlayServerPlayerInfo.Action.REMOVE_PLAYER, new WrapperPlayServerPlayerInfo.PlayerData(null,
-                new UserProfile(entity.getUuid(), null), null, -1)));
+                entity.getGameProfile(), null, -1)));
     }
 
     @Override
-    public void createTeam(Player player, PacketEntity entity) {
+    public void createTeam(Player player, PacketPlayer entity) {
         sendPacket(player, new WrapperPlayServerTeams("npc_team_" + entity.getEntityId(), WrapperPlayServerTeams.TeamMode.CREATE, new WrapperPlayServerTeams.ScoreBoardTeamInfo(
-                Component.empty(),
-                Component.empty(),
-                Component.empty(),
+                Component.empty(), Component.empty(), Component.empty(),
                 WrapperPlayServerTeams.NameTagVisibility.NEVER,
                 WrapperPlayServerTeams.CollisionRule.NEVER,
                 NamedTextColor.WHITE,
@@ -86,7 +83,7 @@ public class V1_8Factory implements PacketFactory {
     }
 
     @Override
-    public void removeTeam(Player player, PacketEntity entity) {
+    public void removeTeam(Player player, PacketPlayer entity) {
         sendPacket(player, new WrapperPlayServerTeams("npc_team_" + entity.getEntityId(), WrapperPlayServerTeams.TeamMode.REMOVE, (WrapperPlayServerTeams.ScoreBoardTeamInfo) null));
     }
 
diff --git a/src/main/java/lol/pyr/znpcsplus/properties/NPCProperty.java b/src/main/java/lol/pyr/znpcsplus/properties/NPCProperty.java
deleted file mode 100644
index bbf2a10..0000000
--- a/src/main/java/lol/pyr/znpcsplus/properties/NPCProperty.java
+++ /dev/null
@@ -1,4 +0,0 @@
-package lol.pyr.znpcsplus.properties;
-
-public class NPCProperty {
-}
diff --git a/src/main/java/lol/pyr/znpcsplus/properties/NPCPropertyKey.java b/src/main/java/lol/pyr/znpcsplus/properties/NPCPropertyKey.java
deleted file mode 100644
index 9f87d74..0000000
--- a/src/main/java/lol/pyr/znpcsplus/properties/NPCPropertyKey.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package lol.pyr.znpcsplus.properties;
-
-public class NPCPropertyKey<T> {
-    public NPCPropertyKey() {}
-
-    public static NPCPropertyKey<>
-}