-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -62,6 +82,13 @@
+
+
+
+
+
+
+
@@ -103,7 +130,7 @@
"node.js.selected.package.eslint": "(autodetect)",
"node.js.selected.package.tslint": "(autodetect)",
"nodejs_package_manager_path": "npm",
- "project.structure.last.edited": "Modules",
+ "project.structure.last.edited": "Project",
"project.structure.proportion": "0.15",
"project.structure.side.proportion": "0.2",
"settings.editor.selected.configurable": "preferences.pluginManager",
@@ -232,6 +259,7 @@
+
@@ -288,6 +316,10 @@
+
+
+
+
diff --git a/api/src/main/java/me/tofaa/entitylib/APIConfig.java b/api/src/main/java/me/tofaa/entitylib/APIConfig.java
index 648644c..0e2828e 100644
--- a/api/src/main/java/me/tofaa/entitylib/APIConfig.java
+++ b/api/src/main/java/me/tofaa/entitylib/APIConfig.java
@@ -20,6 +20,7 @@ public final class APIConfig {
private boolean platformLogger = false;
private boolean useAsyncEvents = false;
private boolean defaultCommands = false;
+ private boolean platformTracking = false;
public APIConfig(PacketEventsAPI> packetEvents) {
this.packetEvents = packetEvents;
@@ -46,6 +47,11 @@ public final class APIConfig {
return this;
}
+ public @NotNull APIConfig usePlatformTracking() {
+ this.platformTracking = true;
+ return this;
+ }
+
public @NotNull APIConfig checkForUpdates() {
this.checkForUpdates = true;
return this;
@@ -99,4 +105,8 @@ public final class APIConfig {
return useAsyncEvents;
}
+ public boolean shouldTrackPlatformEntities() {
+ return platformTracking;
+ }
+
}
diff --git a/api/src/main/java/me/tofaa/entitylib/Platform.java b/api/src/main/java/me/tofaa/entitylib/Platform.java
index 3094a56..b486dc4 100644
--- a/api/src/main/java/me/tofaa/entitylib/Platform.java
+++ b/api/src/main/java/me/tofaa/entitylib/Platform.java
@@ -2,8 +2,11 @@ package me.tofaa.entitylib;
import me.tofaa.entitylib.event.EventBus;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import java.util.concurrent.CompletableFuture;
import java.util.logging.Logger;
+import java.util.stream.Stream;
/**
* A generic representation of a platform that EntityLib is running on.
@@ -11,6 +14,22 @@ import java.util.logging.Logger;
*/
public interface Platform {
+
+ /**
+ * Queries a stream of platform specific entities if the platform supports
+ * @throws UnsupportedOperationException if the platform does not support querying entities.
+ * @return a stream of platform specific entities. The stream is not guaranteed to be synchronized.
+ */
+ @NotNull Stream queryPlatformEntities();
+
+ /**
+ * Finds a platform specific entity by its entityId.
+ * @param entityId the entityId of the entity.
+ * @return a future that completes with the entity if found, or null if not found.
+ * @throws UnsupportedOperationException if the platform does not support querying entities.
+ */
+ @Nullable TrackedEntity findPlatformEntity(int entityId);
+
/**
* Gets the entityId integer provider. This can be provided by a platform if needed.
* @return the entityId integer provider.
diff --git a/api/src/main/java/me/tofaa/entitylib/TrackedEntity.java b/api/src/main/java/me/tofaa/entitylib/TrackedEntity.java
new file mode 100644
index 0000000..3db9a9e
--- /dev/null
+++ b/api/src/main/java/me/tofaa/entitylib/TrackedEntity.java
@@ -0,0 +1,13 @@
+package me.tofaa.entitylib;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.UUID;
+
+public interface TrackedEntity {
+
+ int getEntityId();
+
+ @NotNull UUID getUuid();
+
+}
diff --git a/api/src/main/java/me/tofaa/entitylib/event/CancellableEntityLibEvent.java b/api/src/main/java/me/tofaa/entitylib/event/CancellableEntityLibEvent.java
new file mode 100644
index 0000000..c1c37ad
--- /dev/null
+++ b/api/src/main/java/me/tofaa/entitylib/event/CancellableEntityLibEvent.java
@@ -0,0 +1,9 @@
+package me.tofaa.entitylib.event;
+
+public interface CancellableEntityLibEvent extends EntityLibEvent {
+
+ void setCancelled(boolean cancelled);
+
+ boolean isCancelled();
+
+}
diff --git a/api/src/main/java/me/tofaa/entitylib/event/EntityLibEvent.java b/api/src/main/java/me/tofaa/entitylib/event/EntityLibEvent.java
index 0a22a30..bdfd09c 100644
--- a/api/src/main/java/me/tofaa/entitylib/event/EntityLibEvent.java
+++ b/api/src/main/java/me/tofaa/entitylib/event/EntityLibEvent.java
@@ -2,7 +2,4 @@ package me.tofaa.entitylib.event;
public interface EntityLibEvent {
- boolean isCancelled();
-
- void setCancelled(boolean cancelled);
}
diff --git a/api/src/main/java/me/tofaa/entitylib/event/EventBus.java b/api/src/main/java/me/tofaa/entitylib/event/EventBus.java
index d9ab18f..0cbd3eb 100644
--- a/api/src/main/java/me/tofaa/entitylib/event/EventBus.java
+++ b/api/src/main/java/me/tofaa/entitylib/event/EventBus.java
@@ -6,6 +6,7 @@ import java.util.function.Consumer;
/**
* A basic EventBus for scheduling and handling {@link EntityLibEvent}
+ * Async event busses are not thread safe, and should be handled with care. This means theres no guarantee that the event will be cancelled in time.
*/
public interface EventBus {
diff --git a/api/src/main/java/me/tofaa/entitylib/event/types/UserReceiveMetaUpdateEvent.java b/api/src/main/java/me/tofaa/entitylib/event/types/UserReceiveMetaUpdateEvent.java
index 5b5d64f..9d232a3 100644
--- a/api/src/main/java/me/tofaa/entitylib/event/types/UserReceiveMetaUpdateEvent.java
+++ b/api/src/main/java/me/tofaa/entitylib/event/types/UserReceiveMetaUpdateEvent.java
@@ -1,10 +1,11 @@
package me.tofaa.entitylib.event.types;
import com.github.retrooper.packetevents.protocol.player.User;
+import me.tofaa.entitylib.event.CancellableEntityLibEvent;
import me.tofaa.entitylib.event.EntityLibEvent;
import me.tofaa.entitylib.meta.EntityMeta;
-public final class UserReceiveMetaUpdateEvent implements EntityLibEvent {
+public final class UserReceiveMetaUpdateEvent implements CancellableEntityLibEvent {
private final User user;
private boolean cancelled;
@@ -20,11 +21,11 @@ public final class UserReceiveMetaUpdateEvent implements EntityLibEvent {
@Override
public boolean isCancelled() {
- return false;
+ return cancelled;
}
@Override
public void setCancelled(boolean cancelled) {
-
+ this.cancelled = cancelled;
}
}
diff --git a/api/src/main/java/me/tofaa/entitylib/event/types/UserRefreshEntityEvent.java b/api/src/main/java/me/tofaa/entitylib/event/types/UserRefreshEntityEvent.java
index 9276cf2..20d7e3a 100644
--- a/api/src/main/java/me/tofaa/entitylib/event/types/UserRefreshEntityEvent.java
+++ b/api/src/main/java/me/tofaa/entitylib/event/types/UserRefreshEntityEvent.java
@@ -1,10 +1,11 @@
package me.tofaa.entitylib.event.types;
import com.github.retrooper.packetevents.protocol.player.User;
+import me.tofaa.entitylib.event.CancellableEntityLibEvent;
import me.tofaa.entitylib.event.EntityLibEvent;
import me.tofaa.entitylib.wrapper.WrapperEntity;
-public class UserRefreshEntityEvent implements EntityLibEvent {
+public class UserRefreshEntityEvent implements CancellableEntityLibEvent {
private final User user;
private final WrapperEntity entity;
diff --git a/api/src/main/java/me/tofaa/entitylib/event/types/tracking/GeneralTrackingEvent.java b/api/src/main/java/me/tofaa/entitylib/event/types/tracking/GeneralTrackingEvent.java
new file mode 100644
index 0000000..b45f5dd
--- /dev/null
+++ b/api/src/main/java/me/tofaa/entitylib/event/types/tracking/GeneralTrackingEvent.java
@@ -0,0 +1,26 @@
+package me.tofaa.entitylib.event.types.tracking;
+
+import com.github.retrooper.packetevents.protocol.player.User;
+import me.tofaa.entitylib.TrackedEntity;
+import me.tofaa.entitylib.event.EntityLibEvent;
+import org.jetbrains.annotations.NotNull;
+
+public class GeneralTrackingEvent implements EntityLibEvent {
+
+ private final User user;
+ private final TrackedEntity entity;
+
+ public GeneralTrackingEvent(@NotNull User user, @NotNull TrackedEntity entity) {
+ this.user = user;
+ this.entity = entity;
+ }
+
+ public User getUser() {
+ return user;
+ }
+
+ public TrackedEntity getEntity() {
+ return entity;
+ }
+
+}
diff --git a/api/src/main/java/me/tofaa/entitylib/event/types/tracking/UserStopTrackingEntityEvent.java b/api/src/main/java/me/tofaa/entitylib/event/types/tracking/UserStopTrackingEntityEvent.java
new file mode 100644
index 0000000..fa2ab7c
--- /dev/null
+++ b/api/src/main/java/me/tofaa/entitylib/event/types/tracking/UserStopTrackingEntityEvent.java
@@ -0,0 +1,16 @@
+package me.tofaa.entitylib.event.types.tracking;
+
+import com.github.retrooper.packetevents.protocol.player.User;
+import me.tofaa.entitylib.TrackedEntity;
+
+import org.jetbrains.annotations.NotNull;
+
+public class UserStopTrackingEntityEvent extends GeneralTrackingEvent {
+
+
+ public UserStopTrackingEntityEvent(@NotNull User user, @NotNull TrackedEntity entity) {
+ super(user, entity);
+ }
+
+
+}
diff --git a/api/src/main/java/me/tofaa/entitylib/event/types/tracking/UserTrackingEntityEvent.java b/api/src/main/java/me/tofaa/entitylib/event/types/tracking/UserTrackingEntityEvent.java
new file mode 100644
index 0000000..26e946f
--- /dev/null
+++ b/api/src/main/java/me/tofaa/entitylib/event/types/tracking/UserTrackingEntityEvent.java
@@ -0,0 +1,13 @@
+package me.tofaa.entitylib.event.types.tracking;
+
+import com.github.retrooper.packetevents.protocol.player.User;
+import me.tofaa.entitylib.TrackedEntity;
+import org.jetbrains.annotations.NotNull;
+
+public final class UserTrackingEntityEvent extends GeneralTrackingEvent {
+
+
+ public UserTrackingEntityEvent(@NotNull User user, @NotNull TrackedEntity entity) {
+ super(user, entity);
+ }
+}
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 283c113..0df09bb 100644
--- a/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntity.java
+++ b/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntity.java
@@ -7,6 +7,7 @@ import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import com.github.retrooper.packetevents.wrapper.play.server.*;
import me.tofaa.entitylib.EntityLib;
+import me.tofaa.entitylib.TrackedEntity;
import me.tofaa.entitylib.meta.EntityMeta;
import me.tofaa.entitylib.meta.types.ObjectData;
import me.tofaa.entitylib.tick.Tickable;
@@ -15,7 +16,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.*;
-public class WrapperEntity implements Tickable {
+public class WrapperEntity implements Tickable, TrackedEntity {
private final UUID uuid;
private final int entityId;
diff --git a/common/src/main/java/me/tofaa/entitylib/common/AbstractPlatform.java b/common/src/main/java/me/tofaa/entitylib/common/AbstractPlatform.java
index a2700e1..141228d 100644
--- a/common/src/main/java/me/tofaa/entitylib/common/AbstractPlatform.java
+++ b/common/src/main/java/me/tofaa/entitylib/common/AbstractPlatform.java
@@ -1,13 +1,13 @@
package me.tofaa.entitylib.common;
-import me.tofaa.entitylib.APIConfig;
-import me.tofaa.entitylib.EntityIdProvider;
-import me.tofaa.entitylib.EntityUuidProvider;
-import me.tofaa.entitylib.Platform;
+import me.tofaa.entitylib.*;
import me.tofaa.entitylib.event.EventBus;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import java.util.concurrent.CompletableFuture;
import java.util.logging.Logger;
+import java.util.stream.Stream;
public abstract class AbstractPlatform implements Platform
{
@@ -24,6 +24,15 @@ public abstract class AbstractPlatform
implements Platform
{
this.entityUuidProvider = new EntityUuidProvider.DefaultEntityUuidProvider();
}
+ @Override
+ public @NotNull Stream queryPlatformEntities() {
+ throw new UnsupportedOperationException("Platform does not support querying entities.");
+ }
+
+ @Override
+ public @Nullable TrackedEntity findPlatformEntity(int entityId) {
+ throw new UnsupportedOperationException("Platform does not support querying entities.");
+ }
@Override
public void setupApi(@NotNull APIConfig settings) {
diff --git a/common/src/main/java/me/tofaa/entitylib/common/AbstractTrackedEntity.java b/common/src/main/java/me/tofaa/entitylib/common/AbstractTrackedEntity.java
new file mode 100644
index 0000000..c58bfe1
--- /dev/null
+++ b/common/src/main/java/me/tofaa/entitylib/common/AbstractTrackedEntity.java
@@ -0,0 +1,19 @@
+package me.tofaa.entitylib.common;
+
+import me.tofaa.entitylib.TrackedEntity;
+import org.jetbrains.annotations.NotNull;
+
+public abstract class AbstractTrackedEntity implements TrackedEntity {
+
+ private final E platformEntity;
+
+ protected AbstractTrackedEntity(@NotNull E platformEntity) {
+ this.platformEntity = platformEntity;
+ }
+
+ @NotNull
+ public E getPlatformEntity() {
+ return platformEntity;
+ }
+
+}
diff --git a/platforms/spigot/src/main/java/me/tofaa/entitylib/spigot/InternalRegistryListener.java b/platforms/spigot/src/main/java/me/tofaa/entitylib/spigot/InternalRegistryListener.java
new file mode 100644
index 0000000..cbab5d4
--- /dev/null
+++ b/platforms/spigot/src/main/java/me/tofaa/entitylib/spigot/InternalRegistryListener.java
@@ -0,0 +1,103 @@
+package me.tofaa.entitylib.spigot;
+
+import com.github.retrooper.packetevents.event.PacketListenerAbstract;
+import com.github.retrooper.packetevents.event.PacketSendEvent;
+import com.github.retrooper.packetevents.protocol.packettype.PacketType;
+import com.github.retrooper.packetevents.protocol.packettype.PacketTypeCommon;
+import com.github.retrooper.packetevents.protocol.player.User;
+import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerDestroyEntities;
+import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerSpawnEntity;
+import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerSpawnExperienceOrb;
+import me.tofaa.entitylib.TrackedEntity;
+import me.tofaa.entitylib.event.types.tracking.UserStopTrackingEntityEvent;
+import me.tofaa.entitylib.event.types.tracking.UserTrackingEntityEvent;
+import org.bukkit.entity.Entity;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.entity.EntitySpawnEvent;
+
+final class InternalRegistryListener extends PacketListenerAbstract implements Listener {
+
+ private SpigotEntityLibPlatform platform;
+
+ InternalRegistryListener(SpigotEntityLibPlatform platform) {
+ this.platform = platform;
+ }
+
+
+ @Override
+ public void onPacketSend(PacketSendEvent event) {
+ final User user = event.getUser();
+ final PacketTypeCommon type = event.getPacketType();
+ if (type == PacketType.Play.Server.DESTROY_ENTITIES) {
+ WrapperPlayServerDestroyEntities packet = new WrapperPlayServerDestroyEntities(event);
+ int[] ids = packet.getEntityIds();
+ for (int id : ids) {
+ TrackedEntity tracked = findTracker(id);
+ if (tracked == null) {
+ continue;
+ }
+ platform.getEventBus().call(new UserStopTrackingEntityEvent(user, tracked));
+ }
+ }
+ else if (type == PacketType.Play.Server.SPAWN_ENTITY) {
+ WrapperPlayServerSpawnEntity packet = new WrapperPlayServerSpawnEntity(event);
+ int id = packet.getEntityId();
+ trackEntity(user, id);
+ }
+ else if (type == PacketType.Play.Server.SPAWN_EXPERIENCE_ORB) {
+ WrapperPlayServerSpawnExperienceOrb packet = new WrapperPlayServerSpawnExperienceOrb(event);
+ int id = packet.getEntityId();
+ trackEntity(user, id);
+ }
+ else if (type == PacketType.Play.Server.SPAWN_LIVING_ENTITY) {
+ WrapperPlayServerSpawnEntity packet = new WrapperPlayServerSpawnEntity(event);
+ int id = packet.getEntityId();
+ trackEntity(user, id);
+ }
+ else if (type == PacketType.Play.Server.SPAWN_PLAYER) {
+ WrapperPlayServerSpawnEntity packet = new WrapperPlayServerSpawnEntity(event);
+ int id = packet.getEntityId();
+ trackEntity(user, id);
+ }
+ else if (type == PacketType.Play.Server.SPAWN_WEATHER_ENTITY) {
+ WrapperPlayServerSpawnEntity packet = new WrapperPlayServerSpawnEntity(event);
+ int id = packet.getEntityId();
+ trackEntity(user, id);
+ }
+ else if (type == PacketType.Play.Server.SPAWN_PAINTING) {
+ WrapperPlayServerSpawnEntity packet = new WrapperPlayServerSpawnEntity(event);
+ int id = packet.getEntityId();
+ trackEntity(user, id);
+ }
+ }
+
+ private void trackEntity(User user, int id) {
+ TrackedEntity entity = findTracker(id);
+ if (entity == null) {
+ return;
+ }
+ platform.getEventBus().call(new UserTrackingEntityEvent(user, entity));
+ }
+
+ private TrackedEntity findTracker(int id) {
+ TrackedEntity entity = platform.findPlatformEntity(id);
+ if (entity == null) {
+ entity = platform.getAPI().getEntity(id);
+ }
+ if (entity == null) {
+ if (platform.getAPI().getSettings().isDebugMode()) {
+ platform.getLogger().warning("Failed to find entity with id " + id);
+ }
+ }
+ return entity;
+ }
+
+ @EventHandler
+ public void onEntitySpawn(EntitySpawnEvent event) {
+ Entity e = event.getEntity();
+ platform.getPlatformEntities().put(e.getEntityId(), e);
+ }
+
+
+}
diff --git a/platforms/spigot/src/main/java/me/tofaa/entitylib/spigot/SpigotEntity.java b/platforms/spigot/src/main/java/me/tofaa/entitylib/spigot/SpigotEntity.java
new file mode 100644
index 0000000..f6a2602
--- /dev/null
+++ b/platforms/spigot/src/main/java/me/tofaa/entitylib/spigot/SpigotEntity.java
@@ -0,0 +1,24 @@
+package me.tofaa.entitylib.spigot;
+
+import me.tofaa.entitylib.common.AbstractTrackedEntity;
+import org.bukkit.entity.Entity;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.UUID;
+
+public class SpigotEntity extends AbstractTrackedEntity {
+
+ public SpigotEntity(@NotNull Entity platformEntity) {
+ super(platformEntity);
+ }
+
+ @Override
+ public int getEntityId() {
+ return getPlatformEntity().getEntityId();
+ }
+
+ @Override
+ public @NotNull UUID getUuid() {
+ return getPlatformEntity().getUniqueId();
+ }
+}
diff --git a/platforms/spigot/src/main/java/me/tofaa/entitylib/spigot/SpigotEntityLibPlatform.java b/platforms/spigot/src/main/java/me/tofaa/entitylib/spigot/SpigotEntityLibPlatform.java
index fd509b0..4f20114 100644
--- a/platforms/spigot/src/main/java/me/tofaa/entitylib/spigot/SpigotEntityLibPlatform.java
+++ b/platforms/spigot/src/main/java/me/tofaa/entitylib/spigot/SpigotEntityLibPlatform.java
@@ -1,15 +1,29 @@
package me.tofaa.entitylib.spigot;
import me.tofaa.entitylib.APIConfig;
-import me.tofaa.entitylib.EntityLibAPI;
+import me.tofaa.entitylib.TrackedEntity;
import me.tofaa.entitylib.common.AbstractPlatform;
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Entity;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.entity.EntitySpawnEvent;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.WeakHashMap;
+import java.util.concurrent.CompletableFuture;
import java.util.logging.Logger;
+import java.util.stream.Stream;
public class SpigotEntityLibPlatform extends AbstractPlatform {
private SpigotEntityLibAPI api;
+ private Map platformEntities = Collections.synchronizedMap(new WeakHashMap<>()); // Silly
+
public SpigotEntityLibPlatform(@NotNull JavaPlugin plugin) {
super(plugin);
}
@@ -21,9 +35,33 @@ public class SpigotEntityLibPlatform extends AbstractPlatform {
this.api = new SpigotEntityLibAPI(this, settings);
this.api.onLoad();
this.api.onEnable();
+
+ if (settings.shouldTrackPlatformEntities()) {
+ InternalRegistryListener listener = new InternalRegistryListener(this);
+ handle.getServer().getPluginManager().registerEvents(listener, handle);
+ api.getPacketEvents().getEventManager().registerListener(listener);
+ }
}
+ Map getPlatformEntities() {
+ return platformEntities;
+ }
+
+ @Override
+ public @NotNull Stream queryPlatformEntities() {
+ if (!api.getSettings().shouldTrackPlatformEntities()) return Stream.of();
+ return platformEntities.values().stream().map(SpigotEntity::new);
+ }
+
+ @Override
+ public @Nullable TrackedEntity findPlatformEntity(final int entityId) {
+ if (!api.getSettings().shouldTrackPlatformEntities()) return null;
+ Entity e = platformEntities.get(entityId);
+ if (e == null) return null;
+ return new SpigotEntity(e);
+ }
+
@Override
public SpigotEntityLibAPI getAPI() {
return api;