Add support for jc2mp's custom expanded gamespy protocol

Closes #14
This commit is contained in:
Michael Morrison 2015-01-17 06:51:02 -06:00
parent 71b7f97c99
commit b6bd3b6108
2 changed files with 74 additions and 34 deletions

View File

@ -139,7 +139,7 @@ il2|IL-2 Sturmovik|gamespy1|port_query=21000
insurgency|Insurgency|valve insurgency|Insurgency|valve
ironstorm|Iron Storm|gamespy1|port_query=3505 ironstorm|Iron Storm|gamespy1|port_query=3505
jamesbondnightfire|James Bond: Nightfire|gamespy1|port_query=6550 jamesbondnightfire|James Bond: Nightfire|gamespy1|port_query=6550
jc2mp|Just Cause 2 Multiplayer|jc2mp|port=7777 jc2mp|Just Cause 2 Multiplayer|jc2mp|port=7777|isJc2mp
killingfloor|Killing Floor|killingfloor|port=7707,port_query_offset=1 killingfloor|Killing Floor|killingfloor|port=7707,port_query_offset=1
kingpin|Kingpin: Life of Crime|gamespy1|port=31510,port_query_offset=-10 kingpin|Kingpin: Life of Crime|gamespy1|port=31510,port_query_offset=-10
kisspc|KISS Psycho Circus|gamespy1|port=7777,port_query_offset=1 kisspc|KISS Psycho Circus|gamespy1|port=7777,port_query_offset=1

View File

@ -8,6 +8,7 @@ module.exports = require('./core').extend({
this.byteorder = 'be'; this.byteorder = 'be';
this.noChallenge = false; this.noChallenge = false;
this.useOnlySingleSplit = false; this.useOnlySingleSplit = false;
this.isJc2mp = false;
}, },
run: function(state) { run: function(state) {
var self = this; var self = this;
@ -23,7 +24,15 @@ module.exports = require('./core').extend({
}); });
}, },
function(c) { function(c) {
self.sendPacket(0,challenge,new Buffer([0xff,0xff,0xff,0x01]),true,function(b) { var requestPayload;
if(self.isJc2mp) {
// they completely alter the protocol. because why not.
requestPayload = new Buffer([0xff,0xff,0xff,0x02]);
} else {
requestPayload = new Buffer([0xff,0xff,0xff,0x01]);
}
self.sendPacket(0,challenge,requestPayload,true,function(b) {
packets = b; packets = b;
c(); c();
}); });
@ -32,7 +41,8 @@ module.exports = require('./core').extend({
// iterate over the received packets // iterate over the received packets
// the first packet will start off with k/v pairs, followed with data fields // the first packet will start off with k/v pairs, followed with data fields
// the following packets will only have data fields // the following packets will only have data fields
var data = {};
state.raw.playerTeamInfo = {};
for(var iPacket = 0; iPacket < packets.length; iPacket++) { for(var iPacket = 0; iPacket < packets.length; iPacket++) {
var packet = packets[iPacket]; var packet = packets[iPacket];
@ -43,12 +53,14 @@ module.exports = require('./core').extend({
console.log(":::"+packet.toString('ascii')); console.log(":::"+packet.toString('ascii'));
} }
// Parse raw server key/values
if(iPacket == 0) { if(iPacket == 0) {
while(!reader.done()) { while(!reader.done()) {
var key = reader.string(); var key = reader.string();
if(!key) break; if(!key) break;
var value = reader.string(); var value = reader.string();
// reread the next line if we hit the weird ut3 bug // reread the next line if we hit the weird ut3 bug
if(value == 'p1073741829') value = reader.string(); if(value == 'p1073741829') value = reader.string();
@ -56,48 +68,76 @@ module.exports = require('./core').extend({
} }
} }
var firstMode = true; // Parse player, team, item array state
while(!reader.done()) {
var mode = reader.string();
if(mode.charCodeAt(0) <= 2) mode = mode.substring(1);
if(!mode) continue;
var offset = 0;
if(iPacket != 0 && firstMode) offset = reader.uint(1);
reader.skip(1);
firstMode = false;
if(self.isJc2mp) {
state.raw.numPlayers2 = reader.uint(2);
while(!reader.done()) { while(!reader.done()) {
var item = reader.string(); var player = {};
if(!item) break; player.name = reader.string();
player.steamid = reader.string();
player.ping = reader.uint(2);
state.players.push(player);
}
} else {
var firstMode = true;
while(!reader.done()) {
var mode = reader.string();
if(mode.charCodeAt(0) <= 2) mode = mode.substring(1);
if(!mode) continue;
var offset = 0;
if(iPacket != 0 && firstMode) offset = reader.uint(1);
reader.skip(1);
firstMode = false;
if( var modeSplit = mode.split('_');
mode == 'player_' var modeName = modeSplit[0];
|| mode == 'score_' var modeType = modeSplit.length > 1 ? modeSplit[1] : 'no_';
|| mode == 'ping_'
|| mode == 'team_' if(!(modeType in state.raw.playerTeamInfo)) {
|| mode == 'deaths_' state.raw.playerTeamInfo[modeType] = [];
|| mode == 'pid_' }
) { var store = state.raw.playerTeamInfo[modeType];
if(state.players.length <= offset)
state.players.push({}); while(!reader.done()) {
var item = reader.string();
if(!item) break;
while(store.length <= offset) { store.push({}); }
store[offset][modeName] = item;
offset++;
} }
if(mode == 'player_') state.players[offset].name = item;
if(mode == 'score_') state.players[offset].score = parseInt(item);
if(mode == 'ping_') state.players[offset].ping = parseInt(item);
if(mode == 'team_') state.players[offset].team = parseInt(item);
if(mode == 'deaths_') state.players[offset].deaths = parseInt(item);
if(mode == 'pid_') state.players[offset].pid = item;
offset++;
} }
} }
} }
c();
},
function(c) {
// Turn all that raw state into something useful
if('hostname' in state.raw) state.name = state.raw.hostname; if('hostname' in state.raw) state.name = state.raw.hostname;
else if('servername' in state.raw) state.name = state.raw.servername; else if('servername' in state.raw) state.name = state.raw.servername;
if('mapname' in state.raw) state.map = state.raw.mapname; if('mapname' in state.raw) state.map = state.raw.mapname;
if(state.raw.password == '1') state.password = true; if(state.raw.password == '1') state.password = true;
if('maxplayers' in state.raw) state.maxplayers = parseInt(state.raw.maxplayers); if('maxplayers' in state.raw) state.maxplayers = parseInt(state.raw.maxplayers);
if('' in state.raw.playerTeamInfo) {
state.raw.playerTeamInfo[''].forEach(function(playerInfo) {
var player = {};
for(var from in playerInfo) {
var key = from;
var value = playerInfo[from];
if(key == 'player') key = 'name';
if(key == 'score' || key == 'ping' || key == 'team' || key == 'deaths' || key == 'pid') value = parseInt(value);
player[key] = value;
}
state.players.push(player);
})
}
self.finish(state); self.finish(state);
} }
]); ]);
@ -141,7 +181,7 @@ module.exports = require('./core').extend({
var last = (id & 0x80); var last = (id & 0x80);
id = id & 0x7f; id = id & 0x7f;
if(last) numPackets = id+1; if(last) numPackets = id+1;
reader.skip(1); // "another 'packet number' byte, but isn't understood." reader.skip(1); // "another 'packet number' byte, but isn't understood."
packets[id] = reader.rest(); packets[id] = reader.rest();
@ -151,7 +191,7 @@ module.exports = require('./core').extend({
} }
if(!numPackets || Object.keys(packets).length != numPackets) return; if(!numPackets || Object.keys(packets).length != numPackets) return;
// assemble the parts // assemble the parts
var list = []; var list = [];
for(var i = 0; i < numPackets; i++) { for(var i = 0; i < numPackets; i++) {