Merge pull request #30 from fantasyorg/master

fix: packets send after entity spawned
This commit is contained in:
Tofaa 2025-06-04 01:55:20 +04:00 committed by GitHub
commit 2a15c882db
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 382 additions and 98 deletions

View file

@ -1,14 +1,9 @@
package me.tofaa.entitylib; package me.tofaa.entitylib;
import com.github.retrooper.packetevents.PacketEventsAPI; import com.github.retrooper.packetevents.PacketEventsAPI;
import com.github.retrooper.packetevents.protocol.entity.type.EntityType;
import com.github.retrooper.packetevents.protocol.player.UserProfile;
import com.github.retrooper.packetevents.protocol.world.Location;
import me.tofaa.entitylib.container.EntityContainer; import me.tofaa.entitylib.container.EntityContainer;
import me.tofaa.entitylib.tick.TickContainer; import me.tofaa.entitylib.tick.TickContainer;
import me.tofaa.entitylib.wrapper.WrapperEntity; import me.tofaa.entitylib.wrapper.WrapperEntity;
import me.tofaa.entitylib.wrapper.WrapperPlayer;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;

View file

@ -228,6 +228,7 @@ public class EntityMeta implements EntityMetadataProvider {
/** /**
* Annoying java 8 not letting me do OFFSET + amount in the method call so this is a workaround * Annoying java 8 not letting me do OFFSET + amount in the method call so this is a workaround
*
* @param value the value to offset * @param value the value to offset
* @param amount the amount to offset by * @param amount the amount to offset by
* @return the offset value * @return the offset value

View file

@ -25,11 +25,10 @@ import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Consumer; import java.util.function.Consumer;
public class WrapperEntity implements Tickable { public class WrapperEntity implements Tickable {
private final UUID uuid; private final UUID uuid;
private final int entityId; private final int entityId;
private EntityType entityType; private final EntityType entityType;
private EntityMeta entityMeta; private final EntityMeta entityMeta;
private boolean ticking; private boolean ticking;
protected Location location; protected Location location;
private Location preRidingLocation; private Location preRidingLocation;
@ -62,6 +61,7 @@ public class WrapperEntity implements Tickable {
public WrapperEntity(UUID uuid, EntityType entityType) { public WrapperEntity(UUID uuid, EntityType entityType) {
this(EntityLib.getPlatform().getEntityIdProvider().provide(uuid, entityType), uuid, entityType); this(EntityLib.getPlatform().getEntityIdProvider().provide(uuid, entityType), uuid, entityType);
} }
public WrapperEntity(EntityType entityType) { public WrapperEntity(EntityType entityType) {
this(EntityLib.getPlatform().getEntityUuidProvider().provide(entityType), entityType); this(EntityLib.getPlatform().getEntityUuidProvider().provide(entityType), entityType);
} }
@ -72,8 +72,10 @@ public class WrapperEntity implements Tickable {
public boolean spawn(Location location, EntityContainer parent) { public boolean spawn(Location location, EntityContainer parent) {
if (spawned) return false; if (spawned) return false;
this.location = location; this.location = location;
this.spawned = true; this.spawned = true;
sendPacketsToViewers( sendPacketsToViewers(
new WrapperPlayServerSpawnEntity( new WrapperPlayServerSpawnEntity(
entityId, entityId,
@ -88,9 +90,12 @@ public class WrapperEntity implements Tickable {
), ),
entityMeta.createPacket() entityMeta.createPacket()
); );
if (this instanceof WrapperLivingEntity) { if (this instanceof WrapperLivingEntity) {
sendPacketsToViewers(((WrapperLivingEntity)this).getAttributes().createPacket()); WrapperLivingEntity wrapperLivingEntity = (WrapperLivingEntity) this;
wrapperLivingEntity.createSpawnPackets().forEach(this::sendPacketsToViewers);
} }
this.parent = parent; this.parent = parent;
parent.addEntity(this); parent.addEntity(this);
return true; return true;
@ -101,7 +106,7 @@ public class WrapperEntity implements Tickable {
return SpawnPacketProvider.GENERAL.provide(this); return SpawnPacketProvider.GENERAL.provide(this);
} }
public boolean spawn(Location location) { public boolean spawn(@NotNull Location location) {
return spawn(location, EntityLib.getApi().getDefaultContainer()); return spawn(location, EntityLib.getApi().getDefaultContainer());
} }
@ -110,6 +115,7 @@ public class WrapperEntity implements Tickable {
if (entityMeta instanceof ObjectData) { if (entityMeta instanceof ObjectData) {
return ((ObjectData) entityMeta).getObjectData(); return ((ObjectData) entityMeta).getObjectData();
} }
return 0; return 0;
} }
@ -117,6 +123,7 @@ public class WrapperEntity implements Tickable {
public Optional<Vector3d> createVeloPacket() { public Optional<Vector3d> createVeloPacket() {
Optional<Vector3d> velocity; Optional<Vector3d> velocity;
double veloX = 0, veloY = 0, veloZ = 0; double veloX = 0, veloY = 0, veloZ = 0;
if (entityMeta instanceof ObjectData) { if (entityMeta instanceof ObjectData) {
ObjectData od = (ObjectData) entityMeta; ObjectData od = (ObjectData) entityMeta;
if (od.requiresVelocityPacketAtSpawn()) { if (od.requiresVelocityPacketAtSpawn()) {
@ -126,11 +133,13 @@ public class WrapperEntity implements Tickable {
veloZ = veloPacket.getVelocity().getZ(); veloZ = veloPacket.getVelocity().getZ();
} }
} }
if (veloX == 0 && veloY == 0 && veloZ == 0) { if (veloX == 0 && veloY == 0 && veloZ == 0) {
velocity = Optional.of(Vector3d.zero()); velocity = Optional.of(Vector3d.zero());
} else { } else {
velocity = Optional.of(new Vector3d(veloX, veloY, veloZ)); velocity = Optional.of(new Vector3d(veloX, veloY, veloZ));
} }
return velocity; return velocity;
} }
@ -141,37 +150,37 @@ public class WrapperEntity implements Tickable {
public void remove() { public void remove() {
if (parent != null) { if (parent != null) {
parent.removeEntity(this, true); parent.removeEntity(this, true);
} } else {
else {
despawn(); despawn();
} }
} }
public void despawn() { public void despawn() {
if (!spawned) return; if (!spawned) return;
spawned = false; spawned = false;
if (this instanceof WrapperPlayer) { if (this instanceof WrapperPlayer) {
WrapperPlayer p = (WrapperPlayer) this; WrapperPlayer p = (WrapperPlayer) this;
sendPacketsToViewers(p.tabListRemovePacket()); sendPacketsToViewers(p.tabListRemovePacket());
} }
sendPacketToViewers(new WrapperPlayServerDestroyEntities(entityId)); sendPacketToViewers(new WrapperPlayServerDestroyEntities(entityId));
} }
public void teleport(@NotNull Location location, boolean onGround) { public void teleport(@NotNull Location location, boolean onGround) {
if (!spawned) { if (!spawned) return;
return;
}
this.location = location; this.location = location;
this.onGround = onGround; this.onGround = onGround;
sendPacketToViewers(
new WrapperPlayServerEntityTeleport( sendPacketToViewers(new WrapperPlayServerEntityTeleport(
entityId, entityId,
location.getPosition(), location.getPosition(),
location.getYaw(), location.getYaw(),
location.getPitch(), location.getPitch(),
onGround onGround
) ));
);
} }
public void teleport(@NotNull Location location) { public void teleport(@NotNull Location location) {
@ -180,30 +189,38 @@ public class WrapperEntity implements Tickable {
/** /**
* Adds a viewer to the viewers set. The viewer will receive all packets and be informed of this addition * Adds a viewer to the viewers set. The viewer will receive all packets and be informed of this addition
*
* @param uuid the uuid of the user to add * @param uuid the uuid of the user to add
*/ */
public void addViewer(UUID uuid) { public void addViewer(@NotNull UUID uuid) {
if (!viewers.add(uuid)) { if (!viewers.add(uuid)) {
return; return;
} }
if (location == null) { if (location == null) {
if (EntityLib.getApi().getSettings().isDebugMode()) { if (EntityLib.getApi().getSettings().isDebugMode()) {
EntityLib.getPlatform().getLogger().warning("Location is null for entity " + entityId + ". Cannot spawn."); EntityLib.getPlatform().getLogger().warning("Location is null for entity " + entityId + ". Cannot spawn.");
} }
return; return;
} }
if (spawned) { if (spawned) {
if (this instanceof WrapperPlayer) { if (this instanceof WrapperPlayer) {
WrapperPlayer p = (WrapperPlayer) this; WrapperPlayer p = (WrapperPlayer) this;
sendPacket(uuid, p.tabListPacket()); sendPacket(uuid, p.tabListPacket());
} }
sendPacket(uuid, createSpawnPacket()); sendPacket(uuid, createSpawnPacket());
sendPacket(uuid, entityMeta.createPacket()); sendPacket(uuid, entityMeta.createPacket());
sendPacket(uuid, createPassengerPacket()); sendPacket(uuid, createPassengerPacket());
if (this instanceof WrapperLivingEntity) { if (this instanceof WrapperLivingEntity) {
sendPacket(uuid, ((WrapperLivingEntity)this).getAttributes().createPacket()); WrapperLivingEntity wrapperLivingEntity = (WrapperLivingEntity) this;
wrapperLivingEntity.createSpawnPackets().forEach(packetWrapper -> sendPacket(uuid, packetWrapper));
} }
} }
if (EntityLib.getApi().getSettings().isDebugMode()) { if (EntityLib.getApi().getSettings().isDebugMode()) {
EntityLib.getPlatform().getLogger().info("Added viewer " + uuid + " to entity " + entityId); EntityLib.getPlatform().getLogger().info("Added viewer " + uuid + " to entity " + entityId);
} }
@ -235,62 +252,70 @@ public class WrapperEntity implements Tickable {
); );
} }
public void addViewer(User user) { public void addViewer(@NotNull User user) {
addViewer(user.getUUID()); addViewer(user.getUUID());
} }
/** /**
* Adds a viewer silently into the viewers set. The viewer will not receive any packets or be informed of this addition * Adds a viewer silently into the viewers set. The viewer will not receive any packets or be informed of this addition
*
* @param uuid the uuid of the user to add * @param uuid the uuid of the user to add
*/ */
public void addViewerSilently(UUID uuid) { public void addViewerSilently(@NotNull UUID uuid) {
viewers.add(uuid); viewers.add(uuid);
} }
/** /**
* Adds a viewer silently into the viewers set. The viewer will not receive any packets or be informed of this addition * Adds a viewer silently into the viewers set. The viewer will not receive any packets or be informed of this addition
*
* @param user the user to add * @param user the user to add
*/ */
public void addViewerSilently(User user) { public void addViewerSilently(@NotNull User user) {
addViewerSilently(user.getUUID()); addViewerSilently(user.getUUID());
} }
/** /**
* Removes a viewer from the viewers set of this entity. The viewer will be informed of this removal and will no longer receive any packets * Removes a viewer from the viewers set of this entity. The viewer will be informed of this removal and will no longer receive any packets
*
* @param uuid the uuid of the user to remove * @param uuid the uuid of the user to remove
*/ */
public void removeViewer(UUID uuid) { public void removeViewer(@NotNull UUID uuid) {
if (!viewers.remove(uuid)) { if (!viewers.remove(uuid)) {
return; return;
} }
if (this instanceof WrapperPlayer) { if (this instanceof WrapperPlayer) {
WrapperPlayer p = (WrapperPlayer) this; WrapperPlayer p = (WrapperPlayer) this;
sendPacket(uuid, p.tabListRemovePacket()); sendPacket(uuid, p.tabListRemovePacket());
} }
sendPacket(uuid, new WrapperPlayServerDestroyEntities(entityId)); sendPacket(uuid, new WrapperPlayServerDestroyEntities(entityId));
} }
/** /**
* Removes a viewer from the viewers set of this entity. The viewer will be informed of this removal and will no longer receive any packets * Removes a viewer from the viewers set of this entity. The viewer will be informed of this removal and will no longer receive any packets
*
* @param user the user to remove * @param user the user to remove
*/ */
public void removeViewer(User user) { public void removeViewer(@NotNull User user) {
removeViewer(user.getUUID()); removeViewer(user.getUUID());
} }
/** /**
* removes a viewer silently into the viewers set. The viewer will not receive any packets or be informed of this removal * removes a viewer silently into the viewers set. The viewer will not receive any packets or be informed of this removal
*
* @param uuid of the user to remove * @param uuid of the user to remove
*/ */
public void removeViewerSilently(UUID uuid) { public void removeViewerSilently(@NotNull UUID uuid) {
viewers.remove(uuid); viewers.remove(uuid);
} }
/** /**
* removes a viewer silently into the viewers set. The viewer will not receive any packets or be informed of this removal * removes a viewer silently into the viewers set. The viewer will not receive any packets or be informed of this removal
*
* @param user the user to remove * @param user the user to remove
*/ */
public void removeViewerSilently(User user) { public void removeViewerSilently(@NotNull User user) {
removeViewerSilently(user.getUUID()); removeViewerSilently(user.getUUID());
} }
@ -298,11 +323,11 @@ public class WrapperEntity implements Tickable {
return onGround; return onGround;
} }
public Vector3d getVelocity() { public @NotNull Vector3d getVelocity() {
return velocity; return velocity;
} }
public void setVelocity(Vector3d velocity) { public void setVelocity(@NotNull Vector3d velocity) {
this.velocity = velocity; this.velocity = velocity;
sendPacketToViewers(getVelocityPacket()); sendPacketToViewers(getVelocityPacket());
} }
@ -331,7 +356,7 @@ public class WrapperEntity implements Tickable {
return entityId; return entityId;
} }
public EntityMeta getEntityMeta() { public @NotNull EntityMeta getEntityMeta() {
return entityMeta; return entityMeta;
} }
@ -339,12 +364,12 @@ public class WrapperEntity implements Tickable {
return metaClass.cast(entityMeta); return metaClass.cast(entityMeta);
} }
public <T extends EntityMeta> void consumeEntityMeta(@NotNull Class<T> metaClass, Consumer<T> consumer) { public <T extends EntityMeta> void consumeEntityMeta(@NotNull Class<T> metaClass, @NotNull Consumer<T> consumer) {
T meta = getEntityMeta(metaClass); T meta = getEntityMeta(metaClass);
consumer.accept(meta); consumer.accept(meta);
} }
public void consumeMeta(Consumer<EntityMeta> consumer) { public void consumeMeta(@NotNull Consumer<EntityMeta> consumer) {
consumer.accept(entityMeta); consumer.accept(entityMeta);
} }
@ -358,13 +383,14 @@ public class WrapperEntity implements Tickable {
/** /**
* Returns an unmodifiable set of the passengers of the entity. * Returns an unmodifiable set of the passengers of the entity.
*
* @return the passengers of the entity * @return the passengers of the entity
*/ */
public Set<Integer> getPassengers() { public @NotNull Set<Integer> getPassengers() {
return Collections.unmodifiableSet(passengers); return Collections.unmodifiableSet(passengers);
} }
public WrapperEntity getRiding() { public @Nullable WrapperEntity getRiding() {
return EntityLib.getApi().getEntity(riding); return EntityLib.getApi().getEntity(riding);
} }
@ -433,20 +459,22 @@ public class WrapperEntity implements Tickable {
new WrapperPlayServerEntityRotation(entityId, yaw, pitch, onGround), new WrapperPlayServerEntityRotation(entityId, yaw, pitch, onGround),
new WrapperPlayServerEntityHeadLook(entityId, yaw) new WrapperPlayServerEntityHeadLook(entityId, yaw)
); );
this.location.setYaw(yaw); this.location.setYaw(yaw);
this.location.setPitch(pitch); this.location.setPitch(pitch);
} }
public void rotateHead(Location location) { public void rotateHead(@NotNull Location location) {
rotateHead(location.getYaw(), location.getPitch()); rotateHead(location.getYaw(), location.getPitch());
} }
public void rotateHead(WrapperEntity entity) { public void rotateHead(@NotNull WrapperEntity entity) {
rotateHead(entity.getLocation()); rotateHead(entity.getLocation());
} }
public void refresh() { public void refresh() {
if (!spawned) return; if (!spawned) return;
sendPacketsToViewers(entityMeta.createPacket(), createPassengerPacket()); sendPacketsToViewers(entityMeta.createPacket(), createPassengerPacket());
} }
@ -457,13 +485,12 @@ public class WrapperEntity implements Tickable {
sendPacket(uuid, packet); sendPacket(uuid, packet);
sendPacket(uuid, new WrapperPlayServerBundle()); sendPacket(uuid, new WrapperPlayServerBundle());
}); });
} } else {
else {
viewers.forEach(uuid -> sendPacket(uuid, packet)); viewers.forEach(uuid -> sendPacket(uuid, packet));
} }
} }
public void sendPacketsToViewers(PacketWrapper<?>... wrappers) { public void sendPacketsToViewers(PacketWrapper<?> @NotNull ... wrappers) {
for (PacketWrapper<?> wrapper : wrappers) { for (PacketWrapper<?> wrapper : wrappers) {
sendPacketToViewers(wrapper); sendPacketToViewers(wrapper);
} }
@ -483,13 +510,16 @@ public class WrapperEntity implements Tickable {
private static void sendPacket(UUID user, PacketWrapper<?> wrapper) { private static void sendPacket(UUID user, PacketWrapper<?> wrapper) {
if (wrapper == null) return; if (wrapper == null) return;
Object channel = EntityLib.getApi().getPacketEvents().getProtocolManager().getChannel(user); Object channel = EntityLib.getApi().getPacketEvents().getProtocolManager().getChannel(user);
if (channel == null) { if (channel == null) {
if (EntityLib.getApi().getSettings().isDebugMode()) { if (EntityLib.getApi().getSettings().isDebugMode()) {
EntityLib.getPlatform().getLogger().warning("Failed to send packet to " + user + " because the channel was null. They may be disconnected/not online."); EntityLib.getPlatform().getLogger().warning("Failed to send packet to " + user + " because the channel was null. They may be disconnected/not online.");
} }
return; return;
} }
EntityLib.getApi().getPacketEvents().getProtocolManager().sendPacket(channel, wrapper); EntityLib.getApi().getPacketEvents().getProtocolManager().sendPacket(channel, wrapper);
} }
@ -504,12 +534,14 @@ public class WrapperEntity implements Tickable {
/** /**
* Adds a passenger to the entity. The passenger will be visible to all viewers of the entity. * Adds a passenger to the entity. The passenger will be visible to all viewers of the entity.
*
* @param passenger the entity id of the passenger * @param passenger the entity id of the passenger
*/ */
public void addPassenger(int passenger) { public void addPassenger(int passenger) {
if (passengers.contains(passenger)) { if (passengers.contains(passenger)) {
throw new IllegalArgumentException("Passenger already exists"); throw new IllegalArgumentException("Passenger already exists");
} }
passengers.add(passenger); passengers.add(passenger);
sendPacketToViewers(createPassengerPacket()); sendPacketToViewers(createPassengerPacket());
WrapperEntity e = EntityLib.getApi().getEntity(passenger); WrapperEntity e = EntityLib.getApi().getEntity(passenger);
@ -533,9 +565,10 @@ public class WrapperEntity implements Tickable {
/** /**
* Adds multiple passengers to the entity. The passengers will be visible to all viewers of the entity. * Adds multiple passengers to the entity. The passengers will be visible to all viewers of the entity.
*
* @param passengers the entity ids of the passengers * @param passengers the entity ids of the passengers
*/ */
public void addPassengers(int... passengers) { public void addPassengers(int @NotNull ... passengers) {
for (int passenger : passengers) { for (int passenger : passengers) {
addPassenger(passenger); addPassenger(passenger);
} }
@ -543,17 +576,19 @@ public class WrapperEntity implements Tickable {
/** /**
* Adds a passenger to the entity. The passenger will be visible to all viewers of the entity. * Adds a passenger to the entity. The passenger will be visible to all viewers of the entity.
*
* @param passenger the wrapper entity passenger * @param passenger the wrapper entity passenger
*/ */
public void addPassenger(WrapperEntity passenger) { public void addPassenger(@NotNull WrapperEntity passenger) {
addPassenger(passenger.getEntityId()); addPassenger(passenger.getEntityId());
} }
/** /**
* Adds multiple passengers to the entity. The passengers will be visible to all viewers of the entity. * Adds multiple passengers to the entity. The passengers will be visible to all viewers of the entity.
*
* @param passengers the wrapper entity passengers * @param passengers the wrapper entity passengers
*/ */
public void addPassengers(WrapperEntity... passengers) { public void addPassengers(WrapperEntity @NotNull ... passengers) {
for (WrapperEntity passenger : passengers) { for (WrapperEntity passenger : passengers) {
addPassenger(passenger); addPassenger(passenger);
} }
@ -561,12 +596,14 @@ public class WrapperEntity implements Tickable {
/** /**
* Removes a passenger from the entity. The passenger will be removed from the view of all viewers of the entity. * Removes a passenger from the entity. The passenger will be removed from the view of all viewers of the entity.
*
* @param passenger the entity id of the passenger * @param passenger the entity id of the passenger
*/ */
public void removePassenger(int passenger) { public void removePassenger(int passenger) {
if (!passengers.contains(passenger)) { if (!passengers.contains(passenger)) {
throw new IllegalArgumentException("Passenger does not exist"); throw new IllegalArgumentException("Passenger does not exist");
} }
passengers.remove(passenger); passengers.remove(passenger);
sendPacketToViewers(createPassengerPacket()); sendPacketToViewers(createPassengerPacket());
WrapperEntity e = EntityLib.getApi().getEntity(passenger); WrapperEntity e = EntityLib.getApi().getEntity(passenger);
@ -588,15 +625,16 @@ public class WrapperEntity implements Tickable {
* @param passenger the passenger wrapper entity * @param passenger the passenger wrapper entity
* @return true if the entity has the passenger, false otherwise * @return true if the entity has the passenger, false otherwise
*/ */
public boolean hasPassenger(WrapperEntity passenger) { public boolean hasPassenger(@NotNull WrapperEntity passenger) {
return hasPassenger(passenger.getEntityId()); return hasPassenger(passenger.getEntityId());
} }
/** /**
* Removes multiple passengers from the entity. The passengers will be removed from the view of all viewers of the entity. * Removes multiple passengers from the entity. The passengers will be removed from the view of all viewers of the entity.
*
* @param passengers the entity ids of the passengers * @param passengers the entity ids of the passengers
*/ */
public void removePassengers(int... passengers) { public void removePassengers(int @NotNull ... passengers) {
for (int passenger : passengers) { for (int passenger : passengers) {
removePassenger(passenger); removePassenger(passenger);
} }
@ -604,17 +642,19 @@ public class WrapperEntity implements Tickable {
/** /**
* Removes a passenger from the entity. The passenger will be removed from the view of all viewers of the entity. * Removes a passenger from the entity. The passenger will be removed from the view of all viewers of the entity.
*
* @param passenger the wrapper entity passenger * @param passenger the wrapper entity passenger
*/ */
public void removePassenger(WrapperEntity passenger) { public void removePassenger(@NotNull WrapperEntity passenger) {
removePassenger(passenger.getEntityId()); removePassenger(passenger.getEntityId());
} }
/** /**
* Removes multiple passengers from the entity. The passengers will be removed from the view of all viewers of the entity. * Removes multiple passengers from the entity. The passengers will be removed from the view of all viewers of the entity.
*
* @param passengers the wrapper entity passengers * @param passengers the wrapper entity passengers
*/ */
public void removePassengers(WrapperEntity... passengers) { public void removePassengers(WrapperEntity @NotNull ... passengers) {
for (WrapperEntity passenger : passengers) { for (WrapperEntity passenger : passengers) {
removePassenger(passenger); removePassenger(passenger);
} }
@ -631,11 +671,11 @@ public class WrapperEntity implements Tickable {
return Collections.unmodifiableSet(viewers); return Collections.unmodifiableSet(viewers);
} }
public boolean hasViewer(UUID uuid) { public boolean hasViewer(@NotNull UUID uuid) {
return viewers.contains(uuid); return viewers.contains(uuid);
} }
public boolean hasViewer(User user) { public boolean hasViewer(@NotNull User user) {
return hasViewer(user.getUUID()); return hasViewer(user.getUUID());
} }

View file

@ -0,0 +1,229 @@
package me.tofaa.entitylib.wrapper;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.potion.PotionType;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityEffect;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerRemoveEntityEffect;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class WrapperEntityPotionEffect {
private final WrapperLivingEntity entity;
private final Map<PotionType, WrapperPotionEffect> effects = new ConcurrentHashMap<>();
private boolean notifyChanges = true;
public WrapperEntityPotionEffect(WrapperLivingEntity entity) {
this.entity = entity;
}
/**
* @param type The type of the potion effect {@link com.github.retrooper.packetevents.protocol.potion.PotionTypes}
* @param amplifier The amplifier of the potion effect. The notchian client displays the number as (amplifier + 1)
* @param duration The duration of the potion effect in ticks, if set to -1 the client will display the effect as infinite
* @param ambient A
* @param visible A
* @param showIcons A
* @param factorData The factor data of the potion effect, if hasFactorData is false this will be ignored
*/
public void addPotionEffect(
PotionType type,
int amplifier,
int duration,
boolean ambient,
boolean visible,
boolean showIcons,
@Nullable NBTCompound factorData
) {
WrapperPotionEffect effect = new WrapperPotionEffect(type, amplifier, duration, ambient, visible, showIcons, factorData);
this.effects.put(type, effect);
this.entity.sendPacketToViewers(createEffectPacket(effect));
}
/**
* @param type The type of the potion effect {@link com.github.retrooper.packetevents.protocol.potion.PotionTypes}
* @param amplifier The amplifier of the potion effect. The notchian client displays the number as (amplifier + 1)
* @param duration The duration of the potion effect in ticks, if set to -1 the client will display the effect as infinite
* @param flags The bit flags of the potion effect see: <a href="https://minecraft.wiki/w/Java_Edition_protocol#Entity_Effect">https://minecraft.wiki/w/Java_Edition_protocol#Entity_Effect</a>
* @param factorData The factor data of the potion effect, if hasFactorData is false this will be ignored
*/
public void addPotionEffect(
PotionType type,
int amplifier,
int duration,
byte flags,
@Nullable NBTCompound factorData
) {
BitSet flagsBitSet = BitSet.valueOf(new byte[]{flags});
boolean ambient = flagsBitSet.get(0);
boolean visible = flagsBitSet.get(1);
boolean icons = flagsBitSet.get(2);
addPotionEffect(type, amplifier, duration, ambient, visible, icons, factorData);
}
/**
* @param type The type of the potion effect {@link com.github.retrooper.packetevents.protocol.potion.PotionTypes}
* @param amplifier The amplifier of the potion effect. The notchian client displays the number as (amplifier + 1)
* @param duration The duration of the potion effect in ticks, if set to -1 the client will display the effect as infinite
* @param flags The bit flags of the potion effect see: <a href="https://minecraft.wiki/w/Java_Edition_protocol#Entity_Effect">https://minecraft.wiki/w/Java_Edition_protocol#Entity_Effect</a>
* @param hasFactorData Whether the potion effect has factor data
* @param factorData The factor data of the potion effect, if hasFactorData is false this will be ignored
*/
public void addPotionEffect(
PotionType type,
int amplifier,
int duration,
byte flags,
boolean hasFactorData,
@Nullable NBTCompound factorData
) {
addPotionEffect(type, amplifier, duration, flags, hasFactorData ? factorData : null);
}
/**
* Adds a potion effect to the entity.
* EntityLib will not keep track of the potions you give or what you do with them,
* this simply sends the packet to the viewers of the entity.
*
* @param type The type of the potion effect {@link com.github.retrooper.packetevents.protocol.potion.PotionTypes}
* @param amplifier The amplifier of the potion effect. The notchian client displays the number as (amplifier + 1)
* @param duration The duration of the potion effect in ticks, if set to -1 the client will display the effect as infinite
* @param flags The bit flags of the potion effect see: <a href="https://minecraft.wiki/w/Java_Edition_protocol#Entity_Effect">https://minecraft.wiki/w/Java_Edition_protocol#Entity_Effect</a>
*/
public void addPotionEffect(
PotionType type,
int amplifier,
int duration,
byte flags
) {
addPotionEffect(type, amplifier, duration, flags, false, null);
}
public void removePotionEffect(@NotNull PotionType potionType) {
if (this.effects.remove(potionType) != null) {
this.entity.sendPacketsToViewers(createRemoveEffectPacket(potionType));
}
}
public void clearPotionEffects() {
new ArrayList<>(this.effects.keySet()).forEach(this::removePotionEffect);
}
public @NotNull List<WrapperPlayServerEntityEffect> createEffectPackets() {
List<WrapperPlayServerEntityEffect> packets = new ArrayList<>();
this.effects.forEach((potionType, effect) -> packets.add(createEffectPacket(effect)));
return packets;
}
public @NotNull WrapperPlayServerEntityEffect createEffectPacket(@NotNull WrapperPotionEffect effect) {
PotionType potionType = effect.getPotionType();
int amplifier = effect.getAmplifier();
int duration = effect.getDuration();
boolean ambient = effect.isAmbient();
boolean visible = effect.isVisible();
boolean icons = effect.hasIcons();
NBTCompound factorData = effect.getFactorData();
int flags = 0;
flags |= ambient ? 1 : 0; // Bit 0 para ambient
flags |= visible ? (1 << 1) : 0; // Bit 1 para visible
flags |= icons ? (1 << 2) : 0; // Bit 2 para icons
WrapperPlayServerEntityEffect wrapperPlayServerEntityEffect = new WrapperPlayServerEntityEffect(
0,
null,
0,
0,
(byte) flags
);
wrapperPlayServerEntityEffect.setEntityId(this.entity.getEntityId());
wrapperPlayServerEntityEffect.setPotionType(potionType);
wrapperPlayServerEntityEffect.setEffectAmplifier(amplifier);
wrapperPlayServerEntityEffect.setEffectDurationTicks(duration);
wrapperPlayServerEntityEffect.setFactorData(factorData);
return wrapperPlayServerEntityEffect;
}
public @NotNull WrapperPlayServerRemoveEntityEffect createRemoveEffectPacket(@NotNull PotionType potionType) {
return new WrapperPlayServerRemoveEntityEffect(this.entity.getEntityId(), potionType);
}
public void refresh() {
if (notifyChanges) {
new ArrayList<>(this.effects.values()).forEach(effect -> {
WrapperPlayServerEntityEffect wrapperPlayServerEntityEffect = createEffectPacket(effect);
this.entity.sendPacketToViewers(wrapperPlayServerEntityEffect);
});
}
}
public boolean isNotifyingChanges() {
return notifyChanges;
}
public void setNotifyChanges(boolean notifyChanges) {
this.notifyChanges = notifyChanges;
refresh();
}
public static class WrapperPotionEffect {
private final PotionType potionType;
private final int amplifier;
private final int duration;
private final boolean ambient;
private final boolean visible;
private final boolean icons;
private final @Nullable NBTCompound factorData;
private WrapperPotionEffect(PotionType potionType, int amplifier, int duration, boolean ambient, boolean visible, boolean icons, @Nullable NBTCompound factorData) {
this.potionType = potionType;
this.amplifier = amplifier;
this.duration = duration;
this.ambient = ambient;
this.visible = visible;
this.icons = icons;
this.factorData = factorData;
}
public PotionType getPotionType() {
return potionType;
}
public int getAmplifier() {
return amplifier;
}
public int getDuration() {
return duration;
}
public boolean isAmbient() {
return ambient;
}
public boolean isVisible() {
return visible;
}
public boolean hasIcons() {
return icons;
}
public @Nullable NBTCompound getFactorData() {
return factorData;
}
}
}

View file

@ -4,25 +4,34 @@ import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.entity.type.EntityType; import com.github.retrooper.packetevents.protocol.entity.type.EntityType;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound; import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.potion.PotionType; import com.github.retrooper.packetevents.protocol.potion.PotionType;
import com.github.retrooper.packetevents.protocol.world.Location;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityAnimation; import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityAnimation;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityEffect; import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityEffect;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityEquipment;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerHurtAnimation; import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerHurtAnimation;
import me.tofaa.entitylib.EntityLib; import me.tofaa.entitylib.EntityLib;
import me.tofaa.entitylib.container.EntityContainer;
import me.tofaa.entitylib.meta.EntityMeta; import me.tofaa.entitylib.meta.EntityMeta;
import me.tofaa.entitylib.utils.VersionUtil; import me.tofaa.entitylib.utils.VersionUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID; import java.util.UUID;
public class WrapperLivingEntity extends WrapperEntity{ public class WrapperLivingEntity extends WrapperEntity {
private final WrapperEntityEquipment equipment; private final WrapperEntityEquipment equipment;
private final WrapperEntityAttributes attributes; private final WrapperEntityAttributes attributes;
private final WrapperEntityPotionEffect potionEffect;
public WrapperLivingEntity(int entityId, UUID uuid, EntityType entityType, EntityMeta entityMeta) { public WrapperLivingEntity(int entityId, UUID uuid, EntityType entityType, EntityMeta entityMeta) {
super(entityId, uuid, entityType, entityMeta); super(entityId, uuid, entityType, entityMeta);
this.equipment = new WrapperEntityEquipment(this); this.equipment = new WrapperEntityEquipment(this);
this.attributes = new WrapperEntityAttributes(this); this.attributes = new WrapperEntityAttributes(this);
this.potionEffect = new WrapperEntityPotionEffect(this);
} }
public WrapperLivingEntity(int entityId, UUID uuid, EntityType entityType) { public WrapperLivingEntity(int entityId, UUID uuid, EntityType entityType) {
@ -36,6 +45,7 @@ public class WrapperLivingEntity extends WrapperEntity{
public WrapperLivingEntity(UUID uuid, EntityType entityType) { public WrapperLivingEntity(UUID uuid, EntityType entityType) {
this(EntityLib.getPlatform().getEntityIdProvider().provide(uuid, entityType), uuid, entityType); this(EntityLib.getPlatform().getEntityIdProvider().provide(uuid, entityType), uuid, entityType);
} }
public WrapperLivingEntity(EntityType entityType) { public WrapperLivingEntity(EntityType entityType) {
this(EntityLib.getPlatform().getEntityUuidProvider().provide(entityType), entityType); this(EntityLib.getPlatform().getEntityUuidProvider().provide(entityType), entityType);
} }
@ -43,60 +53,74 @@ public class WrapperLivingEntity extends WrapperEntity{
@Override @Override
public void refresh() { public void refresh() {
super.refresh(); super.refresh();
equipment.refresh();
attributes.refresh(); this.equipment.refresh();
this.potionEffect.refresh();
this.attributes.refresh();
} }
public WrapperEntityAttributes getAttributes() { public WrapperEntityAttributes getAttributes() {
return attributes; return this.attributes;
} }
public WrapperEntityEquipment getEquipment() {
return this.equipment;
}
public WrapperEntityPotionEffect getPotionEffect() {
return this.potionEffect;
}
public @NotNull List<PacketWrapper<?>> createSpawnPackets() {
List<PacketWrapper<?>> packets = new ArrayList<>();
packets.add(getAttributes().createPacket());
packets.add(getEquipment().createPacket());
packets.addAll(getPotionEffect().createEffectPackets());
return packets;
}
/** /**
* Adds a potion effect to the entity. * Adds a potion effect to the entity.
* EntityLib will not keep track of the potions you give or what you do with them, * EntityLib will not keep track of the potions you give or what you do with them,
* this simply sends the packet to the viewers of the entity. * this simply sends the packet to the viewers of the entity.
* @param type The type of the potion effect {@link com.github.retrooper.packetevents.protocol.potion.PotionTypes} *
* @param amplifier The amplifier of the potion effect. The notchian client displays the number as (amplifier + 1) * @param type The type of the potion effect {@link com.github.retrooper.packetevents.protocol.potion.PotionTypes}
* @param duration The duration of the potion effect in ticks, if set to -1 the client will display the effect as infinite * @param amplifier The amplifier of the potion effect. The notchian client displays the number as (amplifier + 1)
* @param flags The bit flags of the potion effect see: https://wiki.vg/Protocol#Entity_Effect * @param duration The duration of the potion effect in ticks, if set to -1 the client will display the effect as infinite
* @param flags The bit flags of the potion effect see:
* <a href="https://minecraft.wiki/w/Java_Edition_protocol#Entity_Effect">https://minecraft.wiki/w/Java_Edition_protocol#Entity_Effect</a>
* @param hasFactorData Whether the potion effect has factor data * @param hasFactorData Whether the potion effect has factor data
* @param factorData The factor data of the potion effect, if hasFactorData is false this will be ignored * @param factorData The factor data of the potion effect, if hasFactorData is false this will be ignored
*/ */
public void addPotionEffect( public void addPotionEffect(
PotionType type, PotionType type,
int amplifier, int amplifier,
int duration, int duration,
byte flags, byte flags,
boolean hasFactorData, boolean hasFactorData,
@Nullable NBTCompound factorData @Nullable NBTCompound factorData
) { ) {
sendPacketToViewers( this.potionEffect.addPotionEffect(type, amplifier, duration, flags, hasFactorData, factorData);
new WrapperPlayServerEntityEffect(
getEntityId(),
type,
amplifier,
duration,
flags
)
);
} }
/** /**
* Adds a potion effect to the entity. * Adds a potion effect to the entity.
* EntityLib will not keep track of the potions you give or what you do with them, * EntityLib will not keep track of the potions you give or what you do with them,
* this simply sends the packet to the viewers of the entity. * this simply sends the packet to the viewers of the entity.
* @param type The type of the potion effect {@link com.github.retrooper.packetevents.protocol.potion.PotionTypes} *
* @param type The type of the potion effect {@link com.github.retrooper.packetevents.protocol.potion.PotionTypes}
* @param amplifier The amplifier of the potion effect. The notchian client displays the number as (amplifier + 1) * @param amplifier The amplifier of the potion effect. The notchian client displays the number as (amplifier + 1)
* @param duration The duration of the potion effect in ticks, if set to -1 the client will display the effect as infinite * @param duration The duration of the potion effect in ticks, if set to -1 the client will display the effect as infinite
* @param flags The bit flags of the potion effect see: https://wiki.vg/Protocol#Entity_Effect * @param flags The bit flags of the potion effect see:
* <a href="https://minecraft.wiki/w/Java_Edition_protocol#Entity_Effect">https://minecraft.wiki/w/Java_Edition_protocol#Entity_Effect</a>
*/ */
public void addPotionEffect( public void addPotionEffect(
PotionType type, PotionType type,
int amplifier, int amplifier,
int duration, int duration,
byte flags byte flags
) { ) {
addPotionEffect(type, amplifier, duration, flags, false, null); addPotionEffect(type, amplifier, duration, flags, false, null);
} }
@ -124,6 +148,7 @@ public class WrapperLivingEntity extends WrapperEntity{
/** /**
* Plays the hurt animation of the entity. * Plays the hurt animation of the entity.
*
* @param yaw The yaw of the entity when the hurt animation is played. * @param yaw The yaw of the entity when the hurt animation is played.
* For any entity other than a player it's safe to simply put 0, as it's not used. * For any entity other than a player it's safe to simply put 0, as it's not used.
* The yaw is only used on 1.19.4+. * The yaw is only used on 1.19.4+.
@ -152,8 +177,4 @@ public class WrapperLivingEntity extends WrapperEntity{
new WrapperPlayServerEntityAnimation(getEntityId(), type) new WrapperPlayServerEntityAnimation(getEntityId(), type)
); );
} }
public WrapperEntityEquipment getEquipment() {
return equipment;
}
} }

View file

@ -10,11 +10,12 @@ import me.tofaa.entitylib.wrapper.WrapperEntity;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.*; import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.UUID;
public abstract class AbstractEntityLibAPI<P, T> implements EntityLibAPI<T> { public abstract class AbstractEntityLibAPI<P, T> implements EntityLibAPI<T> {
protected final Platform<P> platform; protected final Platform<P> platform;
protected final PacketEventsAPI<?> packetEvents; protected final PacketEventsAPI<?> packetEvents;
protected final APIConfig settings; protected final APIConfig settings;
@ -36,7 +37,6 @@ public abstract class AbstractEntityLibAPI<P, T> implements EntityLibAPI<T> {
@Override @Override
public @Nullable WrapperEntity getEntity(@NotNull UUID uuid) { public @Nullable WrapperEntity getEntity(@NotNull UUID uuid) {
return defaultEntityContainer.getEntity(uuid); return defaultEntityContainer.getEntity(uuid);
} }
@Override @Override
@ -49,9 +49,8 @@ public abstract class AbstractEntityLibAPI<P, T> implements EntityLibAPI<T> {
return defaultEntityContainer; return defaultEntityContainer;
} }
@NotNull
@Override @Override
public APIConfig getSettings() { public @NotNull APIConfig getSettings() {
return settings; return settings;
} }
@ -60,9 +59,8 @@ public abstract class AbstractEntityLibAPI<P, T> implements EntityLibAPI<T> {
return packetEvents; return packetEvents;
} }
@NotNull
@Override @Override
public Collection<TickContainer<?, T>> getTickContainers() { public @NotNull Collection<TickContainer<?, T>> getTickContainers() {
return tickContainers; return tickContainers;
} }
} }