mirror of
https://github.com/unmojang/node-minecraft-protocol.git
synced 2025-10-06 09:32:08 -04:00

Normally, the client will send a kick packet if the client is disconnected from the server, but if the connection is lost due to network or lower-level protocol issues, the 'end' event will be emitted. Log this event in the examples so it is clear why the scripts exit.
267 lines
7.1 KiB
JavaScript
267 lines
7.1 KiB
JavaScript
var mc = require('minecraft-protocol');
|
|
var ProtoDef = require('protodef').ProtoDef;
|
|
|
|
if(process.argv.length < 4 || process.argv.length > 6) {
|
|
console.log("Usage : node echo.js <host> <port> [<name>] [<password>]");
|
|
process.exit(1);
|
|
}
|
|
|
|
var client = mc.createClient({
|
|
forge: true,
|
|
host: process.argv[2],
|
|
port: parseInt(process.argv[3]),
|
|
username: process.argv[4] ? process.argv[4] : "echo",
|
|
password: process.argv[5]
|
|
});
|
|
|
|
client.on('connect', function() {
|
|
console.info('connected');
|
|
});
|
|
client.on('disconnect', function(packet) {
|
|
console.log('disconnected: '+ packet.reason);
|
|
});
|
|
client.on('end', function(err) {
|
|
console.log('Connection lost');
|
|
});
|
|
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});
|
|
}
|
|
});
|
|
|
|
var proto = new ProtoDef();
|
|
// copied from ../../dist/transforms/serializer.js TODO: refactor
|
|
proto.addType("string", ["pstring", {
|
|
countType: "varint"
|
|
}]);
|
|
|
|
|
|
// http://wiki.vg/Minecraft_Forge_Handshake
|
|
proto.addType('fml|hsMapper',
|
|
[
|
|
"mapper",
|
|
{
|
|
"type": "byte",
|
|
"mappings": {
|
|
"0": "ServerHello",
|
|
"1": "ClientHello",
|
|
"2": "ModList",
|
|
"3": "RegistryData",
|
|
"-1": "HandshakeAck",
|
|
"-2": "HandshakeReset"
|
|
},
|
|
}
|
|
]
|
|
);
|
|
|
|
// TODO: refactor to use one big switch like https://github.com/PrismarineJS/node-minecraft-protocol/blob/master/src/transforms/serializer.js#L21
|
|
proto.addType('FML|HS',
|
|
[
|
|
"container",
|
|
[
|
|
{
|
|
"name": "discriminator",
|
|
"type": "fml|hsMapper"
|
|
},
|
|
|
|
{
|
|
"anon": true,
|
|
"type":
|
|
[
|
|
"switch",
|
|
{
|
|
"compareTo": "discriminator",
|
|
"fields":
|
|
{
|
|
"ServerHello":
|
|
[
|
|
"container",
|
|
[
|
|
{
|
|
"name": "fmlProtocolVersion",
|
|
"type": "byte"
|
|
},
|
|
{
|
|
"name": "overrideDimension",
|
|
"type":
|
|
[
|
|
"switch",
|
|
{
|
|
// "Only sent if protocol version is greater than 1."
|
|
"compareTo": "fmlProtocolVersion",
|
|
"fields":
|
|
{
|
|
"0": "void",
|
|
"1": "void"
|
|
},
|
|
"default": "int"
|
|
}
|
|
]
|
|
},
|
|
],
|
|
],
|
|
|
|
"ClientHello":
|
|
[
|
|
"container",
|
|
[
|
|
{
|
|
"name": "fmlProtocolVersion",
|
|
"type": "byte"
|
|
}
|
|
]
|
|
],
|
|
|
|
"ModList":
|
|
[
|
|
"container",
|
|
[
|
|
{
|
|
"name": "mods",
|
|
"type":
|
|
[
|
|
"array",
|
|
{
|
|
"countType": "varint",
|
|
"type":
|
|
[
|
|
"container",
|
|
[
|
|
{
|
|
"name": "modid",
|
|
"type": "string"
|
|
},
|
|
{
|
|
"name": "version",
|
|
"type": "string"
|
|
}
|
|
]
|
|
],
|
|
},
|
|
]
|
|
}
|
|
],
|
|
],
|
|
|
|
"RegistryData":
|
|
[
|
|
"container",
|
|
[
|
|
{
|
|
"name": "hasMore",
|
|
"type": "bool"
|
|
},
|
|
|
|
/* TODO: support all fields
|
|
{
|
|
"name": "registryName",
|
|
"type": "string"
|
|
},
|
|
*/
|
|
],
|
|
],
|
|
|
|
"HandshakeAck":
|
|
[
|
|
"container",
|
|
[
|
|
{
|
|
"name": "phase",
|
|
"type": "byte"
|
|
},
|
|
],
|
|
],
|
|
|
|
},
|
|
}
|
|
]
|
|
}
|
|
]
|
|
]
|
|
);
|
|
|
|
function writeAck(client, phase) {
|
|
var ackData = proto.createPacketBuffer('FML|HS', {
|
|
discriminator: 'HandshakeAck', // HandshakeAck,
|
|
phase: phase
|
|
});
|
|
client.write('custom_payload', {
|
|
channel: 'FML|HS',
|
|
data: ackData
|
|
});
|
|
}
|
|
|
|
client.on('custom_payload', function(packet) {
|
|
var channel = packet.channel;
|
|
var data = packet.data;
|
|
|
|
if (channel === 'REGISTER') {
|
|
var channels = data.toString().split('\0');
|
|
console.log('Server-side registered channels:',channels);
|
|
// TODO: do something?
|
|
// expect: [ 'FML|HS', 'FML', 'FML|MP', 'FML', 'FORGE' ]
|
|
} else if (channel === 'FML|HS') {
|
|
var parsed = proto.parsePacketBuffer('FML|HS', data);
|
|
console.log('FML|HS',parsed);
|
|
|
|
|
|
if (parsed.data.discriminator === 'ServerHello') {
|
|
if (parsed.data.fmlProtocolVersion > 2) {
|
|
// TODO: support higher protocols, if they change
|
|
}
|
|
|
|
client.write('custom_payload', {
|
|
channel: 'REGISTER',
|
|
data: new Buffer(['FML|HS', 'FML', 'FML|MP', 'FML', 'FORGE'].join('\0'))
|
|
});
|
|
|
|
var clientHello = proto.createPacketBuffer('FML|HS', {
|
|
discriminator: 'ClientHello',
|
|
fmlProtocolVersion: parsed.data.fmlProtocolVersion
|
|
});
|
|
|
|
client.write('custom_payload', {
|
|
channel: 'FML|HS',
|
|
data: clientHello
|
|
});
|
|
|
|
console.log('Sending client modlist');
|
|
var modList = proto.createPacketBuffer('FML|HS', {
|
|
discriminator: 'ModList',
|
|
//mods: []
|
|
// TODO: send from ServerListPing packet, allow customizing not hardcoding
|
|
mods: [
|
|
{modid:'IronChest', version:'6.0.121.768'}
|
|
]
|
|
});
|
|
client.write('custom_payload', {
|
|
channel: 'FML|HS',
|
|
data: modList
|
|
});
|
|
writeAck(client, 2); // WAITINGSERVERDATA
|
|
} else if (parsed.data.discriminator === 'ModList') {
|
|
console.log('Server ModList:',parsed.data.mods);
|
|
// TODO: client/server check if mods compatible
|
|
|
|
} else if (parsed.data.discriminator === 'RegistryData') {
|
|
console.log('RegistryData',parsed.data);
|
|
if (parsed.data.hasMore === false) {
|
|
console.log('LAST RegistryData');
|
|
|
|
writeAck(client, 3); // WAITINGSERVERCOMPLETE
|
|
}
|
|
} else if (parsed.data.discriminator === 'HandshakeAck') {
|
|
if (parsed.data.phase === 2) { // WAITINGCACK
|
|
writeAck(client, 4); // PENDINGCOMPLETE
|
|
} else if (parsed.data.phase === 3) { // COMPLETE
|
|
writeAck(client, 5); // COMPLETE
|
|
console.log('HandshakeAck Complete!');
|
|
}
|
|
}
|
|
}
|
|
});
|