From ccea96a6ad567b4510696090fa6e793057ac63bb Mon Sep 17 00:00:00 2001
From: = <asdada@gmail.com>
Date: Thu, 14 Nov 2024 01:42:46 +0200
Subject: [PATCH] per player entities

---
 .../entitylib/wrapper/WrapperEntity.java      |  5 ++
 .../wrapper/WrapperPerPlayerEntity.java       | 75 +++++++++++++++++++
 .../wrapper/spawning/SpawnPacketProvider.java |  2 +-
 .../spawning/SpawnPacketProviders.java        | 10 +--
 4 files changed, 86 insertions(+), 6 deletions(-)
 create mode 100644 api/src/main/java/me/tofaa/entitylib/wrapper/WrapperPerPlayerEntity.java

diff --git a/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntity.java b/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntity.java
index 9584452..99d5155 100644
--- a/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntity.java
+++ b/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntity.java
@@ -92,6 +92,11 @@ public class WrapperEntity implements Tickable {
         return true;
     }
 
+    public PacketWrapper<?> getSpawnPacket(User user) {
+        // TODO: Version/EntityType compatibility
+        return SpawnPacketProvider.GENERAL.provide(this);
+    }
+
     public boolean spawn(Location location) {
         return spawn(location, EntityLib.getApi().getDefaultContainer());
     }
diff --git a/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperPerPlayerEntity.java b/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperPerPlayerEntity.java
new file mode 100644
index 0000000..e27f286
--- /dev/null
+++ b/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperPerPlayerEntity.java
@@ -0,0 +1,75 @@
+package me.tofaa.entitylib.wrapper;
+
+
+import com.github.retrooper.packetevents.protocol.player.User;
+import com.github.retrooper.packetevents.protocol.world.Location;
+
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+/**
+ * Generic utility for per player wrapper entities.
+ * These spawn in the same spot for everyone.
+ */
+public class WrapperPerPlayerEntity {
+
+    private final Map<UUID, WrapperEntity> entities = new ConcurrentHashMap<>();
+    private Function<User, WrapperEntity> baseSupplier;
+
+    public WrapperPerPlayerEntity(Function<User, WrapperEntity> baseSupplier) {
+        this.baseSupplier = baseSupplier;
+    }
+
+    public void setBaseSupplier(Function<User, WrapperEntity> baseSupplier) {
+        this.baseSupplier = baseSupplier;
+    }
+
+    public Function<User, WrapperEntity> getBaseSupplier() {
+        return baseSupplier;
+    }
+
+    public void spawn(Location location) {
+        if (check(WrapperEntity::isSpawned)) {
+            return;
+        }
+        execute(e -> e.spawn(location));
+    }
+
+    public void addViewer(User user) {
+        getEntityOf(user).addViewer(user);
+    }
+
+    public void removeViewer(User user) {
+        getEntityOf(user).removeViewer(user);
+    }
+
+    public void execute(Consumer<WrapperEntity> consumer) {
+        entities.values().forEach(consumer);
+    }
+
+    public boolean check(Predicate<WrapperEntity> predicate) {
+        if (entities.isEmpty()) return false;
+        WrapperEntity  e= entities.values().stream().findFirst().get();
+        return predicate.test(e);
+    }
+
+    public void modify(User user, Consumer<WrapperEntity> consumer) {
+        consumer.accept(getEntityOf(user));
+    }
+
+    public WrapperEntity getEntityOf(User user) {
+        if (this.entities.containsKey(user.getUUID())) {
+            return this.entities.get(user.getUUID());
+        }
+        else {
+            WrapperEntity e = baseSupplier.apply(user);
+            this.entities.put(user.getUUID(), e);
+            return e;
+        }
+    }
+
+}
diff --git a/api/src/main/java/me/tofaa/entitylib/wrapper/spawning/SpawnPacketProvider.java b/api/src/main/java/me/tofaa/entitylib/wrapper/spawning/SpawnPacketProvider.java
index 75c6086..04de61e 100644
--- a/api/src/main/java/me/tofaa/entitylib/wrapper/spawning/SpawnPacketProvider.java
+++ b/api/src/main/java/me/tofaa/entitylib/wrapper/spawning/SpawnPacketProvider.java
@@ -7,6 +7,6 @@ import me.tofaa.entitylib.wrapper.WrapperEntity;
 @FunctionalInterface
 public interface SpawnPacketProvider<T extends PacketWrapper<T>> extends SpawnPacketProviders {
 
-    T provide(User user, WrapperEntity entity);
+    T provide(WrapperEntity entity);
 
 }
diff --git a/api/src/main/java/me/tofaa/entitylib/wrapper/spawning/SpawnPacketProviders.java b/api/src/main/java/me/tofaa/entitylib/wrapper/spawning/SpawnPacketProviders.java
index c4fa136..0e92838 100644
--- a/api/src/main/java/me/tofaa/entitylib/wrapper/spawning/SpawnPacketProviders.java
+++ b/api/src/main/java/me/tofaa/entitylib/wrapper/spawning/SpawnPacketProviders.java
@@ -13,7 +13,7 @@ import java.util.Optional;
 
 interface SpawnPacketProviders {
 
-    @NotNull SpawnPacketProvider<WrapperPlayServerSpawnExperienceOrb> EXPERIENCE_ORB = (user, entity) -> {
+    @NotNull SpawnPacketProvider<WrapperPlayServerSpawnExperienceOrb> EXPERIENCE_ORB = (entity) -> {
         Check.stateCondition(!(entity instanceof WrapperExperienceOrbEntity), "Attempted to use spawn packet provider for Experience orbs on a non ExperienceOrb entity. Please use an instance of WrapperExperienceOrbEntity.");
         WrapperExperienceOrbEntity expEntity = (WrapperExperienceOrbEntity) entity;
         return new WrapperPlayServerSpawnExperienceOrb(
@@ -25,7 +25,7 @@ interface SpawnPacketProviders {
         );
     };
 
-    @NotNull SpawnPacketProvider<WrapperPlayServerSpawnEntity> GENERAL = (user, entity) -> {
+    @NotNull SpawnPacketProvider<WrapperPlayServerSpawnEntity> GENERAL = (entity) -> {
         Location location = entity.getLocation();
         return new WrapperPlayServerSpawnEntity(
                 entity.getEntityId(),
@@ -40,10 +40,10 @@ interface SpawnPacketProviders {
         );
     };
 
-    @NotNull SpawnPacketProvider<WrapperPlayServerSpawnWeatherEntity> LEGACY_WEATHER_ENTITY = (user, entity) -> {
+    @NotNull SpawnPacketProvider<WrapperPlayServerSpawnWeatherEntity> LEGACY_WEATHER_ENTITY = (entity) -> {
         throw new NotImplementedException();
     };
-    @NotNull SpawnPacketProvider<WrapperPlayServerSpawnPainting> LEGACY_PAINTING = (user, entity) -> {
+    @NotNull SpawnPacketProvider<WrapperPlayServerSpawnPainting> LEGACY_PAINTING = (entity) -> {
         Check.stateCondition(!(entity.getEntityMeta() instanceof PaintingMeta), "Attempted to use spawn packet provider for paintings but not using an entity with PaintingMeta.");
         PaintingMeta meta = entity.getEntityMeta(PaintingMeta.class);
         return new WrapperPlayServerSpawnPainting(
@@ -55,7 +55,7 @@ interface SpawnPacketProviders {
         );
     };
 
-    @NotNull SpawnPacketProvider<WrapperPlayServerSpawnLivingEntity> PRE_1_19_LIVING = (user, entity) -> {
+    @NotNull SpawnPacketProvider<WrapperPlayServerSpawnLivingEntity> PRE_1_19_LIVING = (entity) -> {
         Location location = entity.getLocation();
         return new WrapperPlayServerSpawnLivingEntity(
                 entity.getEntityId(),