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 @@ + - - - - - - - - - + + + + + + + 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;