merge: preserve ShaCraft 0.1.1 account and Windows features in modular launcher

This commit is contained in:
emil28092005
2026-09-09 13:10:17 +03:00
54 changed files with 2130 additions and 551 deletions
+31
View File
@@ -0,0 +1,31 @@
import { equal } from 'node:assert/strict'
import { test } from 'node:test'
import { launchAccess, linkedNickname, validCredentials } from './account.ts'
test('launch requires a restored ShaCraft account and its verified Aeronautics link', () => {
equal(launchAccess(undefined), 'loading')
equal(launchAccess(null), 'login')
equal(launchAccess({ username: 'account', links: [] }), 'link')
equal(launchAccess({ username: 'account', links: [{ server_id: 'create', mc_username: 'Other_Name' }] }), 'link')
equal(launchAccess({ username: 'account', links: [{ server_id: 'aoc', mc_username: 'Verified_Name' }] }), 'ready')
})
test('identity uses snake-case account link payload and never the account login', () => {
const account = { username: 'Local_Login', links: [
{ server_id: 'create', mc_username: 'Other_Name' },
{ server_id: 'aoc', mc_username: 'Verified_Name' },
] }
equal(linkedNickname(account), 'Verified_Name')
equal(linkedNickname({ username: 'Valid_Login', links: [] }), null)
equal(linkedNickname({ username: 'Valid_Login', links: [{ server_id: 'aoc', mc_username: '../unsafe' }] }), null)
})
test('ShaCraft credentials preserve the server 3–32 login and 3–128 password contract', () => {
equal(validCredentials('abc', '123'), true)
equal(validCredentials('a'.repeat(32), 'p'.repeat(128)), true)
equal(validCredentials('ab', '123'), false)
equal(validCredentials('a'.repeat(33), '123'), false)
equal(validCredentials('неверно', '123'), false)
equal(validCredentials('account', '12'), false)
equal(validCredentials('account', 'p'.repeat(129)), false)
})
+18
View File
@@ -0,0 +1,18 @@
import type { ShaCraftAccount } from '../types/launcher'
import { isValidNickname } from './settings'
/** Only the authenticated account's verified Aeronautics link selects a name. */
export function linkedNickname(account: ShaCraftAccount | null | undefined): string | null {
const nickname = account?.links.find((link) => link.server_id === 'aoc')?.mc_username
return nickname && isValidNickname(nickname) ? nickname : null
}
export function launchAccess(account: ShaCraftAccount | null | undefined) {
if (account === undefined) return 'loading'
if (account === null) return 'login'
return linkedNickname(account) ? 'ready' : 'link'
}
export function validCredentials(username: string, password: string): boolean {
return /^[A-Za-z0-9_]{3,32}$/.test(username) && password.length >= 3 && password.length <= 128
}
+11
View File
@@ -14,6 +14,17 @@ test('installation, running game and exit have distinct states', () => {
deepStrictEqual(gameReducer(running, { type: 'exited', result: { profileId, exitCode: 0 } }), initialGameState)
})
test('every launch can reconcile the modpack before installation and process spawn', () => {
const syncing = gameReducer(initialGameState, { type: 'sync', profileId })
equal(syncing.operation.phase, 'syncing')
const synced = gameReducer(syncing, { type: 'synced', profileId })
const installingAfterSync = gameReducer(synced, { type: 'install', profileId })
equal(installingAfterSync.operation.phase, 'installing')
const launchingAfterInstall = gameReducer(installingAfterSync, { type: 'launch', profileId })
equal(launchingAfterInstall.operation.phase, 'launching')
equal(gameReducer(launchingAfterInstall, { type: 'started', profileId }).operation.phase, 'running')
})
test('a fast child exit cannot be overwritten by a late launch acknowledgement', () => {
const exited = gameReducer(launching, { type: 'exited', result: { profileId, exitCode: 1 } })
const lateAcknowledgement = gameReducer(exited, { type: 'started', profileId })