mirror of
https://github.com/PrismarineJS/node-minecraft-protocol.git
synced 2024-11-24 00:07:51 -05:00
Realm Joining (#1056)
* Add docs * Implement Realm joining * Check for Microsoft auth * Remove on chat event * Add realmName option to example * Passthrough client to `realmAuthenticate` * Fix overwriting existing authflow * Don't use `??=` * Lint
This commit is contained in:
parent
0526edf5f3
commit
28093fb1fb
8 changed files with 107 additions and 7 deletions
|
@ -139,6 +139,10 @@ Returns a `Client` instance and perform login.
|
|||
* id : a numeric client id used for referring to multiple clients in a server
|
||||
* validateChannelProtocol (optional) : whether or not to enable protocol validation for custom protocols using plugin channels. Defaults to true
|
||||
* disableChatSigning (optional) : Don't try obtaining chat signing keys from Mojang (1.19+)
|
||||
* realms : An object which should contain one of the following properties: `realmId` or `pickRealm`. When defined will attempt to join a Realm without needing to specify host/port. **The authenticated account must either own the Realm or have been invited to it**
|
||||
* realmId : The id of the Realm to join.
|
||||
* pickRealm(realms) : A function which will have an array of the user Realms (joined/owned) passed to it. The function should return a Realm.
|
||||
|
||||
|
||||
## mc.Client(isServer,version,[customPackets])
|
||||
|
||||
|
|
|
@ -95,6 +95,20 @@ client.on('chat', function(packet) {
|
|||
If the server is in offline mode, you may leave out the `password` option and switch auth to `offline`.
|
||||
You can also leave out `password` when using a Microsoft account. If provided, password based auth will be attempted first which may fail. *Note:* if using a Microsoft account, your account age must be >= 18 years old.
|
||||
|
||||
### Client example joining a Realm
|
||||
|
||||
Example to connect to a Realm that the authenticating account is owner of or has been invited to:
|
||||
|
||||
```js
|
||||
var mc = require('minecraft-protocol');
|
||||
var client = mc.createClient({
|
||||
realms: {
|
||||
pickRealm: (realms) => realms[0] // Function which recieves an array of joined/owned Realms and must return a single Realm. Can be async
|
||||
},
|
||||
auth: 'microsoft'
|
||||
})
|
||||
```
|
||||
|
||||
### Hello World server example
|
||||
|
||||
```js
|
||||
|
|
25
examples/client_realms/client_realms.js
Normal file
25
examples/client_realms/client_realms.js
Normal file
|
@ -0,0 +1,25 @@
|
|||
'use strict'
|
||||
|
||||
const mc = require('minecraft-protocol')
|
||||
|
||||
const [,, username, realmName] = process.argv
|
||||
if (!realmName) {
|
||||
console.log('Usage : node client_realms.js <username/email> <realm_name>')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const client = mc.createClient({
|
||||
realms: {
|
||||
// realmId: '1234567', // Connect the client to a Realm using the Realms ID
|
||||
pickRealm: (realms) => realms.find(e => e.name === realmName) // Connect the client to a Realm using a function that returns a Realm
|
||||
},
|
||||
username,
|
||||
auth: 'microsoft' // This option must be present and set to 'microsoft' to join a Realm.
|
||||
})
|
||||
|
||||
client.on('connect', function () {
|
||||
console.info('connected')
|
||||
})
|
||||
client.on('disconnect', function (packet) {
|
||||
console.log('disconnected: ' + packet.reason)
|
||||
})
|
8
examples/client_realms/package.json
Normal file
8
examples/client_realms/package.json
Normal file
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"name": "node-minecraft-protocol-example",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
},
|
||||
"description": "A node-minecraft-protocol example"
|
||||
}
|
|
@ -57,6 +57,7 @@
|
|||
"node-rsa": "^0.4.2",
|
||||
"prismarine-auth": "^2.2.0",
|
||||
"prismarine-nbt": "^2.0.0",
|
||||
"prismarine-realms": "^1.2.0",
|
||||
"protodef": "^1.8.0",
|
||||
"readable-stream": "^4.1.0",
|
||||
"uuid-1345": "^1.0.1",
|
||||
|
|
|
@ -2,19 +2,23 @@ const path = require('path')
|
|||
const { Authflow: PrismarineAuth, Titles } = require('prismarine-auth')
|
||||
const minecraftFolderPath = require('minecraft-folder-path')
|
||||
const debug = require('debug')('minecraft-protocol')
|
||||
const { RealmAPI } = require('prismarine-realms')
|
||||
|
||||
async function authenticate (client, options) {
|
||||
function validateOptions (options) {
|
||||
if (!options.profilesFolder) {
|
||||
options.profilesFolder = path.join(minecraftFolderPath, 'nmp-cache')
|
||||
}
|
||||
|
||||
if (options.authTitle === undefined) {
|
||||
options.authTitle = Titles.MinecraftNintendoSwitch
|
||||
options.deviceType = 'Nintendo'
|
||||
options.flow = 'live'
|
||||
}
|
||||
}
|
||||
|
||||
client.authflow = new PrismarineAuth(options.username, options.profilesFolder, options, options.onMsaCode)
|
||||
async function authenticate (client, options) {
|
||||
validateOptions(options)
|
||||
|
||||
if (!client.authflow) client.authflow = new PrismarineAuth(options.username, options.profilesFolder, options, options.onMsaCode)
|
||||
const { token, entitlements, profile, certificates } = await client.authflow.getMinecraftJavaToken({ fetchProfile: true, fetchCertificates: !options.disableChatSigning }).catch(e => {
|
||||
if (options.password) console.warn('Sign in failed, try removing the password field\n')
|
||||
if (e.toString().includes('Not Found')) console.warn(`Please verify that the account ${options.username} owns Minecraft\n`)
|
||||
|
@ -42,6 +46,38 @@ async function authenticate (client, options) {
|
|||
options.connect(client)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
authenticate
|
||||
async function realmAuthenticate (client, options) {
|
||||
validateOptions(options)
|
||||
|
||||
client.authflow = new PrismarineAuth(options.username, options.profilesFolder, options, options.onMsaCode)
|
||||
|
||||
const api = RealmAPI.from(client.authflow, 'java')
|
||||
const realms = await api.getRealms()
|
||||
|
||||
debug('realms', realms)
|
||||
|
||||
if (!realms || !realms.length) throw Error('Couldn\'t find any Realms for the authenticated account')
|
||||
|
||||
let realm
|
||||
|
||||
if (options.realms.realmId) {
|
||||
realm = realms.find(e => e.id === Number(options.realms.realmId))
|
||||
} else if (options.realms.pickRealm) {
|
||||
if (typeof options.realms.pickRealm !== 'function') throw Error('realms.pickRealm must be a function')
|
||||
realm = await options.realms.pickRealm(realms)
|
||||
}
|
||||
|
||||
if (!realm) throw Error('Couldn\'t find a Realm to connect to. Authenticated account must be the owner or has been invited to the Realm.')
|
||||
|
||||
const { host, port } = await realm.getAddress()
|
||||
|
||||
debug('realms connection', { host, port })
|
||||
|
||||
options.host = host
|
||||
options.port = port
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
authenticate,
|
||||
realmAuthenticate
|
||||
}
|
||||
|
|
|
@ -20,7 +20,8 @@ module.exports = createClient
|
|||
function createClient (options) {
|
||||
assert.ok(options, 'options is required')
|
||||
assert.ok(options.username, 'username is required')
|
||||
if (!options.version) { options.version = false }
|
||||
if (!options.version && !options.realms) { options.version = false }
|
||||
if (options.realms && options.auth !== 'microsoft') throw new Error('Currently Realms can only be joined with auth: "microsoft"')
|
||||
|
||||
// TODO: avoid setting default version if autoVersion is enabled
|
||||
const optVersion = options.version || require('./version').defaultVersion
|
||||
|
@ -43,7 +44,11 @@ function createClient (options) {
|
|||
auth(client, options)
|
||||
break
|
||||
case 'microsoft':
|
||||
microsoftAuth.authenticate(client, options).catch((err) => client.emit('error', err))
|
||||
if (options.realms) {
|
||||
microsoftAuth.realmAuthenticate(client, options).then(() => microsoftAuth.authenticate(client, options)).catch((err) => client.emit('error', err))
|
||||
} else {
|
||||
microsoftAuth.authenticate(client, options).catch((err) => client.emit('error', err))
|
||||
}
|
||||
break
|
||||
case 'offline':
|
||||
default:
|
||||
|
|
7
src/index.d.ts
vendored
7
src/index.d.ts
vendored
|
@ -6,6 +6,7 @@ import * as Stream from 'stream'
|
|||
import { Agent } from 'http'
|
||||
import { Transform } from "readable-stream";
|
||||
import { KeyObject } from 'crypto';
|
||||
import { Realm } from "prismarine-realms"
|
||||
|
||||
type PromiseLike = Promise<void> | void
|
||||
|
||||
|
@ -131,6 +132,7 @@ declare module 'minecraft-protocol' {
|
|||
id?: number
|
||||
session?: SessionOption
|
||||
validateChannelProtocol?: boolean,
|
||||
realms?: RealmsOptions
|
||||
// 1.19+
|
||||
disableChatSigning?: boolean
|
||||
}
|
||||
|
@ -259,6 +261,11 @@ declare module 'minecraft-protocol' {
|
|||
latency: number
|
||||
}
|
||||
|
||||
export interface RealmsOptions {
|
||||
realmId?: string
|
||||
pickRealm?: (realms: Realm[]) => Realm
|
||||
}
|
||||
|
||||
export const states: typeof States
|
||||
export const supportedVersions: string[]
|
||||
export const defaultVersion: string
|
||||
|
|
Loading…
Reference in a new issue