mirror of
https://github.com/tribufu/node-gamedig
synced 2026-06-01 09:42:41 +00: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
|
### 2.0.25
|
||||||
* Support challenges in A2S_INFO (upcoming change to valve protocol)
|
* Support challenges in A2S_INFO (upcoming change to valve protocol)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@
|
||||||
],
|
],
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"author": "GameDig Contributors",
|
"author": "GameDig Contributors",
|
||||||
"version": "2.0.25",
|
"version": "2.0.26",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/gamedig/node-gamedig.git"
|
"url": "https://github.com/gamedig/node-gamedig.git"
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,16 @@
|
||||||
const Core = require('./core'),
|
const Core = require('./core'),
|
||||||
MinecraftVanilla = require('./minecraftvanilla'),
|
MinecraftVanilla = require('./minecraftvanilla'),
|
||||||
|
MinecraftBedrock = require('./minecraftbedrock'),
|
||||||
Gamespy3 = require('./gamespy3');
|
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 {
|
class Minecraft extends Core {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
@ -17,25 +26,40 @@ class Minecraft extends Core {
|
||||||
try { return await vanillaResolver.runOnceSafe(); } catch(e) {}
|
try { return await vanillaResolver.runOnceSafe(); } catch(e) {}
|
||||||
})());
|
})());
|
||||||
|
|
||||||
const bedrockResolver = new Gamespy3();
|
const gamespyResolver = new Gamespy3();
|
||||||
bedrockResolver.options = {
|
gamespyResolver.options = {
|
||||||
...this.options,
|
...this.options,
|
||||||
encoding: 'utf8',
|
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;
|
bedrockResolver.udpSocket = this.udpSocket;
|
||||||
promises.push((async () => {
|
promises.push((async () => {
|
||||||
try { return await bedrockResolver.runOnceSafe(); } catch(e) {}
|
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.vanilla = vanillaState;
|
||||||
|
state.raw.gamespy = gamespyState;
|
||||||
state.raw.bedrock = bedrockState;
|
state.raw.bedrock = bedrockState;
|
||||||
|
|
||||||
if (!vanillaState && !bedrockState) {
|
if (!vanillaState && !gamespyState && !bedrockState) {
|
||||||
throw new Error('No protocols succeeded');
|
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) {
|
if (vanillaState) {
|
||||||
try {
|
try {
|
||||||
let name = '';
|
let name = '';
|
||||||
|
|
@ -54,10 +78,10 @@ class Minecraft extends Core {
|
||||||
if (vanillaState.maxplayers) state.maxplayers = vanillaState.maxplayers;
|
if (vanillaState.maxplayers) state.maxplayers = vanillaState.maxplayers;
|
||||||
if (vanillaState.players) state.players = vanillaState.players;
|
if (vanillaState.players) state.players = vanillaState.players;
|
||||||
}
|
}
|
||||||
if (bedrockState) {
|
if (gamespyState) {
|
||||||
if (bedrockState.name) state.name = bedrockState.name;
|
if (gamespyState.name) state.name = gamespyState.name;
|
||||||
if (bedrockState.maxplayers) state.maxplayers = bedrockState.maxplayers;
|
if (gamespyState.maxplayers) state.maxplayers = gamespyState.maxplayers;
|
||||||
if (bedrockState.players) state.players = bedrockState.players;
|
if (gamespyState.players) state.players = gamespyState.players;
|
||||||
}
|
}
|
||||||
// remove dupe spaces from name
|
// remove dupe spaces from name
|
||||||
state.name = state.name.replace(/\s+/g, ' ');
|
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…
Add table
Add a link
Reference in a new issue