From a53a2977f2cd458db86387b1e5016e8a877247bf Mon Sep 17 00:00:00 2001 From: deathcap Date: Sun, 24 Jan 2016 18:53:34 -0800 Subject: [PATCH] Add dynamic cross-protocol support --- doc/README.md | 5 +++ examples/client_auto/client_auto.js | 31 ++++++++++++++ examples/client_auto/package.json | 8 ++++ src/createClientAuto.js | 64 +++++++++++++++++++++++++++++ src/index.js | 2 + 5 files changed, 110 insertions(+) create mode 100644 examples/client_auto/client_auto.js create mode 100644 examples/client_auto/package.json create mode 100644 src/createClientAuto.js diff --git a/doc/README.md b/doc/README.md index daca5a1..c109899 100644 --- a/doc/README.md +++ b/doc/README.md @@ -75,6 +75,11 @@ Returns a `Client` instance and perform login. * checkTimeoutInterval : default to `10*1000` (10s), check if keepalive received at that period, disconnect otherwise. * version : 1.8 or 1.9 : default to 1.8 +## mc.createClientAuto(options, cb) + +Pings the server and attempts to call `createClient(options)` with the appropriate protocol version. +When created, calls the callback `cb(err, client)`. + ## mc.Client(isServer,version) Create a new client, if `isServer` is true then it is a server-side client, otherwise it's a client-side client. diff --git a/examples/client_auto/client_auto.js b/examples/client_auto/client_auto.js new file mode 100644 index 0000000..0af381f --- /dev/null +++ b/examples/client_auto/client_auto.js @@ -0,0 +1,31 @@ +var mc = require('minecraft-protocol'); + +if(process.argv.length < 4 || process.argv.length > 6) { + console.log("Usage : node echo.js [] []"); + process.exit(1); +} + +mc.createClientAuto({ + host: process.argv[2], + port: parseInt(process.argv[3]), + username: process.argv[4] ? process.argv[4] : "echo", + password: process.argv[5] +}, function(err, client) { + if (err) throw err; + + client.on('connect', function() { + console.info('connected'); + }); + client.on('disconnect', function(packet) { + console.log('disconnected: '+ packet.reason); + }); + client.on('chat', function(packet) { + var jsonMsg = JSON.parse(packet.message); + if(jsonMsg.translate == 'chat.type.announcement' || jsonMsg.translate == 'chat.type.text') { + var username = jsonMsg.with[0].text; + var msg = jsonMsg.with[1]; + if(username === client.username) return; + client.write('chat', {message: msg}); + } + }); +}); diff --git a/examples/client_auto/package.json b/examples/client_auto/package.json new file mode 100644 index 0000000..56fcdf2 --- /dev/null +++ b/examples/client_auto/package.json @@ -0,0 +1,8 @@ +{ + "name": "node-minecraft-protocol-example", + "version": "0.0.0", + "private": true, + "dependencies": { + }, + "description": "A node-minecraft-protocol example" +} diff --git a/src/createClientAuto.js b/src/createClientAuto.js new file mode 100644 index 0000000..209fe36 --- /dev/null +++ b/src/createClientAuto.js @@ -0,0 +1,64 @@ +'use strict'; + +var ping = require('./ping'); +var assert = require('assert'); +var debug = require('./debug'); +var createClient = require('./createClient'); + +// see http://wiki.vg/Protocol_version_numbers +// Get the minecraft-data version string for a protocol version +// TODO: switch to using https://github.com/PrismarineJS/minecraft-data/pull/92 +function protocol2version(n) { + if (n >= 48) return '1.9'; // 1.9 snapshots (15w+), 16w03a is 96 + if (n >= 6 && n <= 47) return '1.8.9'; // including 1.8 snapshots (14w) + if (n >= 4 && n <= 5) return '1.7.10'; // including 1.7 prereleases + // TODO: earlier versions "Beginning with the 1.7.1 pre-release (and release 1.7.2), versioning was reset." + throw new Error(`unsupported/unknown protocol version: ${versionProtocol}, update protocol2version`); +} + +function createClientAsync(options, cb) { + assert.ok(options, 'options is required'); + + debug('pinging',options.host); + // TODO: refactor with DNS SRV lookup in NMP + // TODO: detect ping timeout, https://github.com/PrismarineJS/node-minecraft-protocol/issues/329 + ping(options, function(err, response) { + var client; + + if (err) return cb(err, null); + debug('ping response',response); + // TODO: could also use ping pre-connect to save description, type, negotiate protocol etc. + // ^ see https://github.com/PrismarineJS/node-minecraft-protocol/issues/327 + var motd = response.description; + debug('Server description:',motd); // TODO: save + + // Pass server-reported version to protocol handler + // The version string is interpereted by https://github.com/PrismarineJS/node-minecraft-data + var versionName = response.version.name; // 1.8.9, 1.7.10 + var versionProtocol = response.version.protocol;// 47, 5 + + debug(`Server version: ${versionName}, protocol: ${versionProtocol}`); + // Note that versionName is a descriptive version stirng like '1.8.9' on vailla, but other + // servers add their own name (Spigot 1.8.8, Glowstone++ 1.8.9) so we cannot use it directly, + // even though it is in a format accepted by minecraft-data. Instead, translate the protocol. + options.version = protocol2version(versionProtocol); + + // Use the exact same protocol version + // Requires https://github.com/PrismarineJS/node-minecraft-protocol/pull/330 + options.protocolVersion = versionProtocol; + + if (response.modinfo && response.modinfo.type === 'FML') { + // Use the list of Forge mods from the server ping, so client will match server + var forgeMods = response.modinfo.modList; + debug('Using forgeMods:',forgeMods); + // TODO: https://github.com/PrismarineJS/node-minecraft-protocol/issues/114 + // https://github.com/PrismarineJS/node-minecraft-protocol/pull/326 + throw new Error('FML/Forge not yet supported'); + } else { + client = createClient(options); // vanilla + } + cb(null, client); + }); +} + +module.exports = createClientAsync; diff --git a/src/index.js b/src/index.js index 8d90027..697c657 100644 --- a/src/index.js +++ b/src/index.js @@ -2,10 +2,12 @@ var Client = require('./client'); var Server = require('./server'); var serializer = require("./transforms/serializer"); var createClient = require("./createClient"); +var createClientAuto = require("./createClientAuto"); var createServer = require("./createServer"); module.exports = { createClient: createClient, + createClientAuto: createClientAuto, createServer: createServer, Client: Client, Server: Server,