From 0868a756179b496e88356a90188087717d86ebc0 Mon Sep 17 00:00:00 2001 From: Felipe Paschoal Bergamo <64669985+felipepasc@users.noreply.github.com> Date: Tue, 1 Apr 2025 10:43:55 -0300 Subject: [PATCH 1/7] feat: potion effect controller system --- .../entitylib/wrapper/WrapperEntity.java | 142 +++++---- .../wrapper/WrapperEntityPotionEffect.java | 275 ++++++++++++++++++ .../wrapper/WrapperLivingEntity.java | 100 ++++--- 3 files changed, 432 insertions(+), 85 deletions(-) create mode 100644 api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntityPotionEffect.java diff --git a/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntity.java b/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntity.java index a0c211f..ed5c58c 100644 --- a/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntity.java +++ b/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntity.java @@ -25,11 +25,10 @@ import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.Consumer; public class WrapperEntity implements Tickable { - private final UUID uuid; private final int entityId; - private EntityType entityType; - private EntityMeta entityMeta; + private final EntityType entityType; + private final EntityMeta entityMeta; private boolean ticking; protected Location location; private Location preRidingLocation; @@ -62,6 +61,7 @@ public class WrapperEntity implements Tickable { public WrapperEntity(UUID uuid, EntityType entityType) { this(EntityLib.getPlatform().getEntityIdProvider().provide(uuid, entityType), uuid, entityType); } + public WrapperEntity(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) { if (spawned) return false; + this.location = location; this.spawned = true; + sendPacketsToViewers( new WrapperPlayServerSpawnEntity( entityId, @@ -88,9 +90,14 @@ public class WrapperEntity implements Tickable { ), entityMeta.createPacket() ); + if (this instanceof WrapperLivingEntity) { - sendPacketsToViewers(((WrapperLivingEntity)this).getAttributes().createPacket()); + WrapperLivingEntity wrapperLivingEntity = (WrapperLivingEntity) this; + wrapperLivingEntity.createSpawnPackets().forEach(packetWrapper -> { + sendPacket(uuid, packetWrapper); + }); } + this.parent = parent; parent.addEntity(this); return true; @@ -101,7 +108,7 @@ public class WrapperEntity implements Tickable { return SpawnPacketProvider.GENERAL.provide(this); } - public boolean spawn(Location location) { + public boolean spawn(@NotNull Location location) { return spawn(location, EntityLib.getApi().getDefaultContainer()); } @@ -110,6 +117,7 @@ public class WrapperEntity implements Tickable { if (entityMeta instanceof ObjectData) { return ((ObjectData) entityMeta).getObjectData(); } + return 0; } @@ -117,6 +125,7 @@ public class WrapperEntity implements Tickable { public Optional createVeloPacket() { Optional velocity; double veloX = 0, veloY = 0, veloZ = 0; + if (entityMeta instanceof ObjectData) { ObjectData od = (ObjectData) entityMeta; if (od.requiresVelocityPacketAtSpawn()) { @@ -126,11 +135,13 @@ public class WrapperEntity implements Tickable { veloZ = veloPacket.getVelocity().getZ(); } } + if (veloX == 0 && veloY == 0 && veloZ == 0) { velocity = Optional.of(Vector3d.zero()); } else { velocity = Optional.of(new Vector3d(veloX, veloY, veloZ)); } + return velocity; } @@ -141,37 +152,37 @@ public class WrapperEntity implements Tickable { public void remove() { if (parent != null) { parent.removeEntity(this, true); - } - else { + } else { despawn(); } } public void despawn() { if (!spawned) return; + spawned = false; + if (this instanceof WrapperPlayer) { WrapperPlayer p = (WrapperPlayer) this; sendPacketsToViewers(p.tabListRemovePacket()); } + sendPacketToViewers(new WrapperPlayServerDestroyEntities(entityId)); } public void teleport(@NotNull Location location, boolean onGround) { - if (!spawned) { - return; - } + if (!spawned) return; + this.location = location; this.onGround = onGround; - sendPacketToViewers( - new WrapperPlayServerEntityTeleport( - entityId, - location.getPosition(), - location.getYaw(), - location.getPitch(), - onGround - ) - ); + + sendPacketToViewers(new WrapperPlayServerEntityTeleport( + entityId, + location.getPosition(), + location.getYaw(), + location.getPitch(), + onGround + )); } public void teleport(@NotNull Location location) { @@ -180,30 +191,40 @@ public class WrapperEntity implements Tickable { /** * 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 */ - public void addViewer(UUID uuid) { + public void addViewer(@NotNull UUID uuid) { if (!viewers.add(uuid)) { return; } + if (location == null) { if (EntityLib.getApi().getSettings().isDebugMode()) { EntityLib.getPlatform().getLogger().warning("Location is null for entity " + entityId + ". Cannot spawn."); } + return; } + if (spawned) { if (this instanceof WrapperPlayer) { WrapperPlayer p = (WrapperPlayer) this; sendPacket(uuid, p.tabListPacket()); } + sendPacket(uuid, createSpawnPacket()); sendPacket(uuid, entityMeta.createPacket()); sendPacket(uuid, createPassengerPacket()); + 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()) { EntityLib.getPlatform().getLogger().info("Added viewer " + uuid + " to entity " + entityId); } @@ -235,62 +256,70 @@ public class WrapperEntity implements Tickable { ); } - public void addViewer(User user) { + public void addViewer(@NotNull User user) { addViewer(user.getUUID()); } /** * 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 */ - public void addViewerSilently(UUID uuid) { + public void addViewerSilently(@NotNull UUID 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 + * * @param user the user to add */ - public void addViewerSilently(User user) { + public void addViewerSilently(@NotNull User user) { 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 + * * @param uuid the uuid of the user to remove */ - public void removeViewer(UUID uuid) { + public void removeViewer(@NotNull UUID uuid) { if (!viewers.remove(uuid)) { return; } + if (this instanceof WrapperPlayer) { WrapperPlayer p = (WrapperPlayer) this; sendPacket(uuid, p.tabListRemovePacket()); } + 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 + * * @param user the user to remove */ - public void removeViewer(User user) { + public void removeViewer(@NotNull User user) { removeViewer(user.getUUID()); } /** * 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 */ - public void removeViewerSilently(UUID uuid) { + public void removeViewerSilently(@NotNull UUID 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 + * * @param user the user to remove */ - public void removeViewerSilently(User user) { + public void removeViewerSilently(@NotNull User user) { removeViewerSilently(user.getUUID()); } @@ -298,11 +327,11 @@ public class WrapperEntity implements Tickable { return onGround; } - public Vector3d getVelocity() { + public @NotNull Vector3d getVelocity() { return velocity; } - public void setVelocity(Vector3d velocity) { + public void setVelocity(@NotNull Vector3d velocity) { this.velocity = velocity; sendPacketToViewers(getVelocityPacket()); } @@ -331,7 +360,7 @@ public class WrapperEntity implements Tickable { return entityId; } - public EntityMeta getEntityMeta() { + public @NotNull EntityMeta getEntityMeta() { return entityMeta; } @@ -339,12 +368,12 @@ public class WrapperEntity implements Tickable { return metaClass.cast(entityMeta); } - public void consumeEntityMeta(@NotNull Class metaClass, Consumer consumer) { + public void consumeEntityMeta(@NotNull Class metaClass, @NotNull Consumer consumer) { T meta = getEntityMeta(metaClass); consumer.accept(meta); } - public void consumeMeta(Consumer consumer) { + public void consumeMeta(@NotNull Consumer consumer) { consumer.accept(entityMeta); } @@ -358,13 +387,14 @@ public class WrapperEntity implements Tickable { /** * Returns an unmodifiable set of the passengers of the entity. + * * @return the passengers of the entity */ - public Set getPassengers() { + public @NotNull Set getPassengers() { return Collections.unmodifiableSet(passengers); } - public WrapperEntity getRiding() { + public @Nullable WrapperEntity getRiding() { return EntityLib.getApi().getEntity(riding); } @@ -433,20 +463,22 @@ public class WrapperEntity implements Tickable { new WrapperPlayServerEntityRotation(entityId, yaw, pitch, onGround), new WrapperPlayServerEntityHeadLook(entityId, yaw) ); + this.location.setYaw(yaw); this.location.setPitch(pitch); } - public void rotateHead(Location location) { + public void rotateHead(@NotNull Location location) { rotateHead(location.getYaw(), location.getPitch()); } - public void rotateHead(WrapperEntity entity) { + public void rotateHead(@NotNull WrapperEntity entity) { rotateHead(entity.getLocation()); } public void refresh() { if (!spawned) return; + sendPacketsToViewers(entityMeta.createPacket(), createPassengerPacket()); } @@ -457,13 +489,12 @@ public class WrapperEntity implements Tickable { sendPacket(uuid, packet); sendPacket(uuid, new WrapperPlayServerBundle()); }); - } - else { + } else { viewers.forEach(uuid -> sendPacket(uuid, packet)); } } - public void sendPacketsToViewers(PacketWrapper... wrappers) { + public void sendPacketsToViewers(PacketWrapper @NotNull ... wrappers) { for (PacketWrapper wrapper : wrappers) { sendPacketToViewers(wrapper); } @@ -483,13 +514,16 @@ public class WrapperEntity implements Tickable { private static void sendPacket(UUID user, PacketWrapper wrapper) { if (wrapper == null) return; + Object channel = EntityLib.getApi().getPacketEvents().getProtocolManager().getChannel(user); if (channel == null) { 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."); } + return; } + EntityLib.getApi().getPacketEvents().getProtocolManager().sendPacket(channel, wrapper); } @@ -504,12 +538,14 @@ public class WrapperEntity implements Tickable { /** * Adds a passenger to the entity. The passenger will be visible to all viewers of the entity. + * * @param passenger the entity id of the passenger */ public void addPassenger(int passenger) { if (passengers.contains(passenger)) { throw new IllegalArgumentException("Passenger already exists"); } + passengers.add(passenger); sendPacketToViewers(createPassengerPacket()); WrapperEntity e = EntityLib.getApi().getEntity(passenger); @@ -533,9 +569,10 @@ public class WrapperEntity implements Tickable { /** * Adds multiple passengers to the entity. The passengers will be visible to all viewers of the entity. + * * @param passengers the entity ids of the passengers */ - public void addPassengers(int... passengers) { + public void addPassengers(int @NotNull ... passengers) { for (int passenger : passengers) { addPassenger(passenger); } @@ -543,17 +580,19 @@ public class WrapperEntity implements Tickable { /** * Adds a passenger to the entity. The passenger will be visible to all viewers of the entity. + * * @param passenger the wrapper entity passenger */ - public void addPassenger(WrapperEntity passenger) { + public void addPassenger(@NotNull WrapperEntity passenger) { addPassenger(passenger.getEntityId()); } /** * Adds multiple passengers to the entity. The passengers will be visible to all viewers of the entity. + * * @param passengers the wrapper entity passengers */ - public void addPassengers(WrapperEntity... passengers) { + public void addPassengers(WrapperEntity @NotNull ... passengers) { for (WrapperEntity passenger : passengers) { addPassenger(passenger); } @@ -561,12 +600,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. + * * @param passenger the entity id of the passenger */ public void removePassenger(int passenger) { if (!passengers.contains(passenger)) { throw new IllegalArgumentException("Passenger does not exist"); } + passengers.remove(passenger); sendPacketToViewers(createPassengerPacket()); WrapperEntity e = EntityLib.getApi().getEntity(passenger); @@ -588,15 +629,16 @@ public class WrapperEntity implements Tickable { * @param passenger the passenger wrapper entity * @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()); } /** * Removes multiple passengers from the entity. The passengers will be removed from the view of all viewers of the entity. + * * @param passengers the entity ids of the passengers */ - public void removePassengers(int... passengers) { + public void removePassengers(int @NotNull ... passengers) { for (int passenger : passengers) { removePassenger(passenger); } @@ -604,17 +646,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. + * * @param passenger the wrapper entity passenger */ - public void removePassenger(WrapperEntity passenger) { + public void removePassenger(@NotNull WrapperEntity passenger) { removePassenger(passenger.getEntityId()); } /** * Removes multiple passengers from the entity. The passengers will be removed from the view of all viewers of the entity. + * * @param passengers the wrapper entity passengers */ - public void removePassengers(WrapperEntity... passengers) { + public void removePassengers(WrapperEntity @NotNull ... passengers) { for (WrapperEntity passenger : passengers) { removePassenger(passenger); } @@ -631,11 +675,11 @@ public class WrapperEntity implements Tickable { return Collections.unmodifiableSet(viewers); } - public boolean hasViewer(UUID uuid) { + public boolean hasViewer(@NotNull UUID uuid) { return viewers.contains(uuid); } - public boolean hasViewer(User user) { + public boolean hasViewer(@NotNull User user) { return hasViewer(user.getUUID()); } diff --git a/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntityPotionEffect.java b/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntityPotionEffect.java new file mode 100644 index 0000000..f6c123f --- /dev/null +++ b/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntityPotionEffect.java @@ -0,0 +1,275 @@ +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 me.tofaa.entitylib.tick.Tickable; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +public class WrapperEntityPotionEffect implements Tickable { + private final WrapperLivingEntity entity; + private final Map effects = new ConcurrentHashMap<>(); + + private boolean notifyChanges = true; + private boolean ticking = 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 + ) { + this.effects.put(type, new WrapperPotionEffect( + type, + amplifier, + duration, + ambient, + visible, + showIcons, + 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: https://minecraft.wiki/w/Java_Edition_protocol#Entity_Effect + * @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: https://minecraft.wiki/w/Java_Edition_protocol#Entity_Effect + * @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: https://minecraft.wiki/w/Java_Edition_protocol#Entity_Effect + */ + 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 createEffectPackets() { + List 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(); + long createdAt = effect.getCreatedAt(); + + int remainingDuration = duration; + if (duration != -1) { + long elapsedMillis = System.currentTimeMillis() - createdAt; + + int elapsedTicks = (int) (elapsedMillis / 50); + + remainingDuration = Math.max(duration - elapsedTicks, 0); + } + + WrapperPlayServerEntityEffect wrapperPlayServerEntityEffect = new WrapperPlayServerEntityEffect(0, null, 0, 0, (byte) 0); + + wrapperPlayServerEntityEffect.setEntityId(this.entity.getEntityId()); + wrapperPlayServerEntityEffect.setPotionType(potionType); + wrapperPlayServerEntityEffect.setEffectAmplifier(amplifier); + wrapperPlayServerEntityEffect.setEffectDurationTicks(remainingDuration); + wrapperPlayServerEntityEffect.setFactorData(factorData); + wrapperPlayServerEntityEffect.setAmbient(ambient); + wrapperPlayServerEntityEffect.setVisible(visible); + wrapperPlayServerEntityEffect.setShowIcon(icons); + + 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.sendPacketsToViewers(wrapperPlayServerEntityEffect); + }); + } + } + + public boolean isNotifyingChanges() { + return notifyChanges; + } + + public void setNotifyChanges(boolean notifyChanges) { + this.notifyChanges = notifyChanges; + refresh(); + } + + @Override + public boolean isTicking() { + return this.ticking; + } + + @Override + public void setTicking(boolean ticking) { + this.ticking = ticking; + } + + @Override + public void tick(long time) { + Set toRemove = new HashSet<>(); + + this.effects.values().forEach(effect -> { + PotionType potionType = effect.getPotionType(); + + int duration = effect.getDuration(); + if (duration <= -1) return; // Infinity effect + + long createdAt = effect.getCreatedAt(); + + long elapsedMillis = time - createdAt; + + int elapsedTicks = (int) (elapsedMillis / 50); + + int remainingDuration = duration - elapsedTicks; + if (remainingDuration <= 0) toRemove.add(potionType); + }); + + toRemove.forEach(this::removePotionEffect); + } + + 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 final long createdAt; + + 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; + this.createdAt = System.currentTimeMillis(); + } + + 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; + } + + public long getCreatedAt() { + return createdAt; + } + } +} diff --git a/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperLivingEntity.java b/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperLivingEntity.java index 170f3d6..465f840 100644 --- a/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperLivingEntity.java +++ b/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperLivingEntity.java @@ -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.nbt.NBTCompound; 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.WrapperPlayServerEntityEffect; +import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityEquipment; import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerHurtAnimation; import me.tofaa.entitylib.EntityLib; +import me.tofaa.entitylib.container.EntityContainer; import me.tofaa.entitylib.meta.EntityMeta; import me.tofaa.entitylib.utils.VersionUtil; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.ArrayList; +import java.util.List; import java.util.UUID; -public class WrapperLivingEntity extends WrapperEntity{ - +public class WrapperLivingEntity extends WrapperEntity { private final WrapperEntityEquipment equipment; private final WrapperEntityAttributes attributes; + private final WrapperEntityPotionEffect potionEffect; public WrapperLivingEntity(int entityId, UUID uuid, EntityType entityType, EntityMeta entityMeta) { super(entityId, uuid, entityType, entityMeta); + this.equipment = new WrapperEntityEquipment(this); this.attributes = new WrapperEntityAttributes(this); + this.potionEffect = new WrapperEntityPotionEffect(this); } public WrapperLivingEntity(int entityId, UUID uuid, EntityType entityType) { @@ -36,6 +45,7 @@ public class WrapperLivingEntity extends WrapperEntity{ public WrapperLivingEntity(UUID uuid, EntityType entityType) { this(EntityLib.getPlatform().getEntityIdProvider().provide(uuid, entityType), uuid, entityType); } + public WrapperLivingEntity(EntityType entityType) { this(EntityLib.getPlatform().getEntityUuidProvider().provide(entityType), entityType); } @@ -43,60 +53,81 @@ public class WrapperLivingEntity extends WrapperEntity{ @Override public void refresh() { super.refresh(); - equipment.refresh(); - attributes.refresh(); + + this.equipment.refresh(); + this.potionEffect.refresh(); + this.attributes.refresh(); + } + + @Override + public void tick(long time) { + this.potionEffect.tick(time); + + super.tick(time); } public WrapperEntityAttributes getAttributes() { - return attributes; + return this.attributes; } + public WrapperEntityEquipment getEquipment() { + return this.equipment; + } + public WrapperEntityPotionEffect getPotionEffect() { + return this.potionEffect; + } + + public @NotNull List> createSpawnPackets() { + List> packets = new ArrayList<>(); + + packets.add(getAttributes().createPacket()); + packets.add(getEquipment().createPacket()); + packets.addAll(this.potionEffect.createEffectPackets()); + + return packets; + } /** * 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: https://wiki.vg/Protocol#Entity_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: + * https://minecraft.wiki/w/Java_Edition_protocol#Entity_Effect * @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( - PotionType type, - int amplifier, - int duration, - byte flags, - boolean hasFactorData, - @Nullable NBTCompound factorData + PotionType type, + int amplifier, + int duration, + byte flags, + boolean hasFactorData, + @Nullable NBTCompound factorData ) { - sendPacketToViewers( - new WrapperPlayServerEntityEffect( - getEntityId(), - type, - amplifier, - duration, - flags - ) - ); + this.potionEffect.addPotionEffect(type, amplifier, duration, flags, hasFactorData, factorData); } /** * 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 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: 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: + * https://minecraft.wiki/w/Java_Edition_protocol#Entity_Effect */ public void addPotionEffect( - PotionType type, - int amplifier, - int duration, - byte flags + PotionType type, + int amplifier, + int duration, + byte flags ) { addPotionEffect(type, amplifier, duration, flags, false, null); } @@ -124,6 +155,7 @@ public class WrapperLivingEntity extends WrapperEntity{ /** * Plays the hurt animation of the entity. + * * @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. * The yaw is only used on 1.19.4+. @@ -152,8 +184,4 @@ public class WrapperLivingEntity extends WrapperEntity{ new WrapperPlayServerEntityAnimation(getEntityId(), type) ); } - - public WrapperEntityEquipment getEquipment() { - return equipment; - } } From 35111c0149024926397c36036457df273571cfef Mon Sep 17 00:00:00 2001 From: Felipe Paschoal Bergamo <64669985+felipepasc@users.noreply.github.com> Date: Tue, 1 Apr 2025 11:25:33 -0300 Subject: [PATCH 2/7] fix(wrapper): streamline potion effect handling and packet creation --- .../entitylib/wrapper/WrapperEntity.java | 8 ++--- .../wrapper/WrapperEntityPotionEffect.java | 31 +++++++++++-------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntity.java b/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntity.java index ed5c58c..eb65d0c 100644 --- a/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntity.java +++ b/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntity.java @@ -93,9 +93,7 @@ public class WrapperEntity implements Tickable { if (this instanceof WrapperLivingEntity) { WrapperLivingEntity wrapperLivingEntity = (WrapperLivingEntity) this; - wrapperLivingEntity.createSpawnPackets().forEach(packetWrapper -> { - sendPacket(uuid, packetWrapper); - }); + wrapperLivingEntity.createSpawnPackets().forEach(packetWrapper -> sendPacket(uuid, packetWrapper)); } this.parent = parent; @@ -219,9 +217,7 @@ public class WrapperEntity implements Tickable { if (this instanceof WrapperLivingEntity) { WrapperLivingEntity wrapperLivingEntity = (WrapperLivingEntity) this; - wrapperLivingEntity.createSpawnPackets().forEach(packetWrapper -> { - sendPacket(uuid, packetWrapper); - }); + wrapperLivingEntity.createSpawnPackets().forEach(packetWrapper -> sendPacket(uuid, packetWrapper)); } } diff --git a/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntityPotionEffect.java b/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntityPotionEffect.java index f6c123f..50a2705 100644 --- a/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntityPotionEffect.java +++ b/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntityPotionEffect.java @@ -40,15 +40,11 @@ public class WrapperEntityPotionEffect implements Tickable { boolean showIcons, @Nullable NBTCompound factorData ) { - this.effects.put(type, new WrapperPotionEffect( - type, - amplifier, - duration, - ambient, - visible, - showIcons, - factorData - )); + WrapperPotionEffect effect = new WrapperPotionEffect(type, amplifier, duration, ambient, visible, showIcons, factorData); + + this.effects.put(type, effect); + + this.entity.sendPacketToViewers(createEffectPacket(effect)); } /** @@ -149,16 +145,25 @@ public class WrapperEntityPotionEffect implements Tickable { remainingDuration = Math.max(duration - elapsedTicks, 0); } - WrapperPlayServerEntityEffect wrapperPlayServerEntityEffect = new WrapperPlayServerEntityEffect(0, null, 0, 0, (byte) 0); + 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(remainingDuration); wrapperPlayServerEntityEffect.setFactorData(factorData); - wrapperPlayServerEntityEffect.setAmbient(ambient); - wrapperPlayServerEntityEffect.setVisible(visible); - wrapperPlayServerEntityEffect.setShowIcon(icons); return wrapperPlayServerEntityEffect; } From d7eda34611c2854aaac7863caac48b302c06437b Mon Sep 17 00:00:00 2001 From: Felipe Paschoal Bergamo <64669985+felipepasc@users.noreply.github.com> Date: Thu, 3 Apr 2025 00:02:49 -0300 Subject: [PATCH 3/7] feat: enhance EntityLib with packet dispatcher and server version check --- .../java/me/tofaa/entitylib/APIConfig.java | 9 +++++ .../java/me/tofaa/entitylib/EntityLibAPI.java | 8 +++-- .../me/tofaa/entitylib/meta/EntityMeta.java | 36 ++++++++++--------- .../entitylib/wrapper/WrapperEntity.java | 13 ++----- .../common/AbstractEntityLibAPI.java | 34 ++++++++++++++---- 5 files changed, 63 insertions(+), 37 deletions(-) diff --git a/api/src/main/java/me/tofaa/entitylib/APIConfig.java b/api/src/main/java/me/tofaa/entitylib/APIConfig.java index 3cba7e7..5f59c05 100644 --- a/api/src/main/java/me/tofaa/entitylib/APIConfig.java +++ b/api/src/main/java/me/tofaa/entitylib/APIConfig.java @@ -13,6 +13,7 @@ public final class APIConfig { private boolean platformLogger = false; private boolean bstats = true; private boolean forceBundle = false; + private boolean ignoreServerVersionVerify = false; public APIConfig(PacketEventsAPI packetEvents) { this.packetEvents = packetEvents; @@ -53,6 +54,11 @@ public final class APIConfig { return this; } + public @NotNull APIConfig ignoreServerVersionVerify() { + this.ignoreServerVersionVerify = true; + return this; + } + public boolean isDebugMode() { return debugMode; } @@ -83,4 +89,7 @@ public final class APIConfig { && EntityLib.getOptionalApi().get().getPacketEvents().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_19_4); } + public boolean shouldIgnoreServerVersionVerify() { + return ignoreServerVersionVerify; + } } diff --git a/api/src/main/java/me/tofaa/entitylib/EntityLibAPI.java b/api/src/main/java/me/tofaa/entitylib/EntityLibAPI.java index 663c368..4c3c66e 100644 --- a/api/src/main/java/me/tofaa/entitylib/EntityLibAPI.java +++ b/api/src/main/java/me/tofaa/entitylib/EntityLibAPI.java @@ -4,6 +4,7 @@ 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 com.github.retrooper.packetevents.wrapper.PacketWrapper; import me.tofaa.entitylib.container.EntityContainer; import me.tofaa.entitylib.tick.TickContainer; import me.tofaa.entitylib.wrapper.WrapperEntity; @@ -14,6 +15,8 @@ import org.jetbrains.annotations.Nullable; import java.util.Collection; import java.util.UUID; +import java.util.function.BiConsumer; +import java.util.function.Consumer; /** * Represents the API for EntityLib. @@ -55,7 +58,8 @@ public interface EntityLibAPI { */ void addTickContainer(@NotNull TickContainer tickContainer); - @NotNull - EntityContainer getDefaultContainer(); + @NotNull BiConsumer> getPacketDispatcher(); + + @NotNull EntityContainer getDefaultContainer(); } diff --git a/api/src/main/java/me/tofaa/entitylib/meta/EntityMeta.java b/api/src/main/java/me/tofaa/entitylib/meta/EntityMeta.java index ba03d99..d6907af 100644 --- a/api/src/main/java/me/tofaa/entitylib/meta/EntityMeta.java +++ b/api/src/main/java/me/tofaa/entitylib/meta/EntityMeta.java @@ -140,60 +140,60 @@ public class EntityMeta implements EntityMetadataProvider { } public short getAirTicks() { - return this.metadata.getIndex((byte)1, (short) 300); + return this.metadata.getIndex((byte) 1, (short) 300); } public void setAirTicks(short value) { - this.metadata.setIndex((byte)1, EntityDataTypes.SHORT, value); + this.metadata.setIndex((byte) 1, EntityDataTypes.SHORT, value); } public Component getCustomName() { - Optional component = this.metadata.getIndex((byte)2, Optional.empty()); + Optional component = this.metadata.getIndex((byte) 2, Optional.empty()); return component.orElse(null); } public void setCustomName(Component value) { - this.metadata.setIndex((byte)2, EntityDataTypes.OPTIONAL_ADV_COMPONENT, Optional.ofNullable(value)); + this.metadata.setIndex((byte) 2, EntityDataTypes.OPTIONAL_ADV_COMPONENT, Optional.ofNullable(value)); } public boolean isCustomNameVisible() { - return this.metadata.getIndex((byte)3, false); + return this.metadata.getIndex((byte) 3, false); } public void setCustomNameVisible(boolean value) { - this.metadata.setIndex((byte)3, EntityDataTypes.BOOLEAN, value); + this.metadata.setIndex((byte) 3, EntityDataTypes.BOOLEAN, value); } public boolean isSilent() { - return this.metadata.getIndex((byte)4, false); + return this.metadata.getIndex((byte) 4, false); } public void setSilent(boolean value) { - this.metadata.setIndex((byte)4, EntityDataTypes.BOOLEAN, value); + this.metadata.setIndex((byte) 4, EntityDataTypes.BOOLEAN, value); } public boolean hasNoGravity() { - return this.metadata.getIndex((byte)5, true); + return this.metadata.getIndex((byte) 5, true); } public void setHasNoGravity(boolean value) { - this.metadata.setIndex((byte)5, EntityDataTypes.BOOLEAN, value); + this.metadata.setIndex((byte) 5, EntityDataTypes.BOOLEAN, value); } public EntityPose getPose() { - return this.metadata.getIndex((byte)6, EntityPose.STANDING); + return this.metadata.getIndex((byte) 6, EntityPose.STANDING); } public void setPose(EntityPose value) { - this.metadata.setIndex((byte)6, EntityDataTypes.ENTITY_POSE, value); + this.metadata.setIndex((byte) 6, EntityDataTypes.ENTITY_POSE, value); } public int getTicksFrozenInPowderedSnow() { - return this.metadata.getIndex((byte)7, 0); + return this.metadata.getIndex((byte) 7, 0); } public void setTicksFrozenInPowderedSnow(int value) { - this.metadata.setIndex((byte)7, EntityDataTypes.INT, value); + this.metadata.setIndex((byte) 7, EntityDataTypes.INT, value); } public WrapperPlayServerEntityMetadata createPacket() { @@ -201,6 +201,7 @@ public class EntityMeta implements EntityMetadataProvider { } protected static void isVersionNewer(ServerVersion version) { + if (EntityLib.getApi().getSettings().shouldIgnoreServerVersionVerify()) return; if (EntityLib.getOptionalApi().isPresent()) { if (!EntityLib.getApi().getPacketEvents().getServerManager().getVersion().is(VersionComparison.NEWER_THAN, version)) { throw new InvalidVersionException("This method is only available for versions newer than " + version.name() + "."); @@ -228,7 +229,8 @@ public class EntityMeta implements EntityMetadataProvider { /** * 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 * @return the offset value */ @@ -257,7 +259,7 @@ public class EntityMeta implements EntityMetadataProvider { } public void setMaskBit(int index, byte bit, boolean value) { - byte mask = getMask((byte)index); + byte mask = getMask((byte) index); boolean currentValue = (mask & bit) == bit; if (currentValue == value) { return; @@ -267,7 +269,7 @@ public class EntityMeta implements EntityMetadataProvider { } else { mask &= (byte) ~bit; } - setMask((byte)index, mask); + setMask((byte) index, mask); } @Override diff --git a/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntity.java b/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntity.java index eb65d0c..4206a79 100644 --- a/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntity.java +++ b/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntity.java @@ -509,18 +509,9 @@ public class WrapperEntity implements Tickable { } private static void sendPacket(UUID user, PacketWrapper wrapper) { - if (wrapper == null) return; - - Object channel = EntityLib.getApi().getPacketEvents().getProtocolManager().getChannel(user); - if (channel == null) { - 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."); - } - - return; + if (wrapper != null) { + EntityLib.getApi().getPacketDispatcher().accept(user, wrapper); } - - EntityLib.getApi().getPacketEvents().getProtocolManager().sendPacket(channel, wrapper); } public boolean hasNoGravity() { diff --git a/common/src/main/java/me/tofaa/entitylib/common/AbstractEntityLibAPI.java b/common/src/main/java/me/tofaa/entitylib/common/AbstractEntityLibAPI.java index 5bb4f54..ba98c95 100644 --- a/common/src/main/java/me/tofaa/entitylib/common/AbstractEntityLibAPI.java +++ b/common/src/main/java/me/tofaa/entitylib/common/AbstractEntityLibAPI.java @@ -1,7 +1,9 @@ package me.tofaa.entitylib.common; import com.github.retrooper.packetevents.PacketEventsAPI; +import com.github.retrooper.packetevents.wrapper.PacketWrapper; import me.tofaa.entitylib.APIConfig; +import me.tofaa.entitylib.EntityLib; import me.tofaa.entitylib.EntityLibAPI; import me.tofaa.entitylib.Platform; import me.tofaa.entitylib.container.EntityContainer; @@ -10,22 +12,38 @@ import me.tofaa.entitylib.wrapper.WrapperEntity; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.*; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.UUID; +import java.util.function.BiConsumer; public abstract class AbstractEntityLibAPI implements EntityLibAPI { - protected final Platform

platform; protected final PacketEventsAPI packetEvents; protected final APIConfig settings; protected final Collection> tickContainers; protected final EntityContainer defaultEntityContainer = EntityContainer.basic(); + protected final BiConsumer> packetDispatcher; protected AbstractEntityLibAPI(Platform

platform, APIConfig settings) { this.platform = platform; this.packetEvents = settings.getPacketEvents(); this.settings = settings; this.tickContainers = settings.shouldTickTickables() ? new HashSet<>() : Collections.emptyList(); + this.packetDispatcher = (user, wrapper) -> { + Object channel = EntityLib.getApi().getPacketEvents().getProtocolManager().getChannel(user); + if (channel == null) { + 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."); + } + + return; + } + + EntityLib.getApi().getPacketEvents().getProtocolManager().sendPacket(channel, wrapper); + }; } @Override @@ -36,7 +54,6 @@ public abstract class AbstractEntityLibAPI implements EntityLibAPI { @Override public @Nullable WrapperEntity getEntity(@NotNull UUID uuid) { return defaultEntityContainer.getEntity(uuid); - } @Override @@ -49,9 +66,8 @@ public abstract class AbstractEntityLibAPI implements EntityLibAPI { return defaultEntityContainer; } - @NotNull @Override - public APIConfig getSettings() { + public @NotNull APIConfig getSettings() { return settings; } @@ -60,9 +76,13 @@ public abstract class AbstractEntityLibAPI implements EntityLibAPI { return packetEvents; } - @NotNull @Override - public Collection> getTickContainers() { + public @NotNull BiConsumer> getPacketDispatcher() { + return packetDispatcher; + } + + @Override + public @NotNull Collection> getTickContainers() { return tickContainers; } } From 9e7ecf310ea0f0e3cf650eda036187a2983d6857 Mon Sep 17 00:00:00 2001 From: Felipe Paschoal Bergamo <64669985+felipepasc@users.noreply.github.com> Date: Thu, 3 Apr 2025 00:03:40 -0300 Subject: [PATCH 4/7] feat(api): add setPacketDispatcher method to EntityLibAPI --- api/src/main/java/me/tofaa/entitylib/EntityLibAPI.java | 2 ++ .../me/tofaa/entitylib/common/AbstractEntityLibAPI.java | 8 +++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/api/src/main/java/me/tofaa/entitylib/EntityLibAPI.java b/api/src/main/java/me/tofaa/entitylib/EntityLibAPI.java index 4c3c66e..44614fc 100644 --- a/api/src/main/java/me/tofaa/entitylib/EntityLibAPI.java +++ b/api/src/main/java/me/tofaa/entitylib/EntityLibAPI.java @@ -60,6 +60,8 @@ public interface EntityLibAPI { @NotNull BiConsumer> getPacketDispatcher(); + void setPacketDispatcher(@NotNull BiConsumer> packetDispatcher); + @NotNull EntityContainer getDefaultContainer(); } diff --git a/common/src/main/java/me/tofaa/entitylib/common/AbstractEntityLibAPI.java b/common/src/main/java/me/tofaa/entitylib/common/AbstractEntityLibAPI.java index ba98c95..d8577c9 100644 --- a/common/src/main/java/me/tofaa/entitylib/common/AbstractEntityLibAPI.java +++ b/common/src/main/java/me/tofaa/entitylib/common/AbstractEntityLibAPI.java @@ -25,7 +25,8 @@ public abstract class AbstractEntityLibAPI implements EntityLibAPI { protected final APIConfig settings; protected final Collection> tickContainers; protected final EntityContainer defaultEntityContainer = EntityContainer.basic(); - protected final BiConsumer> packetDispatcher; + + protected BiConsumer> packetDispatcher; protected AbstractEntityLibAPI(Platform

platform, APIConfig settings) { this.platform = platform; @@ -81,6 +82,11 @@ public abstract class AbstractEntityLibAPI implements EntityLibAPI { return packetDispatcher; } + @Override + public void setPacketDispatcher(@NotNull BiConsumer> packetDispatcher) { + this.packetDispatcher = packetDispatcher; + } + @Override public @NotNull Collection> getTickContainers() { return tickContainers; From 2e05530f363c190734c6cab15b5ff54c9f806ad6 Mon Sep 17 00:00:00 2001 From: Felipe Paschoal Bergamo <64669985+felipepasc@users.noreply.github.com> Date: Tue, 3 Jun 2025 18:37:58 -0300 Subject: [PATCH 5/7] Cleaned up the WrapperEntityPotionEffect class by removing --- .../java/me/tofaa/entitylib/APIConfig.java | 10 ---- .../me/tofaa/entitylib/meta/EntityMeta.java | 2 +- .../meta/display/TextDisplayMeta.java | 1 - .../wrapper/WrapperEntityPotionEffect.java | 55 +------------------ .../wrapper/WrapperLivingEntity.java | 9 +-- 5 files changed, 4 insertions(+), 73 deletions(-) diff --git a/api/src/main/java/me/tofaa/entitylib/APIConfig.java b/api/src/main/java/me/tofaa/entitylib/APIConfig.java index 5f59c05..4394618 100644 --- a/api/src/main/java/me/tofaa/entitylib/APIConfig.java +++ b/api/src/main/java/me/tofaa/entitylib/APIConfig.java @@ -13,7 +13,6 @@ public final class APIConfig { private boolean platformLogger = false; private boolean bstats = true; private boolean forceBundle = false; - private boolean ignoreServerVersionVerify = false; public APIConfig(PacketEventsAPI packetEvents) { this.packetEvents = packetEvents; @@ -54,11 +53,6 @@ public final class APIConfig { return this; } - public @NotNull APIConfig ignoreServerVersionVerify() { - this.ignoreServerVersionVerify = true; - return this; - } - public boolean isDebugMode() { return debugMode; } @@ -88,8 +82,4 @@ public final class APIConfig { && EntityLib.getOptionalApi().isPresent() && EntityLib.getOptionalApi().get().getPacketEvents().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_19_4); } - - public boolean shouldIgnoreServerVersionVerify() { - return ignoreServerVersionVerify; - } } diff --git a/api/src/main/java/me/tofaa/entitylib/meta/EntityMeta.java b/api/src/main/java/me/tofaa/entitylib/meta/EntityMeta.java index d6907af..8f99e4b 100644 --- a/api/src/main/java/me/tofaa/entitylib/meta/EntityMeta.java +++ b/api/src/main/java/me/tofaa/entitylib/meta/EntityMeta.java @@ -201,12 +201,12 @@ public class EntityMeta implements EntityMetadataProvider { } protected static void isVersionNewer(ServerVersion version) { - if (EntityLib.getApi().getSettings().shouldIgnoreServerVersionVerify()) return; if (EntityLib.getOptionalApi().isPresent()) { if (!EntityLib.getApi().getPacketEvents().getServerManager().getVersion().is(VersionComparison.NEWER_THAN, version)) { throw new InvalidVersionException("This method is only available for versions newer than " + version.name() + "."); } } + if (!PacketEvents.getAPI().getServerManager().getVersion().is(VersionComparison.NEWER_THAN, version)) { throw new InvalidVersionException("This method is only available for versions newer than " + version.name() + "."); } diff --git a/api/src/main/java/me/tofaa/entitylib/meta/display/TextDisplayMeta.java b/api/src/main/java/me/tofaa/entitylib/meta/display/TextDisplayMeta.java index 85ef64e..7fb8f71 100644 --- a/api/src/main/java/me/tofaa/entitylib/meta/display/TextDisplayMeta.java +++ b/api/src/main/java/me/tofaa/entitylib/meta/display/TextDisplayMeta.java @@ -19,7 +19,6 @@ public class TextDisplayMeta extends AbstractDisplayMeta { super(entityId, metadata); } - public Component getText() { return metadata.getIndex(OFFSET, Component.empty()); } diff --git a/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntityPotionEffect.java b/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntityPotionEffect.java index 50a2705..2d47629 100644 --- a/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntityPotionEffect.java +++ b/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntityPotionEffect.java @@ -4,19 +4,17 @@ 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 me.tofaa.entitylib.tick.Tickable; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; import java.util.concurrent.ConcurrentHashMap; -public class WrapperEntityPotionEffect implements Tickable { +public class WrapperEntityPotionEffect { private final WrapperLivingEntity entity; private final Map effects = new ConcurrentHashMap<>(); private boolean notifyChanges = true; - private boolean ticking = true; public WrapperEntityPotionEffect(WrapperLivingEntity entity) { this.entity = entity; @@ -134,16 +132,6 @@ public class WrapperEntityPotionEffect implements Tickable { boolean visible = effect.isVisible(); boolean icons = effect.hasIcons(); NBTCompound factorData = effect.getFactorData(); - long createdAt = effect.getCreatedAt(); - - int remainingDuration = duration; - if (duration != -1) { - long elapsedMillis = System.currentTimeMillis() - createdAt; - - int elapsedTicks = (int) (elapsedMillis / 50); - - remainingDuration = Math.max(duration - elapsedTicks, 0); - } int flags = 0; @@ -162,7 +150,7 @@ public class WrapperEntityPotionEffect implements Tickable { wrapperPlayServerEntityEffect.setEntityId(this.entity.getEntityId()); wrapperPlayServerEntityEffect.setPotionType(potionType); wrapperPlayServerEntityEffect.setEffectAmplifier(amplifier); - wrapperPlayServerEntityEffect.setEffectDurationTicks(remainingDuration); + wrapperPlayServerEntityEffect.setEffectDurationTicks(duration); wrapperPlayServerEntityEffect.setFactorData(factorData); return wrapperPlayServerEntityEffect; @@ -191,39 +179,6 @@ public class WrapperEntityPotionEffect implements Tickable { refresh(); } - @Override - public boolean isTicking() { - return this.ticking; - } - - @Override - public void setTicking(boolean ticking) { - this.ticking = ticking; - } - - @Override - public void tick(long time) { - Set toRemove = new HashSet<>(); - - this.effects.values().forEach(effect -> { - PotionType potionType = effect.getPotionType(); - - int duration = effect.getDuration(); - if (duration <= -1) return; // Infinity effect - - long createdAt = effect.getCreatedAt(); - - long elapsedMillis = time - createdAt; - - int elapsedTicks = (int) (elapsedMillis / 50); - - int remainingDuration = duration - elapsedTicks; - if (remainingDuration <= 0) toRemove.add(potionType); - }); - - toRemove.forEach(this::removePotionEffect); - } - public static class WrapperPotionEffect { private final PotionType potionType; private final int amplifier; @@ -232,7 +187,6 @@ public class WrapperEntityPotionEffect implements Tickable { private final boolean visible; private final boolean icons; private final @Nullable NBTCompound factorData; - private final long createdAt; private WrapperPotionEffect(PotionType potionType, int amplifier, int duration, boolean ambient, boolean visible, boolean icons, @Nullable NBTCompound factorData) { this.potionType = potionType; @@ -242,7 +196,6 @@ public class WrapperEntityPotionEffect implements Tickable { this.visible = visible; this.icons = icons; this.factorData = factorData; - this.createdAt = System.currentTimeMillis(); } public PotionType getPotionType() { @@ -272,9 +225,5 @@ public class WrapperEntityPotionEffect implements Tickable { public @Nullable NBTCompound getFactorData() { return factorData; } - - public long getCreatedAt() { - return createdAt; - } } } diff --git a/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperLivingEntity.java b/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperLivingEntity.java index 465f840..290e557 100644 --- a/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperLivingEntity.java +++ b/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperLivingEntity.java @@ -59,13 +59,6 @@ public class WrapperLivingEntity extends WrapperEntity { this.attributes.refresh(); } - @Override - public void tick(long time) { - this.potionEffect.tick(time); - - super.tick(time); - } - public WrapperEntityAttributes getAttributes() { return this.attributes; } @@ -83,7 +76,7 @@ public class WrapperLivingEntity extends WrapperEntity { packets.add(getAttributes().createPacket()); packets.add(getEquipment().createPacket()); - packets.addAll(this.potionEffect.createEffectPackets()); + packets.addAll(getPotionEffect().createEffectPackets()); return packets; } From 1fc7a00ec73b727939f84317de1bd8dcaa7a47d7 Mon Sep 17 00:00:00 2001 From: Felipe Paschoal Bergamo <64669985+felipepasc@users.noreply.github.com> Date: Tue, 3 Jun 2025 18:53:19 -0300 Subject: [PATCH 6/7] fix: errors --- .../java/me/tofaa/entitylib/APIConfig.java | 1 + .../java/me/tofaa/entitylib/EntityLibAPI.java | 15 ++------ .../me/tofaa/entitylib/meta/EntityMeta.java | 35 +++++++++---------- .../meta/display/TextDisplayMeta.java | 1 + .../entitylib/wrapper/WrapperEntity.java | 13 +++++-- .../wrapper/WrapperEntityPotionEffect.java | 2 +- .../common/AbstractEntityLibAPI.java | 28 --------------- 7 files changed, 33 insertions(+), 62 deletions(-) diff --git a/api/src/main/java/me/tofaa/entitylib/APIConfig.java b/api/src/main/java/me/tofaa/entitylib/APIConfig.java index 4394618..3cba7e7 100644 --- a/api/src/main/java/me/tofaa/entitylib/APIConfig.java +++ b/api/src/main/java/me/tofaa/entitylib/APIConfig.java @@ -82,4 +82,5 @@ public final class APIConfig { && EntityLib.getOptionalApi().isPresent() && EntityLib.getOptionalApi().get().getPacketEvents().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_19_4); } + } diff --git a/api/src/main/java/me/tofaa/entitylib/EntityLibAPI.java b/api/src/main/java/me/tofaa/entitylib/EntityLibAPI.java index 44614fc..c6041f7 100644 --- a/api/src/main/java/me/tofaa/entitylib/EntityLibAPI.java +++ b/api/src/main/java/me/tofaa/entitylib/EntityLibAPI.java @@ -1,22 +1,14 @@ package me.tofaa.entitylib; 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 com.github.retrooper.packetevents.wrapper.PacketWrapper; import me.tofaa.entitylib.container.EntityContainer; import me.tofaa.entitylib.tick.TickContainer; 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.Nullable; import java.util.Collection; import java.util.UUID; -import java.util.function.BiConsumer; -import java.util.function.Consumer; /** * Represents the API for EntityLib. @@ -58,10 +50,7 @@ public interface EntityLibAPI { */ void addTickContainer(@NotNull TickContainer tickContainer); - @NotNull BiConsumer> getPacketDispatcher(); - - void setPacketDispatcher(@NotNull BiConsumer> packetDispatcher); - - @NotNull EntityContainer getDefaultContainer(); + @NotNull + EntityContainer getDefaultContainer(); } diff --git a/api/src/main/java/me/tofaa/entitylib/meta/EntityMeta.java b/api/src/main/java/me/tofaa/entitylib/meta/EntityMeta.java index 8f99e4b..b628c8a 100644 --- a/api/src/main/java/me/tofaa/entitylib/meta/EntityMeta.java +++ b/api/src/main/java/me/tofaa/entitylib/meta/EntityMeta.java @@ -140,60 +140,60 @@ public class EntityMeta implements EntityMetadataProvider { } public short getAirTicks() { - return this.metadata.getIndex((byte) 1, (short) 300); + return this.metadata.getIndex((byte)1, (short) 300); } public void setAirTicks(short value) { - this.metadata.setIndex((byte) 1, EntityDataTypes.SHORT, value); + this.metadata.setIndex((byte)1, EntityDataTypes.SHORT, value); } public Component getCustomName() { - Optional component = this.metadata.getIndex((byte) 2, Optional.empty()); + Optional component = this.metadata.getIndex((byte)2, Optional.empty()); return component.orElse(null); } public void setCustomName(Component value) { - this.metadata.setIndex((byte) 2, EntityDataTypes.OPTIONAL_ADV_COMPONENT, Optional.ofNullable(value)); + this.metadata.setIndex((byte)2, EntityDataTypes.OPTIONAL_ADV_COMPONENT, Optional.ofNullable(value)); } public boolean isCustomNameVisible() { - return this.metadata.getIndex((byte) 3, false); + return this.metadata.getIndex((byte)3, false); } public void setCustomNameVisible(boolean value) { - this.metadata.setIndex((byte) 3, EntityDataTypes.BOOLEAN, value); + this.metadata.setIndex((byte)3, EntityDataTypes.BOOLEAN, value); } public boolean isSilent() { - return this.metadata.getIndex((byte) 4, false); + return this.metadata.getIndex((byte)4, false); } public void setSilent(boolean value) { - this.metadata.setIndex((byte) 4, EntityDataTypes.BOOLEAN, value); + this.metadata.setIndex((byte)4, EntityDataTypes.BOOLEAN, value); } public boolean hasNoGravity() { - return this.metadata.getIndex((byte) 5, true); + return this.metadata.getIndex((byte)5, true); } public void setHasNoGravity(boolean value) { - this.metadata.setIndex((byte) 5, EntityDataTypes.BOOLEAN, value); + this.metadata.setIndex((byte)5, EntityDataTypes.BOOLEAN, value); } public EntityPose getPose() { - return this.metadata.getIndex((byte) 6, EntityPose.STANDING); + return this.metadata.getIndex((byte)6, EntityPose.STANDING); } public void setPose(EntityPose value) { - this.metadata.setIndex((byte) 6, EntityDataTypes.ENTITY_POSE, value); + this.metadata.setIndex((byte)6, EntityDataTypes.ENTITY_POSE, value); } public int getTicksFrozenInPowderedSnow() { - return this.metadata.getIndex((byte) 7, 0); + return this.metadata.getIndex((byte)7, 0); } public void setTicksFrozenInPowderedSnow(int value) { - this.metadata.setIndex((byte) 7, EntityDataTypes.INT, value); + this.metadata.setIndex((byte)7, EntityDataTypes.INT, value); } public WrapperPlayServerEntityMetadata createPacket() { @@ -206,7 +206,6 @@ public class EntityMeta implements EntityMetadataProvider { throw new InvalidVersionException("This method is only available for versions newer than " + version.name() + "."); } } - if (!PacketEvents.getAPI().getServerManager().getVersion().is(VersionComparison.NEWER_THAN, version)) { throw new InvalidVersionException("This method is only available for versions newer than " + version.name() + "."); } @@ -230,7 +229,7 @@ public class EntityMeta implements EntityMetadataProvider { /** * 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 * @return the offset value */ @@ -259,7 +258,7 @@ public class EntityMeta implements EntityMetadataProvider { } public void setMaskBit(int index, byte bit, boolean value) { - byte mask = getMask((byte) index); + byte mask = getMask((byte)index); boolean currentValue = (mask & bit) == bit; if (currentValue == value) { return; @@ -269,7 +268,7 @@ public class EntityMeta implements EntityMetadataProvider { } else { mask &= (byte) ~bit; } - setMask((byte) index, mask); + setMask((byte)index, mask); } @Override diff --git a/api/src/main/java/me/tofaa/entitylib/meta/display/TextDisplayMeta.java b/api/src/main/java/me/tofaa/entitylib/meta/display/TextDisplayMeta.java index 7fb8f71..85ef64e 100644 --- a/api/src/main/java/me/tofaa/entitylib/meta/display/TextDisplayMeta.java +++ b/api/src/main/java/me/tofaa/entitylib/meta/display/TextDisplayMeta.java @@ -19,6 +19,7 @@ public class TextDisplayMeta extends AbstractDisplayMeta { super(entityId, metadata); } + public Component getText() { return metadata.getIndex(OFFSET, Component.empty()); } diff --git a/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntity.java b/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntity.java index 4206a79..eb65d0c 100644 --- a/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntity.java +++ b/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntity.java @@ -509,9 +509,18 @@ public class WrapperEntity implements Tickable { } private static void sendPacket(UUID user, PacketWrapper wrapper) { - if (wrapper != null) { - EntityLib.getApi().getPacketDispatcher().accept(user, wrapper); + if (wrapper == null) return; + + Object channel = EntityLib.getApi().getPacketEvents().getProtocolManager().getChannel(user); + if (channel == null) { + 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."); + } + + return; } + + EntityLib.getApi().getPacketEvents().getProtocolManager().sendPacket(channel, wrapper); } public boolean hasNoGravity() { diff --git a/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntityPotionEffect.java b/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntityPotionEffect.java index 2d47629..eecc52d 100644 --- a/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntityPotionEffect.java +++ b/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntityPotionEffect.java @@ -165,7 +165,7 @@ public class WrapperEntityPotionEffect { new ArrayList<>(this.effects.values()).forEach(effect -> { WrapperPlayServerEntityEffect wrapperPlayServerEntityEffect = createEffectPacket(effect); - this.entity.sendPacketsToViewers(wrapperPlayServerEntityEffect); + this.entity.sendPacketToViewers(wrapperPlayServerEntityEffect); }); } } diff --git a/common/src/main/java/me/tofaa/entitylib/common/AbstractEntityLibAPI.java b/common/src/main/java/me/tofaa/entitylib/common/AbstractEntityLibAPI.java index d8577c9..bfee9b4 100644 --- a/common/src/main/java/me/tofaa/entitylib/common/AbstractEntityLibAPI.java +++ b/common/src/main/java/me/tofaa/entitylib/common/AbstractEntityLibAPI.java @@ -1,9 +1,7 @@ package me.tofaa.entitylib.common; import com.github.retrooper.packetevents.PacketEventsAPI; -import com.github.retrooper.packetevents.wrapper.PacketWrapper; import me.tofaa.entitylib.APIConfig; -import me.tofaa.entitylib.EntityLib; import me.tofaa.entitylib.EntityLibAPI; import me.tofaa.entitylib.Platform; import me.tofaa.entitylib.container.EntityContainer; @@ -16,8 +14,6 @@ import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.UUID; -import java.util.function.BiConsumer; - public abstract class AbstractEntityLibAPI implements EntityLibAPI { protected final Platform

platform; @@ -26,25 +22,11 @@ public abstract class AbstractEntityLibAPI implements EntityLibAPI { protected final Collection> tickContainers; protected final EntityContainer defaultEntityContainer = EntityContainer.basic(); - protected BiConsumer> packetDispatcher; - protected AbstractEntityLibAPI(Platform

platform, APIConfig settings) { this.platform = platform; this.packetEvents = settings.getPacketEvents(); this.settings = settings; this.tickContainers = settings.shouldTickTickables() ? new HashSet<>() : Collections.emptyList(); - this.packetDispatcher = (user, wrapper) -> { - Object channel = EntityLib.getApi().getPacketEvents().getProtocolManager().getChannel(user); - if (channel == null) { - 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."); - } - - return; - } - - EntityLib.getApi().getPacketEvents().getProtocolManager().sendPacket(channel, wrapper); - }; } @Override @@ -77,16 +59,6 @@ public abstract class AbstractEntityLibAPI implements EntityLibAPI { return packetEvents; } - @Override - public @NotNull BiConsumer> getPacketDispatcher() { - return packetDispatcher; - } - - @Override - public void setPacketDispatcher(@NotNull BiConsumer> packetDispatcher) { - this.packetDispatcher = packetDispatcher; - } - @Override public @NotNull Collection> getTickContainers() { return tickContainers; From 26cccce9a8d2a05e421999206fce70dc1699e3c7 Mon Sep 17 00:00:00 2001 From: Felipe Paschoal Bergamo <64669985+felipepasc@users.noreply.github.com> Date: Tue, 3 Jun 2025 18:54:38 -0300 Subject: [PATCH 7/7] fix(wrapper): refactor spawn packet sending method --- api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntity.java b/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntity.java index eb65d0c..e3e7041 100644 --- a/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntity.java +++ b/api/src/main/java/me/tofaa/entitylib/wrapper/WrapperEntity.java @@ -93,7 +93,7 @@ public class WrapperEntity implements Tickable { if (this instanceof WrapperLivingEntity) { WrapperLivingEntity wrapperLivingEntity = (WrapperLivingEntity) this; - wrapperLivingEntity.createSpawnPackets().forEach(packetWrapper -> sendPacket(uuid, packetWrapper)); + wrapperLivingEntity.createSpawnPackets().forEach(this::sendPacketsToViewers); } this.parent = parent;