2019-08-10 08:44:35 +02:00
|
|
|
package fr.themode.minestom.entity;
|
|
|
|
|
2019-08-19 17:04:19 +02:00
|
|
|
import fr.adamaq01.ozao.net.Buffer;
|
2019-08-20 17:41:07 +02:00
|
|
|
import fr.themode.minestom.Main;
|
|
|
|
import fr.themode.minestom.Viewable;
|
2019-08-23 23:55:09 +02:00
|
|
|
import fr.themode.minestom.data.Data;
|
|
|
|
import fr.themode.minestom.data.DataContainer;
|
2019-08-20 17:41:07 +02:00
|
|
|
import fr.themode.minestom.event.Callback;
|
|
|
|
import fr.themode.minestom.event.CancellableEvent;
|
|
|
|
import fr.themode.minestom.event.Event;
|
2019-08-11 07:42:56 +02:00
|
|
|
import fr.themode.minestom.instance.Chunk;
|
|
|
|
import fr.themode.minestom.instance.Instance;
|
2019-08-21 16:50:52 +02:00
|
|
|
import fr.themode.minestom.net.packet.server.play.*;
|
|
|
|
import fr.themode.minestom.utils.Position;
|
2019-08-19 17:04:19 +02:00
|
|
|
import fr.themode.minestom.utils.Utils;
|
2019-08-24 20:34:01 +02:00
|
|
|
import fr.themode.minestom.utils.Vector;
|
2019-08-10 08:44:35 +02:00
|
|
|
|
2019-08-21 16:50:52 +02:00
|
|
|
import java.util.*;
|
2019-08-20 17:41:07 +02:00
|
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
|
|
import java.util.concurrent.CopyOnWriteArraySet;
|
2019-08-12 08:30:59 +02:00
|
|
|
import java.util.concurrent.atomic.AtomicInteger;
|
2019-08-10 08:44:35 +02:00
|
|
|
|
2019-08-23 23:55:09 +02:00
|
|
|
public abstract class Entity implements Viewable, DataContainer {
|
2019-08-10 08:44:35 +02:00
|
|
|
|
2019-08-21 16:50:52 +02:00
|
|
|
private static Map<Integer, Entity> entityById = new HashMap<>();
|
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-08-11 07:42:56 +02:00
|
|
|
protected Instance instance;
|
2019-08-21 16:50:52 +02:00
|
|
|
protected Position position;
|
2019-08-24 20:34:01 +02:00
|
|
|
protected boolean onGround;
|
2019-08-11 07:42:56 +02:00
|
|
|
protected double 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-20 17:41:07 +02:00
|
|
|
|
|
|
|
protected Entity vehicle;
|
|
|
|
private Map<Class<Event>, Callback> eventCallbacks = new ConcurrentHashMap<>();
|
|
|
|
private Set<Player> viewers = new CopyOnWriteArraySet<>();
|
2019-08-23 23:55:09 +02:00
|
|
|
private Data data;
|
2019-08-20 17:41:07 +02:00
|
|
|
private Set<Entity> passengers = new CopyOnWriteArraySet<>();
|
|
|
|
|
2019-08-11 03:40:34 +02:00
|
|
|
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;
|
|
|
|
|
2019-08-24 20:34:01 +02:00
|
|
|
// Velocity
|
|
|
|
// TODO gravity implementation for entity other than players
|
|
|
|
protected float velocityX, velocityY, velocityZ; // Movement in block per second
|
|
|
|
protected long velocityTime; // Reset velocity to 0 after countdown
|
|
|
|
|
|
|
|
// Synchronization
|
|
|
|
private long synchronizationDelay = 2000; // In ms
|
|
|
|
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;
|
|
|
|
|
|
|
|
public Entity(int entityType) {
|
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();
|
2019-08-21 16:50:52 +02:00
|
|
|
this.position = new Position();
|
|
|
|
|
|
|
|
synchronized (entityById) {
|
|
|
|
entityById.put(id, this);
|
|
|
|
}
|
2019-08-10 08:44:35 +02:00
|
|
|
}
|
|
|
|
|
2019-08-21 16:50:52 +02:00
|
|
|
public static Entity getEntity(int id) {
|
|
|
|
synchronized (entityById) {
|
|
|
|
return entityById.get(id);
|
|
|
|
}
|
|
|
|
}
|
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-24 20:34:01 +02:00
|
|
|
// Called when entity a new instance is set
|
|
|
|
public abstract void spawn();
|
|
|
|
|
2019-08-21 16:50:52 +02:00
|
|
|
public void teleport(Position position) {
|
|
|
|
if (isChunkUnloaded(position.getX(), position.getZ()))
|
|
|
|
return;
|
|
|
|
|
2019-08-22 14:52:32 +02:00
|
|
|
refreshPosition(position.getX(), position.getY(), position.getZ());
|
|
|
|
refreshView(position.getYaw(), position.getPitch());
|
2019-08-21 16:50:52 +02:00
|
|
|
EntityTeleportPacket entityTeleportPacket = new EntityTeleportPacket();
|
|
|
|
entityTeleportPacket.entityId = getEntityId();
|
|
|
|
entityTeleportPacket.position = position;
|
2019-08-24 20:34:01 +02:00
|
|
|
entityTeleportPacket.onGround = onGround;
|
2019-08-21 16:50:52 +02:00
|
|
|
sendPacketToViewers(entityTeleportPacket);
|
|
|
|
}
|
|
|
|
|
2019-08-20 17:41:07 +02:00
|
|
|
@Override
|
|
|
|
public void addViewer(Player player) {
|
|
|
|
this.viewers.add(player);
|
2019-08-24 20:34:01 +02:00
|
|
|
player.getPlayerConnection().sendPacket(getVelocityPacket());
|
2019-08-20 17:41:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@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-08-20 17:41:07 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Set<Player> getViewers() {
|
|
|
|
return Collections.unmodifiableSet(viewers);
|
|
|
|
}
|
|
|
|
|
2019-08-23 23:55:09 +02:00
|
|
|
@Override
|
|
|
|
public Data getData() {
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void setData(Data data) {
|
|
|
|
this.data = data;
|
|
|
|
}
|
|
|
|
|
2019-08-19 17:04:19 +02:00
|
|
|
public void tick() {
|
2019-08-23 23:55:09 +02:00
|
|
|
if (instance == null)
|
|
|
|
return;
|
|
|
|
|
2019-08-21 16:50:52 +02:00
|
|
|
if (scheduledRemoveTime != 0) { // Any entity with scheduled remove does not update
|
|
|
|
boolean finished = System.currentTimeMillis() >= scheduledRemoveTime;
|
|
|
|
if (finished) {
|
|
|
|
remove();
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2019-08-23 23:55:09 +02:00
|
|
|
|
|
|
|
if (shouldRemove()) {
|
|
|
|
remove();
|
|
|
|
return;
|
|
|
|
} else if (shouldUpdate()) {
|
2019-08-24 20:34:01 +02:00
|
|
|
long time = System.currentTimeMillis();
|
|
|
|
|
|
|
|
// Velocity
|
|
|
|
if (velocityTime != 0) {
|
|
|
|
if (time >= velocityTime) {
|
|
|
|
// TODO send synchronization packet?
|
|
|
|
resetVelocity();
|
|
|
|
} else {
|
|
|
|
if (this instanceof Player) {
|
|
|
|
sendPacketToViewersAndSelf(getVelocityPacket());
|
|
|
|
} else {
|
|
|
|
float tps = Main.TICK_PER_SECOND;
|
|
|
|
refreshPosition(position.getX() + velocityX / tps, position.getY() + velocityY / tps, position.getZ() + velocityZ / tps);
|
|
|
|
if (this instanceof ObjectEntity) {
|
|
|
|
sendPacketToViewers(getVelocityPacket());
|
|
|
|
} else if (this instanceof EntityCreature) {
|
|
|
|
teleport(getPosition());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-19 17:04:19 +02:00
|
|
|
update();
|
2019-08-24 20:34:01 +02:00
|
|
|
|
|
|
|
// Synchronization
|
|
|
|
if (time - lastSynchronizationTime >= synchronizationDelay) {
|
|
|
|
lastSynchronizationTime = System.currentTimeMillis();
|
|
|
|
sendPositionSynchronization();
|
|
|
|
}
|
|
|
|
|
2019-08-19 17:04:19 +02:00
|
|
|
this.lastUpdate = System.currentTimeMillis();
|
|
|
|
}
|
2019-08-23 23:55:09 +02:00
|
|
|
|
|
|
|
if (shouldRemove()) {
|
|
|
|
remove();
|
|
|
|
}
|
2019-08-19 17:04:19 +02:00
|
|
|
}
|
|
|
|
|
2019-08-20 17:41:07 +02:00
|
|
|
public <E extends Event> void setEventCallback(Class<E> eventClass, Callback<E> callback) {
|
|
|
|
this.eventCallbacks.put((Class<Event>) eventClass, callback);
|
|
|
|
}
|
|
|
|
|
|
|
|
public <E extends Event> Callback<E> getEventCallback(Class<E> eventClass) {
|
|
|
|
return this.eventCallbacks.getOrDefault(eventClass, null);
|
|
|
|
}
|
|
|
|
|
|
|
|
public <E extends Event> void callEvent(Class<E> eventClass, E event) {
|
|
|
|
Callback<E> callback = getEventCallback(eventClass);
|
|
|
|
if (callback != null)
|
|
|
|
getEventCallback(eventClass).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-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();
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setVelocity(Vector velocity, long velocityTime) {
|
|
|
|
this.velocityX = velocity.getX();
|
|
|
|
this.velocityY = velocity.getY();
|
|
|
|
this.velocityZ = velocity.getZ();
|
|
|
|
this.velocityTime = System.currentTimeMillis() + velocityTime;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void resetVelocity() {
|
|
|
|
this.velocityX = 0;
|
|
|
|
this.velocityY = 0;
|
|
|
|
this.velocityZ = 0;
|
|
|
|
this.velocityTime = 0;
|
2019-08-10 08:44:35 +02:00
|
|
|
}
|
|
|
|
|
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-20 17:41:07 +02:00
|
|
|
public void addPassenger(Entity entity) {
|
2019-08-21 16:50:52 +02:00
|
|
|
// TODO if entity already has a vehicle, leave it before?
|
2019-08-20 17:41:07 +02:00
|
|
|
this.passengers.add(entity);
|
|
|
|
entity.vehicle = this;
|
|
|
|
if (instance != null) {
|
|
|
|
SetPassengersPacket passengersPacket = new SetPassengersPacket();
|
|
|
|
passengersPacket.vehicleEntityId = getEntityId();
|
2019-08-21 16:50:52 +02:00
|
|
|
passengersPacket.passengersId = new int[]{entity.getEntityId()}; // TODO all passengers not only the new
|
2019-08-20 17:41:07 +02:00
|
|
|
sendPacketToViewers(passengersPacket);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean hasPassenger() {
|
|
|
|
return !passengers.isEmpty();
|
|
|
|
}
|
|
|
|
|
|
|
|
public Set<Entity> getPassengers() {
|
|
|
|
return Collections.unmodifiableSet(passengers);
|
|
|
|
}
|
|
|
|
|
2019-08-21 16:50:52 +02:00
|
|
|
public void triggerStatus(byte status) {
|
|
|
|
EntityStatusPacket statusPacket = new EntityStatusPacket();
|
|
|
|
statusPacket.entityId = getEntityId();
|
|
|
|
statusPacket.status = status;
|
|
|
|
sendPacketToViewers(statusPacket);
|
|
|
|
}
|
|
|
|
|
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-24 20:34:01 +02:00
|
|
|
public void setGlowing(boolean glowing) {
|
|
|
|
this.glowing = glowing;
|
|
|
|
sendMetadataIndex(0);
|
|
|
|
}
|
|
|
|
|
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-21 16:50:52 +02:00
|
|
|
public boolean isChunkUnloaded(float x, float z) {
|
|
|
|
return getInstance().getChunk((int) Math.floor(x / 16), (int) Math.floor(z / 16)) == null;
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
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-11 07:42:56 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-08-10 08:44:35 +02:00
|
|
|
}
|
|
|
|
|
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-08-23 15:37:38 +02:00
|
|
|
sendMetadataIndex(0);
|
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
|
|
|
}
|
|
|
|
|
2019-08-10 08:44:35 +02:00
|
|
|
public void remove() {
|
|
|
|
this.shouldRemove = true;
|
2019-08-21 16:50:52 +02:00
|
|
|
synchronized (entityById) {
|
|
|
|
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-24 20:34:01 +02:00
|
|
|
protected EntityVelocityPacket getVelocityPacket() {
|
|
|
|
final float strength = 8000f / Main.TICK_PER_SECOND;
|
|
|
|
EntityVelocityPacket velocityPacket = new EntityVelocityPacket();
|
|
|
|
velocityPacket.entityId = getEntityId();
|
|
|
|
velocityPacket.velocityX = (short) (velocityX * strength);
|
|
|
|
velocityPacket.velocityY = (short) (velocityY * strength);
|
|
|
|
velocityPacket.velocityZ = (short) (velocityZ * strength);
|
|
|
|
return velocityPacket;
|
|
|
|
}
|
|
|
|
|
2019-08-22 14:52:32 +02:00
|
|
|
public EntityMetaDataPacket getMetadataPacket() {
|
|
|
|
EntityMetaDataPacket metaDataPacket = new EntityMetaDataPacket();
|
|
|
|
metaDataPacket.entityId = getEntityId();
|
|
|
|
metaDataPacket.data = getMetadataBuffer();
|
|
|
|
return metaDataPacket;
|
|
|
|
}
|
|
|
|
|
2019-08-19 17:04:19 +02:00
|
|
|
public Buffer getMetadataBuffer() {
|
|
|
|
Buffer buffer = Buffer.create();
|
|
|
|
fillMetadataIndex(buffer, 0);
|
2019-08-22 14:52:32 +02:00
|
|
|
fillMetadataIndex(buffer, 1);
|
2019-08-23 15:37:38 +02:00
|
|
|
fillMetadataIndex(buffer, 5);
|
2019-08-19 17:04:19 +02:00
|
|
|
return buffer;
|
|
|
|
}
|
|
|
|
|
2019-08-23 15:37:38 +02:00
|
|
|
protected void sendMetadataIndex(int index) {
|
2019-08-20 22:40:57 +02:00
|
|
|
Buffer buffer = Buffer.create();
|
|
|
|
fillMetadataIndex(buffer, index);
|
|
|
|
|
|
|
|
EntityMetaDataPacket metaDataPacket = new EntityMetaDataPacket();
|
|
|
|
metaDataPacket.entityId = getEntityId();
|
|
|
|
metaDataPacket.data = buffer;
|
|
|
|
if (this instanceof Player) {
|
2019-08-24 20:34:01 +02:00
|
|
|
Player player = (Player) this;
|
|
|
|
player.sendPacketToViewersAndSelf(metaDataPacket);
|
|
|
|
} else {
|
|
|
|
sendPacketToViewers(metaDataPacket);
|
2019-08-20 22:40:57 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-19 17:04:19 +02:00
|
|
|
private void fillMetadataIndex(Buffer buffer, int index) {
|
|
|
|
switch (index) {
|
|
|
|
case 0:
|
|
|
|
fillStateMetadata(buffer);
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
fillAirTickMetaData(buffer);
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
fillCustomNameMetaData(buffer);
|
|
|
|
break;
|
2019-08-23 15:37:38 +02:00
|
|
|
case 5:
|
|
|
|
fillNoGravityMetaData(buffer);
|
|
|
|
break;
|
2019-08-19 17:04:19 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void fillStateMetadata(Buffer buffer) {
|
|
|
|
buffer.putByte((byte) 0);
|
|
|
|
buffer.putByte(METADATA_BYTE);
|
|
|
|
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;
|
|
|
|
buffer.putByte(index0);
|
|
|
|
}
|
|
|
|
|
2019-08-24 20:34:01 +02:00
|
|
|
protected void sendPositionSynchronization() {
|
|
|
|
EntityTeleportPacket entityTeleportPacket = new EntityTeleportPacket();
|
|
|
|
entityTeleportPacket.entityId = getEntityId();
|
|
|
|
entityTeleportPacket.position = getPosition();
|
|
|
|
entityTeleportPacket.onGround = onGround;
|
|
|
|
sendPacketToViewers(entityTeleportPacket);
|
|
|
|
}
|
|
|
|
|
2019-08-19 17:04:19 +02:00
|
|
|
private void fillAirTickMetaData(Buffer buffer) {
|
|
|
|
buffer.putByte((byte) 1);
|
|
|
|
buffer.putByte(METADATA_VARINT);
|
|
|
|
Utils.writeVarInt(buffer, air);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void fillCustomNameMetaData(Buffer buffer) {
|
|
|
|
buffer.putByte((byte) 2);
|
|
|
|
buffer.putByte(METADATA_CHAT);
|
|
|
|
Utils.writeString(buffer, customName);
|
|
|
|
}
|
|
|
|
|
2019-08-23 15:37:38 +02:00
|
|
|
private void fillNoGravityMetaData(Buffer buffer) {
|
|
|
|
buffer.putByte((byte) 5);
|
|
|
|
buffer.putByte(METADATA_BOOLEAN);
|
|
|
|
buffer.putBoolean(noGravity);
|
|
|
|
}
|
|
|
|
|
2019-08-19 17:04:19 +02:00
|
|
|
private boolean shouldUpdate() {
|
2019-08-20 17:41:07 +02:00
|
|
|
return (float) (System.currentTimeMillis() - lastUpdate) >= Main.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,
|
|
|
|
DYING;
|
|
|
|
}
|
|
|
|
|
2019-08-10 08:44:35 +02:00
|
|
|
protected boolean shouldRemove() {
|
|
|
|
return shouldRemove;
|
|
|
|
}
|
|
|
|
}
|