diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index aa331ae..d31bff8 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -5,16 +5,15 @@
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
@@ -256,7 +255,8 @@
-
+
+
diff --git a/api/src/main/java/me/tofaa/entitylib/WorldWrapper.java b/api/src/main/java/me/tofaa/entitylib/WorldWrapper.java
index 76d809a..d421c35 100644
--- a/api/src/main/java/me/tofaa/entitylib/WorldWrapper.java
+++ b/api/src/main/java/me/tofaa/entitylib/WorldWrapper.java
@@ -4,6 +4,7 @@ import com.github.retrooper.packetevents.protocol.entity.type.EntityType;
import com.github.retrooper.packetevents.protocol.world.Dimension;
import com.github.retrooper.packetevents.protocol.world.Location;
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
+import me.tofaa.entitylib.tick.TickContainer;
import me.tofaa.entitylib.wrapper.WrapperEntity;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
diff --git a/api/src/main/java/me/tofaa/entitylib/meta/CompatibilityIndex.java b/api/src/main/java/me/tofaa/entitylib/meta/CompatibilityIndex.java
new file mode 100644
index 0000000..2683859
--- /dev/null
+++ b/api/src/main/java/me/tofaa/entitylib/meta/CompatibilityIndex.java
@@ -0,0 +1,9 @@
+package me.tofaa.entitylib.meta;
+
+import com.github.retrooper.packetevents.protocol.entity.data.EntityDataType;
+
+public interface CompatibilityIndex {
+
+ byte getOffSet(byte latestOffset, EntityDataType> type);
+
+}
diff --git a/api/src/main/java/me/tofaa/entitylib/meta/UsedVersion.java b/api/src/main/java/me/tofaa/entitylib/meta/UsedVersion.java
deleted file mode 100644
index d0bbd32..0000000
--- a/api/src/main/java/me/tofaa/entitylib/meta/UsedVersion.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package me.tofaa.entitylib.meta;
-
-import com.github.retrooper.packetevents.manager.server.ServerVersion;
-
-public @interface UsedVersion {
-
- ServerVersion[] value();
-
-}
diff --git a/api/src/main/java/me/tofaa/entitylib/meta/VersionCompatCheck.java b/api/src/main/java/me/tofaa/entitylib/meta/VersionCompatCheck.java
deleted file mode 100644
index 9bc5281..0000000
--- a/api/src/main/java/me/tofaa/entitylib/meta/VersionCompatCheck.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package me.tofaa.entitylib.meta;
-
-import com.github.retrooper.packetevents.manager.server.ServerVersion;
-import com.github.retrooper.packetevents.manager.server.VersionComparison;
-import me.tofaa.entitylib.EntityLib;
-
-public final class VersionCompatCheck {
-
- private VersionCompatCheck() {}
-
- static boolean isVersion(ServerVersion version) {
- return version.is(VersionComparison.EQUALS, EntityLib.getApi().getPacketEvents().getServerManager().getVersion());
- }
-
-}
diff --git a/api/src/main/java/me/tofaa/entitylib/tick/Tickable.java b/api/src/main/java/me/tofaa/entitylib/tick/Tickable.java
index 11dd41b..4b38f89 100644
--- a/api/src/main/java/me/tofaa/entitylib/tick/Tickable.java
+++ b/api/src/main/java/me/tofaa/entitylib/tick/Tickable.java
@@ -2,6 +2,21 @@ package me.tofaa.entitylib.tick;
public interface Tickable {
+ /**
+ * @return if the entity is ticking.
+ */
+ boolean isTicking();
+
+ /**
+ * Sets the entities ticking status, incase you want to stop ticking for a moment then continue
+ * @param ticking if the entity should tick.
+ */
+ void setTicking(boolean ticking);
+
+ /**
+ * Ticks this entity. This method will not be called if {@link #isTicking()} returns false.
+ * @param time the current time in milliseconds.
+ */
void tick(long time);
}
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 32e33d6..8bf058e 100644
--- a/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntity.java
+++ b/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntity.java
@@ -2,52 +2,106 @@ package me.tofaa.entitylib.wrapper;
import com.github.retrooper.packetevents.protocol.entity.type.EntityType;
import com.github.retrooper.packetevents.protocol.player.User;
+import com.github.retrooper.packetevents.protocol.world.BoundingBox;
import com.github.retrooper.packetevents.protocol.world.Location;
import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
-import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerDestroyEntities;
-import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityRotation;
-import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityVelocity;
-import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerSpawnEntity;
+import com.github.retrooper.packetevents.wrapper.play.server.*;
import me.tofaa.entitylib.EntityLib;
+import me.tofaa.entitylib.WorldWrapper;
import me.tofaa.entitylib.meta.EntityMeta;
+import me.tofaa.entitylib.meta.types.ObjectData;
import me.tofaa.entitylib.tick.Tickable;
+import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import javax.swing.text.html.parser.Entity;
-import java.util.Collections;
-import java.util.Optional;
-import java.util.Set;
-import java.util.UUID;
+import java.util.*;
public class WrapperEntity implements Tickable {
private final UUID uuid;
private final int entityId;
-
private EntityType entityType;
private EntityMeta entityMeta;
+ private boolean ticking;
private Location location;
+ private Location preRidingLocation;
private Set viewers;
private boolean onGround;
private boolean spawned;
private Vector3d velocity;
-
+ private int riding = -1;
+ private Set passengers = new HashSet<>();
+ private WorldWrapper> world;
public WrapperEntity(int entityId, UUID uuid, EntityType entityType, EntityMeta entityMeta) {
this.entityId = entityId;
this.uuid = uuid;
this.entityType = entityType;
this.entityMeta = entityMeta;
+ this.ticking = true;
}
- public void spawn() {}
-
- public void despawn() {}
-
- public void teleport(@NotNull Location location) {
+ public boolean spawn(WorldWrapper> world, Location location) {
+ if (spawned) return false;
this.location = location;
+ this.world = world;
+ this.spawned = true;
+ int data = 0;
+ Optional velocity;
+ double veloX = 0, veloY = 0, veloZ = 0;
+ if (entityMeta instanceof ObjectData) {
+ ObjectData od = (ObjectData) entityMeta;
+ data = od.getObjectData();
+ if (od.requiresVelocityPacketAtSpawn()) {
+ final WrapperPlayServerEntityVelocity veloPacket = getVelocityPacket();
+ veloX = veloPacket.getVelocity().getX();
+ veloY = veloPacket.getVelocity().getY();
+ veloZ = veloPacket.getVelocity().getZ();
+ }
+ }
+ if (veloX == 0 && veloY == 0 && veloZ == 0) {
+ velocity = Optional.empty();
+ } else {
+ velocity = Optional.of(new Vector3d(veloX, veloY, veloZ));
+ }
+ sendPacketToViewers(
+ new WrapperPlayServerSpawnEntity(
+ entityId,
+ Optional.of(this.uuid),
+ entityType,
+ location.getPosition(),
+ location.getPitch(),
+ location.getYaw(),
+ location.getYaw(),
+ data,
+ velocity
+ )
+ );
+ sendPacketToViewers(entityMeta.createPacket());
+ return true;
+ }
+ public void despawn() {
+ if (!spawned) return;
+ spawned = false;
+ sendPacketToViewers(new WrapperPlayServerDestroyEntities(entityId));
+ }
+
+ public void teleport(WorldWrapper> world, @NotNull Location location) {
+ this.location = location;
+ this.world = world;
+ sendPacketToViewers(
+ new WrapperPlayServerEntityTeleport(
+ entityId,
+ location.getPosition(),
+ location.getYaw(),
+ location.getPitch(),
+ onGround
+ )
+ );
}
public boolean addViewer(UUID uuid) {
@@ -131,12 +185,44 @@ public class WrapperEntity implements Tickable {
return entityType;
}
+ /**
+ * Returns an unmodifiable set of the passengers of the entity.
+ * @return the passengers of the entity
+ */
+ public Set getPassengers() {
+ return Collections.unmodifiableSet(passengers);
+ }
+
+ public WrapperEntity getRiding() {
+ return world.getEntity(riding);
+ }
+
+
+
+ protected WrapperPlayServerSetPassengers createPassengerPacket() {
+ return new WrapperPlayServerSetPassengers(entityId, passengers.stream().mapToInt(i -> i).toArray());
+ }
+
private WrapperPlayServerEntityVelocity getVelocityPacket() {
Vector3d velocity = this.velocity.multiply(8000.0f / 20.0f);
return new WrapperPlayServerEntityVelocity(entityId, velocity);
}
+ public boolean isSpawned() {
+ return spawned;
+ }
+
+ @Override
+ public boolean isTicking() {
+ return ticking;
+ }
+
+ @Override
+ public void setTicking(boolean ticking) {
+ this.ticking = ticking;
+ }
+
public boolean hasVelocity() {
if (isOnGround()) {
// if the entity is on the ground and only "moves" downwards, it does not have a velocity.
@@ -189,6 +275,135 @@ public class WrapperEntity implements Tickable {
refresh();
}
+ /**
+ * Adds a passenger to the entity. The passenger will be visible to all viewers of the entity.
+ * @param passenger the entity id of the passenger
+ */
+ public void addPassenger(int passenger) {
+ if (passengers.contains(passenger)) {
+ throw new IllegalArgumentException("Passenger already exists");
+ }
+ passengers.add(passenger);
+ sendPacketToViewers(createPassengerPacket());
+ WrapperEntity e = world.getEntity(passenger);
+ if (e != null) {
+ e.riding = this.entityId;
+ e.preRidingLocation = e.location;
+ }
+ }
+
+ public @Nullable Location getPreRidingLocation() {
+ return preRidingLocation;
+ }
+
+ /**
+ * @return the entity id of the entity that the entity is riding, -1 if the entity is not riding
+ */
+ public int getRidingId() {
+ return riding;
+ }
+
+
+ /**
+ * Adds multiple passengers to the entity. The passengers will be visible to all viewers of the entity.
+ * @param passengers the entity ids of the passengers
+ */
+ public void addPassengers(int... passengers) {
+ for (int passenger : passengers) {
+ addPassenger(passenger);
+ }
+ }
+
+ /**
+ * Adds a passenger to the entity. The passenger will be visible to all viewers of the entity.
+ * @param passenger the wrapper entity passenger
+ */
+ public void addPassenger(WrapperEntity passenger) {
+ addPassenger(passenger.getEntityId());
+ }
+
+ /**
+ * Adds multiple passengers to the entity. The passengers will be visible to all viewers of the entity.
+ * @param passengers the wrapper entity passengers
+ */
+ public void addPassengers(WrapperEntity... passengers) {
+ for (WrapperEntity passenger : passengers) {
+ addPassenger(passenger);
+ }
+ }
+
+ /**
+ * Removes a passenger from the entity. The passenger will be removed from the view of all viewers of the entity.
+ * @param passenger the entity id of the passenger
+ */
+ public void removePassenger(int passenger) {
+ if (!passengers.contains(passenger)) {
+ throw new IllegalArgumentException("Passenger does not exist");
+ }
+ passengers.remove(passenger);
+ sendPacketToViewers(createPassengerPacket());
+ WrapperEntity e = world.getEntity(passenger);
+ if (e != null) {
+ e.riding = -1;
+ e.teleport(world, e.preRidingLocation);
+ }
+ }
+
+ public WorldWrapper> getWorld() {
+ return world;
+ }
+
+ /**
+ * @param passenger the entity id of the passenger
+ * @return true if the entity has the passenger, false otherwise
+ */
+ public boolean hasPassenger(int passenger) {
+ return passengers.contains(passenger);
+ }
+
+ /**
+ * @param passenger the passenger wrapper entity
+ * @return true if the entity has the passenger, false otherwise
+ */
+ public boolean hasPassenger(WrapperEntity passenger) {
+ return hasPassenger(passenger.getEntityId());
+ }
+
+ /**
+ * Removes multiple passengers from the entity. The passengers will be removed from the view of all viewers of the entity.
+ * @param passengers the entity ids of the passengers
+ */
+ public void removePassengers(int... passengers) {
+ for (int passenger : passengers) {
+ removePassenger(passenger);
+ }
+ }
+
+ /**
+ * Removes a passenger from the entity. The passenger will be removed from the view of all viewers of the entity.
+ * @param passenger the wrapper entity passenger
+ */
+ public void removePassenger(WrapperEntity passenger) {
+ removePassenger(passenger.getEntityId());
+ }
+
+ /**
+ * Removes multiple passengers from the entity. The passengers will be removed from the view of all viewers of the entity.
+ * @param passengers the wrapper entity passengers
+ */
+ public void removePassengers(WrapperEntity... passengers) {
+ for (WrapperEntity passenger : passengers) {
+ removePassenger(passenger);
+ }
+ }
+
+ /**
+ * @return true if the entity has passengers, false otherwise
+ */
+ public boolean isRiding() {
+ return riding != -1;
+ }
+
public @NotNull Set getViewers() {
return Collections.unmodifiableSet(viewers);
}
@@ -199,6 +414,18 @@ public class WrapperEntity implements Tickable {
@Override
public void tick(long time) {
-
+ if (isRiding()) {
+ WrapperEntity riding = getRiding();
+ if (riding != null) {
+ Location l = riding.getLocation();
+ location = new Location(
+ l.getX(),
+ l.getY() + 1,
+ l.getZ(),
+ l.getYaw(),
+ l.getPitch()
+ );
+ }
+ }
}
}
diff --git a/api/src/main/java/me/tofaa/entitylib/wrapper/ai/goals/RandomHeadMovementGoal.java b/api/src/main/java/me/tofaa/entitylib/wrapper/ai/goals/RandomHeadMovementGoal.java
index 973151c..af8868b 100644
--- a/api/src/main/java/me/tofaa/entitylib/wrapper/ai/goals/RandomHeadMovementGoal.java
+++ b/api/src/main/java/me/tofaa/entitylib/wrapper/ai/goals/RandomHeadMovementGoal.java
@@ -66,7 +66,7 @@ public class RandomHeadMovementGoal extends GoalSelector {
@Override
public void tick(long time) {
--lookTime;
- entity.teleport(CoordinateUtil.withDirection(entity.getLocation(), lookDirection));
+ entity.teleport(entity.getWorld(), CoordinateUtil.withDirection(entity.getLocation(), lookDirection));
}
@Override
diff --git a/common/src/main/java/me/tofaa/entitylib/common/AbstractWorldWrapper.java b/common/src/main/java/me/tofaa/entitylib/common/AbstractWorldWrapper.java
index 2fbaa67..b8515e3 100644
--- a/common/src/main/java/me/tofaa/entitylib/common/AbstractWorldWrapper.java
+++ b/common/src/main/java/me/tofaa/entitylib/common/AbstractWorldWrapper.java
@@ -37,8 +37,7 @@ public abstract class AbstractWorldWrapper implements WorldWrapper {
@Override
public @NotNull T spawnEntity(@NotNull T entity, @NotNull Location location) {
- entity.teleport(location);
- entity.spawn();
+ entity.spawn(this, location);
entities.put(entity.getUuid(), entity);
entitiesById.put(entity.getEntityId(), entity);
return entity;