diff --git a/api/src/main/java/net/azisaba/azipluginmessaging/api/AziPluginMessaging.java b/api/src/main/java/net/azisaba/azipluginmessaging/api/AziPluginMessaging.java index 37b2cca..02fc942 100644 --- a/api/src/main/java/net/azisaba/azipluginmessaging/api/AziPluginMessaging.java +++ b/api/src/main/java/net/azisaba/azipluginmessaging/api/AziPluginMessaging.java @@ -2,6 +2,7 @@ import net.azisaba.azipluginmessaging.api.entity.Player; import net.azisaba.azipluginmessaging.api.entity.PlayerAdapter; +import net.azisaba.azipluginmessaging.api.protocol.PacketQueue; import net.azisaba.azipluginmessaging.api.server.PacketSender; import org.jetbrains.annotations.NotNull; @@ -48,6 +49,13 @@ public interface AziPluginMessaging { */ PlayerAdapter getPlayerAdapter(@NotNull Class clazz); + /** + * Returns the packet queue. + * @return the packet queue + */ + @NotNull + PacketQueue getPacketQueue(); + interface Proxy { } diff --git a/api/src/main/java/net/azisaba/azipluginmessaging/api/protocol/PacketQueue.java b/api/src/main/java/net/azisaba/azipluginmessaging/api/protocol/PacketQueue.java new file mode 100644 index 0000000..114b2a0 --- /dev/null +++ b/api/src/main/java/net/azisaba/azipluginmessaging/api/protocol/PacketQueue.java @@ -0,0 +1,25 @@ +package net.azisaba.azipluginmessaging.api.protocol; + +import net.azisaba.azipluginmessaging.api.protocol.message.Message; +import net.azisaba.azipluginmessaging.api.server.PacketSender; +import org.jetbrains.annotations.NotNull; + +public abstract class PacketQueue { + public static final PacketQueue EMPTY = new Empty(); + + public abstract void add(@NotNull Protocol protocol, @NotNull Message message); + public abstract void flush(@NotNull PacketSender sender); + + private static class Empty extends PacketQueue { + private Empty() { + } + + @Override + public void add(@NotNull Protocol protocol, @NotNull Message message) { + } + + @Override + public void flush(@NotNull PacketSender sender) { + } + } +} diff --git a/api/src/main/java/net/azisaba/azipluginmessaging/api/protocol/Protocol.java b/api/src/main/java/net/azisaba/azipluginmessaging/api/protocol/Protocol.java index 725f1a5..6ecbbfc 100644 --- a/api/src/main/java/net/azisaba/azipluginmessaging/api/protocol/Protocol.java +++ b/api/src/main/java/net/azisaba/azipluginmessaging/api/protocol/Protocol.java @@ -1,5 +1,6 @@ package net.azisaba.azipluginmessaging.api.protocol; +import net.azisaba.azipluginmessaging.api.AziPluginMessagingProvider; import net.azisaba.azipluginmessaging.api.Logger; import net.azisaba.azipluginmessaging.api.protocol.handler.MessageHandler; import net.azisaba.azipluginmessaging.api.protocol.handler.ProxyMessageHandler; @@ -100,13 +101,22 @@ public T getHandler() { /** * Attempt to send a packet. - * @param sender the packet sender to send the packet from. + * @param sender the packet sender to send the packet from. if null, the packet will always be queued. * @param msg the message to send * @return true if the message was sent successfully, false otherwise. */ - public boolean sendPacket(@NotNull PacketSender sender, @NotNull M msg) { - if (id != 0 && !sender.isEncrypted()) { - throw new IllegalStateException("Cannot send packet " + id + " without encryption"); + public boolean sendPacket(@Nullable PacketSender sender, @NotNull M msg) { + if (id != 0 && (sender == null || !sender.isEncrypted())) { + if (this.getPacketFlow() == PacketFlow.TO_SERVER) { + throw new IllegalStateException("Cannot send packet " + id + " without encryption"); + } + // usually it should get executed soon after the encryption is done + AziPluginMessagingProvider.get().getPacketQueue().add(this, msg); + Logger.getCurrentLogger().info("Queued packet " + id + " to send after encryption (sender: {})", sender); + return true; + } + if (sender == null) { + throw new IllegalArgumentException("Cannot send packet " + id + " without sender"); } try (ByteArrayOutputStream bout = new ByteArrayOutputStream(); DataOutputStream out = new DataOutputStream(bout)) { diff --git a/api/src/main/java/net/azisaba/azipluginmessaging/api/protocol/handler/ServerboundEncryptionPacket.java b/api/src/main/java/net/azisaba/azipluginmessaging/api/protocol/handler/ServerboundEncryptionPacket.java index f0d0be3..1c0b736 100644 --- a/api/src/main/java/net/azisaba/azipluginmessaging/api/protocol/handler/ServerboundEncryptionPacket.java +++ b/api/src/main/java/net/azisaba/azipluginmessaging/api/protocol/handler/ServerboundEncryptionPacket.java @@ -1,5 +1,6 @@ package net.azisaba.azipluginmessaging.api.protocol.handler; +import net.azisaba.azipluginmessaging.api.AziPluginMessagingProvider; import net.azisaba.azipluginmessaging.api.Logger; import net.azisaba.azipluginmessaging.api.entity.Player; import net.azisaba.azipluginmessaging.api.protocol.message.EncryptionMessage; @@ -38,5 +39,7 @@ public void handle(@NotNull PacketSender sender, @NotNull EncryptionMessage msg) if (Constants.DEBUG) { Logger.getCurrentLogger().info("Encryption enabled for " + sender); } + + AziPluginMessagingProvider.get().getPacketQueue().flush(sender); } } diff --git a/build.gradle.kts b/build.gradle.kts index f9b5cca..ab65d2b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,7 +6,7 @@ plugins { } group = "net.azisaba.azipluginmessaging" -version = "2.0.3" +version = "2.1.0" repositories { mavenCentral() diff --git a/spigot/src/main/java/net/azisaba/azipluginmessaging/spigot/AziPluginMessagingSpigot.java b/spigot/src/main/java/net/azisaba/azipluginmessaging/spigot/AziPluginMessagingSpigot.java index f0e268b..998ada0 100644 --- a/spigot/src/main/java/net/azisaba/azipluginmessaging/spigot/AziPluginMessagingSpigot.java +++ b/spigot/src/main/java/net/azisaba/azipluginmessaging/spigot/AziPluginMessagingSpigot.java @@ -3,11 +3,14 @@ import net.azisaba.azipluginmessaging.api.AziPluginMessaging; import net.azisaba.azipluginmessaging.api.Logger; import net.azisaba.azipluginmessaging.api.entity.PlayerAdapter; +import net.azisaba.azipluginmessaging.api.protocol.PacketQueue; import net.azisaba.azipluginmessaging.api.server.PacketSender; import net.azisaba.azipluginmessaging.spigot.entity.PlayerImpl; +import net.azisaba.azipluginmessaging.spigot.protocol.SimplePacketQueue; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.List; import java.util.Optional; @@ -15,8 +18,9 @@ import java.util.stream.Collectors; public class AziPluginMessagingSpigot implements AziPluginMessaging { + private final PacketQueue packetQueue = new SimplePacketQueue(); private final Logger logger; - private final Server server; + private final ServerImpl server; public AziPluginMessagingSpigot(@NotNull SpigotPlugin plugin) { this.logger = Logger.createFromJavaLogger(plugin.getLogger()); @@ -34,7 +38,7 @@ public AziPluginMessagingSpigot(@NotNull SpigotPlugin plugin) { } @Override - public @NotNull Server getServer() { + public @NotNull ServerImpl getServer() { return server; } @@ -52,6 +56,11 @@ public PlayerAdapter getPlayerAdapter(@NotNull Class clazz) { return (PlayerAdapter) (PlayerAdapter) PlayerImpl::of; } + @Override + public @NotNull PacketQueue getPacketQueue() { + return packetQueue; + } + public static class ServerImpl implements Server { @Override public @NotNull PacketSender getPacketSender() { @@ -64,5 +73,15 @@ public static class ServerImpl implements Server { Optional encryptedPlayer = players.stream().filter(PlayerImpl::isEncrypted).findAny(); return encryptedPlayer.orElseGet(() -> players.get(0)); } + + public @Nullable PacketSender getPacketSenderOrNull() { + try { + PacketSender sender = getPacketSender(); + if (sender.isEncrypted()) { + return sender; + } + } catch (RuntimeException ignored) {} + return null; + } } } diff --git a/spigot/src/main/java/net/azisaba/azipluginmessaging/spigot/SpigotPlugin.java b/spigot/src/main/java/net/azisaba/azipluginmessaging/spigot/SpigotPlugin.java index 14176c2..efb509d 100644 --- a/spigot/src/main/java/net/azisaba/azipluginmessaging/spigot/SpigotPlugin.java +++ b/spigot/src/main/java/net/azisaba/azipluginmessaging/spigot/SpigotPlugin.java @@ -16,6 +16,7 @@ import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.security.KeyPair; import java.util.Objects; @@ -50,6 +51,14 @@ public static PacketSender getAnyPacketSender() { return AziPluginMessagingProvider.get().getServer().getPacketSender(); } + @Nullable + public static PacketSender getAnyPacketSenderOrNull() { + if (Bukkit.getOnlinePlayers().isEmpty()) { + return null; + } + return ((AziPluginMessagingSpigot.ServerImpl) AziPluginMessagingProvider.get().getServer()).getPacketSenderOrNull(); + } + @EventHandler public void onPlayerJoin(PlayerJoinEvent e) { Bukkit.getScheduler().runTaskLaterAsynchronously(this, () -> { diff --git a/spigot/src/main/java/net/azisaba/azipluginmessaging/spigot/commands/GiveGamingSaraCommand.java b/spigot/src/main/java/net/azisaba/azipluginmessaging/spigot/commands/GiveGamingSaraCommand.java index 6534571..e381f23 100644 --- a/spigot/src/main/java/net/azisaba/azipluginmessaging/spigot/commands/GiveGamingSaraCommand.java +++ b/spigot/src/main/java/net/azisaba/azipluginmessaging/spigot/commands/GiveGamingSaraCommand.java @@ -18,7 +18,7 @@ public void execute(@NotNull CommandSender sender, @NotNull String[] args) { return; } Player target = PlayerUtil.getOfflinePlayer(args[0]); - boolean res = Protocol.P_GIVE_GAMING_SARA.sendPacket(SpigotPlugin.getAnyPacketSender(), new PlayerMessage(target)); + boolean res = Protocol.P_GIVE_GAMING_SARA.sendPacket(SpigotPlugin.getAnyPacketSenderOrNull(), new PlayerMessage(target)); if (res) { sender.sendMessage(ChatColor.GREEN + "Sent a request to give " + target.getUsername() + " the gaming sara"); } else { diff --git a/spigot/src/main/java/net/azisaba/azipluginmessaging/spigot/commands/GiveSaraCommand.java b/spigot/src/main/java/net/azisaba/azipluginmessaging/spigot/commands/GiveSaraCommand.java index 0ccc446..8b42050 100644 --- a/spigot/src/main/java/net/azisaba/azipluginmessaging/spigot/commands/GiveSaraCommand.java +++ b/spigot/src/main/java/net/azisaba/azipluginmessaging/spigot/commands/GiveSaraCommand.java @@ -19,7 +19,7 @@ public void execute(@NotNull CommandSender sender, @NotNull String[] args) { } int amount = Integer.parseInt(args[0]); Player target = PlayerUtil.getOfflinePlayer(args[1]); - boolean res = Protocol.P_GIVE_SARA.sendPacket(SpigotPlugin.getAnyPacketSender(), new ProxyboundGiveSaraMessage(amount, target)); + boolean res = Protocol.P_GIVE_SARA.sendPacket(SpigotPlugin.getAnyPacketSenderOrNull(), new ProxyboundGiveSaraMessage(amount, target)); if (res) { sender.sendMessage(ChatColor.GREEN + "Sent a request to give " + target.getUsername() + " the " + amount + "yen sara"); } else { diff --git a/spigot/src/main/java/net/azisaba/azipluginmessaging/spigot/protocol/SimplePacketQueue.java b/spigot/src/main/java/net/azisaba/azipluginmessaging/spigot/protocol/SimplePacketQueue.java new file mode 100644 index 0000000..52a4a35 --- /dev/null +++ b/spigot/src/main/java/net/azisaba/azipluginmessaging/spigot/protocol/SimplePacketQueue.java @@ -0,0 +1,30 @@ +package net.azisaba.azipluginmessaging.spigot.protocol; + +import net.azisaba.azipluginmessaging.api.protocol.PacketQueue; +import net.azisaba.azipluginmessaging.api.protocol.Protocol; +import net.azisaba.azipluginmessaging.api.protocol.message.Message; +import net.azisaba.azipluginmessaging.api.server.PacketSender; +import org.jetbrains.annotations.NotNull; + +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +public class SimplePacketQueue extends PacketQueue { + private final List, Message>> list = new ArrayList<>(); + + public void add(@NotNull Protocol protocol, @NotNull Message message) { + Objects.requireNonNull(protocol, "protocol must not be null"); + Objects.requireNonNull(message, "message must not be null"); + list.add(new AbstractMap.SimpleImmutableEntry<>(protocol, message)); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + public void flush(@NotNull PacketSender sender) { + for (Map.Entry, ? extends Message> entry : list) { + ((Protocol) entry.getKey()).sendPacket(sender, entry.getValue()); + } + } +} diff --git a/velocity/src/main/java/net/azisaba/azipluginmessaging/velocity/AziPluginMessagingVelocity.java b/velocity/src/main/java/net/azisaba/azipluginmessaging/velocity/AziPluginMessagingVelocity.java index 844ed85..4a0c816 100644 --- a/velocity/src/main/java/net/azisaba/azipluginmessaging/velocity/AziPluginMessagingVelocity.java +++ b/velocity/src/main/java/net/azisaba/azipluginmessaging/velocity/AziPluginMessagingVelocity.java @@ -2,14 +2,11 @@ import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.ProxyServer; -import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier; import net.azisaba.azipluginmessaging.api.AziPluginMessaging; import net.azisaba.azipluginmessaging.api.Logger; import net.azisaba.azipluginmessaging.api.entity.PlayerAdapter; -import net.azisaba.azipluginmessaging.api.protocol.Protocol; -import net.azisaba.azipluginmessaging.api.server.PacketSender; +import net.azisaba.azipluginmessaging.api.protocol.PacketQueue; import net.azisaba.azipluginmessaging.velocity.entity.PlayerImpl; -import net.azisaba.azipluginmessaging.velocity.server.ServerConnectionImpl; import org.jetbrains.annotations.NotNull; import java.util.Optional; @@ -53,6 +50,11 @@ public PlayerAdapter getPlayerAdapter(@NotNull Class clazz) { return (PlayerAdapter) (PlayerAdapter) PlayerImpl::new; } + @Override + public @NotNull PacketQueue getPacketQueue() { + return PacketQueue.EMPTY; + } + public static class ProxyImpl implements Proxy { } }