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)
|
||||
| `mohwf` | Medal of Honor: Warfighter (2012)
|
||||
| `medievalengineers` | Medieval Engineers (2015)
|
||||
| `minecraft`<br>`minecraftping` | Minecraft (2009) | [Notes](#minecraft)
|
||||
| `minecraft`<br>`minecraftping` | Minecraft (2009)
|
||||
| `minecraftpe`<br>`minecraftbe` | Minecraft: Bedrock Edition (2011)
|
||||
| `mnc` | Monday Night Combat (2011)
|
||||
| `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
|
||||
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
|
||||
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
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
const Core = require('./core'),
|
||||
Varint = require('varint');
|
||||
MinecraftVanilla = require('./minecraftvanilla'),
|
||||
Gamespy3 = require('./gamespy3');
|
||||
|
||||
class Minecraft extends Core {
|
||||
constructor() {
|
||||
|
@ -7,72 +8,57 @@ class Minecraft extends Core {
|
|||
this.srvRecord = "_minecraft._tcp";
|
||||
}
|
||||
async run(state) {
|
||||
const portBuf = Buffer.alloc(2);
|
||||
portBuf.writeUInt16BE(this.options.port,0);
|
||||
const promises = [];
|
||||
|
||||
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 = [
|
||||
this.varIntBuffer(4),
|
||||
this.varIntBuffer(addressBuf.length),
|
||||
addressBuf,
|
||||
portBuf,
|
||||
this.varIntBuffer(1)
|
||||
];
|
||||
const bedrockResolver = new Gamespy3();
|
||||
bedrockResolver.options = {
|
||||
...this.options,
|
||||
encoding: 'utf8',
|
||||
};
|
||||
bedrockResolver.udpSocket = this.udpSocket;
|
||||
promises.push((async () => {
|
||||
try { return await bedrockResolver.runOnceSafe(); } catch(e) {}
|
||||
})());
|
||||
|
||||
const outBuffer = Buffer.concat([
|
||||
this.buildPacket(0,Buffer.concat(bufs)),
|
||||
this.buildPacket(0)
|
||||
]);
|
||||
const [ vanillaState, bedrockState ] = await Promise.all(promises);
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
state.raw.vanilla = vanillaState;
|
||||
state.raw.bedrock = bedrockState;
|
||||
|
||||
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
|
||||
});
|
||||
}
|
||||
if (vanillaState) {
|
||||
try {
|
||||
let name = '';
|
||||
const description = vanillaState.raw.description;
|
||||
if (typeof description === 'string') {
|
||||
name = description;
|
||||
}
|
||||
if (!name && typeof description === 'object' && description.text) {
|
||||
name = description.text;
|
||||
}
|
||||
if (!name && typeof description === 'object' && description.extra) {
|
||||
name = description.extra.map(part => part.text).join('');
|
||||
}
|
||||
state.name = name;
|
||||
} catch(e) {}
|
||||
if (vanillaState.maxplayers) state.maxplayers = vanillaState.maxplayers;
|
||||
if (vanillaState.players) state.players = vanillaState.players;
|
||||
}
|
||||
state.players = json.players.online;
|
||||
}
|
||||
|
||||
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
|
||||
]);
|
||||
if (bedrockState) {
|
||||
if (bedrockState.name) state.name = bedrockState.name;
|
||||
if (bedrockState.maxplayers) state.maxplayers = bedrockState.maxplayers;
|
||||
if (bedrockState.players) state.players = bedrockState.players;
|
||||
}
|
||||
// remove dupe spaces from name
|
||||
state.name = state.name.replace(/\s+/g, ' ');
|
||||
// remove color codes from name
|
||||
state.name = state.name.replace(/\u00A7./g, '');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
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