Архитектура termi#
Цель структуры — чтобы по папкам и именам классов было понятно, что где лежит и зачем. Одно правило на весь проект: обработчик пакета тонкий, логика живёт в сервисах, сервисы не знают про сеть.
1. Слои и зависимости#
┌───────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ Client │ │ Hub │ │ Server │ │ Bot │ приложения
└─────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │ │
└──────────────┴──────┬───────┴──────────────┘
▼
┌────────────┐
│ Termi.Core │ протокол · шифрование · сеть · JSON · файлы
└────────────┘
Приложения зависят только от Core и не знают друг о друге — они общаются по сети (JSON-строки поверх защищённого TCP). Core подключается к каждому проекту исходниками (<Compile Include="..\Termi.Core\**\*.cs">), поэтому каждый exe — один файл без dll рядом.
Termi.Core#
| папка | namespace | что внутри |
|---|---|---|
Protocol/ | Termi.Protocol | Pkt (один пакет), User/RInfo/CInfo/PInfo, Perm (права), Names (имена и токены входа) |
Security/ | Termi.Security | X25519, Gcm (AES-256-GCM), Crypto (SHA, HMAC, PBKDF2, резервная копия ключа) — в .NET Framework своих нет |
Networking/ | Termi.Networking | Conn (шифрованное соединение, очередь отправки), PacketRouter<TSession> + IPacketHandler<TSession> |
Serialization/ | Termi.Serialization | Json — маленький сериализатор по публичным полям (формат совместим с версией на Go) |
Files/ | Termi.Files | FileStore — файлы сообщений, кусками до 100 МБ, шифруются на диске |
Util/ | Termi.Util | Args, Log, U |
2. Сетевой протокол#
JSON-строки; перед ними — рукопожатие X25519 (статический ключ сервера + эфемерный клиентский), дальше каждая строка шифруется AES-GCM. Клиент запоминает отпечаток ключа сервера при первом входе (TOFU). Личные сообщения дополнительно шифруются между клиентами (e1: + base64), hub видит только шифртекст. Поле t пакета — его тип; список типов — в обработчиках (Net/Handlers/*, свойство Types).
3. Termi.Server — как устроен#
TcpListener ─► ConnectionHandler ─► PacketRouter ─► Handler ─► Service ─► Storage
│ рукопожатие по полю t тонкий логика диск
│ вход, бан,
│ PlayerLoginEvent
└─ на выходе: PlayerQuitEvent
| папка | роль |
|---|---|
Hosting/ | ServerHost — корень: создаёт всё и держит Lock; ClientSession (одно подключение = IPlayer), SessionRegistry, ServerOptions |
Net/ | ConnectionHandler (жизнь подключения), Handlers/ — SessionHandler, ChatHandler, VoiceHandler, FileHandler, AdminHandler |
Services/ | PermissionService, ChannelService, MemberService, ChatService, VoiceService, ModerationService, FileTransferService, ClientPluginCatalog |
Storage/ | ConfigStore (роли, баны, каналы), HistoryStore (шифрованный журнал), AuditLog, MemberBook |
Commands/ | встроенные команды: по классу на команду (RoleCommand, KickCommand, …), PlayerSender/ConsoleSender |
Plugins/ | ServerPluginManager — поиск dll, порядок по зависимостям, включение/выключение |
Api/ | публичный API плагинов — единственная папка, которую читает автор плагина |
Terminal/ | консоль оператора (те же команды) |
Потоки и блокировки. Каждое подключение — свой поток, общее состояние защищено одной реентерабельной блокировкой ServerHost.Lock. Методы сервисов вызываются под ней; поэтому обработчики событий плагинов тоже выполняются под ней и могут свободно вызывать API сервера. Отправка клиенту неблокирующая (очередь в Conn), так что медленный клиент не тормозит остальных.
Как добавить…#
| что | что сделать |
|---|---|
| новый тип пакета | метод в подходящем сервисе → строка в Types и case в подходящем Handlers/* (или новый IPacketHandler<ClientSession> + Router.Add в ServerHost) |
| встроенную команду | класс-наследник BuiltinCommand в Commands/ + строка в RegisterBuiltinCommands |
| событие | класс в Api/Events/ + Events.Call(new …) в нужном сервисе |
| хранилище | класс в Storage/, создать в ServerHost.LoadStorage |
4. Termi.Hub#
Та же схема: Hosting/HubHost → Net/ConnectionHandler → PacketRouter<UserSession> → Handlers/* → Services/* → Storage/*. Сервисы: AuthService (регистрация/вход, PBKDF2), PresenceService, ContactService, ProfileService, DirectMessageService, CallService.
5. Termi.Client#
Tui/ терминал: цвета, текст, экранный буфер, клавиши, строка ввода
Imaging/ картинки: Image2, декодеры (JPEG/PNG/GIF/DIB), sixel и kitty, видео-кадры звонков
Os/ всё, что зависит от ОС — за интерфейсом IPlatform
Windows/ winmm · GDI · VFW · WinForms (буфер, трей, всплывающие окна) · нативные плагины-DLL
Linux/ ffmpeg · grim · parecord/paplay · xclip/wl-paste
DemoPlatform заглушки для тестов (TERMI_DEMO=1)
Model/ данные: сообщения, серверы, темы, настройки, шифрование личных сообщений
Application/ само приложение — разбито по функциям (partial class App)
Rendering/ отрисовка панелей и главный цикл (sixel/kitty)
Ui/ модальные окна
Network/ подключение к hub, приём пакетов
Social/ контакты, личные чаты, файлы
Profile/ профиль и баннер
Servers/ роли и участники сервера
Plugins/ подключение к серверам, клиентские плагины
Calls/ звонки и голосовые каналы (VoiceMixer — микшер)
Extras/ выбор фото, вставка
Os/Platform.cs выбирает IPlatform (Windows / Linux / Demo) один раз; остальной код вызывает Platform.ScreenWorker(...), Platform.StartAudio(...) и не знает, какая ОС под ним. Пространства имён подключены глобально (GlobalUsings.cs), поэтому в файлах нет длинных списков using.
6. Плагины двух видов — не путать#
| серверные | клиентские | |
|---|---|---|
| где | папка plugins/ сервера | папка client-plugins/ сервера |
| что | .NET-сборки с классом PluginBase | нативные DLL с функцией termi_command |
| работают | на сервере, для всех участников | у каждого клиента (Windows), скачиваются при входе |
| описание | docs/PLUGINS.md | PLUGINS.txt в сборке для Windows |