fix: fix auth problem

This commit is contained in:
lucasdpt
2026-06-19 11:04:00 +02:00
parent c0d7d7ce4f
commit 81f66e25eb
3 changed files with 194 additions and 2 deletions
+4 -2
View File
@@ -46,9 +46,11 @@ class LauncherPaths {
return this.ensure(join(this.root, 'java')) return this.ensure(join(this.root, 'java'))
} }
/** Cache des tokens d'auth (prismarine-auth). */ /** Cache des tokens d'auth (prismarine-auth). Toujours dans userData,
* indépendamment de dataDir — les tokens survivent ainsi aux changements
* de dossier de données et aux mises à jour du launcher. */
get authCache(): string { get authCache(): string {
return this.ensure(join(this.root, 'auth-cache')) return this.ensure(join(app.getPath('userData'), 'auth-cache'))
} }
/** Fichier de réglages utilisateur. */ /** Fichier de réglages utilisateur. */
+2
View File
@@ -5,6 +5,7 @@ import { fetchPackMeta } from './modpack'
import { ensureJava } from './java' import { ensureJava } from './java'
import { installMinecraft, installNeoForge } from './install' import { installMinecraft, installNeoForge } from './install'
import { syncModpack } from './modpack' import { syncModpack } from './modpack'
import { ensureServerInList } from './server-list'
import { launchGame } from './launch' import { launchGame } from './launch'
import { getSettings } from './settings' import { getSettings } from './settings'
import { emit } from './events' import { emit } from './events'
@@ -42,6 +43,7 @@ export async function play(opts?: PlayOptions): Promise<void> {
await installMinecraft(meta.minecraft, repair) await installMinecraft(meta.minecraft, repair)
const versionId = await installNeoForge(meta.neoforge, meta.minecraft, javaPath, repair) const versionId = await installNeoForge(meta.neoforge, meta.minecraft, javaPath, repair)
await syncModpack(javaPath) await syncModpack(javaPath)
await ensureServerInList()
const settings = await getSettings() const settings = await getSettings()
const proc = await launchGame(versionId, auth, javaPath, settings) const proc = await launchGame(versionId, auth, javaPath, settings)
+188
View File
@@ -0,0 +1,188 @@
import { readFile, writeFile } from 'fs/promises'
import { join } from 'path'
import { paths } from './paths'
import { config } from '../shared/config'
/**
* Assure que le serveur configuré est présent dans servers.dat de l'instance.
* Si le fichier n'existe pas, il est créé. Si le serveur est déjà listé (même
* IP), rien n'est modifié pour ne pas écraser les préférences utilisateur.
*
* servers.dat : NBT non compressé (contrairement à level.dat).
* Structure : TAG_Compound root -> TAG_List "servers" -> TAG_Compound[] serveurs.
*/
export async function ensureServerInList(): Promise<void> {
if (!config.serverAddress?.trim()) return
const ip = config.serverAddress.trim()
const serversDat = join(paths.instanceDir, 'servers.dat')
let servers: ServerEntry[] = []
try {
const buf = await readFile(serversDat)
servers = decodeServersDat(buf)
} catch {
// Fichier absent ou illisible : on part d'une liste vide.
}
if (servers.some((s) => s.ip === ip)) return
// Notre serveur en tête de liste.
servers = [{ name: config.appName, ip }, ...servers]
await writeFile(serversDat, encodeServersDat(servers))
}
// ---------------------------------------------------------------------------
// Types internes
// ---------------------------------------------------------------------------
interface ServerEntry {
name: string
ip: string
}
// ---------------------------------------------------------------------------
// Encodeur NBT minimal
// ---------------------------------------------------------------------------
function b1(n: number): Buffer {
return Buffer.from([n])
}
function b2(n: number): Buffer {
const b = Buffer.allocUnsafe(2)
b.writeUInt16BE(n)
return b
}
function b4(n: number): Buffer {
const b = Buffer.allocUnsafe(4)
b.writeInt32BE(n)
return b
}
function encStr(s: string): Buffer {
const d = Buffer.from(s, 'utf8')
return Buffer.concat([b2(d.length), d])
}
function encTag(type: number, name: string, payload: Buffer): Buffer {
return Buffer.concat([b1(type), encStr(name), payload])
}
function encodeServersDat(servers: ServerEntry[]): Buffer {
const entries = servers.flatMap((s) => [
encTag(8, 'name', encStr(s.name)),
encTag(8, 'ip', encStr(s.ip)),
encTag(1, 'acceptTextures', b1(1)),
b1(0) // TAG_End clôture le compound dans la liste
])
const listPayload = Buffer.concat([b1(10), b4(servers.length), ...entries])
const rootPayload = Buffer.concat([encTag(9, 'servers', listPayload), b1(0)])
return Buffer.concat([b1(10), encStr(''), rootPayload])
}
// ---------------------------------------------------------------------------
// Décodeur NBT minimal
// ---------------------------------------------------------------------------
interface Reader {
buf: Buffer
pos: number
}
function ru8(r: Reader): number {
return r.buf[r.pos++]
}
function ru16(r: Reader): number {
const v = r.buf.readUInt16BE(r.pos)
r.pos += 2
return v
}
function ri32(r: Reader): number {
const v = r.buf.readInt32BE(r.pos)
r.pos += 4
return v
}
function rstr(r: Reader): string {
const len = ru16(r)
const s = r.buf.subarray(r.pos, r.pos + len).toString('utf8')
r.pos += len
return s
}
function skipPayload(r: Reader, type: number): void {
switch (type) {
case 1: r.pos++; return // TAG_Byte
case 2: r.pos += 2; return // TAG_Short
case 3: r.pos += 4; return // TAG_Int
case 4: r.pos += 8; return // TAG_Long
case 5: r.pos += 4; return // TAG_Float
case 6: r.pos += 8; return // TAG_Double
case 7: r.pos += ri32(r); return // TAG_Byte_Array
case 8: r.pos += ru16(r); return // TAG_String
case 9: { // TAG_List
const et = ru8(r)
const n = ri32(r)
for (let i = 0; i < n; i++) skipPayload(r, et)
return
}
case 10: { // TAG_Compound
while (r.pos < r.buf.length) {
const t = ru8(r)
if (t === 0) return
r.pos += ru16(r) // skip name
skipPayload(r, t)
}
return
}
case 11: r.pos += ri32(r) * 4; return // TAG_Int_Array
case 12: r.pos += ri32(r) * 8; return // TAG_Long_Array
}
}
function decodeServersDat(buf: Buffer): ServerEntry[] {
const r: Reader = { buf, pos: 0 }
if (ru8(r) !== 10) return [] // root doit être TAG_Compound
rstr(r) // nom de la racine (vide)
const servers: ServerEntry[] = []
while (r.pos < buf.length) {
const type = ru8(r)
if (type === 0) break
const key = rstr(r)
if (type === 9 && key === 'servers') {
const elemType = ru8(r)
const count = ri32(r)
if (elemType !== 10) {
// Type inattendu : skip
for (let i = 0; i < count; i++) skipPayload(r, elemType)
continue
}
for (let i = 0; i < count; i++) {
const entry: Partial<ServerEntry> = {}
while (r.pos < buf.length) {
const t = ru8(r)
if (t === 0) break
const k = rstr(r)
if (t === 8) {
const val = rstr(r)
if (k === 'name') entry.name = val
else if (k === 'ip') entry.ip = val
} else {
skipPayload(r, t)
}
}
if (entry.ip) servers.push({ name: entry.name ?? '', ip: entry.ip })
}
} else {
skipPayload(r, type)
}
}
return servers
}