MAINER4IK XMCL-compatible multiplayer core
  • JavaScript 90.4%
  • HTML 9.6%
Find a file
2026-08-21 01:37:14 +05:00
src fix: align with XMCL - enableIceUdpMux, single ICE server, no answer delay 2026-08-21 01:37:14 +05:00
.gitignore Initial import: MAINER4IK XMCL-compatible multiplayer core 2026-08-20 22:57:32 +05:00
diag-probe.js Initial import: MAINER4IK XMCL-compatible multiplayer core 2026-08-20 22:57:32 +05:00
final-multifeature-test.js Initial import: MAINER4IK XMCL-compatible multiplayer core 2026-08-20 22:57:32 +05:00
itg-peer.js fix: align with XMCL - enableIceUdpMux, single ICE server, no answer delay 2026-08-21 01:37:14 +05:00
itg-server.js Initial import: MAINER4IK XMCL-compatible multiplayer core 2026-08-20 22:57:32 +05:00
LICENSE Initial import: MAINER4IK XMCL-compatible multiplayer core 2026-08-20 22:57:32 +05:00
online-deploy-test.js Initial import: MAINER4IK XMCL-compatible multiplayer core 2026-08-20 22:57:32 +05:00
package-lock.json fix: align with XMCL - enableIceUdpMux, single ICE server, no answer delay 2026-08-21 01:37:14 +05:00
package.json fix: align with XMCL - enableIceUdpMux, single ICE server, no answer delay 2026-08-21 01:37:14 +05:00
README.md Initial import: MAINER4IK XMCL-compatible multiplayer core 2026-08-20 22:57:32 +05:00

MAINER4IK XMCL-compatible P2P

Проект повторяет сетевую модель XMCL без прямого runtime-зависимого использования @xmcl/nat-api и @xmcl/stun-client.

Уже реализовано

  • STUN Binding Request/Response, transaction IDs, IPv4/IPv6 XOR-MAPPED-ADDRESS;
  • NAT probe с повторными измерениями;
  • UPnP SSDP discovery + SOAP Add/DeletePortMapping;
  • NAT-PMP public address и UDP/TCP mapping;
  • lodash.debounce остаётся обычной npm-зависимостью;
  • node-datachannel PeerConnection;
  • metadata heartbeat;
  • minecraft DataChannel;
  • guest local TCP proxy → DataChannel → host LAN TCP;
  • signaling rooms и Electron IPC;
  • Electron UI.
  • Named rooms with optional password protection; passwords are stored as scrypt hashes by the signaling server.
  • Регистрация и авторизация по логину/паролю:
    • auth:register / auth:login / auth:token / auth:logout / auth:me;
    • пароли хранятся как scrypt-хэши в SQLite-БД (src/signaling/xmcl.db, DB_FILE — переопределить путь);
    • после входа клиент получает сессионный токен и сохраняет его в localStorage;
    • комнаты (room:create/room:join) доступны только авторизованным — иначе error auth_required;
    • логин: 332 символа [a-z0-9_.-], пароль: 4128 символов.
  • Мультирумы: один клиент может состоять сразу в нескольких комнатах (вкладки в UI), пользователи и их комнаты хранятся в SQLite. Структура БД: users, rooms, room_members, chat_messages, chat_attachments.
  • Чат в комнате через signaling WebSocket: chat:send / chat:message / chat:history (последние CHAT_HISTORY, по умолчанию 100); вложения отправляются как base64 и хранятся в БД (BLOB), лимит MAX_ATTACHMENT (по умолчанию 5 МБ на файл).
  • Управление комнатой: мастер (хост) может передать роль другому участнику (transfer-master, требует статус connected) и удалить участника (remove-member, без разрыва его соединения — он остаётся в других комнатах).
  • Dynamic TURN credentials from https://test2.xneon.org/turn-credentials with 10-minute expiry.
  • Порт P2P-слоя XMCL (@xmcl/wrtc-multiplayer) в src/wrtc:
    • ice-servers.js — пул ICE-серверов с живым тестированием/ротацией (createIceServersProvider), нормализация TURN-credentials с test2.xneon.org, toNativeIceServer() (только native-формат);
    • peer-context.js — ротация серверов на пир, target/shared TURN, heartbeat;
    • peer-session.js — PeerConnection lifecycle (offer/answer, кандидаты с debounce, ICE-состояния), metadata heartbeat, minecraft/download каналы, TCP-прокси;
    • peer-group.jsPeers/PeerGroup: создать/принять пир, retry offer до 6 попыток, shared TURN broadcast, rtc-state, реконнект с backoff;
    • nat.js — определение типа NAT (symmetric/blocked) через собственный @stun-client;
    • lan-discover.js, map-port-candidate.js, tcp-bridge.js, messages.js, ssdp-client.js.

Проверка P2P без Electron

Интеграционный тест поднимает локальный signaling-сервер и два PeerGroup в РАЗНЫХ процессах (обязательно: два PeerGroup в одном процессе вызывают fast-fail в libdatachannel на Windows 26200):

Start-Process node -ArgumentList "itg-server.js" -NoNewWindow
Start-Sleep -Seconds 2
Start-Process node -ArgumentList "itg-peer.js master" -NoNewWindow
Start-Sleep -Seconds 1
Start-Process node -ArgumentList "itg-peer.js member" -NoNewWindow

В логах обеих сторон должны появиться RTC STATE connected и обмен IDENTITY (хост и гость видят друг друга по metadata-каналу через реальный signaling).

ВАЖНО про эту dev-машину (Windows build 26200, за симметричным NAT): если оба пира сидят за одним NAT, ICE может номинировать srflx-пару через общий публичный IP (hairpin NAT), DTLS/SCTP не завершится и канал не откроется — это не баг порта, а ограничение среды. На двух устройствах с разными публичными IP такой петли нет (это и была первопричина симптома «второй друг не может подключиться»).

Технические ограничения libdatachannel (Windows, build 26200)

  • В одном Node-процессе нельзя держать одновременно два PeerConnection одной пары — libdatachannel fast-fail (0xC0000409) в момент открытия DataChannel. Реальная игра всегда запускается отдельным процессом на каждом устройстве, поэтому для продакшена это ограничение некритично.
  • Ответчик должен отвечать на offer не в том же тике: setRemoteDescription → пауза (~500ms, см. PeerSession.setRemoteDescription) → setLocalDescription('answer').
  • Колбэки PeerConnection (onLocalDescription и др.) нужно регистрировать ДО вызова createDataChannel на этом же PeerConnection.

Запуск

npm install --cache .npm-cache
npm run signal
npm start

Для native dependency рекомендуется Node 22.16 LTS. В текущем окружении Electron installer блокируется политикой записи в %LocalAppData%, поэтому production .exe ещё не собран.

NAT/TURN

STUN не гарантирует direct connection при symmetric NAT. Для production добавляется coturn и ICE config:

[
  'stun:turn.example.com:3478',
  'turn:user:password@turn.example.com:3478?transport=udp',
  'turn:user:password@turn.example.com:3478?transport=tcp',
  'turns:user:password@turn.example.com:5349?transport=tcp'
]

Сервер coturn будет настраиваться отдельно на Ubuntu: 3478/udp, 3478/tcp, 5349/tcp, relay range и временные credentials.

Авторизация и БД (сервер)

Всё хранится в SQLite (src/signaling/xmcl.db, создаётся автоматически; DB_FILE — переопределить путь), использован встроенный модуль node:sqlite (без внешних зависимостей).

Таблицы:

  • users — аккаунты (login, display_name, scrypt-хэш пароля, токен сессии);
  • rooms — комнаты (name, пароль-хэш, текущий владелец master_id, epoch);
  • room_members — членство: комнаты, в которых состоит юзер (переживает отключение; удаляется при явном выходе или кике);
  • chat_messages / chat_attachments — история чата и вложений (BLOB).

Регистрация и вход в UI лаунчера — кнопки Войти / Регистрация. После входа токен лежит в localStorage, при переподключении шлётся auth:token, открытые комнаты автоматически восстанавливаются. Выход — кнопка Выйти.

Полезные команды протокола:

  • auth:register / auth:login / auth:token / auth:logout / auth:me / auth:rooms (список своих комнат);
  • room:create / room:join / room:leave / room:ping;
  • chat:send / chat:history;
  • transfer-master (передать корону), remove-member (кикнуть), close-room.

Проверка мультирумов/чата/кика без Electron:

Start-Process node -ArgumentList "itg-server.js" -NoNewWindow
Start-Sleep -Seconds 2
node final-multifeature-test.js

Ожидается ALL PASS (регистрация двух юзеров, две комнаты у одного клиента, передача мастера, кик без разрыва ws, чат с вложением, история из БД, восстановление членства после реконнекта).

Attribution

Интеграция и модификации: MAINER4IK. Поведение и API сверяются с XMCL и публичной документацией @xmcl/wrtc-multiplayer.