Хэндшейк, улучшенный граф, новые методы в репозитории в DB

This commit is contained in:
RoyceDa
2026-02-04 06:08:24 +02:00
parent cd1e6e6b14
commit f74f4e7af7
16 changed files with 386 additions and 31 deletions

View File

@@ -1,13 +1,20 @@
package com.rosetta.im;
import com.rosetta.im.client.ClientManager;
import com.rosetta.im.event.EventManager;
import com.rosetta.im.executors.Executor0Handshake;
import com.rosetta.im.listeners.HandshakeCompleteListener;
import com.rosetta.im.listeners.ServerStopListener;
import com.rosetta.im.logger.Logger;
import com.rosetta.im.logger.enums.Color;
import com.rosetta.im.logger.enums.LogLevel;
import com.rosetta.im.packet.Packet0Handshake;
import com.rosetta.im.packet.Packet1UserInfo;
import com.rosetta.im.packet.Packet2Result;
import com.rosetta.im.packet.Packet3Search;
import io.orprotocol.Server;
import io.orprotocol.Settings;
import io.orprotocol.packet.PacketManager;
/**
@@ -19,11 +26,20 @@ public class Boot {
private PacketManager packetManager;
private EventManager eventManager;
private Logger logger;
private Server server;
private ServerAdapter serverAdapter;
private ClientManager clientManager;
public Boot() {
this.packetManager = new PacketManager();
this.eventManager = new EventManager();
this.logger = new Logger(LogLevel.INFO);
this.serverAdapter = new ServerAdapter(this.eventManager);
this.server = new Server(new Settings(
8881,
30
), packetManager, this.serverAdapter);
this.clientManager = new ClientManager(server);
}
/**
@@ -42,6 +58,14 @@ public class Boot {
return this.eventManager;
}
/**
* Получить менеджера клиентов, нужно для того чтобы отправить пакет списку клиентов например
* @return менеджер клиентов
*/
public ClientManager getClientManager() {
return this.clientManager;
}
/**
* Получить логгер приложения
* @return Logger
@@ -64,10 +88,14 @@ public class Boot {
private void registerAllEvents() {
this.eventManager.registerListener(new ServerStopListener(this.logger));
this.eventManager.registerListener(new HandshakeCompleteListener());
}
private void registerAllPackets() {
this.packetManager.registerPacket(0, Packet0Handshake.class);
this.packetManager.registerPacket(1, Packet1UserInfo.class);
this.packetManager.registerPacket(2, Packet2Result.class);
this.packetManager.registerPacket(3, Packet3Search.class);
}
private void registerAllExecutors() {

View File

@@ -8,6 +8,10 @@ public enum Failures implements BaseFailures {
*/
AUTHENTIFICATION_ERROR(3001),
DATA_MISSMATCH(3001),
/**
* Handshake не завершен
*/
HANDSHAKE_NOT_COMPLETED(3002),
/**
* Неподдерживаемый протокол
*/

View File

@@ -1,28 +1,14 @@
package com.rosetta.im;
import io.orprotocol.Server;
import io.orprotocol.Settings;
public class Main {
public static void main(String[] args) {
/**
* Регистрация всех пакетов и их обработчиков
*/
Boot boot = new Boot().bootstrap();
Boot boot = new Boot();
/**
* Загрузка настроек сервера
* Стартуем сервер
*/
Settings settings = new Settings(8881, 30);
/**
* Создание адаптера сервера для трансляции событий протокола в события приложения,
* обрабатываются основыне события такие как запуск/остановка сервера,
* подключение/отключение клиентов, ошибки сервера и получение пакетов
*/
ServerAdapter serverAdapter = new ServerAdapter(boot.getEventManager());
/**
* Запуск сервера
*/
Server server = new Server(settings, boot.getPacketManager(), serverAdapter);
server.start();
boot.bootstrap();
}
}

View File

@@ -1,13 +1,15 @@
package com.rosetta.im.client.tags;
import com.rosetta.im.packet.enums.HandshakeStage;
import java.util.Map;
import com.rosetta.im.packet.runtime.HandshakeStage;
import io.orprotocol.client.ECITag;
/**
* Это вложенный обьект для клиента, содержащий информацию об аутентификации.
*/
public class ECIAuthentificate extends ECITag {
public class ECIAuthentificate implements ECITag {
public String publicKey;
public String privateKey;
@@ -51,4 +53,12 @@ public class ECIAuthentificate extends ECITag {
return this.handshakeStage == HandshakeStage.COMPLETED;
}
/**
* Создаем индекс для быстрого поиска клиентов (индексацию реализует ORProtocol)
*/
@Override
public Map<String, Object> getIndex() {
return null;
}
}

View File

@@ -2,7 +2,7 @@ package com.rosetta.im.client.tags;
import io.orprotocol.client.ECITag;
public class ECIDevice extends ECITag {
public class ECIDevice implements ECITag {
public String deviceId;
public String deviceName;

View File

@@ -67,6 +67,36 @@ public abstract class Repository<T> {
});
}
/**
* Поиск сущности по значению одного поля с использованием оператора LIKE
* @param fieldName поле
* @param value значение
* @return найденная сущность или null
*/
public T likeSearch(String fieldName, String value) {
return executeInSession(session -> {
String queryString = "FROM " + entityClass.getSimpleName() + " WHERE " + fieldName + " LIKE :value";
return session.createQuery(queryString, entityClass)
.setParameter("value", "%" + value + "%")
.uniqueResult();
});
}
/**
* Поиск сущности по значению одного поля с использованием оператора LIKE
* @param fieldName поле
* @param value значение
* @return найденная сущность или null
*/
public List<T> likeSearchAll(String fieldName, String value) {
return executeInSession(session -> {
String queryString = "FROM " + entityClass.getSimpleName() + " WHERE " + fieldName + " LIKE :value";
return session.createQuery(queryString, entityClass)
.setParameter("value", "%" + value + "%")
.list();
});
}
/**
* Поиск сущности по набору полей
* @param fields карта полей и их значений

View File

@@ -13,16 +13,15 @@ import com.rosetta.im.event.events.handshake.HandshakeCompletedEvent;
import com.rosetta.im.event.events.handshake.HandshakeDeviceConfirmEvent;
import com.rosetta.im.event.events.handshake.HandshakeFailedEvent;
import com.rosetta.im.packet.Packet0Handshake;
import com.rosetta.im.packet.enums.HandshakeStage;
import com.rosetta.im.packet.runtime.HandshakeStage;
import com.rosetta.im.service.services.DeviceService;
import io.orprotocol.ProtocolException;
import io.orprotocol.client.Client;
import io.orprotocol.lock.Lock;
import io.orprotocol.packet.Packet;
import io.orprotocol.packet.PacketExecutor;
public class Executor0Handshake extends PacketExecutor {
public class Executor0Handshake extends PacketExecutor<Packet0Handshake> {
private final UserRepository userRepository = new UserRepository();
private final DeviceRepository deviceRepository = new DeviceRepository();
@@ -36,8 +35,7 @@ public class Executor0Handshake extends PacketExecutor {
@Override
@Lock(lockFor = "publicKey")
public void onPacketReceived(Packet packet, Client client) throws ProtocolException {
Packet0Handshake handshake = (Packet0Handshake) packet;
public void onPacketReceived(Packet0Handshake handshake, Client client) throws ProtocolException {
String publicKey = handshake.getPublicKey();
String privateKey = handshake.getPrivateKey();
String deviceId = handshake.getDeviceId();
@@ -67,7 +65,7 @@ public class Executor0Handshake extends PacketExecutor {
* Создаем минимальную информацию об устройстве клиента
*/
ECIDevice device = new ECIDevice(deviceId, deviceName, deviceOs);
client.addTag(device);
client.addTag(ECIDevice.class, device);
/**
* Проверяем есть ли такой пользователь
@@ -95,7 +93,7 @@ public class Executor0Handshake extends PacketExecutor {
*/
ECIAuthentificate eciTag = new ECIAuthentificate
(publicKey, privateKey, HandshakeStage.COMPLETED);
client.addTag(eciTag);
client.addTag(ECIAuthentificate.class, eciTag);
/**
* Вызываем событие завершения хэндшейка
*/
@@ -151,7 +149,7 @@ public class Executor0Handshake extends PacketExecutor {
*/
ECIAuthentificate eciTag = new ECIAuthentificate
(publicKey, privateKey, HandshakeStage.NEED_DEVICE_VERIFICATION);
client.addTag(eciTag);
client.addTag(ECIAuthentificate.class, eciTag);
/**
* Отправляем клиенту информацию о необходимости
* подтверждения устройства
@@ -179,7 +177,7 @@ public class Executor0Handshake extends PacketExecutor {
*/
ECIAuthentificate eciTag = new ECIAuthentificate
(publicKey, privateKey, HandshakeStage.COMPLETED);
client.addTag(eciTag);
client.addTag(ECIAuthentificate.class, eciTag);
/**
* Вызываем событие завершения хэндшейка
*/

View File

@@ -0,0 +1,32 @@
package com.rosetta.im.executors;
import com.rosetta.im.Failures;
import com.rosetta.im.client.tags.ECIAuthentificate;
import com.rosetta.im.database.repository.UserRepository;
import com.rosetta.im.packet.Packet3Search;
import io.orprotocol.ProtocolException;
import io.orprotocol.client.Client;
import io.orprotocol.packet.PacketExecutor;
public class Executor3Search extends PacketExecutor<Packet3Search> {
private final UserRepository userRepository = new UserRepository();
@Override
public void onPacketReceived(Packet3Search packet, Client client) throws Exception, ProtocolException {
String search = packet.getSearch();
ECIAuthentificate eciAuthentificate = client.getTag(ECIAuthentificate.class);
if(eciAuthentificate == null || !eciAuthentificate.hasAuthorized()) {
/**
* Клиент не авторизован, не разрешаем ему выполнять поиск
*/
client.disconnect(Failures.HANDSHAKE_NOT_COMPLETED);
return;
}
}
}

View File

@@ -0,0 +1,12 @@
package com.rosetta.im.listeners;
import com.rosetta.im.event.Listener;
import com.rosetta.im.event.events.handshake.HandshakeCompletedEvent;
public class HandshakeCompleteListener implements Listener {
public void onHandshakeComplete(HandshakeCompletedEvent event) {
//TODO: Обработка завершения Handshake и синхронизация недоставленных сообщений (переписок)
}
}

View File

@@ -1,6 +1,6 @@
package com.rosetta.im.packet;
import com.rosetta.im.packet.enums.HandshakeStage;
import com.rosetta.im.packet.runtime.HandshakeStage;
import io.orprotocol.Stream;
import io.orprotocol.packet.Packet;

View File

@@ -0,0 +1,85 @@
package com.rosetta.im.packet;
import io.orprotocol.Stream;
import io.orprotocol.packet.Packet;
public class Packet1UserInfo extends Packet {
@Deprecated(since = "1.1", forRemoval = true)
private String privateKey;
private String username;
private String title;
@Override
public void read(Stream stream) {
this.packetId = stream.readInt16();
this.username = stream.readString();
this.title = stream.readString();
this.privateKey = stream.readString();
}
@Override
public Stream write() {
Stream steram = new Stream();
steram.writeInt16(this.packetId);
steram.writeString(this.username);
steram.writeString(this.title);
steram.writeString(this.privateKey);
return steram;
}
/**
* Получает приватный ключ пользователя
* @return приватный ключ
* @deprecated с версии сервера 1.1 использование приватных ключей
* в протоколе устарело, так как теперь сервер использует Handshake для аутентификации пользователей.
*/
@Deprecated(since = "1.1", forRemoval = true)
public String getPrivateKey() {
return this.privateKey;
}
/**
* Устанавливает приватный ключ пользователя
* @param privateKey приватный ключ
* @deprecated с версии сервера 1.1 использование приватных ключей
* в протоколе устарело, так как теперь сервер использует Handshake для аутентификации пользователей.
*/
public void setPrivateKey(String privateKey) {
this.privateKey = privateKey;
}
/**
* Возвращает имя пользователя
* @return имя пользователя
*/
public String getUsername() {
return this.username;
}
/**
* Возвращает заголовок (титул) пользователя
* @return заголовок пользователя
*/
public String getTitle() {
return this.title;
}
/**
* Устанавливает имя пользователя
* @param username имя пользователя
*/
public void setUsername(String username) {
this.username = username;
}
/**
* Устанавливает заголовок (титул) пользователя
* @param title заголовок пользователя
*/
public void setTitle(String title) {
this.title = title;
}
}

View File

@@ -0,0 +1,40 @@
package com.rosetta.im.packet;
import io.orprotocol.Stream;
import io.orprotocol.packet.Packet;
public class Packet2Result extends Packet {
private int resultCode;
@Override
public void read(Stream stream) {
this.resultCode = stream.readInt8();
}
@Override
public Stream write() {
Stream stream = new Stream();
stream.writeInt16(this.packetId);
stream.writeInt8(this.resultCode);
return stream;
}
/**
* Получает код результата операции
* @return код результата
*/
public int getResultCode() {
return this.resultCode;
}
/**
* Устанавливает код результата операции
* @param resultCode код результата
*/
public void setResultCode(int resultCode) {
this.resultCode = resultCode;
}
}

View File

@@ -0,0 +1,81 @@
package com.rosetta.im.packet;
import java.util.ArrayList;
import java.util.List;
import com.rosetta.im.packet.runtime.SearchInfo;
import io.orprotocol.Stream;
import io.orprotocol.packet.Packet;
public class Packet3Search extends Packet {
@Deprecated(since = "1.1", forRemoval = true)
private String privateKey;
private String search;
private List<SearchInfo> searchInfo;
@Override
public void read(Stream stream) {
this.privateKey = stream.readString();
this.search = stream.readString();
int searchInfoCount = stream.readInt16();
this.searchInfo = new ArrayList<>();
for (int i = 0; i < searchInfoCount; i++) {
SearchInfo info = new SearchInfo();
info.readFromStream(stream);
this.searchInfo.add(info);
}
}
@Override
public Stream write() {
Stream stream = new Stream();
stream.writeInt16(this.packetId);
stream.writeString(this.privateKey);
stream.writeString(this.search);
stream.writeInt16(this.searchInfo.size());
for (SearchInfo info : this.searchInfo) {
info.writeToStream(stream);
}
return stream;
}
/**
* Получает приватный ключ пользователя
* @return приватный ключ
* @deprecated с версии сервера 1.1 использование приватных ключей
* в протоколе устарело, так как теперь сервер использует Handshake для аутентификации пользователей.
*/
@Deprecated(since = "1.1", forRemoval = true)
public String getPrivateKey() {
return this.privateKey;
}
/**
* Устанавливает приватный ключ пользователя
* @param privateKey приватный ключ
* @deprecated с версии сервера 1.1 использование приватных ключей
* в протоколе устарело, так как теперь сервер использует Handshake для аутентификации пользователей.
*/
public void setPrivateKey(String privateKey) {
this.privateKey = privateKey;
}
/**
* Получает строку поиска
* @return строка поиска
*/
public String getSearch() {
return this.search;
}
/**
* Устанавливает строку поиска
* @param search строка поиска
*/
public void setSearch(String search) {
this.search = search;
}
}

View File

@@ -1,4 +1,4 @@
package com.rosetta.im.packet.enums;
package com.rosetta.im.packet.runtime;
/**
* Этапы хэндшейка между клиентом и сервером.

View File

@@ -0,0 +1,25 @@
package com.rosetta.im.packet.runtime;
public enum NetworkStatus {
ONLINE(0),
OFFILE(1);
private final int code;
NetworkStatus(int code) {
this.code = code;
}
public int getCode() {
return code;
}
public static NetworkStatus fromCode(int code) {
for (NetworkStatus status : NetworkStatus.values()) {
if (status.getCode() == code) {
return status;
}
}
throw new IllegalArgumentException("Invalid NetworkStatus code: " + code);
}
}

View File

@@ -0,0 +1,24 @@
package com.rosetta.im.service.services;
import java.util.List;
import com.rosetta.im.database.entity.User;
import com.rosetta.im.database.repository.UserRepository;
import com.rosetta.im.service.Service;
public class UserService extends Service<UserRepository> {
public UserService(UserRepository repository) {
super(repository);
}
/**
* Поиск пользователей по части имени пользователя.
* @param query часть имени пользователя
* @return список пользователей, соответствующих запросу
*/
public List<User> searchUsers(String query) {
return getRepository().likeSearchAll("username", query);
}
}