2020-04-24 03:25:58 +02:00
|
|
|
package net.minestom.server.entity;
|
2019-08-10 08:44:35 +02:00
|
|
|
|
2020-12-08 09:36:19 +01:00
|
|
|
import com.google.common.collect.Queues;
|
2020-04-24 03:25:58 +02:00
|
|
|
import net.minestom.server.MinecraftServer;
|
|
|
|
import net.minestom.server.Viewable;
|
2020-12-31 19:50:26 +01:00
|
|
|
import net.minestom.server.chat.JsonMessage;
|
2020-04-24 03:25:58 +02:00
|
|
|
import net.minestom.server.collision.BoundingBox;
|
|
|
|
import net.minestom.server.collision.CollisionUtils;
|
|
|
|
import net.minestom.server.data.Data;
|
|
|
|
import net.minestom.server.data.DataContainer;
|
2021-02-23 16:37:00 +01:00
|
|
|
import net.minestom.server.entity.metadata.EntityMeta;
|
2020-04-24 03:25:58 +02:00
|
|
|
import net.minestom.server.event.Event;
|
2020-04-28 01:20:11 +02:00
|
|
|
import net.minestom.server.event.EventCallback;
|
2021-01-03 00:23:41 +01:00
|
|
|
import net.minestom.server.event.entity.*;
|
2020-05-08 06:54:33 +02:00
|
|
|
import net.minestom.server.event.handler.EventHandler;
|
2020-08-15 13:32:36 +02:00
|
|
|
import net.minestom.server.instance.Chunk;
|
|
|
|
import net.minestom.server.instance.Instance;
|
|
|
|
import net.minestom.server.instance.InstanceManager;
|
2020-05-05 15:55:21 +02:00
|
|
|
import net.minestom.server.instance.block.CustomBlock;
|
2020-04-24 03:25:58 +02:00
|
|
|
import net.minestom.server.network.packet.server.play.*;
|
2021-02-25 08:37:02 +01:00
|
|
|
import net.minestom.server.network.player.PlayerConnection;
|
2020-11-14 07:09:36 +01:00
|
|
|
import net.minestom.server.permission.Permission;
|
|
|
|
import net.minestom.server.permission.PermissionHandler;
|
2020-12-31 01:29:07 +01:00
|
|
|
import net.minestom.server.potion.Potion;
|
|
|
|
import net.minestom.server.potion.PotionEffect;
|
2020-12-31 01:47:43 +01:00
|
|
|
import net.minestom.server.potion.TimedPotion;
|
2020-10-12 04:14:06 +02:00
|
|
|
import net.minestom.server.thread.ThreadProvider;
|
2020-07-11 14:16:36 +02:00
|
|
|
import net.minestom.server.utils.BlockPosition;
|
|
|
|
import net.minestom.server.utils.Position;
|
2020-04-28 01:20:11 +02:00
|
|
|
import net.minestom.server.utils.Vector;
|
2020-10-22 22:55:40 +02:00
|
|
|
import net.minestom.server.utils.callback.OptionalCallback;
|
2020-11-22 13:56:36 +01:00
|
|
|
import net.minestom.server.utils.chunk.ChunkCallback;
|
2020-05-25 13:46:48 +02:00
|
|
|
import net.minestom.server.utils.chunk.ChunkUtils;
|
2020-07-11 14:16:36 +02:00
|
|
|
import net.minestom.server.utils.entity.EntityUtils;
|
2020-05-25 02:37:57 +02:00
|
|
|
import net.minestom.server.utils.player.PlayerUtils;
|
2020-11-18 05:13:49 +01:00
|
|
|
import net.minestom.server.utils.time.CooldownUtils;
|
2020-05-15 18:03:28 +02:00
|
|
|
import net.minestom.server.utils.time.TimeUnit;
|
2020-11-18 05:13:49 +01:00
|
|
|
import net.minestom.server.utils.time.UpdateOption;
|
2020-05-23 04:20:01 +02:00
|
|
|
import net.minestom.server.utils.validate.Check;
|
2020-10-24 10:46:23 +02:00
|
|
|
import org.jetbrains.annotations.NotNull;
|
|
|
|
import org.jetbrains.annotations.Nullable;
|
2019-08-10 08:44:35 +02:00
|
|
|
|
2020-04-28 01:20:11 +02:00
|
|
|
import java.util.*;
|
2019-08-20 17:41:07 +02:00
|
|
|
import java.util.concurrent.ConcurrentHashMap;
|
2020-12-31 01:47:43 +01:00
|
|
|
import java.util.concurrent.CopyOnWriteArrayList;
|
2019-08-20 17:41:07 +02:00
|
|
|
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
|
|
|
|
2020-11-01 22:53:36 +01:00
|
|
|
/**
|
|
|
|
* Could be a player, a monster, or an object.
|
|
|
|
* <p>
|
|
|
|
* To create your own entity you probably want to extends {@link ObjectEntity} or {@link EntityCreature} instead.
|
|
|
|
*/
|
2021-02-25 11:00:02 +01:00
|
|
|
public class Entity implements Viewable, EventHandler, DataContainer, PermissionHandler {
|
2019-08-10 08:44:35 +02:00
|
|
|
|
2020-08-16 19:18:34 +02:00
|
|
|
private static final Map<Integer, Entity> entityById = new ConcurrentHashMap<>();
|
2020-12-29 19:58:40 +01:00
|
|
|
private static final Map<UUID, Entity> entityByUuid = new ConcurrentHashMap<>();
|
2020-08-16 19:18:34 +02:00
|
|
|
private static final AtomicInteger lastEntityId = new AtomicInteger();
|
2019-08-12 08:30:59 +02:00
|
|
|
|
2019-08-11 07:42:56 +02:00
|
|
|
protected Instance instance;
|
2020-12-12 06:13:50 +01:00
|
|
|
protected final Position position;
|
2021-01-25 13:47:13 +01:00
|
|
|
protected double lastX, lastY, lastZ;
|
|
|
|
protected double cacheX, cacheY, cacheZ; // Used to synchronize with #getPosition
|
2019-08-20 22:40:57 +02:00
|
|
|
protected float lastYaw, lastPitch;
|
2020-05-27 23:13:13 +02:00
|
|
|
protected float cacheYaw, cachePitch;
|
2020-09-23 22:01:47 +02:00
|
|
|
protected boolean onGround;
|
2019-08-20 17:41:07 +02:00
|
|
|
|
2019-08-30 01:17:46 +02:00
|
|
|
private BoundingBox boundingBox;
|
|
|
|
|
2019-08-20 17:41:07 +02:00
|
|
|
protected Entity vehicle;
|
2020-08-17 16:50:23 +02:00
|
|
|
|
2019-08-25 20:03:43 +02:00
|
|
|
// Velocity
|
2021-02-25 15:48:48 +01:00
|
|
|
protected Vector velocity = new Vector(); // Movement in block per second
|
2021-02-22 12:42:46 +01:00
|
|
|
protected boolean hasPhysics = true;
|
2020-08-17 16:50:23 +02:00
|
|
|
|
2021-01-25 19:33:53 +01:00
|
|
|
protected double gravityDragPerTick;
|
|
|
|
protected double gravityAcceleration;
|
|
|
|
protected double gravityTerminalVelocity;
|
2020-12-11 20:17:33 +01:00
|
|
|
protected int gravityTickCount; // Number of tick where gravity tick was applied
|
2019-08-27 20:49:11 +02:00
|
|
|
|
2020-05-23 17:57:56 +02:00
|
|
|
private boolean autoViewable;
|
2020-08-16 19:18:34 +02:00
|
|
|
private final int id;
|
2021-03-20 03:50:28 +01:00
|
|
|
protected final Set<Player> viewers = ConcurrentHashMap.newKeySet();
|
2020-12-14 06:27:39 +01:00
|
|
|
private final Set<Player> unmodifiableViewers = Collections.unmodifiableSet(viewers);
|
2020-11-14 07:09:36 +01:00
|
|
|
private Data data;
|
2020-12-10 02:56:56 +01:00
|
|
|
private final Set<Permission> permissions = new CopyOnWriteArraySet<>();
|
2019-08-20 17:41:07 +02:00
|
|
|
|
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
|
2020-08-09 08:16:54 +02:00
|
|
|
private boolean removed;
|
2019-08-10 08:44:35 +02:00
|
|
|
private boolean shouldRemove;
|
2019-08-21 16:50:52 +02:00
|
|
|
private long scheduledRemoveTime;
|
2020-08-17 16:50:23 +02:00
|
|
|
|
2020-08-16 19:18:34 +02:00
|
|
|
private final Set<Entity> passengers = new CopyOnWriteArraySet<>();
|
2020-12-05 01:36:06 +01:00
|
|
|
protected EntityType entityType; // UNSAFE to change, modify at your own risk
|
2020-08-17 16:50:23 +02:00
|
|
|
|
2020-11-18 05:13:49 +01:00
|
|
|
// Network synchronization, send the absolute position of the entity each X milliseconds
|
|
|
|
private static final UpdateOption SYNCHRONIZATION_COOLDOWN = new UpdateOption(1500, TimeUnit.MILLISECOND);
|
2021-03-01 11:09:08 +01:00
|
|
|
private UpdateOption customSynchronizationCooldown;
|
2020-11-18 05:13:49 +01:00
|
|
|
private long lastAbsoluteSynchronizationTime;
|
2019-08-24 20:34:01 +02:00
|
|
|
|
2020-09-23 22:01:47 +02:00
|
|
|
// Events
|
2020-10-06 04:06:59 +02:00
|
|
|
private final Map<Class<? extends Event>, Collection<EventCallback>> eventCallbacks = new ConcurrentHashMap<>();
|
2021-02-03 17:11:06 +01:00
|
|
|
private final Map<String, Collection<EventCallback<?>>> extensionCallbacks = new ConcurrentHashMap<>();
|
2020-08-17 16:50:23 +02:00
|
|
|
|
2021-01-30 04:44:44 +01:00
|
|
|
protected Metadata metadata = new Metadata(this);
|
2021-02-23 16:37:00 +01:00
|
|
|
protected EntityMeta entityMeta;
|
2020-05-07 16:00:52 +02:00
|
|
|
|
2020-12-31 12:05:36 +01:00
|
|
|
private final List<TimedPotion> effects = new CopyOnWriteArrayList<>();
|
2020-12-31 01:29:07 +01:00
|
|
|
|
2020-10-12 04:14:06 +02:00
|
|
|
// list of scheduled tasks to be executed during the next entity tick
|
2020-12-08 09:36:19 +01:00
|
|
|
protected final Queue<Consumer<Entity>> nextTick = Queues.newConcurrentLinkedQueue();
|
2020-05-07 16:00:52 +02:00
|
|
|
|
|
|
|
// Tick related
|
|
|
|
private long ticks;
|
|
|
|
private final EntityTickEvent tickEvent = new EntityTickEvent(this);
|
|
|
|
|
2021-02-25 08:37:02 +01:00
|
|
|
/**
|
|
|
|
* Lock used to support #switchEntityType
|
|
|
|
*/
|
|
|
|
private final Object entityTypeLock = new Object();
|
|
|
|
|
2021-02-25 11:56:10 +01:00
|
|
|
public Entity(@NotNull EntityType entityType, @NotNull UUID uuid) {
|
|
|
|
this.id = generateId();
|
|
|
|
this.entityType = entityType;
|
|
|
|
this.uuid = uuid;
|
|
|
|
this.position = new Position();
|
|
|
|
|
|
|
|
setBoundingBox(entityType.getWidth(), entityType.getHeight(), entityType.getWidth());
|
|
|
|
|
|
|
|
this.entityMeta = entityType.getMetaConstructor().apply(this, this.metadata);
|
|
|
|
|
|
|
|
setAutoViewable(true);
|
|
|
|
|
|
|
|
Entity.entityById.put(id, this);
|
|
|
|
Entity.entityByUuid.put(uuid, this);
|
|
|
|
}
|
|
|
|
|
|
|
|
public Entity(@NotNull EntityType entityType) {
|
|
|
|
this(entityType, UUID.randomUUID());
|
|
|
|
}
|
|
|
|
|
|
|
|
@Deprecated
|
2020-12-29 19:58:40 +01:00
|
|
|
public Entity(@NotNull EntityType entityType, @NotNull UUID uuid, @NotNull Position spawnPosition) {
|
2021-02-25 12:40:14 +01:00
|
|
|
this(entityType, uuid);
|
|
|
|
this.position.set(spawnPosition);
|
2021-01-23 15:43:06 +01:00
|
|
|
this.lastX = spawnPosition.getX();
|
|
|
|
this.lastY = spawnPosition.getY();
|
|
|
|
this.lastZ = spawnPosition.getZ();
|
2020-12-29 19:58:40 +01:00
|
|
|
}
|
|
|
|
|
2021-02-25 11:56:10 +01:00
|
|
|
@Deprecated
|
2020-12-29 19:58:40 +01:00
|
|
|
public Entity(@NotNull EntityType entityType, @NotNull Position spawnPosition) {
|
|
|
|
this(entityType, UUID.randomUUID(), spawnPosition);
|
2019-08-10 08:44:35 +02:00
|
|
|
}
|
|
|
|
|
2020-10-12 04:14:06 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Schedules a task to be run during the next entity tick.
|
|
|
|
* It ensures that the task will be executed in the same thread as the entity (depending of the {@link ThreadProvider}).
|
2020-10-12 04:14:06 +02:00
|
|
|
*
|
|
|
|
* @param callback the task to execute during the next entity tick
|
|
|
|
*/
|
2020-10-24 10:46:23 +02:00
|
|
|
public void scheduleNextTick(@NotNull Consumer<Entity> callback) {
|
2020-10-12 04:14:06 +02:00
|
|
|
this.nextTick.add(callback);
|
2020-06-28 23:11:40 +02:00
|
|
|
}
|
|
|
|
|
2020-05-15 18:03:28 +02:00
|
|
|
/**
|
2020-11-14 23:24:16 +01:00
|
|
|
* Gets an entity based on its id (from {@link #getEntityId()}).
|
|
|
|
* <p>
|
|
|
|
* Entity id are unique server-wide.
|
|
|
|
*
|
|
|
|
* @param id the entity unique id
|
2020-05-15 18:03:28 +02:00
|
|
|
* @return the entity having the specified id, null if not found
|
|
|
|
*/
|
2020-10-24 10:46:23 +02:00
|
|
|
@Nullable
|
2019-08-21 16:50:52 +02:00
|
|
|
public static Entity getEntity(int id) {
|
2020-12-30 20:30:48 +01:00
|
|
|
return Entity.entityById.getOrDefault(id, null);
|
2019-08-21 16:50:52 +02:00
|
|
|
}
|
2019-08-19 17:04:19 +02:00
|
|
|
|
2020-12-29 19:58:40 +01:00
|
|
|
/**
|
|
|
|
* Gets an entity based on its UUID (from {@link #getUuid()}).
|
|
|
|
*
|
|
|
|
* @param uuid the entity UUID
|
|
|
|
* @return the entity having the specified uuid, null if not found
|
|
|
|
*/
|
|
|
|
@Nullable
|
|
|
|
public static Entity getEntity(@NotNull UUID uuid) {
|
2020-12-30 20:30:48 +01:00
|
|
|
return Entity.entityByUuid.getOrDefault(uuid, null);
|
2020-12-29 19:58:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-11-27 13:29:38 +01:00
|
|
|
/**
|
|
|
|
* Generate and return a new unique entity id.
|
|
|
|
* <p>
|
|
|
|
* Useful if you want to spawn entities using packet but don't risk to have duplicated id.
|
|
|
|
*
|
|
|
|
* @return a newly generated entity id
|
|
|
|
*/
|
|
|
|
public static int generateId() {
|
2019-08-12 08:30:59 +02:00
|
|
|
return lastEntityId.incrementAndGet();
|
2019-08-10 08:44:35 +02:00
|
|
|
}
|
|
|
|
|
2020-05-15 18:03:28 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Called each tick.
|
2020-06-01 00:51:31 +02:00
|
|
|
*
|
2020-11-15 05:12:28 +01:00
|
|
|
* @param time time of the update in milliseconds
|
2020-05-15 18:03:28 +02:00
|
|
|
*/
|
2021-02-25 11:00:02 +01:00
|
|
|
public void update(long time) {
|
|
|
|
|
|
|
|
}
|
2019-08-19 17:04:19 +02:00
|
|
|
|
2020-05-15 18:03:28 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Called when a new instance is set.
|
2020-05-15 18:03:28 +02:00
|
|
|
*/
|
2021-02-25 11:00:02 +01:00
|
|
|
public void spawn() {
|
|
|
|
|
|
|
|
}
|
2019-08-24 20:34:01 +02:00
|
|
|
|
2020-03-29 20:58:30 +02:00
|
|
|
public boolean isOnGround() {
|
2020-05-02 23:34:09 +02:00
|
|
|
return onGround || EntityUtils.isOnGround(this) /* backup for levitating entities */;
|
|
|
|
}
|
|
|
|
|
2021-02-23 16:37:00 +01:00
|
|
|
/**
|
|
|
|
* Gets metadata of this entity.
|
|
|
|
* You may want to cast it to specific implementation.
|
|
|
|
*
|
|
|
|
* @return metadata of this entity.
|
|
|
|
*/
|
|
|
|
@NotNull
|
|
|
|
public EntityMeta getEntityMeta() {
|
|
|
|
return this.entityMeta;
|
|
|
|
}
|
|
|
|
|
2020-08-06 07:42:00 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Teleports the entity only if the chunk at {@code position} is loaded or if
|
|
|
|
* {@link Instance#hasEnabledAutoChunkLoad()} returns true.
|
2020-08-06 07:42:00 +02:00
|
|
|
*
|
|
|
|
* @param position the teleport position
|
2020-11-22 13:56:36 +01:00
|
|
|
* @param chunks the chunk indexes to load before teleporting the entity,
|
|
|
|
* indexes are from {@link ChunkUtils#getChunkIndex(int, int)},
|
|
|
|
* can be null or empty to only load the chunk at {@code position}
|
2020-10-24 10:46:23 +02:00
|
|
|
* @param callback the optional callback executed, even if auto chunk is not enabled
|
2020-11-22 13:56:36 +01:00
|
|
|
* @throws IllegalStateException if you try to teleport an entity before settings its instance
|
2020-08-06 07:42:00 +02:00
|
|
|
*/
|
2020-11-22 13:56:36 +01:00
|
|
|
public void teleport(@NotNull Position position, @Nullable long[] chunks, @Nullable Runnable callback) {
|
2020-05-23 04:20:01 +02:00
|
|
|
Check.stateCondition(instance == null, "You need to use Entity#setInstance before teleporting an entity!");
|
2019-08-21 16:50:52 +02:00
|
|
|
|
2021-01-23 08:42:49 +01:00
|
|
|
final Position teleportPosition = position.clone(); // Prevent synchronization issue
|
|
|
|
|
2020-11-22 13:56:36 +01:00
|
|
|
final ChunkCallback endCallback = (chunk) -> {
|
2021-01-23 08:42:49 +01:00
|
|
|
refreshPosition(teleportPosition);
|
|
|
|
refreshView(teleportPosition.getYaw(), teleportPosition.getPitch());
|
2020-11-22 13:56:36 +01:00
|
|
|
|
2020-05-24 22:21:38 +02:00
|
|
|
sendSynchronization();
|
2020-10-22 22:55:40 +02:00
|
|
|
|
|
|
|
OptionalCallback.execute(callback);
|
2019-08-26 00:29:40 +02:00
|
|
|
};
|
|
|
|
|
2020-11-22 13:56:36 +01:00
|
|
|
if (chunks == null || chunks.length == 0) {
|
2021-01-23 08:42:49 +01:00
|
|
|
instance.loadOptionalChunk(teleportPosition, endCallback);
|
2019-08-25 20:03:43 +02:00
|
|
|
} else {
|
2020-11-22 13:56:36 +01:00
|
|
|
ChunkUtils.optionalLoadAll(instance, chunks, null, endCallback);
|
2019-08-25 20:03:43 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-22 13:56:36 +01:00
|
|
|
public void teleport(@NotNull Position position, @Nullable Runnable callback) {
|
|
|
|
teleport(position, null, callback);
|
|
|
|
}
|
|
|
|
|
2020-10-24 10:46:23 +02:00
|
|
|
public void teleport(@NotNull Position position) {
|
2019-08-25 20:03:43 +02:00
|
|
|
teleport(position, null);
|
2019-08-21 16:50:52 +02:00
|
|
|
}
|
|
|
|
|
2020-05-27 23:13:13 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Changes the view of the entity.
|
2020-05-27 23:13:13 +02:00
|
|
|
*
|
|
|
|
* @param yaw the new yaw
|
|
|
|
* @param pitch the new pitch
|
|
|
|
*/
|
|
|
|
public void setView(float yaw, float pitch) {
|
|
|
|
refreshView(yaw, pitch);
|
|
|
|
|
|
|
|
EntityRotationPacket entityRotationPacket = new EntityRotationPacket();
|
|
|
|
entityRotationPacket.entityId = getEntityId();
|
|
|
|
entityRotationPacket.yaw = yaw;
|
|
|
|
entityRotationPacket.pitch = pitch;
|
|
|
|
entityRotationPacket.onGround = onGround;
|
|
|
|
|
|
|
|
EntityHeadLookPacket entityHeadLookPacket = new EntityHeadLookPacket();
|
|
|
|
entityHeadLookPacket.entityId = getEntityId();
|
|
|
|
entityHeadLookPacket.yaw = yaw;
|
2020-05-27 23:26:45 +02:00
|
|
|
|
|
|
|
sendPacketToViewersAndSelf(entityHeadLookPacket);
|
2020-05-27 23:13:13 +02:00
|
|
|
sendPacketToViewersAndSelf(entityRotationPacket);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Changes the view of the entity.
|
|
|
|
* Only the yaw and pitch are used.
|
2020-05-27 23:13:13 +02:00
|
|
|
*
|
|
|
|
* @param position the new view
|
|
|
|
*/
|
2020-10-24 10:46:23 +02:00
|
|
|
public void setView(@NotNull Position position) {
|
2020-05-27 23:13:13 +02:00
|
|
|
setView(position.getYaw(), position.getPitch());
|
|
|
|
}
|
|
|
|
|
2020-05-23 17:57:56 +02:00
|
|
|
/**
|
2020-12-06 20:11:14 +01:00
|
|
|
* When set to true, the entity will automatically get new viewers when they come too close.
|
|
|
|
* This can be use to have complete control over which player can see it, without having to deal with
|
2020-10-15 21:16:31 +02:00
|
|
|
* raw packets.
|
2020-05-23 17:57:56 +02:00
|
|
|
* <p>
|
2020-10-15 21:16:31 +02:00
|
|
|
* True by default for all entities.
|
2020-05-23 17:57:56 +02:00
|
|
|
* When set to false, it is important to mention that the players will not be removed automatically from its viewers
|
2020-12-06 20:11:14 +01:00
|
|
|
* list, you would have to do that manually using {@link #addViewer(Player)} and {@link #removeViewer(Player)}..
|
2020-05-23 17:57:56 +02:00
|
|
|
*
|
|
|
|
* @return true if the entity is automatically viewable for close players, false otherwise
|
|
|
|
*/
|
|
|
|
public boolean isAutoViewable() {
|
|
|
|
return autoViewable;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-10-24 10:46:23 +02:00
|
|
|
* Makes the entity auto viewable or only manually.
|
|
|
|
*
|
2020-05-23 17:57:56 +02:00
|
|
|
* @param autoViewable should the entity be automatically viewable for close players
|
2020-10-24 10:46:23 +02:00
|
|
|
* @see #isAutoViewable()
|
2020-05-23 17:57:56 +02:00
|
|
|
*/
|
|
|
|
public void setAutoViewable(boolean autoViewable) {
|
|
|
|
this.autoViewable = autoViewable;
|
|
|
|
}
|
|
|
|
|
2019-08-20 17:41:07 +02:00
|
|
|
@Override
|
2021-02-25 08:37:02 +01:00
|
|
|
public final boolean addViewer(@NotNull Player player) {
|
|
|
|
synchronized (this.entityTypeLock) {
|
|
|
|
return addViewer0(player);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean addViewer0(@NotNull Player player) {
|
|
|
|
if (!this.viewers.add(player)) {
|
2020-05-29 02:11:41 +02:00
|
|
|
return false;
|
2021-02-25 08:37:02 +01:00
|
|
|
}
|
2019-09-01 06:18:41 +02:00
|
|
|
player.viewableEntities.add(this);
|
2021-02-25 08:37:02 +01:00
|
|
|
|
|
|
|
PlayerConnection playerConnection = player.getPlayerConnection();
|
|
|
|
playerConnection.sendPacket(getEntityType().getSpawnType().getSpawnPacket(this));
|
|
|
|
playerConnection.sendPacket(getVelocityPacket());
|
|
|
|
playerConnection.sendPacket(getMetadataPacket());
|
|
|
|
|
2021-03-22 14:54:52 +01:00
|
|
|
// Passenger
|
2021-02-25 08:37:02 +01:00
|
|
|
if (hasPassenger()) {
|
|
|
|
playerConnection.sendPacket(getPassengersPacket());
|
|
|
|
}
|
|
|
|
|
2021-03-22 14:54:52 +01:00
|
|
|
// Head position
|
|
|
|
{
|
|
|
|
EntityHeadLookPacket entityHeadLookPacket = new EntityHeadLookPacket();
|
|
|
|
entityHeadLookPacket.entityId = getEntityId();
|
|
|
|
entityHeadLookPacket.yaw = position.getYaw();
|
|
|
|
playerConnection.sendPacket(entityHeadLookPacket);
|
|
|
|
}
|
|
|
|
|
2020-08-16 19:18:34 +02:00
|
|
|
return true;
|
2019-08-20 17:41:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2021-03-05 14:30:50 +01:00
|
|
|
public final boolean removeViewer(@NotNull Player player) {
|
2021-02-25 08:37:02 +01:00
|
|
|
synchronized (this.entityTypeLock) {
|
|
|
|
return removeViewer0(player);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean removeViewer0(@NotNull Player player) {
|
|
|
|
if (!viewers.remove(player)) {
|
2020-05-24 19:22:58 +02:00
|
|
|
return false;
|
2021-02-25 08:37:02 +01:00
|
|
|
}
|
2020-05-24 19:22:58 +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);
|
2020-05-24 19:22:58 +02:00
|
|
|
return true;
|
2019-08-20 17:41:07 +02:00
|
|
|
}
|
|
|
|
|
2020-10-24 10:46:23 +02:00
|
|
|
@NotNull
|
2019-08-20 17:41:07 +02:00
|
|
|
@Override
|
|
|
|
public Set<Player> getViewers() {
|
2020-12-14 06:27:39 +01:00
|
|
|
return unmodifiableViewers;
|
2019-08-20 17:41:07 +02:00
|
|
|
}
|
|
|
|
|
2021-02-25 08:37:02 +01:00
|
|
|
/**
|
|
|
|
* Changes the entity type of this entity.
|
|
|
|
* <p>
|
|
|
|
* Works by changing the internal entity type field and by calling {@link #removeViewer(Player)}
|
|
|
|
* followed by {@link #addViewer(Player)} to all current viewers.
|
|
|
|
* <p>
|
|
|
|
* Be aware that this only change the visual of the entity, the {@link net.minestom.server.collision.BoundingBox}
|
|
|
|
* will not be modified.
|
|
|
|
*
|
|
|
|
* @param entityType the new entity type
|
|
|
|
*/
|
2021-03-04 23:06:29 +01:00
|
|
|
public void switchEntityType(@NotNull EntityType entityType) {
|
2021-02-25 08:37:02 +01:00
|
|
|
synchronized (entityTypeLock) {
|
|
|
|
this.entityType = entityType;
|
|
|
|
this.metadata = new Metadata(this);
|
|
|
|
this.entityMeta = entityType.getMetaConstructor().apply(this, this.metadata);
|
|
|
|
|
|
|
|
Set<Player> viewers = new HashSet<>(getViewers());
|
2021-02-25 11:39:22 +01:00
|
|
|
getViewers().forEach(this::removeViewer0);
|
|
|
|
viewers.forEach(this::addViewer0);
|
2021-02-25 08:37:02 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-23 23:55:09 +02:00
|
|
|
@Override
|
|
|
|
public Data getData() {
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-10-24 10:46:23 +02:00
|
|
|
public void setData(@Nullable Data data) {
|
2019-08-23 23:55:09 +02:00
|
|
|
this.data = data;
|
|
|
|
}
|
|
|
|
|
2020-11-14 07:09:36 +01:00
|
|
|
@NotNull
|
|
|
|
@Override
|
2020-12-10 02:56:56 +01:00
|
|
|
public Set<Permission> getAllPermissions() {
|
2020-11-14 07:09:36 +01:00
|
|
|
return permissions;
|
|
|
|
}
|
|
|
|
|
2020-05-29 23:17:14 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Updates the entity, called every tick.
|
2020-10-31 01:38:57 +01:00
|
|
|
* <p>
|
|
|
|
* Ignored if {@link #getInstance()} returns null.
|
2020-05-29 23:17:14 +02:00
|
|
|
*
|
2020-08-06 07:42:00 +02:00
|
|
|
* @param time the update time in milliseconds
|
2020-05-29 23:17:14 +02:00
|
|
|
*/
|
2020-02-09 15:34:09 +01:00
|
|
|
public void tick(long time) {
|
2019-08-23 23:55:09 +02:00
|
|
|
if (instance == null)
|
|
|
|
return;
|
|
|
|
|
2020-12-05 23:57:07 +01:00
|
|
|
// Scheduled remove
|
|
|
|
if (scheduledRemoveTime != 0) {
|
2020-07-24 16:11:48 +02:00
|
|
|
final boolean finished = time >= scheduledRemoveTime;
|
2019-08-21 16:50:52 +02:00
|
|
|
if (finished) {
|
|
|
|
remove();
|
2020-12-05 23:57:07 +01:00
|
|
|
return;
|
2019-08-21 16:50:52 +02:00
|
|
|
}
|
|
|
|
}
|
2019-08-23 23:55:09 +02:00
|
|
|
|
2020-12-05 23:57:07 +01:00
|
|
|
// Instant remove
|
2019-08-23 23:55:09 +02:00
|
|
|
if (shouldRemove()) {
|
|
|
|
remove();
|
|
|
|
return;
|
2020-05-09 18:24:14 +02:00
|
|
|
}
|
|
|
|
|
2020-12-05 23:57:07 +01:00
|
|
|
// Check if the entity chunk is loaded
|
|
|
|
final Chunk currentChunk = getChunk();
|
2020-11-14 09:02:29 +01:00
|
|
|
if (!ChunkUtils.isLoaded(currentChunk)) {
|
2020-05-09 18:24:14 +02:00
|
|
|
// No update for entities in unloaded chunk
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-10-12 04:14:06 +02:00
|
|
|
// scheduled tasks
|
|
|
|
if (!nextTick.isEmpty()) {
|
|
|
|
Consumer<Entity> callback;
|
|
|
|
while ((callback = nextTick.poll()) != null) {
|
|
|
|
callback.accept(this);
|
2020-06-28 23:11:40 +02:00
|
|
|
}
|
|
|
|
}
|
2020-10-12 04:14:06 +02:00
|
|
|
|
2021-01-27 19:20:52 +01:00
|
|
|
final boolean isNettyClient = PlayerUtils.isNettyClient(this);
|
|
|
|
|
2020-05-27 22:24:58 +02:00
|
|
|
// Synchronization with updated fields in #getPosition()
|
2020-05-27 23:13:13 +02:00
|
|
|
{
|
2020-11-26 12:46:49 +01:00
|
|
|
final boolean positionChange = cacheX != position.getX() ||
|
2020-05-27 23:13:13 +02:00
|
|
|
cacheY != position.getY() ||
|
2020-11-26 12:46:49 +01:00
|
|
|
cacheZ != position.getZ();
|
|
|
|
final boolean viewChange = cacheYaw != position.getYaw() ||
|
|
|
|
cachePitch != position.getPitch();
|
2021-01-25 13:47:13 +01:00
|
|
|
final double distance = positionChange ? position.getDistance(cacheX, cacheY, cacheZ) : 0;
|
2020-11-26 12:46:49 +01:00
|
|
|
|
2021-01-27 19:20:52 +01:00
|
|
|
if (distance >= 8 || (positionChange && isNettyClient)) {
|
2020-11-26 12:46:49 +01:00
|
|
|
// Teleport has the priority over everything else
|
2020-05-27 23:13:13 +02:00
|
|
|
teleport(position);
|
2020-11-26 12:46:49 +01:00
|
|
|
} else if (positionChange && viewChange) {
|
|
|
|
EntityPositionAndRotationPacket positionAndRotationPacket =
|
|
|
|
EntityPositionAndRotationPacket.getPacket(getEntityId(),
|
|
|
|
position, new Position(cacheX, cacheY, cacheZ), isOnGround());
|
|
|
|
|
|
|
|
sendPacketToViewersAndSelf(positionAndRotationPacket);
|
|
|
|
|
2020-12-09 21:34:53 +01:00
|
|
|
refreshPosition(position.clone());
|
2020-11-26 12:46:49 +01:00
|
|
|
|
|
|
|
// Fix head rotation
|
|
|
|
EntityHeadLookPacket entityHeadLookPacket = new EntityHeadLookPacket();
|
|
|
|
entityHeadLookPacket.entityId = getEntityId();
|
|
|
|
entityHeadLookPacket.yaw = position.getYaw();
|
|
|
|
|
|
|
|
sendPacketToViewersAndSelf(entityHeadLookPacket);
|
|
|
|
|
|
|
|
} else if (positionChange) {
|
|
|
|
EntityPositionPacket entityPositionPacket = EntityPositionPacket.getPacket(getEntityId(),
|
|
|
|
position, new Position(cacheX, cacheY, cacheZ), isOnGround());
|
|
|
|
|
|
|
|
sendPacketToViewersAndSelf(entityPositionPacket);
|
|
|
|
|
2020-12-09 21:34:53 +01:00
|
|
|
refreshPosition(position.clone());
|
2020-11-26 12:46:49 +01:00
|
|
|
|
|
|
|
} else if (viewChange) {
|
|
|
|
// Yaw/Pitch
|
2020-05-27 23:13:13 +02:00
|
|
|
setView(position);
|
|
|
|
}
|
2020-11-26 12:46:49 +01:00
|
|
|
|
2020-05-27 22:06:22 +02:00
|
|
|
}
|
|
|
|
|
2020-11-26 12:46:49 +01:00
|
|
|
// Entity tick
|
2020-12-10 18:12:05 +01:00
|
|
|
{
|
2019-08-24 20:34:01 +02:00
|
|
|
|
2021-01-04 03:04:45 +01:00
|
|
|
// Cache the number of "gravity tick"
|
|
|
|
if (!onGround) {
|
|
|
|
gravityTickCount++;
|
|
|
|
} else {
|
|
|
|
gravityTickCount = 0;
|
|
|
|
}
|
|
|
|
|
2019-08-24 20:34:01 +02:00
|
|
|
// Velocity
|
2020-12-11 21:36:35 +01:00
|
|
|
boolean applyVelocity;
|
2020-11-29 16:50:57 +01:00
|
|
|
// Non-player entities with either velocity or gravity enabled
|
2021-01-27 19:20:52 +01:00
|
|
|
applyVelocity = !isNettyClient && (hasVelocity() || !hasNoGravity());
|
2020-11-29 16:50:57 +01:00
|
|
|
// Players with a velocity applied (client is responsible for gravity)
|
2021-01-27 19:20:52 +01:00
|
|
|
applyVelocity |= isNettyClient && hasVelocity();
|
2020-11-29 16:50:57 +01:00
|
|
|
|
2020-05-25 11:01:38 +02:00
|
|
|
if (applyVelocity) {
|
2020-05-02 23:34:09 +02:00
|
|
|
final float tps = MinecraftServer.TICK_PER_SECOND;
|
2021-01-25 13:47:13 +01:00
|
|
|
final double newX = position.getX() + velocity.getX() / tps;
|
|
|
|
final double newY = position.getY() + velocity.getY() / tps;
|
|
|
|
final double newZ = position.getZ() + velocity.getZ() / tps;
|
2020-05-02 23:34:09 +02:00
|
|
|
Position newPosition = new Position(newX, newY, newZ);
|
2020-05-17 01:42:07 +02:00
|
|
|
|
2021-01-04 03:04:45 +01:00
|
|
|
Vector newVelocityOut = new Vector();
|
2020-12-06 01:36:37 +01:00
|
|
|
|
2021-01-04 03:04:45 +01:00
|
|
|
// Gravity force
|
2021-01-30 04:44:44 +01:00
|
|
|
final double gravityY = !hasNoGravity() ? Math.min(
|
2021-01-25 19:33:53 +01:00
|
|
|
gravityDragPerTick + (gravityAcceleration * (double) gravityTickCount),
|
2021-01-27 19:35:53 +01:00
|
|
|
gravityTerminalVelocity) : 0;
|
2020-05-02 23:34:09 +02:00
|
|
|
|
2020-07-24 16:11:48 +02:00
|
|
|
final Vector deltaPos = new Vector(
|
2020-05-05 21:04:05 +02:00
|
|
|
getVelocity().getX() / tps,
|
2021-01-04 03:04:45 +01:00
|
|
|
(getVelocity().getY() - gravityY) / tps,
|
2020-05-05 21:04:05 +02:00
|
|
|
getVelocity().getZ() / tps
|
2020-05-02 23:34:09 +02:00
|
|
|
);
|
2021-01-04 03:04:45 +01:00
|
|
|
|
2021-02-22 12:42:46 +01:00
|
|
|
if (this.hasPhysics) {
|
|
|
|
this.onGround = CollisionUtils.handlePhysics(this, deltaPos, newPosition, newVelocityOut);
|
|
|
|
} else {
|
|
|
|
newVelocityOut = deltaPos;
|
|
|
|
}
|
2020-05-02 23:34:09 +02:00
|
|
|
|
2020-12-26 18:52:44 +01:00
|
|
|
// World border collision
|
|
|
|
final Position finalVelocityPosition = CollisionUtils.applyWorldBorder(instance, position, newPosition);
|
|
|
|
final Chunk finalChunk = instance.getChunkAt(finalVelocityPosition);
|
|
|
|
|
|
|
|
// Entity shouldn't be updated when moving in an unloaded chunk
|
|
|
|
if (!ChunkUtils.isLoaded(finalChunk)) {
|
2020-08-13 20:43:45 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-01-11 08:44:38 +01:00
|
|
|
// Apply the position if changed
|
|
|
|
if (!newPosition.isSimilar(position)) {
|
2020-12-04 19:19:30 +01:00
|
|
|
refreshPosition(finalVelocityPosition);
|
2020-05-27 01:04:39 +02:00
|
|
|
}
|
|
|
|
|
2020-05-02 23:34:09 +02:00
|
|
|
|
2020-12-04 19:19:30 +01:00
|
|
|
// Update velocity
|
2021-01-27 19:35:53 +01:00
|
|
|
if (hasVelocity() || !newVelocityOut.isZero()) {
|
2020-12-04 19:19:30 +01:00
|
|
|
this.velocity.copy(newVelocityOut);
|
|
|
|
this.velocity.multiply(tps);
|
|
|
|
|
|
|
|
float drag;
|
|
|
|
if (onGround) {
|
|
|
|
final BlockPosition blockPosition = position.toBlockPosition();
|
2020-12-26 18:52:44 +01:00
|
|
|
final CustomBlock customBlock = finalChunk.getCustomBlock(
|
|
|
|
blockPosition.getX(),
|
|
|
|
blockPosition.getY(),
|
|
|
|
blockPosition.getZ());
|
2020-12-04 19:19:30 +01:00
|
|
|
if (customBlock != null) {
|
|
|
|
// Custom drag
|
|
|
|
drag = customBlock.getDrag(instance, blockPosition);
|
|
|
|
} else {
|
|
|
|
// Default ground drag
|
|
|
|
drag = 0.5f;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Stop player velocity
|
2021-01-27 19:20:52 +01:00
|
|
|
if (isNettyClient) {
|
2021-01-04 03:04:45 +01:00
|
|
|
this.velocity.zero();
|
2020-12-04 19:19:30 +01:00
|
|
|
}
|
2020-08-06 04:54:02 +02:00
|
|
|
} else {
|
2020-12-04 19:19:30 +01:00
|
|
|
drag = 0.98f; // air drag
|
2020-08-06 04:54:02 +02:00
|
|
|
}
|
2020-05-25 11:01:38 +02:00
|
|
|
|
2020-12-04 19:19:30 +01:00
|
|
|
this.velocity.setX(velocity.getX() * drag);
|
|
|
|
this.velocity.setZ(velocity.getZ() * drag);
|
2021-01-27 19:35:53 +01:00
|
|
|
|
|
|
|
if (velocity.equals(new Vector())) {
|
|
|
|
this.velocity.zero();
|
|
|
|
}
|
2019-08-24 20:34:01 +02:00
|
|
|
}
|
2020-09-11 00:14:17 +02:00
|
|
|
|
2020-12-04 19:19:30 +01:00
|
|
|
// Synchronization and packets...
|
2021-01-27 19:20:52 +01:00
|
|
|
if (!isNettyClient) {
|
|
|
|
sendSynchronization();
|
|
|
|
}
|
2020-12-11 20:17:33 +01:00
|
|
|
// Verify if velocity packet has to be sent
|
2021-01-27 19:20:52 +01:00
|
|
|
if (hasVelocity() || (!isNettyClient && gravityTickCount > 0)) {
|
|
|
|
sendPacketToViewersAndSelf(getVelocityPacket());
|
2019-08-27 20:49:11 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-05 15:55:21 +02:00
|
|
|
// handle block contacts
|
2020-11-13 21:57:45 +01:00
|
|
|
// TODO do not call every tick (it is pretty expensive)
|
2020-07-24 16:11:48 +02:00
|
|
|
final int minX = (int) Math.floor(boundingBox.getMinX());
|
|
|
|
final int maxX = (int) Math.ceil(boundingBox.getMaxX());
|
|
|
|
final int minY = (int) Math.floor(boundingBox.getMinY());
|
|
|
|
final int maxY = (int) Math.ceil(boundingBox.getMaxY());
|
|
|
|
final int minZ = (int) Math.floor(boundingBox.getMinZ());
|
|
|
|
final int maxZ = (int) Math.ceil(boundingBox.getMaxZ());
|
|
|
|
final BlockPosition tmpPosition = new BlockPosition(0, 0, 0); // allow reuse
|
2020-05-05 15:55:21 +02:00
|
|
|
for (int y = minY; y <= maxY; y++) {
|
|
|
|
for (int x = minX; x <= maxX; x++) {
|
|
|
|
for (int z = minZ; z <= maxZ; z++) {
|
2020-08-13 20:43:45 +02:00
|
|
|
final Chunk chunk = instance.getChunkAt(x, z);
|
|
|
|
if (!ChunkUtils.isLoaded(chunk))
|
2020-05-17 01:42:07 +02:00
|
|
|
continue;
|
2020-08-13 20:43:45 +02:00
|
|
|
|
|
|
|
final CustomBlock customBlock = chunk.getCustomBlock(x, y, z);
|
2020-05-05 21:04:05 +02:00
|
|
|
if (customBlock != null) {
|
2020-05-05 15:55:21 +02:00
|
|
|
tmpPosition.setX(x);
|
|
|
|
tmpPosition.setY(y);
|
|
|
|
tmpPosition.setZ(z);
|
|
|
|
// checks that we are actually in the block, and not just here because of a rounding error
|
2020-05-05 21:04:05 +02:00
|
|
|
if (boundingBox.intersect(tmpPosition)) {
|
2020-05-05 15:55:21 +02:00
|
|
|
// TODO: replace with check with custom block bounding box
|
|
|
|
customBlock.handleContact(instance, tmpPosition, this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-27 20:33:08 +02:00
|
|
|
handleVoid();
|
|
|
|
|
2020-03-29 20:58:30 +02:00
|
|
|
// Call the abstract update method
|
2020-06-01 00:51:31 +02:00
|
|
|
update(time);
|
2019-08-24 20:34:01 +02:00
|
|
|
|
2020-05-07 16:00:52 +02:00
|
|
|
ticks++;
|
|
|
|
callEvent(EntityTickEvent.class, tickEvent); // reuse tickEvent to avoid recreating it each tick
|
2021-01-03 00:23:41 +01:00
|
|
|
|
|
|
|
// remove expired effects
|
|
|
|
{
|
|
|
|
this.effects.removeIf(timedPotion -> {
|
|
|
|
final long potionTime = (long) timedPotion.getPotion().getDuration() * MinecraftServer.TICK_MS;
|
|
|
|
// Remove if the potion should be expired
|
|
|
|
if (time >= timedPotion.getStartingTime() + potionTime) {
|
|
|
|
// Send the packet that the potion should no longer be applied
|
|
|
|
timedPotion.getPotion().sendRemovePacket(this);
|
|
|
|
callEvent(EntityPotionRemoveEvent.class, new EntityPotionRemoveEvent(
|
|
|
|
this,
|
|
|
|
timedPotion.getPotion()
|
|
|
|
));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
});
|
|
|
|
}
|
2020-05-31 00:39:56 +02:00
|
|
|
}
|
2020-05-07 16:00:52 +02:00
|
|
|
|
2020-05-31 00:39:56 +02:00
|
|
|
// Scheduled synchronization
|
2021-03-01 11:09:08 +01:00
|
|
|
if (!CooldownUtils.hasCooldown(time, lastAbsoluteSynchronizationTime, getSynchronizationCooldown())) {
|
2020-11-18 05:13:49 +01:00
|
|
|
this.lastAbsoluteSynchronizationTime = time;
|
2020-05-31 00:39:56 +02:00
|
|
|
sendSynchronization();
|
2019-08-19 17:04:19 +02:00
|
|
|
}
|
2019-08-23 23:55:09 +02:00
|
|
|
|
2020-12-02 21:28:36 +01:00
|
|
|
if (shouldRemove() && !MinecraftServer.isStopping()) {
|
2019-08-23 23:55:09 +02:00
|
|
|
remove();
|
|
|
|
}
|
2019-08-19 17:04:19 +02:00
|
|
|
}
|
|
|
|
|
2020-05-07 16:00:52 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Gets the number of ticks this entity has been active for.
|
2020-05-08 06:54:33 +02:00
|
|
|
*
|
2020-05-29 23:17:14 +02:00
|
|
|
* @return the number of ticks this entity has been active for
|
2020-05-07 16:00:52 +02:00
|
|
|
*/
|
|
|
|
public long getAliveTicks() {
|
|
|
|
return ticks;
|
|
|
|
}
|
|
|
|
|
2020-04-27 20:33:08 +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)) {
|
2020-04-27 20:33:08 +02:00
|
|
|
remove();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-24 11:19:54 +02:00
|
|
|
@NotNull
|
2020-05-08 06:54:33 +02:00
|
|
|
@Override
|
2020-10-06 04:06:59 +02:00
|
|
|
public Map<Class<? extends Event>, Collection<EventCallback>> getEventCallbacksMap() {
|
|
|
|
return eventCallbacks;
|
2020-09-20 20:11:46 +02:00
|
|
|
}
|
|
|
|
|
2021-02-03 17:11:06 +01:00
|
|
|
@NotNull
|
|
|
|
@Override
|
|
|
|
public Collection<EventCallback<?>> getExtensionCallbacks(String extension) {
|
|
|
|
return extensionCallbacks.computeIfAbsent(extension, e -> new CopyOnWriteArrayList<>());
|
|
|
|
}
|
|
|
|
|
2020-05-15 18:03:28 +02:00
|
|
|
/**
|
2020-11-14 23:24:16 +01:00
|
|
|
* Each entity has an unique id (server-wide) which will change after a restart.
|
2020-05-15 18:03:28 +02:00
|
|
|
*
|
|
|
|
* @return the unique entity id
|
2020-11-15 08:03:33 +01:00
|
|
|
* @see Entity#getEntity(int) to retrive an entity based on its id
|
2020-05-15 18:03:28 +02:00
|
|
|
*/
|
2019-08-10 08:44:35 +02:00
|
|
|
public int getEntityId() {
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
2020-05-29 23:17:14 +02:00
|
|
|
/**
|
2020-10-22 12:21:50 +02:00
|
|
|
* Returns the entity type.
|
2020-05-29 23:17:14 +02:00
|
|
|
*
|
2020-10-22 12:21:50 +02:00
|
|
|
* @return the entity type
|
2020-05-29 23:17:14 +02:00
|
|
|
*/
|
2021-02-25 08:38:53 +01:00
|
|
|
@NotNull
|
2020-08-03 18:45:58 +02:00
|
|
|
public EntityType getEntityType() {
|
2019-08-19 17:04:19 +02:00
|
|
|
return entityType;
|
|
|
|
}
|
|
|
|
|
2020-05-29 23:17:14 +02:00
|
|
|
/**
|
2020-10-22 12:21:50 +02:00
|
|
|
* Gets the entity {@link UUID}.
|
2020-05-29 23:17:14 +02:00
|
|
|
*
|
2020-10-22 12:21:50 +02:00
|
|
|
* @return the entity unique id
|
2020-05-29 23:17:14 +02:00
|
|
|
*/
|
2020-10-24 10:46:23 +02:00
|
|
|
@NotNull
|
2019-08-10 08:44:35 +02:00
|
|
|
public UUID getUuid() {
|
|
|
|
return uuid;
|
|
|
|
}
|
|
|
|
|
2020-06-21 22:11:56 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Changes the internal entity UUID, mostly unsafe.
|
2020-06-21 22:11:56 +02:00
|
|
|
*
|
|
|
|
* @param uuid the new entity uuid
|
|
|
|
*/
|
2021-01-06 19:02:35 +01:00
|
|
|
public void setUuid(@NotNull UUID uuid) {
|
2021-01-03 00:23:41 +01:00
|
|
|
// Refresh internal map
|
|
|
|
Entity.entityByUuid.remove(this.uuid);
|
|
|
|
Entity.entityByUuid.put(uuid, this);
|
|
|
|
|
2020-06-21 22:11:56 +02:00
|
|
|
this.uuid = uuid;
|
|
|
|
}
|
|
|
|
|
2020-05-29 23:17:14 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Returns false just after instantiation, set to true after calling {@link #setInstance(Instance)}.
|
2020-05-29 23:17:14 +02:00
|
|
|
*
|
|
|
|
* @return true if the entity has been linked to an instance, false otherwise
|
|
|
|
*/
|
2019-08-10 08:44:35 +02:00
|
|
|
public boolean isActive() {
|
|
|
|
return isActive;
|
|
|
|
}
|
|
|
|
|
2020-05-29 23:17:14 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Is used to check collision with coordinates or other blocks/entities.
|
2020-05-29 23:17:14 +02:00
|
|
|
*
|
|
|
|
* @return the entity bounding box
|
|
|
|
*/
|
2020-10-24 10:46:23 +02:00
|
|
|
@NotNull
|
2019-08-30 01:17:46 +02:00
|
|
|
public BoundingBox getBoundingBox() {
|
|
|
|
return boundingBox;
|
|
|
|
}
|
|
|
|
|
2020-05-29 23:17:14 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Changes the internal entity bounding box.
|
2020-05-29 23:17:14 +02:00
|
|
|
* <p>
|
2020-10-15 21:16:31 +02:00
|
|
|
* WARNING: this does not change the entity hit-box which is client-side.
|
2021-01-25 19:33:53 +01:00
|
|
|
*
|
|
|
|
* @param x the bounding box X size
|
2020-05-29 23:17:14 +02:00
|
|
|
* @param y the bounding box Y size
|
|
|
|
* @param z the bounding box Z size
|
|
|
|
*/
|
2021-01-25 13:47:13 +01:00
|
|
|
public void setBoundingBox(double x, double y, double z) {
|
2019-08-30 01:17:46 +02:00
|
|
|
this.boundingBox = new BoundingBox(this, x, y, z);
|
|
|
|
}
|
2021-01-23 08:23:24 +01:00
|
|
|
|
2021-01-13 12:57:55 +01:00
|
|
|
/**
|
|
|
|
* Changes the internal entity bounding box.
|
|
|
|
* <p>
|
|
|
|
* WARNING: this does not change the entity hit-box which is client-side.
|
|
|
|
*
|
|
|
|
* @param boundingBox the new bounding box
|
|
|
|
*/
|
|
|
|
public void setBoundingBox(BoundingBox boundingBox) {
|
|
|
|
this.boundingBox = boundingBox;
|
|
|
|
}
|
2019-08-30 01:17:46 +02:00
|
|
|
|
2020-09-03 00:43:42 +02:00
|
|
|
/**
|
2020-10-22 12:21:50 +02:00
|
|
|
* Convenient method to get the entity current chunk.
|
2020-09-03 00:43:42 +02:00
|
|
|
*
|
2020-10-29 22:52:07 +01:00
|
|
|
* @return the entity chunk, can be null even if unlikely
|
2020-09-03 00:43:42 +02:00
|
|
|
*/
|
2020-10-29 22:52:07 +01:00
|
|
|
@Nullable
|
2020-09-03 00:43:42 +02:00
|
|
|
public Chunk getChunk() {
|
2020-11-18 05:13:49 +01:00
|
|
|
return instance.getChunkAt(position.getX(), position.getZ());
|
2020-09-03 00:43:42 +02:00
|
|
|
}
|
|
|
|
|
2020-05-29 23:17:14 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Gets the entity current instance.
|
2020-05-29 23:17:14 +02:00
|
|
|
*
|
2020-10-31 01:02:54 +01:00
|
|
|
* @return the entity instance, can be null if the entity doesn't have an instance yet
|
2020-05-29 23:17:14 +02:00
|
|
|
*/
|
2020-10-31 01:02:54 +01:00
|
|
|
@Nullable
|
2019-08-11 07:42:56 +02:00
|
|
|
public Instance getInstance() {
|
|
|
|
return instance;
|
|
|
|
}
|
|
|
|
|
2020-05-29 23:17:14 +02:00
|
|
|
/**
|
2021-02-25 11:56:10 +01:00
|
|
|
* Changes the entity instance, i.e. spawns it.
|
2020-05-29 23:17:14 +02:00
|
|
|
*
|
2021-02-25 15:48:48 +01:00
|
|
|
* @param instance the new instance of the entity
|
2021-02-25 11:56:10 +01:00
|
|
|
* @param spawnPosition the spawn position for the entity.
|
2020-08-15 13:32:36 +02:00
|
|
|
* @throws IllegalStateException if {@code instance} has not been registered in {@link InstanceManager}
|
2020-05-29 23:17:14 +02:00
|
|
|
*/
|
2021-02-25 11:56:10 +01:00
|
|
|
public void setInstance(@NotNull Instance instance, @NotNull Position spawnPosition) {
|
2020-08-15 13:32:36 +02:00
|
|
|
Check.stateCondition(!instance.isRegistered(),
|
2020-09-28 18:27:28 +02:00
|
|
|
"Instances need to be registered, please use InstanceManager#registerInstance or InstanceManager#registerSharedInstance");
|
2020-05-17 03:15:47 +02:00
|
|
|
|
2019-08-11 07:42:56 +02:00
|
|
|
if (this.instance != null) {
|
2020-10-31 01:02:54 +01:00
|
|
|
this.instance.UNSAFE_removeEntity(this);
|
2019-08-10 08:44:35 +02:00
|
|
|
}
|
2019-08-11 07:42:56 +02:00
|
|
|
|
2021-02-25 11:56:10 +01:00
|
|
|
this.position.set(spawnPosition);
|
|
|
|
this.lastX = this.position.getX();
|
|
|
|
this.lastY = this.position.getY();
|
|
|
|
this.lastZ = this.position.getZ();
|
2021-03-02 19:36:11 +01:00
|
|
|
this.lastYaw = this.position.getYaw();
|
|
|
|
this.lastPitch = this.position.getPitch();
|
2021-02-25 11:56:10 +01:00
|
|
|
|
2019-08-11 07:42:56 +02:00
|
|
|
this.isActive = true;
|
|
|
|
this.instance = instance;
|
2020-10-31 01:02:54 +01:00
|
|
|
instance.UNSAFE_addEntity(this);
|
2019-08-24 20:34:01 +02:00
|
|
|
spawn();
|
2020-09-26 21:24:10 +02:00
|
|
|
EntitySpawnEvent entitySpawnEvent = new EntitySpawnEvent(this, instance);
|
2020-05-05 21:04:05 +02:00
|
|
|
callEvent(EntitySpawnEvent.class, entitySpawnEvent);
|
2019-08-24 20:34:01 +02:00
|
|
|
}
|
|
|
|
|
2020-05-29 23:17:14 +02:00
|
|
|
/**
|
2021-02-25 11:56:10 +01:00
|
|
|
* Changes the entity instance.
|
|
|
|
*
|
|
|
|
* @param instance the new instance of the entity
|
|
|
|
* @throws NullPointerException if {@code instance} is null
|
|
|
|
* @throws IllegalStateException if {@code instance} has not been registered in {@link InstanceManager}
|
|
|
|
*/
|
|
|
|
public void setInstance(@NotNull Instance instance) {
|
|
|
|
setInstance(instance, this.position);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Gets the entity current velocity.
|
2020-05-29 23:17:14 +02:00
|
|
|
*
|
|
|
|
* @return the entity current velocity
|
|
|
|
*/
|
2020-10-24 10:46:23 +02:00
|
|
|
@NotNull
|
2019-08-25 20:03:43 +02:00
|
|
|
public Vector getVelocity() {
|
|
|
|
return velocity;
|
|
|
|
}
|
|
|
|
|
2020-05-29 23:17:14 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Changes the entity velocity and calls {@link EntityVelocityEvent}.
|
2020-05-29 23:17:14 +02:00
|
|
|
* <p>
|
2020-10-15 21:16:31 +02:00
|
|
|
* The final velocity can be cancelled or modified by the event.
|
2020-05-29 23:17:14 +02:00
|
|
|
*
|
|
|
|
* @param velocity the new entity velocity
|
|
|
|
*/
|
2020-10-24 10:46:23 +02:00
|
|
|
public void setVelocity(@NotNull Vector velocity) {
|
2020-05-14 18:57:44 +02:00
|
|
|
EntityVelocityEvent entityVelocityEvent = new EntityVelocityEvent(this, velocity);
|
|
|
|
callCancellableEvent(EntityVelocityEvent.class, entityVelocityEvent, () -> {
|
|
|
|
this.velocity.copy(entityVelocityEvent.getVelocity());
|
2020-08-09 05:13:36 +02:00
|
|
|
sendPacketToViewersAndSelf(getVelocityPacket());
|
2020-05-14 18:57:44 +02:00
|
|
|
});
|
2019-08-10 08:44:35 +02:00
|
|
|
}
|
|
|
|
|
2020-05-29 23:17:14 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Gets if the entity currently has a velocity applied.
|
2020-08-06 07:42:00 +02:00
|
|
|
*
|
2020-05-29 23:17:14 +02:00
|
|
|
* @return true if velocity is not set to 0
|
|
|
|
*/
|
|
|
|
public boolean hasVelocity() {
|
2021-01-27 19:35:53 +01:00
|
|
|
return !velocity.isZero();
|
2020-05-29 23:17:14 +02:00
|
|
|
}
|
|
|
|
|
2020-12-06 01:36:37 +01:00
|
|
|
/**
|
|
|
|
* Gets the gravity drag per tick.
|
|
|
|
*
|
|
|
|
* @return the gravity drag per tick in block
|
|
|
|
*/
|
2021-01-25 19:33:53 +01:00
|
|
|
public double getGravityDragPerTick() {
|
2020-12-06 01:36:37 +01:00
|
|
|
return gravityDragPerTick;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the gravity acceleration.
|
|
|
|
*
|
|
|
|
* @return the gravity acceleration in block
|
|
|
|
*/
|
2021-01-25 19:33:53 +01:00
|
|
|
public double getGravityAcceleration() {
|
2020-12-06 01:36:37 +01:00
|
|
|
return gravityAcceleration;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the maximum gravity velocity.
|
|
|
|
*
|
|
|
|
* @return the maximum gravity velocity in block
|
|
|
|
*/
|
2021-01-25 19:33:53 +01:00
|
|
|
public double getGravityTerminalVelocity() {
|
2020-12-06 01:36:37 +01:00
|
|
|
return gravityTerminalVelocity;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the number of tick this entity has been applied gravity.
|
|
|
|
*
|
|
|
|
* @return the number of tick of which gravity has been consequently applied
|
|
|
|
*/
|
2020-12-11 20:17:33 +01:00
|
|
|
public int getGravityTickCount() {
|
2020-12-06 01:36:37 +01:00
|
|
|
return gravityTickCount;
|
|
|
|
}
|
|
|
|
|
2020-05-29 23:17:14 +02:00
|
|
|
/**
|
2020-10-16 09:12:31 +02:00
|
|
|
* Changes the gravity of the entity.
|
2020-05-29 23:17:14 +02:00
|
|
|
*
|
2020-12-06 01:36:37 +01:00
|
|
|
* @param gravityDragPerTick the gravity drag per tick in block
|
|
|
|
* @param gravityAcceleration the gravity acceleration in block
|
|
|
|
* @param gravityTerminalVelocity the gravity terminal velocity (maximum) in block
|
|
|
|
* @see <a href="https://minecraft.gamepedia.com/Entity#Motion_of_entities">Entities motion</a>
|
2020-05-29 23:17:14 +02:00
|
|
|
*/
|
2021-01-25 21:30:56 +01:00
|
|
|
public void setGravity(double gravityDragPerTick, double gravityAcceleration, double gravityTerminalVelocity) {
|
2019-08-27 20:49:11 +02:00
|
|
|
this.gravityDragPerTick = gravityDragPerTick;
|
2020-12-06 01:36:37 +01:00
|
|
|
this.gravityAcceleration = gravityAcceleration;
|
|
|
|
this.gravityTerminalVelocity = gravityTerminalVelocity;
|
2019-08-27 20:49:11 +02:00
|
|
|
}
|
|
|
|
|
2020-05-29 23:17:14 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Gets the distance between two entities.
|
2020-05-29 23:17:14 +02:00
|
|
|
*
|
|
|
|
* @param entity the entity to get the distance from
|
|
|
|
* @return the distance between this and {@code entity}
|
|
|
|
*/
|
2021-01-25 13:47:13 +01:00
|
|
|
public double getDistance(@NotNull Entity entity) {
|
2019-08-21 16:50:52 +02:00
|
|
|
return getPosition().getDistance(entity.getPosition());
|
2019-08-19 17:04:19 +02:00
|
|
|
}
|
|
|
|
|
2021-02-22 06:46:37 +01:00
|
|
|
/**
|
|
|
|
* Gets the distance squared between two entities.
|
|
|
|
*
|
|
|
|
* @param entity the entity to get the distance from
|
|
|
|
* @return the distance squared between this and {@code entity}
|
|
|
|
*/
|
|
|
|
public double getDistanceSquared(@NotNull Entity entity) {
|
|
|
|
return getPosition().getDistanceSquared(entity.getPosition());
|
|
|
|
}
|
|
|
|
|
2020-05-29 23:17:14 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Gets the entity vehicle or null.
|
2020-05-29 23:17:14 +02:00
|
|
|
*
|
|
|
|
* @return the entity vehicle, or null if there is not any
|
|
|
|
*/
|
2020-10-24 10:46:23 +02:00
|
|
|
@Nullable
|
2019-08-27 05:23:25 +02:00
|
|
|
public Entity getVehicle() {
|
|
|
|
return vehicle;
|
|
|
|
}
|
|
|
|
|
2020-05-29 23:17:14 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Adds a new passenger to this entity.
|
2020-05-29 23:17:14 +02:00
|
|
|
*
|
|
|
|
* @param entity the new passenger
|
|
|
|
* @throws NullPointerException if {@code entity} is null
|
|
|
|
* @throws IllegalStateException if {@link #getInstance()} returns null
|
|
|
|
*/
|
2020-10-24 10:46:23 +02:00
|
|
|
public void addPassenger(@NotNull Entity entity) {
|
2020-05-23 04:20:01 +02:00
|
|
|
Check.stateCondition(instance == null, "You need to set an instance using Entity#setInstance");
|
|
|
|
|
2019-08-27 05:23:25 +02:00
|
|
|
if (entity.getVehicle() != null) {
|
|
|
|
entity.getVehicle().removePassenger(entity);
|
|
|
|
}
|
|
|
|
|
2019-08-20 17:41:07 +02:00
|
|
|
this.passengers.add(entity);
|
|
|
|
entity.vehicle = this;
|
2019-08-26 00:29:40 +02:00
|
|
|
|
2019-08-30 01:17:46 +02:00
|
|
|
sendPacketToViewersAndSelf(getPassengersPacket());
|
2019-08-27 05:23:25 +02:00
|
|
|
}
|
2019-08-26 00:29:40 +02:00
|
|
|
|
2020-05-29 23:17:14 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Removes a passenger to this entity.
|
2020-05-29 23:17:14 +02:00
|
|
|
*
|
|
|
|
* @param entity the passenger to remove
|
|
|
|
* @throws NullPointerException if {@code entity} is null
|
|
|
|
* @throws IllegalStateException if {@link #getInstance()} returns null
|
|
|
|
*/
|
2020-10-24 10:46:23 +02:00
|
|
|
public void removePassenger(@NotNull Entity entity) {
|
2020-05-23 04:20:01 +02:00
|
|
|
Check.stateCondition(instance == null, "You need to set an instance using Entity#setInstance");
|
|
|
|
|
2020-05-24 19:59:50 +02:00
|
|
|
if (!passengers.remove(entity))
|
2019-08-27 05:23:25 +02:00
|
|
|
return;
|
|
|
|
entity.vehicle = null;
|
2019-08-30 01:17:46 +02:00
|
|
|
sendPacketToViewersAndSelf(getPassengersPacket());
|
2019-08-20 17:41:07 +02:00
|
|
|
}
|
|
|
|
|
2020-05-29 23:17:14 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Gets if the entity has any passenger.
|
2020-05-29 23:17:14 +02:00
|
|
|
*
|
|
|
|
* @return true if the entity has any passenger, false otherwise
|
|
|
|
*/
|
2019-08-20 17:41:07 +02:00
|
|
|
public boolean hasPassenger() {
|
|
|
|
return !passengers.isEmpty();
|
|
|
|
}
|
|
|
|
|
2020-05-24 19:59:50 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Gets the entity passengers.
|
2020-05-29 23:17:14 +02:00
|
|
|
*
|
2020-05-24 19:59:50 +02:00
|
|
|
* @return an unmodifiable list containing all the entity passengers
|
|
|
|
*/
|
2020-10-24 10:46:23 +02:00
|
|
|
@NotNull
|
2019-08-20 17:41:07 +02:00
|
|
|
public Set<Entity> getPassengers() {
|
|
|
|
return Collections.unmodifiableSet(passengers);
|
|
|
|
}
|
|
|
|
|
2020-10-24 10:46:23 +02:00
|
|
|
@NotNull
|
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
|
|
|
}
|
|
|
|
|
2020-05-24 19:59:50 +02:00
|
|
|
/**
|
2020-10-22 12:21:50 +02:00
|
|
|
* Entity statuses can be found <a href="https://wiki.vg/Entity_statuses">here</a>.
|
2020-05-24 19:59:50 +02:00
|
|
|
*
|
|
|
|
* @param status the status to trigger
|
|
|
|
*/
|
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
|
|
|
}
|
|
|
|
|
2020-05-24 19:59:50 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Gets if the entity is on fire.
|
2020-05-29 23:17:14 +02:00
|
|
|
*
|
2020-05-24 19:59:50 +02:00
|
|
|
* @return true if the entity is in fire, false otherwise
|
|
|
|
*/
|
|
|
|
public boolean isOnFire() {
|
2021-02-23 17:18:53 +01:00
|
|
|
return this.entityMeta.isOnFire();
|
2020-05-24 19:59:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Sets the entity in fire visually.
|
2020-05-24 19:59:50 +02:00
|
|
|
* <p>
|
|
|
|
* WARNING: if you want to apply damage or specify a duration,
|
2020-10-15 21:16:31 +02:00
|
|
|
* see {@link LivingEntity#setFireForDuration(int, TimeUnit)}.
|
2020-05-24 19:59:50 +02:00
|
|
|
*
|
|
|
|
* @param fire should the entity be set in fire
|
|
|
|
*/
|
2019-08-20 22:40:57 +02:00
|
|
|
public void setOnFire(boolean fire) {
|
2021-02-23 17:18:53 +01:00
|
|
|
this.entityMeta.setOnFire(fire);
|
2021-01-30 04:44:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets if the entity is sneaking.
|
|
|
|
* <p>
|
|
|
|
* WARNING: this can be bypassed by hacked client, this is only what the client told the server.
|
|
|
|
*
|
|
|
|
* @return true if the player is sneaking
|
|
|
|
*/
|
|
|
|
public boolean isSneaking() {
|
2021-02-23 17:18:53 +01:00
|
|
|
return this.entityMeta.isSneaking();
|
2021-01-30 04:44:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Makes the entity sneak.
|
|
|
|
* <p>
|
|
|
|
* WARNING: this will not work for the client itself.
|
|
|
|
*
|
|
|
|
* @param sneaking true to make the entity sneak
|
|
|
|
*/
|
|
|
|
public void setSneaking(boolean sneaking) {
|
|
|
|
setPose(sneaking ? Pose.SNEAKING : Pose.STANDING);
|
2021-02-23 17:18:53 +01:00
|
|
|
this.entityMeta.setSneaking(sneaking);
|
2021-01-30 04:44:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets if the player is sprinting.
|
|
|
|
* <p>
|
|
|
|
* WARNING: this can be bypassed by hacked client, this is only what the client told the server.
|
|
|
|
*
|
|
|
|
* @return true if the player is sprinting
|
|
|
|
*/
|
|
|
|
public boolean isSprinting() {
|
2021-02-23 17:18:53 +01:00
|
|
|
return this.entityMeta.isSprinting();
|
2021-01-30 04:44:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Makes the entity sprint.
|
|
|
|
* <p>
|
|
|
|
* WARNING: this will not work on the client itself.
|
|
|
|
*
|
|
|
|
* @param sprinting true to make the entity sprint
|
|
|
|
*/
|
|
|
|
public void setSprinting(boolean sprinting) {
|
2021-02-23 17:18:53 +01:00
|
|
|
this.entityMeta.setSprinting(sprinting);
|
2019-08-23 15:37:38 +02:00
|
|
|
}
|
|
|
|
|
2020-05-29 23:17:14 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Gets if the entity is invisible or not.
|
2020-05-29 23:17:14 +02:00
|
|
|
*
|
|
|
|
* @return true if the entity is invisible, false otherwise
|
|
|
|
*/
|
2020-05-26 00:07:35 +02:00
|
|
|
public boolean isInvisible() {
|
2021-02-23 17:18:53 +01:00
|
|
|
return this.entityMeta.isInvisible();
|
2020-05-26 00:07:35 +02:00
|
|
|
}
|
|
|
|
|
2020-05-29 23:17:14 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Changes the internal invisible value and send a {@link EntityMetaDataPacket}
|
|
|
|
* to make visible or invisible the entity to its viewers.
|
2020-05-29 23:17:14 +02:00
|
|
|
*
|
|
|
|
* @param invisible true to set the entity invisible, false otherwise
|
|
|
|
*/
|
2020-05-26 00:07:35 +02:00
|
|
|
public void setInvisible(boolean invisible) {
|
2021-02-23 17:18:53 +01:00
|
|
|
this.entityMeta.setInvisible(invisible);
|
2020-05-26 00:07:35 +02:00
|
|
|
}
|
|
|
|
|
2020-05-24 19:59:50 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Gets if the entity is glowing or not.
|
2020-05-29 23:17:14 +02:00
|
|
|
*
|
2020-05-24 19:59:50 +02:00
|
|
|
* @return true if the entity is glowing, false otherwise
|
|
|
|
*/
|
2019-08-27 20:49:11 +02:00
|
|
|
public boolean isGlowing() {
|
2021-02-23 17:18:53 +01:00
|
|
|
return this.entityMeta.isHasGlowingEffect();
|
2019-08-27 20:49:11 +02:00
|
|
|
}
|
|
|
|
|
2020-05-29 23:17:14 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Sets or remove the entity glowing effect.
|
2020-05-29 23:17:14 +02:00
|
|
|
*
|
|
|
|
* @param glowing true to make the entity glows, false otherwise
|
|
|
|
*/
|
|
|
|
public void setGlowing(boolean glowing) {
|
2021-02-23 17:18:53 +01:00
|
|
|
this.entityMeta.setHasGlowingEffect(glowing);
|
2021-01-30 04:44:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the current entity pose.
|
|
|
|
*
|
|
|
|
* @return the entity pose
|
|
|
|
*/
|
|
|
|
@NotNull
|
|
|
|
public Pose getPose() {
|
2021-02-23 17:18:53 +01:00
|
|
|
return this.entityMeta.getPose();
|
2021-01-30 04:44:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Changes the entity pose.
|
|
|
|
* <p>
|
|
|
|
* The internal {@code crouched} and {@code swimming} field will be
|
|
|
|
* updated accordingly.
|
|
|
|
*
|
|
|
|
* @param pose the new entity pose
|
|
|
|
*/
|
|
|
|
@NotNull
|
|
|
|
public void setPose(@NotNull Pose pose) {
|
2021-02-23 17:18:53 +01:00
|
|
|
this.entityMeta.setPose(pose);
|
2020-05-29 23:17:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Gets the entity custom name.
|
2020-05-29 23:17:14 +02:00
|
|
|
*
|
|
|
|
* @return the custom name of the entity, null if there is not
|
|
|
|
*/
|
2021-01-30 04:44:44 +01:00
|
|
|
@Nullable
|
2020-12-31 19:50:26 +01:00
|
|
|
public JsonMessage getCustomName() {
|
2021-02-23 17:18:53 +01:00
|
|
|
return this.entityMeta.getCustomName();
|
2020-05-26 00:07:35 +02:00
|
|
|
}
|
|
|
|
|
2020-05-29 23:17:14 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Changes the entity custom name.
|
2020-05-29 23:17:14 +02:00
|
|
|
*
|
|
|
|
* @param customName the custom name of the entity, null to remove it
|
|
|
|
*/
|
2021-01-30 04:44:44 +01:00
|
|
|
public void setCustomName(@Nullable JsonMessage customName) {
|
2021-02-23 17:18:53 +01:00
|
|
|
this.entityMeta.setCustomName(customName);
|
2020-05-26 00:07:35 +02:00
|
|
|
}
|
|
|
|
|
2020-05-29 23:17:14 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Gets the custom name visible metadata field.
|
2020-05-29 23:17:14 +02:00
|
|
|
*
|
|
|
|
* @return true if the custom name is visible, false otherwise
|
|
|
|
*/
|
2020-05-26 00:07:35 +02:00
|
|
|
public boolean isCustomNameVisible() {
|
2021-02-23 17:18:53 +01:00
|
|
|
return this.entityMeta.isCustomNameVisible();
|
2020-05-26 00:07:35 +02:00
|
|
|
}
|
|
|
|
|
2020-05-29 23:17:14 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Changes the internal custom name visible field and send a {@link EntityMetaDataPacket}
|
|
|
|
* to update the entity state to its viewers.
|
2020-05-29 23:17:14 +02:00
|
|
|
*
|
|
|
|
* @param customNameVisible true to make the custom name visible, false otherwise
|
|
|
|
*/
|
2020-05-26 00:07:35 +02:00
|
|
|
public void setCustomNameVisible(boolean customNameVisible) {
|
2021-02-23 17:18:53 +01:00
|
|
|
this.entityMeta.setCustomNameVisible(customNameVisible);
|
2020-05-26 00:07:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public boolean isSilent() {
|
2021-02-23 17:18:53 +01:00
|
|
|
return this.entityMeta.isSilent();
|
2020-05-26 00:07:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public void setSilent(boolean silent) {
|
2021-02-23 17:18:53 +01:00
|
|
|
this.entityMeta.setSilent(silent);
|
2020-05-26 00:07:35 +02:00
|
|
|
}
|
|
|
|
|
2020-05-24 19:59:50 +02:00
|
|
|
/**
|
2021-01-30 04:44:44 +01:00
|
|
|
* Gets the noGravity metadata field.
|
2020-05-29 23:17:14 +02:00
|
|
|
*
|
2021-01-30 04:44:44 +01:00
|
|
|
* @return true if the entity ignore gravity, false otherwise
|
2020-05-24 19:59:50 +02:00
|
|
|
*/
|
2021-01-30 04:44:44 +01:00
|
|
|
public boolean hasNoGravity() {
|
2021-02-23 17:18:53 +01:00
|
|
|
return this.entityMeta.isHasNoGravity();
|
2019-08-20 22:40:57 +02:00
|
|
|
}
|
|
|
|
|
2020-05-24 19:59:50 +02:00
|
|
|
/**
|
2021-01-30 04:44:44 +01:00
|
|
|
* Changes the noGravity metadata field and change the gravity behaviour accordingly.
|
2020-05-29 23:17:14 +02:00
|
|
|
*
|
2021-01-30 04:44:44 +01:00
|
|
|
* @param noGravity should the entity ignore gravity
|
2020-05-24 19:59:50 +02:00
|
|
|
*/
|
2021-01-30 04:44:44 +01:00
|
|
|
public void setNoGravity(boolean noGravity) {
|
2021-02-23 17:18:53 +01:00
|
|
|
this.entityMeta.setHasNoGravity(noGravity);
|
2019-08-21 16:50:52 +02:00
|
|
|
}
|
|
|
|
|
2020-05-24 19:59:50 +02:00
|
|
|
/**
|
|
|
|
* Used to refresh the entity and its passengers position
|
|
|
|
* - put the entity in the right instance chunk
|
|
|
|
* - update the viewable chunks (load and unload)
|
|
|
|
* - add/remove players from the viewers list if {@link #isAutoViewable()} is enabled
|
|
|
|
* <p>
|
2020-10-12 06:41:47 +02:00
|
|
|
* WARNING: unsafe, should only be used internally in Minestom. Use {@link #teleport(Position)} instead.
|
2020-05-24 19:59:50 +02:00
|
|
|
*
|
|
|
|
* @param x new position X
|
|
|
|
* @param y new position Y
|
|
|
|
* @param z new position Z
|
|
|
|
*/
|
2021-01-25 13:47:13 +01:00
|
|
|
public void refreshPosition(double x, double y, double z) {
|
2019-08-21 16:50:52 +02:00
|
|
|
position.setX(x);
|
|
|
|
position.setY(y);
|
|
|
|
position.setZ(z);
|
2020-05-27 22:06:22 +02:00
|
|
|
this.cacheX = x;
|
|
|
|
this.cacheY = y;
|
|
|
|
this.cacheZ = z;
|
2019-08-11 07:42:56 +02:00
|
|
|
|
2020-05-23 14:04:53 +02:00
|
|
|
if (hasPassenger()) {
|
|
|
|
for (Entity passenger : getPassengers()) {
|
|
|
|
passenger.refreshPosition(x, y, z);
|
|
|
|
}
|
2019-08-30 01:17:46 +02:00
|
|
|
}
|
|
|
|
|
2020-07-22 17:39:48 +02:00
|
|
|
final Instance instance = getInstance();
|
2019-08-11 07:42:56 +02:00
|
|
|
if (instance != null) {
|
2020-07-22 17:39:48 +02:00
|
|
|
final Chunk lastChunk = instance.getChunkAt(lastX, lastZ);
|
|
|
|
final Chunk newChunk = instance.getChunkAt(x, z);
|
2020-12-12 00:07:41 +01:00
|
|
|
|
2020-12-12 00:10:42 +01:00
|
|
|
Check.notNull(lastChunk, "The entity " + getEntityId() + " was in an unloaded chunk at " + lastX + ";" + lastZ);
|
|
|
|
Check.notNull(newChunk, "The entity " + getEntityId() + " tried to move in an unloaded chunk at " + x + ";" + z);
|
|
|
|
|
2021-03-16 05:16:11 +01:00
|
|
|
if (lastChunk != newChunk) {
|
2020-11-22 13:56:36 +01:00
|
|
|
instance.switchEntityChunk(this, lastChunk, newChunk);
|
2020-11-04 19:14:04 +01:00
|
|
|
if (this instanceof Player) {
|
|
|
|
// Refresh player view
|
|
|
|
final Player player = (Player) this;
|
|
|
|
player.refreshVisibleChunks(newChunk);
|
|
|
|
player.refreshVisibleEntities(newChunk);
|
|
|
|
}
|
2019-08-11 07:42:56 +02:00
|
|
|
}
|
|
|
|
}
|
2021-01-23 15:43:06 +01:00
|
|
|
|
|
|
|
this.lastX = position.getX();
|
|
|
|
this.lastY = position.getY();
|
|
|
|
this.lastZ = position.getZ();
|
2019-08-10 08:44:35 +02:00
|
|
|
}
|
|
|
|
|
2020-05-29 23:17:14 +02:00
|
|
|
/**
|
|
|
|
* @param position the new position
|
2021-01-25 13:47:13 +01:00
|
|
|
* @see #refreshPosition(double, double, double)
|
2020-05-29 23:17:14 +02:00
|
|
|
*/
|
2020-10-24 10:46:23 +02:00
|
|
|
public void refreshPosition(@NotNull Position position) {
|
2019-08-27 20:49:11 +02:00
|
|
|
refreshPosition(position.getX(), position.getY(), position.getZ());
|
|
|
|
}
|
|
|
|
|
2020-05-27 20:55:33 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Updates the entity view internally.
|
2020-05-27 20:55:33 +02:00
|
|
|
* <p>
|
2020-10-15 21:16:31 +02:00
|
|
|
* Warning: you probably want to use {@link #setView(float, float)}.
|
2020-05-27 20:55:33 +02:00
|
|
|
*
|
|
|
|
* @param yaw the yaw
|
|
|
|
* @param pitch the pitch
|
|
|
|
*/
|
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);
|
2020-05-27 23:13:13 +02:00
|
|
|
this.cacheYaw = yaw;
|
|
|
|
this.cachePitch = pitch;
|
2019-08-20 22:40:57 +02:00
|
|
|
}
|
|
|
|
|
2020-05-15 18:03:28 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Gets the entity position.
|
2020-05-29 23:17:14 +02:00
|
|
|
*
|
2020-05-15 18:03:28 +02:00
|
|
|
* @return the current position of the entity
|
|
|
|
*/
|
2020-12-29 00:04:15 +01:00
|
|
|
@NotNull
|
2019-08-21 16:50:52 +02:00
|
|
|
public Position getPosition() {
|
|
|
|
return position;
|
2019-08-19 17:04:19 +02:00
|
|
|
}
|
|
|
|
|
2020-05-29 23:17:14 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Gets the entity eye height.
|
2020-12-29 20:42:41 +01:00
|
|
|
* <p>
|
|
|
|
* Default to {@link BoundingBox#getHeight()}x0.85
|
2020-05-29 23:17:14 +02:00
|
|
|
*
|
|
|
|
* @return the entity eye height
|
|
|
|
*/
|
2021-01-25 13:47:13 +01:00
|
|
|
public double getEyeHeight() {
|
|
|
|
return boundingBox.getHeight() * 0.85;
|
2020-05-21 00:33:56 +02:00
|
|
|
}
|
|
|
|
|
2021-01-03 00:23:41 +01:00
|
|
|
/**
|
|
|
|
* Gets all the potion effect of this entity.
|
|
|
|
*
|
|
|
|
* @return an unmodifiable list of all this entity effects
|
|
|
|
*/
|
|
|
|
@NotNull
|
|
|
|
public List<TimedPotion> getActiveEffects() {
|
|
|
|
return Collections.unmodifiableList(effects);
|
|
|
|
}
|
|
|
|
|
2021-02-22 06:00:49 +01:00
|
|
|
/**
|
|
|
|
* Adds an effect to an entity.
|
|
|
|
*
|
|
|
|
* @param potion The potion to add
|
|
|
|
*/
|
|
|
|
public void addEffect(@NotNull Potion potion) {
|
|
|
|
removeEffect(potion.getEffect());
|
|
|
|
this.effects.add(new TimedPotion(potion, System.currentTimeMillis()));
|
|
|
|
potion.sendAddPacket(this);
|
|
|
|
callEvent(EntityPotionAddEvent.class, new EntityPotionAddEvent(this, potion));
|
|
|
|
}
|
|
|
|
|
2021-01-03 00:23:41 +01:00
|
|
|
/**
|
|
|
|
* Removes effect from entity, if it has it.
|
|
|
|
*
|
|
|
|
* @param effect The effect to remove
|
|
|
|
*/
|
|
|
|
public void removeEffect(@NotNull PotionEffect effect) {
|
|
|
|
this.effects.removeIf(timedPotion -> {
|
|
|
|
if (timedPotion.getPotion().getEffect() == effect) {
|
|
|
|
timedPotion.getPotion().sendRemovePacket(this);
|
|
|
|
callEvent(EntityPotionRemoveEvent.class, new EntityPotionRemoveEvent(
|
|
|
|
this,
|
|
|
|
timedPotion.getPotion()
|
|
|
|
));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2021-02-22 06:00:49 +01:00
|
|
|
* Removes all the effects currently applied to the entity.
|
2021-01-03 00:23:41 +01:00
|
|
|
*/
|
2021-02-22 06:00:49 +01:00
|
|
|
public void clearEffects() {
|
|
|
|
for (TimedPotion timedPotion : effects) {
|
|
|
|
timedPotion.getPotion().sendRemovePacket(this);
|
|
|
|
callEvent(EntityPotionRemoveEvent.class, new EntityPotionRemoveEvent(
|
|
|
|
this,
|
|
|
|
timedPotion.getPotion()
|
|
|
|
));
|
|
|
|
}
|
|
|
|
this.effects.clear();
|
2021-01-03 00:23:41 +01:00
|
|
|
}
|
|
|
|
|
2020-05-15 18:03:28 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Removes the entity from the server immediately.
|
2020-10-12 06:41:47 +02:00
|
|
|
* <p>
|
2020-12-30 20:29:46 +01:00
|
|
|
* WARNING: this does not trigger {@link EntityDeathEvent}.
|
2020-05-15 18:03:28 +02:00
|
|
|
*/
|
2019-08-10 08:44:35 +02:00
|
|
|
public void remove() {
|
2020-08-09 08:16:54 +02:00
|
|
|
this.removed = true;
|
2019-08-10 08:44:35 +02:00
|
|
|
this.shouldRemove = true;
|
2020-12-30 20:29:46 +01:00
|
|
|
Entity.entityById.remove(id);
|
|
|
|
Entity.entityByUuid.remove(uuid);
|
2019-08-24 01:05:52 +02:00
|
|
|
if (instance != null)
|
2020-10-31 01:02:54 +01:00
|
|
|
instance.UNSAFE_removeEntity(this);
|
2019-08-10 08:44:35 +02:00
|
|
|
}
|
|
|
|
|
2020-08-09 08:16:54 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Gets if this entity has been removed.
|
2020-08-09 08:16:54 +02:00
|
|
|
*
|
|
|
|
* @return true if this entity is removed
|
|
|
|
*/
|
|
|
|
public boolean isRemoved() {
|
|
|
|
return removed;
|
|
|
|
}
|
|
|
|
|
2020-05-15 18:03:28 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Triggers {@link #remove()} after the specified time.
|
2020-05-15 18:03:28 +02:00
|
|
|
*
|
2020-11-24 22:56:12 +01:00
|
|
|
* @param delay the time before removing the entity,
|
|
|
|
* 0 to cancel the removing
|
2020-08-07 09:14:50 +02:00
|
|
|
* @param timeUnit the unit of the delay
|
2020-05-15 18:03:28 +02:00
|
|
|
*/
|
2020-10-24 10:46:23 +02:00
|
|
|
public void scheduleRemove(long delay, @NotNull TimeUnit timeUnit) {
|
2019-08-21 16:50:52 +02:00
|
|
|
if (delay == 0) { // Cancel the scheduled remove
|
|
|
|
this.scheduledRemoveTime = 0;
|
|
|
|
return;
|
|
|
|
}
|
2020-11-24 22:56:12 +01:00
|
|
|
this.scheduledRemoveTime = System.currentTimeMillis() + timeUnit.toMilliseconds(delay);
|
2019-08-20 22:40:57 +02:00
|
|
|
}
|
|
|
|
|
2020-05-15 18:03:28 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Gets if the entity removal has been scheduled with {@link #scheduleRemove(long, TimeUnit)}.
|
2020-05-29 23:17:14 +02:00
|
|
|
*
|
2020-10-12 06:41:47 +02:00
|
|
|
* @return true if the entity removal has been scheduled
|
2020-05-15 18:03:28 +02:00
|
|
|
*/
|
2019-08-30 01:17:46 +02:00
|
|
|
public boolean isRemoveScheduled() {
|
|
|
|
return scheduledRemoveTime != 0;
|
|
|
|
}
|
|
|
|
|
2021-02-25 06:59:55 +01:00
|
|
|
@NotNull
|
|
|
|
protected Vector getVelocityForPacket() {
|
|
|
|
return this.velocity.clone().multiply(8000f / MinecraftServer.TICK_PER_SECOND);
|
|
|
|
}
|
|
|
|
|
2020-10-24 10:46:23 +02:00
|
|
|
@NotNull
|
2019-08-24 20:34:01 +02:00
|
|
|
protected EntityVelocityPacket getVelocityPacket() {
|
|
|
|
EntityVelocityPacket velocityPacket = new EntityVelocityPacket();
|
|
|
|
velocityPacket.entityId = getEntityId();
|
2021-02-25 06:59:55 +01:00
|
|
|
Vector velocity = getVelocityForPacket();
|
|
|
|
velocityPacket.velocityX = (short) velocity.getX();
|
|
|
|
velocityPacket.velocityY = (short) velocity.getY();
|
|
|
|
velocityPacket.velocityZ = (short) velocity.getZ();
|
2019-08-24 20:34:01 +02:00
|
|
|
return velocityPacket;
|
|
|
|
}
|
|
|
|
|
2020-05-15 18:03:28 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Gets an {@link EntityMetaDataPacket} sent when adding viewers. Used for synchronization.
|
2020-05-15 18:03:28 +02:00
|
|
|
*
|
|
|
|
* @return The {@link EntityMetaDataPacket} related to this entity
|
|
|
|
*/
|
2020-10-24 10:46:23 +02:00
|
|
|
@NotNull
|
2019-08-22 14:52:32 +02:00
|
|
|
public EntityMetaDataPacket getMetadataPacket() {
|
|
|
|
EntityMetaDataPacket metaDataPacket = new EntityMetaDataPacket();
|
|
|
|
metaDataPacket.entityId = getEntityId();
|
2021-01-30 04:44:44 +01:00
|
|
|
metaDataPacket.entries = metadata.getEntries();
|
2019-08-22 14:52:32 +02:00
|
|
|
return metaDataPacket;
|
|
|
|
}
|
|
|
|
|
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();
|
2021-01-23 08:23:24 +01:00
|
|
|
entityTeleportPacket.position = getPosition().clone();
|
2019-08-27 20:49:11 +02:00
|
|
|
entityTeleportPacket.onGround = isOnGround();
|
2019-08-24 20:34:01 +02:00
|
|
|
sendPacketToViewers(entityTeleportPacket);
|
2020-05-24 22:21:38 +02:00
|
|
|
}
|
2019-08-31 12:10:46 +02:00
|
|
|
|
2020-05-24 22:21:38 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Asks for a synchronization (position) to happen during next entity tick.
|
2020-05-24 22:21:38 +02:00
|
|
|
*/
|
|
|
|
public void askSynchronization() {
|
2020-11-18 05:13:49 +01:00
|
|
|
this.lastAbsoluteSynchronizationTime = 0;
|
2019-08-24 20:34:01 +02:00
|
|
|
}
|
|
|
|
|
2021-03-01 11:09:08 +01:00
|
|
|
/**
|
|
|
|
* Set custom cooldown for position synchronization.
|
|
|
|
*
|
|
|
|
* @param cooldown custom cooldown for position synchronization.
|
|
|
|
*/
|
|
|
|
public void setCustomSynchronizationCooldown(@Nullable UpdateOption cooldown) {
|
|
|
|
this.customSynchronizationCooldown = cooldown;
|
|
|
|
}
|
|
|
|
|
|
|
|
private UpdateOption getSynchronizationCooldown() {
|
|
|
|
if (this.customSynchronizationCooldown != null) {
|
|
|
|
return this.customSynchronizationCooldown;
|
|
|
|
}
|
|
|
|
return SYNCHRONIZATION_COOLDOWN;
|
|
|
|
}
|
|
|
|
|
2020-12-29 00:43:04 +01:00
|
|
|
public enum Pose {
|
2019-08-19 17:04:19 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|