mirror of
https://github.com/barkeser2002/mcsleepingserverstarter.git
synced 2026-09-25 05:10:09 +03:00
177 lines
7.1 KiB
JavaScript
177 lines
7.1 KiB
JavaScript
const UUID = require('uuid-1345')
|
|
const yggdrasil = require('yggdrasil')
|
|
const fs = require('fs').promises
|
|
const mcDefaultFolderPath = require('minecraft-folder-path')
|
|
const path = require('path')
|
|
|
|
const launcherDataFile = 'launcher_accounts.json'
|
|
|
|
module.exports = async function (client, options) {
|
|
if (!options.profilesFolder && options.profilesFolder !== false) { // not defined, but not explicitly false. fallback to default
|
|
let mcFolderExists = true
|
|
try {
|
|
await fs.access(mcDefaultFolderPath)
|
|
} catch (ignoreErr) {
|
|
mcFolderExists = false
|
|
}
|
|
options.profilesFolder = mcFolderExists ? mcDefaultFolderPath : '.' // local folder if mc folder doesn't exist
|
|
}
|
|
|
|
const yggdrasilClient = yggdrasil({ agent: options.agent, host: options.authServer || 'https://authserver.mojang.com' })
|
|
const clientToken = options.clientToken || (options.session && options.session.clientToken) || (options.profilesFolder && (await getLauncherProfiles()).mojangClientToken) || UUID.v4().toString().replace(/-/g, '')
|
|
const skipValidation = false || options.skipValidation
|
|
options.accessToken = null
|
|
options.haveCredentials = !!options.password || (clientToken != null && options.session != null) || (options.profilesFolder && !!getProfileId(await getLauncherProfiles()))
|
|
|
|
async function getLauncherProfiles () { // get launcher profiles
|
|
try {
|
|
return JSON.parse(await fs.readFile(path.join(options.profilesFolder, launcherDataFile), 'utf8'))
|
|
} catch (err) {
|
|
await fs.mkdir(options.profilesFolder, { recursive: true })
|
|
await fs.writeFile(path.join(options.profilesFolder, launcherDataFile), '{}')
|
|
return { accounts: {} }
|
|
}
|
|
}
|
|
|
|
function getProfileId (auths) {
|
|
try {
|
|
const lowerUsername = options.username.toLowerCase()
|
|
return Object.keys(auths.accounts).find(key =>
|
|
auths.accounts[key].username.toLowerCase() === lowerUsername ||
|
|
auths.accounts[key].minecraftProfile.name.toLowerCase() === lowerUsername
|
|
)
|
|
} catch (err) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
if (options.haveCredentials) {
|
|
// make a request to get the case-correct username before connecting.
|
|
const cb = function (err, session) {
|
|
if (options.profilesFolder) {
|
|
getLauncherProfiles().then((auths) => {
|
|
if (!auths.accounts) auths.accounts = []
|
|
try {
|
|
let profile = getProfileId(auths)
|
|
if (err) {
|
|
if (profile && auths.accounts[profile].type !== 'Xbox') { // MS accounts are deemed invalid in case someone tries to use one without specifying options.auth, but we shouldn't remove these
|
|
delete auths.accounts[profile] // profile is invalid, remove
|
|
}
|
|
} else { // successful login
|
|
if (!profile) {
|
|
profile = UUID.v4().toString().replace(/-/g, '') // create new profile
|
|
throw new Error('Account not found') // TODO: Find a way to calculate remoteId. Launcher ignores account entry and makes a new one if remoteId is incorrect
|
|
}
|
|
if (!auths.accounts[profile].remoteId) {
|
|
delete auths.accounts[profile]
|
|
throw new Error('Account has no remoteId') // TODO: Find a way to calculate remoteId. Launcher ignores account entry and makes a new one if remoteId is incorrect
|
|
}
|
|
if (!auths.mojangClientToken) {
|
|
auths.mojangClientToken = clientToken
|
|
}
|
|
|
|
if (clientToken === auths.mojangClientToken) { // only do something when we can save a new clienttoken or they match
|
|
const oldProfileObj = auths.accounts[profile]
|
|
const newProfileObj = {
|
|
accessToken: session.accessToken,
|
|
minecraftProfile: {
|
|
id: session.selectedProfile.id,
|
|
name: session.selectedProfile.name
|
|
},
|
|
userProperites: oldProfileObj?.userProperites ?? [],
|
|
remoteId: oldProfileObj?.remoteId ?? '',
|
|
username: options.username,
|
|
localId: profile,
|
|
type: (options.auth?.toLowerCase() === 'mojang' ? 'Mojang' : 'Xbox'),
|
|
persistent: true
|
|
}
|
|
auths.accounts[profile] = newProfileObj
|
|
}
|
|
}
|
|
} catch (ignoreErr) {
|
|
// again, silently fail, just don't save anything
|
|
}
|
|
fs.writeFile(path.join(options.profilesFolder, launcherDataFile), JSON.stringify(auths, null, 2)).then(() => {}, (ignoreErr) => {
|
|
// console.warn("Couldn't save tokens:\n", err) // not any error, we just don't save the file
|
|
})
|
|
}, (ignoreErr) => {
|
|
// console.warn("Skipped saving tokens because of error\n", err) // not any error, we just don't save the file
|
|
})
|
|
}
|
|
|
|
if (err) {
|
|
client.emit('error', err)
|
|
} else {
|
|
client.session = session
|
|
client.username = session.selectedProfile.name
|
|
options.accessToken = session.accessToken
|
|
client.emit('session', session)
|
|
options.connect(client)
|
|
}
|
|
}
|
|
|
|
if (!options.session && options.profilesFolder) {
|
|
try {
|
|
const auths = await getLauncherProfiles()
|
|
const profile = getProfileId(auths)
|
|
|
|
if (profile) {
|
|
const newUsername = auths.accounts[profile].username
|
|
const displayName = auths.accounts[profile].minecraftProfile.name
|
|
const uuid = auths.accounts[profile].minecraftProfile.id
|
|
const newProfile = {
|
|
id: uuid,
|
|
name: displayName
|
|
}
|
|
|
|
options.session = {
|
|
accessToken: auths.accounts[profile].accessToken,
|
|
clientToken: auths.mojangClientToken,
|
|
selectedProfile: newProfile,
|
|
availableProfiles: [newProfile]
|
|
}
|
|
options.username = newUsername
|
|
}
|
|
} catch (ignoreErr) {
|
|
// skip the error :/
|
|
}
|
|
}
|
|
|
|
if (options.session) {
|
|
if (!skipValidation) {
|
|
yggdrasilClient.validate(options.session.accessToken, function (err) {
|
|
if (!err) { cb(null, options.session) } else {
|
|
yggdrasilClient.refresh(options.session.accessToken, options.session.clientToken, function (err, accessToken, data) {
|
|
if (!err) {
|
|
cb(null, data)
|
|
} else if (options.username && options.password) {
|
|
yggdrasilClient.auth({
|
|
user: options.username,
|
|
pass: options.password,
|
|
token: clientToken,
|
|
requestUser: true
|
|
}, cb)
|
|
} else {
|
|
cb(err, data)
|
|
}
|
|
})
|
|
}
|
|
})
|
|
} else {
|
|
// trust that the provided session is a working one
|
|
cb(null, options.session)
|
|
}
|
|
} else {
|
|
yggdrasilClient.auth({
|
|
user: options.username,
|
|
pass: options.password,
|
|
token: clientToken
|
|
}, cb)
|
|
}
|
|
} else {
|
|
// assume the server is in offline mode and just go for it.
|
|
client.username = options.username
|
|
options.connect(client)
|
|
}
|
|
}
|