mirror of
https://github.com/unmojang/node-minecraft-protocol.git
synced 2025-09-30 14:41:27 -04:00
Merge branch 'master' of https://github.com/PrismarineJS/node-minecraft-protocol into es6
Conflicts: .gitignore index.js package.json
This commit is contained in:
commit
4e32c12bfd
16
.editorconfig
Normal file
16
.editorconfig
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# Editorconfig
|
||||||
|
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
charset = utf-8
|
||||||
|
|
||||||
|
[*.js]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[{package.json}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
8
.gitignore
vendored
8
.gitignore
vendored
@ -1,4 +1,4 @@
|
|||||||
/node_modules
|
node_modules
|
||||||
/test/npm-debug.log
|
test/npm-debug.log
|
||||||
/test/server
|
test/server
|
||||||
/dist/
|
dist/
|
||||||
|
264
README.md
264
README.md
@ -1,22 +1,26 @@
|
|||||||
# minecraft protocol [](http://badge.fury.io/js/minecraft-protocol)
|
# minecraft protocol [](http://badge.fury.io/js/minecraft-protocol)
|
||||||
|
|
||||||
|
[](https://gitter.im/PrismarineJS/node-minecraft-protocol?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||||
|
|
||||||
Parse and serialize minecraft packets, plus authentication and encryption.
|
Parse and serialize minecraft packets, plus authentication and encryption.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
* Supports Minecraft version 1.7.10
|
* Supports Minecraft version 1.8.1
|
||||||
* Parses all packets and emits events with packet fields as JavaScript
|
* Parses all packets and emits events with packet fields as JavaScript
|
||||||
objects.
|
objects.
|
||||||
* Send a packet by supplying fields as a JavaScript object.
|
* Send a packet by supplying fields as a JavaScript object.
|
||||||
* Client
|
* Client
|
||||||
- Authenticating and logging in
|
- Authenticating and logging in
|
||||||
- Encryption on and encryption off
|
- Encryption
|
||||||
|
- Compression
|
||||||
- Both online and offline mode
|
- Both online and offline mode
|
||||||
- Respond to keep-alive packets.
|
- Respond to keep-alive packets.
|
||||||
- Ping a server for status
|
- Ping a server for status
|
||||||
* Server
|
* Server
|
||||||
- Offline mode
|
- Online/Offline mode
|
||||||
- Encryption and online mode
|
- Encryption
|
||||||
|
- Compression
|
||||||
- Handshake
|
- Handshake
|
||||||
- Keep-alive checking
|
- Keep-alive checking
|
||||||
- Ping status
|
- Ping status
|
||||||
@ -98,7 +102,10 @@ server.on('login', function(client) {
|
|||||||
|
|
||||||
`npm install minecraft-protocol`
|
`npm install minecraft-protocol`
|
||||||
|
|
||||||
On Windows, first follow the Windows instructions from
|
URSA, an optional dependency, should improve login times
|
||||||
|
for servers. However, it can be somewhat complicated to install.
|
||||||
|
|
||||||
|
Follow the instructions from
|
||||||
[Obvious/ursa](https://github.com/Obvious/ursa)
|
[Obvious/ursa](https://github.com/Obvious/ursa)
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
@ -228,125 +235,138 @@ correct data type.
|
|||||||
### Test Coverage
|
### Test Coverage
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
packets
|
packets
|
||||||
√ handshaking,ServerBound,0x00
|
✓ handshaking,ServerBound,0x00
|
||||||
√ status,ServerBound,0x00
|
✓ status,ServerBound,0x00
|
||||||
√ status,ServerBound,0x01
|
✓ status,ServerBound,0x01
|
||||||
√ status,ClientBound,0x00
|
✓ status,ClientBound,0x00
|
||||||
√ status,ClientBound,0x01
|
✓ status,ClientBound,0x01
|
||||||
√ login,ServerBound,0x00
|
✓ login,ServerBound,0x00
|
||||||
√ login,ServerBound,0x01
|
✓ login,ServerBound,0x01
|
||||||
√ login,ClientBound,0x00
|
✓ login,ClientBound,0x00
|
||||||
√ login,ClientBound,0x01
|
✓ login,ClientBound,0x01
|
||||||
√ login,ClientBound,0x02
|
✓ login,ClientBound,0x02
|
||||||
√ play,ServerBound,0x00
|
✓ login,ClientBound,0x03
|
||||||
√ play,ServerBound,0x01
|
✓ play,ServerBound,0x00
|
||||||
√ play,ServerBound,0x02
|
✓ play,ServerBound,0x01
|
||||||
√ play,ServerBound,0x03
|
✓ play,ServerBound,0x02
|
||||||
√ play,ServerBound,0x04
|
✓ play,ServerBound,0x03
|
||||||
√ play,ServerBound,0x05
|
✓ play,ServerBound,0x04
|
||||||
√ play,ServerBound,0x06
|
✓ play,ServerBound,0x05
|
||||||
√ play,ServerBound,0x07
|
✓ play,ServerBound,0x06
|
||||||
√ play,ServerBound,0x08
|
✓ play,ServerBound,0x07
|
||||||
√ play,ServerBound,0x09
|
✓ play,ServerBound,0x08
|
||||||
√ play,ServerBound,0x0a
|
✓ play,ServerBound,0x09
|
||||||
√ play,ServerBound,0x0b
|
✓ play,ServerBound,0x0a
|
||||||
√ play,ServerBound,0x0c
|
✓ play,ServerBound,0x0b
|
||||||
√ play,ServerBound,0x0d
|
✓ play,ServerBound,0x0c
|
||||||
√ play,ServerBound,0x0e
|
✓ play,ServerBound,0x0d
|
||||||
√ play,ServerBound,0x0f
|
✓ play,ServerBound,0x0e
|
||||||
√ play,ServerBound,0x10
|
✓ play,ServerBound,0x0f
|
||||||
√ play,ServerBound,0x11
|
✓ play,ServerBound,0x10
|
||||||
√ play,ServerBound,0x12
|
✓ play,ServerBound,0x11
|
||||||
√ play,ServerBound,0x13
|
✓ play,ServerBound,0x12
|
||||||
√ play,ServerBound,0x14
|
✓ play,ServerBound,0x13
|
||||||
√ play,ServerBound,0x15
|
✓ play,ServerBound,0x14
|
||||||
√ play,ServerBound,0x16
|
✓ play,ServerBound,0x15
|
||||||
√ play,ServerBound,0x17
|
✓ play,ServerBound,0x16
|
||||||
√ play,ClientBound,0x00
|
✓ play,ServerBound,0x17
|
||||||
√ play,ClientBound,0x01
|
✓ play,ServerBound,0x18
|
||||||
√ play,ClientBound,0x02
|
✓ play,ServerBound,0x19
|
||||||
√ play,ClientBound,0x03
|
✓ play,ClientBound,0x00
|
||||||
√ play,ClientBound,0x04
|
✓ play,ClientBound,0x01
|
||||||
√ play,ClientBound,0x05
|
✓ play,ClientBound,0x02
|
||||||
√ play,ClientBound,0x06
|
✓ play,ClientBound,0x03
|
||||||
√ play,ClientBound,0x07
|
✓ play,ClientBound,0x04
|
||||||
√ play,ClientBound,0x08
|
✓ play,ClientBound,0x05
|
||||||
√ play,ClientBound,0x09
|
✓ play,ClientBound,0x06
|
||||||
√ play,ClientBound,0x0a
|
✓ play,ClientBound,0x07
|
||||||
√ play,ClientBound,0x0b
|
✓ play,ClientBound,0x08
|
||||||
√ play,ClientBound,0x0c
|
✓ play,ClientBound,0x09
|
||||||
√ play,ClientBound,0x0d
|
✓ play,ClientBound,0x0a
|
||||||
√ play,ClientBound,0x0e
|
✓ play,ClientBound,0x0b
|
||||||
√ play,ClientBound,0x0f
|
✓ play,ClientBound,0x0c
|
||||||
√ play,ClientBound,0x10
|
✓ play,ClientBound,0x0d
|
||||||
√ play,ClientBound,0x11
|
✓ play,ClientBound,0x0e
|
||||||
√ play,ClientBound,0x12
|
✓ play,ClientBound,0x0f
|
||||||
√ play,ClientBound,0x13
|
✓ play,ClientBound,0x10
|
||||||
√ play,ClientBound,0x14
|
✓ play,ClientBound,0x11
|
||||||
√ play,ClientBound,0x15
|
✓ play,ClientBound,0x12
|
||||||
√ play,ClientBound,0x16
|
✓ play,ClientBound,0x13
|
||||||
√ play,ClientBound,0x17
|
✓ play,ClientBound,0x14
|
||||||
√ play,ClientBound,0x18
|
✓ play,ClientBound,0x15
|
||||||
√ play,ClientBound,0x19
|
✓ play,ClientBound,0x16
|
||||||
√ play,ClientBound,0x1a
|
✓ play,ClientBound,0x17
|
||||||
√ play,ClientBound,0x1b
|
✓ play,ClientBound,0x18
|
||||||
√ play,ClientBound,0x1c
|
✓ play,ClientBound,0x19
|
||||||
√ play,ClientBound,0x1d
|
✓ play,ClientBound,0x1a
|
||||||
√ play,ClientBound,0x1e
|
✓ play,ClientBound,0x1b
|
||||||
√ play,ClientBound,0x1f
|
✓ play,ClientBound,0x1c
|
||||||
√ play,ClientBound,0x20
|
✓ play,ClientBound,0x1d
|
||||||
√ play,ClientBound,0x21
|
✓ play,ClientBound,0x1e
|
||||||
√ play,ClientBound,0x22
|
✓ play,ClientBound,0x1f
|
||||||
√ play,ClientBound,0x23
|
✓ play,ClientBound,0x20
|
||||||
√ play,ClientBound,0x24
|
✓ play,ClientBound,0x21
|
||||||
√ play,ClientBound,0x25
|
✓ play,ClientBound,0x22
|
||||||
√ play,ClientBound,0x26
|
✓ play,ClientBound,0x23
|
||||||
√ play,ClientBound,0x27
|
✓ play,ClientBound,0x24
|
||||||
√ play,ClientBound,0x28
|
✓ play,ClientBound,0x25
|
||||||
√ play,ClientBound,0x29
|
✓ play,ClientBound,0x26
|
||||||
√ play,ClientBound,0x2a
|
✓ play,ClientBound,0x27
|
||||||
√ play,ClientBound,0x2b
|
✓ play,ClientBound,0x28
|
||||||
√ play,ClientBound,0x2c
|
✓ play,ClientBound,0x29
|
||||||
√ play,ClientBound,0x2d
|
✓ play,ClientBound,0x2a
|
||||||
√ play,ClientBound,0x2e
|
✓ play,ClientBound,0x2b
|
||||||
√ play,ClientBound,0x2f
|
✓ play,ClientBound,0x2c
|
||||||
√ play,ClientBound,0x30
|
✓ play,ClientBound,0x2d
|
||||||
√ play,ClientBound,0x31
|
✓ play,ClientBound,0x2e
|
||||||
√ play,ClientBound,0x32
|
✓ play,ClientBound,0x2f
|
||||||
√ play,ClientBound,0x33
|
✓ play,ClientBound,0x30
|
||||||
√ play,ClientBound,0x34
|
✓ play,ClientBound,0x31
|
||||||
√ play,ClientBound,0x35
|
✓ play,ClientBound,0x32
|
||||||
√ play,ClientBound,0x36
|
✓ play,ClientBound,0x33
|
||||||
√ play,ClientBound,0x37
|
✓ play,ClientBound,0x34
|
||||||
√ play,ClientBound,0x38
|
✓ play,ClientBound,0x35
|
||||||
√ play,ClientBound,0x39
|
✓ play,ClientBound,0x36
|
||||||
√ play,ClientBound,0x3a
|
✓ play,ClientBound,0x37
|
||||||
√ play,ClientBound,0x3b
|
✓ play,ClientBound,0x38
|
||||||
√ play,ClientBound,0x3c
|
✓ play,ClientBound,0x39
|
||||||
√ play,ClientBound,0x3d
|
✓ play,ClientBound,0x3a
|
||||||
√ play,ClientBound,0x3e
|
✓ play,ClientBound,0x3b
|
||||||
√ play,ClientBound,0x3f
|
✓ play,ClientBound,0x3c
|
||||||
√ play,ClientBound,0x40
|
✓ play,ClientBound,0x3d
|
||||||
|
✓ play,ClientBound,0x3e
|
||||||
|
✓ play,ClientBound,0x3f
|
||||||
|
✓ play,ClientBound,0x40
|
||||||
|
✓ play,ClientBound,0x41
|
||||||
|
✓ play,ClientBound,0x42
|
||||||
|
✓ play,ClientBound,0x43
|
||||||
|
✓ play,ClientBound,0x44
|
||||||
|
✓ play,ClientBound,0x45
|
||||||
|
✓ play,ClientBound,0x46
|
||||||
|
✓ play,ClientBound,0x47
|
||||||
|
✓ play,ClientBound,0x48
|
||||||
|
✓ play,ClientBound,0x49
|
||||||
|
|
||||||
client
|
client
|
||||||
√ pings the server (32734ms)
|
✓ pings the server (65754ms)
|
||||||
√ connects successfully - online mode (23367ms)
|
✓ connects successfully - online mode (STUBBED)
|
||||||
√ connects successfully - offline mode (10261ms)
|
✓ connects successfully - offline mode (STUBBED)
|
||||||
√ gets kicked when no credentials supplied in online mode (18400ms)
|
✓ gets kicked when no credentials supplied in online mode (67167ms)
|
||||||
√ does not crash for 10000ms (24780ms)
|
✓ does not crash for 10000ms (69597ms)
|
||||||
|
|
||||||
mc-server
|
mc-server
|
||||||
√ starts listening and shuts down cleanly (73ms)
|
✓ starts listening and shuts down cleanly
|
||||||
√ kicks clients that do not log in (295ms)
|
✓ kicks clients that do not log in (133ms)
|
||||||
√ kicks clients that do not send keepalive packets (266ms)
|
✓ kicks clients that do not send keepalive packets (122ms)
|
||||||
√ responds to ping requests (168ms)
|
✓ responds to ping requests
|
||||||
√ clients can log in and chat (158ms)
|
✓ clients can log in and chat (39ms)
|
||||||
√ kicks clients when invalid credentials (680ms)
|
✓ kicks clients when invalid credentials (8430ms)
|
||||||
√ gives correct reason for kicking clients when shutting down (123ms)
|
✓ gives correct reason for kicking clients when shutting down (42ms)
|
||||||
|
|
||||||
|
|
||||||
111 tests complete (3 minutes)
|
123 tests complete (4 minutes)
|
||||||
```
|
```
|
||||||
|
|
||||||
# Debugging
|
# Debugging
|
||||||
@ -359,6 +379,14 @@ NODE_DEBUG="minecraft-protocol" node [...]
|
|||||||
|
|
||||||
## History
|
## History
|
||||||
|
|
||||||
|
### 0.13.0
|
||||||
|
|
||||||
|
* Updated protocol version to support 1.8.1 (thanks [wtfaremyinitials](https://github.com/wtfaremyinitials))
|
||||||
|
* Lots of changes in how some formats are handled.
|
||||||
|
* Crypto now defaults to a pure-js library if URSA is missing, making the lib easier to use on windows.
|
||||||
|
* Fix a bug in yggdrasil handling of sessions, making reloading a session impossible (thanks [Frase](https://github.com/mrfrase3))
|
||||||
|
* Set noDelay on the TCP streams, making the bot a lot less laggy.
|
||||||
|
|
||||||
### 0.12.3
|
### 0.12.3
|
||||||
|
|
||||||
* Fix for/in used over array, causing glitches with augmented Array prototypes (thanks [pelikhan](https://github.com/pelikhan))
|
* Fix for/in used over array, causing glitches with augmented Array prototypes (thanks [pelikhan](https://github.com/pelikhan))
|
||||||
|
5
browser.js
Normal file
5
browser.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
|
||||||
|
module.exports = {
|
||||||
|
protocol: require('./lib/protocol')
|
||||||
|
};
|
||||||
|
|
@ -91,6 +91,17 @@ client.on('connect', function() {
|
|||||||
console.info(color('Successfully connected to ' + host + ':' + port, "blink+green"));
|
console.info(color('Successfully connected to ' + host + ':' + port, "blink+green"));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
client.on('end', function() {
|
||||||
|
console.log("Connection lost");
|
||||||
|
process.exit();
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on('error', function(err) {
|
||||||
|
console.log("Error occured");
|
||||||
|
console.log(err);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
|
||||||
client.on('state', function(newState) {
|
client.on('state', function(newState) {
|
||||||
if (newState === states.PLAY) {
|
if (newState === states.PLAY) {
|
||||||
chats.forEach(function(chat) {
|
chats.forEach(function(chat) {
|
||||||
|
122
examples/proxy.js
Normal file
122
examples/proxy.js
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
var mc = require('../');
|
||||||
|
|
||||||
|
var states = mc.protocol.states;
|
||||||
|
function print_help() {
|
||||||
|
console.log("usage: node proxy.js <target_srv> <user> [<password>]");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.argv.length < 4) {
|
||||||
|
console.log("Too few arguments!");
|
||||||
|
print_help();
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
process.argv.forEach(function(val, index, array) {
|
||||||
|
if (val == "-h") {
|
||||||
|
print_help();
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var host = process.argv[2];
|
||||||
|
var port = 25565;
|
||||||
|
var user = process.argv[3];
|
||||||
|
var passwd = process.argv[4];
|
||||||
|
|
||||||
|
if (host.indexOf(':') != -1) {
|
||||||
|
port = host.substring(host.indexOf(':')+1);
|
||||||
|
host = host.substring(0, host.indexOf(':'));
|
||||||
|
}
|
||||||
|
|
||||||
|
var srv = mc.createServer({
|
||||||
|
'online-mode': false,
|
||||||
|
port: 25566
|
||||||
|
});
|
||||||
|
srv.on('login', function (client) {
|
||||||
|
var addr = client.socket.remoteAddress;
|
||||||
|
console.log('Incoming connection', '('+addr+')');
|
||||||
|
var endedClient = false;
|
||||||
|
var endedTargetClient = false;
|
||||||
|
client.on('end', function() {
|
||||||
|
endedClient = true;
|
||||||
|
console.log('Connection closed by client', '('+addr+')');
|
||||||
|
if (!endedTargetClient)
|
||||||
|
targetClient.end("End");
|
||||||
|
});
|
||||||
|
client.on('error', function() {
|
||||||
|
endedClient = true;
|
||||||
|
console.log('Connection error by client', '('+addr+')');
|
||||||
|
if (!endedTargetClient)
|
||||||
|
targetClient.end("Error");
|
||||||
|
});
|
||||||
|
var targetClient = mc.createClient({
|
||||||
|
host: host,
|
||||||
|
port: port,
|
||||||
|
username: user,
|
||||||
|
password: passwd,
|
||||||
|
'online-mode': passwd != null ? true : false
|
||||||
|
});
|
||||||
|
var brokenPackets = [/*0x04, 0x2f, 0x30*/];
|
||||||
|
client.on('packet', function(packet) {
|
||||||
|
if (targetClient.state == states.PLAY && packet.state == states.PLAY) {
|
||||||
|
//console.log(`client->server: ${client.state}.${packet.id} : ${JSON.stringify(packet)}`);
|
||||||
|
if (!endedTargetClient)
|
||||||
|
targetClient.write(packet.id, packet);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
targetClient.on('packet', function(packet) {
|
||||||
|
if (packet.state == states.PLAY && client.state == states.PLAY &&
|
||||||
|
brokenPackets.indexOf(packet.id) === -1)
|
||||||
|
{
|
||||||
|
//console.log(`client<-server: ${targetClient.state}.${packet.id} : ${packet.id != 38 ? JSON.stringify(packet) : "Packet too big"}`);
|
||||||
|
if (!endedClient)
|
||||||
|
client.write(packet.id, packet);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var buffertools = require('buffertools');
|
||||||
|
targetClient.on('raw', function(buffer, state) {
|
||||||
|
if (client.state != states.PLAY || state != states.PLAY)
|
||||||
|
return;
|
||||||
|
var packetId = mc.protocol.types.varint[0](buffer, 0);
|
||||||
|
var packetData = mc.protocol.parsePacketData(buffer, state, false, {"packet": 1}).results;
|
||||||
|
var packetBuff = mc.protocol.createPacketBuffer(packetData.id, packetData.state, packetData, true);
|
||||||
|
if (buffertools.compare(buffer, packetBuff) != 0)
|
||||||
|
{
|
||||||
|
console.log("client<-server: Error in packetId " + state + ".0x" + packetId.value.toString(16));
|
||||||
|
console.log(buffer.toString('hex'));
|
||||||
|
console.log(packetBuff.toString('hex'));
|
||||||
|
}
|
||||||
|
/*if (client.state == states.PLAY && brokenPackets.indexOf(packetId.value) !== -1)
|
||||||
|
{
|
||||||
|
console.log(`client<-server: raw packet);
|
||||||
|
console.log(packetData);
|
||||||
|
if (!endedClient)
|
||||||
|
client.writeRaw(buffer);
|
||||||
|
}*/
|
||||||
|
});
|
||||||
|
client.on('raw', function(buffer, state) {
|
||||||
|
if (state != states.PLAY || targetClient.state != states.PLAY)
|
||||||
|
return;
|
||||||
|
var packetId = mc.protocol.types.varint[0](buffer, 0);
|
||||||
|
var packetData = mc.protocol.parsePacketData(buffer, state, true, {"packet": 1}).results;
|
||||||
|
var packetBuff = mc.protocol.createPacketBuffer(packetData.id, packetData.state, packetData, false);
|
||||||
|
if (buffertools.compare(buffer, packetBuff) != 0)
|
||||||
|
{
|
||||||
|
console.log("client->server: Error in packetId " + state + ".0x" + packetId.value.toString(16));
|
||||||
|
console.log(buffer.toString('hex'));
|
||||||
|
console.log(packetBuff.toString('hex'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
targetClient.on('end', function() {
|
||||||
|
endedTargetClient = true;
|
||||||
|
console.log('Connection closed by server', '('+addr+')');
|
||||||
|
if (!endedClient)
|
||||||
|
client.end("End");
|
||||||
|
});
|
||||||
|
targetClient.on('error', function() {
|
||||||
|
endedTargetClient = true;
|
||||||
|
console.log('Connection error by server', '('+addr+')');
|
||||||
|
if (!endedClient)
|
||||||
|
client.end("Error");
|
||||||
|
});
|
||||||
|
});
|
16
package.json
16
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "minecraft-protocol",
|
"name": "minecraft-protocol",
|
||||||
"version": "0.12.3",
|
"version": "0.13.0",
|
||||||
"description": "Parse and serialize minecraft packets, plus authentication and encryption.",
|
"description": "Parse and serialize minecraft packets, plus authentication and encryption.",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@ -25,6 +25,7 @@
|
|||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.8.16"
|
"node": ">=0.8.16"
|
||||||
},
|
},
|
||||||
|
"browser": "browser.js",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"batch": "~0.3.1",
|
"batch": "~0.3.1",
|
||||||
"gulp": "^3.8.11",
|
"gulp": "^3.8.11",
|
||||||
@ -33,14 +34,17 @@
|
|||||||
"mkdirp": "~0.3.4",
|
"mkdirp": "~0.3.4",
|
||||||
"mocha": "~1.8.2",
|
"mocha": "~1.8.2",
|
||||||
"rimraf": "~2.1.1",
|
"rimraf": "~2.1.1",
|
||||||
"zfill": "0.0.1"
|
"zfill": "0.0.1",
|
||||||
|
"batch": "~0.3.1",
|
||||||
|
"buffertools": "^2.1.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"node-rsa": "^0.1.53",
|
|
||||||
"superagent": "~0.10.0",
|
|
||||||
"buffer-equal": "0.0.0",
|
|
||||||
"ansi-color": "0.2.1",
|
"ansi-color": "0.2.1",
|
||||||
"node-uuid": "~1.4.1"
|
"buffer-equal": "0.0.0",
|
||||||
|
"node-uuid": "~1.4.1",
|
||||||
|
"prismarine-nbt": "0.0.1",
|
||||||
|
"superagent": "~0.10.0",
|
||||||
|
"ursa-purejs": "0.0.1"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"ursa": "~0.8.0"
|
"ursa": "~0.8.0"
|
||||||
|
650
rsa-wrap.js
650
rsa-wrap.js
@ -1,650 +0,0 @@
|
|||||||
// Copyright 2012 The Obvious Corporation.
|
|
||||||
|
|
||||||
/*
|
|
||||||
* "ursa": RSA crypto, with an emphasis on Buffer objects
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Modules used
|
|
||||||
*/
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
// Note: This also forces OpenSSL to be initialized, which is important!
|
|
||||||
var crypto = require("crypto");
|
|
||||||
|
|
||||||
var assert = require("assert");
|
|
||||||
|
|
||||||
//var ursaNative = require("../bin/ursaNative");
|
|
||||||
var ursaNative = require("node-rsa");
|
|
||||||
|
|
||||||
var RsaWrap = ursaNative;
|
|
||||||
//var textToNid = ursaNative.textToNid;
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Variable definitions
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** encoding constant */
|
|
||||||
var BASE64 = "base64";
|
|
||||||
|
|
||||||
/** encoding constant */
|
|
||||||
var BINARY = "binary";
|
|
||||||
|
|
||||||
/** encoding constant */
|
|
||||||
var HEX = "hex";
|
|
||||||
|
|
||||||
/** type name */
|
|
||||||
var STRING = "string";
|
|
||||||
|
|
||||||
/** encoding constant */
|
|
||||||
var UTF8 = "utf8";
|
|
||||||
|
|
||||||
/** hash algorithm constant */
|
|
||||||
var MD5 = "md5";
|
|
||||||
|
|
||||||
/** regex that matches PEM files, capturing the file type */
|
|
||||||
var PEM_REGEX =
|
|
||||||
/^(-----BEGIN (.*) KEY-----\r?\n[\/+=a-zA-Z0-9\r\n]*\r?\n-----END \2 KEY-----\r?\n)/m;
|
|
||||||
|
|
||||||
/** "unsealer" key object to authenticate objects */
|
|
||||||
var theUnsealer = [ "ursa unsealer" ];
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Helper functions
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true iff x is either a string or a Buffer.
|
|
||||||
*/
|
|
||||||
function isStringOrBuffer(x) {
|
|
||||||
return (typeof x === STRING) || Buffer.isBuffer(x);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extract and identify the PEM file type represented in the given
|
|
||||||
* buffer. Returns the extracted type string or undefined if the
|
|
||||||
* buffer doesn't seem to be any sort of PEM format file.
|
|
||||||
*/
|
|
||||||
function identifyPemType(buf) {
|
|
||||||
var str = encodeBuffer(buf, UTF8);
|
|
||||||
var match = PEM_REGEX.exec(str);
|
|
||||||
|
|
||||||
if (!match) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
return match[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return whether the given buffer or string appears (trivially) to be a
|
|
||||||
* valid public key file in PEM format.
|
|
||||||
*/
|
|
||||||
function isPublicKeyPem(buf) {
|
|
||||||
var kind = identifyPemType(buf);
|
|
||||||
return (kind == "PUBLIC");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return whether the given buffer or string appears (trivially) to be a
|
|
||||||
* valid private key file in PEM format.
|
|
||||||
*/
|
|
||||||
function isPrivateKeyPem(buf) {
|
|
||||||
var kind = identifyPemType(buf);
|
|
||||||
return (kind == "RSA PRIVATE");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a buffer containing the encoding of the given bigint for use
|
|
||||||
* as part of an SSH-style public key file. The input value must be a
|
|
||||||
* buffer representing an unsigned bigint in big-endian order.
|
|
||||||
*/
|
|
||||||
function toSshBigint(value) {
|
|
||||||
// The output is signed, so we need to add an extra 00 byte at the
|
|
||||||
// head if the high-order bit is set.
|
|
||||||
var prefix00 = ((value[0] & 0x80) !== 0);
|
|
||||||
var length = value.length + (prefix00 ? 1 : 0);
|
|
||||||
var result = new Buffer(length + 4);
|
|
||||||
var offset = 0;
|
|
||||||
|
|
||||||
result.writeUInt32BE(length, offset);
|
|
||||||
offset += 4;
|
|
||||||
|
|
||||||
if (prefix00) {
|
|
||||||
result[offset] = 0;
|
|
||||||
offset++;
|
|
||||||
}
|
|
||||||
|
|
||||||
value.copy(result, offset);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create and return a buffer containing an SSH-style public key file for
|
|
||||||
* the given RsaWrap object.
|
|
||||||
*
|
|
||||||
* For the record, an SSH-style public key file consists of three
|
|
||||||
* concatenated values, each one length-prefixed:
|
|
||||||
*
|
|
||||||
* literal string "ssh-rsa"
|
|
||||||
* exponent
|
|
||||||
* modulus
|
|
||||||
*
|
|
||||||
* The literal string header is length-prefixed. The two numbers are
|
|
||||||
* represented as signed big-int values in big-endian order, also
|
|
||||||
* length-prefixed.
|
|
||||||
*/
|
|
||||||
function createSshPublicKey(rsa) {
|
|
||||||
var e = toSshBigint(rsa.getExponent());
|
|
||||||
var m = toSshBigint(rsa.getModulus());
|
|
||||||
|
|
||||||
var header = toSshBigint(new Buffer("ssh-rsa", UTF8));
|
|
||||||
var result = new Buffer(header.length + m.length + e.length);
|
|
||||||
var offset = 0;
|
|
||||||
|
|
||||||
header.copy(result, offset);
|
|
||||||
offset += header.length;
|
|
||||||
e.copy(result, offset);
|
|
||||||
offset += e.length;
|
|
||||||
m.copy(result, offset);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validate the given encoding name. Throws an exception if invalid.
|
|
||||||
*/
|
|
||||||
function validateEncoding(encoding) {
|
|
||||||
switch (encoding) {
|
|
||||||
case BASE64:
|
|
||||||
case BINARY:
|
|
||||||
case HEX:
|
|
||||||
case UTF8: {
|
|
||||||
// These are all valid.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
throw new Error("Invalid encoding: " + encoding);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert a buffer into an appropriately-encoded string, or return it
|
|
||||||
* unmodified if the encoding is undefined.
|
|
||||||
*/
|
|
||||||
function encodeBuffer(buf, encoding) {
|
|
||||||
if (encoding === undefined) {
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
validateEncoding(encoding);
|
|
||||||
return buf.toString(encoding);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a buffer or undefined argument as-is, or convert a given
|
|
||||||
* string into a buffer by using the indicated encoding. An undefined
|
|
||||||
* encoding is interpreted to mean UTF8.
|
|
||||||
*/
|
|
||||||
function decodeString(str, encoding) {
|
|
||||||
if ((str === undefined) || Buffer.isBuffer(str)) {
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (encoding === undefined) {
|
|
||||||
encoding = UTF8;
|
|
||||||
}
|
|
||||||
|
|
||||||
validateEncoding(encoding);
|
|
||||||
return new Buffer(str, encoding);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Public Key object. This is the externally-visible object that one gets
|
|
||||||
* when constructing an instance from a public key. The constructor takes
|
|
||||||
* a native RsaWrap object.
|
|
||||||
*/
|
|
||||||
function PublicKey(rsa) {
|
|
||||||
var self;
|
|
||||||
|
|
||||||
function getExponent(encoding) {
|
|
||||||
var buf = new Buffer(4);
|
|
||||||
buf.writeUInt32BE(rsa.keyPair.e);
|
|
||||||
return encodeBuffer(buf, encoding);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getModulus(encoding) {
|
|
||||||
var buf = new Buffer(4);
|
|
||||||
// TODO : How do I get modulus ?
|
|
||||||
return encodeBuffer(rsa.getModulus(), encoding);
|
|
||||||
}
|
|
||||||
|
|
||||||
function toPublicPem(encoding) {
|
|
||||||
return encodeBuffer(rsa.getPublicPEM() + "\n", encoding);
|
|
||||||
}
|
|
||||||
|
|
||||||
function toPublicSsh(encoding) {
|
|
||||||
return encodeBuffer(createSshPublicKey(rsa), encoding);
|
|
||||||
}
|
|
||||||
|
|
||||||
function toPublicSshFingerprint(encoding) {
|
|
||||||
return sshFingerprint(createSshPublicKey(rsa), undefined, encoding);
|
|
||||||
}
|
|
||||||
|
|
||||||
function encrypt(buf, bufEncoding, outEncoding, padding) {
|
|
||||||
buf = decodeString(buf, bufEncoding);
|
|
||||||
padding = padding || ursaNative.RSA_PKCS1_OAEP_PADDING;
|
|
||||||
return rsa.encrypt(buf);
|
|
||||||
//return encodeBuffer(rsa.publicEncrypt(buf, padding), outEncoding);
|
|
||||||
}
|
|
||||||
|
|
||||||
function publicDecrypt(buf, bufEncoding, outEncoding) {
|
|
||||||
throw new Exception("Unsupported operation : publicDecrypt");
|
|
||||||
//buf = decodeString(buf, bufEncoding);
|
|
||||||
//return encodeBuffer(rsa.publicDecrypt(buf), outEncoding);
|
|
||||||
}
|
|
||||||
|
|
||||||
function verify(algorithm, hash, sig, encoding) {
|
|
||||||
//algorithm = textToNid(algorithm);
|
|
||||||
hash = decodeString(hash, encoding);
|
|
||||||
sig = decodeString(sig, encoding);
|
|
||||||
rsa.options.signingAlgorithm = algorithm;
|
|
||||||
return rsa.verify(hash, sig);
|
|
||||||
}
|
|
||||||
|
|
||||||
function hashAndVerify(algorithm, buf, sig, encoding) {
|
|
||||||
var verifier = createVerifier(algorithm);
|
|
||||||
verifier.update(buf, encoding);
|
|
||||||
return verifier.verify(self, sig, encoding);
|
|
||||||
}
|
|
||||||
|
|
||||||
function unseal(unsealer) {
|
|
||||||
return (unsealer === theUnsealer) ? self : undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
self = {
|
|
||||||
encrypt: encrypt,
|
|
||||||
getExponent: getExponent,
|
|
||||||
getModulus: getModulus,
|
|
||||||
hashAndVerify: hashAndVerify,
|
|
||||||
publicDecrypt: publicDecrypt,
|
|
||||||
toPublicPem: toPublicPem,
|
|
||||||
toPublicSsh: toPublicSsh,
|
|
||||||
toPublicSshFingerprint: toPublicSshFingerprint,
|
|
||||||
verify: verify,
|
|
||||||
unseal: unseal
|
|
||||||
};
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Private Key object. This is the externally-visible object that one
|
|
||||||
* gets when constructing an instance from a private key (aka a
|
|
||||||
* keypair). The constructor takes a native RsaWrap object.
|
|
||||||
*/
|
|
||||||
function PrivateKey(rsa) {
|
|
||||||
var self;
|
|
||||||
|
|
||||||
function toPrivatePem(encoding) {
|
|
||||||
return encodeBuffer(rsa.getPrivatePEM(), encoding);
|
|
||||||
}
|
|
||||||
|
|
||||||
function decrypt(buf, bufEncoding, outEncoding, padding) {
|
|
||||||
buf = decodeString(buf, bufEncoding);
|
|
||||||
padding = padding || ursaNative.RSA_PKCS1_OAEP_PADDING;
|
|
||||||
return encodeBuffer(rsa.decrypt(buf), outEncoding);
|
|
||||||
//return encodeBuffer(rsa.privateDecrypt(buf, padding), outEncoding);
|
|
||||||
}
|
|
||||||
|
|
||||||
function privateEncrypt(buf, bufEncoding, outEncoding) {
|
|
||||||
throw new Exception("Unsupported operation : Private encrypt");
|
|
||||||
buf = decodeString(buf, bufEncoding);
|
|
||||||
return encodeBuffer(rsa.privateEncrypt(buf), outEncoding);
|
|
||||||
}
|
|
||||||
|
|
||||||
function sign(algorithm, hash, hashEncoding, outEncoding) {
|
|
||||||
//algorithm = textToNid(algorithm);
|
|
||||||
hash = decodeString(hash, hashEncoding);
|
|
||||||
rsa.options.signingAlgorithm = algorithm;
|
|
||||||
return encodeBuffer(rsa.sign(hash), outEncoding);
|
|
||||||
}
|
|
||||||
|
|
||||||
function hashAndSign(algorithm, buf, bufEncoding, outEncoding) {
|
|
||||||
var signer = createSigner(algorithm);
|
|
||||||
signer.update(buf, bufEncoding);
|
|
||||||
return signer.sign(self, outEncoding);
|
|
||||||
}
|
|
||||||
|
|
||||||
self = PublicKey(rsa);
|
|
||||||
self.decrypt = decrypt;
|
|
||||||
self.hashAndSign = hashAndSign;
|
|
||||||
self.privateEncrypt = privateEncrypt;
|
|
||||||
self.sign = sign;
|
|
||||||
self.toPrivatePem = toPrivatePem;
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Exported bindings
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new public key object, from the given PEM-encoded file.
|
|
||||||
*/
|
|
||||||
function createPublicKey(pem, encoding) {
|
|
||||||
var rsa = new RsaWrap();
|
|
||||||
pem = decodeString(pem, encoding);
|
|
||||||
|
|
||||||
try {
|
|
||||||
rsa.loadFromPEM(pem.toString('utf8'));
|
|
||||||
} catch (ex) {
|
|
||||||
if (!isPublicKeyPem(pem)) {
|
|
||||||
throw new Error("Not a public key.");
|
|
||||||
}
|
|
||||||
throw ex;
|
|
||||||
}
|
|
||||||
|
|
||||||
return PublicKey(rsa);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new private key object, from the given PEM-encoded file,
|
|
||||||
* optionally decrypting the file with a password.
|
|
||||||
*/
|
|
||||||
function createPrivateKey(pem, password, encoding) {
|
|
||||||
var rsa = new RsaWrap();
|
|
||||||
//pem = decodeString(pem, encoding);
|
|
||||||
//password = decodeString(password, encoding);
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Note: The native code is sensitive to the actual number of
|
|
||||||
// arguments. It's *not* okay to pass undefined as a password.
|
|
||||||
if (password) {
|
|
||||||
throw new Exception("Unsupported method : createPrivateKey with password");
|
|
||||||
//rsa.setPrivateKeyPem(pem, password);
|
|
||||||
} else {
|
|
||||||
rsa.loadFromPEM(pem);
|
|
||||||
}
|
|
||||||
} catch (ex) {
|
|
||||||
if (!isPrivateKeyPem(pem)) {
|
|
||||||
throw new Error("Not a private key.");
|
|
||||||
}
|
|
||||||
throw ex;
|
|
||||||
}
|
|
||||||
|
|
||||||
return PrivateKey(rsa);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate a new private key object (aka a keypair).
|
|
||||||
*/
|
|
||||||
function generatePrivateKey(modulusBits, exponent) {
|
|
||||||
if (modulusBits === undefined) {
|
|
||||||
modulusBits = 2048;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (exponent === undefined) {
|
|
||||||
exponent = 65537;
|
|
||||||
}
|
|
||||||
|
|
||||||
var rsa = new RsaWrap();
|
|
||||||
rsa.generateKeyPair(modulusBits, exponent);
|
|
||||||
|
|
||||||
return PrivateKey(rsa);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a key object from a PEM format file, either a private or
|
|
||||||
* public key depending on what kind of file is passed in. If given
|
|
||||||
* a private key file, it must not be encrypted.
|
|
||||||
*/
|
|
||||||
function createKey(pem, encoding) {
|
|
||||||
pem = decodeString(pem, encoding);
|
|
||||||
|
|
||||||
if (isPublicKeyPem(pem)) {
|
|
||||||
return createPublicKey(pem);
|
|
||||||
} else if (isPrivateKeyPem(pem)) {
|
|
||||||
return createPrivateKey(pem);
|
|
||||||
} else {
|
|
||||||
throw new Error("Not a key.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the SSH-style public key fingerprint of the given SSH-format
|
|
||||||
* public key.
|
|
||||||
*/
|
|
||||||
function sshFingerprint(sshKey, sshEncoding, outEncoding) {
|
|
||||||
var hash = crypto.createHash(MD5);
|
|
||||||
|
|
||||||
hash.update(decodeString(sshKey, sshEncoding));
|
|
||||||
var result = new Buffer(hash.digest(BINARY), BINARY);
|
|
||||||
return encodeBuffer(result, outEncoding);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return whether the given object is a key object (either public or
|
|
||||||
* private), as constructed by this module.
|
|
||||||
*/
|
|
||||||
function isKey(obj) {
|
|
||||||
var obj2;
|
|
||||||
|
|
||||||
try {
|
|
||||||
var unseal = obj.unseal;
|
|
||||||
if (typeof unseal !== "function") {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
obj2 = unseal(theUnsealer);
|
|
||||||
} catch (ex) {
|
|
||||||
// Ignore; can't assume that other objects obey any particular
|
|
||||||
// unsealing protocol.
|
|
||||||
// TODO: Log?
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj2 !== undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return whether the given object is a private key object, as
|
|
||||||
* constructed by this module.
|
|
||||||
*/
|
|
||||||
function isPrivateKey(obj) {
|
|
||||||
return isKey(obj) && (obj.decrypt !== undefined);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return whether the given object is a public key object (per se), as
|
|
||||||
* constructed by this module.
|
|
||||||
*/
|
|
||||||
function isPublicKey(obj) {
|
|
||||||
return isKey(obj) && !isPrivateKey(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Assert wrapper for isKey().
|
|
||||||
*/
|
|
||||||
function assertKey(obj) {
|
|
||||||
assert(isKey(obj));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Assert wrapper for isPrivateKey().
|
|
||||||
*/
|
|
||||||
function assertPrivateKey(obj) {
|
|
||||||
assert(isPrivateKey(obj));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Assert wrapper for isPublicKey().
|
|
||||||
*/
|
|
||||||
function assertPublicKey(obj) {
|
|
||||||
assert(isPublicKey(obj));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Coerce the given key value into an private key object, returning
|
|
||||||
* it. If given a private key object, this just returns it as-is. If
|
|
||||||
* given a string or Buffer, it tries to parse it as PEM. Anything
|
|
||||||
* else is an error.
|
|
||||||
*/
|
|
||||||
function coercePrivateKey(orig) {
|
|
||||||
if (isPrivateKey(orig)) {
|
|
||||||
return orig;
|
|
||||||
} else if (isStringOrBuffer(orig)) {
|
|
||||||
return createPrivateKey(orig);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error("Not a private key: " + orig);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Coerce the given key value into a public key object, returning
|
|
||||||
* it. If given a private key object, this just returns it as-is. If
|
|
||||||
* given a string or Buffer, it tries to parse it as PEM. Anything
|
|
||||||
* else is an error.
|
|
||||||
*/
|
|
||||||
function coercePublicKey(orig) {
|
|
||||||
if (isPublicKey(orig)) {
|
|
||||||
return orig;
|
|
||||||
} else if (isStringOrBuffer(orig)) {
|
|
||||||
return createPublicKey(orig);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error("Not a public key: " + orig);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Coerce the given key value into a key object (either public or
|
|
||||||
* private), returning it. If given a private key object, this just
|
|
||||||
* returns it as-is. If given a string or Buffer, it tries to parse it
|
|
||||||
* as PEM. Anything else is an error.
|
|
||||||
*/
|
|
||||||
function coerceKey(orig) {
|
|
||||||
if (isKey(orig)) {
|
|
||||||
return orig;
|
|
||||||
} else if (isStringOrBuffer(orig)) {
|
|
||||||
return createKey(orig);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error("Not a key: " + orig);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check whether the two objects are both keys of some sort and
|
|
||||||
* have the same public part.
|
|
||||||
*/
|
|
||||||
function matchingPublicKeys(key1, key2) {
|
|
||||||
if (!(isKey(key1) && isKey(key2))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This isn't the most efficient implementation, but it will suffice:
|
|
||||||
// We convert both to ssh form, which has very little leeway for
|
|
||||||
// variation, and compare bytes.
|
|
||||||
|
|
||||||
var ssh1 = key1.toPublicSsh(UTF8);
|
|
||||||
var ssh2 = key2.toPublicSsh(UTF8);
|
|
||||||
|
|
||||||
return ssh1 === ssh2;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check whether the two objects are both keys of some sort, are
|
|
||||||
* both public or both private, and have the same contents.
|
|
||||||
*/
|
|
||||||
function equalKeys(key1, key2) {
|
|
||||||
// See above for rationale. In this case, there's no ssh form for
|
|
||||||
// private keys, so we just use PEM for that.
|
|
||||||
|
|
||||||
if (isPrivateKey(key1) && isPrivateKey(key2)) {
|
|
||||||
var pem1 = key1.toPrivatePem(UTF8);
|
|
||||||
var pem2 = key2.toPrivatePem(UTF8);
|
|
||||||
return pem1 === pem2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isPublicKey(key1) && isPublicKey(key2)) {
|
|
||||||
return matchingPublicKeys(key1, key2);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a signer object.
|
|
||||||
*/
|
|
||||||
function createSigner(algorithm) {
|
|
||||||
var hash = crypto.createHash(algorithm);
|
|
||||||
|
|
||||||
function update(buf, bufEncoding) {
|
|
||||||
buf = decodeString(buf, bufEncoding);
|
|
||||||
hash.update(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
function sign(privateKey, outEncoding) {
|
|
||||||
var hashBuf = new Buffer(hash.digest(BINARY), BINARY);
|
|
||||||
return privateKey.sign(algorithm, hashBuf, undefined, outEncoding);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
sign: sign,
|
|
||||||
update: update
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a verifier object.
|
|
||||||
*/
|
|
||||||
function createVerifier(algorithm) {
|
|
||||||
var hash = crypto.createHash(algorithm);
|
|
||||||
|
|
||||||
function update(buf, bufEncoding) {
|
|
||||||
buf = decodeString(buf, bufEncoding);
|
|
||||||
hash.update(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
function verify(publicKey, sig, sigEncoding) {
|
|
||||||
var hashBuf = new Buffer(hash.digest(BINARY), BINARY);
|
|
||||||
sig = decodeString(sig, sigEncoding);
|
|
||||||
return publicKey.verify(algorithm, hashBuf, sig);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
update: update,
|
|
||||||
verify: verify
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Initialization
|
|
||||||
*/
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
assertKey: assertKey,
|
|
||||||
assertPrivateKey: assertPrivateKey,
|
|
||||||
assertPublicKey: assertPublicKey,
|
|
||||||
coerceKey: coerceKey,
|
|
||||||
coercePrivateKey: coercePrivateKey,
|
|
||||||
coercePublicKey: coercePublicKey,
|
|
||||||
createKey: createKey,
|
|
||||||
createPrivateKey: createPrivateKey,
|
|
||||||
createPublicKey: createPublicKey,
|
|
||||||
createSigner: createSigner,
|
|
||||||
createVerifier: createVerifier,
|
|
||||||
equalKeys: equalKeys,
|
|
||||||
generatePrivateKey: generatePrivateKey,
|
|
||||||
isKey: isKey,
|
|
||||||
isPrivateKey: isPrivateKey,
|
|
||||||
isPublicKey: isPublicKey,
|
|
||||||
matchingPublicKeys: matchingPublicKeys,
|
|
||||||
sshFingerprint: sshFingerprint,
|
|
||||||
RSA_PKCS1_PADDING: ursaNative.RSA_PKCS1_PADDING,
|
|
||||||
RSA_PKCS1_OAEP_PADDING: ursaNative.RSA_PKCS1_OAEP_PADDING,
|
|
||||||
};
|
|
@ -93,10 +93,11 @@ Client.prototype.setSocket = function(socket) {
|
|||||||
//incomingBuffer = incomingBuffer.slice(parsed.size); TODO: Already removed in prepare
|
//incomingBuffer = incomingBuffer.slice(parsed.size); TODO: Already removed in prepare
|
||||||
|
|
||||||
var packetName = protocol.packetNames[self.state][self.isServer ? 'toServer' : 'toClient'][packet.id];
|
var packetName = protocol.packetNames[self.state][self.isServer ? 'toServer' : 'toClient'][packet.id];
|
||||||
|
var packetState = self.state;
|
||||||
self.emit(packetName, packet);
|
self.emit(packetName, packet);
|
||||||
self.emit('packet', packet);
|
self.emit('packet', packet);
|
||||||
self.emit('raw.' + packetName, parsed.buffer);
|
self.emit('raw.' + packetName, parsed.buffer, packetState);
|
||||||
self.emit('raw', parsed.buffer);
|
self.emit('raw', parsed.buffer, packetState);
|
||||||
prepareParse();
|
prepareParse();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,7 +154,7 @@ Client.prototype.setSocket = function(socket) {
|
|||||||
|
|
||||||
Client.prototype.connect = function(port, host) {
|
Client.prototype.connect = function(port, host) {
|
||||||
var self = this;
|
var self = this;
|
||||||
if (port == 25565) {
|
if (port == 25565 && net.isIP(host) === 0) {
|
||||||
dns.resolveSrv("_minecraft._tcp." + host, function(err, addresses) {
|
dns.resolveSrv("_minecraft._tcp." + host, function(err, addresses) {
|
||||||
if (addresses && addresses.length > 0) {
|
if (addresses && addresses.length > 0) {
|
||||||
self.setSocket(net.connect(addresses[0].port, addresses[0].name));
|
self.setSocket(net.connect(addresses[0].port, addresses[0].name));
|
||||||
@ -211,21 +212,20 @@ Client.prototype.write = function(packetId, params) {
|
|||||||
// TODO : Perhaps this should only accept buffers without length, so we can
|
// TODO : Perhaps this should only accept buffers without length, so we can
|
||||||
// handle compression ourself ? Needs to ask peopl who actually use this feature
|
// handle compression ourself ? Needs to ask peopl who actually use this feature
|
||||||
// like @deathcap
|
// like @deathcap
|
||||||
Client.prototype.writeRaw = function(buffer, shouldEncrypt) {
|
Client.prototype.writeRaw = function(buffer) {
|
||||||
if (shouldEncrypt === null) {
|
var self = this;
|
||||||
shouldEncrypt = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
var that = this;
|
var finishWriting = function(error, buffer) {
|
||||||
|
if (error)
|
||||||
var finishWriting = function(buffer) {
|
throw error; // TODO : How do we handle this error ?
|
||||||
var out = (shouldEncrypt && this.encryptionEnabled) ? new Buffer(this.cipher.update(buffer), 'binary') : buffer;
|
var out = self.encryptionEnabled ? new Buffer(self.cipher.update(buffer), 'binary') : buffer;
|
||||||
that.socket.write(out);
|
self.socket.write(out);
|
||||||
};
|
};
|
||||||
|
if (this.compressionThreshold >= 0 && buffer.length >= this.compressionThreshold) {
|
||||||
if(this.compressionThreshold != -1 && buffer.length > this.compressionThreshold) {
|
|
||||||
compressPacketBuffer(buffer, finishWriting);
|
compressPacketBuffer(buffer, finishWriting);
|
||||||
|
} else if (this.compressionThreshold >= -1) {
|
||||||
|
newStylePacket(buffer, finishWriting);
|
||||||
} else {
|
} else {
|
||||||
finishWriting(buffer);
|
oldStylePacket(buffer, finishWriting);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
12
src/index.js
12
src/index.js
@ -4,10 +4,10 @@ var EventEmitter = require('events').EventEmitter
|
|||||||
, crypto = require('crypto')
|
, crypto = require('crypto')
|
||||||
, bufferEqual = require('buffer-equal')
|
, bufferEqual = require('buffer-equal')
|
||||||
, superagent = require('superagent')
|
, superagent = require('superagent')
|
||||||
, protocol = require('./protocol')
|
, protocol = require('./lib/protocol')
|
||||||
, Client = require('./client')
|
, Client = require('./lib/client')
|
||||||
, Server = require('./server')
|
, Server = require('./lib/server')
|
||||||
, Yggdrasil = require('./yggdrasil.js')
|
, Yggdrasil = require('./lib/yggdrasil.js')
|
||||||
, getSession = Yggdrasil.getSession
|
, getSession = Yggdrasil.getSession
|
||||||
, validateSession = Yggdrasil.validateSession
|
, validateSession = Yggdrasil.validateSession
|
||||||
, joinServer = Yggdrasil.joinServer
|
, joinServer = Yggdrasil.joinServer
|
||||||
@ -20,7 +20,7 @@ try {
|
|||||||
} catch(e) {
|
} catch(e) {
|
||||||
console.log("You are using a pure-javascript implementation of RSA.");
|
console.log("You are using a pure-javascript implementation of RSA.");
|
||||||
console.log("Your performance might be subpar. Please consider installing URSA");
|
console.log("Your performance might be subpar. Please consider installing URSA");
|
||||||
ursa = require("../rsa-wrap");
|
ursa = require("ursa-purejs");
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
@ -28,7 +28,7 @@ module.exports = {
|
|||||||
createServer: createServer,
|
createServer: createServer,
|
||||||
Client: Client,
|
Client: Client,
|
||||||
Server: Server,
|
Server: Server,
|
||||||
ping: require('./ping'),
|
ping: require('./lib/ping'),
|
||||||
protocol: protocol,
|
protocol: protocol,
|
||||||
yggdrasil: Yggdrasil,
|
yggdrasil: Yggdrasil,
|
||||||
};
|
};
|
||||||
|
221
src/protocol.js
221
src/protocol.js
@ -1,6 +1,7 @@
|
|||||||
var assert = require('assert');
|
var assert = require('assert');
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
var zlib = require('zlib');
|
var zlib = require('zlib');
|
||||||
|
var nbt = require('prismarine-nbt');
|
||||||
|
|
||||||
var STRING_MAX_LENGTH = 240;
|
var STRING_MAX_LENGTH = 240;
|
||||||
var SRV_STRING_MAX_LENGTH = 32767;
|
var SRV_STRING_MAX_LENGTH = 32767;
|
||||||
@ -201,17 +202,17 @@ var packets = {
|
|||||||
{ name: "count", type: "short" }
|
{ name: "count", type: "short" }
|
||||||
]},
|
]},
|
||||||
entity_velocity: {id: 0x12, fields: [
|
entity_velocity: {id: 0x12, fields: [
|
||||||
{ name: "entityId", type: "int" },
|
{ name: "entityId", type: "varint" },
|
||||||
{ name: "velocityX", type: "short" },
|
{ name: "velocityX", type: "short" },
|
||||||
{ name: "velocityY", type: "short" },
|
{ name: "velocityY", type: "short" },
|
||||||
{ name: "velocityZ", type: "short" }
|
{ name: "velocityZ", type: "short" }
|
||||||
]},
|
]},
|
||||||
entity_destroy: {id: 0x13, fields: [
|
entity_destroy: {id: 0x13, fields: [
|
||||||
{ name: "count", type: "count", typeArgs: { type: "byte", countFor: "entityIds" } }, /* TODO: Might not be correct */
|
{ name: "count", type: "count", typeArgs: { type: "varint", countFor: "entityIds" } },
|
||||||
{ name: "entityIds", type: "array", typeArgs: { type: "int", count: "count" } }
|
{ name: "entityIds", type: "array", typeArgs: { type: "varint", count: "count" } }
|
||||||
]},
|
]},
|
||||||
entity: {id: 0x14, fields: [
|
entity: {id: 0x14, fields: [
|
||||||
{ name: "entityId", type: "int" }
|
{ name: "entityId", type: "varint" }
|
||||||
]},
|
]},
|
||||||
rel_entity_move: {id: 0x15, fields: [
|
rel_entity_move: {id: 0x15, fields: [
|
||||||
{ name: "entityId", type: "varint" },
|
{ name: "entityId", type: "varint" },
|
||||||
@ -284,7 +285,7 @@ var packets = {
|
|||||||
type: "container", typeArgs: { fields: [
|
type: "container", typeArgs: { fields: [
|
||||||
{ name: "key", type: "string" },
|
{ name: "key", type: "string" },
|
||||||
{ name: "value", type: "double" },
|
{ name: "value", type: "double" },
|
||||||
{ name: "listLength", type: "count", typeArgs: { type: "short", countFor: "this.modifiers" } },
|
{ name: "listLength", type: "count", typeArgs: { type: "varint", countFor: "this.modifiers" } },
|
||||||
{ name: "modifiers", type: "array", typeArgs: { count: "this.listLength",
|
{ name: "modifiers", type: "array", typeArgs: { count: "this.listLength",
|
||||||
type: "container", typeArgs: { fields: [
|
type: "container", typeArgs: { fields: [
|
||||||
{ name: "UUID", type: "UUID" },
|
{ name: "UUID", type: "UUID" },
|
||||||
@ -305,10 +306,12 @@ var packets = {
|
|||||||
multi_block_change: {id: 0x22, fields: [
|
multi_block_change: {id: 0x22, fields: [
|
||||||
{ name: "chunkX", type: "int" },
|
{ name: "chunkX", type: "int" },
|
||||||
{ name: "chunkZ", type: "int" },
|
{ name: "chunkZ", type: "int" },
|
||||||
{ name: "recordCount", type: "varint" },
|
{ name: "recordCount", type: "count", typeArgs: { type: "varint", countFor: "records" } },
|
||||||
/* TODO: Is dataLength needed? */
|
{ name: "records", type: "array", typeArgs: { count: "recordCount", type: "container", typeArgs: { fields: [
|
||||||
{ name: "dataLength", type: "count", typeArgs: { type: "int", countFor: "data" } },
|
{ name: "horizontalPos", type: "ubyte" },
|
||||||
{ name: "data", type: "buffer", typeArgs: { count: "dataLength" } },
|
{ name: "y", type: "ubyte" },
|
||||||
|
{ name: "blockId", type: "varint" }
|
||||||
|
]}}}
|
||||||
]},
|
]},
|
||||||
block_change: {id: 0x23, fields: [
|
block_change: {id: 0x23, fields: [
|
||||||
{ name: "location", type: "position" },
|
{ name: "location", type: "position" },
|
||||||
@ -376,8 +379,8 @@ var packets = {
|
|||||||
{ name: "offsetY", type: "float" },
|
{ name: "offsetY", type: "float" },
|
||||||
{ name: "offsetZ", type: "float" },
|
{ name: "offsetZ", type: "float" },
|
||||||
{ name: "particleData", type: "float" },
|
{ name: "particleData", type: "float" },
|
||||||
{ name: "particles", type: "int" }
|
{ name: "particles", type: "count", typeArgs: { countFor: "data", type: "int" } },
|
||||||
/* TODO: Create an Array of VarInts */
|
{ name: "data", type: "array", typeArgs: { count: "particles", type: "varint" } }
|
||||||
]},
|
]},
|
||||||
game_state_change: {id: 0x2b, fields: [
|
game_state_change: {id: 0x2b, fields: [
|
||||||
{ name: "reason", type: "ubyte" },
|
{ name: "reason", type: "ubyte" },
|
||||||
@ -458,8 +461,7 @@ var packets = {
|
|||||||
tile_entity_data:{id: 0x35, fields: [
|
tile_entity_data:{id: 0x35, fields: [
|
||||||
{ name: "location", type: "position" },
|
{ name: "location", type: "position" },
|
||||||
{ name: "action", type: "ubyte" },
|
{ name: "action", type: "ubyte" },
|
||||||
{ name: "nbtDataLength", type: "count", typeArgs: { type: "short", countFor: "nbtData" } },
|
{ name: "nbtData", type: "restBuffer" }
|
||||||
{ name: "nbtData", type: "buffer", typeArgs: { count: "nbtDataLength" } },
|
|
||||||
]},
|
]},
|
||||||
open_sign_entity: {id: 0x36, fields: [
|
open_sign_entity: {id: 0x36, fields: [
|
||||||
{ name: "location", type: "position" },
|
{ name: "location", type: "position" },
|
||||||
@ -483,15 +485,15 @@ var packets = {
|
|||||||
}},
|
}},
|
||||||
{ name: "propertiesLength", type: "count", condition: function(field_values) {
|
{ name: "propertiesLength", type: "count", condition: function(field_values) {
|
||||||
return field_values["action"] === 0;
|
return field_values["action"] === 0;
|
||||||
}, typeArgs: { countFor: "properties", type: "varint" }},
|
}, typeArgs: { countFor: "this.properties", type: "varint" }},
|
||||||
{ name: "properties", type: "array", condition: function(field_values) {
|
{ name: "properties", type: "array", condition: function(field_values) {
|
||||||
return field_values["action"] === 0;
|
return field_values["action"] === 0;
|
||||||
}, typeArgs: { count: "propertiesLength", type: "container", typeArgs: { fields: [
|
}, typeArgs: { count: "this.propertiesLength", type: "container", typeArgs: { fields: [
|
||||||
{ name: "name", type: "string" },
|
{ name: "name", type: "string" },
|
||||||
{ name: "value", type: "string" },
|
{ name: "value", type: "ustring" },
|
||||||
{ name: "isSigned", type: "bool" },
|
{ name: "isSigned", type: "bool" },
|
||||||
{ name: "signature", type: "string", condition: function(field_values) {
|
{ name: "signature", type: "ustring", condition: function(field_values) {
|
||||||
return field_values["isSigned"];
|
return field_values["this"]["isSigned"];
|
||||||
}}
|
}}
|
||||||
]}}},
|
]}}},
|
||||||
{ name: "gamemode", type: "varint", condition: function(field_values) {
|
{ name: "gamemode", type: "varint", condition: function(field_values) {
|
||||||
@ -520,14 +522,18 @@ var packets = {
|
|||||||
scoreboard_objective: {id: 0x3b, fields: [
|
scoreboard_objective: {id: 0x3b, fields: [
|
||||||
{ name: "name", type: "string" },
|
{ name: "name", type: "string" },
|
||||||
{ name: "action", type: "byte" },
|
{ name: "action", type: "byte" },
|
||||||
{ name: "displayText", type: "string" },
|
{ name: "displayText", type: "string", condition: function(field_values) {
|
||||||
{ name: "type", type: "string"}
|
return field_values["action"] == 0 || field_values["action"] == 2;
|
||||||
|
}},
|
||||||
|
{ name: "type", type: "string", condition: function(field_values) {
|
||||||
|
return field_values["action"] == 0 || field_values["action"] == 2;
|
||||||
|
}}
|
||||||
]},
|
]},
|
||||||
scoreboard_score: {id: 0x3c, fields: [ /* TODO: itemName and scoreName may need to be switched */
|
scoreboard_score: {id: 0x3c, fields: [ /* TODO: itemName and scoreName may need to be switched */
|
||||||
{ name: "itemName", type: "string" },
|
{ name: "itemName", type: "string" },
|
||||||
{ name: "action", type: "byte" },
|
{ name: "action", type: "byte" },
|
||||||
{ name: "scoreName", type: "string" },
|
{ name: "scoreName", type: "string" },
|
||||||
{ name: "value", type: "int", condition: function(field_values) {
|
{ name: "value", type: "varint", condition: function(field_values) {
|
||||||
return field_values['action'] != 1;
|
return field_values['action'] != 1;
|
||||||
} }
|
} }
|
||||||
]},
|
]},
|
||||||
@ -550,6 +556,12 @@ var packets = {
|
|||||||
{ name: "friendlyFire", type: "byte", condition: function(field_values) {
|
{ name: "friendlyFire", type: "byte", condition: function(field_values) {
|
||||||
return field_values['mode'] == 0 || field_values['mode'] == 2;
|
return field_values['mode'] == 0 || field_values['mode'] == 2;
|
||||||
} },
|
} },
|
||||||
|
{ name: "nameTagVisibility", type: "string", condition: function(field_values) {
|
||||||
|
return field_values['mode'] == 0 || field_values['mode'] == 2;
|
||||||
|
} },
|
||||||
|
{ name: "color", type: "byte", condition: function(field_values) {
|
||||||
|
return field_values['mode'] == 0 || field_values['mode'] == 2;
|
||||||
|
} },
|
||||||
{ name: "playerCount", type: "count", condition: function(field_values) {
|
{ name: "playerCount", type: "count", condition: function(field_values) {
|
||||||
return field_values['mode'] == 0 || field_values['mode'] == 3 || field_values['mode'] == 4;
|
return field_values['mode'] == 0 || field_values['mode'] == 3 || field_values['mode'] == 4;
|
||||||
}, typeArgs: { type: "short", countFor: "players" } },
|
}, typeArgs: { type: "short", countFor: "players" } },
|
||||||
@ -572,14 +584,11 @@ var packets = {
|
|||||||
{ name: "duration", type: "varint", condition: function(field_values) {
|
{ name: "duration", type: "varint", condition: function(field_values) {
|
||||||
return field_values['event'] == 1;
|
return field_values['event'] == 1;
|
||||||
} },
|
} },
|
||||||
{ name: "entityId", type: "int", condition: function(field_values) {
|
|
||||||
return field_values['event'] == 1;
|
|
||||||
} },
|
|
||||||
{ name: "playerId", type: "varint", condition: function(field_values) {
|
{ name: "playerId", type: "varint", condition: function(field_values) {
|
||||||
return field_values['event'] == 2;
|
return field_values['event'] == 2;
|
||||||
} },
|
} },
|
||||||
{ name: "entityId", type: "int", condition: function(field_values) {
|
{ name: "entityId", type: "int", condition: function(field_values) {
|
||||||
return field_values['event'] == 2;
|
return field_values['event'] == 1 || field_values['event'] == 2;
|
||||||
} },
|
} },
|
||||||
{ name: "message", type: "string", condition: function(field_values) {
|
{ name: "message", type: "string", condition: function(field_values) {
|
||||||
return field_values['event'] == 2;
|
return field_values['event'] == 2;
|
||||||
@ -658,17 +667,22 @@ var packets = {
|
|||||||
]},
|
]},
|
||||||
use_entity: {id: 0x02, fields: [
|
use_entity: {id: 0x02, fields: [
|
||||||
{ name: "target", type: "varint" },
|
{ name: "target", type: "varint" },
|
||||||
{ name: "mouse", type: "byte" },
|
{ name: "mouse", type: "varint" },
|
||||||
{ name: "x", type: "float"},
|
{ name: "x", type: "float", condition: function(field_values) {
|
||||||
{ name: "y", type: "float"},
|
return field_values["mouse"] == 2;
|
||||||
{ name: "size", type: "float"}
|
}},
|
||||||
|
{ name: "y", type: "float", condition: function(field_values) {
|
||||||
|
return field_values["mouse"] == 2;
|
||||||
|
}},
|
||||||
|
{ name: "z", type: "float", condition: function(field_values) {
|
||||||
|
return field_values["mouse"] == 2;
|
||||||
|
}},
|
||||||
]},
|
]},
|
||||||
flying: {id: 0x03, fields: [
|
flying: {id: 0x03, fields: [
|
||||||
{ name: "onGround", type: "bool" }
|
{ name: "onGround", type: "bool" }
|
||||||
]},
|
]},
|
||||||
position: {id: 0x04, fields: [
|
position: {id: 0x04, fields: [
|
||||||
{ name: "x", type: "double" },
|
{ name: "x", type: "double" },
|
||||||
{ name: "stance", type: "double" },
|
|
||||||
{ name: "y", type: "double" },
|
{ name: "y", type: "double" },
|
||||||
{ name: "z", type: "double" },
|
{ name: "z", type: "double" },
|
||||||
{ name: "onGround", type: "bool" }
|
{ name: "onGround", type: "bool" }
|
||||||
@ -702,10 +716,7 @@ var packets = {
|
|||||||
held_item_slot: {id: 0x09, fields: [
|
held_item_slot: {id: 0x09, fields: [
|
||||||
{ name: "slotId", type: "short" }
|
{ name: "slotId", type: "short" }
|
||||||
]},
|
]},
|
||||||
arm_animation: {id: 0x0a, fields: [
|
arm_animation: {id: 0x0a, fields: []},
|
||||||
{ name: "entityId", type: "int" }, /* TODO: wiki.vg says this is empty? */
|
|
||||||
{ name: "animation", type: "byte" }
|
|
||||||
]},
|
|
||||||
entity_action: {id: 0x0b, fields: [
|
entity_action: {id: 0x0b, fields: [
|
||||||
{ name: "entityId", type: "varint" },
|
{ name: "entityId", type: "varint" },
|
||||||
{ name: "actionId", type: "varint" },
|
{ name: "actionId", type: "varint" },
|
||||||
@ -842,6 +853,7 @@ var types = {
|
|||||||
// TODO : remove type-specific, replace with generic containers and arrays.
|
// TODO : remove type-specific, replace with generic containers and arrays.
|
||||||
'position': [readPosition, writePosition, 8],
|
'position': [readPosition, writePosition, 8],
|
||||||
'slot': [readSlot, writeSlot, sizeOfSlot],
|
'slot': [readSlot, writeSlot, sizeOfSlot],
|
||||||
|
'nbt': [readNbt, writeBuffer, sizeOfBuffer],
|
||||||
'entityMetadata': [readEntityMetadata, writeEntityMetadata, sizeOfEntityMetadata],
|
'entityMetadata': [readEntityMetadata, writeEntityMetadata, sizeOfEntityMetadata],
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -870,6 +882,11 @@ var entityMetadataTypes = {
|
|||||||
{ name: 'x', type: 'int' },
|
{ name: 'x', type: 'int' },
|
||||||
{ name: 'y', type: 'int' },
|
{ name: 'y', type: 'int' },
|
||||||
{ name: 'z', type: 'int' }
|
{ name: 'z', type: 'int' }
|
||||||
|
]}},
|
||||||
|
7: { type: 'container', typeArgs: { fields: [
|
||||||
|
{ name: 'pitch', type: 'float' },
|
||||||
|
{ name: 'yaw', type: 'float' },
|
||||||
|
{ name: 'roll', type: 'float' }
|
||||||
]}}
|
]}}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -904,10 +921,10 @@ function writeEntityMetadata(value, buffer, offset) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function writeUUID(value, buffer, offset) {
|
function writeUUID(value, buffer, offset) {
|
||||||
buffer.writeInt32BE(value[0], offset);
|
buffer.writeUInt32BE(value[0], offset);
|
||||||
buffer.writeInt32BE(value[1], offset + 4);
|
buffer.writeUInt32BE(value[1], offset + 4);
|
||||||
buffer.writeInt32BE(value[2], offset + 8);
|
buffer.writeUInt32BE(value[2], offset + 8);
|
||||||
buffer.writeInt32BE(value[3], offset + 12);
|
buffer.writeUInt32BE(value[3], offset + 12);
|
||||||
return offset + 16;
|
return offset + 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -929,7 +946,7 @@ function readEntityMetadata(buffer, offset) {
|
|||||||
type = item >> 5;
|
type = item >> 5;
|
||||||
dataType = entityMetadataTypes[type];
|
dataType = entityMetadataTypes[type];
|
||||||
typeName = dataType.type;
|
typeName = dataType.type;
|
||||||
debug("Reading entity metadata type " + dataType + " (" + ( typeName || "unknown" ) + ")");
|
//debug("Reading entity metadata type " + dataType + " (" + ( typeName || "unknown" ) + ")");
|
||||||
if (!dataType) {
|
if (!dataType) {
|
||||||
return {
|
return {
|
||||||
error: new Error("unrecognized entity metadata type " + type)
|
error: new Error("unrecognized entity metadata type " + type)
|
||||||
@ -946,6 +963,21 @@ function readEntityMetadata(buffer, offset) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function readNbt(buffer, offset) {
|
||||||
|
buffer = buffer.slice(offset);
|
||||||
|
return nbt.parseUncompressed(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeNbt(value, buffer, offset) {
|
||||||
|
var newbuf = nbt.writeUncompressed(value);
|
||||||
|
newbuf.copy(buffer, offset);
|
||||||
|
return offset + newbuf.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sizeOfNbt(value) {
|
||||||
|
return nbt.writeUncompressed(value).length;
|
||||||
|
}
|
||||||
|
|
||||||
function readString (buffer, offset) {
|
function readString (buffer, offset) {
|
||||||
var length = readVarInt(buffer, offset);
|
var length = readVarInt(buffer, offset);
|
||||||
if (!!!length) return null;
|
if (!!!length) return null;
|
||||||
@ -966,10 +998,10 @@ function readString (buffer, offset) {
|
|||||||
function readUUID(buffer, offset) {
|
function readUUID(buffer, offset) {
|
||||||
return {
|
return {
|
||||||
value: [
|
value: [
|
||||||
buffer.readInt32BE(offset),
|
buffer.readUInt32BE(offset),
|
||||||
buffer.readInt32BE(offset + 4),
|
buffer.readUInt32BE(offset + 4),
|
||||||
buffer.readInt32BE(offset + 8),
|
buffer.readUInt32BE(offset + 8),
|
||||||
buffer.readInt32BE(offset + 12),
|
buffer.readUInt32BE(offset + 12),
|
||||||
],
|
],
|
||||||
size: 16,
|
size: 16,
|
||||||
};
|
};
|
||||||
@ -1058,8 +1090,8 @@ function readBool(buffer, offset) {
|
|||||||
function readPosition(buffer, offset) {
|
function readPosition(buffer, offset) {
|
||||||
var longVal = readLong(buffer, offset).value; // I wish I could do destructuring...
|
var longVal = readLong(buffer, offset).value; // I wish I could do destructuring...
|
||||||
var x = longVal[0] >> 6;
|
var x = longVal[0] >> 6;
|
||||||
var y = ((longVal[0] & 0x3F) << 6) | (longVal[1] >> 26);
|
var y = ((longVal[0] & 0x3F) << 6) | ((longVal[1] >> 26) & 0x3f);
|
||||||
var z = longVal[1] << 6 >> 6
|
var z = longVal[1] & 0x3FFFFFF;
|
||||||
return {
|
return {
|
||||||
value: { x: x, y: y, z: z },
|
value: { x: x, y: y, z: z },
|
||||||
size: 8
|
size: 8
|
||||||
@ -1067,60 +1099,72 @@ function readPosition(buffer, offset) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function readSlot(buffer, offset) {
|
function readSlot(buffer, offset) {
|
||||||
|
var value = {};
|
||||||
var results = readShort(buffer, offset);
|
var results = readShort(buffer, offset);
|
||||||
if (! results) return null;
|
if (! results) return null;
|
||||||
var blockId = results.value;
|
value.blockId = results.value;
|
||||||
var cursor = offset + results.size;
|
|
||||||
|
|
||||||
if (blockId === -1) {
|
if (value.blockId === -1) {
|
||||||
return {
|
return {
|
||||||
value: { id: blockId },
|
value: value,
|
||||||
size: cursor - offset,
|
size: 2,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
var cursorEnd = cursor + 5;
|
var cursorEnd = offset + 6;
|
||||||
if (cursorEnd > buffer.length) return null;
|
if (cursorEnd > buffer.length) return null;
|
||||||
var itemCount = buffer.readInt8(cursor);
|
value.itemCount = buffer.readInt8(offset + 2);
|
||||||
var itemDamage = buffer.readInt16BE(cursor + 1);
|
value.itemDamage = buffer.readInt16BE(offset + 3);
|
||||||
var nbtDataSize = buffer.readInt16BE(cursor + 3);
|
var nbtData = buffer.readInt8(offset + 5);
|
||||||
if (nbtDataSize === -1) nbtDataSize = 0;
|
if (nbtData == 0) {
|
||||||
var nbtDataEnd = cursorEnd + nbtDataSize;
|
|
||||||
if (nbtDataEnd > buffer.length) return null;
|
|
||||||
var nbtData = buffer.slice(cursorEnd, nbtDataEnd);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
value: {
|
value: value,
|
||||||
id: blockId,
|
size: 6
|
||||||
itemCount: itemCount,
|
}
|
||||||
itemDamage: itemDamage,
|
}
|
||||||
nbtData: nbtData,
|
var nbtData = readNbt(buffer, offset + 5);
|
||||||
},
|
value.nbtData = nbtData.value;
|
||||||
size: nbtDataEnd - offset,
|
return {
|
||||||
|
value: value,
|
||||||
|
size: nbtData.size + 5
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function sizeOfSlot(value) {
|
function sizeOfSlot(value) {
|
||||||
return value.id === -1 ? 2 : 7 + value.nbtData.length;
|
if (value.blockId === -1)
|
||||||
|
return (2);
|
||||||
|
else if (!value.nbtData) {
|
||||||
|
return (6);
|
||||||
|
} else {
|
||||||
|
return (5 + sizeOfNbt(value.nbtData));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function writePosition(value, buffer, offset) {
|
function writePosition(value, buffer, offset) {
|
||||||
var longVal = [];
|
var longVal = [];
|
||||||
longVal[0] = ((value.x & 0x3FFFFFF) << 6) | ((value.y & 0xFC0) >> 6);
|
longVal[0] = ((value.x & 0x3FFFFFF) << 6) | ((value.y & 0xFFF) >> 6);
|
||||||
longVal[1] = ((value.y & 0x3F) << 26) | (value.z & 0x3FFFFFF);
|
longVal[1] = ((value.y & 0x3F) << 26) | (value.z & 0x3FFFFFF);
|
||||||
return writeLong(longVal, buffer, offset);
|
return writeLong(longVal, buffer, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
function writeSlot(value, buffer, offset) {
|
function writeSlot(value, buffer, offset) {
|
||||||
buffer.writeInt16BE(value.id, offset);
|
buffer.writeInt16BE(value.blockId, offset);
|
||||||
if (value.id === -1) return offset + 2;
|
if (value.blockId === -1) return offset + 2;
|
||||||
buffer.writeInt8(value.itemCount, offset + 2);
|
buffer.writeInt8(value.itemCount, offset + 2);
|
||||||
buffer.writeInt16BE(value.itemDamage, offset + 3);
|
buffer.writeInt16BE(value.itemDamage, offset + 3);
|
||||||
var nbtDataSize = value.nbtData.length;
|
var nbtDataLen;
|
||||||
if (nbtDataSize === 0) nbtDataSize = -1; // I don't know wtf mojang smokes
|
if (value.nbtData)
|
||||||
buffer.writeInt16BE(nbtDataSize, offset + 5);
|
{
|
||||||
value.nbtData.copy(buffer, offset + 7);
|
var newbuf = nbt.writeUncompressed(value.nbtData);
|
||||||
return offset + 7 + value.nbtData.length;
|
newbuf.copy(buffer, offset + 5);
|
||||||
|
nbtDataLen = newbuf.length;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
buffer.writeInt8(0, offset + 5);
|
||||||
|
nbtDataLen = 1;
|
||||||
|
}
|
||||||
|
return offset + 5 + nbtDataLen;
|
||||||
}
|
}
|
||||||
|
|
||||||
function sizeOfString(value) {
|
function sizeOfString(value) {
|
||||||
@ -1236,6 +1280,7 @@ function readContainer(buffer, offset, typeArgs, rootNode) {
|
|||||||
};
|
};
|
||||||
// BLEIGH. Huge hack because I have no way of knowing my current name.
|
// BLEIGH. Huge hack because I have no way of knowing my current name.
|
||||||
// TODO : either pass fieldInfo instead of typeArgs as argument (bleigh), or send name as argument (verybleigh).
|
// TODO : either pass fieldInfo instead of typeArgs as argument (bleigh), or send name as argument (verybleigh).
|
||||||
|
// TODO : what I do inside of roblabla/Protocols is have each "frame" create a new empty slate with just a "super" object pointing to the parent.
|
||||||
rootNode.this = results.value;
|
rootNode.this = results.value;
|
||||||
for (var index in typeArgs.fields) {
|
for (var index in typeArgs.fields) {
|
||||||
var readResults = read(buffer, offset, typeArgs.fields[index], rootNode);
|
var readResults = read(buffer, offset, typeArgs.fields[index], rootNode);
|
||||||
@ -1249,11 +1294,15 @@ function readContainer(buffer, offset, typeArgs, rootNode) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function writeContainer(value, buffer, offset, typeArgs, rootNode) {
|
function writeContainer(value, buffer, offset, typeArgs, rootNode) {
|
||||||
|
var context = value.this ? value.this : value;
|
||||||
rootNode.this = value;
|
rootNode.this = value;
|
||||||
for (var index in typeArgs.fields) {
|
for (var index in typeArgs.fields) {
|
||||||
if (!value.hasOwnProperty(typeArgs.fields[index].name && typeArgs.fields[index].type != "count" && !typeArgs.fields[index].condition))
|
if (!context.hasOwnProperty(typeArgs.fields[index].name) && typeArgs.fields[index].type != "count" && !typeArgs.fields[index].condition)
|
||||||
|
{
|
||||||
debug(new Error("Missing Property " + typeArgs.fields[index].name).stack);
|
debug(new Error("Missing Property " + typeArgs.fields[index].name).stack);
|
||||||
offset = write(value[typeArgs.fields[index].name], buffer, offset, typeArgs.fields[index], rootNode);
|
console.log(context);
|
||||||
|
}
|
||||||
|
offset = write(context[typeArgs.fields[index].name], buffer, offset, typeArgs.fields[index], rootNode);
|
||||||
}
|
}
|
||||||
delete rootNode.this;
|
delete rootNode.this;
|
||||||
return offset;
|
return offset;
|
||||||
@ -1261,9 +1310,10 @@ function writeContainer(value, buffer, offset, typeArgs, rootNode) {
|
|||||||
|
|
||||||
function sizeOfContainer(value, typeArgs, rootNode) {
|
function sizeOfContainer(value, typeArgs, rootNode) {
|
||||||
var size = 0;
|
var size = 0;
|
||||||
|
var context = value.this ? value.this : value;
|
||||||
rootNode.this = value;
|
rootNode.this = value;
|
||||||
for (var index in typeArgs.fields) {
|
for (var index in typeArgs.fields) {
|
||||||
size += sizeOf(value[typeArgs.fields[index].name], typeArgs.fields[index], rootNode);
|
size += sizeOf(context[typeArgs.fields[index].name], typeArgs.fields[index], rootNode);
|
||||||
}
|
}
|
||||||
delete rootNode.this;
|
delete rootNode.this;
|
||||||
return size;
|
return size;
|
||||||
@ -1360,7 +1410,10 @@ function read(buffer, cursor, fieldInfo, rootNodes) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
var readResults = type[0](buffer, cursor, fieldInfo.typeArgs, rootNodes);
|
var readResults = type[0](buffer, cursor, fieldInfo.typeArgs, rootNodes);
|
||||||
if (readResults.error) return { error: readResults.error };
|
if (readResults == null) {
|
||||||
|
throw new Error("Reader returned null : " + JSON.stringify(fieldInfo));
|
||||||
|
}
|
||||||
|
if (readResults && readResults.error) return { error: readResults.error };
|
||||||
return readResults;
|
return readResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1415,7 +1468,13 @@ function createPacketBuffer(packetId, state, params, isServer) {
|
|||||||
var packet = get(packetId, state, !isServer);
|
var packet = get(packetId, state, !isServer);
|
||||||
assert.notEqual(packet, null);
|
assert.notEqual(packet, null);
|
||||||
packet.forEach(function(fieldInfo) {
|
packet.forEach(function(fieldInfo) {
|
||||||
|
try {
|
||||||
length += sizeOf(params[fieldInfo.name], fieldInfo, params);
|
length += sizeOf(params[fieldInfo.name], fieldInfo, params);
|
||||||
|
} catch (e) {
|
||||||
|
console.log("fieldInfo : " + JSON.stringify(fieldInfo));
|
||||||
|
console.log("params : " + JSON.stringify(params));
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
length += sizeOfVarInt(packetId);
|
length += sizeOfVarInt(packetId);
|
||||||
var size = length;// + sizeOfVarInt(length);
|
var size = length;// + sizeOfVarInt(length);
|
||||||
@ -1466,7 +1525,7 @@ function parsePacketData(buffer, state, isServer, packetsToParse) {
|
|||||||
var packetId = packetIdField.value;
|
var packetId = packetIdField.value;
|
||||||
cursor += packetIdField.size;
|
cursor += packetIdField.size;
|
||||||
|
|
||||||
var results = { id: packetId };
|
var results = { id: packetId, state: state };
|
||||||
// Only parse the packet if there is a need for it, AKA if there is a listener attached to it
|
// Only parse the packet if there is a need for it, AKA if there is a listener attached to it
|
||||||
var name = packetNames[state][isServer ? "toServer" : "toClient"][packetId];
|
var name = packetNames[state][isServer ? "toServer" : "toClient"][packetId];
|
||||||
var shouldParse = (!packetsToParse.hasOwnProperty(name) || packetsToParse[name] <= 0)
|
var shouldParse = (!packetsToParse.hasOwnProperty(name) || packetsToParse[name] <= 0)
|
||||||
@ -1513,6 +1572,8 @@ function parsePacketData(buffer, state, isServer, packetsToParse) {
|
|||||||
results[fieldInfo.name] = readResults.value;
|
results[fieldInfo.name] = readResults.value;
|
||||||
cursor += readResults.size;
|
cursor += readResults.size;
|
||||||
}
|
}
|
||||||
|
if (buffer.length > cursor)
|
||||||
|
console.log("DID NOT PARSE THE WHOLE THING!");
|
||||||
debug(results);
|
debug(results);
|
||||||
return {
|
return {
|
||||||
results: results,
|
results: results,
|
||||||
|
@ -35,8 +35,8 @@ console.log('Finished write test in ' + (Date.now() - start) / 1000 + ' seconds'
|
|||||||
|
|
||||||
var testDataRead = [
|
var testDataRead = [
|
||||||
{id: 0x00, params: {keepAliveId: 957759560}},
|
{id: 0x00, params: {keepAliveId: 957759560}},
|
||||||
{id: 0x02, params: {message: '<Bob> Hello World!'}},
|
{id: 0x02, params: {message: '<Bob> Hello World!', position: 0}},
|
||||||
{id: 0x08, params: {x: 6.5, y: 65.62, stance: 67.24, z: 7.5, yaw: 0, pitch: 0, onGround: true}},
|
{id: 0x08, params: {x: 6.5, y: 65.62, z: 7.5, yaw: 0, pitch: 0, flags: 0}},
|
||||||
];
|
];
|
||||||
|
|
||||||
client.isServer = true;
|
client.isServer = true;
|
||||||
|
85
test/test.js
85
test/test.js
@ -90,10 +90,18 @@ var values = {
|
|||||||
'double': 99999.2222,
|
'double': 99999.2222,
|
||||||
'float': -333.444,
|
'float': -333.444,
|
||||||
'slot': {
|
'slot': {
|
||||||
id: 5,
|
blockId: 5,
|
||||||
itemCount: 56,
|
itemCount: 56,
|
||||||
itemDamage: 2,
|
itemDamage: 2,
|
||||||
nbtData: new Buffer(90),
|
nbtData: { root: "test", value: {
|
||||||
|
test1: { type: "int", value: 4 },
|
||||||
|
test2: { type: "long", value: [12,42] },
|
||||||
|
test3: { type: "byteArray", value: new Buffer(32) },
|
||||||
|
test4: { type: "string", value: "ohi" },
|
||||||
|
test5: { type: "list", value: { type: "int", value: [4] } },
|
||||||
|
test6: { type: "compound", value: { test: { type: "int", value: 4 } } },
|
||||||
|
test7: { type: "intArray", value: [12, 42] }
|
||||||
|
} }
|
||||||
},
|
},
|
||||||
'long': [0, 1],
|
'long': [0, 1],
|
||||||
'entityMetadata': [
|
'entityMetadata': [
|
||||||
@ -179,6 +187,7 @@ describe("packets", function() {
|
|||||||
if (toServer) {
|
if (toServer) {
|
||||||
serverClient.once([state, packetId], function(receivedPacket) {
|
serverClient.once([state, packetId], function(receivedPacket) {
|
||||||
delete receivedPacket.id;
|
delete receivedPacket.id;
|
||||||
|
delete receivedPacket.state;
|
||||||
assertPacketsMatch(packet, receivedPacket);
|
assertPacketsMatch(packet, receivedPacket);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
@ -186,6 +195,7 @@ describe("packets", function() {
|
|||||||
} else {
|
} else {
|
||||||
client.once([state, packetId], function(receivedPacket) {
|
client.once([state, packetId], function(receivedPacket) {
|
||||||
delete receivedPacket.id;
|
delete receivedPacket.id;
|
||||||
|
delete receivedPacket.state;
|
||||||
assertPacketsMatch(packet, receivedPacket);
|
assertPacketsMatch(packet, receivedPacket);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
@ -207,7 +217,7 @@ describe("packets", function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("client", function() {
|
describe("client", function() {
|
||||||
this.timeout(4000000);
|
this.timeout(10 * 60 * 1000);
|
||||||
|
|
||||||
var mcServer;
|
var mcServer;
|
||||||
function startServer(propOverrides, done) {
|
function startServer(propOverrides, done) {
|
||||||
@ -277,11 +287,16 @@ describe("client", function() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
afterEach(function(done) {
|
afterEach(function(done) {
|
||||||
|
if (mcServer)
|
||||||
|
{
|
||||||
mcServer.stdin.write("stop\n");
|
mcServer.stdin.write("stop\n");
|
||||||
mcServer.on('exit', function() {
|
mcServer.on('exit', function() {
|
||||||
mcServer = null;
|
mcServer = null;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
after(function(done) {
|
after(function(done) {
|
||||||
rimraf(MC_SERVER_PATH, done);
|
rimraf(MC_SERVER_PATH, done);
|
||||||
@ -308,8 +323,8 @@ describe("client", function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it("connects successfully - online mode", function(done) {
|
it("connects successfully - online mode (STUBBED)", function(done) {
|
||||||
startServer({ 'online-mode': 'true' }, function() {
|
/*startServer({ 'online-mode': 'true' }, function() {
|
||||||
var client = mc.createClient({
|
var client = mc.createClient({
|
||||||
username: process.env.MC_USERNAME,
|
username: process.env.MC_USERNAME,
|
||||||
password: process.env.MC_PASSWORD,
|
password: process.env.MC_PASSWORD,
|
||||||
@ -332,32 +347,13 @@ describe("client", function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
client.on('chat', function(packet) {
|
client.on('chat', function(packet) {
|
||||||
chatCount += 1;
|
|
||||||
assert.ok(chatCount <= 2);
|
|
||||||
var message = JSON.parse(packet.message);
|
|
||||||
if (chatCount === 1) {
|
|
||||||
assert.strictEqual(message.translate, "chat.type.text");
|
|
||||||
assert.deepEqual(message["with"][0], {
|
|
||||||
clickEvent: {
|
|
||||||
action: "suggest_command",
|
|
||||||
value: "/msg " + client.session.username + " "
|
|
||||||
},
|
|
||||||
text: client.session.username
|
|
||||||
});
|
|
||||||
assert.strictEqual(message["with"][1], "hello everyone; I have logged in.");
|
|
||||||
} else if (chatCount === 2) {
|
|
||||||
assert.strictEqual(message.translate, "chat.type.announcement");
|
|
||||||
assert.strictEqual(message["with"][0], "Server");
|
|
||||||
assert.deepEqual(message["with"][1], { text: "",
|
|
||||||
extra: ["hello"]
|
|
||||||
});
|
|
||||||
done();
|
done();
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
});*/
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
});
|
it("connects successfully - offline mode (STUBBED)", function(done) {
|
||||||
it("connects successfully - offline mode", function(done) {
|
/*startServer({ 'online-mode': 'false' }, function() {
|
||||||
startServer({ 'online-mode': 'false' }, function() {
|
|
||||||
var client = mc.createClient({
|
var client = mc.createClient({
|
||||||
username: 'Player',
|
username: 'Player',
|
||||||
});
|
});
|
||||||
@ -401,7 +397,8 @@ describe("client", function() {
|
|||||||
done();
|
done();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});*/
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
it("gets kicked when no credentials supplied in online mode", function(done) {
|
it("gets kicked when no credentials supplied in online mode", function(done) {
|
||||||
startServer({ 'online-mode': 'true' }, function() {
|
startServer({ 'online-mode': 'true' }, function() {
|
||||||
@ -432,13 +429,13 @@ describe("client", function() {
|
|||||||
client.on([states.PLAY, 0x02], function(packet) {
|
client.on([states.PLAY, 0x02], function(packet) {
|
||||||
var message = JSON.parse(packet.message);
|
var message = JSON.parse(packet.message);
|
||||||
assert.strictEqual(message.translate, "chat.type.text");
|
assert.strictEqual(message.translate, "chat.type.text");
|
||||||
assert.deepEqual(message["with"][0], {
|
/*assert.deepEqual(message["with"][0], {
|
||||||
clickEvent: {
|
clickEvent: {
|
||||||
action: "suggest_command",
|
action: "suggest_command",
|
||||||
value: "/msg Player "
|
value: "/msg Player "
|
||||||
},
|
},
|
||||||
text: "Player"
|
text: "Player"
|
||||||
});
|
});*/
|
||||||
assert.strictEqual(message["with"][1], "hello everyone; I have logged in.");
|
assert.strictEqual(message["with"][1], "hello everyone; I have logged in.");
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
done();
|
done();
|
||||||
@ -481,7 +478,7 @@ describe("mc-server", function() {
|
|||||||
client.on('end', function() {
|
client.on('end', function() {
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
client.connect(25565, 'localhost');
|
client.connect(25565, '127.0.0.1');
|
||||||
});
|
});
|
||||||
|
|
||||||
function resolve() {
|
function resolve() {
|
||||||
@ -508,6 +505,8 @@ describe("mc-server", function() {
|
|||||||
server.on('listening', function() {
|
server.on('listening', function() {
|
||||||
var client = mc.createClient({
|
var client = mc.createClient({
|
||||||
username: 'superpants',
|
username: 'superpants',
|
||||||
|
host: '127.0.0.1',
|
||||||
|
port: 25565,
|
||||||
keepAlive: false,
|
keepAlive: false,
|
||||||
});
|
});
|
||||||
client.on('end', function() {
|
client.on('end', function() {
|
||||||
@ -526,15 +525,15 @@ describe("mc-server", function() {
|
|||||||
'max-players': 120,
|
'max-players': 120,
|
||||||
});
|
});
|
||||||
server.on('listening', function() {
|
server.on('listening', function() {
|
||||||
mc.ping({}, function(err, results) {
|
mc.ping({host: '127.0.0.1'}, function(err, results) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
assert.ok(results.latency >= 0);
|
assert.ok(results.latency >= 0);
|
||||||
assert.ok(results.latency <= 1000);
|
assert.ok(results.latency <= 1000);
|
||||||
delete results.latency;
|
delete results.latency;
|
||||||
assert.deepEqual(results, {
|
assert.deepEqual(results, {
|
||||||
version: { //TODO : Make this dynamic, based on protocol.version
|
version: { //TODO : Make this dynamic, based on protocol.version
|
||||||
name: "1.7.10",
|
name: "1.8.1",
|
||||||
protocol: 5
|
protocol: 47
|
||||||
},
|
},
|
||||||
players: {
|
players: {
|
||||||
max: 120,
|
max: 120,
|
||||||
@ -566,7 +565,8 @@ describe("mc-server", function() {
|
|||||||
gameMode: 1,
|
gameMode: 1,
|
||||||
dimension: 0,
|
dimension: 0,
|
||||||
difficulty: 2,
|
difficulty: 2,
|
||||||
maxPlayers: server.maxPlayers
|
maxPlayers: server.maxPlayers,
|
||||||
|
reducedDebugInfo: 0
|
||||||
});
|
});
|
||||||
client.on([states.PLAY, 0x01], function(packet) {
|
client.on([states.PLAY, 0x01], function(packet) {
|
||||||
var message = '<' + client.username + '>' + ' ' + packet.message;
|
var message = '<' + client.username + '>' + ' ' + packet.message;
|
||||||
@ -575,7 +575,7 @@ describe("mc-server", function() {
|
|||||||
});
|
});
|
||||||
server.on('close', done);
|
server.on('close', done);
|
||||||
server.on('listening', function() {
|
server.on('listening', function() {
|
||||||
var player1 = mc.createClient({ username: 'player1' });
|
var player1 = mc.createClient({ username: 'player1', host: '127.0.0.1' });
|
||||||
player1.on([states.PLAY, 0x01], function(packet) {
|
player1.on([states.PLAY, 0x01], function(packet) {
|
||||||
assert.strictEqual(packet.gameMode, 1);
|
assert.strictEqual(packet.gameMode, 1);
|
||||||
assert.strictEqual(packet.levelType, 'default');
|
assert.strictEqual(packet.levelType, 'default');
|
||||||
@ -602,7 +602,7 @@ describe("mc-server", function() {
|
|||||||
});
|
});
|
||||||
player2.write(0x01, { message: "hi" } );
|
player2.write(0x01, { message: "hi" } );
|
||||||
});
|
});
|
||||||
var player2 = mc.createClient({ username: 'player2' });
|
var player2 = mc.createClient({ username: 'player2', host: '127.0.0.1' });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -612,11 +612,12 @@ describe("mc-server", function() {
|
|||||||
if (!server.clients.hasOwnProperty(clientId)) continue;
|
if (!server.clients.hasOwnProperty(clientId)) continue;
|
||||||
|
|
||||||
client = server.clients[clientId];
|
client = server.clients[clientId];
|
||||||
if (client !== exclude) client.write(0x02, { message: JSON.stringify({text: message})});
|
if (client !== exclude) client.write(0x02, { message: JSON.stringify({text: message}), position: 0});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
it("kicks clients when invalid credentials", function(done) {
|
it("kicks clients when invalid credentials", function(done) {
|
||||||
|
this.timeout(10000);
|
||||||
var server = mc.createServer();
|
var server = mc.createServer();
|
||||||
var count = 4;
|
var count = 4;
|
||||||
server.on('connection', function(client) {
|
server.on('connection', function(client) {
|
||||||
@ -632,6 +633,7 @@ describe("mc-server", function() {
|
|||||||
resolve();
|
resolve();
|
||||||
var client = mc.createClient({
|
var client = mc.createClient({
|
||||||
username: 'lalalal',
|
username: 'lalalal',
|
||||||
|
host: "127.0.0.1"
|
||||||
});
|
});
|
||||||
client.on('end', function() {
|
client.on('end', function() {
|
||||||
resolve();
|
resolve();
|
||||||
@ -656,14 +658,15 @@ describe("mc-server", function() {
|
|||||||
gameMode: 1,
|
gameMode: 1,
|
||||||
dimension: 0,
|
dimension: 0,
|
||||||
difficulty: 2,
|
difficulty: 2,
|
||||||
maxPlayers: server.maxPlayers
|
maxPlayers: server.maxPlayers,
|
||||||
|
reducedDebugInfo: 0
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
server.on('close', function() {
|
server.on('close', function() {
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
server.on('listening', function() {
|
server.on('listening', function() {
|
||||||
var client = mc.createClient({ username: 'lalalal', });
|
var client = mc.createClient({ username: 'lalalal', host: '127.0.0.1' });
|
||||||
client.on([states.PLAY, 0x01], function() {
|
client.on([states.PLAY, 0x01], function() {
|
||||||
server.close();
|
server.close();
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user