diff --git a/nms/nms-1_16_R3/src/main/java/io/github/zap/arenaapi/nms/v1_16_R3/ArenaNMSBridge_v1_16_R3.java b/nms/nms-1_16_R3/src/main/java/io/github/zap/arenaapi/nms/v1_16_R3/ArenaNMSBridge_v1_16_R3.java index 2f84cbb..e619aaa 100644 --- a/nms/nms-1_16_R3/src/main/java/io/github/zap/arenaapi/nms/v1_16_R3/ArenaNMSBridge_v1_16_R3.java +++ b/nms/nms-1_16_R3/src/main/java/io/github/zap/arenaapi/nms/v1_16_R3/ArenaNMSBridge_v1_16_R3.java @@ -1,21 +1,39 @@ package io.github.zap.arenaapi.nms.v1_16_R3; +import com.comphenix.protocol.ProtocolLibrary; +import com.comphenix.protocol.ProtocolManager; import io.github.zap.arenaapi.nms.common.ArenaNMSBridge; import io.github.zap.arenaapi.nms.common.entity.EntityBridge; import io.github.zap.arenaapi.nms.common.itemstack.ItemStackBridge; +import io.github.zap.arenaapi.nms.common.packet.PacketBridge; import io.github.zap.arenaapi.nms.common.player.PlayerBridge; import io.github.zap.arenaapi.nms.common.world.WorldBridge; import io.github.zap.arenaapi.nms.v1_16_R3.entity.EntityBridge_v1_16_R3; import io.github.zap.arenaapi.nms.v1_16_R3.itemstack.ItemStackBridge_v1_16_R3; +import io.github.zap.arenaapi.nms.v1_16_R3.packet.ProtocolLibPacketBridge_v1_16_R3; import io.github.zap.arenaapi.nms.v1_16_R3.player.PlayerBridge_v1_16_R3; import io.github.zap.arenaapi.nms.v1_16_R3.world.WorldBridge_v1_16_R3; +import org.apache.commons.lang3.NotImplementedException; import org.jetbrains.annotations.NotNull; public class ArenaNMSBridge_v1_16_R3 implements ArenaNMSBridge { public static final ArenaNMSBridge_v1_16_R3 INSTANCE = new ArenaNMSBridge_v1_16_R3(); private static final String VERSION = "v1_16_R3"; - private ArenaNMSBridge_v1_16_R3() {} + private final PacketBridge packetBridge; + + private ArenaNMSBridge_v1_16_R3() { + try { + Class.forName("com.comphenix.protocol.ProtocolLibrary"); + } + catch (ClassNotFoundException error) { + throw new NotImplementedException("No minecraft PacketBridge implementation exists for version " + + VERSION + ", and ProtocolLibrary was not detected on the server!"); + } + + ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager(); + this.packetBridge = new ProtocolLibPacketBridge_v1_16_R3(protocolManager); + } @Override public @NotNull String version() { @@ -32,6 +50,11 @@ private ArenaNMSBridge_v1_16_R3() {} return ItemStackBridge_v1_16_R3.INSTANCE; } + @Override + public @NotNull PacketBridge packetBridge() { + return packetBridge; + } + @Override public @NotNull PlayerBridge playerBridge() { return PlayerBridge_v1_16_R3.INSTANCE; diff --git a/nms/nms-1_16_R3/src/main/java/io/github/zap/arenaapi/nms/v1_16_R3/packet/ProtocolLibPacketBridge_v1_16_R3.java b/nms/nms-1_16_R3/src/main/java/io/github/zap/arenaapi/nms/v1_16_R3/packet/ProtocolLibPacketBridge_v1_16_R3.java new file mode 100644 index 0000000..0ba0052 --- /dev/null +++ b/nms/nms-1_16_R3/src/main/java/io/github/zap/arenaapi/nms/v1_16_R3/packet/ProtocolLibPacketBridge_v1_16_R3.java @@ -0,0 +1,94 @@ +package io.github.zap.arenaapi.nms.v1_16_R3.packet; + +import com.comphenix.protocol.PacketType; +import com.comphenix.protocol.ProtocolManager; +import com.comphenix.protocol.events.PacketContainer; +import com.comphenix.protocol.wrappers.AdventureComponentConverter; +import com.comphenix.protocol.wrappers.WrappedDataWatcher; +import io.github.zap.arenaapi.nms.common.packet.Packet; +import io.github.zap.arenaapi.nms.common.packet.PacketBridge; +import io.github.zap.arenaapi.nms.common.packet.ProtocolLibPacket; +import net.kyori.adventure.text.Component; +import org.jetbrains.annotations.NotNull; + +import java.util.Optional; +import java.util.UUID; + +@SuppressWarnings("ClassCanBeRecord") +public class ProtocolLibPacketBridge_v1_16_R3 implements PacketBridge { + + private final static byte INVISIBLE_BYTE_MASK = (byte) 0x20; + + private final static byte MARKER_ARMOR_STAND_MASK = (byte) 0x10; + + protected final ProtocolManager protocolManager; + + public ProtocolLibPacketBridge_v1_16_R3(@NotNull ProtocolManager protocolManager) { + this.protocolManager = protocolManager; + } + + @Override + public @NotNull Packet createSpawnLivingEntityPacket(int entityId, int typeId, @NotNull UUID uuid, double x, + double y, double z) { + PacketContainer packetContainer = new PacketContainer(PacketType.Play.Server.SPAWN_ENTITY_LIVING); + packetContainer.getIntegers() + .write(0, entityId) + .write(1,typeId); + packetContainer.getUUIDs().write(0, uuid); + packetContainer.getDoubles() + .write(0, x) + .write(1, y) + .write(2, z); + + return createProtocolLibPacket(packetContainer); + } + + @Override + public @NotNull Packet createHologramLinePacket(int entityId, @NotNull Component line) { + PacketContainer packetContainer = new PacketContainer(PacketType.Play.Server.ENTITY_METADATA); + packetContainer.getIntegers().write(0, entityId); + + WrappedDataWatcher wrappedDataWatcher = new WrappedDataWatcher(); + + WrappedDataWatcher.Serializer byteSerializer = WrappedDataWatcher.Registry.get(Byte.class); + WrappedDataWatcher.Serializer optChatSerializer = WrappedDataWatcher.Registry + .getChatComponentSerializer(true); + WrappedDataWatcher.Serializer booleanSerializer = WrappedDataWatcher.Registry.get(Boolean.class); + + WrappedDataWatcher.WrappedDataWatcherObject invisible + = new WrappedDataWatcher.WrappedDataWatcherObject(0, byteSerializer); + WrappedDataWatcher.WrappedDataWatcherObject customName + = new WrappedDataWatcher.WrappedDataWatcherObject(2, optChatSerializer); + WrappedDataWatcher.WrappedDataWatcherObject customNameVisible + = new WrappedDataWatcher.WrappedDataWatcherObject(3, booleanSerializer); + WrappedDataWatcher.WrappedDataWatcherObject marker + = new WrappedDataWatcher.WrappedDataWatcherObject(14, byteSerializer); + + wrappedDataWatcher.setObject(invisible, INVISIBLE_BYTE_MASK); + wrappedDataWatcher.setObject(customName, Optional.of(AdventureComponentConverter.fromComponent(line))); + wrappedDataWatcher.setObject(customNameVisible, true); + wrappedDataWatcher.setObject(marker, MARKER_ARMOR_STAND_MASK); + + packetContainer.getWatchableCollectionModifier().write(0, wrappedDataWatcher.getWatchableObjects()); + + return createProtocolLibPacket(packetContainer); + } + + @Override + public @NotNull Packet createDestroyEntityPacket(int id) { + return createDestroyEntitiesPacket(new int[] { id }); + } + + @Override + public @NotNull Packet createDestroyEntitiesPacket(int[] ids) { + PacketContainer packetContainer = new PacketContainer(PacketType.Play.Server.ENTITY_DESTROY); + packetContainer.getIntegerArrays().write(0, ids); + + return createProtocolLibPacket(packetContainer); + } + + protected @NotNull Packet createProtocolLibPacket(@NotNull PacketContainer packetContainer) { + return new ProtocolLibPacket(protocolManager, packetContainer); + } + +} diff --git a/nms/nms-common/src/main/java/io/github/zap/arenaapi/nms/common/ArenaNMSBridge.java b/nms/nms-common/src/main/java/io/github/zap/arenaapi/nms/common/ArenaNMSBridge.java index f8dd662..a6d914f 100644 --- a/nms/nms-common/src/main/java/io/github/zap/arenaapi/nms/common/ArenaNMSBridge.java +++ b/nms/nms-common/src/main/java/io/github/zap/arenaapi/nms/common/ArenaNMSBridge.java @@ -2,6 +2,7 @@ import io.github.zap.arenaapi.nms.common.entity.EntityBridge; import io.github.zap.arenaapi.nms.common.itemstack.ItemStackBridge; +import io.github.zap.arenaapi.nms.common.packet.PacketBridge; import io.github.zap.arenaapi.nms.common.player.PlayerBridge; import io.github.zap.arenaapi.nms.common.world.WorldBridge; import org.bukkit.Bukkit; @@ -37,10 +38,16 @@ private static String nmsVersion() { /** * Returns a bridge used to proxy methods relating to item stacks. - * @return A PlayerBridge instance + * @return A {@link ItemStackBridge} instance */ @NotNull ItemStackBridge itemStackBridge(); + /** + * Returns a bridge used to interact with packets + * @return A {@link PacketBridge} instance + */ + @NotNull PacketBridge packetBridge(); + /** * Returns a bridge used to proxy methods relating to players. * @return A PlayerBridge instance diff --git a/nms/nms-common/src/main/java/io/github/zap/arenaapi/nms/common/packet/MultiPacket.java b/nms/nms-common/src/main/java/io/github/zap/arenaapi/nms/common/packet/MultiPacket.java new file mode 100644 index 0000000..33d3e6c --- /dev/null +++ b/nms/nms-common/src/main/java/io/github/zap/arenaapi/nms/common/packet/MultiPacket.java @@ -0,0 +1,26 @@ +package io.github.zap.arenaapi.nms.common.packet; + +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; + +/** + * A packet formed from many other packets + */ +@SuppressWarnings("ClassCanBeRecord") +public class MultiPacket implements Packet { + + private final Packet[] packets; + + public MultiPacket(@NotNull Packet... packets) { + this.packets = packets; + } + + @Override + public void sendToPlayer(@NotNull Plugin plugin, @NotNull Player player) { + for (Packet packet : packets) { + packet.sendToPlayer(plugin, player); + } + } + +} diff --git a/nms/nms-common/src/main/java/io/github/zap/arenaapi/nms/common/packet/Packet.java b/nms/nms-common/src/main/java/io/github/zap/arenaapi/nms/common/packet/Packet.java new file mode 100644 index 0000000..499481f --- /dev/null +++ b/nms/nms-common/src/main/java/io/github/zap/arenaapi/nms/common/packet/Packet.java @@ -0,0 +1,19 @@ +package io.github.zap.arenaapi.nms.common.packet; + +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; + +/** + * A minecraft packet used to circumvent limitations of the Bukkit API + */ +public interface Packet { + + /** + * Sends the packet to a {@link Player}. + * @param plugin The {@link Plugin} from which the packet is being sent + * @param player The {@link Player} to send the packet to + */ + void sendToPlayer(@NotNull Plugin plugin, @NotNull Player player); + +} diff --git a/nms/nms-common/src/main/java/io/github/zap/arenaapi/nms/common/packet/PacketBridge.java b/nms/nms-common/src/main/java/io/github/zap/arenaapi/nms/common/packet/PacketBridge.java new file mode 100644 index 0000000..0c599dd --- /dev/null +++ b/nms/nms-common/src/main/java/io/github/zap/arenaapi/nms/common/packet/PacketBridge.java @@ -0,0 +1,21 @@ +package io.github.zap.arenaapi.nms.common.packet; + +import net.kyori.adventure.text.Component; +import org.jetbrains.annotations.NotNull; + +import java.util.UUID; + +/** + * A bridge used for creating and interacting with packets + */ +public interface PacketBridge { + + @NotNull Packet createSpawnLivingEntityPacket(int entityId, int typeId, @NotNull UUID uuid, double x, double y, + double z); + + @NotNull Packet createHologramLinePacket(int entityId, @NotNull Component line); + + @NotNull Packet createDestroyEntityPacket(int id); + + @NotNull Packet createDestroyEntitiesPacket(int[] ids); +} diff --git a/nms/nms-common/src/main/java/io/github/zap/arenaapi/nms/common/packet/ProtocolLibPacket.java b/nms/nms-common/src/main/java/io/github/zap/arenaapi/nms/common/packet/ProtocolLibPacket.java new file mode 100644 index 0000000..ba4876a --- /dev/null +++ b/nms/nms-common/src/main/java/io/github/zap/arenaapi/nms/common/packet/ProtocolLibPacket.java @@ -0,0 +1,37 @@ +package io.github.zap.arenaapi.nms.common.packet; + +import com.comphenix.protocol.ProtocolManager; +import com.comphenix.protocol.events.PacketContainer; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; + +import java.lang.reflect.InvocationTargetException; +import java.util.logging.Level; + +/** + * A packet using {@link PacketContainer}s + */ +@SuppressWarnings("ClassCanBeRecord") +public class ProtocolLibPacket implements Packet { + + private final ProtocolManager protocolManager; + + private final PacketContainer handle; + + public ProtocolLibPacket(@NotNull ProtocolManager protocolManager, @NotNull PacketContainer handle) { + this.protocolManager = protocolManager; + this.handle = handle; + } + + @Override + public void sendToPlayer(@NotNull Plugin plugin, @NotNull Player player) { + try { + protocolManager.sendServerPacket(player, handle); + } catch (InvocationTargetException e) { + plugin.getLogger().log(Level.WARNING, "Failed to send a packet to player with UUID " + + player.getUniqueId() + "!", e); + } + } + +} diff --git a/src/main/java/io/github/zap/arenaapi/hologram2/Hologram.java b/src/main/java/io/github/zap/arenaapi/hologram2/Hologram.java new file mode 100644 index 0000000..27bef84 --- /dev/null +++ b/src/main/java/io/github/zap/arenaapi/hologram2/Hologram.java @@ -0,0 +1,146 @@ +package io.github.zap.arenaapi.hologram2; + +import io.github.zap.arenaapi.ArenaApi; +import io.github.zap.arenaapi.nms.common.entity.EntityBridge; +import io.github.zap.arenaapi.nms.common.packet.PacketBridge; +import net.kyori.adventure.text.Component; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +/** + * Represents a hologram which can show lines of text or items + */ +public class Hologram { + + public static final double DEFAULT_LINE_SPACE = 0.25; + + private final Plugin plugin; + + private final EntityBridge entityBridge; + + private final PacketBridge packetBridge; + + private final List> hologramLines = new ArrayList<>(); + + private final double lineSpace; + + private final Location rootLocation; + + public Hologram(@NotNull Plugin plugin, @NotNull EntityBridge entityBridge, @NotNull PacketBridge packetBridge, + @NotNull Location location, double lineSpace) { + this.plugin = plugin; + this.entityBridge = entityBridge; + this.packetBridge = packetBridge; + this.rootLocation = location; + this.lineSpace = lineSpace; + } + + public Hologram(@NotNull ArenaApi arenaApi, @NotNull Location location) { + this(arenaApi, arenaApi.getNmsBridge().entityBridge(), arenaApi.getNmsBridge().packetBridge(), location, + DEFAULT_LINE_SPACE); + } + + @Deprecated + public Hologram(@NotNull Location location, double lineSpace) { + this(ArenaApi.getInstance(), ArenaApi.getInstance().getNmsBridge().entityBridge(), + ArenaApi.getInstance().getNmsBridge().packetBridge(), location, lineSpace); + } + + @Deprecated + public Hologram(@NotNull Location location) { + this(location, DEFAULT_LINE_SPACE); + } + + /** + * Adds a line with a message key and format arguments + * @param message A pair of the message key and format arguments + */ + public void addLine(@NotNull Component message) { + PacketLine textLine = createTextLine(rootLocation.clone().subtract(0, + lineSpace * hologramLines.size(), 0), message); + hologramLines.add(textLine); + } + + private @NotNull PacketLine createTextLine(@NotNull Location location, @NotNull Component message) { + + return TextLine.textLine(entityBridge, packetBridge, location); + } + + /** + * Updates a text line for all players and overrides custom visuals + * @param index The index of the line to update + * @param message The updated line + */ + public void updateLineForEveryone(int index, @NotNull Component message) { + HologramLine hologramLine = hologramLines.get(index); + if (hologramLine instanceof TextLine textLine) { + for (Player player : rootLocation.getWorld().getPlayers()) { + textLine.setVisualForPlayer(plugin, player, message); + } + } else { + + } + } + + /** + * Updates a text line for all players + * @param index The index of the line to update + * @param message The updated line + */ + public void updateLine(int index, @NotNull Component message) { + HologramLine hologramLine = hologramLines.get(index); + if (hologramLine instanceof TextLine textLine) { + textLine.setVisual(message); + } else { + + } + } + + /** + * Updates a text line for a single player + * @param player The player to update the line for + * @param index The index of the line to update + * @param message The updated line + */ + public void updateLineForPlayer(@NotNull Player player, int index, @NotNull Component message) { + HologramLine hologramLine = hologramLines.get(index); + if (hologramLine instanceof TextLine textLine) { + textLine.setVisualForPlayer(plugin, player, message); + } else { + + } + } + + /** + * Creates and renders the hologram for a player + * @param player The player to render the hologram to + */ + public void renderToPlayer(@NotNull Player player) { + for (HologramLine hologramLine : hologramLines) { + hologramLine.createVisualForPlayer(plugin, player); + hologramLine.updateVisualForPlayer(plugin, player); + } + } + + /** + * Destroys the hologram + */ + public void destroy() { + while (!hologramLines.isEmpty()) { + HologramLine line = hologramLines.remove(0); + for (Player player : rootLocation.getWorld().getPlayers()) { + line.destroyVisualForPlayer(plugin, player); + } + } + } + + public @NotNull List> getHologramLines() { + return hologramLines; + } + +} diff --git a/src/main/java/io/github/zap/arenaapi/hologram2/HologramLine.java b/src/main/java/io/github/zap/arenaapi/hologram2/HologramLine.java new file mode 100644 index 0000000..b0d06c6 --- /dev/null +++ b/src/main/java/io/github/zap/arenaapi/hologram2/HologramLine.java @@ -0,0 +1,87 @@ +package io.github.zap.arenaapi.hologram2; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.UUID; + +/** + * A line of a hologram + */ +public abstract class HologramLine { + + private final Map visualMap = new HashMap<>(); + + private V defaultVisual; + + public HologramLine(@NotNull V defaultVisual) { + this.defaultVisual = defaultVisual; + } + + /** + * Sets the visual of the hologram for all players, overriding player specific visuals + * @param plugin The {@link Plugin} that cleared the visuals + */ + public void clearVisuals(@NotNull Plugin plugin) { + Iterator viewers = visualMap.keySet().iterator(); + while (viewers.hasNext()) { + UUID viewer = viewers.next(); + Player player = Bukkit.getPlayer(viewer); + + if (player != null) { + destroyVisualForPlayer(plugin, player); + } + + viewers.remove(); + } + } + + /** + * Sets the default visual of the hologram for all players + * @param visual The new visual + */ + public void setVisual(@NotNull V visual) { + defaultVisual = visual; + } + + /** + * Sets the visual for a single player + * @param plugin The {@link Plugin} to set the visual from + * @param player The player to set the visual for + * @param visual The new visual + */ + public void setVisualForPlayer(@NotNull Plugin plugin, @NotNull Player player, @NotNull V visual) { + visualMap.put(player.getUniqueId(), visual); + updateVisualForPlayer(plugin, player); + } + + /** + * Gets the visual for a player + * @param player The player to get the visual for + * @return The visual the player sees + */ + public @NotNull V getVisualForPlayer(@NotNull Player player) { + return visualMap.getOrDefault(player.getUniqueId(), defaultVisual); + } + /** + * Spawns the visual for a player + * @param plugin The {@link Plugin} to send the visual from + * @param player The player to spawn the visual for + */ + public abstract void createVisualForPlayer(@NotNull Plugin plugin, @NotNull Player player); + + /** + * Updates the visual for a player + * @param plugin The {@link Plugin} to update the visual from + * @param player The player to update for + */ + public abstract void updateVisualForPlayer(@NotNull Plugin plugin, @NotNull Player player); + + public abstract void destroyVisualForPlayer(@NotNull Plugin plugin, @NotNull Player player); + +} diff --git a/src/main/java/io/github/zap/arenaapi/hologram2/PacketLine.java b/src/main/java/io/github/zap/arenaapi/hologram2/PacketLine.java new file mode 100644 index 0000000..d6460a2 --- /dev/null +++ b/src/main/java/io/github/zap/arenaapi/hologram2/PacketLine.java @@ -0,0 +1,42 @@ +package io.github.zap.arenaapi.hologram2; + +import io.github.zap.arenaapi.nms.common.packet.Packet; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; + +import java.util.function.Function; + +public class PacketLine extends HologramLine { + + private final Packet spawnPacket; + + private final Function<@NotNull V, @NotNull Packet> updatePacket; + + private final Packet destructionPacket; + + public PacketLine(@NotNull Packet spawnPacket, @NotNull Function<@NotNull V, @NotNull Packet> updatePacket, + @NotNull Packet destructionPacket, @NotNull V defaultVisual) { + super(defaultVisual); + + this.spawnPacket = spawnPacket; + this.updatePacket = updatePacket; + this.destructionPacket = destructionPacket; + } + + @Override + public void createVisualForPlayer(@NotNull Plugin plugin, @NotNull Player player) { + spawnPacket.sendToPlayer(plugin, player); + } + + @Override + public void updateVisualForPlayer(@NotNull Plugin plugin, @NotNull Player player) { + updatePacket.apply(getVisualForPlayer(player)).sendToPlayer(plugin, player); + } + + @Override + public void destroyVisualForPlayer(@NotNull Plugin plugin, @NotNull Player player) { + destructionPacket.sendToPlayer(plugin, player); + } + +} diff --git a/src/main/java/io/github/zap/arenaapi/hologram2/TextLine.java b/src/main/java/io/github/zap/arenaapi/hologram2/TextLine.java new file mode 100644 index 0000000..b8c15a7 --- /dev/null +++ b/src/main/java/io/github/zap/arenaapi/hologram2/TextLine.java @@ -0,0 +1,33 @@ +package io.github.zap.arenaapi.hologram2; + +import io.github.zap.arenaapi.nms.common.entity.EntityBridge; +import io.github.zap.arenaapi.nms.common.packet.Packet; +import io.github.zap.arenaapi.nms.common.packet.PacketBridge; +import net.kyori.adventure.text.Component; +import org.bukkit.Location; +import org.bukkit.entity.EntityType; +import org.jetbrains.annotations.NotNull; + +import java.util.function.Function; + +public class TextLine extends PacketLine { + + private TextLine(@NotNull Packet spawnPacket, @NotNull Function<@NotNull Component, @NotNull Packet> updatePacket, + @NotNull Packet destroyPacket) { + super(spawnPacket, updatePacket, destroyPacket, Component.empty()); + } + + public static @NotNull TextLine textLine(@NotNull EntityBridge entityBridge, @NotNull PacketBridge packetBridge, + @NotNull Location location) { + int entityId = entityBridge.nextEntityID(); + Packet spawnPacket = packetBridge.createSpawnLivingEntityPacket(entityId, + entityBridge.getEntityTypeID(EntityType.ARMOR_STAND), entityBridge.randomUUID(), location.getX(), + location.getY(), location.getZ()); + Function<@NotNull Component, @NotNull Packet> updatePacket + = component -> packetBridge.createHologramLinePacket(entityId, component); + Packet destroyPacket = packetBridge.createDestroyEntityPacket(entityId); + + return new TextLine(spawnPacket, updatePacket, destroyPacket); + } + +}