From 14a8615746c7966c57c251222ffd2c2dbb67bf5b Mon Sep 17 00:00:00 2001 From: RoyceDa Date: Mon, 2 Feb 2026 05:13:32 +0200 Subject: [PATCH] =?UTF-8?q?=D0=A1=D0=B8=D1=81=D1=82=D0=B5=D0=BC=D0=B0=20?= =?UTF-8?q?=D0=B2=D0=BB=D0=BE=D0=B6=D0=B5=D0=BD=D0=BD=D1=8B=D1=85=20=D1=82?= =?UTF-8?q?=D0=B5=D0=B3=D0=BE=D0=B2=20(ECI),=20=D1=81=D0=B2=D1=8F=D0=B7?= =?UTF-8?q?=D1=8C=20=D1=81=20=D0=B1=D0=B0=D0=B7=D0=BE=D0=B9=20=D0=B4=D0=B0?= =?UTF-8?q?=D0=BD=D0=BD=D1=8B=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.dev.yml | 22 ++--- .../java/com/rosetta/im/Configuration.java | 7 ++ src/main/java/com/rosetta/im/Failures.java | 26 ++++++ src/main/java/com/rosetta/im/Main.java | 2 +- .../im/client/tags/Authentificate.java | 68 ++++++++++++++++ .../im/{device => client/tags}/Device.java | 2 +- .../rosetta/im/database/DatabaseManager.java | 23 ++++++ .../com/rosetta/im/database/entity/User.java | 11 --- .../im/executors/Executor0Handshake.java | 76 +++++++++++++++++- .../rosetta/im/packet/Packet0Handshake.java | 2 +- .../com/rosetta/im/protocol/BaseFailures.java | 9 +++ .../java/com/rosetta/im/protocol/Client.java | 50 ++++++++---- .../java/com/rosetta/im/protocol/ECITag.java | 31 +++++++ .../java/com/rosetta/im/protocol/Server.java | 12 ++- .../{Failures.java => ServerFailures.java} | 4 +- .../im/protocol/packet/PacketExecutor.java | 32 +++++++- src/main/resources/hibernate.cfg.xml | 8 +- target/classes/com/rosetta/im/Main.class | Bin 1061 -> 1115 bytes .../com/rosetta/im/device/Device.class | Bin 1064 -> 0 bytes .../im/executors/Executor0Handshake.class | Bin 953 -> 3725 bytes .../rosetta/im/packet/Packet0Handshake.class | Bin 3038 -> 3058 bytes .../com/rosetta/im/protocol/Client.class | Bin 2798 -> 3511 bytes .../com/rosetta/im/protocol/Server.class | Bin 6559 -> 6698 bytes .../im/protocol/packet/PacketExecutor.class | Bin 244 -> 765 bytes 24 files changed, 326 insertions(+), 59 deletions(-) create mode 100644 src/main/java/com/rosetta/im/Configuration.java create mode 100644 src/main/java/com/rosetta/im/Failures.java create mode 100644 src/main/java/com/rosetta/im/client/tags/Authentificate.java rename src/main/java/com/rosetta/im/{device => client/tags}/Device.java (95%) create mode 100644 src/main/java/com/rosetta/im/protocol/BaseFailures.java create mode 100644 src/main/java/com/rosetta/im/protocol/ECITag.java rename src/main/java/com/rosetta/im/protocol/{Failures.java => ServerFailures.java} (94%) delete mode 100644 target/classes/com/rosetta/im/device/Device.class diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 21b1c22..d40619c 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -2,27 +2,15 @@ version: '3.8' services: db: - image: postgres:16-alpine + image: postgres:latest environment: - POSTGRES_DB: rosetta_db - POSTGRES_USER: rosetta_user - POSTGRES_PASSWORD: rosetta_password + POSTGRES_DB: your_database + POSTGRES_USER: your_user + POSTGRES_PASSWORD: your_password ports: - "5432:5432" - volumes: - - postgres_data:/var/lib/postgresql/data - healthcheck: - test: ["CMD-SHELL", "pg_isready -U rosetta_user"] - interval: 10s - timeout: 5s - retries: 5 adminer: image: adminer ports: - - "8080:8080" - depends_on: - - db - -volumes: - postgres_data: \ No newline at end of file + - "8080:8080" \ No newline at end of file diff --git a/src/main/java/com/rosetta/im/Configuration.java b/src/main/java/com/rosetta/im/Configuration.java new file mode 100644 index 0000000..be0b559 --- /dev/null +++ b/src/main/java/com/rosetta/im/Configuration.java @@ -0,0 +1,7 @@ +package com.rosetta.im; + +public class Configuration { + + public static final int PROTOCOL_VERSION = 1; + +} diff --git a/src/main/java/com/rosetta/im/Failures.java b/src/main/java/com/rosetta/im/Failures.java new file mode 100644 index 0000000..7309553 --- /dev/null +++ b/src/main/java/com/rosetta/im/Failures.java @@ -0,0 +1,26 @@ +package com.rosetta.im; + +import com.rosetta.im.protocol.BaseFailures; + +public enum Failures implements BaseFailures { + /** + * Код ошибки, указывающий на неизвестную ошибку. + */ + UNSUPPORTED_PROTOCOL(3008); + + private final int code; + + Failures(int code) { + this.code = code; + } + + /** + * Получает код ошибки. + * @return Код ошибки. + */ + public int getCode() { + return code; + } + + +} diff --git a/src/main/java/com/rosetta/im/Main.java b/src/main/java/com/rosetta/im/Main.java index 20f20de..492f78e 100644 --- a/src/main/java/com/rosetta/im/Main.java +++ b/src/main/java/com/rosetta/im/Main.java @@ -22,7 +22,7 @@ public class Main { /** * Запуск сервера на порту 8881 */ - Server server = new Server(settings, manager); + Server server = new Server(settings, manager, Configuration.class); server.start(); } } \ No newline at end of file diff --git a/src/main/java/com/rosetta/im/client/tags/Authentificate.java b/src/main/java/com/rosetta/im/client/tags/Authentificate.java new file mode 100644 index 0000000..82c350b --- /dev/null +++ b/src/main/java/com/rosetta/im/client/tags/Authentificate.java @@ -0,0 +1,68 @@ +package com.rosetta.im.client.tags; + +import com.rosetta.im.packet.enums.HandshakeStage; +import com.rosetta.im.protocol.ECITag; + +/** + * Это вложенный обьект для клиента, содержащий информацию об аутентификации. + */ +public class Authentificate extends ECITag { + + public Device device; + public String publicKey; + public String privateKey; + public HandshakeStage handshakeStage; + + public Authentificate() { + super("authentificate"); + } + + public Authentificate(Device device, String publicKey, String privateKey, HandshakeStage handshakeStage) { + super("authentificate"); + this.device = device; + this.publicKey = publicKey; + this.privateKey = privateKey; + this.handshakeStage = handshakeStage; + } + + public Device getDevice() { + return device; + } + + public String getPublicKey() { + return publicKey; + } + + public String getPrivateKey() { + return privateKey; + } + + public HandshakeStage getHandshakeStage() { + return handshakeStage; + } + + public void setDevice(Device device) { + this.device = device; + } + + public void setPublicKey(String publicKey) { + this.publicKey = publicKey; + } + + public void setPrivateKey(String privateKey) { + this.privateKey = privateKey; + } + + public void setHandshakeStage(HandshakeStage handshakeStage) { + this.handshakeStage = handshakeStage; + } + + /** + * Проверяет, прошел ли клиент аутентификацию. В том числе подтвердил ли устройство. + * @return true если аутентификация пройдена, иначе false. + */ + public boolean hasAuthorized() { + return this.handshakeStage == HandshakeStage.COMPLETED; + } + +} diff --git a/src/main/java/com/rosetta/im/device/Device.java b/src/main/java/com/rosetta/im/client/tags/Device.java similarity index 95% rename from src/main/java/com/rosetta/im/device/Device.java rename to src/main/java/com/rosetta/im/client/tags/Device.java index e3ebfb9..e323afe 100644 --- a/src/main/java/com/rosetta/im/device/Device.java +++ b/src/main/java/com/rosetta/im/client/tags/Device.java @@ -1,4 +1,4 @@ -package com.rosetta.im.device; +package com.rosetta.im.client.tags; public class Device { diff --git a/src/main/java/com/rosetta/im/database/DatabaseManager.java b/src/main/java/com/rosetta/im/database/DatabaseManager.java index 62029b7..bbb5c1c 100644 --- a/src/main/java/com/rosetta/im/database/DatabaseManager.java +++ b/src/main/java/com/rosetta/im/database/DatabaseManager.java @@ -3,6 +3,7 @@ package com.rosetta.im.database; import org.hibernate.Session; import org.hibernate.Transaction; +import java.util.HashMap; import java.util.List; public class DatabaseManager { @@ -61,6 +62,28 @@ public class DatabaseManager { } } + public static T findBy(Class entity, HashMap params) { + Session session = HibernateUtil.openSession(); + try { + StringBuilder queryString = new StringBuilder("FROM " + entity.getSimpleName() + " WHERE "); + int index = 0; + for (String key : params.keySet()) { + if (index > 0) { + queryString.append(" AND "); + } + queryString.append(key).append(" = :").append(key); + index++; + } + var query = session.createQuery(queryString.toString(), entity); + for (var entry : params.entrySet()) { + query.setParameter(entry.getKey(), entry.getValue()); + } + return query.uniqueResult(); + } finally { + session.close(); + } + } + public static void delete(T entity) { Session session = HibernateUtil.openSession(); Transaction transaction = null; diff --git a/src/main/java/com/rosetta/im/database/entity/User.java b/src/main/java/com/rosetta/im/database/entity/User.java index e6d7a40..d757604 100644 --- a/src/main/java/com/rosetta/im/database/entity/User.java +++ b/src/main/java/com/rosetta/im/database/entity/User.java @@ -43,9 +43,6 @@ public class User extends BaseEntity { @Column(name = "publicKey", nullable = false, unique = true) private String publicKey; - @Column(name = "timestamp", nullable = false) - private long timestamp; - @Column(name = "createdTime", nullable = false, updatable = false) private Date createdTime; @@ -104,14 +101,6 @@ public class User extends BaseEntity { this.publicKey = publicKey; } - public long getTimestamp() { - return timestamp; - } - - public void setTimestamp(long timestamp) { - this.timestamp = timestamp; - } - public Date getCreatedTime() { return createdTime; } diff --git a/src/main/java/com/rosetta/im/executors/Executor0Handshake.java b/src/main/java/com/rosetta/im/executors/Executor0Handshake.java index eda8ba5..7f99260 100644 --- a/src/main/java/com/rosetta/im/executors/Executor0Handshake.java +++ b/src/main/java/com/rosetta/im/executors/Executor0Handshake.java @@ -1,18 +1,90 @@ package com.rosetta.im.executors; +import java.util.HashMap; + +import com.rosetta.im.Configuration; +import com.rosetta.im.Failures; +import com.rosetta.im.client.tags.Authentificate; +import com.rosetta.im.client.tags.Device; +import com.rosetta.im.database.DatabaseManager; +import com.rosetta.im.database.entity.User; import com.rosetta.im.packet.Packet0Handshake; +import com.rosetta.im.packet.enums.HandshakeStage; import com.rosetta.im.protocol.Client; import com.rosetta.im.protocol.packet.Packet; import com.rosetta.im.protocol.packet.PacketExecutor; -public class Executor0Handshake implements PacketExecutor { +public class Executor0Handshake extends PacketExecutor { @Override public void onPacketReceived(Packet packet, Client client) { Packet0Handshake handshake = (Packet0Handshake) packet; String publicKey = handshake.getPublicKey(); String privateKey = handshake.getPrivateKey(); - int b = 0; + Device device = handshake.getDevice(); + int protocolVersion = handshake.getProtocolVersion(); + + /** + * Получаем информацию об аутентификации клиента + * используя возможности ECI тэгов. + */ + Authentificate authentificate = client.getTag(new Authentificate()); + if(authentificate != null && authentificate.hasAuthorized()) { + /** + * Клиент уже авторизован, повторный хэндшейк не допускается + */ + return; + } + + /** + * Проверяем корректность версии протокола + */ + if(protocolVersion != Configuration.PROTOCOL_VERSION) { + client.disconnect(Failures.UNSUPPORTED_PROTOCOL); + return; + } + + /** + * Проверяем есть ли такой пользователь + */ + User user = DatabaseManager.findBy(User.class, new HashMap<>() {{ + put("publicKey", publicKey); + }}); + + if(user == null) { + /** + * Пользователь не найден, создаем нового + */ + user = new User(); + user.setPrivateKey(privateKey); + user.setPublicKey(publicKey); + user.setUsername(""); + user.setTitle(publicKey.substring(0, 7)); + /** + * Новый пользователь не верифицирован + */ + user.setVerified(0); + + DatabaseManager.save(user); + + Authentificate eciTag = new Authentificate(device, publicKey, privateKey, HandshakeStage.COMPLETED); + client.addTag(eciTag); + + + System.out.println("New user created: " + user.getId()); + + Authentificate authTag = client.getTag(new Authentificate()); + + System.out.println("eciTag" + authTag); + + + return; + } + + + + + } } diff --git a/src/main/java/com/rosetta/im/packet/Packet0Handshake.java b/src/main/java/com/rosetta/im/packet/Packet0Handshake.java index f21ec45..11480a1 100644 --- a/src/main/java/com/rosetta/im/packet/Packet0Handshake.java +++ b/src/main/java/com/rosetta/im/packet/Packet0Handshake.java @@ -1,6 +1,6 @@ package com.rosetta.im.packet; -import com.rosetta.im.device.Device; +import com.rosetta.im.client.tags.Device; import com.rosetta.im.packet.enums.HandshakeStage; import com.rosetta.im.protocol.Stream; import com.rosetta.im.protocol.packet.Packet; diff --git a/src/main/java/com/rosetta/im/protocol/BaseFailures.java b/src/main/java/com/rosetta/im/protocol/BaseFailures.java new file mode 100644 index 0000000..91c2c03 --- /dev/null +++ b/src/main/java/com/rosetta/im/protocol/BaseFailures.java @@ -0,0 +1,9 @@ +package com.rosetta.im.protocol; + +public interface BaseFailures { + /** + * Получает код ошибки. + * @return + */ + int getCode(); +} diff --git a/src/main/java/com/rosetta/im/protocol/Client.java b/src/main/java/com/rosetta/im/protocol/Client.java index a285afe..0656f76 100644 --- a/src/main/java/com/rosetta/im/protocol/Client.java +++ b/src/main/java/com/rosetta/im/protocol/Client.java @@ -1,7 +1,7 @@ package com.rosetta.im.protocol; -import java.util.HashMap; -import java.util.Map; +import java.util.HashSet; +import java.util.Set; import org.java_websocket.WebSocket; @@ -14,7 +14,10 @@ public class Client { public WebSocket socket; public String clientId; - public Map clientData; + /** + * Любые данные, связанные с клиентом. + */ + public Set eciTags; /** * Интервал отправки heartbeat пакетов в миллисекундах. */ @@ -33,7 +36,7 @@ public class Client { public Client(WebSocket socket, long heartbeatInterval) { this.socket = socket; this.clientId = StringUtil.randomString(32); - this.clientData = new HashMap<>(); + this.eciTags = new HashSet(); this.heartbeatInterval = heartbeatInterval; this.lastHeartbeatTime = System.currentTimeMillis(); } @@ -68,8 +71,8 @@ public class Client { * Получает данные, связанные с клиентом. * @return Данные клиента. */ - public Map getClientData() { - return clientData; + public Set getEciTags() { + return this.eciTags; } /** @@ -85,16 +88,16 @@ public class Client { * @param key Ключ данных. * @param value Значение данных. */ - public void setData(String key, Object value) { - this.clientData.put(key, value); + public void addTag(T eciTag) { + this.eciTags.add(eciTag); } /** * Устанавливает данные клиента. * @param data Данные клиента. */ - public void setData(Map data) { - this.clientData = data; + public void setTags(Set eciTags) { + this.eciTags = eciTags; } /** @@ -102,8 +105,27 @@ public class Client { * @param key Ключ данных. * @return Значение данных. */ - public Object getData(String key) { - return this.clientData.get(key); + public T getTag(ECITag eciTag) { + for (ECITag tag : this.eciTags) { + if (tag.getKey().equals(eciTag.getKey())) { + return (T) tag; + } + } + return null; + } + + /** + * Проверяет наличие данных клиента по указанному ключу. + * @param key Ключ данных. + * @return true если данные существуют, иначе false. + */ + public boolean hasTag(ECITag eciTag) { + for (ECITag tag : this.eciTags) { + if (tag.getKey().equals(eciTag.getKey())) { + return true; + } + } + return false; } /** @@ -125,7 +147,7 @@ public class Client { * Отключает клиента с указанным кодом отказа. * @param code Код отказа. */ - public void disconnect(Failures code) { + public void disconnect(BaseFailures code) { this.disconnect(code.getCode()); } @@ -133,7 +155,7 @@ public class Client { * Отключает клиента с неизвестной причиной. */ public void disconnect() { - this.disconnect(Failures.UNKNOWN_FAILURE); + this.disconnect(ServerFailures.UNKNOWN_FAILURE); } } diff --git a/src/main/java/com/rosetta/im/protocol/ECITag.java b/src/main/java/com/rosetta/im/protocol/ECITag.java new file mode 100644 index 0000000..efc4e2d --- /dev/null +++ b/src/main/java/com/rosetta/im/protocol/ECITag.java @@ -0,0 +1,31 @@ +package com.rosetta.im.protocol; + +/** + * Embedded Client Information Tag. + * + * Используется для хранения дополнительной информации о клиенте. + */ +public abstract class ECITag { + + /** + * Ключ тега. + */ + public String key; + + /** + * Создает новый тег с указанным ключом и значением. + * @param key Ключ тега. + */ + public ECITag(String key) { + this.key = key; + } + + /** + * Получает ключ тега. + * @return Ключ тега. + */ + public String getKey() { + return this.key; + } + +} diff --git a/src/main/java/com/rosetta/im/protocol/Server.java b/src/main/java/com/rosetta/im/protocol/Server.java index 3f99a24..effe878 100644 --- a/src/main/java/com/rosetta/im/protocol/Server.java +++ b/src/main/java/com/rosetta/im/protocol/Server.java @@ -19,11 +19,13 @@ public class Server extends WebSocketServer { private PacketManager packetManager; private Settings settings; private ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); + private Object attachment; - public Server(Settings settings, PacketManager packetManager) { + public Server(Settings settings, PacketManager packetManager, Object attachment) { super(new InetSocketAddress(settings.port)); this.settings = settings; this.packetManager = packetManager; + this.attachment = attachment; } @Override @@ -62,14 +64,14 @@ public class Server extends WebSocketServer { /** * Если пакет не поддерживается, отключаем клиента с соответствующим кодом ошибки. */ - client.disconnect(Failures.UNSUPPORTED_PACKET); + client.disconnect(ServerFailures.UNSUPPORTED_PACKET); return; } if(!this.packetManager.hasExecutorDelegated(packetId)){ /** * Если для пакета не назначен обработчик, отключаем клиента с соответствующим кодом ошибки. */ - client.disconnect(Failures.UNSUPPORTED_PACKET); + client.disconnect(ServerFailures.UNSUPPORTED_PACKET); return; } Class packetClass = this.packetManager.getPacketClass(packetId); @@ -86,6 +88,8 @@ public class Server extends WebSocketServer { */ Class executorClass = this.packetManager.getExecutors().get(packetId); PacketExecutor executor = executorClass.getConstructor().newInstance(); + executor.settings = this.settings; + executor.attachment = this.attachment; executor.onPacketReceived(packet, client); } catch (Exception e) { System.out.println("Error while processing packet " + packetClass.getName()); @@ -124,7 +128,7 @@ public class Server extends WebSocketServer { for(WebSocket socket : this.getConnections()) { Client client = socket.getAttachment(); if(!client.isAlive()) { - client.disconnect(Failures.INACTIVITY_TIMEOUT); + client.disconnect(ServerFailures.INACTIVITY_TIMEOUT); } } }, this.settings.heartbeatInterval, this.settings.heartbeatInterval, TimeUnit.MILLISECONDS); diff --git a/src/main/java/com/rosetta/im/protocol/Failures.java b/src/main/java/com/rosetta/im/protocol/ServerFailures.java similarity index 94% rename from src/main/java/com/rosetta/im/protocol/Failures.java rename to src/main/java/com/rosetta/im/protocol/ServerFailures.java index ac36bbd..ddf32ea 100644 --- a/src/main/java/com/rosetta/im/protocol/Failures.java +++ b/src/main/java/com/rosetta/im/protocol/ServerFailures.java @@ -3,7 +3,7 @@ package com.rosetta.im.protocol; /** * Перечисление кодов ошибок, используемых в протоколе. */ -public enum Failures { +public enum ServerFailures implements BaseFailures { /** * Код ошибки, указывающий на несоответствие данных. */ @@ -40,7 +40,7 @@ public enum Failures { private final int code; - Failures(int code) { + ServerFailures(int code) { this.code = code; } diff --git a/src/main/java/com/rosetta/im/protocol/packet/PacketExecutor.java b/src/main/java/com/rosetta/im/protocol/packet/PacketExecutor.java index c1bac51..cd197be 100644 --- a/src/main/java/com/rosetta/im/protocol/packet/PacketExecutor.java +++ b/src/main/java/com/rosetta/im/protocol/packet/PacketExecutor.java @@ -1,7 +1,35 @@ package com.rosetta.im.protocol.packet; import com.rosetta.im.protocol.Client; +import com.rosetta.im.protocol.Settings; -public interface PacketExecutor { - public void onPacketReceived(Packet packet, Client client); +/** + * Базовый класс для обработчиков пакетов. + */ +public abstract class PacketExecutor { + public Settings settings; + public Object attachment; + + /** + * Настройки сервера. + * @return + */ + public Settings getSettings() { + return settings; + } + + /** + * Вложенный обьект, который был передан при создании сервера. + * @return вложенный обьект + */ + public Object getAttachment() { + return attachment; + } + + /** + * Вызывается при получении пакета от клиента. + * @param packet Пакет, полученный от клиента. + * @param client Клиент, отправивший пакет. + */ + public abstract void onPacketReceived(Packet packet, Client client); } diff --git a/src/main/resources/hibernate.cfg.xml b/src/main/resources/hibernate.cfg.xml index 80bb79d..dcf79da 100644 --- a/src/main/resources/hibernate.cfg.xml +++ b/src/main/resources/hibernate.cfg.xml @@ -3,10 +3,10 @@ org.postgresql.Driver - jdbc:postgresql://localhost:5432/rosetta_db - rosetta_user - rosetta_password - org.hibernate.dialect.PostgreSQLDialect + jdbc:postgresql://localhost:5432/your_database + your_user + your_password + update true true diff --git a/target/classes/com/rosetta/im/Main.class b/target/classes/com/rosetta/im/Main.class index 4dd96c5d9dfd58bfac660d1f0c7dc7c79e54f3d4..c5f68bbd6e55a9dfa13a5e34d2348c7b9cab3abe 100644 GIT binary patch delta 217 zcmZ3=ahrqd)W2Q(7#J9A87ww(?PD}xXHaEikV($Z)i25~PAw@()X&V-ch1jC%SK{U}ex?WJuMRyq`&F@()Hm8K11gvPAuy#JqHU|D>$cSk@a8BjG7gE;^> C@+t2C delta 167 zcmcc3v6O@B)W2Q(7#J9A8O%0v?PKKQVo+jG B7d!v} diff --git a/target/classes/com/rosetta/im/device/Device.class b/target/classes/com/rosetta/im/device/Device.class deleted file mode 100644 index f6959d71e2900d19f8842a0dc40d2fb6fe4e3b31..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1064 zcma)3+fLg+5Ivh1hd2%)7bvvhl3t|56#QJZ<)sx;r3fCV^6ogRbWsvZ#_(N5BC6B} zKA<0knAx?QDj3Ae&SmGEnK|?C{_h=tuXtBNL7*K*Q$LO_Ws(HGn))MotwQO4*QNwZ zVErPv4*W?l8~bO&3mGN?tETG5NTAVQe4i(=nvFjTIOhK}n98j9>{7t~qGl>N7WmYC z40}nYHxRJCMI-5;h^mV+)&z>(-k=N`)Wr&H_8Klq#%sA)g)LC)tC>8#nhs_BGZ;>2 ztVTZygUKL>6~}4PN`5JtbGQGTDzso_EE6LUYtZWU9uF^2&5Dc=3lM8jA;}jM8##jT zvem3A<;Y^@aL%KvIFu)f7pfSxBfbYZq(YI-TqH}PkSACYZAlQaCCZfH&|9Hsf&whE zE$K8cQ{A$hVlh$(7Q=oAe2S8GU3+XqKPWy0ngy^3R@HrlkO|D$DIq> z+Mnj~ssx-Z6o0$F2jqAWdmiWkKM^}l?6ol^e@w^iO8$_Ncde@7OKwmruhH>Z+#!(Q%A~AXCV4waH4!XkfHdtJYhJo8 z7U;cG$Lr8XrUSio`z=e?vm86HQ%=L{1%?h#cCO;DlLKqjG6^b)ek77OiMuqM61X$6 zafjbJaXJPuM91Lai^iJ3#Qp}E%H}9C)^x8OjiG=7a9YO*MoHGX;VF2IYh6+Hrr$lH zAuVu61Xk9uSFN>*Yxul(0{7sJ8txT177?2^tYXELo{l%+%{121?84Ig{M?1bsmaIZ zFU&2@W#{q&sVMc^+er9HyhX=-c&k9NV0orv+tT#O@Ba6mFg&@VK=~P6`9uU)!SIb0 z%B7VZ{LS=|CtVHqH%I6}Au@1J661KAip96Lvcy4q$C{mXXgP@qOlrsq+_fLx$q@LA zVH<1G)iH%>y2Gkv7bX}D_YF4a%@z&M3lFOJRxy3v@YZLHGF@UfV&+yZlTZ?!QC^rq zPRE0Ih@QZ#wi`AAy^Wf~FjI&#n9(t-cIaxyc3{RaF95ZCLC1nx?&7kd*)~e_`6XP` zz?9a#i=?hp$2)L|vFTM-ydanbjt=H>jgWVOJcCDdyi+NUQ~9Fh7pXaecj@2;Q7 z$Yh);SB?$l>I|>wFrg+qqsquX)p)UxjmFc$sRUNBu7M#p9I;K9v!q=qd89bZ_Y2G$ zQip}hyi9g(WB0)76He{*A%>R#{X<9{~qoa)X z(9MiOA;>x>ny^HZ%FuEG9x56btBp3^^n6*;QN4B3y>*0^lcu}bK4D9v6!_z& zjw^VtKnIgujt1-5lj`$Mkm#52ejOjc2L-gMQLMVkTK+oUJm0W=R?R*B5$`tX$thYk225m~b)yUP?YpI3C@7m)-*!fUW`i|$9J`fPZ7XjFv1%ux@kxOsZxNPL z0xE-DifG2l zgyZ<0?;7P9$ueBl&XXbY41Of=d}C4zrRxe+ zf+|XHAn!OEm2!(R)L`*F8y7drvW57g4e~9rXQ(1;6pIU%FUJmd;(KxYgi$gppXgI| z>oXbfzAR+=L>xb3jD*%70A@^i6cnj@HnViE%ud>EM{kbaFg?MjN?-{!7WNu*m(r_wv4QH?8+)ZRMiB#ebc=iS|)bW7&N_Et5o{v}!`Bb83 zu7-!zS9`Zs!(t7Oq!R5lJeEpahjCFIo$5@*QXN+zuVEvT97!c>u&=^RC2Dx`I^OpR zo&sw4a0F(T66p%op9|Kv6g%xK{eaCr8|WpCN6FAJ@^m}L6ppil9z`EZ^#H~&jB_}R z^K3wKxEGJ%3|6?Z!4;S7)h2&!J%zLQ6nmpDU=lB28ZYAkY_U!G9rE}Kn~#6sVf>SQ zgR;VvI@6&Q$T>caPXv~E6`!P*Hlq1EK8?>1V;BC0&*F245!qkydA@ZJ-=BHT7iqEM z_#?iAFVljf_zk{-=Mg8m=ZN5|NDxB?FLEwP1V`{SzIEa2Jbwu7{~`2*2BF~_8cu5H z*02=(f0I-Fz@dNPMUC5I_%=7R;XC*)pSSSs#{~LvF#Z%j=Uflx#`#o#zra=eisb#8 J?=i}a{})LnEV2Lq delta 351 zcmYL^OH0F05QWdQd70d38RVeZl3l@qu6>~OFQgJmizrAaxDwooLdY+1qv%4q za^a5>PgB9gJ!j_Lx!=q@)!$9~`{&~mzyX@6-S$hvXcUE4(RDxU_J;9gV!}udPYg%F zgq1#zWj^al%kmH?R8Y+xq*?Q@psa2cy<0cJ7BOi)6ZI{ExaH|arZ4DWpJ6@Evf z);fBzZT!P-DtTi?b>WaxCdnsfLO+@k{^(?33CnrM0ClWzT%DDxSmOc?Y_75ajRXyw RjC67YTiDJg-XX7m#xEl6C#L`a diff --git a/target/classes/com/rosetta/im/packet/Packet0Handshake.class b/target/classes/com/rosetta/im/packet/Packet0Handshake.class index 32100677bafe3154363fc254b7fe8deb9a7597d3..3b91468e165c51698f147b3f35a79b9798efc224 100644 GIT binary patch delta 142 zcmca7{z-ho5gBElmMX2Sgx-{-tsq#p$=WXDQj$%ff{J(W zHag>s%8dHaFML490@WGE&;ANO`YXiq?rw7{Y3q#4kllSR=Q+=L&ilOm^PgXT2XF$P zD{u+yNo3QJTsE(prWP^Mk@;NK%qFs_$Uw@_Go}KMK*L3CL5rlc%xq+Q=Axc31$_Bz z;*!pnJtNuNY(y@YzM{|Aqme0nCT7j|2-GDkplDK{aijzkGjm2}mh+09Fyh*5USNwo zT`-MQB&M63tB)D88OLzDFo*rs9=Hp{o| zD!lOdu@k!$>=HOwi?E%rrN;zwSFs0=3#d6Qlgy@VodlYL(a>u7WSqS!6x1o$$Gj|k zExc1&evTsg(T*m$>VS%R`27eXtRPfT)7WC()YB?DaflF!LN3R`OHoFRRLWpsnA>TI zaYV%yY?g&GGWu8{J)`I1+DwY#HI8HxT53|u8S>j1^q6yUcU#R$*%p%?F!Co;#sYyD z>YSJMl>c1Li~DbZO)*nTTpHEp9qcWI`J`s*#Tp6(W_8mBlvZmBK0+-4KNk(I_-*UT z8oL0N5Jlq>UzTUdNU_9OvnhanF zrxd(Eok|X1^;JYcySo`!I;f16eU5uV=Sa&wfNK@XVI|N~S?9(1m0G*WgLR}1_E~Op zf)%Be8Fg0;Za`a0Hx|v(t$%&p))P!1x*=KD!wE58srsafDZI=sXRyC$W;RESDl8Or z#<}_&&MSC@how}bm01-R@G84XM!(9QT4`RVR0>{Oqg&e*@?#n^DiY{rS*hBvzUYUJ zSp~0Go_MR-sF;)P@adNeS}HI3T~u)i-K^uBmLIdIxJ_wTAHO&1Tso?rq3!-pQ&Ej| zZU}$U$S1OyjCHYi*yreH6AKCo0=w4QtKteMxHpmF{YCW>?8$8KQGrlxr}S%ieMmD> zyj1em(YcPd6uc?0zgB-P-=X4d+@L_rP~HGm!;1RZ8{buNS&}(W(>diZJ@xniA1U~d z8)}7(@xsz`D^yk7#4WboiLv3a@u{)tp_9>(i8F%&wKJ^*=#eZxku2X}AB<%Sxr9Ds z$mSieTh|e3OYG$;H=D1QPhMYqW45TXp1eT#6_BkND&Kg~@x5PO3Vd$nNCUR=w~=4B zh~nWh626be!u3mN33o1`HQdnl06Uh@7XA(GXYXVG5<0?da`piZF5&P`+~PunzwK7g z{RC;{&DBBp1K5Ux91rq0gd+&!39f6i=tgi9UHoD|i?*=dBtaJm9YeQ6Yn&;`xm^qw z{so8iCOm2IrKJf^8()0~ujh{Y4(Ht#2YYR|6-Oq_eNLQbtTkc>2aluK0e_0yuRd!jPwZtz6>8J)7 zek90qIO#BuV3Ms*7ObsQ5Ict$<&9eiE%vIS@IK2Yl6^l0iiDoG2u(VeDzob7T*jaa zQxe&h*+xFk6fTq@t3_lL!x$m%D0kRpkRxLp5g5md#aJ&{vE+A=e@AtcB>C%QQGCTH zKKTbyMHD$A;|ZKWtQaM3MY+vy>F+L?(K03k#(u=^!^=1;@CT+x58Z>-$^Z9|{1)Cr zcR6DS+3Xo>KCU6h$ tR0DoZz;9{-zUKgzT7&FEvNimS_wV83YP9cJyC2H3EVYJDxyyym{sjgF?j`^L literal 2798 zcma)6TUQfT7~Ll!gd`I|3ux8ah$11VQ`=g#ctex|B3L6(>_sQT6b6P2olNYqeQKZT zdtdtxd}zChmv*iGfc}#HimvW+&LkNFsCmdabI$kev%mda{`v3EzX4oAEscc05!>G| z1Ha~kp=G)o=4RlBzU_PFxaT_6FpUm@o;B;CWqMY1#hk0GId&+}S@Z36hnpiaez0On zgZtk)6@6+hI+e1z9~J1b70}eOz`mIlP&o|T>I(0(`u@5VS_0kLq8_@QdCS`5b*AjD zRIRWcI09#1zP-2|E_R}sj$S6l-c`p6!ir;sQ`OK39$KD&nC7`>)xycf-CcKsZ**RC zt8RE%V4(Qw$fk!E1Ukn3WhaXydJXiTPas(wUdTXTzkxjV$lcopQs~U$5Z+1Su)xr6 zf~%mlEc1-tz!AJFU<6im+27FT0)54);aHH7it?SK2GZzC;~3*oGAq1EtF}rZvlu|H zv^rrRgKQQ>l+qY}DYfNCwb0oxFoKhWu&_#A6x*Y&a@lIu2!{!d(3?8RNE%Nz4&P8H)j!N0+rIzhx_PwOsGT$urA0ask1Hzs zcy1$#Qlq;D=0RRbIV znk;wq3j1m*%NAZUup~(gy#B!qpW{@hVx1j7KYMd_ZgKYh#Mso#{GA&TisiS23g>v) zuLrg>;mSdf)AfH^mJp88D#=DpapfrFj+D@)Rt_V6M1$h8IEsZuvCz@&oQ)xQ@_zA0h9j& z>d~VF>Ejd};1kENAIG^H#1Ky4H2>eDT|v<`aR%@6iw`Q=LhU5M1POhB4&1s_A?QmuaQbaz%+B2N`S! zncNZND#juP5=^q~lnE=e5~5?cO?f|62>sfsDSW4@6UqJ>#v`mbDk7^X&u9imPQJkP zBo^Btmsm{q6r|2&z6qJfG^;?$k;hHU@IeWFi>r=yRz-W8-%Y}U5?yCc6ShdWvJ2rk zRnWKTp|jhGIkgiE@lF)Nl?XkjLU@640_}>f#o%*I^0|n-z#=}2z~YHH&<+zQ7|o=W zUABJYJRW0m;Lr_&Z%6xy1@br+W2^kXp;rRI|0{$y(*OVf diff --git a/target/classes/com/rosetta/im/protocol/Server.class b/target/classes/com/rosetta/im/protocol/Server.class index c49548fd6743d68086c0bfd77083908dba969108..cbcc02d70d3ccbdb29dabbc42a3d33d73a8dd42e 100644 GIT binary patch delta 2989 zcmZuz33yc175>j`cV_aEnIwc{LdZh4nMsBKVI7tT3DmF#Vxl1ePQq$z|Df?#PU zNJYB?w~M8=TC37xYYmVI0l~Dz+G-c7)>^xzyDde#*wxl}-kTvo>X&csefQpT&wrNx zoSXaWPt4AI=FPJg04%220w%f3S|ndHS&vK~4LC!Z7V6%%Q;lkboYmVydqQ=QP_(CR zUDtNCTU$u5Eec1&+7g25mD!3%&Z2o`|1aljCs-PLx2gi!ghF|R3hfT$B2QqFEU`_g zZ&fud9PNoKn2dau-PIdoL~3@z4Ud9M6!NCG+1Kub8AbZWRQU>3*h)|)P%3jw<>qN9 zm-9^E?;B!og*sxH>MZ+qwn43U;BJFlk#TzI?CR-KX`&n$(Ee z6VlYJx|$1NWg?A8MRDd|ttW_=4Q&=qi2~#|0jFn^&#-;+ncs!58r*dBss` z@57@CzKpNPJZGits|p^+fV{!EyzCSP1x_=E3?sw6buF=QRBP3k6ns00A^C{2!ZwU^ z0%zqVXMy<%oR^oKbL^+^q<%hf%A4^l(M9>XvT1rQex%?f{FwdOriNl#mm1P^mt%WE z5iXpSdggzk6Y-~nXS{wnYnJ$Wx@>B;*WUYAd2)!Y7oe+m5a zsB4z_-}sN5LKd=0x4UYUAcr73 z6MRCfnVkwT2^Dgpk&7*=?$y{kQWVOhEVeitUm6MTQH_$?=zvOYg|ZQr2i*B~LAeS| zqAdAt!*_G_y zO?opjW^PZ$%#Da=y(?uRi$@o)4#L8JK(+rI+#NNiku!vXAx!aG&Y*Y@755Eqle>=iL1|TfL;CSTlT=Lu7t_gc;}X8X8Z2ziQ+O(YZ*tzI z;e!5{Fv&Z3ej49m>gz^)8_#e_9_E60miHWNNFlz1@3Oj!eEoY^$fw_DA15gFZB znUenj%Rk3wOJEseCJO-)Xb@--XcBl{pp9!JH!;SKrTrbw1V?}`3>R@&U%(GjbmGU| zYLK_!9GW_6^i-Y3i$i#MZB1YpKQrNa67S%^N7%8Q*O0hiJWd5znMkv^fNOD-y>PQf zS6Tc^G^b(P`9v@O$^ImM#lPP!;>1rmi&qR1On~MX#%Vn6VefC}PP5kl*5ji`DOI5k zZfE>o9P1_|_%lbrl*s5Z5V$SxcPd(1SjMWCnUiQ?QR25 zNoh&}x=ngVPNO`?rs(CrfZh1-FxNj}&2d!L4wH?zJR4R|6lx$z8OPuZoFo^QAxq>C zj*Y#fqnizar&?-1yK;b$?}V4Ta~=Ab=7$U`QjKXr+U&s)D$MUDUf delta 2877 zcmZ`*dtj4S75|+i&CTO$lLiV&D71y9G-L2}X?z#7z-|swc zPv7w1Qs*;Q#xDX`P1gw+2n*!%FX$x)G-LkHJQ>cxwaInu)di7$h4Rz?!(2W?u zwl|jS?2X5xog6%0Ip3?EzNgsj#4Q?nu$wTuJCY0?jCKyBdJ`L>J<+a6D!Nx)h->IY zpIj*d^AiNezQ}<{RZk?|RaM^;NhWRRM@qv04#=Y-Ty&7&oVn4wXCDLGa2t*Y945?{ zkBa&1=e+AKAuqs(cr-&bTs8pk!CNu9N1`AG@;>L7?gIauSnwE0uNlhGmwh)RNWX!b~i@)G`t7z zmCe>A^gemadcDn#6L`Od58#9HDeImE58@$#5C4ZFt%s7S=za|k<0CR;E4K9G5e*;1 zDY@U~Ge4@~G(I6u*g_?b;c-vltZ^xq3^F zW$0jMv@aFw z|FX_llzWrE-HuvZ*6`3D+a;OgC$SL}UJ!;e%Eimqs?gRDjAxiy1BmyoWpcq|3xQ5^GFl<~Wig$(W~FrUZ08Qgyf{WVsP^;)$PH8zjUV+oGo zq!Fi(F0Qc*<3k>i!AHmOaqz(Tq}p3D7|!7Q5PR5x$1#kv92-?rJGiRA`Ll5bomj@z zRL!MRi!!X{2(9A`tVaz$lN&isjU1^aY+?&dh@+X`g;pHFHXh2`@rZ7BM;asis(c>1 z@Kt{CR1$9ElIKuPCt+eb2@|-0PwPo|dMXJ{&g>Jo$kgUx6rbT;4wE^f-VUzd4%~{* z;&WWraVGX8qgWWa2v4!h$^>k98tE_aGQ#R##Bx6V5<|~nh^*=}teRDLmKDCtXKP@Z zWcP#s3DgQS2-FFDMPM`c!mLSj!ty_k1V4*>|7;;g^Eq}jTkzdX)|6(W?hB~vsNnRK zj$q;(zR^^{+4;5s*H&v62fUjbIrf@jbx+61Jsl@Ex7>Hx2`4*rwOGH0&=hPtpQv`6 z6UgBEfpi8xT*V2WH;$L|? z=g8@!guPlA7{^bzxw8IEEyH+a48IjfZ@U)oFel_VC*&}XsylT+H3HRq(gB?YHEE|{ z9%d@4%~!H7e;LC+2#u%Us~l5gE}^zzrXPbD$~g&JaG0#zZG1x`oSeKoq~dSX3)`y? z;UgU1+Zp)|mZiD*k8qnG*I8r-=E^Cz=ki$14u=vRAK56Y0?CJAk|SbN?h9hxB`}hM m4-Dj_d|utWpUW?XTmO2!w2%dhsEihqYA^Me_^E<|@VpA!&m{l= diff --git a/target/classes/com/rosetta/im/protocol/packet/PacketExecutor.class b/target/classes/com/rosetta/im/protocol/packet/PacketExecutor.class index 77062369dc0ef4e554566dc794b3b8d8aedb5311..047954be6be82d8456c00ecc5ae00f45d6ad4518 100644 GIT binary patch literal 765 zcmZ`%$w~u35Ph9Y_R(mJTUatWW}Zp= z3vFdbgc_*7U9}t-mR+QfW|$-R*}oghG+^q=2(a!VgDi<^f`QbxN-F)L9vCjG zu{+0D*J9i1o+gEBk%0N8>4j2V=t!!i@mD>Hm^nMdCP_adL7r})h(*-MI~0-VWDAsW z@{D}y;1ljU1qsZPP4|mA6e)HG4OqY;W%M*yB0CQLK>2L~T$%}9!Rid~+9bF#4W_?Z M!A76KW`EVzAL;I~V*mgE delta 53 zcmey%`h}6})W2Q(7#J9A88|0$c~73s*eJ)w$iT?J3>0HvU}RxrU}azfu^8BaBohM% E0BY+9W&i*H