Created
February 5, 2013 02:02
-
-
Save daniel-j/4711506 to your computer and use it in GitHub Desktop.
SGIP BOT
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
var net = require('net'); | |
var dgram = require('dgram'); | |
var sqlite3 = require('sqlite3').verbose(); | |
function openDb () { | |
var db = new sqlite3.Database('bot.sqlite', function (err) { | |
if (err) { | |
console.log("Error opening database", err); | |
} | |
}); | |
return db; | |
} | |
var BOT_NAME = "[sgip]"; | |
var listservers = ['list1.digiex.net', 'list2.digiex.net']; | |
var connections = {}; | |
var servers = []; | |
var serverLookup = []; | |
var database = {}; | |
//db.run("DROP TABLE serverinfo"); | |
//db.run("CREATE TABLE IF NOT EXISTS serverinfo (address TEXT NOT NULL UNIQUE, name TEXT, password TEXT, adminpassword TEXT, blacklistlevel INTEGER)"); | |
console.log('all ok'); | |
refreshDb(function () { | |
startApiServer(); | |
scanServerlist(); | |
}); | |
function refreshDb (cb) { | |
function checkCb () { | |
var currentBlacklisted = []; | |
for (var i in database) { | |
if (database[i].blacklistlevel > 0) { | |
currentBlacklisted.push(i); | |
} | |
} | |
if (--cbcounter === 0) { | |
console.log("Checking database..."); | |
for (var i in database) { | |
var address = i; | |
var serverinfo = database[i]; | |
var index = serverLookup.indexOf(address); | |
if (currentBlacklisted.indexOf(address) !== -1 && database[i].blacklistlevel === 0) { | |
console.log("Whitelisted", address); | |
} else if (currentBlacklisted.indexOf(address) === -1 && database[i].blacklistlevel > 0) { | |
console.log("Blacklisted", address); | |
} | |
if (index !== -1 && database[i].blacklistlevel > 0) { | |
var server = servers[index]; | |
console.log("Closing connection to", address); | |
var conn = connections[address]; | |
server.isConnected = false; | |
conn.udp && conn.udp._handle && conn.udp.close(); | |
conn.client && conn.client._handle && conn.client.destroy(); | |
detachServer(index); | |
} | |
} | |
if (typeof cb === 'function') {cb();} | |
} | |
} | |
console.log("Refreshing database..."); | |
var cbcounter = 0; | |
cbcounter++; | |
var db = openDb(); | |
db.all("SELECT * FROM serverinfo", function (err, list) { | |
for (var i = 0; i < list.length; i++) { | |
database[list[i].address] = list[i]; | |
} | |
checkCb(); | |
}); | |
db.close(); | |
}; | |
// Zero fill | |
function zfs(v) {v=v+'';if(v.length == 1) return "0"+v; return v;}; | |
function Server (config) { | |
if(!config) config = {}; | |
this.ip = config.ip; | |
this.port = parseInt(config.port, 10) || 0; | |
this.address = this.ip+":"+this.port; | |
this['private'] = !!(config['private'] || false); | |
this.gametype = parseInt(config.gametype, 10) || 0; | |
this.version = new Buffer(4); | |
if(Buffer.isBuffer(config.version)) { | |
config.version.copy(this.version, 0, 0, 4); | |
} | |
else if(typeof config.version === 'string') { | |
this.version.write(config.version, 0, 4, 'binary'); | |
} | |
if(!config.capacity) config.capacity = [0, 0]; | |
this.capacity = [+config.capacity[0] || 0, +config.capacity[1] || 0]; | |
this.servername = config.servername || ''; | |
}; | |
Server.prototype.toJSON = function () { | |
return { | |
ip: this.ip, | |
port: this.port, | |
address: this.address, | |
'private': this['private'], | |
gametype: this.gametype, | |
version: this.version.toString('binary'), | |
capacity: this.capacity, | |
servername: this.servername | |
}; | |
}; | |
function udpchecksum (buf) { | |
var x = 1, y = 1, size = buf.length; | |
for(var i = 2; i < size; i+=1) { | |
x += buf[i]; | |
y += x; | |
} | |
buf[0] = x % 251; | |
buf[1] = y % 251; | |
return buf; | |
}; | |
var disconnectMessages = { | |
1: "Server is full", | |
2: "Version different", | |
3: "Server is full", | |
4: "Error during handshaking", | |
5: "Feature not supported in shareware", | |
6: "Error downloading level", | |
7: "Connection lost", | |
8: "Winsock error", | |
9: "Connection timed out", | |
10:"Server stopped", | |
11:"Kicked off", | |
12:"Banned", | |
17:"Unauthorized file request", | |
18:"No splitscreeners allowed" | |
}; | |
var actionSizes = { | |
0x01: 3, | |
0x02: 3, | |
0x03: 3, | |
0x04: 4, | |
0x05: 1, | |
0x06: 2, | |
0x07: 15, | |
0x08: 12, | |
0x09: 1, | |
0x0A: 3, | |
0x0B: 4, | |
0x0C: 4, | |
0x0D: 1, | |
0x0E: 10, | |
0x0F: 2, | |
0x15: 3, | |
0x19: 4, | |
0x1E: 21, | |
0x1A: 3 | |
} | |
function attachServer (info) { | |
var server = { | |
info: new Server(info), | |
client: {}, | |
isConnected: false | |
}; | |
console.log('Add server '+server.info.address+' '+server.info.servername); | |
var udpPort = 0; | |
var udpCounter = 0; | |
function connectFailed () { | |
//console.log('CONNECT FAILED'); | |
server.isConnected = false; | |
udp._handle && udp.close(); | |
detachServer(servers.indexOf(server)); | |
}; | |
function udpSend (data) { | |
data[3] = (udpCounter++) % 256; | |
data = udpchecksum(data); | |
udp._handle && udp.send(data, 0, data.length, server.info.port, server.info.ip); | |
}; | |
function joinServer () { | |
/*if (server.info.address !== "80.78.216.229:10052") { | |
connectFailed(); | |
return; | |
}*/ | |
//connectFailed(); | |
//return; | |
server.client.socketId = 0; | |
server.client.playerId = 0; | |
server.client.levelName = ""; | |
server.client.isPlus = false; | |
server.client.players = []; | |
server.client.game = { | |
gamemode: 0, | |
maxscore: 0, | |
customMode: 0, | |
ctf: { | |
teams: [ | |
{score: 0, holder: -1}, | |
{score: 0, holder: -1} | |
] | |
}, | |
time: { | |
started: false, | |
inPregame: false, | |
inOvertime: false, | |
timeLeft: 0, | |
time: 0, | |
autoCycle: false | |
}, | |
isIdleserver: false, | |
}; | |
var game = server.client.game; | |
server.client.lastTimeUpdate = Date.now(); | |
// TODO: Get password from database | |
var password = (database[server.info.address] && database[server.info.address].password) || ""; | |
var toSend = udpchecksum(new Buffer("yy"+"\x0A"+String.fromCharCode(password.length)+password)); | |
udp.send(toSend, 0, toSend.length, server.info.port, server.info.ip); | |
udp.on('message', function (data, rinfo) { | |
if (connections[rinfo.address+":"+rinfo.port] === conn) { | |
var packetId = data[2]; | |
if (packetId === 0x02) { | |
} else if (packetId === 0x07) { // Subpacket/action | |
var i = 4; | |
var packets = []; | |
var ids = []; | |
while (i < data.length) { | |
var id = data[i]; | |
//console.log(id); | |
if (!actionSizes[id] || i+actionSizes[id]+1 > data.length) break; | |
packets.push(data.slice(i, i+actionSizes[id]+1)); | |
i += actionSizes[id] + 1; | |
ids.push(id); | |
} | |
/*if (Array.prototype.indexOf.call(data.slice(4), 0x0E) > -1) { | |
console.log("UDP ACTION: ", Array.prototype.indexOf.call(data.slice(4), 0x0E), data.slice(4)); | |
console.log(ids); | |
}*/ | |
for (var key = 0; key < packets.length; key++) { | |
var data = packets[key]; | |
//console.log(data); | |
var subPacketId = data[0]; | |
//console.log(subPacketId); | |
if (subPacketId === 0x03) { // Object destroyed/despawn | |
} else if (subPacketId === 0x04) { // Player hit | |
//sendChat("unknown packet"); | |
} else if (subPacketId === 0x07) { // Bullet | |
} else if (subPacketId === 0x0E) { // Roast/kill/suicide | |
//console.log("ROAST"); | |
var i = 1; | |
var victimId = data[i++]; | |
var victimPoints = data.readUInt32LE(i); i+=4; | |
var killerId = data[i++]; | |
var killerPoints = data.readUInt32LE(i); i+=4; | |
var victim = server.client.players[victimId]; | |
var killer = server.client.players[killerId]; | |
if (victim && killer) { | |
//sendChat(killerPoints+", "+victimPoints); | |
if (victimId !== killerId) { | |
killer.points = killerPoints; | |
victim.points = victimPoints; | |
//sendChat(killer.name+" roasted "+victim.name); | |
//sendChat(killer.name+' got '+killer.points+' points'); | |
//sendChat(victim.name+' got '+victim.points+' points'); | |
} else { | |
killer.points = killerPoints; | |
//sendChat(killer.name+" ate it"); | |
//sendChat(killer.name+' got '+killer.points+' points'); | |
} | |
} else { | |
//sendChat("Unknown killer/victim player.", killerId, victimId); | |
} | |
} else if (subPacketId === 0x0F) { // Streetfightad | |
var victimId = data[1]; | |
var killerId = data[2]; | |
var victim = server.client.players[victimId]; | |
var killer = server.client.players[killerId]; | |
if (victim && killer) { | |
//sendChat(killer.name+" streetfighted "+victim.name); | |
} | |
} else if (subPacketId === 0x15) { | |
} else if (subPacketId === 0x1E) { | |
//console.log(data); | |
var bluescore = data[5]; | |
var redscore = data[10]; | |
var blueHolder = data[20] > 0 ? data[21] : -1; | |
var redHolder = data[17] > 0 ? data[18] : -1; | |
game.ctf.teams[0].score = bluescore; | |
game.ctf.teams[1].score = redscore; | |
game.ctf.teams[0].holder = blueHolder; | |
game.ctf.teams[1].holder = redHolder; | |
//console.log('score: ', bluescore, redscore, 'holder: ', blueHolder, redHolder); | |
} else { | |
//console.log("GOT ACTION UDP", "0x"+subPacketId.toString(16), data); | |
} | |
} | |
} else if (packetId === 0x09) { // Keep alive (JJ2+) | |
var toSend = data; | |
udpSend(toSend); | |
} else if (packetId === 0x0B) { | |
if (data[3] > 0) { | |
startTCP(); | |
} else if (password) { | |
//console.log("Wrong password '"+password+"' for "+server.info.servername); | |
} | |
} else { | |
//console.log("UDP", data) | |
} | |
} | |
}); | |
var extraPlusData = new Buffer(0); | |
var sendChat = function (msg) { | |
if(server.client.socketId) { | |
var toSend = "\x1B"+String.fromCharCode(server.client.socketId)+"\x20"+msg; | |
toSend = String.fromCharCode(toSend.length+1)+toSend; | |
if(conn.client._handle) { | |
conn.client.write(new Buffer(toSend, "binary")); | |
return true; | |
} | |
} | |
return false; | |
}; | |
var getPlayerId = function (sock) { | |
var result = []; | |
for(var i=0; i < server.client.players.length; i+=1) { | |
if(server.client.players[i] !== undefined && server.client.players[i].socketId === sock) { | |
result.push(i); | |
} | |
} | |
return result; | |
}; | |
function startTCP () { | |
var client = net.connect(server.info.port, server.info.ip); | |
conn.client = client; | |
client.on('connect', function () { | |
clearTimeout(clientTimer); | |
console.log('connected to '+server.info.address); | |
var buf = new Buffer(9); | |
buf.write("\x09\x0F", 0, 'binary'); | |
buf.writeUInt16LE(udpPort, 2); | |
server.info.version.copy(buf, 4); | |
buf.write("\x01", 8, 'binary'); | |
client.write(buf); | |
}); | |
client.on('data', function (res) { | |
// TODO: Clean up the code! | |
var p = 0, j = res.length, res_a = [], l = 0; | |
var special = false; | |
while (p < j) { | |
l = res[p]; | |
if (l === 0 && ++p < j) { | |
l = res[p]; | |
var buf = new Buffer(l+1); | |
buf[0] = l; | |
res.copy(buf, 1, p+2, p+2+l); | |
res_a.push(buf); | |
p += 2; | |
} else { | |
if (p+l > j) { | |
//console.log('oob', p, l, j, res); | |
break; | |
} | |
res_a.push(res.slice(p, p+l)); | |
} | |
p += l; | |
} | |
for (var key = 0; key < res_a.length; key++) { // Loops through packet array | |
var data = res_a[key]; | |
//console.log(data); | |
var packetId = data[1]; | |
if (packetId === 0x0D) { // Disconnect | |
//console.log(data); | |
var type = data[2]; | |
var message = disconnectMessages[type] || ("Unknown error |"+type); | |
var playerid = getPlayerId(data[3]); | |
var reason = data.length > 10 ? data.slice(10).toString('binary') : ""; | |
//console.log("Disconnected:", data[3], type, message, reason && "("+reason+")"); | |
for(var i = 0; i < playerid.length; i++) { | |
if(type !== 7) { | |
sendChat("§1"+server.client.players[playerid[i]].name+" left: |"+message +" |"+(reason && "("+reason+")")); | |
} | |
if (playerid[i] === server.client.playerId) { | |
console.log("The bot was disconnected from", server.info.address, server.info.servername, message, reason && "("+reason+")"); | |
} | |
delete server.client.players[playerid[i]]; | |
} | |
} else if (packetId === 0x10) { // Server info | |
var i = 2; | |
server.client.socketId = data[i++]; | |
server.client.playerId = data[i++]; | |
var l = data[i++]; | |
server.client.levelName = data.slice(i, i + l).toString('binary'); | |
i += l; | |
i+=8; // Skip CRC stuff | |
server.client.game.gamemode = data[i++]; | |
server.client.game.maxscore = data[i++]; | |
if (data.length > i) { | |
extraPlusData = data.slice(i); | |
} else { | |
client.destroy(); | |
connectFailed(); | |
return; | |
} | |
client.write("\x08\x3F\x20\x01\x00\x00\x03\x00"); // JJ2+ New | |
//console.log("info?!1+", data.slice(i)); | |
var toSend = "\x0E\x01"+String.fromCharCode(server.client.playerId)+"\x01\x00"+"SGIP"+BOT_NAME+"\x00"; | |
toSend = String.fromCharCode(toSend.length+1)+toSend; | |
client.write(toSend, 'binary'); | |
} else if(packetId === 0x11) { // Someone joins... | |
var packetLength = data.length; | |
var i=4; // Skip player amount | |
var sockID = data[2]; | |
var playerid; | |
var pname = ""; | |
var fur; | |
var charTeam; | |
while(packetLength>i) { | |
playerid = data[i++]; | |
charTeam = data[i++]; | |
i++; | |
fur = [data[i++], data[i++], data[i++], data[i++]]; | |
pname = ""; | |
while(data[i] != undefined) {pname+=String.fromCharCode(data[i++]);if(data[i]==0) break;} | |
i+=1; | |
server.client.players[playerid] = { | |
charTeam: charTeam, | |
fur: fur, | |
name: pname, | |
socketId: sockID, | |
spectating: false, | |
points: 0, | |
deaths: 0, | |
ping: -1 | |
}; | |
} | |
} else if(packetId === 0x12) { // Playerlist | |
var packetLength = data.length; | |
var totalplayers = 0; | |
var i = 3; | |
while (packetLength>i) { | |
var psock = data[i++]; | |
var pplayer = data[i++]; | |
var pcharTeam = data[i++]; | |
i++; | |
var fur = [data[i++], data[i++], data[i++], data[i++]]; | |
var pname = ""; | |
while(data[i] != undefined) {pname+=String.fromCharCode(data[i++]);if(data[i]==0) break;} | |
i += 1; | |
totalplayers += 1; | |
if (!server.client.players[pplayer]) { | |
server.client.players[pplayer] = { | |
spectating: false, | |
points: 0, | |
deaths: 0, | |
ping: -1 | |
}; | |
} | |
var player = server.client.players[pplayer]; | |
player.name = pname; | |
player.fur = fur; | |
player.charTeam = pcharTeam; | |
player.socketId = psock; | |
//roomBroadcast(JSON.stringify({'console': [(pplayer+1)+". "+pname, 1]}), roomId); | |
} | |
//console.log(server.client.players); | |
//roomBroadcast(JSON.stringify({'console': [Array.prototype.slice.call(data, 2).join(" "), 1]}), roomId); | |
} else if (packetId === 0x13) { | |
if (server.isConnected) { | |
continue; | |
} | |
console.log("Game init", server.info.address, server.info.servername); | |
// ?? | |
//udp.send(new Buffer([0x0A, 0x15, 0x09, 0x00 ]), 0, 4, server.info.port, server.info.ip); | |
var toSend = new Buffer(8); | |
toSend[2] = 0x09; | |
toSend[3] = server.client.socketId; | |
extraPlusData.copy(toSend, 4, 4, 8); | |
toSend = udpchecksum(toSend); | |
udpSend(toSend); | |
toSend = new Buffer(6); | |
toSend[0] = 6; | |
toSend[1] = 0x1A; | |
extraPlusData.copy(toSend, 2, 0, 4); | |
client.write(toSend); | |
client.write("\x03\x42\x21", 'binary'); // Make bot spectate | |
server.isConnected = true; // Important | |
var startTime = Date.now(); | |
var udpTimer = setInterval(function () { | |
var toSend = new Buffer([0, 0, 0x01, 0, server.client.playerId, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); | |
udpSend(toSend); | |
toSend = new Buffer(5+7); | |
toSend.fill(0); | |
toSend[2] = 0x09; | |
toSend[4] = 0x07; | |
toSend[5] = server.client.playerId; | |
toSend[6] = 0x0C; | |
toSend[7] = 0x20; | |
toSend[8] = 0xA0; | |
toSend[9] = 0x01; | |
toSend[10] = 0x00; | |
toSend[11] = 0x02; | |
toSend = udpchecksum(toSend); | |
udpSend(toSend); | |
}, 1000); | |
} else if(packetId === 0x16) { // Level cycle | |
//console.log("CYCLE", data.slice(2)); | |
toSend = new Buffer(6); | |
toSend[0] = 6; | |
toSend[1] = 0x1A; | |
data.copy(toSend, 2, data.length-10, data.length-10+4); | |
//console.log("CYCLE RESPONSE", toSend); | |
client.write(toSend); | |
for (var i = 0; i < server.client.players.length; i++) { | |
var player = server.client.players[i]; | |
if (player) { | |
player.points = 0; | |
player.deaths = 0; | |
//console.log("Reset points/deaths for "+player.name); | |
} | |
} | |
} else if (packetId === 0x17) { // End of level | |
} else if (packetId === 0x18) { // Events update | |
} else if (packetId === 0x1B) { // Chat | |
} else if (packetId === 0x3F) { // Plus packet | |
game.customMode = data[6]; | |
} else if (packetId === 0x40) { // Console message | |
//console.log("Console: "+data.slice(3).toString('binary')); | |
} else if (packetId === 0x41) { | |
//console.log("SPECTATE", data.slice(2)); | |
//sendChat('got spectate packet: '+data[2].toString(2)+' ['+Array.prototype.slice.call(data, 2).join(", ")+']'); | |
var i = 2; | |
var spectatePacketType = data[i++]; | |
if (spectatePacketType === 0) { | |
//sendChat("zepectate: "+data[i++].toString(2)+" "+data[i++].toString(2)+" ["+Array.prototype.slice.call(data, 2).join(", ")+']'); | |
var unknown = data[i++]; | |
var binary = data[i++]; | |
for (var i = 0; i < server.client.players.length; i++) { | |
var player = server.client.players[i]; | |
if (player) { | |
var isSpec = ((binary >> player.socketId) & 1) === 1; | |
if (isSpec !== player.spectating) { | |
if (isSpec) { | |
//sendChat(player.name+" is now spectating"); | |
player.points = 0; | |
player.deaths = 0; | |
//console.log("Reset points/deaths for "+player.name); | |
} else { | |
//sendChat(player.name+" is no longer spectating"); | |
} | |
player.spectating = isSpec; | |
} | |
} | |
} | |
} else if (spectatePacketType === 1) { | |
var spectateType = data[i++]; | |
var clientId = data[i++]; | |
var spectateId = data[i++]; | |
var playerIds = getPlayerId(clientId); | |
//console.log("someone changed spec", data); | |
var isSpec = spectateId !== 0xFE; | |
for (var i = 0; i < playerIds.length; i++) { | |
var player = server.client.players[playerIds[i]]; | |
if (player && isSpec !== player.spectating) { | |
if (isSpec) { | |
//sendChat(player.name+" is spectating"); | |
player.points = 0; | |
player.deaths = 0; | |
} else { | |
//sendChat(player.name+" is no longer spectating"); | |
} | |
player.spectating = isSpec; | |
} | |
} | |
} | |
/*if(data[0] === 5) { | |
console.log("got spec list: "+data[2].toString(2), data.slice(3)); | |
//sendChat('got spec list: '+data[2].toString(2)+' ['+Array.prototype.slice.call(data, 2)+']'); | |
var binary = data[2]; | |
for (var i = 0; i < server.client.players.length; i++) { | |
var player = server.client.players[i]; | |
if (player) { | |
var isSpec = ((binary >> player.socketId) & 1) === 1; | |
console.log(isSpec, player.spectating) | |
if (isSpec !== player.spectating) { | |
if (isSpec) { | |
sendChat(player.name+" is spectating"); | |
} else { | |
sendChat(player.name+" is no longer spectating"); | |
} | |
player.spectating = isSpec; | |
} | |
} | |
} | |
} | |
else { | |
var playerIds = getPlayerId(data[2]); | |
//console.log("someone changed spec", data); | |
var isSpec = data[3] !== 0xFE; | |
for (var i = 0; i < playerIds.length; i++) { | |
var player = server.client.players[playerIds[i]]; | |
if (player && isSpec !== player.spectating) { | |
if (isSpec) { | |
sendChat(player.name+" is spectating"); | |
} else { | |
sendChat(player.name+" is no longer spectating"); | |
} | |
player.spectating = isSpec; | |
} | |
} | |
}*/ | |
} else if (packetId === 0x45) { // Time | |
var gameState = data[2]; | |
var gameStarted = !!(gameState & 1); | |
var inPregame = !((gameState >> 1) & 1) && ((gameState >> 2) & 1); | |
var inOvertime = !!(((gameState >> 1) & 1) && ((gameState >> 2) & 1)); | |
var timeLeft = data.readUInt32LE(3)/1000; | |
var mins = Math.floor(timeLeft / 60); | |
var secs = Math.floor(timeLeft) % 60; | |
game.time.started = gameStarted; | |
game.time.autoCycle = !!((gameState >> 1) & 1); | |
game.time.inPregame = inPregame; | |
game.time.inOvertime = inOvertime; | |
game.time.timeLeft = timeLeft; | |
server.client.lastTimeUpdate = Date.now(); | |
//console.log(timeLeft); | |
//sendChat("§0|"+(gameStarted?(inPregame?"|||":"")+(inOvertime?"||":""):"|")+zfs(mins)+":"+zfs(secs)+" left"); | |
} else if (packetId === 0x49) { // Pings | |
//sendChat("Pings: "+Array.prototype.slice.call(data, 2).join(", ")); | |
var i = 2; | |
while (i < data.length) { | |
var playerId = data[i++]; | |
var ping = data.readUInt16LE(i); i+=2; | |
var player = server.client.players[playerId]; | |
if (player) { | |
player.ping = ping; | |
} | |
} | |
} else if (packetId === 0x4A) { // Gamemode and maxscore | |
var gamemode = data[2]; | |
var customGamemode = data[3]; | |
var maxscore = data[4]; | |
game.gamemode = gamemode; | |
game.maxscore = maxscore; | |
game.customMode = customGamemode; | |
//console.log(customGamemode > 0 ? "Custom gamemode: "+customGamemode : "Gamemode: "+gamemode, "maxscore: "+maxscore) | |
} else if (packetId === 0x4C) { // Player stats | |
var i = 2, len = data.length; | |
while (i < len) { | |
var playerId = data[i++]; | |
var points = data.readUInt32LE(i); i += 4; | |
var deaths = data.readUInt32LE(i); i += 4; | |
var player = server.client.players[playerId]; | |
if (player) { | |
player.points = points; | |
player.deaths = deaths; | |
//sendChat(player.name+' got '+player.points+' points and '+player.deaths+' deaths'); | |
} else { | |
sendChat("Unknown player "+playerId); | |
} | |
} | |
} else if (packetId === 0x4D) { // Deaths | |
//sendChat(Array.prototype.slice.call(data, 2).join(", ")); | |
var i = 2, len = data.length; | |
while (i < len) { | |
var playerId = data[i++]; | |
var deaths = data.readUInt32LE(i); i += 4; | |
var player = server.client.players[playerId]; | |
if (player) { | |
player.deaths = deaths; | |
//sendChat(player.name+' got '+player.deaths+' deaths'); | |
} else { | |
sendChat("Unknown player "+playerId); | |
} | |
} | |
} else if (packetId === 0x50) { // TCP_SC_MISC | |
} else if (packetId === 0x52) { // Idleserver | |
game.isIdleserver = !!data[2]; | |
//sendChat("IDLESERVER IS "+(isIdleserver? "|ENABLED" : "||DISABLED")); | |
} else { | |
//console.log("TCP: 0x"+packetId.toString(16), data.slice(2)); | |
} | |
//console.log("0x"+packetId.toString(16)); | |
} | |
}); | |
client.on('error', function (err) { | |
console.log("client "+err); | |
}); | |
client.on('close', function () { | |
console.log('Disconnected from', server.info.address, server.info.servername); | |
connectFailed(); | |
}); | |
var clientTimer = setTimeout(function () { | |
client.destroy(); | |
}, 3000); | |
} | |
}; | |
var conn = {}; | |
var query = udpchecksum(new Buffer("yy\x05\x00")); | |
conn.udp = dgram.createSocket("udp4"); | |
var udp = conn.udp; | |
var isFirst = true; | |
udp.on('listening', function () { | |
var rinfo = udp.address(); | |
//console.log("Listening on "+rinfo.address+":"+rinfo.port); | |
udpPort = rinfo.port; | |
udp.send(query, 0, query.length, server.info.port, server.info.ip); | |
}); | |
udp.on('message', function (data, rinfo) { | |
if (rinfo.address === server.info.ip && rinfo.port === server.info.port && isFirst) { | |
isFirst = false; | |
clearTimeout(queryTimer); | |
server.info.version = data.slice(8, 8+4); | |
server.info.capacity = [data[12], data[15]]; | |
server.info.gametype = data[14]; | |
server.info.servername = data.slice(17, 17+data[16]).toString('binary'); | |
joinServer(); | |
} | |
}); | |
udp.on('error', function (err) { | |
console.log('UDP '+err) | |
}); | |
udp.bind(); | |
var queryTimer = setTimeout(function () { | |
conn.udp.close(); | |
connectFailed(); | |
}, 3000); | |
connections[server.info.address] = conn; | |
servers.push(server); | |
serverLookup.push(server.info.address); | |
}; | |
function detachServer (index) { | |
if (index > -1) { | |
console.log('Remove server '+servers[index].info.address); | |
var lookupIndex = serverLookup.indexOf(servers[index].info.address); | |
//console.log("indices should be same:", index, lookupIndex); | |
delete connections[servers[index].info.address]; | |
servers.splice(index, 1); | |
serverLookup.splice(lookupIndex, 1); | |
} | |
}; | |
function handleServerlist (list, addresses) { | |
for (var i = 0; i < servers.length; i++) { | |
var server = servers[i]; | |
if(addresses.indexOf(server.info.address) === -1 && !connections[server.info.address]) { | |
// remove/disconnect from server | |
detachServer(i); | |
} else { | |
// server exists, do nothing? | |
} | |
} | |
for (var i = 0; i < list.length; i++) { | |
if (serverLookup.indexOf(addresses[i]) === -1 && !connections[addresses[i]]) { | |
// add/connect to server | |
attachServer(list[i]); | |
} else { | |
// server exists, do nothing? | |
} | |
} | |
}; | |
function scanServerlist() { | |
console.log(new Date()+" Scanning listservers...") | |
var counter = 0; | |
var list = []; | |
var addresses = []; | |
function handleListserver (listserver) { | |
var buffers = ""; | |
var ls = net.connect(10053, listserver); | |
var timeout = setTimeout(function () { | |
console.log(listserver, "timeout"); | |
ls.destroy(); | |
}, 5*1000); | |
ls.on('connect', function () { | |
console.log(listserver, "connected"); | |
}); | |
ls.on('data', function (data) { | |
clearTimeout(timeout); | |
buffers += data.toString('binary'); | |
}); | |
ls.on('error', function (err) { | |
clearTimeout(timeout); | |
console.log(listserver, err); | |
}); | |
ls.on('close', function () { | |
console.log(listserver, "closed"); | |
clearTimeout(timeout); | |
var buffer = new Buffer(buffers, 'binary'); | |
var i = 7; | |
var len = buffer.length; | |
while (i < len) { | |
var l = buffer[i]; | |
var part = buffer.slice(i, i+l); | |
i += l; | |
var ip = [part[4], part[3], part[2], part[1]].join("."); | |
var port = part.readUInt16LE(5); | |
var address = ip+":"+port; | |
var isBlacklisted = !(!database[address] || (database[address] && database[address].blacklistlevel === 0)); | |
if (addresses.indexOf(ip+":"+port) === -1 && !isBlacklisted) { | |
var name = part.slice(7, l).toString('binary'); | |
addresses.push(address); | |
list.push({ip: ip, port: port, servername: name}); | |
} else if (isBlacklisted) { | |
console.log("Access to "+address+" was denied."); | |
} | |
} | |
if (--counter === 0) { | |
console.log("Found "+list.length+" servers"); | |
handleServerlist(list, addresses); | |
setTimeout(function () { | |
refreshDb(function () { | |
scanServerlist(); | |
}) | |
}, 15*60*1000); | |
} | |
}); | |
}; | |
for (var i = 0; i < listservers.length; i++) { | |
counter++; | |
handleListserver(listservers[i]); | |
} | |
}; | |
function startApiServer () { | |
var apiServer = net.createServer(function (client) { | |
client.on('data', function (data) { | |
try { | |
var message = JSON.parse(data.toString('utf8')); | |
} catch (e) { | |
client.end("Invalid JSON"); | |
return; | |
} | |
var toSend = {error: 0}; | |
if (message.ip !== undefined && message.port !== undefined) { | |
//console.log("Got API request"); | |
var index = serverLookup.indexOf(message.ip+":"+message.port); | |
if (index !== -1) { | |
var server = servers[index]; | |
if (server.isConnected) { | |
server.client.game.time.time = server.client.game.time.timeLeft - (server.client.game.time.started? (Date.now() - server.client.lastTimeUpdate)/1000 : 0); | |
toSend.players = server.client.players; | |
toSend.game = server.client.game; | |
} else { | |
toSend.error = 2; | |
} | |
} else { | |
//console.log("No such server: ", message.ip+":"+message.port, serverLookup); | |
toSend.error = 1; | |
} | |
} else if (message.update !== undefined) { | |
refreshDb(); | |
} else { | |
toSend.error = 3; | |
} | |
client.end(JSON.stringify(toSend)); | |
}); | |
client.on('close', function () { | |
//console.log('api client left'); | |
}); | |
client.on('error', function () { | |
}); | |
}); | |
apiServer.listen(8007, 'localhost', function () { | |
console.log('SGIP bot API server listening on localhost:8007'); | |
}); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment