Refactor client connection sequence (#189)

* Refactor client connection sequence
Allow connection info to come in after Client construction, emit "connect_allowed" similar to nmp

* Fix breaking ping behavior change

* fix createClient connect callback

* correct behavior

* remove comments

* refactor impl

* fix incorrect use of `this`
This commit is contained in:
extremeheat 2022-03-27 16:15:20 -04:00 committed by GitHub
commit dfff13867d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 60 additions and 33 deletions

View file

@ -4,7 +4,7 @@ const { serialize, isDebug } = require('./datatypes/util')
const debug = require('debug')('minecraft-protocol')
const Options = require('./options')
const auth = require('./client/auth')
const initRaknet = require('./rak')
const { KeyExchange } = require('./handshake/keyExchange')
const Login = require('./handshake/login')
const LoginVerify = require('./handshake/loginVerify')
@ -19,20 +19,6 @@ class Client extends Connection {
constructor (options) {
super()
this.options = { ...Options.defaultOptions, ...options }
this.validateOptions()
const { RakClient } = require('./rak')(this.options.useNativeRaknet)
this.serializer = createSerializer(this.options.version)
this.deserializer = createDeserializer(this.options.version)
KeyExchange(this, null, this.options)
Login(this, null, this.options)
LoginVerify(this, null, this.options)
const host = this.options.host
const port = this.options.port
this.connection = new RakClient({ useWorkers: this.options.useRaknetWorkers, host, port })
this.startGameData = {}
this.clientRuntimeId = null
@ -42,9 +28,31 @@ class Client extends Connection {
this.outLog = (...args) => debug('C <-', ...args)
}
this.conLog = this.options.conLog === undefined ? console.log : this.options.conLog
if (!options.delayedInit) {
this.init()
}
}
init () {
this.validateOptions()
this.serializer = createSerializer(this.options.version)
this.deserializer = createDeserializer(this.options.version)
KeyExchange(this, null, this.options)
Login(this, null, this.options)
LoginVerify(this, null, this.options)
const { RakClient } = initRaknet(this.options.useNativeRaknet)
const host = this.options.host
const port = this.options.port
this.connection = new RakClient({ useWorkers: this.options.useRaknetWorkers, host, port })
this.emit('connect_allowed')
}
connect () {
if (!this.connection) throw new Error('Connect not currently allowed') // must wait for `connect_allowed`, or use `createClient`
this.on('session', this._connect)
if (this.options.offline) {

View file

@ -4,6 +4,21 @@ const minecraftFolderPath = require('minecraft-folder-path')
const debug = require('debug')('minecraft-protocol')
const { uuidFrom } = require('../datatypes/util')
function validateOptions (options) {
if (!options.profilesFolder) {
options.profilesFolder = path.join(minecraftFolderPath, 'nmp-cache')
}
if (options.authTitle === undefined) {
options.authTitle = Titles.MinecraftNintendoSwitch
options.deviceType = 'Nintendo'
}
}
async function realmAuthenticate (options) {
validateOptions(options)
throw new Error('Not implemented')
}
/**
* Authenticates to Minecraft via device code based Microsoft auth,
* then connects to the specified server in Client Options
@ -13,16 +28,10 @@ const { uuidFrom } = require('../datatypes/util')
* @param {object} options - Client Options
*/
async function authenticate (client, options) {
if (!options.profilesFolder) {
options.profilesFolder = path.join(minecraftFolderPath, 'nmp-cache')
}
if (options.authTitle === undefined) {
options.authTitle = Titles.MinecraftNintendoSwitch
options.deviceType = 'Nintendo'
}
validateOptions(options)
try {
const Authflow = new PrismarineAuth(options.username, options.profilesFolder, options, options.onMsaCode)
const chains = await Authflow.getMinecraftBedrockToken(client.clientX509).catch(e => {
const authflow = options.authflow || new PrismarineAuth(options.username, options.profilesFolder, options, options.onMsaCode)
const chains = await authflow.getMinecraftBedrockToken(client.clientX509).catch(e => {
if (options.password) console.warn('Sign in failed, try removing the password field')
throw e
})
@ -71,5 +80,6 @@ function postAuthenticate (client, profile, chains) {
module.exports = {
createOfflineSession,
authenticate
authenticate,
realmAuthenticate
}

View file

@ -1,27 +1,36 @@
const { Client } = require('./client')
const { RakClient } = require('./rak')(true)
const { Versions, CURRENT_VERSION } = require('./options')
const { sleep } = require('./datatypes/util')
const assert = require('assert')
const Options = require('./options')
const advertisement = require('./server/advertisement')
const auth = require('./client/auth')
/** @param {{ version?: number, host: string, port?: number, connectTimeout?: number, skipPing?: boolean }} options */
function createClient (options) {
assert(options)
const client = new Client({ port: 19132, ...options })
const client = new Client({ port: 19132, ...options, delayedInit: true })
if (options.skipPing) {
connect(client)
} else { // Try to ping
client.ping().then(data => {
const ad = advertisement.fromServerName(data)
client.options.version = options.version ?? (Versions[ad.version] ? ad.version : CURRENT_VERSION)
if (client.conLog) client.conLog(`Connecting to server ${ad.motd} (${ad.name}), version ${ad.version}`, client.options.version !== ad.version ? ` (as ${client.options.version})` : '')
client.emit('connect_allowed')
connect(client)
}, client)
function onServerInfo () {
if (options.skipPing) {
client.init()
} else {
ping(options).then(ad => {
const adVersion = ad.version?.split('.').slice(0, 3).join('.') // Only 3 version units
client.options.version = options.version ?? (Options.Versions[adVersion] ? adVersion : Options.CURRENT_VERSION)
client.conLog?.(`Connecting to server ${ad.motd} (${ad.name}), version ${ad.version}`, client.options.version !== ad.version ? ` (as ${client.options.version})` : '')
client.init()
})
}
}
if (options.realms) {
auth.realmAuthenticate(client.options).then(onServerInfo).catch(e => client.emit('error', e))
} else {
onServerInfo()
}
client.on('connect_allowed', () => connect(client))
return client
}