Minestom/src/main/java/net/minestom/server/entity/Entity.java

741 lines
24 KiB
Java
Raw Normal View History

2020-04-24 03:25:58 +02:00
package net.minestom.server.entity;
2019-08-10 08:44:35 +02:00
2020-04-24 03:25:58 +02:00
import net.minestom.server.MinecraftServer;
import net.minestom.server.Viewable;
import net.minestom.server.collision.BoundingBox;
import net.minestom.server.collision.CollisionUtils;
import net.minestom.server.data.Data;
import net.minestom.server.data.DataContainer;
import net.minestom.server.event.CancellableEvent;
import net.minestom.server.event.Event;
2020-04-28 01:20:11 +02:00
import net.minestom.server.event.EventCallback;
2020-04-24 03:25:58 +02:00
import net.minestom.server.instance.Chunk;
import net.minestom.server.instance.Instance;
import net.minestom.server.network.packet.PacketWriter;
import net.minestom.server.network.packet.server.play.*;
import net.minestom.server.network.player.PlayerConnection;
2020-04-28 01:20:11 +02:00
import net.minestom.server.utils.Vector;
2020-04-24 03:25:58 +02:00
import net.minestom.server.utils.*;
2019-08-10 08:44:35 +02:00
2020-04-28 01:20:11 +02:00
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
2020-04-28 01:20:11 +02:00
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
2019-08-12 08:30:59 +02:00
import java.util.concurrent.atomic.AtomicInteger;
2019-09-02 06:02:12 +02:00
import java.util.function.Consumer;
2019-08-10 08:44:35 +02:00
public abstract class Entity implements Viewable, DataContainer {
2019-08-10 08:44:35 +02:00
2019-09-03 07:36:04 +02:00
private static Map<Integer, Entity> entityById = new ConcurrentHashMap<>();
2019-08-12 08:30:59 +02:00
private static AtomicInteger lastEntityId = new AtomicInteger();
2019-08-19 17:04:19 +02:00
// Metadata
protected static final byte METADATA_BYTE = 0;
protected static final byte METADATA_VARINT = 1;
protected static final byte METADATA_FLOAT = 2;
protected static final byte METADATA_STRING = 3;
protected static final byte METADATA_CHAT = 4;
protected static final byte METADATA_OPTCHAT = 5;
protected static final byte METADATA_SLOT = 6;
protected static final byte METADATA_BOOLEAN = 7;
2019-09-10 06:59:15 +02:00
protected static final byte METADATA_POSE = 18;
2019-08-19 17:04:19 +02:00
2019-08-11 07:42:56 +02:00
protected Instance instance;
2019-08-21 16:50:52 +02:00
protected Position position;
2019-08-25 20:03:43 +02:00
protected float lastX, lastY, lastZ;
2019-08-20 22:40:57 +02:00
protected float lastYaw, lastPitch;
2019-08-10 08:44:35 +02:00
private int id;
2019-08-30 01:17:46 +02:00
private BoundingBox boundingBox;
protected Entity vehicle;
2019-08-25 20:03:43 +02:00
// Velocity
protected Vector velocity = new Vector(); // Movement in block per second
2019-08-27 20:49:11 +02:00
protected float gravityDragPerTick;
private int gravityTickCounter;
private Set<Player> viewers = new CopyOnWriteArraySet<>();
private Data data;
private Set<Entity> passengers = new CopyOnWriteArraySet<>();
protected UUID uuid;
2019-08-10 08:44:35 +02:00
private boolean isActive; // False if entity has only been instanced without being added somewhere
private boolean shouldRemove;
2019-08-21 16:50:52 +02:00
private long scheduledRemoveTime;
private int entityType;
private long lastUpdate;
2020-04-28 01:20:11 +02:00
private Map<Class<? extends Event>, List<EventCallback>> eventCallbacks = new ConcurrentHashMap<>();
protected long lastVelocityUpdateTime; // Reset velocity to 0 after countdown
2019-08-24 20:34:01 +02:00
// Synchronization
2019-08-27 05:23:25 +02:00
private long synchronizationDelay = 1500; // In ms
2019-08-24 20:34:01 +02:00
private long lastSynchronizationTime;
2019-08-21 16:50:52 +02:00
// Metadata
protected boolean onFire;
protected boolean crouched;
2019-08-19 17:04:19 +02:00
protected boolean UNUSED_METADATA;
protected boolean sprinting;
protected boolean swimming;
protected boolean invisible;
protected boolean glowing;
protected boolean usingElytra;
protected int air = 300;
protected String customName = "";
protected boolean customNameVisible;
protected boolean silent;
protected boolean noGravity;
protected Pose pose = Pose.STANDING;
private long velocityUpdatePeriod;
protected boolean onGround;
2019-08-19 17:04:19 +02:00
2020-04-09 14:25:42 +02:00
public Entity(int entityType, Position spawnPosition) {
2019-08-10 08:44:35 +02:00
this.id = generateId();
2019-08-19 17:04:19 +02:00
this.entityType = entityType;
2019-08-10 08:44:35 +02:00
this.uuid = UUID.randomUUID();
2020-04-09 14:25:42 +02:00
this.position = spawnPosition.clone();
2019-08-21 16:50:52 +02:00
2019-08-30 01:17:46 +02:00
setBoundingBox(0, 0, 0);
2019-09-03 07:36:04 +02:00
entityById.put(id, this);
setVelocityUpdatePeriod(5);
2019-08-10 08:44:35 +02:00
}
2020-03-29 20:58:30 +02:00
public Entity(int entityType) {
this(entityType, new Position());
}
2019-08-21 16:50:52 +02:00
public static Entity getEntity(int id) {
2019-09-03 07:36:04 +02:00
return entityById.get(id);
2019-08-21 16:50:52 +02:00
}
2019-08-19 17:04:19 +02:00
2019-08-10 08:44:35 +02:00
private static int generateId() {
2019-08-12 08:30:59 +02:00
return lastEntityId.incrementAndGet();
2019-08-10 08:44:35 +02:00
}
2019-08-19 17:04:19 +02:00
public abstract void update();
2019-08-30 01:17:46 +02:00
// Called when a new instance is set
2019-08-24 20:34:01 +02:00
public abstract void spawn();
2020-03-29 20:58:30 +02:00
public boolean isOnGround() {
return onGround || EntityUtils.isOnGround(this) /* backup for levitating entities */;
}
/**
* Checks if now is a good time to send a velocity update packet
* @return
* @param time
*/
protected boolean shouldSendVelocityUpdate(long time) {
return (time-lastVelocityUpdateTime) >= velocityUpdatePeriod;
}
/**
* Gets the period, in ms, between two velocity update packets
* @return period, in ms, between two velocity update packets
*/
public long getVelocityUpdatePeriod() {
return velocityUpdatePeriod;
}
/**
* Sets the period, in ms, between two velocity update packets
* @param velocityUpdatePeriod period, in ms, between two velocity update packets
*/
public void setVelocityUpdatePeriod(long velocityUpdatePeriod) {
this.velocityUpdatePeriod = velocityUpdatePeriod;
2020-03-29 20:58:30 +02:00
}
2019-08-27 20:49:11 +02:00
2019-08-25 20:03:43 +02:00
public void teleport(Position position, Runnable callback) {
if (instance == null)
2019-08-27 05:23:25 +02:00
throw new IllegalStateException("You need to use Entity#setInstance before teleporting an entity!");
2019-08-21 16:50:52 +02:00
Runnable runnable = () -> {
refreshPosition(position.getX(), position.getY(), position.getZ());
refreshView(position.getYaw(), position.getPitch());
EntityTeleportPacket entityTeleportPacket = new EntityTeleportPacket();
entityTeleportPacket.entityId = getEntityId();
entityTeleportPacket.position = position;
2019-08-27 20:49:11 +02:00
entityTeleportPacket.onGround = isOnGround();
sendPacketToViewers(entityTeleportPacket);
};
2019-08-25 20:03:43 +02:00
if (instance.hasEnabledAutoChunkLoad()) {
instance.loadChunk(position, chunk -> {
runnable.run();
2019-08-25 20:03:43 +02:00
if (callback != null)
callback.run();
});
} else {
2019-08-27 20:49:11 +02:00
if (ChunkUtils.isChunkUnloaded(instance, position.getX(), position.getZ()))
2019-08-25 20:03:43 +02:00
return;
runnable.run();
2019-08-25 20:03:43 +02:00
if (callback != null)
callback.run();
}
}
public void teleport(Position position) {
teleport(position, null);
2019-08-21 16:50:52 +02:00
}
@Override
public void addViewer(Player player) {
this.viewers.add(player);
2019-09-01 06:18:41 +02:00
player.viewableEntities.add(this);
2019-08-30 01:17:46 +02:00
PlayerConnection playerConnection = player.getPlayerConnection();
playerConnection.sendPacket(getVelocityPacket());
2019-08-31 07:54:53 +02:00
playerConnection.sendPacket(getPassengersPacket());
}
@Override
public void removeViewer(Player player) {
synchronized (viewers) {
if (!viewers.contains(player))
return;
this.viewers.remove(player);
2019-08-20 22:40:57 +02:00
DestroyEntitiesPacket destroyEntitiesPacket = new DestroyEntitiesPacket();
destroyEntitiesPacket.entityIds = new int[]{getEntityId()};
player.getPlayerConnection().sendPacket(destroyEntitiesPacket);
}
2019-09-01 06:18:41 +02:00
player.viewableEntities.remove(this);
}
@Override
public Set<Player> getViewers() {
return Collections.unmodifiableSet(viewers);
}
@Override
public Data getData() {
return data;
}
@Override
public void setData(Data data) {
this.data = data;
}
2020-02-09 15:34:09 +01:00
public void tick(long time) {
if (instance == null)
return;
2019-08-21 16:50:52 +02:00
if (scheduledRemoveTime != 0) { // Any entity with scheduled remove does not update
2020-02-09 15:34:09 +01:00
boolean finished = time >= scheduledRemoveTime;
2019-08-21 16:50:52 +02:00
if (finished) {
remove();
}
return;
}
if (shouldRemove()) {
remove();
return;
2020-02-09 15:34:09 +01:00
} else if (shouldUpdate(time)) {
2019-08-29 02:15:52 +02:00
this.lastUpdate = time;
2019-08-24 20:34:01 +02:00
// Velocity
if (!(this instanceof Player)) {
final float tps = MinecraftServer.TICK_PER_SECOND;
float newX = position.getX() + velocity.getX() / tps;
float newY = position.getY() + velocity.getY() / tps;
float newZ = position.getZ() + velocity.getZ() / tps;
Position newPosition = new Position(newX, newY, newZ);
if (!(this instanceof Player) && !noGravity) { // players handle gravity by themselves
velocity.setY(velocity.getY() - gravityDragPerTick*tps);
2019-09-14 18:00:18 +02:00
}
Vector newVelocityOut = new Vector();
Vector deltaPos = new Vector(
getVelocity().getX()/tps,
getVelocity().getY()/tps,
getVelocity().getZ()/tps
);
onGround = CollisionUtils.handlePhysics(this, deltaPos, newPosition, newVelocityOut);
refreshPosition(newPosition);
velocity.copy(newVelocityOut);
velocity.multiply(tps);
float drag;
if(onGround) {
drag = 0.5f; // ground drag
} else {
drag = 0.98f; // air drag
2019-08-24 20:34:01 +02:00
}
velocity.setX(velocity.getX() * drag);
velocity.setZ(velocity.getZ() * drag);
2019-08-24 20:34:01 +02:00
sendSynchronization();
if (shouldSendVelocityUpdate(time)) {
sendPacketToViewers(getVelocityPacket());
lastVelocityUpdateTime = time;
2019-08-27 20:49:11 +02:00
}
}
handleVoid();
2020-03-29 20:58:30 +02:00
// Call the abstract update method
2019-08-19 17:04:19 +02:00
update();
2019-08-24 20:34:01 +02:00
// Scheduled synchronization
2019-08-24 20:34:01 +02:00
if (time - lastSynchronizationTime >= synchronizationDelay) {
2020-02-09 15:34:09 +01:00
lastSynchronizationTime = time;
2019-08-30 01:17:46 +02:00
sendSynchronization();
2019-08-24 20:34:01 +02:00
}
2019-08-19 17:04:19 +02:00
}
if (shouldRemove()) {
remove();
}
2019-08-19 17:04:19 +02:00
}
/**
* How does this entity handle being in the void?
*/
protected void handleVoid() {
// Kill if in void
2020-04-28 01:20:11 +02:00
if (getInstance().isInVoid(this.position)) {
remove();
}
}
2020-04-28 01:20:11 +02:00
public <E extends Event> void addEventCallback(Class<E> eventClass, EventCallback<E> eventCallback) {
List<EventCallback> callbacks = getEventCallbacks(eventClass);
callbacks.add(eventCallback);
this.eventCallbacks.put(eventClass, callbacks);
}
2020-04-28 01:20:11 +02:00
public <E extends Event> List<EventCallback> getEventCallbacks(Class<E> eventClass) {
return eventCallbacks.getOrDefault(eventClass, new CopyOnWriteArrayList<>());
}
public <E extends Event> void callEvent(Class<E> eventClass, E event) {
2020-04-28 01:20:11 +02:00
List<EventCallback> eventCallbacks = getEventCallbacks(eventClass);
for (EventCallback<E> eventCallback : eventCallbacks) {
eventCallback.run(event);
}
}
public <E extends CancellableEvent> void callCancellableEvent(Class<E> eventClass, E event, Runnable runnable) {
callEvent(eventClass, event);
if (!event.isCancelled()) {
runnable.run();
}
}
2019-08-10 08:44:35 +02:00
public int getEntityId() {
return id;
}
2019-08-19 17:04:19 +02:00
public int getEntityType() {
return entityType;
}
2019-08-10 08:44:35 +02:00
public UUID getUuid() {
return uuid;
}
public boolean isActive() {
return isActive;
}
2019-08-30 01:17:46 +02:00
public BoundingBox getBoundingBox() {
return boundingBox;
}
public void setBoundingBox(float x, float y, float z) {
this.boundingBox = new BoundingBox(this, x, y, z);
}
2019-08-11 07:42:56 +02:00
public Instance getInstance() {
return instance;
}
public void setInstance(Instance instance) {
if (instance == null)
throw new IllegalArgumentException("instance cannot be null!");
if (this.instance != null) {
this.instance.removeEntity(this);
2019-08-10 08:44:35 +02:00
}
2019-08-11 07:42:56 +02:00
this.isActive = true;
this.instance = instance;
instance.addEntity(this);
2019-08-24 20:34:01 +02:00
spawn();
}
2019-08-25 20:03:43 +02:00
public Vector getVelocity() {
return velocity;
}
public void setVelocity(Vector velocity) {
this.velocity.copy(velocity);
2019-08-10 08:44:35 +02:00
}
2019-08-27 20:49:11 +02:00
public void setGravity(float gravityDragPerTick) {
this.gravityDragPerTick = gravityDragPerTick;
}
2019-08-19 17:04:19 +02:00
public float getDistance(Entity entity) {
2019-08-21 16:50:52 +02:00
return getPosition().getDistance(entity.getPosition());
2019-08-19 17:04:19 +02:00
}
2019-08-27 05:23:25 +02:00
public Entity getVehicle() {
return vehicle;
}
public void addPassenger(Entity entity) {
2019-08-27 05:23:25 +02:00
if (instance == null)
throw new IllegalStateException("You need to set an instance using Entity#setInstance");
if (entity.getVehicle() != null) {
entity.getVehicle().removePassenger(entity);
}
this.passengers.add(entity);
entity.vehicle = this;
2019-08-30 01:17:46 +02:00
sendPacketToViewersAndSelf(getPassengersPacket());
2019-08-27 05:23:25 +02:00
}
2019-08-27 05:23:25 +02:00
public void removePassenger(Entity entity) {
if (instance == null)
throw new IllegalStateException("You need to set an instance using Entity#setInstance");
if (!passengers.contains(entity))
return;
this.passengers.remove(entity);
entity.vehicle = null;
2019-08-30 01:17:46 +02:00
sendPacketToViewersAndSelf(getPassengersPacket());
}
public boolean hasPassenger() {
return !passengers.isEmpty();
}
public Set<Entity> getPassengers() {
return Collections.unmodifiableSet(passengers);
}
2019-08-30 01:17:46 +02:00
protected SetPassengersPacket getPassengersPacket() {
2019-08-27 05:23:25 +02:00
SetPassengersPacket passengersPacket = new SetPassengersPacket();
passengersPacket.vehicleEntityId = getEntityId();
int[] passengers = new int[this.passengers.size()];
int counter = 0;
for (Entity passenger : this.passengers) {
passengers[counter++] = passenger.getEntityId();
}
passengersPacket.passengersId = passengers;
2019-08-30 01:17:46 +02:00
return passengersPacket;
2019-08-27 05:23:25 +02:00
}
2019-08-21 16:50:52 +02:00
public void triggerStatus(byte status) {
EntityStatusPacket statusPacket = new EntityStatusPacket();
statusPacket.entityId = getEntityId();
statusPacket.status = status;
2020-04-20 23:43:09 +02:00
sendPacketToViewersAndSelf(statusPacket);
2019-08-21 16:50:52 +02:00
}
2019-08-20 22:40:57 +02:00
public void setOnFire(boolean fire) {
this.onFire = fire;
2019-08-23 15:37:38 +02:00
sendMetadataIndex(0);
}
2019-08-27 20:49:11 +02:00
public boolean isOnFire() {
return onFire;
}
2019-08-24 20:34:01 +02:00
public void setGlowing(boolean glowing) {
this.glowing = glowing;
sendMetadataIndex(0);
}
2019-08-27 20:49:11 +02:00
public boolean isGlowing() {
return glowing;
}
2019-08-23 15:37:38 +02:00
public void setNoGravity(boolean noGravity) {
this.noGravity = noGravity;
sendMetadataIndex(5);
2019-08-20 22:40:57 +02:00
}
2019-08-27 20:49:11 +02:00
public boolean hasNoGravity() {
return noGravity;
2019-08-21 16:50:52 +02:00
}
public void refreshPosition(float x, float y, float z) {
this.lastX = position.getX();
this.lastY = position.getY();
this.lastZ = position.getZ();
position.setX(x);
position.setY(y);
position.setZ(z);
2019-08-11 07:42:56 +02:00
2019-08-30 01:17:46 +02:00
for (Entity passenger : getPassengers()) {
passenger.refreshPosition(x, y, z);
2019-08-30 01:17:46 +02:00
}
2019-08-11 07:42:56 +02:00
Instance instance = getInstance();
if (instance != null) {
Chunk lastChunk = instance.getChunkAt(lastX, lastZ);
Chunk newChunk = instance.getChunkAt(x, z);
2019-08-11 08:57:23 +02:00
if (lastChunk != null && newChunk != null && lastChunk != newChunk) {
2019-08-24 21:41:43 +02:00
synchronized (instance) {
instance.removeEntityFromChunk(this, lastChunk);
instance.addEntityToChunk(this, newChunk);
2019-08-30 01:17:46 +02:00
}
updateView(this, lastChunk, newChunk);
2019-08-11 07:42:56 +02:00
}
}
2019-08-10 08:44:35 +02:00
}
2019-08-27 20:49:11 +02:00
public void refreshPosition(Position position) {
refreshPosition(position.getX(), position.getY(), position.getZ());
}
2019-08-30 01:17:46 +02:00
private void updateView(Entity entity, Chunk lastChunk, Chunk newChunk) {
if (entity instanceof Player)
((Player) entity).onChunkChange(lastChunk, newChunk); // Refresh loaded chunk
// Refresh entity viewable list
2020-02-17 17:33:53 +01:00
long[] lastVisibleChunksEntity = ChunkUtils.getChunksInRange(new Position(16 * lastChunk.getChunkX(), 0, 16 * lastChunk.getChunkZ()), MinecraftServer.ENTITY_VIEW_DISTANCE);
long[] updatedVisibleChunksEntity = ChunkUtils.getChunksInRange(new Position(16 * newChunk.getChunkX(), 0, 16 * newChunk.getChunkZ()), MinecraftServer.ENTITY_VIEW_DISTANCE);
2019-08-30 01:17:46 +02:00
boolean isPlayer = entity instanceof Player;
int[] oldChunksEntity = ArrayUtils.getDifferencesBetweenArray(lastVisibleChunksEntity, updatedVisibleChunksEntity);
for (int index : oldChunksEntity) {
int[] chunkPos = ChunkUtils.getChunkCoord(lastVisibleChunksEntity[index]);
Chunk chunk = instance.getChunk(chunkPos[0], chunkPos[1]);
if (chunk == null)
continue;
instance.getChunkEntities(chunk).forEach(ent -> {
if (ent instanceof Player) {
Player player = (Player) ent;
removeViewer(player);
if (isPlayer) {
player.removeViewer((Player) entity);
}
} else if (isPlayer) {
ent.removeViewer((Player) entity);
}
});
}
int[] newChunksEntity = ArrayUtils.getDifferencesBetweenArray(updatedVisibleChunksEntity, lastVisibleChunksEntity);
for (int index : newChunksEntity) {
int[] chunkPos = ChunkUtils.getChunkCoord(updatedVisibleChunksEntity[index]);
Chunk chunk = instance.getChunk(chunkPos[0], chunkPos[1]);
if (chunk == null)
continue;
instance.getChunkEntities(chunk).forEach(ent -> {
if (ent instanceof Player) {
Player player = (Player) ent;
addViewer(player);
if (entity instanceof Player) {
player.addViewer((Player) entity);
}
} else if (isPlayer) {
ent.addViewer((Player) entity);
}
});
}
}
2019-08-20 22:40:57 +02:00
public void refreshView(float yaw, float pitch) {
2019-08-21 16:50:52 +02:00
this.lastYaw = position.getYaw();
this.lastPitch = position.getPitch();
position.setYaw(yaw);
position.setPitch(pitch);
2019-08-20 22:40:57 +02:00
}
public void refreshSneaking(boolean sneaking) {
this.crouched = sneaking;
2019-09-10 06:59:15 +02:00
this.pose = sneaking ? Pose.SNEAKING : Pose.STANDING;
2019-08-23 15:37:38 +02:00
sendMetadataIndex(0);
2019-09-10 06:59:15 +02:00
sendMetadataIndex(6);
2019-08-20 22:40:57 +02:00
}
public void refreshSprinting(boolean sprinting) {
this.sprinting = sprinting;
2019-08-23 15:37:38 +02:00
sendMetadataIndex(0);
2019-08-20 22:40:57 +02:00
}
2019-08-21 16:50:52 +02:00
public Position getPosition() {
return position;
2019-08-19 17:04:19 +02:00
}
2020-02-13 15:14:41 +01:00
public boolean sameChunk(Position position) {
Position pos = getPosition();
int chunkX1 = ChunkUtils.getChunkCoordinate((int) Math.floor(pos.getX()));
int chunkZ1 = ChunkUtils.getChunkCoordinate((int) Math.floor(pos.getZ()));
2020-02-13 15:14:41 +01:00
int chunkX2 = ChunkUtils.getChunkCoordinate((int) Math.floor(position.getX()));
int chunkZ2 = ChunkUtils.getChunkCoordinate((int) Math.floor(position.getZ()));
2020-02-13 15:14:41 +01:00
return chunkX1 == chunkX2 && chunkZ1 == chunkZ2;
}
public boolean sameChunk(Entity entity) {
return sameChunk(entity.getPosition());
}
2019-08-10 08:44:35 +02:00
public void remove() {
this.shouldRemove = true;
2019-09-03 07:36:04 +02:00
entityById.remove(id);
2019-08-24 01:05:52 +02:00
if (instance != null)
instance.removeEntity(this);
2019-08-10 08:44:35 +02:00
}
2019-08-21 16:50:52 +02:00
public void scheduleRemove(long delay) {
if (delay == 0) { // Cancel the scheduled remove
this.scheduledRemoveTime = 0;
return;
}
this.scheduledRemoveTime = System.currentTimeMillis() + delay;
2019-08-20 22:40:57 +02:00
}
2019-08-30 01:17:46 +02:00
public boolean isRemoveScheduled() {
return scheduledRemoveTime != 0;
}
2019-08-24 20:34:01 +02:00
protected EntityVelocityPacket getVelocityPacket() {
2020-02-17 17:33:53 +01:00
final float strength = 8000f / MinecraftServer.TICK_PER_SECOND;
2019-08-24 20:34:01 +02:00
EntityVelocityPacket velocityPacket = new EntityVelocityPacket();
velocityPacket.entityId = getEntityId();
2019-08-25 20:03:43 +02:00
velocityPacket.velocityX = (short) (velocity.getX() * strength);
velocityPacket.velocityY = (short) (velocity.getY() * strength);
velocityPacket.velocityZ = (short) (velocity.getZ() * strength);
2019-08-24 20:34:01 +02:00
return velocityPacket;
}
2019-08-22 14:52:32 +02:00
public EntityMetaDataPacket getMetadataPacket() {
EntityMetaDataPacket metaDataPacket = new EntityMetaDataPacket();
metaDataPacket.entityId = getEntityId();
2019-09-02 06:02:12 +02:00
metaDataPacket.consumer = getMetadataConsumer();
2019-08-22 14:52:32 +02:00
return metaDataPacket;
}
2020-04-16 16:40:29 +02:00
public Consumer<PacketWriter> getMetadataConsumer() {
2019-09-02 06:02:12 +02:00
return packet -> {
fillMetadataIndex(packet, 0);
fillMetadataIndex(packet, 1);
fillMetadataIndex(packet, 5);
2019-09-10 06:59:15 +02:00
fillMetadataIndex(packet, 6);
2019-09-02 06:02:12 +02:00
};
2019-08-19 17:04:19 +02:00
}
2019-08-23 15:37:38 +02:00
protected void sendMetadataIndex(int index) {
2019-08-20 22:40:57 +02:00
EntityMetaDataPacket metaDataPacket = new EntityMetaDataPacket();
metaDataPacket.entityId = getEntityId();
2019-09-02 06:02:12 +02:00
metaDataPacket.consumer = packet -> {
fillMetadataIndex(packet, index);
};
2019-08-27 05:23:25 +02:00
sendPacketToViewersAndSelf(metaDataPacket);
2019-08-20 22:40:57 +02:00
}
2020-04-16 16:40:29 +02:00
private void fillMetadataIndex(PacketWriter packet, int index) {
2019-08-19 17:04:19 +02:00
switch (index) {
case 0:
2019-09-02 06:02:12 +02:00
fillStateMetadata(packet);
2019-08-19 17:04:19 +02:00
break;
case 1:
2019-09-02 06:02:12 +02:00
fillAirTickMetaData(packet);
2019-08-19 17:04:19 +02:00
break;
case 2:
2019-09-02 06:02:12 +02:00
fillCustomNameMetaData(packet);
2019-08-19 17:04:19 +02:00
break;
2019-08-23 15:37:38 +02:00
case 5:
2019-09-02 06:02:12 +02:00
fillNoGravityMetaData(packet);
2019-08-23 15:37:38 +02:00
break;
2019-09-10 06:59:15 +02:00
case 6:
fillPoseMetaData(packet);
break;
2019-08-19 17:04:19 +02:00
}
}
2020-04-16 16:40:29 +02:00
private void fillStateMetadata(PacketWriter packet) {
packet.writeByte((byte) 0);
packet.writeByte(METADATA_BYTE);
2019-08-19 17:04:19 +02:00
byte index0 = 0;
if (onFire)
index0 += 1;
if (crouched)
index0 += 2;
if (UNUSED_METADATA)
index0 += 4;
if (sprinting)
index0 += 8;
if (swimming)
index0 += 16;
if (invisible)
index0 += 32;
if (glowing)
index0 += 64;
if (usingElytra)
index0 += 128;
2020-04-16 16:40:29 +02:00
packet.writeByte(index0);
2019-09-02 06:02:12 +02:00
}
2020-04-16 16:40:29 +02:00
private void fillAirTickMetaData(PacketWriter packet) {
packet.writeByte((byte) 1);
packet.writeByte(METADATA_VARINT);
packet.writeVarInt(air);
2019-09-02 06:02:12 +02:00
}
2020-04-16 16:40:29 +02:00
private void fillCustomNameMetaData(PacketWriter packet) {
packet.writeByte((byte) 2);
packet.writeByte(METADATA_CHAT);
packet.writeSizedString(customName);
2019-09-02 06:02:12 +02:00
}
2020-04-16 16:40:29 +02:00
private void fillNoGravityMetaData(PacketWriter packet) {
packet.writeByte((byte) 5);
packet.writeByte(METADATA_BOOLEAN);
packet.writeBoolean(noGravity);
2019-08-19 17:04:19 +02:00
}
2020-04-16 16:40:29 +02:00
private void fillPoseMetaData(PacketWriter packet) {
packet.writeByte((byte) 6);
packet.writeByte(METADATA_POSE);
packet.writeVarInt(pose.ordinal());
2019-09-10 06:59:15 +02:00
}
2019-08-30 01:17:46 +02:00
protected void sendSynchronization() {
2019-08-24 20:34:01 +02:00
EntityTeleportPacket entityTeleportPacket = new EntityTeleportPacket();
entityTeleportPacket.entityId = getEntityId();
entityTeleportPacket.position = getPosition();
2019-08-27 20:49:11 +02:00
entityTeleportPacket.onGround = isOnGround();
2019-08-24 20:34:01 +02:00
sendPacketToViewers(entityTeleportPacket);
if (!passengers.isEmpty())
sendPacketToViewers(getPassengersPacket());
2019-08-24 20:34:01 +02:00
}
2020-02-09 15:34:09 +01:00
private boolean shouldUpdate(long time) {
2020-02-17 17:33:53 +01:00
return (float) (time - lastUpdate) >= MinecraftServer.TICK_MS * 0.9f; // Margin of error
2019-08-19 17:04:19 +02:00
}
public enum Pose {
STANDING,
FALL_FLYING,
SLEEPING,
SWIMMING,
SPIN_ATTACK,
SNEAKING,
2020-04-09 14:25:42 +02:00
DYING
2019-08-19 17:04:19 +02:00
}
2019-08-10 08:44:35 +02:00
protected boolean shouldRemove() {
return shouldRemove;
}
}