Files
RustNotMC/rsx-server-core/scripts/test.ps1
T
sasheg dev 620f136361 Серверная база со своим логином
Теперь можно зарегистрировать аккаунт, поднять сервер и зайти в него.

Пароль не ходит по сети и не хранится на сервере в обратимом виде. Клиент
называет аккаунт, сервер отдаёт его соль и одноразовый вызов для этого
соединения, клиент тянет из пароля ключ через PBKDF2-HMAC-SHA256 и
подписывает им вызов вместе с именем. Пароль пересекает сокет ноль раз, а
запись обрезать нельзя: сервер не знает исходного пароля.

Отсюда важное следствие, которое сразу отозвало одну ошибку в протоколе:
соль обязана прийти раньше, чем клиент что-то доказывает, поэтому аккаунт
называется в Hello, а не в Auth. Иначе клиент получает соль не того
аккаунта и не может вывести ключ.

Учётная запись хранится как PBKDF2 соль, счётчик раундов и хеш. Раунды
лежат рядом с записью, а не в исходниках: число, существующее только в
коде, однажды становится причиной того, что аккаунт невозможно проверить.

Имена уникальны независимо от регистра: Player и player — это один
аккаунт. Иначе игрок получает двух персонажей и не понимает, какой его.

Троттлинг двумя ведрами, по аккаунту и по адресу: одно не мешает
подбирать одно имя, другое не мешает перебирать много имён с разных
адресов. Ответ на несуществующий аккаунт выполняет ту же работу, что и на
неверный пароль, и занимает то же время.

Токены живут только в памяти. Записанный на диск токен пережил бы процесс,
который его выдал, а украденный каталог сервера стал бы набором живых
сессий.

Сессии различаются номером, а не адресом. Ключ по адресу вытеснял всех, кто
зашёл с одного IP, то есть всех игроков за одним NAT.

Тик-цикл: 20 раз в секунду, с ограничением догоняющих тиков. Мир, который
тикает по кадрам, тикает в зависимости от видеокарты — ровно то, от чего
мы уходим, перенося симуляцию на свой сервер.

Регистрация — команда оператора, а не функция у клиента. Открытая
саморегистрация превращает хранилище аккаунтов в спам-цель и в очередь
поддержки.

Проверено 12 сценариев, и главный из них — не юнит-тест, а настоящий
сокет: регистрация, вход, доказательство пароля, токен, его погашение и
появление игроком. Отдельно проверено, что неверный пароль отвергается, а
старое доказательство не принимается против нового вызова. Тик-цикл
замеряется числом тиков за секунду, а не наличием потока.

Всё одним JDK, без Gradle и без сети, как и весь проект.
2026-10-19 01:45:56 +03:00

55 lines
2.4 KiB
PowerShell

# End to end test: a real server, a real socket, a real login.
#
# This is the test that matters for the login path. A unit test can show that a
# hash matches itself; only this shows that somebody can actually register,
# connect, prove a password, get a token, redeem it and arrive as a player, and
# that a wrong password does not.
$ErrorActionPreference = 'Stop'
$Repo = Split-Path -Parent $PSScriptRoot
if (-not $Repo) { $Repo = $PSCommandPath | Split-Path -Parent | Split-Path -Parent }
$Workspace = Split-Path -Parent $Repo
$Out = Join-Path $Repo 'build'
function Say($text) { Write-Host " $text" -ForegroundColor Cyan }
$Protocol = Join-Path $Workspace 'rsx-protocol\build\classes'
$Auth = Join-Path $Workspace 'rsx-auth\build\classes'
foreach ($dep in @($Protocol, $Auth)) {
if (-not (Test-Path $dep)) {
Write-Host " Missing dependency: $dep" -ForegroundColor Red
Write-Host ' Build rsx-protocol and rsx-auth first.' -ForegroundColor Red
exit 1
}
}
New-Item -ItemType Directory -Force -Path $Out | Out-Null
$mainOut = Join-Path $Out 'classes'
Remove-Item $mainOut -Recurse -Force -ErrorAction SilentlyContinue
New-Item -ItemType Directory -Force -Path $mainOut | Out-Null
$mainFiles = @(Get-ChildItem (Join-Path $Repo 'src\main\java') -Recurse -Filter '*.java' |
ForEach-Object { $_.FullName })
$cp = "$Protocol;$Auth"
& javac -encoding UTF-8 --release 17 -Xlint:-options -nowarn -cp $cp -d $mainOut $mainFiles
if ($LASTEXITCODE -ne 0) { Write-Host ' Main failed.' -ForegroundColor Red; exit 1 }
$testOut = Join-Path $Out 'test-classes'
Remove-Item $testOut -Recurse -Force -ErrorAction SilentlyContinue
New-Item -ItemType Directory -Force -Path $testOut | Out-Null
$testFiles = @(Get-ChildItem (Join-Path $Repo 'src\test\java') -Recurse -Filter '*.java' |
ForEach-Object { $_.FullName })
& javac -encoding UTF-8 --release 17 -Xlint:-options -nowarn -cp "$cp;$mainOut" -d $testOut $testFiles
if ($LASTEXITCODE -ne 0) { Write-Host ' Test failed to compile.' -ForegroundColor Red; exit 1 }
Say "compiled $($mainFiles.Count) main + $($testFiles.Count) test files"
& java -cp "$cp;$mainOut;$testOut" net.rustnotmc.server.ServerTest (Join-Path $Out 'selftest')
if ($LASTEXITCODE -ne 0) {
Write-Host ''
Write-Host ' SERVER TESTS FAILED' -ForegroundColor Red
exit 1
}
Write-Host ''
Write-Host ' Server tests passed.' -ForegroundColor Green