diff --git a/api/src/main/java/net/azisaba/azipluginmessaging/api/entity/Player.java b/api/src/main/java/net/azisaba/azipluginmessaging/api/entity/Player.java index 53e0aa0..dee0123 100644 --- a/api/src/main/java/net/azisaba/azipluginmessaging/api/entity/Player.java +++ b/api/src/main/java/net/azisaba/azipluginmessaging/api/entity/Player.java @@ -21,7 +21,7 @@ public interface Player { UUID getUniqueId(); /** - * Sends a message to the player. This method might do nothing depending on the implementation. + * Sends a message to the player. * @param message the message */ void sendMessage(@NotNull String message); @@ -35,5 +35,11 @@ default String getUsernameOrUniqueId() { return getUsername() != null ? getUsername() : getUniqueId().toString(); } + /** + * Checks if the player has the challenge equals to the provided challenge. Challenge token is used for securing + * initial encryption packet. + * @param challenge the challenge token to check + * @return true if the player has the challenge equals to the provided challenge + */ boolean isChallengeEquals(@NotNull String challenge); } diff --git a/api/src/main/java/net/azisaba/azipluginmessaging/api/entity/SimplePlayer.java b/api/src/main/java/net/azisaba/azipluginmessaging/api/entity/SimplePlayer.java index a23defd..bcaf2dd 100644 --- a/api/src/main/java/net/azisaba/azipluginmessaging/api/entity/SimplePlayer.java +++ b/api/src/main/java/net/azisaba/azipluginmessaging/api/entity/SimplePlayer.java @@ -10,7 +10,8 @@ import java.util.UUID; /** - * Simple instance of {@link Player}. + * Simple instance of {@link Player}. It lacks most of the features, and for example, you can't send messages to this + * instance. */ public final class SimplePlayer implements Player { private final UUID uuid; diff --git a/api/src/main/java/net/azisaba/azipluginmessaging/api/protocol/PacketFlow.java b/api/src/main/java/net/azisaba/azipluginmessaging/api/protocol/PacketFlow.java index 936f8e6..bc64a98 100644 --- a/api/src/main/java/net/azisaba/azipluginmessaging/api/protocol/PacketFlow.java +++ b/api/src/main/java/net/azisaba/azipluginmessaging/api/protocol/PacketFlow.java @@ -1,6 +1,12 @@ package net.azisaba.azipluginmessaging.api.protocol; public enum PacketFlow { + /** + * Server (Backend) -> Proxy + */ TO_PROXY, + /** + * Proxy -> Server (Backend) + */ TO_SERVER, } 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 index 114b2a0..61e9ade 100644 --- a/api/src/main/java/net/azisaba/azipluginmessaging/api/protocol/PacketQueue.java +++ b/api/src/main/java/net/azisaba/azipluginmessaging/api/protocol/PacketQueue.java @@ -5,9 +5,22 @@ import org.jetbrains.annotations.NotNull; public abstract class PacketQueue { + /** + * Empty packet queue that does nothing. + */ public static final PacketQueue EMPTY = new Empty(); + /** + * Adds the message to the queue. + * @param protocol The protocol. + * @param message The message. + */ public abstract void add(@NotNull Protocol protocol, @NotNull Message message); + + /** + * Removes the message from the queue and sends all messages to provided sender. + * @param sender the sender + */ public abstract void flush(@NotNull PacketSender sender); private static class Empty extends PacketQueue { 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 7852bf2..f4a95ed 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 @@ -45,7 +45,15 @@ public final class Protocol, M extends Message> { private static final Map> TO_PROXY_BY_ID = new ConcurrentHashMap<>(); private static final Map> TO_SERVER_BY_ID = new ConcurrentHashMap<>(); + + /** + * Legacy plugin channel id + */ public static final String LEGACY_CHANNEL_ID = "AziPluginMessaging"; + + /** + * Modern plugin channel id + */ public static final String CHANNEL_ID = "azipm:main"; public static final Protocol P_ENCRYPTION = new Protocol<>(PacketFlow.TO_PROXY, 0x00, new ProxyboundEncryptionPacket()); @@ -65,6 +73,16 @@ public final class Protocol, M extends Message> { private final byte id; private final T handler; + /** + * Creates a new packet. Handler should be a subclass of {@link ProxyMessageHandler} if packet flow is + * {@link PacketFlow#TO_PROXY}, and a subclass of {@link ServerMessageHandler} if packet flow is + * {@link PacketFlow#TO_SERVER}. + * @param packetFlow The packet flow + * @param id The packet id, must be unique in the packet flow + * @param handler The handler + * @throws IllegalArgumentException if the id is already registered + * @throws IllegalArgumentException if the handler is implementing wrong type for the packet flow + */ private Protocol(@NotNull PacketFlow packetFlow, int id, @NotNull T handler) { this.packetFlow = packetFlow; this.id = (byte) (id & 0xFF); @@ -74,7 +92,7 @@ private Protocol(@NotNull PacketFlow packetFlow, int id, @NotNull T handler) { throw new IllegalArgumentException("Handler must be instance of ProxyMessageHandler"); } if (TO_PROXY_BY_ID.containsKey(this.id)) { - throw new AssertionError("Duplicate protocol id: " + this.id); + throw new IllegalArgumentException("Duplicate protocol id: " + this.id); } TO_PROXY_BY_ID.put(this.id, this); } else { @@ -82,7 +100,7 @@ private Protocol(@NotNull PacketFlow packetFlow, int id, @NotNull T handler) { throw new IllegalArgumentException("Handler must be instance of ServerMessageHandler"); } if (TO_SERVER_BY_ID.containsKey(this.id)) { - throw new AssertionError("Duplicate protocol id: " + this.id); + throw new IllegalArgumentException("Duplicate protocol id: " + this.id); } TO_SERVER_BY_ID.put(this.id, this); } @@ -149,9 +167,11 @@ public boolean sendPacket(@Nullable PacketSender sender, @NotNull M msg) { } /** - * This method is called when a packet is received (proxy-side). + * This method is called when a raw packet is received (proxy-side). * @param server the server connection * @param rawData the data of the packet + * @throws RuntimeException if the connection is encrypted but cannot decrypt the packet + * @throws RuntimeException if the packet must be received encrypted but the connection is not encrypted */ public static void handleProxySide(ServerConnection server, byte[] rawData) { byte[] data; @@ -168,7 +188,7 @@ public static void handleProxySide(ServerConnection server, byte[] rawData) { DataInputStream in = new DataInputStream(bin)) { byte id = (byte) (in.readByte() & 0xFF); if (id != 0 && !server.isEncrypted()) { - throw new RuntimeException("Packet " + id + " must be sent encrypted (server: " + server + ")"); + throw new RuntimeException("Packet " + id + " must be received encrypted (server: " + server + ")"); } Protocol protocol = Protocol.getById(PacketFlow.TO_PROXY, id); if (protocol == null) { @@ -183,7 +203,7 @@ public static void handleProxySide(ServerConnection server, byte[] rawData) { Logger.getCurrentLogger().info("Received packet {} (0x{}) from server connection {} (encrypted: {})", id, hex, server, server.isEncrypted()); } if (protocol.packetFlow != PacketFlow.TO_PROXY) { - throw new IllegalArgumentException("Packet " + protocol + " is not proxybound"); + throw new AssertionError("Packet " + protocol + " is not proxybound"); } @SuppressWarnings("unchecked") ProxyMessageHandler handler = (ProxyMessageHandler) protocol.getHandler(); @@ -195,8 +215,10 @@ public static void handleProxySide(ServerConnection server, byte[] rawData) { } /** - * This method is called when a packet is received (server-side). + * This method is called when a raw packet is received (server-side). * @param rawData the data of the packet + * @throws RuntimeException if the connection is encrypted but cannot decrypt the packet + * @throws RuntimeException if the packet must be received encrypted but the connection is not encrypted */ public static void handleServerSide(@NotNull PacketSender sender, byte[] rawData) { byte[] data; @@ -213,7 +235,7 @@ public static void handleServerSide(@NotNull PacketSender sender, byte[] rawData DataInputStream in = new DataInputStream(bin)) { byte id = (byte) (in.readByte() & 0xFF); if (id != 0 && !sender.isEncrypted()) { - throw new RuntimeException("Packet " + id + " must be sent encrypted (sender: " + sender + ")"); + throw new RuntimeException("Packet " + id + " must be received encrypted (sender: " + sender + ")"); } Protocol protocol = Protocol.getById(PacketFlow.TO_SERVER, id); if (protocol == null) { @@ -226,7 +248,7 @@ public static void handleServerSide(@NotNull PacketSender sender, byte[] rawData Logger.getCurrentLogger().info("Received packet {} (0x{}) from {}", id, hex, sender); } if (protocol.packetFlow != PacketFlow.TO_SERVER) { - throw new IllegalArgumentException("Packet " + protocol + " is not serverbound"); + throw new AssertionError("Packet " + protocol + " is not serverbound"); } @SuppressWarnings("unchecked") ServerMessageHandler handler = (ServerMessageHandler) protocol.getHandler(); @@ -237,6 +259,10 @@ public static void handleServerSide(@NotNull PacketSender sender, byte[] rawData } } + /** + * Returns the packet flow of this packet. + * @return the packet flow + */ @NotNull @Contract(pure = true) public PacketFlow getPacketFlow() { @@ -271,10 +297,15 @@ public PacketFlow getPacketFlow() { } } + /** + * Returns the protocol data in string form. + * @return the protocol data + */ @Override public String toString() { return "Protocol{" + "id=" + id + + ", packetFlow=" + packetFlow + ", handler=" + handler + '}'; } diff --git a/api/src/main/java/net/azisaba/azipluginmessaging/api/protocol/handler/ServerboundActionResponsePacket.java b/api/src/main/java/net/azisaba/azipluginmessaging/api/protocol/handler/ServerboundActionResponsePacket.java index a7c5805..4574dda 100644 --- a/api/src/main/java/net/azisaba/azipluginmessaging/api/protocol/handler/ServerboundActionResponsePacket.java +++ b/api/src/main/java/net/azisaba/azipluginmessaging/api/protocol/handler/ServerboundActionResponsePacket.java @@ -9,6 +9,9 @@ import java.io.IOException; import java.util.UUID; +/** + * Sends the arbitrary message to a player. + */ public class ServerboundActionResponsePacket implements ServerMessageHandler { @NotNull @Override @@ -20,6 +23,8 @@ public ServerboundActionResponseMessage read(@NotNull DataInputStream in) throws @Override public void handle(@NotNull PacketSender sender, @NotNull ServerboundActionResponseMessage msg) throws Exception { - AziPluginMessagingProvider.get().getPlayer(msg.getUniqueId()).ifPresent(player -> player.sendMessage(msg.getMessage())); + AziPluginMessagingProvider.get() + .getPlayer(msg.getUniqueId()) + .ifPresent(player -> player.sendMessage(msg.getMessage())); } } diff --git a/api/src/main/java/net/azisaba/azipluginmessaging/api/protocol/message/ServerboundActionResponseMessage.java b/api/src/main/java/net/azisaba/azipluginmessaging/api/protocol/message/ServerboundActionResponseMessage.java index 8389769..072e8a5 100644 --- a/api/src/main/java/net/azisaba/azipluginmessaging/api/protocol/message/ServerboundActionResponseMessage.java +++ b/api/src/main/java/net/azisaba/azipluginmessaging/api/protocol/message/ServerboundActionResponseMessage.java @@ -10,16 +10,29 @@ public class ServerboundActionResponseMessage implements Message { private final UUID uuid; private final String message; + /** + * Creates a new instance. + * @param uuid The UUID of the player. + * @param message The message (may contain \u00a7). + */ public ServerboundActionResponseMessage(@NotNull UUID uuid, @NotNull String message) { this.uuid = uuid; this.message = message; } + /** + * Gets the UUID of the player. + * @return the uuid + */ @NotNull public UUID getUniqueId() { return uuid; } + /** + * Gets the message. + * @return the message + */ @NotNull public String getMessage() { return message; diff --git a/api/src/main/java/net/azisaba/azipluginmessaging/api/server/Connection.java b/api/src/main/java/net/azisaba/azipluginmessaging/api/server/Connection.java new file mode 100644 index 0000000..553ed12 --- /dev/null +++ b/api/src/main/java/net/azisaba/azipluginmessaging/api/server/Connection.java @@ -0,0 +1,37 @@ +package net.azisaba.azipluginmessaging.api.server; + +import org.jetbrains.annotations.NotNull; + +import java.security.KeyPair; +import java.security.PublicKey; + +/** + * Represents something that can have a connection. + */ +public interface Connection { + /** + * Marks the connection as encrypted. + * @param encrypted true if the connection is encrypted; false otherwise + */ + void setEncrypted(boolean encrypted); + + /** + * Checks if the connection is encrypted. + * @return true if the connection is encrypted; false otherwise + */ + boolean isEncrypted(); + + /** + * Gets the local key pair for encrypting packets. + * @return the local key pair + */ + @NotNull + KeyPair getKeyPair(); + + /** + * Gets the remote public key for decrypting packets. + * @return the remote public key + */ + @NotNull + PublicKey getRemotePublicKey(); +} diff --git a/api/src/main/java/net/azisaba/azipluginmessaging/api/server/PacketSender.java b/api/src/main/java/net/azisaba/azipluginmessaging/api/server/PacketSender.java index 3a8402a..a7733b9 100644 --- a/api/src/main/java/net/azisaba/azipluginmessaging/api/server/PacketSender.java +++ b/api/src/main/java/net/azisaba/azipluginmessaging/api/server/PacketSender.java @@ -8,7 +8,7 @@ /** * Represents an object that can send packet to target. */ -public interface PacketSender { +public interface PacketSender extends Connection { /** * Attempt to send a packet to the target. * @param data the data @@ -16,15 +16,5 @@ public interface PacketSender { */ boolean sendPacket(byte @NotNull [] data); - void setEncrypted(boolean encrypted); - - boolean isEncrypted(); - - @NotNull - KeyPair getKeyPair(); - - @NotNull - PublicKey getRemotePublicKey(); - void setRemotePublicKey(@NotNull PublicKey publicKey); } diff --git a/api/src/main/java/net/azisaba/azipluginmessaging/api/util/Base64Util.java b/api/src/main/java/net/azisaba/azipluginmessaging/api/util/Base64Util.java index a432cd2..7cb985e 100644 --- a/api/src/main/java/net/azisaba/azipluginmessaging/api/util/Base64Util.java +++ b/api/src/main/java/net/azisaba/azipluginmessaging/api/util/Base64Util.java @@ -6,12 +6,22 @@ import java.util.Base64; public class Base64Util { + /** + * Encodes the given byte array to a base64 string. + * @param bytes the byte array + * @return the base64 string + */ @Contract("_ -> new") - public static @NotNull String encode(byte[] bytes) { + public static @NotNull String encode(byte @NotNull [] bytes) { return Base64.getEncoder().encodeToString(bytes); } - public static byte[] decode(@NotNull String string) { + /** + * Decodes the given base64 string to a byte array. + * @param string the base64 string + * @return the byte array + */ + public static byte @NotNull [] decode(@NotNull String string) { return Base64.getDecoder().decode(string); } } diff --git a/api/src/main/java/net/azisaba/azipluginmessaging/api/util/Constants.java b/api/src/main/java/net/azisaba/azipluginmessaging/api/util/Constants.java index ce6b734..6822839 100644 --- a/api/src/main/java/net/azisaba/azipluginmessaging/api/util/Constants.java +++ b/api/src/main/java/net/azisaba/azipluginmessaging/api/util/Constants.java @@ -3,6 +3,14 @@ import java.util.Arrays; import java.util.List; +/** + * @deprecated Deprecated because {@link #SARA_GROUPS} is deprecated. + */ +@Deprecated public class Constants { + /** + * @deprecated sara track in LuckPerms should be used instead + */ + @Deprecated public static final List SARA_GROUPS = Arrays.asList(50000, 20000, 10000, 5000, 2000, 1000, 500, 100); } diff --git a/api/src/main/java/net/azisaba/azipluginmessaging/api/util/EncryptionUtil.java b/api/src/main/java/net/azisaba/azipluginmessaging/api/util/EncryptionUtil.java index d4afdcc..3619a5d 100644 --- a/api/src/main/java/net/azisaba/azipluginmessaging/api/util/EncryptionUtil.java +++ b/api/src/main/java/net/azisaba/azipluginmessaging/api/util/EncryptionUtil.java @@ -10,7 +10,13 @@ import java.security.PublicKey; import java.security.spec.RSAKeyGenParameterSpec; +/** + * Represents a utility class for encryption. + */ public class EncryptionUtil { + /** + * Algorithm to use for encrypting and decrypting packets. + */ public static final String ALGORITHM = "RSA"; /** @@ -56,11 +62,22 @@ public static byte[] decrypt(byte[] data, @NotNull PrivateKey key) throws Except return cipher.doFinal(data); } + /** + * Encodes the public key as base64 string. + * @param key The key to encode + * @return The base64 string + */ @Contract("_ -> new") public static @NotNull String encodePublicKey(@NotNull PublicKey key) { return Base64Util.encode(key.getEncoded()); } + /** + * Decodes the base64 string to a public key. + * @param key The base64 string + * @return The public key + * @throws Exception If a public key could not be decoded for any reason + */ @Contract("_ -> new") public static @NotNull PublicKey decodePublicKey(@NotNull String key) throws Exception { return KeyFactoryUtil.getPublicKey(Base64Util.decode(key)); diff --git a/api/src/main/java/net/azisaba/azipluginmessaging/api/util/KeyFactoryUtil.java b/api/src/main/java/net/azisaba/azipluginmessaging/api/util/KeyFactoryUtil.java index 341c78f..217c280 100644 --- a/api/src/main/java/net/azisaba/azipluginmessaging/api/util/KeyFactoryUtil.java +++ b/api/src/main/java/net/azisaba/azipluginmessaging/api/util/KeyFactoryUtil.java @@ -5,6 +5,12 @@ import java.security.spec.X509EncodedKeySpec; public class KeyFactoryUtil { + /** + * Creates the new instance of PublicKey from given encoded data. + * @param encoded the encoded data + * @return the PublicKey + * @throws Exception If PublicKey could not be created for any reason. + */ public static PublicKey getPublicKey(byte[] encoded) throws Exception { X509EncodedKeySpec spec = new X509EncodedKeySpec(encoded); KeyFactory kf = KeyFactory.getInstance(EncryptionUtil.ALGORITHM); diff --git a/api/src/main/java/net/azisaba/azipluginmessaging/api/util/LuckPermsUtil.java b/api/src/main/java/net/azisaba/azipluginmessaging/api/util/LuckPermsUtil.java index e247102..842fb5f 100644 --- a/api/src/main/java/net/azisaba/azipluginmessaging/api/util/LuckPermsUtil.java +++ b/api/src/main/java/net/azisaba/azipluginmessaging/api/util/LuckPermsUtil.java @@ -63,6 +63,13 @@ public static Node findParentNode(@NotNull NodeMap map, @NotNull String group, @ return map.add(builder.build()); } + /** + * Finds the prefix nodes set by {@link #setPrefix(NodeMap, String, String)}. + * @param map the node map + * @param server the server to apply server context to + * @return the stream of prefix nodes + * @see #findPrefixNode(NodeMap, String, String) findPrefixNode(NodeMap, String, String) for finding a specific prefix node + */ @NotNull public static Stream findPrefixNodes(@NotNull NodeMap map, @Nullable String server) { if (server == null) { @@ -81,6 +88,14 @@ public static Stream findPrefixNodes(@NotNull NodeMap map, @Nullable Strin node.getContexts().getValues("server").contains(server)); } + /** + * Find a prefix node set by {@link #setPrefix(NodeMap, String, String)}. + * @param map the node map + * @param prefix the prefix to find + * @param server the server to apply server context to + * @return the node, or null if not found + * @see #findPrefixNodes(NodeMap, String) findPrefixNodes(NodeMap, String) for finding all prefix nodes under the given server + */ @Nullable public static Node findPrefixNode(@NotNull NodeMap map, @NotNull String prefix, @Nullable String server) { if (server == null) { @@ -103,6 +118,13 @@ public static Node findPrefixNode(@NotNull NodeMap map, @NotNull String prefix, .orElse(null); } + /** + * Sets the prefix node. + * @param map the node map + * @param prefix the prefix to set + * @param server the server to apply server context to + * @return the result of the operation + */ public static @NotNull DataMutateResult setPrefix(@NotNull NodeMap map, @NotNull String prefix, @Nullable String server) { PrefixNode.Builder builder = PrefixNode.builder(prefix, 666).value(true); if (server != null) builder = builder.withContext("server", server); diff --git a/api/src/main/java/net/azisaba/azipluginmessaging/api/util/ReflectionUtil.java b/api/src/main/java/net/azisaba/azipluginmessaging/api/util/ReflectionUtil.java index 1af6876..b42999c 100644 --- a/api/src/main/java/net/azisaba/azipluginmessaging/api/util/ReflectionUtil.java +++ b/api/src/main/java/net/azisaba/azipluginmessaging/api/util/ReflectionUtil.java @@ -8,6 +8,13 @@ public class ReflectionUtil { // 1. look for method on superclass // 2. look for method on interfaces, then interfaces in interface... + + /** + * Finds the method recursively with the given method (uses name and parameter types for finding a method). + * @param clazz the base class to look for the method + * @param m the method to look for + * @return the method if found; null otherwise + */ @Nullable public static Method findMethod(@NotNull Class clazz, @NotNull Method m) { try { diff --git a/api/src/main/java/net/azisaba/azipluginmessaging/api/util/TokenUtil.java b/api/src/main/java/net/azisaba/azipluginmessaging/api/util/TokenUtil.java index 3ec5be1..6be68e2 100644 --- a/api/src/main/java/net/azisaba/azipluginmessaging/api/util/TokenUtil.java +++ b/api/src/main/java/net/azisaba/azipluginmessaging/api/util/TokenUtil.java @@ -8,6 +8,10 @@ public class TokenUtil { private static final SecureRandom RANDOM = new SecureRandom(); + /** + * Generates a new token. + * @return the token + */ public static @NotNull String generateNewToken() { return new BigInteger(130, RANDOM).toString(32); } diff --git a/velocity/src/main/java/net/azisaba/azipluginmessaging/velocity/server/ServerConnectionImpl.java b/velocity/src/main/java/net/azisaba/azipluginmessaging/velocity/server/ServerConnectionImpl.java index 72eefe0..0f77c57 100644 --- a/velocity/src/main/java/net/azisaba/azipluginmessaging/velocity/server/ServerConnectionImpl.java +++ b/velocity/src/main/java/net/azisaba/azipluginmessaging/velocity/server/ServerConnectionImpl.java @@ -98,7 +98,7 @@ public void setKeyPair(@NotNull KeyPair keyPair) { @Override public @NotNull PublicKey getRemotePublicKey() { - return publicKey; + return Objects.requireNonNull(publicKey, "public key is not set"); } @Override