mirror of
https://github.com/gamedig/node-gamedig.git
synced 2024-11-17 09:18:31 +01:00
Improve minecraft protocol compatibility
This commit is contained in:
parent
2df7fdae7a
commit
18be566540
4 changed files with 127 additions and 64 deletions
|
@ -215,7 +215,7 @@ Games List
|
||||||
| `mohpa` | Medal of Honor: Pacific Assault (2004)
|
| `mohpa` | Medal of Honor: Pacific Assault (2004)
|
||||||
| `mohwf` | Medal of Honor: Warfighter (2012)
|
| `mohwf` | Medal of Honor: Warfighter (2012)
|
||||||
| `medievalengineers` | Medieval Engineers (2015)
|
| `medievalengineers` | Medieval Engineers (2015)
|
||||||
| `minecraft`<br>`minecraftping` | Minecraft (2009) | [Notes](#minecraft)
|
| `minecraft`<br>`minecraftping` | Minecraft (2009)
|
||||||
| `minecraftpe`<br>`minecraftbe` | Minecraft: Bedrock Edition (2011)
|
| `minecraftpe`<br>`minecraftbe` | Minecraft: Bedrock Edition (2011)
|
||||||
| `mnc` | Monday Night Combat (2011)
|
| `mnc` | Monday Night Combat (2011)
|
||||||
| `mumble` | Mumble - GTmurmur Plugin (2005) | [Notes](#mumble)
|
| `mumble` | Mumble - GTmurmur Plugin (2005) | [Notes](#mumble)
|
||||||
|
|
|
@ -152,9 +152,9 @@ mohab|Medal of Honor: Airborne (2007)|gamespy1|port=12203,port_query_offset=97
|
||||||
moh2010|Medal of Honor (2010)|battlefield|port=7673,port_query=48888
|
moh2010|Medal of Honor (2010)|battlefield|port=7673,port_query=48888
|
||||||
mohwf|Medal of Honor: Warfighter (2012)|battlefield|port=25200,port_query_offset=22000
|
mohwf|Medal of Honor: Warfighter (2012)|battlefield|port=25200,port_query_offset=22000
|
||||||
|
|
||||||
minecraft,minecraftping|Minecraft (2009)|minecraft|port=25565|doc_notes=minecraft
|
minecraft,minecraftping|Minecraft (2009)|minecraft|port=25565
|
||||||
|
minecraftpe,minecraftbe|Minecraft: Bedrock Edition (2011)|minecraft|port=19132
|
||||||
|
|
||||||
minecraftpe,minecraftbe|Minecraft: Bedrock Edition (2011)|gamespy3|port=19132,maxAttempts=2
|
|
||||||
mnc|Monday Night Combat (2011)|valve|port=7777,port_query=27016
|
mnc|Monday Night Combat (2011)|valve|port=7777,port_query=27016
|
||||||
mtavc|Grand Theft Auto: Vice City - Multi Theft Auto (2002)|ase|port=22003,port_query_offset=123
|
mtavc|Grand Theft Auto: Vice City - Multi Theft Auto (2002)|ase|port=22003,port_query_offset=123
|
||||||
mtasa|Grand Theft Auto: San Andreas - Multi Theft Auto (2004)|ase|port=22003,port_query_offset=123
|
mtasa|Grand Theft Auto: San Andreas - Multi Theft Auto (2004)|ase|port=22003,port_query_offset=123
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
const Core = require('./core'),
|
const Core = require('./core'),
|
||||||
Varint = require('varint');
|
MinecraftVanilla = require('./minecraftvanilla'),
|
||||||
|
Gamespy3 = require('./gamespy3');
|
||||||
|
|
||||||
class Minecraft extends Core {
|
class Minecraft extends Core {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -7,72 +8,57 @@ class Minecraft extends Core {
|
||||||
this.srvRecord = "_minecraft._tcp";
|
this.srvRecord = "_minecraft._tcp";
|
||||||
}
|
}
|
||||||
async run(state) {
|
async run(state) {
|
||||||
const portBuf = Buffer.alloc(2);
|
const promises = [];
|
||||||
portBuf.writeUInt16BE(this.options.port,0);
|
|
||||||
|
|
||||||
const addressBuf = Buffer.from(this.options.host,'utf8');
|
const vanillaResolver = new MinecraftVanilla();
|
||||||
|
vanillaResolver.options = this.options;
|
||||||
|
vanillaResolver.udpSocket = this.udpSocket;
|
||||||
|
promises.push((async () => {
|
||||||
|
try { return await vanillaResolver.runOnceSafe(); } catch(e) {}
|
||||||
|
})());
|
||||||
|
|
||||||
const bufs = [
|
const bedrockResolver = new Gamespy3();
|
||||||
this.varIntBuffer(4),
|
bedrockResolver.options = {
|
||||||
this.varIntBuffer(addressBuf.length),
|
...this.options,
|
||||||
addressBuf,
|
encoding: 'utf8',
|
||||||
portBuf,
|
};
|
||||||
this.varIntBuffer(1)
|
bedrockResolver.udpSocket = this.udpSocket;
|
||||||
];
|
promises.push((async () => {
|
||||||
|
try { return await bedrockResolver.runOnceSafe(); } catch(e) {}
|
||||||
|
})());
|
||||||
|
|
||||||
const outBuffer = Buffer.concat([
|
const [ vanillaState, bedrockState ] = await Promise.all(promises);
|
||||||
this.buildPacket(0,Buffer.concat(bufs)),
|
|
||||||
this.buildPacket(0)
|
|
||||||
]);
|
|
||||||
|
|
||||||
const data = await this.withTcp(async socket => {
|
state.raw.vanilla = vanillaState;
|
||||||
return await this.tcpSend(socket, outBuffer, data => {
|
state.raw.bedrock = bedrockState;
|
||||||
if(data.length < 10) return;
|
|
||||||
const reader = this.reader(data);
|
|
||||||
const length = reader.varint();
|
|
||||||
if(data.length < length) return;
|
|
||||||
return reader.rest();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const reader = this.reader(data);
|
if (vanillaState) {
|
||||||
|
try {
|
||||||
const packetId = reader.varint();
|
let name = '';
|
||||||
this.debugLog("Packet ID: "+packetId);
|
const description = vanillaState.raw.description;
|
||||||
|
if (typeof description === 'string') {
|
||||||
const strLen = reader.varint();
|
name = description;
|
||||||
this.debugLog("String Length: "+strLen);
|
}
|
||||||
|
if (!name && typeof description === 'object' && description.text) {
|
||||||
const str = reader.rest().toString('utf8');
|
name = description.text;
|
||||||
this.debugLog(str);
|
}
|
||||||
|
if (!name && typeof description === 'object' && description.extra) {
|
||||||
const json = JSON.parse(str);
|
name = description.extra.map(part => part.text).join('');
|
||||||
delete json.favicon;
|
}
|
||||||
|
state.name = name;
|
||||||
state.raw = json;
|
} catch(e) {}
|
||||||
state.maxplayers = json.players.max;
|
if (vanillaState.maxplayers) state.maxplayers = vanillaState.maxplayers;
|
||||||
if(json.players.sample) {
|
if (vanillaState.players) state.players = vanillaState.players;
|
||||||
for(const player of json.players.sample) {
|
|
||||||
state.players.push({
|
|
||||||
id: player.id,
|
|
||||||
name: player.name
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
state.players = json.players.online;
|
if (bedrockState) {
|
||||||
}
|
if (bedrockState.name) state.name = bedrockState.name;
|
||||||
|
if (bedrockState.maxplayers) state.maxplayers = bedrockState.maxplayers;
|
||||||
varIntBuffer(num) {
|
if (bedrockState.players) state.players = bedrockState.players;
|
||||||
return Buffer.from(Varint.encode(num));
|
}
|
||||||
}
|
// remove dupe spaces from name
|
||||||
buildPacket(id,data) {
|
state.name = state.name.replace(/\s+/g, ' ');
|
||||||
if(!data) data = Buffer.from([]);
|
// remove color codes from name
|
||||||
const idBuffer = this.varIntBuffer(id);
|
state.name = state.name.replace(/\u00A7./g, '');
|
||||||
return Buffer.concat([
|
|
||||||
this.varIntBuffer(data.length+idBuffer.length),
|
|
||||||
idBuffer,
|
|
||||||
data
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
77
protocols/minecraftvanilla.js
Normal file
77
protocols/minecraftvanilla.js
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
const Core = require('./core'),
|
||||||
|
Varint = require('varint');
|
||||||
|
|
||||||
|
class MinecraftVanilla extends Core {
|
||||||
|
async run(state) {
|
||||||
|
const portBuf = Buffer.alloc(2);
|
||||||
|
portBuf.writeUInt16BE(this.options.port,0);
|
||||||
|
|
||||||
|
const addressBuf = Buffer.from(this.options.host,'utf8');
|
||||||
|
|
||||||
|
const bufs = [
|
||||||
|
this.varIntBuffer(47),
|
||||||
|
this.varIntBuffer(addressBuf.length),
|
||||||
|
addressBuf,
|
||||||
|
portBuf,
|
||||||
|
this.varIntBuffer(1)
|
||||||
|
];
|
||||||
|
|
||||||
|
const outBuffer = Buffer.concat([
|
||||||
|
this.buildPacket(0,Buffer.concat(bufs)),
|
||||||
|
this.buildPacket(0)
|
||||||
|
]);
|
||||||
|
|
||||||
|
const data = await this.withTcp(async socket => {
|
||||||
|
return await this.tcpSend(socket, outBuffer, data => {
|
||||||
|
if(data.length < 10) return;
|
||||||
|
const reader = this.reader(data);
|
||||||
|
const length = reader.varint();
|
||||||
|
if(data.length < length) return;
|
||||||
|
return reader.rest();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const reader = this.reader(data);
|
||||||
|
|
||||||
|
const packetId = reader.varint();
|
||||||
|
this.debugLog("Packet ID: "+packetId);
|
||||||
|
|
||||||
|
const strLen = reader.varint();
|
||||||
|
this.debugLog("String Length: "+strLen);
|
||||||
|
|
||||||
|
const str = reader.rest().toString('utf8');
|
||||||
|
this.debugLog(str);
|
||||||
|
|
||||||
|
const json = JSON.parse(str);
|
||||||
|
delete json.favicon;
|
||||||
|
|
||||||
|
state.raw = json;
|
||||||
|
state.maxplayers = json.players.max;
|
||||||
|
if(json.players.sample) {
|
||||||
|
for(const player of json.players.sample) {
|
||||||
|
state.players.push({
|
||||||
|
id: player.id,
|
||||||
|
name: player.name
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let i = 0; i < Math.min(json.players.online, 10000); i++) {
|
||||||
|
state.players.push({});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
varIntBuffer(num) {
|
||||||
|
return Buffer.from(Varint.encode(num));
|
||||||
|
}
|
||||||
|
buildPacket(id,data) {
|
||||||
|
if(!data) data = Buffer.from([]);
|
||||||
|
const idBuffer = this.varIntBuffer(id);
|
||||||
|
return Buffer.concat([
|
||||||
|
this.varIntBuffer(data.length+idBuffer.length),
|
||||||
|
idBuffer,
|
||||||
|
data
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = MinecraftVanilla;
|
Loading…
Reference in a new issue