diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 5ec86af..33dd8d6 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -14,6 +14,7 @@ <option value="$PROJECT_DIR$/model-engine-addon" /> <option value="$PROJECT_DIR$/platforms" /> <option value="$PROJECT_DIR$/platforms/spigot" /> + <option value="$PROJECT_DIR$/platforms/standalone" /> <option value="$PROJECT_DIR$/platforms/velocity" /> <option value="$PROJECT_DIR$/test-plugin" /> </set> diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 4d8c3df..23c0d15 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -5,14 +5,39 @@ </component> <component name="ChangeListManager"> <list default="true" id="9d5d9b6f-43c8-41a4-bb42-a66ffc96c9b0" name="Changes" comment=""> - <change afterPath="$PROJECT_DIR$/test-plugin/src/main/java/me/tofaa/testentitylib/TestHologramsCommand.java" afterDir="false" /> + <change afterPath="$PROJECT_DIR$/api/src/main/java/me/tofaa/entitylib/container/AbstractEntityContainer.java" afterDir="false" /> + <change afterPath="$PROJECT_DIR$/api/src/main/java/me/tofaa/entitylib/container/ContainerImpl.java" afterDir="false" /> + <change afterPath="$PROJECT_DIR$/api/src/main/java/me/tofaa/entitylib/container/EntityContainer.java" afterDir="false" /> + <change afterPath="$PROJECT_DIR$/platforms/standalone/build.gradle" afterDir="false" /> + <change afterPath="$PROJECT_DIR$/platforms/standalone/src/main/java/me/tofaa/entitylib/standalone/StandaloneEntityLibApi.java" afterDir="false" /> + <change afterPath="$PROJECT_DIR$/platforms/standalone/src/main/java/me/tofaa/entitylib/standalone/StandaloneEntityLibPlatform.java" afterDir="false" /> + <change afterPath="$PROJECT_DIR$/platforms/standalone/src/main/java/me/tofaa/entitylib/standalone/task/ExecutionType.java" afterDir="false" /> + <change afterPath="$PROJECT_DIR$/platforms/standalone/src/main/java/me/tofaa/entitylib/standalone/task/Scheduler.java" afterDir="false" /> + <change afterPath="$PROJECT_DIR$/platforms/standalone/src/main/java/me/tofaa/entitylib/standalone/task/SchedulerImpl.java" afterDir="false" /> + <change afterPath="$PROJECT_DIR$/platforms/standalone/src/main/java/me/tofaa/entitylib/standalone/task/Task.java" afterDir="false" /> + <change afterPath="$PROJECT_DIR$/platforms/standalone/src/main/java/me/tofaa/entitylib/standalone/task/TaskImpl.java" afterDir="false" /> + <change afterPath="$PROJECT_DIR$/platforms/standalone/src/main/java/me/tofaa/entitylib/standalone/task/TaskSchedule.java" afterDir="false" /> + <change beforePath="$PROJECT_DIR$/.idea/gradle.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/gradle.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" /> - <change beforePath="$PROJECT_DIR$/api/src/main/java/me/tofaa/entitylib/utils/Check.java" beforeDir="false" afterPath="$PROJECT_DIR$/api/src/main/java/me/tofaa/entitylib/utils/Check.java" afterDir="false" /> - <change beforePath="$PROJECT_DIR$/api/src/main/java/me/tofaa/entitylib/wrapper/hologram/Hologram.java" beforeDir="false" afterPath="$PROJECT_DIR$/api/src/main/java/me/tofaa/entitylib/wrapper/hologram/Hologram.java" afterDir="false" /> + <change beforePath="$PROJECT_DIR$/api/src/main/java/me/tofaa/entitylib/EntityLibAPI.java" beforeDir="false" afterPath="$PROJECT_DIR$/api/src/main/java/me/tofaa/entitylib/EntityLibAPI.java" afterDir="false" /> + <change beforePath="$PROJECT_DIR$/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntity.java" beforeDir="false" afterPath="$PROJECT_DIR$/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntity.java" afterDir="false" /> + <change beforePath="$PROJECT_DIR$/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntityAttributes.java" beforeDir="false" afterPath="$PROJECT_DIR$/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntityAttributes.java" afterDir="false" /> + <change beforePath="$PROJECT_DIR$/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntityCreature.java" beforeDir="false" afterPath="$PROJECT_DIR$/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntityCreature.java" afterDir="false" /> + <change beforePath="$PROJECT_DIR$/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperExperienceOrbEntity.java" beforeDir="false" afterPath="$PROJECT_DIR$/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperExperienceOrbEntity.java" afterDir="false" /> + <change beforePath="$PROJECT_DIR$/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperLivingEntity.java" beforeDir="false" afterPath="$PROJECT_DIR$/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperLivingEntity.java" afterDir="false" /> + <change beforePath="$PROJECT_DIR$/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperPlayer.java" beforeDir="false" afterPath="$PROJECT_DIR$/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperPlayer.java" afterDir="false" /> <change beforePath="$PROJECT_DIR$/api/src/main/java/me/tofaa/entitylib/wrapper/hologram/LegacyHologram.java" beforeDir="false" afterPath="$PROJECT_DIR$/api/src/main/java/me/tofaa/entitylib/wrapper/hologram/LegacyHologram.java" afterDir="false" /> <change beforePath="$PROJECT_DIR$/api/src/main/java/me/tofaa/entitylib/wrapper/hologram/ModernHologram.java" beforeDir="false" afterPath="$PROJECT_DIR$/api/src/main/java/me/tofaa/entitylib/wrapper/hologram/ModernHologram.java" afterDir="false" /> - <change beforePath="$PROJECT_DIR$/test-plugin/build.gradle" beforeDir="false" afterPath="$PROJECT_DIR$/test-plugin/build.gradle" afterDir="false" /> + <change beforePath="$PROJECT_DIR$/build.gradle" beforeDir="false" afterPath="$PROJECT_DIR$/build.gradle" afterDir="false" /> + <change beforePath="$PROJECT_DIR$/common/src/main/java/me/tofaa/entitylib/common/AbstractEntityLibAPI.java" beforeDir="false" afterPath="$PROJECT_DIR$/common/src/main/java/me/tofaa/entitylib/common/AbstractEntityLibAPI.java" afterDir="false" /> + <change beforePath="$PROJECT_DIR$/platforms/spigot/build.gradle" beforeDir="false" afterPath="$PROJECT_DIR$/platforms/spigot/build.gradle" afterDir="false" /> + <change beforePath="$PROJECT_DIR$/platforms/spigot/src/main/java/me/tofaa/entitylib/spigot/SpigotEntityLibAPI.java" beforeDir="false" afterPath="$PROJECT_DIR$/platforms/spigot/src/main/java/me/tofaa/entitylib/spigot/SpigotEntityLibAPI.java" afterDir="false" /> + <change beforePath="$PROJECT_DIR$/platforms/velocity/build.gradle" beforeDir="false" afterPath="$PROJECT_DIR$/platforms/velocity/build.gradle" afterDir="false" /> + <change beforePath="$PROJECT_DIR$/platforms/velocity/src/main/java/me/tofaa/entitylib/velocity/VelocityEntityLibAPI.java" beforeDir="false" afterPath="$PROJECT_DIR$/platforms/velocity/src/main/java/me/tofaa/entitylib/velocity/VelocityEntityLibAPI.java" afterDir="false" /> + <change beforePath="$PROJECT_DIR$/settings.gradle" beforeDir="false" afterPath="$PROJECT_DIR$/settings.gradle" afterDir="false" /> <change beforePath="$PROJECT_DIR$/test-plugin/src/main/java/me/tofaa/testentitylib/TestEntityLibPlugin.java" beforeDir="false" afterPath="$PROJECT_DIR$/test-plugin/src/main/java/me/tofaa/testentitylib/TestEntityLibPlugin.java" afterDir="false" /> + <change beforePath="$PROJECT_DIR$/test-plugin/src/main/java/me/tofaa/testentitylib/TestMassivePigCommand.java" beforeDir="false" afterPath="$PROJECT_DIR$/test-plugin/src/main/java/me/tofaa/testentitylib/TestMassivePigCommand.java" afterDir="false" /> + <change beforePath="$PROJECT_DIR$/test-plugin/src/main/java/me/tofaa/testentitylib/TestTextDisplayCommand.java" beforeDir="false" afterPath="$PROJECT_DIR$/test-plugin/src/main/java/me/tofaa/testentitylib/TestTextDisplayCommand.java" afterDir="false" /> </list> <option name="SHOW_DIALOG" value="false" /> <option name="HIGHLIGHT_CONFLICTS" value="true" /> @@ -83,51 +108,51 @@ <option name="showExcludedFiles" value="false" /> <option name="showLibraryContents" value="true" /> </component> - <component name="PropertiesComponent"><![CDATA[{ - "keyToString": { - "Downloaded.Files.Path.Enabled": "false", - "Gradle.Build EntityLib.executor": "Run", - "Gradle.EntityLib [dependencies].executor": "Run", - "Gradle.EntityLib [publish].executor": "Run", - "Gradle.EntityLib [runServer] (1).executor": "Run", - "Gradle.EntityLib [runServer].executor": "Run", - "Gradle.EntityLib:code-gen [:code-gen:Main.main()].executor": "Run", - "Gradle.EntityLib:test-plugin [cleanAllRunTaskCaches].executor": "Run", - "Gradle.EntityLib:test-plugin [cleanCustomServiceCaches].executor": "Run", - "Gradle.EntityLib:test-plugin [cleanPaperCache].executor": "Run", - "Gradle.EntityLib:test-plugin [cleanPaperPluginsCache].executor": "Run", - "Gradle.EntityLib:test-plugin [publish].executor": "Run", - "Gradle.EntityLib:test-plugin [runServer].executor": "Run", - "Gradle.EntityLib:test-plugin [shadowJar].executor": "Run", - "JAR Application.Unnamed.executor": "Run", - "Repository.Attach.Annotations": "false", - "Repository.Attach.JavaDocs": "false", - "Repository.Attach.Sources": "false", - "RunOnceActivity.OpenProjectViewOnStart": "true", - "RunOnceActivity.ShowReadmeOnStart": "true", - "WebServerToolWindowFactoryState": "false", - "git-widget-placeholder": "master", - "ignore.virus.scanning.warn.message": "true", - "jdk.selected.JAVA_MODULE": "corretto-17", - "kotlin-language-version-configured": "true", - "last_opened_file_path": "D:/Github/EntityLib", - "node.js.detected.package.eslint": "true", - "node.js.detected.package.tslint": "true", - "node.js.selected.package.eslint": "(autodetect)", - "node.js.selected.package.tslint": "(autodetect)", - "nodejs_package_manager_path": "npm", - "project.structure.last.edited": "Modules", - "project.structure.proportion": "0.15", - "project.structure.side.proportion": "0.2", - "settings.editor.selected.configurable": "preferences.editor", - "vue.rearranger.settings.migration": "true" + <component name="PropertiesComponent">{ + "keyToString": { + "Downloaded.Files.Path.Enabled": "false", + "Gradle.Build EntityLib.executor": "Run", + "Gradle.EntityLib [dependencies].executor": "Run", + "Gradle.EntityLib [publish].executor": "Run", + "Gradle.EntityLib [runServer] (1).executor": "Run", + "Gradle.EntityLib [runServer].executor": "Run", + "Gradle.EntityLib:code-gen [:code-gen:Main.main()].executor": "Run", + "Gradle.EntityLib:test-plugin [cleanAllRunTaskCaches].executor": "Run", + "Gradle.EntityLib:test-plugin [cleanCustomServiceCaches].executor": "Run", + "Gradle.EntityLib:test-plugin [cleanPaperCache].executor": "Run", + "Gradle.EntityLib:test-plugin [cleanPaperPluginsCache].executor": "Run", + "Gradle.EntityLib:test-plugin [publish].executor": "Run", + "Gradle.EntityLib:test-plugin [runServer].executor": "Run", + "Gradle.EntityLib:test-plugin [shadowJar].executor": "Run", + "JAR Application.Unnamed.executor": "Run", + "Repository.Attach.Annotations": "false", + "Repository.Attach.JavaDocs": "false", + "Repository.Attach.Sources": "false", + "RunOnceActivity.OpenProjectViewOnStart": "true", + "RunOnceActivity.ShowReadmeOnStart": "true", + "WebServerToolWindowFactoryState": "false", + "git-widget-placeholder": "master", + "ignore.virus.scanning.warn.message": "true", + "jdk.selected.JAVA_MODULE": "corretto-17", + "kotlin-language-version-configured": "true", + "last_opened_file_path": "D:/Github/EntityLib", + "node.js.detected.package.eslint": "true", + "node.js.detected.package.tslint": "true", + "node.js.selected.package.eslint": "(autodetect)", + "node.js.selected.package.tslint": "(autodetect)", + "nodejs_package_manager_path": "npm", + "project.structure.last.edited": "Modules", + "project.structure.proportion": "0.15", + "project.structure.side.proportion": "0.2", + "settings.editor.selected.configurable": "preferences.editor", + "vue.rearranger.settings.migration": "true" }, - "keyToStringList": { - "kotlin-gradle-user-dirs": [ - "/home/tofaa/.gradle" + "keyToStringList": { + "kotlin-gradle-user-dirs": [ + "/home/tofaa/.gradle" ] } -}]]></component> +}</component> <component name="RecentsManager"> <key name="CopyFile.RECENT_KEYS"> <recent name="$PROJECT_DIR$/model-engine-addon" /> @@ -385,7 +410,8 @@ <workItem from="1716488324710" duration="3710000" /> <workItem from="1716722129881" duration="965000" /> <workItem from="1716926795264" duration="1000" /> - <workItem from="1718023888535" duration="1738000" /> + <workItem from="1718023888535" duration="2457000" /> + <workItem from="1719160307151" duration="6107000" /> </task> <servers /> </component> diff --git a/api/src/main/java/me/tofaa/entitylib/EntityLibAPI.java b/api/src/main/java/me/tofaa/entitylib/EntityLibAPI.java index d647977..623e82d 100644 --- a/api/src/main/java/me/tofaa/entitylib/EntityLibAPI.java +++ b/api/src/main/java/me/tofaa/entitylib/EntityLibAPI.java @@ -4,6 +4,7 @@ import com.github.retrooper.packetevents.PacketEventsAPI; import com.github.retrooper.packetevents.protocol.entity.type.EntityType; import com.github.retrooper.packetevents.protocol.player.UserProfile; import com.github.retrooper.packetevents.protocol.world.Location; +import me.tofaa.entitylib.container.EntityContainer; import me.tofaa.entitylib.tick.TickContainer; import me.tofaa.entitylib.wrapper.WrapperEntity; import me.tofaa.entitylib.wrapper.WrapperPlayer; @@ -30,30 +31,12 @@ public interface EntityLibAPI<T> { void onEnable(); - @NotNull <T extends WrapperEntity> T createEntity(UUID uuid, int entityId, EntityType type); - - @NotNull <T extends WrapperEntity> T createEntity(EntityType type); - - @NotNull WrapperPlayer spawnPlayer(UserProfile profile, Location location); - - @NotNull WrapperPlayer createPlayer(UserProfile profile); - - @NotNull WrapperPlayer createPlayer(UserProfile profile, int entityId); - - @NotNull <T extends WrapperEntity> T spawnEntity(@NotNull Class<T> wrapperClass, @NotNull EntityType entityType, @NotNull Location location); - - @NotNull WrapperEntity spawnEntity(@NotNull EntityType entityType, @NotNull Location location); - - @NotNull <T extends WrapperEntity> T spawnEntity(@NotNull T entity, @NotNull Location location); - - @NotNull <T extends WrapperEntity> T cloneEntity(@NotNull Object platformEntity, @NotNull Location location); + @NotNull <P extends WrapperEntity> P cloneEntity(@NotNull Object platformEntity); @Nullable WrapperEntity getEntity(int id); @Nullable WrapperEntity getEntity(@NotNull UUID uuid); - void removeEntity(WrapperEntity entity); - @NotNull Collection<WrapperEntity> getAllEntities(); /** @@ -71,4 +54,13 @@ public interface EntityLibAPI<T> { * @param tickContainer the TickContainer to add. */ void addTickContainer(@NotNull TickContainer<?, T> tickContainer); + + @NotNull + EntityContainer getDefaultContainer(); + + void addContainer(EntityContainer container); + + void removeContainer(EntityContainer container); + + @NotNull Collection<EntityContainer> getEntityContainers(); } diff --git a/api/src/main/java/me/tofaa/entitylib/container/AbstractEntityContainer.java b/api/src/main/java/me/tofaa/entitylib/container/AbstractEntityContainer.java new file mode 100644 index 0000000..04556a1 --- /dev/null +++ b/api/src/main/java/me/tofaa/entitylib/container/AbstractEntityContainer.java @@ -0,0 +1,90 @@ +package me.tofaa.entitylib.container; + +import me.tofaa.entitylib.wrapper.WrapperEntity; +import org.jetbrains.annotations.NotNull; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +public abstract class AbstractEntityContainer implements EntityContainer { + + private final Map<UUID, WrapperEntity> entities = new ConcurrentHashMap<>(); + private final Map<Integer, WrapperEntity> entitiesById = new ConcurrentHashMap<>(); + + + @Override + public void addEntity(WrapperEntity entity) { + entities.put(entity.getUuid(), entity); + entitiesById.put(entity.getEntityId(), entity); + } + + @Override + public void removeEntity(WrapperEntity entity, boolean despawn) { + entities.remove(entity.getUuid()); + entitiesById.remove(entity.getEntityId()); + if (despawn) { + entity.despawn(); + } + } + + @Override + public void removeEntity(int entityId, boolean despawn) { + WrapperEntity entity = entitiesById.get(entityId); + if (entity != null) { + removeEntity(entity, despawn); + } + } + + @Override + public void removeEntity(UUID uuid, boolean despawn) { + WrapperEntity entity = entities.get(uuid); + if (entity != null) { + removeEntity(entity, despawn); + } + } + + @Override + public void clearEntities(boolean despawn) { + entities.values().forEach(entity -> removeEntity(entity, despawn)); + } + + + @Override + public void tick() { + + } + + public @NotNull Iterator<WrapperEntity> iterator() { + return entities.values().iterator(); + } + + @Override + public Collection<WrapperEntity> getEntities() { + return Collections.unmodifiableCollection(entities.values()); + } + + @Override + public WrapperEntity getEntity(UUID uuid) { + return entities.get(uuid); + } + + @Override + public WrapperEntity getEntity(int entityId) { + return entitiesById.get(entityId); + } + + @Override + public boolean containsEntity(UUID uuid) { + return entities.containsKey(uuid); + } + + @Override + public boolean containsEntity(int entityId) { + return entitiesById.containsKey(entityId); + } + + @Override + public boolean containsEntity(WrapperEntity entity) { + return entities.containsValue(entity); + } +} diff --git a/api/src/main/java/me/tofaa/entitylib/container/ContainerImpl.java b/api/src/main/java/me/tofaa/entitylib/container/ContainerImpl.java new file mode 100644 index 0000000..50cf86e --- /dev/null +++ b/api/src/main/java/me/tofaa/entitylib/container/ContainerImpl.java @@ -0,0 +1,4 @@ +package me.tofaa.entitylib.container; + +class ContainerImpl extends AbstractEntityContainer { +} diff --git a/api/src/main/java/me/tofaa/entitylib/container/EntityContainer.java b/api/src/main/java/me/tofaa/entitylib/container/EntityContainer.java new file mode 100644 index 0000000..40c28e5 --- /dev/null +++ b/api/src/main/java/me/tofaa/entitylib/container/EntityContainer.java @@ -0,0 +1,63 @@ +package me.tofaa.entitylib.container; + +import me.tofaa.entitylib.wrapper.WrapperEntity; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.UUID; + + +/** + * Containers are basic iterable classes that hold entities. These containers can be extended to provide more functionality, and this allows for seperation of logic between certain entities. + * <p> + * To access a specific {@link WrapperEntity}'s container you can use {@link WrapperEntity#getParentContainer()}. + * </p> + */ +public interface EntityContainer extends Iterable<WrapperEntity> { + + static EntityContainer basic() { + return new ContainerImpl(); + } + + void addEntity(WrapperEntity entity); + + void removeEntity(WrapperEntity entity, boolean despawn); + + default void removeEntity(WrapperEntity entity) { + removeEntity(entity, false); + } + + void removeEntity(int entityId, boolean despawn); + + default void removeEntity(int entityId) { + removeEntity(entityId, false); + } + + void removeEntity(UUID uuid, boolean despawn); + + default void removeEntity(UUID entity) { + removeEntity(entity, false); + } + + void clearEntities(boolean despawn); + + default void clearEntities() { + clearEntities(false); + } + + void tick(); + + Collection<WrapperEntity> getEntities(); + + @Nullable WrapperEntity getEntity(UUID uuid); + + @Nullable WrapperEntity getEntity(int entityId); + + boolean containsEntity(UUID uuid); + + boolean containsEntity(int entityId); + + /** Value based contains, not reference based */ + boolean containsEntity(WrapperEntity 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 b75735e..02bd72f 100644 --- a/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntity.java +++ b/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntity.java @@ -8,6 +8,7 @@ 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.container.EntityContainer; import me.tofaa.entitylib.meta.EntityMeta; import me.tofaa.entitylib.meta.types.ObjectData; import me.tofaa.entitylib.tick.Tickable; @@ -34,6 +35,7 @@ public class WrapperEntity implements Tickable, TrackedEntity { private Vector3d velocity; private int riding = -1; private final Set<Integer> passengers; + private EntityContainer parent; public WrapperEntity(int entityId, UUID uuid, EntityType entityType, EntityMeta entityMeta) { this.entityId = entityId; @@ -45,7 +47,22 @@ public class WrapperEntity implements Tickable, TrackedEntity { this.passengers = ConcurrentHashMap.newKeySet(); } - public boolean spawn(Location location) { + public WrapperEntity(int entityId, EntityType entityType) { + this(entityId, EntityLib.getPlatform().getEntityUuidProvider().provide(entityType), entityType); + } + + public WrapperEntity(UUID uuid, EntityType entityType) { + this(EntityLib.getPlatform().getEntityIdProvider().provide(uuid, entityType), uuid, entityType); + } + public WrapperEntity(EntityType entityType) { + this(EntityLib.getPlatform().getEntityUuidProvider().provide(entityType), entityType); + } + + public WrapperEntity(int entityId, UUID uuid, EntityType entityType) { + this(entityId, uuid, entityType, EntityMeta.createMeta(entityId, entityType)); + } + + public boolean spawn(Location location, EntityContainer parent) { if (spawned) return false; this.location = location; this.spawned = true; @@ -63,9 +80,15 @@ public class WrapperEntity implements Tickable, TrackedEntity { ) ); sendPacketToViewers(entityMeta.createPacket()); + this.parent = parent; + parent.addEntity(this); return true; } + public boolean spawn(Location location) { + return spawn(location, EntityLib.getApi().getDefaultContainer()); + } + protected int getObjectData() { if (entityMeta instanceof ObjectData) { return ((ObjectData) entityMeta).getObjectData(); @@ -98,7 +121,7 @@ public class WrapperEntity implements Tickable, TrackedEntity { } public void remove() { - EntityLib.getApi().removeEntity(this); + parent.removeEntity(this, true); } public void despawn() { @@ -159,6 +182,10 @@ public class WrapperEntity implements Tickable, TrackedEntity { } } + public EntityContainer getParentContainer() { + return parent; + } + public void sendMessageToViewers(Component message) { sendPacketToViewers(new WrapperPlayServerSystemChatMessage(false, message)); } @@ -381,6 +408,18 @@ public class WrapperEntity implements Tickable, TrackedEntity { } } + public void sendPacketToViewersIfSpawned(PacketWrapper<?> packet) { + if (spawned) { + sendPacketToViewers(packet); + } + } + + public void sendPacketsToViewersIfSpawned(PacketWrapper<?>... wrappers) { + if (spawned) { + sendPacketsToViewers(wrappers); + } + } + private static void sendPacket(UUID user, PacketWrapper<?> wrapper) { if (wrapper == null) return; Object channel = EntityLib.getApi().getPacketEvents().getProtocolManager().getChannel(user); diff --git a/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntityAttributes.java b/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntityAttributes.java index 8c4a1f4..4f9ead5 100644 --- a/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntityAttributes.java +++ b/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntityAttributes.java @@ -75,7 +75,7 @@ public final class WrapperEntityAttributes { public void refresh() { - entity.sendPacketToViewers(createPacket()); + if (entity.isSpawned()) entity.sendPacketToViewers(createPacket()); } public WrapperPlayServerUpdateAttributes createPacket() { diff --git a/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntityCreature.java b/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntityCreature.java index 6771d15..6be04b3 100644 --- a/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntityCreature.java +++ b/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntityCreature.java @@ -1,6 +1,7 @@ package me.tofaa.entitylib.wrapper; import com.github.retrooper.packetevents.protocol.entity.type.EntityType; +import me.tofaa.entitylib.EntityLib; import me.tofaa.entitylib.meta.EntityMeta; import me.tofaa.entitylib.wrapper.ai.AIGroup; import org.jetbrains.annotations.NotNull; @@ -29,6 +30,21 @@ public class WrapperEntityCreature extends WrapperLivingEntity { this.aiGroups = new HashSet<>(); } + public WrapperEntityCreature(int entityId, @NotNull UUID uuid, EntityType entityType) { + this(entityId, uuid, entityType, EntityMeta.createMeta(entityId, entityType)); + } + + public WrapperEntityCreature(int entityId, EntityType entityType) { + this(entityId, EntityLib.getPlatform().getEntityUuidProvider().provide(entityType), entityType); + } + + public WrapperEntityCreature(UUID uuid, EntityType entityType) { + this(EntityLib.getPlatform().getEntityIdProvider().provide(uuid, entityType), uuid, entityType); + } + public WrapperEntityCreature(EntityType entityType) { + this(EntityLib.getPlatform().getEntityUuidProvider().provide(entityType), entityType); + } + @Override public void tick(long time) { super.tick(time); diff --git a/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperExperienceOrbEntity.java b/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperExperienceOrbEntity.java index 27b38b8..6744044 100644 --- a/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperExperienceOrbEntity.java +++ b/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperExperienceOrbEntity.java @@ -3,6 +3,7 @@ package me.tofaa.entitylib.wrapper; import com.github.retrooper.packetevents.protocol.entity.type.EntityType; import com.github.retrooper.packetevents.protocol.world.Location; import com.github.retrooper.packetevents.util.Vector3d; +import me.tofaa.entitylib.EntityLib; import me.tofaa.entitylib.meta.EntityMeta; import org.jetbrains.annotations.NotNull; @@ -17,6 +18,21 @@ public class WrapperExperienceOrbEntity extends WrapperEntity { super(entityId, uuid, entityType, meta); } + public WrapperExperienceOrbEntity(int entityId, @NotNull UUID uuid, EntityType entityType) { + this(entityId, uuid, entityType, EntityMeta.createMeta(entityId, entityType)); + } + + public WrapperExperienceOrbEntity(int entityId, EntityType entityType) { + this(entityId, EntityLib.getPlatform().getEntityUuidProvider().provide(entityType), entityType); + } + + public WrapperExperienceOrbEntity(UUID uuid, EntityType entityType) { + this(EntityLib.getPlatform().getEntityIdProvider().provide(uuid, entityType), uuid, entityType); + } + public WrapperExperienceOrbEntity(EntityType entityType) { + this(EntityLib.getPlatform().getEntityUuidProvider().provide(entityType), entityType); + } + /** * Applies a slight slide motion towards the given location. * <p> diff --git a/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperLivingEntity.java b/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperLivingEntity.java index cfa8069..df599ce 100644 --- a/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperLivingEntity.java +++ b/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperLivingEntity.java @@ -1,19 +1,14 @@ package me.tofaa.entitylib.wrapper; -import com.github.retrooper.packetevents.protocol.attribute.Attribute; import com.github.retrooper.packetevents.protocol.entity.type.EntityType; import com.github.retrooper.packetevents.protocol.nbt.NBTCompound; -import com.github.retrooper.packetevents.protocol.player.HumanoidArm; import com.github.retrooper.packetevents.protocol.potion.PotionType; import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityAnimation; import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityEffect; -import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerTeams; -import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerUpdateAttributes; +import me.tofaa.entitylib.EntityLib; import me.tofaa.entitylib.meta.EntityMeta; -import me.tofaa.entitylib.meta.types.LivingEntityMeta; import org.jetbrains.annotations.Nullable; -import java.util.List; import java.util.UUID; public class WrapperLivingEntity extends WrapperEntity{ @@ -27,6 +22,21 @@ public class WrapperLivingEntity extends WrapperEntity{ this.attributes = new WrapperEntityAttributes(this); } + public WrapperLivingEntity(int entityId, UUID uuid, EntityType entityType) { + this(entityId, uuid, entityType, EntityMeta.createMeta(entityId, entityType)); + } + + public WrapperLivingEntity(int entityId, EntityType entityType) { + this(entityId, EntityLib.getPlatform().getEntityUuidProvider().provide(entityType), entityType); + } + + public WrapperLivingEntity(UUID uuid, EntityType entityType) { + this(EntityLib.getPlatform().getEntityIdProvider().provide(uuid, entityType), uuid, entityType); + } + public WrapperLivingEntity(EntityType entityType) { + this(EntityLib.getPlatform().getEntityUuidProvider().provide(entityType), entityType); + } + @Override public void refresh() { super.refresh(); diff --git a/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperPlayer.java b/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperPlayer.java index 93a056d..ff7d510 100644 --- a/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperPlayer.java +++ b/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperPlayer.java @@ -46,12 +46,12 @@ public class WrapperPlayer extends WrapperLivingEntity { public void setGameMode(GameMode gameMode) { this.gameMode = gameMode; - sendPacketsToViewers(new WrapperPlayServerPlayerInfoUpdate(WrapperPlayServerPlayerInfoUpdate.Action.UPDATE_GAME_MODE, createInfo())); + sendPacketsToViewersIfSpawned(new WrapperPlayServerPlayerInfoUpdate(WrapperPlayServerPlayerInfoUpdate.Action.UPDATE_GAME_MODE, createInfo())); } public void setDisplayName(Component displayName) { this.displayName = displayName; - sendPacketsToViewers(new WrapperPlayServerPlayerInfoUpdate(WrapperPlayServerPlayerInfoUpdate.Action.UPDATE_DISPLAY_NAME, createInfo())); + sendPacketsToViewersIfSpawned(new WrapperPlayServerPlayerInfoUpdate(WrapperPlayServerPlayerInfoUpdate.Action.UPDATE_DISPLAY_NAME, createInfo())); } public Component getDisplayName() { @@ -94,9 +94,9 @@ public class WrapperPlayer extends WrapperLivingEntity { public void setInTablist(boolean tablist) { this.tablist = tablist; - sendPacketToViewers(tabListPacket()); + sendPacketsToViewersIfSpawned(tabListPacket()); if (!tablist) { - sendPacketToViewers(tabListRemovePacket()); + sendPacketsToViewersIfSpawned(tabListRemovePacket()); } } @@ -106,7 +106,7 @@ public class WrapperPlayer extends WrapperLivingEntity { public void setLatency(int latency) { this.latency = latency; - sendPacketsToViewers( + sendPacketsToViewersIfSpawned( new WrapperPlayServerPlayerInfoUpdate( WrapperPlayServerPlayerInfoUpdate.Action.UPDATE_LATENCY, createInfo() diff --git a/api/src/main/java/me/tofaa/entitylib/wrapper/hologram/LegacyHologram.java b/api/src/main/java/me/tofaa/entitylib/wrapper/hologram/LegacyHologram.java index 4062f21..6cf847c 100644 --- a/api/src/main/java/me/tofaa/entitylib/wrapper/hologram/LegacyHologram.java +++ b/api/src/main/java/me/tofaa/entitylib/wrapper/hologram/LegacyHologram.java @@ -96,7 +96,8 @@ final class LegacyHologram implements Hologram.Legacy { @Override public void setLine(int index, @Nullable Component line) { - WrapperEntity e = EntityLib.getApi().spawnEntity(EntityTypes.ARMOR_STAND, location); + WrapperEntity e = new WrapperEntity(EntityTypes.ARMOR_STAND); + e.spawn(location); ArmorStandMeta meta = (ArmorStandMeta) e.getEntityMeta(); meta.setCustomName(line); meta.setCustomNameVisible(true); diff --git a/api/src/main/java/me/tofaa/entitylib/wrapper/hologram/ModernHologram.java b/api/src/main/java/me/tofaa/entitylib/wrapper/hologram/ModernHologram.java index 110af16..7391d1f 100644 --- a/api/src/main/java/me/tofaa/entitylib/wrapper/hologram/ModernHologram.java +++ b/api/src/main/java/me/tofaa/entitylib/wrapper/hologram/ModernHologram.java @@ -10,6 +10,7 @@ import net.kyori.adventure.text.Component; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.sql.Wrapper; import java.util.ArrayList; import java.util.List; import java.util.UUID; @@ -72,7 +73,8 @@ final class ModernHologram implements Hologram.Modern { @Override public void setLine(int index, @Nullable Component line) { - WrapperEntity e = EntityLib.getApi().spawnEntity(EntityTypes.TEXT_DISPLAY, location); + WrapperEntity e = new WrapperEntity(EntityTypes.TEXT_DISPLAY); + e.spawn(location); TextDisplayMeta meta = (TextDisplayMeta) e.getEntityMeta(); meta.setInvisible(true); meta.setHasNoGravity(true); diff --git a/build.gradle b/build.gradle index 981e340..81db5c3 100644 --- a/build.gradle +++ b/build.gradle @@ -11,6 +11,8 @@ allprojects { "net.kyori:adventure-text-serializer-legacy:${adventureVersion}", "net.kyori:adventure-nbt:${adventureVersion}"] + project.ext.peVersion = '2.3.0' + apply plugin: 'java' apply plugin: 'java-library' apply plugin: 'maven-publish' @@ -59,6 +61,6 @@ allprojects { dependencies { - compileOnlyApi 'com.github.retrooper.packetevents:spigot:2.3.0' + compileOnlyApi 'com.github.retrooper.packetevents:spigot:${project.ext.peVersion}' } diff --git a/common/src/main/java/me/tofaa/entitylib/common/AbstractEntityLibAPI.java b/common/src/main/java/me/tofaa/entitylib/common/AbstractEntityLibAPI.java index 564174d..1287850 100644 --- a/common/src/main/java/me/tofaa/entitylib/common/AbstractEntityLibAPI.java +++ b/common/src/main/java/me/tofaa/entitylib/common/AbstractEntityLibAPI.java @@ -9,6 +9,7 @@ import me.tofaa.entitylib.APIConfig; import me.tofaa.entitylib.EntityLib; import me.tofaa.entitylib.EntityLibAPI; import me.tofaa.entitylib.Platform; +import me.tofaa.entitylib.container.EntityContainer; import me.tofaa.entitylib.meta.EntityMeta; import me.tofaa.entitylib.meta.projectile.ThrownExpBottleMeta; import me.tofaa.entitylib.meta.types.LivingEntityMeta; @@ -33,6 +34,9 @@ public abstract class AbstractEntityLibAPI<P, T> implements EntityLibAPI<T> { protected final Map<Integer, WrapperEntity> entitiesById = new ConcurrentHashMap<>(); protected final Map<UUID, WrapperEntity> entities = new ConcurrentHashMap<>(); + protected final Set<EntityContainer> entityContainers = ConcurrentHashMap.newKeySet(); + protected final EntityContainer defaultEntityContainer = EntityContainer.basic(); + protected AbstractEntityLibAPI(Platform<P> platform, APIConfig settings) { this.platform = platform; this.packetEvents = settings.getPacketEvents(); @@ -40,125 +44,6 @@ public abstract class AbstractEntityLibAPI<P, T> implements EntityLibAPI<T> { this.tickContainers = settings.shouldTickTickables() ? new HashSet<>() : Collections.emptyList(); } - @Override - public <T1 extends WrapperEntity> @NotNull T1 createEntity(UUID uuid, int entityId, EntityType type) { - if (entities.containsKey(uuid)) { - throw new IllegalArgumentException("Entity with UUID " + uuid + " already exists in this world."); - } - if (entitiesById.containsKey(entityId)) { - throw new IllegalArgumentException("Entity with ID " + entityId + " already exists in this world."); - } - EntityMeta meta = EntityMeta.createMeta(entityId, type); - WrapperEntity e; - if (meta instanceof LivingEntityMeta) { - e = new WrapperLivingEntity(entityId, uuid, type, meta); - } - else if (meta instanceof ThrownExpBottleMeta) { - e = new WrapperExperienceOrbEntity(entityId, uuid, type, meta); - } - else { - e = new WrapperEntity(entityId, uuid, type, meta); - } - entities.put(uuid, e); - entitiesById.put(entityId, e); - return (T1) e; - } - - @Override - public <T1 extends WrapperEntity> @NotNull T1 createEntity(EntityType type) { - UUID uuid = EntityLib.getPlatform().getEntityUuidProvider().provide(type); - while (entities.containsKey(uuid)) { - uuid = EntityLib.getPlatform().getEntityUuidProvider().provide(type); - } - int entityId = EntityLib.getPlatform().getEntityIdProvider().provide(uuid, type); - while (entitiesById.containsKey(entityId)) { - entityId = EntityLib.getPlatform().getEntityIdProvider().provide(uuid, type); - } - return createEntity(uuid, entityId, type); - } - - @Override - public @NotNull WrapperPlayer spawnPlayer(UserProfile profile, Location location) { - if (getEntity(profile.getUUID()) != null) { - throw new IllegalArgumentException("Entity with UUID " + profile.getUUID() + " already exists in this world."); - } - - int id = EntityLib.getPlatform().getEntityIdProvider().provide(profile.getUUID(), EntityTypes.PLAYER); - while (entitiesById.containsKey(id)) { - id = EntityLib.getPlatform().getEntityIdProvider().provide(profile.getUUID(), EntityTypes.PLAYER); - } - WrapperPlayer player = new WrapperPlayer(profile, id); - player.spawn(location); - entities.put(player.getUuid(), player); - entitiesById.put(player.getEntityId(), player); - return player; - } - - @Override - public @NotNull WrapperPlayer createPlayer(UserProfile profile) { - int id = EntityLib.getPlatform().getEntityIdProvider().provide(profile.getUUID(), EntityTypes.PLAYER); - while (entitiesById.containsKey - (id)) { - id = EntityLib.getPlatform().getEntityIdProvider().provide(profile.getUUID(), EntityTypes.PLAYER); - } - return createPlayer(profile, id); - } - - @Override - public @NotNull WrapperPlayer createPlayer(UserProfile profile, int entityId) { - if (getEntity(profile.getUUID()) != null) { - throw new IllegalArgumentException("Entity with UUID " + profile.getUUID() + " already exists in this world."); - } - WrapperPlayer player = new WrapperPlayer(profile, entityId); - entities.put(player.getUuid(), player); - entitiesById.put(player.getEntityId(), player); - return player; - } - - @Override - public <T1 extends WrapperEntity> @NotNull T1 spawnEntity(@NotNull T1 entity, @NotNull Location location) { - entity.spawn(location); - entities.put(entity.getUuid(), entity); - entitiesById.put(entity.getEntityId(), entity); - return entity; - } - - @Override - public void removeEntity(WrapperEntity entity) { - entity.despawn(); - this.entities.remove(entity.getUuid()); - this.entitiesById.remove(entity.getEntityId()); - } - - @Override - public <T1 extends WrapperEntity> @NotNull T1 spawnEntity(@NotNull Class<T1> wrapperClass, @NotNull EntityType entityType, @NotNull Location location) { - UUID uuid = EntityLib.getPlatform().getEntityUuidProvider().provide(entityType); - while (entities.containsKey(uuid)) { - uuid = EntityLib.getPlatform().getEntityUuidProvider().provide(entityType); - } - int entityId = EntityLib.getPlatform().getEntityIdProvider().provide(uuid, entityType); - while (entitiesById.containsKey(entityId)) { - entityId = EntityLib.getPlatform().getEntityIdProvider().provide(uuid, entityType); - } - EntityMeta meta = EntityMeta.createMeta(entityId, entityType); - WrapperEntity e; - if (meta instanceof LivingEntityMeta) { - e = new WrapperLivingEntity(entityId, uuid, entityType, meta); - } - else if (meta instanceof ThrownExpBottleMeta) { - e = new WrapperExperienceOrbEntity(entityId, uuid, entityType, meta); - } - else { - e = new WrapperEntity(entityId, uuid, entityType, meta); - } - return spawnEntity(wrapperClass.cast(e), location); - } - - @Override - public @NotNull WrapperEntity spawnEntity(@NotNull EntityType entityType, @NotNull Location location) { - return spawnEntity(WrapperEntity.class, entityType, location); - } - @Override public @Nullable WrapperEntity getEntity(int id) { return entitiesById.get(id); @@ -174,6 +59,27 @@ public abstract class AbstractEntityLibAPI<P, T> implements EntityLibAPI<T> { return Collections.unmodifiableCollection(entities.values()); } + @Override + public void addContainer(EntityContainer container) { + entityContainers.add(container); + } + + @Override + public void removeContainer(EntityContainer container) { + entityContainers.remove(container); + } + + @Override + public @NotNull EntityContainer getDefaultContainer() { + return defaultEntityContainer; + } + + @NotNull + @Override + public Set<EntityContainer> getEntityContainers() { + return entityContainers; + } + @NotNull @Override public APIConfig getSettings() { diff --git a/platforms/spigot/build.gradle b/platforms/spigot/build.gradle index 6a9b678..0a819da 100644 --- a/platforms/spigot/build.gradle +++ b/platforms/spigot/build.gradle @@ -1,6 +1,6 @@ dependencies { api(project(":common")) - compileOnly('com.github.retrooper.packetevents:spigot:2.3.0') + compileOnly('com.github.retrooper.packetevents:spigot:' + peVersion) compileOnly('org.spigotmc:spigot-api:1.20.1-R0.1-SNAPSHOT') } diff --git a/platforms/spigot/src/main/java/me/tofaa/entitylib/spigot/SpigotEntityLibAPI.java b/platforms/spigot/src/main/java/me/tofaa/entitylib/spigot/SpigotEntityLibAPI.java index a7e519d..a9a8122 100644 --- a/platforms/spigot/src/main/java/me/tofaa/entitylib/spigot/SpigotEntityLibAPI.java +++ b/platforms/spigot/src/main/java/me/tofaa/entitylib/spigot/SpigotEntityLibAPI.java @@ -44,7 +44,7 @@ public class SpigotEntityLibAPI extends AbstractEntityLibAPI<JavaPlugin, BukkitT } @Override - public <T extends WrapperEntity> @NotNull T cloneEntity(@NotNull Object platformEntity, @NotNull Location location) { + public <T extends WrapperEntity> @NotNull T cloneEntity(@NotNull Object platformEntity) { Check.stateCondition(!(platformEntity instanceof Entity), "Entity must be a Bukkit entity"); Entity e = (Entity) platformEntity; EntityType type = SpigotConversionUtil.fromBukkitEntityType(e.getType()); @@ -84,10 +84,7 @@ public class SpigotEntityLibAPI extends AbstractEntityLibAPI<JavaPlugin, BukkitT else { entity = new WrapperEntity(id, uuid, type, meta); } - if (entity == null) { - throw new IllegalArgumentException("Could not clone entity"); - } - return (T) this.spawnEntity(entity, location); + return (T) entity; } @Override diff --git a/platforms/standalone/build.gradle b/platforms/standalone/build.gradle new file mode 100644 index 0000000..9dec5ae --- /dev/null +++ b/platforms/standalone/build.gradle @@ -0,0 +1,6 @@ + + +dependencies { + api(project(":common")) + compileOnly('com.github.retrooper.packetevents:api:2.3.0') +} diff --git a/platforms/standalone/src/main/java/me/tofaa/entitylib/standalone/StandaloneEntityLibApi.java b/platforms/standalone/src/main/java/me/tofaa/entitylib/standalone/StandaloneEntityLibApi.java new file mode 100644 index 0000000..009dead --- /dev/null +++ b/platforms/standalone/src/main/java/me/tofaa/entitylib/standalone/StandaloneEntityLibApi.java @@ -0,0 +1,57 @@ +package me.tofaa.entitylib.standalone; + +import com.github.retrooper.packetevents.protocol.world.Location; +import me.tofaa.entitylib.APIConfig; +import me.tofaa.entitylib.Platform; +import me.tofaa.entitylib.common.AbstractEntityLibAPI; +import me.tofaa.entitylib.standalone.task.ExecutionType; +import me.tofaa.entitylib.standalone.task.Scheduler; +import me.tofaa.entitylib.standalone.task.Task; +import me.tofaa.entitylib.standalone.task.TaskSchedule; +import me.tofaa.entitylib.tick.TickContainer; +import me.tofaa.entitylib.wrapper.WrapperEntity; +import org.jetbrains.annotations.NotNull; + +import java.util.logging.Level; + +public class StandaloneEntityLibApi extends AbstractEntityLibAPI<Object, Task> { + + private final Scheduler scheduler = Scheduler.newScheduler(); + + protected StandaloneEntityLibApi(Platform<Object> platform, APIConfig settings) { + super(platform, settings); + } + + @Override + public void onLoad() { + + } + + @Override + public void onEnable() { + + } + + + @Override + public <P extends WrapperEntity> @NotNull P cloneEntity(@NotNull Object platformEntity) { + throw new UnsupportedOperationException("Not supported in standalone mode"); + } + + @Override + public void addTickContainer(@NotNull TickContainer<?, Task> tickContainer) { + if (!settings.shouldTickTickables()) { + if (settings.isDebugMode()) { + platform.getLogger().log(Level.WARNING, "Tried to add a TickContainer when ticking tickables is disabled!"); + } + return; + } + tickContainers.add(tickContainer); + if (settings.isDebugMode()) { + platform.getLogger().log(Level.CONFIG, "Registering new tick container..."); + } + getTickContainers().add(tickContainer); + Task task = scheduler.scheduleTask(() -> tickContainer.tick(System.currentTimeMillis()), TaskSchedule.duration(50L), TaskSchedule.duration(50L), ExecutionType.ASYNC); + tickContainer.setHandle(task); + } +} diff --git a/platforms/standalone/src/main/java/me/tofaa/entitylib/standalone/StandaloneEntityLibPlatform.java b/platforms/standalone/src/main/java/me/tofaa/entitylib/standalone/StandaloneEntityLibPlatform.java new file mode 100644 index 0000000..f344e45 --- /dev/null +++ b/platforms/standalone/src/main/java/me/tofaa/entitylib/standalone/StandaloneEntityLibPlatform.java @@ -0,0 +1,37 @@ +package me.tofaa.entitylib.standalone; + +import me.tofaa.entitylib.APIConfig; +import me.tofaa.entitylib.EntityLibAPI; +import me.tofaa.entitylib.common.AbstractPlatform; +import org.jetbrains.annotations.NotNull; + +public class StandaloneEntityLibPlatform extends AbstractPlatform<Object> { + + + private StandaloneEntityLibApi api; + + private StandaloneEntityLibPlatform() { + super(null); + } + + @Override + public void setupApi(@NotNull APIConfig settings) { + super.setupApi(settings); + api = new StandaloneEntityLibApi(this, settings); + } + + @Override + public @NotNull Object getHandle() { + throw new UnsupportedOperationException("Standalone platform does not have a handle."); + } + + @Override + public EntityLibAPI<?> getAPI() { + return api; + } + + @Override + public String getName() { + return "Standalone"; + } +} diff --git a/platforms/standalone/src/main/java/me/tofaa/entitylib/standalone/task/ExecutionType.java b/platforms/standalone/src/main/java/me/tofaa/entitylib/standalone/task/ExecutionType.java new file mode 100644 index 0000000..ffc01ed --- /dev/null +++ b/platforms/standalone/src/main/java/me/tofaa/entitylib/standalone/task/ExecutionType.java @@ -0,0 +1,8 @@ +package me.tofaa.entitylib.standalone.task; + +public enum ExecutionType { + + BLOCKING, + ASYNC, + +} diff --git a/platforms/standalone/src/main/java/me/tofaa/entitylib/standalone/task/Scheduler.java b/platforms/standalone/src/main/java/me/tofaa/entitylib/standalone/task/Scheduler.java new file mode 100644 index 0000000..bed0413 --- /dev/null +++ b/platforms/standalone/src/main/java/me/tofaa/entitylib/standalone/task/Scheduler.java @@ -0,0 +1,29 @@ +package me.tofaa.entitylib.standalone.task; + +import java.util.function.Supplier; + +public interface Scheduler { + + static Scheduler newScheduler() { + return new SchedulerImpl(); + } + + void process(); + + void shutdown(); + + Task submitTask(Supplier<TaskSchedule> supplier, ExecutionType type); + + default Task scheduleTask(Runnable task, TaskSchedule delay, TaskSchedule repeat, ExecutionType type) { + return buildTask(task) + .delay(delay) + .repeat(repeat) + .executionType(type) + .schedule(); + } + + default Task.Builder buildTask(Runnable task) { + return new Task.Builder(task, this); + } + +} diff --git a/platforms/standalone/src/main/java/me/tofaa/entitylib/standalone/task/SchedulerImpl.java b/platforms/standalone/src/main/java/me/tofaa/entitylib/standalone/task/SchedulerImpl.java new file mode 100644 index 0000000..ffee587 --- /dev/null +++ b/platforms/standalone/src/main/java/me/tofaa/entitylib/standalone/task/SchedulerImpl.java @@ -0,0 +1,87 @@ +package me.tofaa.entitylib.standalone.task; + +import java.util.Queue; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; + +class SchedulerImpl implements Scheduler { + + private static final AtomicInteger TASK_COUNTER = new AtomicInteger(0); + private static final ScheduledExecutorService SCHEDULED_EXECUTOR = Executors.newSingleThreadScheduledExecutor((e) -> { + Thread thread = new Thread(e); + thread.setDaemon(true); + return thread; + }); + private static final ForkJoinPool FORK_JOIN_POOL = new ForkJoinPool(); + private static final Queue<TaskImpl> TASKS = new ConcurrentLinkedDeque<>(); + + private Thread thread; + + + @Override + public void process() { + thread = new Thread(() -> { + while (true) { + process(); + } + }); + thread.start(); + } + + @Override + public void shutdown() { + thread.interrupt(); + thread = null; + } + + @Override + public Task submitTask(Supplier<TaskSchedule> supplier, ExecutionType type) { + TaskImpl task = new TaskImpl(TASK_COUNTER.getAndIncrement(), supplier, type, this); + handleTask(task); + return task; + } + + private void handleTask(TaskImpl task) { + TaskSchedule schedule = task.task.get(); + if (schedule instanceof TaskSchedule.DurationSchedule) { + TaskSchedule.DurationSchedule d = (TaskSchedule.DurationSchedule) schedule; + SCHEDULED_EXECUTOR.schedule(() -> { + safeExecute(task); + }, d.duration.toMillis(), TimeUnit.MILLISECONDS); + } + else if (schedule instanceof TaskSchedule.FutureSchedule) { + TaskSchedule.FutureSchedule f = (TaskSchedule.FutureSchedule) schedule; + f.future.thenRun(() -> { + safeExecute(task); + }); + } + else if (schedule instanceof TaskSchedule.ParkSchedule) { + task.parked = 1; + } + else if (schedule instanceof TaskSchedule.StopSchedule) { + task.cancel(); + } + else if (schedule instanceof TaskSchedule.ImmediateSchedule) { + TASKS.offer(task); + } + } + + void unparkTask(TaskImpl task) { + task.tryUnpark(); + TASKS.offer(task); + } + + private void safeExecute(TaskImpl task) { + if (task.executionType() == ExecutionType.BLOCKING) { + TASKS.offer(task); + } + else { + FORK_JOIN_POOL.submit(() -> { + if (task.alive) { + handleTask(task); + } + }); + } + } +} diff --git a/platforms/standalone/src/main/java/me/tofaa/entitylib/standalone/task/Task.java b/platforms/standalone/src/main/java/me/tofaa/entitylib/standalone/task/Task.java new file mode 100644 index 0000000..f9cd1e6 --- /dev/null +++ b/platforms/standalone/src/main/java/me/tofaa/entitylib/standalone/task/Task.java @@ -0,0 +1,62 @@ +package me.tofaa.entitylib.standalone.task; + +import java.util.concurrent.atomic.AtomicBoolean; + +public interface Task { + + + int id(); + + ExecutionType executionType(); + + void unpark(); + boolean isParked(); + + void cancel(); + + + class Builder { + + private final Runnable runnable; + private final Scheduler scheduler; + + private ExecutionType executionType = ExecutionType.SYNC;//default + private TaskSchedule delay = TaskSchedule.immediate(); + private TaskSchedule repeat = TaskSchedule.stop(); + + Builder(Runnable runnable, Scheduler scheduler) { + this.runnable = runnable; + this.scheduler = scheduler; + } + + public Builder executionType(ExecutionType executionType) { + this.executionType = executionType; + return this; + } + + public Builder delay(TaskSchedule delay) { + this.delay = delay; + return this; + } + + public Builder repeat(TaskSchedule repeat) { + this.repeat = repeat; + return this; + } + + public Task schedule() { + AtomicBoolean first = new AtomicBoolean(); + return scheduler.submitTask(() -> { + if (first.get()) { + first.set(false); + return delay; + } + runnable.run(); + return repeat; + }, executionType); + } + + + } + +} diff --git a/platforms/standalone/src/main/java/me/tofaa/entitylib/standalone/task/TaskImpl.java b/platforms/standalone/src/main/java/me/tofaa/entitylib/standalone/task/TaskImpl.java new file mode 100644 index 0000000..b3ec01f --- /dev/null +++ b/platforms/standalone/src/main/java/me/tofaa/entitylib/standalone/task/TaskImpl.java @@ -0,0 +1,79 @@ +package me.tofaa.entitylib.standalone.task; + +import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; +import java.util.function.Supplier; + +class TaskImpl implements Task { + + // thread safe way to set the parked value of any task, Like VarHandle but for older java versions + private static final AtomicIntegerFieldUpdater<TaskImpl> PARKED_UPDATER = AtomicIntegerFieldUpdater.newUpdater(TaskImpl.class, "parked"); + + volatile boolean alive = true; + volatile int parked = 1; // 1 is true, 0 is false + + final int id; + final Supplier<TaskSchedule> task; + final ExecutionType type; + final Scheduler owner; + + TaskImpl(int id, Supplier<TaskSchedule> task, ExecutionType type, Scheduler owner) { + this.id = id; + this.task = task; + this.type = type; + this.owner = owner; + } + + + @Override + public int id() { + return id; + } + + @Override + public ExecutionType executionType() { + return type; + } + + @Override + public void unpark() { + this.owner.unparkTask(this); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null || getClass() != obj.getClass()) return false; + return this.id == ((TaskImpl) obj).id; + } + + @Override + public int hashCode() { + // murmur hash 3 + int h = id; + h ^= h >>> 16; + h *= 0x85ebca6b; + h ^= h >>> 13; + h *= 0xc2b2ae35; + h ^= h >>> 16; + return h; + } + + @Override + public String toString() { + return "SimpleTask(id=" + id + ", task=" + task + ", executionType=" + type + ", owner=" + owner + ", alive=" + alive + ", parked=" + parked + ")"; + } + + void tryUnpark() { + PARKED_UPDATER.compareAndSet(this, 1, 0); + } + + @Override + public boolean isParked() { + return parked == 1; + } + + @Override + public void cancel() { + alive = false; + } +} diff --git a/platforms/standalone/src/main/java/me/tofaa/entitylib/standalone/task/TaskSchedule.java b/platforms/standalone/src/main/java/me/tofaa/entitylib/standalone/task/TaskSchedule.java new file mode 100644 index 0000000..4c7bc80 --- /dev/null +++ b/platforms/standalone/src/main/java/me/tofaa/entitylib/standalone/task/TaskSchedule.java @@ -0,0 +1,79 @@ +package me.tofaa.entitylib.standalone.task; + +import java.time.Duration; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +public interface TaskSchedule { + + static TaskSchedule duration(Duration duration) { + return new DurationSchedule(duration); + } + + static TaskSchedule duration(long duration) { + return new DurationSchedule(Duration.ofMillis(duration)); + } + + static TaskSchedule duration(long duration, TimeUnit unit) { + return new DurationSchedule(Duration.ofMillis(unit.toMillis(duration))); + } + + static TaskSchedule seconds(long seconds) { + return new DurationSchedule(Duration.ofSeconds(seconds)); + } + + static TaskSchedule minutes(long minutes) { + return new DurationSchedule(Duration.ofMinutes(minutes)); + } + + static TaskSchedule hours(long hours) { + return new DurationSchedule(Duration.ofHours(hours)); + } + + static TaskSchedule milliseconds(long milliseconds) { + return new DurationSchedule(Duration.ofMillis(milliseconds)); + } + + static TaskSchedule future(CompletableFuture<?> future) { + return new FutureSchedule(future); + } + + static TaskSchedule park() { + return ParkSchedule.INSTANCE; + } + + static TaskSchedule stop() { + return StopSchedule.INSTANCE; + } + + static TaskSchedule immediate() { + return ImmediateSchedule.INSTANCE; + } + + class DurationSchedule implements TaskSchedule { + Duration duration; + DurationSchedule(Duration duration) { + this.duration = duration; + } + } + + class FutureSchedule implements TaskSchedule { + CompletableFuture<?> future; + FutureSchedule(CompletableFuture<?> future) { + this.future = future; + } + } + class ParkSchedule implements TaskSchedule { + private static final ParkSchedule INSTANCE = new ParkSchedule(); + ParkSchedule() {} + } + class StopSchedule implements TaskSchedule { + private static final StopSchedule INSTANCE = new StopSchedule(); + StopSchedule() {} + } + class ImmediateSchedule implements TaskSchedule { + private static final ImmediateSchedule INSTANCE = new ImmediateSchedule(); + ImmediateSchedule() {} + } + +} diff --git a/platforms/velocity/build.gradle b/platforms/velocity/build.gradle index 30e72d2..689b917 100644 --- a/platforms/velocity/build.gradle +++ b/platforms/velocity/build.gradle @@ -15,6 +15,6 @@ java.toolchain.languageVersion.set(JavaLanguageVersion.of(17)) dependencies { api(project(":common")) - compileOnly('com.github.retrooper.packetevents:velocity:2.3.0') + compileOnly('com.github.retrooper.packetevents:velocity:' + peVersion) compileOnly 'com.velocitypowered:velocity-api:3.3.0-SNAPSHOT' } diff --git a/platforms/velocity/src/main/java/me/tofaa/entitylib/velocity/VelocityEntityLibAPI.java b/platforms/velocity/src/main/java/me/tofaa/entitylib/velocity/VelocityEntityLibAPI.java index 866cab3..4e38e5f 100644 --- a/platforms/velocity/src/main/java/me/tofaa/entitylib/velocity/VelocityEntityLibAPI.java +++ b/platforms/velocity/src/main/java/me/tofaa/entitylib/velocity/VelocityEntityLibAPI.java @@ -1,10 +1,8 @@ package me.tofaa.entitylib.velocity; -import com.github.retrooper.packetevents.protocol.world.Location; import com.velocitypowered.api.proxy.ProxyServer; import com.velocitypowered.api.scheduler.ScheduledTask; import me.tofaa.entitylib.APIConfig; -import me.tofaa.entitylib.Platform; import me.tofaa.entitylib.common.AbstractEntityLibAPI; import me.tofaa.entitylib.tick.TickContainer; import me.tofaa.entitylib.wrapper.WrapperEntity; @@ -50,7 +48,7 @@ public class VelocityEntityLibAPI extends AbstractEntityLibAPI<ProxyServer, Sche @NotNull @Override - public WrapperEntity cloneEntity(@NotNull Object platformEntity, @NotNull Location location) { + public <P extends WrapperEntity> P cloneEntity(@NotNull Object platformEntity) { throw new UnsupportedOperationException("No support for cloning entities on Velocity"); } } diff --git a/settings.gradle b/settings.gradle index 93c431b..020ecc3 100644 --- a/settings.gradle +++ b/settings.gradle @@ -16,3 +16,6 @@ if (!Boolean.parseBoolean(System.getenv("JITPACK"))) { include 'platforms:velocity' findProject(':platforms:velocity')?.name = 'velocity' +include 'platforms:standalone' +findProject(':platforms:standalone')?.name = 'standalone' + diff --git a/test-plugin/src/main/java/me/tofaa/testentitylib/TestEntityLibPlugin.java b/test-plugin/src/main/java/me/tofaa/testentitylib/TestEntityLibPlugin.java index 415de2d..291fbf9 100644 --- a/test-plugin/src/main/java/me/tofaa/testentitylib/TestEntityLibPlugin.java +++ b/test-plugin/src/main/java/me/tofaa/testentitylib/TestEntityLibPlugin.java @@ -43,6 +43,7 @@ public class TestEntityLibPlugin extends JavaPlugin { commandMap.register("testapi", new TestTextDisplayCommand()); commandMap.register("testplayer", new TestPlayerCommand()); commandMap.register("testholo", new TestHologramsCommand()); + commandMap.register("testmassivepig", new TestMassivePigCommand()); } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { e.printStackTrace(); diff --git a/test-plugin/src/main/java/me/tofaa/testentitylib/TestMassivePigCommand.java b/test-plugin/src/main/java/me/tofaa/testentitylib/TestMassivePigCommand.java index 1797574..d7f6d03 100644 --- a/test-plugin/src/main/java/me/tofaa/testentitylib/TestMassivePigCommand.java +++ b/test-plugin/src/main/java/me/tofaa/testentitylib/TestMassivePigCommand.java @@ -3,6 +3,7 @@ package me.tofaa.testentitylib; import com.github.retrooper.packetevents.protocol.attribute.Attributes; import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes; import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerUpdateAttributes; +import io.github.retrooper.packetevents.util.SpigotConversionUtil; import me.tofaa.entitylib.EntityLib; import me.tofaa.entitylib.wrapper.WrapperLivingEntity; import org.bukkit.command.CommandSender; @@ -30,7 +31,7 @@ public class TestMassivePigCommand extends BukkitCommand { player.sendMessage("Large pig removed"); return true; } - pig = EntityLib.getApi().createEntity(EntityTypes.PIG); + pig = new WrapperLivingEntity(EntityTypes.PIG); pig.getAttributes().setAttribute( Attributes.GENERIC_SCALE, 10, @@ -40,6 +41,8 @@ public class TestMassivePigCommand extends BukkitCommand { WrapperPlayServerUpdateAttributes.PropertyModifier.Operation.MULTIPLY_BASE ) ); + pig.addViewer(player.getUniqueId()); + pig.spawn(SpigotConversionUtil.fromBukkitLocation(player.getLocation())); player.sendMessage("Large pig spawned"); return true; } diff --git a/test-plugin/src/main/java/me/tofaa/testentitylib/TestTextDisplayCommand.java b/test-plugin/src/main/java/me/tofaa/testentitylib/TestTextDisplayCommand.java index 641bec3..fa85813 100644 --- a/test-plugin/src/main/java/me/tofaa/testentitylib/TestTextDisplayCommand.java +++ b/test-plugin/src/main/java/me/tofaa/testentitylib/TestTextDisplayCommand.java @@ -22,19 +22,19 @@ public class TestTextDisplayCommand extends BukkitCommand { @Override public boolean execute(@NotNull CommandSender commandSender, @NotNull String s, @NotNull String[] strings) { - if (!(commandSender instanceof Player)) return true; - Player player = (Player) commandSender; - if (e == null) { - e = EntityLib.getApi().createEntity(EntityTypes.PIG); - e.spawn(SpigotConversionUtil.fromBukkitLocation(player.getLocation())); - e.addViewer(player.getUniqueId()); - player.sendMessage("Spawned"); - } - String msg = String.join(" ", strings); - PigMeta meta = (PigMeta) e.getEntityMeta(); - meta.setCustomNameVisible(true); - meta.setCustomName(Component.text(msg)); - player.sendMessage("Set text to: " + msg); +// if (!(commandSender instanceof Player)) return true; +// Player player = (Player) commandSender; +// if (e == null) { +// e = EntityLib.getApi().createEntity(EntityTypes.PIG); +// e.spawn(SpigotConversionUtil.fromBukkitLocation(player.getLocation())); +// e.addViewer(player.getUniqueId()); +// player.sendMessage("Spawned"); +// } +// String msg = String.join(" ", strings); +// PigMeta meta = (PigMeta) e.getEntityMeta(); +// meta.setCustomNameVisible(true); +// meta.setCustomName(Component.text(msg)); +// player.sendMessage("Set text to: " + msg); return true; } }