mirror of
https://github.com/gamedig/node-gamedig.git
synced 2024-11-17 17:25:19 +01:00
Add support for native minecraft bedrock protocol, as some bedrock servers apparently don't respond to gamespy3. Fixes #211 (2.0.26)
This commit is contained in:
parent
e4c29f9cbc
commit
4ecce4eff8
4 changed files with 104 additions and 9 deletions
|
@ -1,3 +1,7 @@
|
|||
### 2.0.26
|
||||
* Added support for the native minecraft bedrock protocol, since some
|
||||
bedrock servers apparently do not respond to the gamespy3 protocol.
|
||||
|
||||
### 2.0.25
|
||||
* Support challenges in A2S_INFO (upcoming change to valve protocol)
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
],
|
||||
"main": "lib/index.js",
|
||||
"author": "GameDig Contributors",
|
||||
"version": "2.0.25",
|
||||
"version": "2.0.26",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/gamedig/node-gamedig.git"
|
||||
|
|
|
@ -1,7 +1,16 @@
|
|||
const Core = require('./core'),
|
||||
MinecraftVanilla = require('./minecraftvanilla'),
|
||||
MinecraftBedrock = require('./minecraftbedrock'),
|
||||
Gamespy3 = require('./gamespy3');
|
||||
|
||||
/*
|
||||
Vanilla servers respond to minecraftvanilla only
|
||||
Some modded vanilla servers respond to minecraftvanilla and gamespy3, or gamespy3 only
|
||||
Some bedrock servers respond to gamespy3 only
|
||||
Some bedrock servers respond to minecraftbedrock only
|
||||
Unsure if any bedrock servers respond to gamespy3 and minecraftbedrock
|
||||
*/
|
||||
|
||||
class Minecraft extends Core {
|
||||
constructor() {
|
||||
super();
|
||||
|
@ -17,25 +26,40 @@ class Minecraft extends Core {
|
|||
try { return await vanillaResolver.runOnceSafe(); } catch(e) {}
|
||||
})());
|
||||
|
||||
const bedrockResolver = new Gamespy3();
|
||||
bedrockResolver.options = {
|
||||
const gamespyResolver = new Gamespy3();
|
||||
gamespyResolver.options = {
|
||||
...this.options,
|
||||
encoding: 'utf8',
|
||||
};
|
||||
gamespyResolver.udpSocket = this.udpSocket;
|
||||
promises.push((async () => {
|
||||
try { return await gamespyResolver.runOnceSafe(); } catch(e) {}
|
||||
})());
|
||||
|
||||
const bedrockResolver = new MinecraftBedrock();
|
||||
bedrockResolver.options = this.options;
|
||||
bedrockResolver.udpSocket = this.udpSocket;
|
||||
promises.push((async () => {
|
||||
try { return await bedrockResolver.runOnceSafe(); } catch(e) {}
|
||||
})());
|
||||
|
||||
const [ vanillaState, bedrockState ] = await Promise.all(promises);
|
||||
const [ vanillaState, gamespyState, bedrockState ] = await Promise.all(promises);
|
||||
|
||||
state.raw.vanilla = vanillaState;
|
||||
state.raw.gamespy = gamespyState;
|
||||
state.raw.bedrock = bedrockState;
|
||||
|
||||
if (!vanillaState && !bedrockState) {
|
||||
if (!vanillaState && !gamespyState && !bedrockState) {
|
||||
throw new Error('No protocols succeeded');
|
||||
}
|
||||
|
||||
// Ordered from least worth to most worth (player names / etc)
|
||||
if (bedrockState) {
|
||||
if (bedrockState.name) state.name = bedrockState.name;
|
||||
if (bedrockState.maxplayers) state.maxplayers = bedrockState.maxplayers;
|
||||
if (bedrockState.players) state.players = bedrockState.players;
|
||||
if (bedrockState.map) state.map = bedrockState.map;
|
||||
}
|
||||
if (vanillaState) {
|
||||
try {
|
||||
let name = '';
|
||||
|
@ -54,10 +78,10 @@ class Minecraft extends Core {
|
|||
if (vanillaState.maxplayers) state.maxplayers = vanillaState.maxplayers;
|
||||
if (vanillaState.players) state.players = vanillaState.players;
|
||||
}
|
||||
if (bedrockState) {
|
||||
if (bedrockState.name) state.name = bedrockState.name;
|
||||
if (bedrockState.maxplayers) state.maxplayers = bedrockState.maxplayers;
|
||||
if (bedrockState.players) state.players = bedrockState.players;
|
||||
if (gamespyState) {
|
||||
if (gamespyState.name) state.name = gamespyState.name;
|
||||
if (gamespyState.maxplayers) state.maxplayers = gamespyState.maxplayers;
|
||||
if (gamespyState.players) state.players = gamespyState.players;
|
||||
}
|
||||
// remove dupe spaces from name
|
||||
state.name = state.name.replace(/\s+/g, ' ');
|
||||
|
|
67
protocols/minecraftbedrock.js
Normal file
67
protocols/minecraftbedrock.js
Normal file
|
@ -0,0 +1,67 @@
|
|||
const Core = require('./core');
|
||||
|
||||
class MinecraftBedrock extends Core {
|
||||
constructor() {
|
||||
super();
|
||||
this.byteorder = 'be';
|
||||
}
|
||||
|
||||
async run(state) {
|
||||
const bufs = [
|
||||
Buffer.from([0x01]), // Message ID, ID_UNCONNECTED_PING
|
||||
Buffer.from('0000000000000000', 'hex'), // Nonce / timestamp
|
||||
Buffer.from('00ffff00fefefefefdfdfdfd12345678', 'hex'), // Magic
|
||||
Buffer.from('0000000000000000', 'hex') // Cliend GUID
|
||||
];
|
||||
|
||||
return await this.udpSend(Buffer.concat(bufs), buffer => {
|
||||
const reader = this.reader(buffer);
|
||||
|
||||
const messageId = reader.uint(1);
|
||||
if (messageId !== 0x1c) {
|
||||
throw new Error('Invalid message id');
|
||||
}
|
||||
|
||||
const nonce = reader.part(8).toString('hex'); // should match the nonce we sent
|
||||
this.logger.debug('Nonce: ' + nonce);
|
||||
|
||||
state.raw.guid = reader.part(8).toString('hex');
|
||||
|
||||
const magic = reader.part(16).toString('hex');
|
||||
this.logger.debug('Magic value: ' + magic);
|
||||
|
||||
const statusLen = reader.uint(2);
|
||||
if (reader.remaining() !== statusLen) {
|
||||
throw new Error('Invalid status length: ' + reader.remaining() + ' vs ' + statusLen);
|
||||
}
|
||||
|
||||
const statusStr = reader.rest().toString('utf8');
|
||||
this.logger.debug('Raw status str: ' + statusStr);
|
||||
|
||||
const split = statusStr.split(';');
|
||||
if (split.length < 12) {
|
||||
throw new Error('Missing enough chunks in status str');
|
||||
}
|
||||
|
||||
let i = 0;
|
||||
state.raw.edition = split[i++];
|
||||
state.name = split[i++];
|
||||
state.raw.protocolVersion = split[i++];
|
||||
state.raw.mcVersion = split[i++];
|
||||
state.players = parseInt(split[i++]);
|
||||
state.maxplayers = parseInt(split[i++]);
|
||||
state.raw.serverId = split[i++];
|
||||
state.map = split[i++];
|
||||
state.raw.gameMode = split[i++];
|
||||
state.raw.nintendoOnly = !!parseInt(split[i++]);
|
||||
state.raw.ipv4Port = split[i++];
|
||||
state.raw.ipv6Port = split[i++];
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = MinecraftBedrock;
|
Loading…
Reference in a new issue