mirror of
https://github.com/smogon/pokemon-showdown.git
synced 2026-05-09 12:36:41 -05:00
- the original names BattleItems etc have been deprecated; please use: - Tools.getItem() accessor method if possible - Tools.data.Items for lower-level access (e.g. iteration) - app.js was also rearranged a bit
277 lines
8.4 KiB
JavaScript
277 lines
8.4 KiB
JavaScript
require('sugar');
|
|
|
|
fs = require('fs');
|
|
path = require('path');
|
|
|
|
request = require('request');
|
|
|
|
// Synchronously copy config-example.js over to config.js if it doesn't exist
|
|
if (!path.existsSync('./config/config.js')) {
|
|
console.log("config.js doesn't exist - creating one with default settings...");
|
|
var BUF_LENGTH, buff, bytesRead, fdr, fdw, pos;
|
|
BUF_LENGTH = 64 * 1024;
|
|
buff = new Buffer(BUF_LENGTH);
|
|
fdr = fs.openSync('./config/config-example.js', 'r');
|
|
fdw = fs.openSync('./config/config.js', 'w');
|
|
bytesRead = 1;
|
|
pos = 0;
|
|
while (bytesRead > 0) {
|
|
bytesRead = fs.readSync(fdr, buff, 0, BUF_LENGTH, pos);
|
|
fs.writeSync(fdw, buff, 0, bytesRead);
|
|
pos += bytesRead;
|
|
}
|
|
fs.closeSync(fdr);
|
|
}
|
|
|
|
config = require('./config/config.js');
|
|
|
|
/*
|
|
var app = require('http').createServer()
|
|
, io = require('socket.io').listen(app)
|
|
, fs = require('fs');
|
|
|
|
function handler (req, res) {
|
|
fs.readFile(__dirname + '/index.html',
|
|
function (err, data) {
|
|
if (err) {
|
|
res.writeHead(500);
|
|
return res.end('Error loading index.html');
|
|
}
|
|
|
|
res.writeHead(200);
|
|
res.end(data);
|
|
});
|
|
}
|
|
|
|
app.listen(8000); */
|
|
|
|
if (process.argv[2] && parseInt(process.argv[2])) {
|
|
config.port = parseInt(process.argv[2]);
|
|
}
|
|
|
|
var io = require('socket.io').listen(config.port).set('log level', 1);
|
|
io.set('transports', ['websocket', 'htmlfile', 'xhr-polling']); // temporary hack until https://github.com/LearnBoost/socket.io/issues/609 is fixed
|
|
console.log("Server started on port "+config.port);
|
|
console.log("Test your server at http://psim.tk/~~localhost:"+config.port);
|
|
|
|
/**
|
|
* Converts anything to an ID. An ID must have only lowercase alphanumeric
|
|
* characters.
|
|
* If a string is passed, it will be converted to lowercase and
|
|
* non-alphanumeric characters will be stripped.
|
|
* If an object with an ID is passed, its ID will be returned.
|
|
* Otherwise, an empty string will be returned.
|
|
*/
|
|
toId = function(text) {
|
|
text = (''+(text||''));
|
|
if (typeof text === 'number') text = ''+text;
|
|
if (text && text.id) text = text.id;
|
|
if (typeof text !== 'string') return ''; //???
|
|
return text.toLowerCase().replace(/[^a-z0-9]+/g, '');
|
|
};
|
|
toUserid = toId;
|
|
|
|
/**
|
|
* Escapes a string for HTML
|
|
* If strEscape is true, escapes it for JavaScript, too
|
|
*/
|
|
sanitize = function(str, strEscape) {
|
|
str = (''+(str||''));
|
|
str = str.escapeHTML();
|
|
if (strEscape) str = str.replace(/'/g, '\\\'');
|
|
return str;
|
|
};
|
|
|
|
/**
|
|
* Converts any variable to an integer (numbers get floored, non-numbers
|
|
* become 0). Then clamps it between min and (optionally) max.
|
|
*/
|
|
clampIntRange = function(num, min, max) {
|
|
if (typeof num !== 'number') num = 0;
|
|
num = Math.floor(num);
|
|
if (num < min) num = min;
|
|
if (typeof max !== 'undefined' && num > max) num = max;
|
|
return num;
|
|
};
|
|
|
|
Data = {};
|
|
Tools = require('./tools.js');
|
|
|
|
Users = require('./users.js');
|
|
|
|
Rooms = require('./rooms.js');
|
|
|
|
parseCommand = require('./chat-commands.js').parseCommand;
|
|
|
|
var sim = require('./simulator.js');
|
|
BattlePokemon = sim.BattlePokemon;
|
|
BattleSide = sim.BattleSide;
|
|
Battle = sim.Battle;
|
|
|
|
lockdown = false;
|
|
|
|
mutedIps = {};
|
|
bannedIps = {};
|
|
nameLockedIps = {};
|
|
|
|
function resolveUser(you, socket) {
|
|
if (!you) {
|
|
socket.emit('connectionError', 'There has been a connection error. Please refresh the page.');
|
|
return false;
|
|
}
|
|
return you.user;
|
|
}
|
|
|
|
if (config.crashguard) {
|
|
// graceful crash - allow current battles to finish before restarting
|
|
process.on('uncaughtException', function (err) {
|
|
console.log("\n"+err.stack+"\n");
|
|
fs.createWriteStream('logs/errors.txt', {'flags': 'a'}).on("open", function(fd) {
|
|
this.write("\n"+err.stack+"\n");
|
|
this.end();
|
|
});
|
|
var stack = (""+err.stack).split("\n").slice(0,2).join("<br />");
|
|
Rooms.lobby.addRaw('<div style="background-color:#BB6655;color:white;padding:2px 4px"><b>THE SERVER HAS CRASHED:</b> '+stack+'<br />Please restart the server.</div>');
|
|
Rooms.lobby.addRaw('<div style="background-color:#BB6655;color:white;padding:2px 4px">You will not be able to talk in the lobby or start new battles until the server restarts.</div>');
|
|
config.modchat = 'crash';
|
|
lockdown = true;
|
|
});
|
|
}
|
|
|
|
io.sockets.on('connection', function (socket) {
|
|
var you = null;
|
|
console.log('INIT SOCKET: '+socket.id);
|
|
|
|
if (socket.handshake && socket.handshake.address && socket.handshake.address.address) {
|
|
if (bannedIps[socket.handshake.address.address]) {
|
|
console.log('IP BANNED: '+socket.handshake.address.address);
|
|
return;
|
|
}
|
|
}
|
|
|
|
socket.on('join', function(data) {
|
|
if (!data || typeof data.room !== 'string' || typeof data.name !== 'string') return;
|
|
if (!you) {
|
|
you = Users.connectUser(data.name, socket, data.token, data.room);
|
|
console.log('JOIN: '+data.name+' => '+you.name+' ['+data.token+']');
|
|
} else {
|
|
var youUser = resolveUser(you, socket);
|
|
if (!youUser) return;
|
|
youUser.joinRoom(data.room, socket);
|
|
}
|
|
});
|
|
socket.on('rename', function(data) {
|
|
if (!data) return;
|
|
data.name = ''+data.name;
|
|
var youUser = resolveUser(you, socket);
|
|
if (!youUser) return;
|
|
youUser.rename(data.name, data.token, data.auth);
|
|
});
|
|
socket.on('chat', function(message) {
|
|
if (!message || typeof message.room !== 'string' || typeof message.message !== 'string') return;
|
|
var youUser = resolveUser(you, socket);
|
|
if (!youUser) return;
|
|
var room = Rooms.get(message.room);
|
|
youUser.chat(message.message, room, socket);
|
|
});
|
|
socket.on('leave', function(data) {
|
|
if (!data || typeof data.room !== 'string') return;
|
|
var youUser = resolveUser(you, socket);
|
|
if (!youUser) return;
|
|
youUser.leaveRoom(Rooms.get(data.room), socket);
|
|
});
|
|
socket.on('leaveBattle', function(data) {
|
|
if (!data || typeof data.room !== 'string') return;
|
|
var youUser = resolveUser(you, socket);
|
|
if (!youUser) return;
|
|
var room = Rooms.get(data.room);
|
|
if (room.leaveBattle) room.leaveBattle(youUser);
|
|
});
|
|
socket.on('joinBattle', function(data) {
|
|
if (!data || typeof data.room !== 'string') return;
|
|
var youUser = resolveUser(you, socket);
|
|
if (!youUser) return;
|
|
var room = Rooms.get(data.room);
|
|
if (room.joinBattle) room.joinBattle(youUser);
|
|
});
|
|
|
|
socket.on('command', function(data) {
|
|
if (!data || typeof data.room !== 'string') return;
|
|
var youUser = resolveUser(you, socket);
|
|
if (!youUser) return;
|
|
parseCommand(youUser, 'command', data, Rooms.get(data.room), socket);
|
|
});
|
|
socket.on('disconnect', function() {
|
|
var youUser = resolveUser(you, socket);
|
|
if (!youUser) return;
|
|
youUser.disconnect(socket);
|
|
});
|
|
socket.on('challenge', function(data) {
|
|
if (!data) return;
|
|
var youUser = resolveUser(you, socket);
|
|
if (!youUser) return;
|
|
console.log('CHALLENGE: '+youUser.name+' => '+data.userid+' ('+data.act+')');
|
|
switch (data.act) {
|
|
case 'make':
|
|
if (typeof data.format !== 'string') data.format = 'debugmode';
|
|
if (typeof data.userid !== 'string') return;
|
|
var problems = Tools.validateTeam(youUser.team, data.format);
|
|
if (problems) {
|
|
socket.emit('message', "Your team was rejected for the following reasons:\n\n- "+problems.join("\n- "));
|
|
return;
|
|
}
|
|
if (!Users.get(data.userid) || !Users.get(data.userid).connected) {
|
|
socket.emit('message', "The user '"+data.userid+"' was not found.");
|
|
}
|
|
youUser.makeChallenge(data.userid, data.format);
|
|
break;
|
|
case 'cancel':
|
|
youUser.cancelChallengeTo(data.userid);
|
|
break;
|
|
case 'accept':
|
|
if (typeof data.userid !== 'string') return;
|
|
var format = 'debugmode';
|
|
if (youUser.challengesFrom[data.userid]) format = youUser.challengesFrom[data.userid].format;
|
|
var problems = Tools.validateTeam(youUser.team, format);
|
|
if (problems) {
|
|
socket.emit('message', "Your team was rejected for the following reasons:\n\n- "+problems.join("\n- "));
|
|
return;
|
|
}
|
|
youUser.acceptChallengeFrom(data.userid);
|
|
break;
|
|
case 'reject':
|
|
if (typeof data.userid !== 'string') return;
|
|
youUser.rejectChallengeFrom(data.userid);
|
|
break;
|
|
}
|
|
});
|
|
socket.on('decision', function(data) {
|
|
if (!data) return;
|
|
var youUser = resolveUser(you, socket);
|
|
if (!youUser) return;
|
|
var room = Rooms.get(data.room);
|
|
switch (data.choice) {
|
|
case 'move':
|
|
case 'switch':
|
|
case 'undo':
|
|
case 'team':
|
|
if (room.decision) room.decision(youUser,data.choice,data.move);
|
|
break;
|
|
case 'search':
|
|
if (data.search) {
|
|
if (typeof data.format !== 'string') return;
|
|
if (room.searchBattle) room.searchBattle(youUser, data.format);
|
|
} else {
|
|
if (room.cancelSearch) room.cancelSearch(youUser);
|
|
}
|
|
break;
|
|
}
|
|
});
|
|
socket.on('saveTeam', function(data) {
|
|
if (!data) return;
|
|
var youUser = resolveUser(you, socket);
|
|
if (!youUser) return;
|
|
youUser.team = data.team;
|
|
youUser.emit('update', {team: 'saved', room: 'teambuilder'});
|
|
});
|
|
}); |