server.js 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146
  1. /*jslint bitwise: true, node: true */
  2. 'use strict';
  3. var express = require('express');
  4. var app = express();
  5. var http = require('http').Server(app);
  6. var io = require('socket.io')(http);
  7. var SAT = require('sat');
  8. var sql = require ("mysql");
  9. // Import game settings.
  10. var c = require('../../config.json');
  11. // Import utilities.
  12. var util = require('./lib/util');
  13. // Import quadtree.
  14. var quadtree = require('simple-quadtree');
  15. //call sqlinfo
  16. var s = c.sqlinfo;
  17. var tree = quadtree(0, 0, c.gameWidth, c.gameHeight);
  18. var users = [];
  19. var massFood = [];
  20. var food = [];
  21. var virus = [];
  22. var mothercell = [];
  23. var mothercellFood = [];
  24. var foodStructSpiral = [];
  25. var foodStructSpiralFood = [];
  26. var foodStructDotCircle = [];
  27. var foodStructDotCircleFood = [];
  28. var sockets = {};
  29. var leaderboard = [];
  30. var leaderboardChanged = false;
  31. var V = SAT.Vector;
  32. var C = SAT.Circle;
  33. if(s.host !== "DEFAULT") {
  34. var pool = sql.createConnection({
  35. host: s.host,
  36. user: s.user,
  37. password: s.password,
  38. database: s.database
  39. });
  40. //log sql errors
  41. pool.connect(function(err){
  42. if (err){
  43. console.log (err);
  44. }
  45. });
  46. }
  47. var initMassLog = util.log(c.defaultPlayerMass, c.slowBase);
  48. app.use(express.static(__dirname + '/../client'));
  49. function addFood(toAdd) {
  50. while (toAdd--) {
  51. var mass = util.randomInRange(c.foodMass.from,c.foodMass.to);
  52. var radius = util.massToRadius(mass);
  53. var position = c.foodUniformDisposition ? util.uniformPosition(food, radius) : util.randomPosition(radius);
  54. food.push({
  55. // Make IDs unique.
  56. id: ((new Date()).getTime() + '' + food.length) >>> 0,
  57. x: position.x,
  58. y: position.y,
  59. radius: radius,
  60. mass: mass,
  61. hue: Math.round(Math.random() * 360)
  62. });
  63. }
  64. }
  65. function addVirus(toAdd) {
  66. while (toAdd--) {
  67. var mass = util.randomInRange(c.virus.defaultMass.from, c.virus.defaultMass.to, true);
  68. var radius = util.massToRadius(mass);
  69. var position = c.virusUniformDisposition ? util.uniformPosition(virus, radius) : util.randomPosition(radius);
  70. virus.push({
  71. id: ((new Date()).getTime() + '' + virus.length) >>> 0,
  72. x: position.x,
  73. y: position.y,
  74. radius: radius,
  75. mass: mass,
  76. });
  77. }
  78. }
  79. function addMothercell(toAdd) {
  80. while (toAdd--) {
  81. var mass = util.randomInRange(c.mothercell.defaultMass.from, c.mothercell.defaultMass.to, true);
  82. var radius = util.massToRadius(mass);
  83. var position = c.mothercellUniformDisposition ? util.uniformPosition(mothercell, radius) : util.randomPosition(radius);
  84. mothercell.push({
  85. id: ((new Date()).getTime() + '' + mothercell.length) >>> 0,
  86. x: position.x,
  87. y: position.y,
  88. radius: radius,
  89. mass: mass,
  90. });
  91. }
  92. }
  93. function mothercellAddFood(mCell,toAdd,limit=true) {
  94. var currentCells = mothercellFood.map(function(thisFood) {
  95. return thisFood.for == this.id;
  96. },mCell).length;
  97. if(!limit || currentCells+toAdd <= c.mothercell.foodLimit) {
  98. var radius = util.massToRadius(1);
  99. var distance = util.randomInRange(mCell.radius+25,mCell.radius+25+(c.mothercell.foodLimit/5));
  100. var position = util.relativePosition({x:mCell.x,y:mCell.y},distance);
  101. mothercellFood.push({
  102. // Make IDs unique.
  103. id: ((new Date()).getTime() + '' + mothercellFood.length) >>> 0,
  104. x: position.x,
  105. y: position.y,
  106. radius: radius,
  107. mass: 1,
  108. hue: Math.round(Math.random() * 360),
  109. for: mCell.id
  110. });
  111. if(toAdd > 1) setTimeout(function() {mothercellAddFood(mCell,toAdd-1,limit);},100);
  112. }
  113. }
  114. function addFoodStructSpiral(toAdd) {
  115. var radius = util.massToRadius(c.foodStruct.spiral.centerMass);
  116. var foodRadius = util.massToRadius(c.foodStruct.spiral.borderMass);
  117. while (toAdd--) {
  118. var position = c.foodUniformDisposition ? util.uniformPosition(foodStructSpiral, radius) : util.randomPosition(radius);
  119. var hue = Math.round(Math.random() * 360);
  120. foodStructSpiral.push({
  121. // Make IDs unique.
  122. id: ((new Date()).getTime() + '' + foodStructSpiral.length) >>> 0,
  123. x: position.x,
  124. y: position.y,
  125. radius: radius,
  126. mass: c.foodStruct.spiral.centerMass,
  127. hue: hue
  128. });
  129. var addSpiralDots = 20;
  130. for(var i=0;i<addSpiralDots/2;i++) {
  131. var aFoodPosition = util.relativePosition(position,radius+15+(i*3),foodRadius*1.7*i-90);
  132. foodStructSpiralFood.push({
  133. // Make IDs unique.
  134. id: ((new Date()).getTime() + '' + foodStructSpiralFood.length) >>> 0,
  135. x: aFoodPosition.x,
  136. y: aFoodPosition.y,
  137. radius: foodRadius,
  138. mass: c.foodStruct.spiral.borderMass,
  139. hue: hue+(i*3)
  140. });
  141. }
  142. for(i=0;i<addSpiralDots/2;i++) {
  143. var bFoodPosition = util.relativePosition(position,radius+15+(i*3),foodRadius*1.7*i+90);
  144. foodStructSpiralFood.push({
  145. // Make IDs unique.
  146. id: ((new Date()).getTime() + '' + foodStructSpiralFood.length) >>> 0,
  147. x: bFoodPosition.x,
  148. y: bFoodPosition.y,
  149. radius: foodRadius,
  150. mass: c.foodStruct.spiral.borderMass,
  151. hue: hue-(i*3)
  152. });
  153. }
  154. }
  155. }
  156. function addFoodStructDotCircle(toAdd) {
  157. var radius = util.massToRadius(c.foodStruct.dotCircle.centerMass);
  158. var dotRadius = util.massToRadius(c.foodStruct.dotCircle.borderDotMass);
  159. var foodRadius = util.massToRadius(c.foodStruct.dotCircle.borderMass);
  160. while (toAdd--) {
  161. var position = c.foodUniformDisposition ? util.uniformPosition(foodStructDotCircle, radius) : util.randomPosition(radius);
  162. var dotHue = Math.round(Math.random() * 360);
  163. var foodHue = Math.round(Math.random() * 360);
  164. foodStructDotCircle.push({
  165. // Make IDs unique.
  166. id: ((new Date()).getTime() + '' + foodStructDotCircle.length) >>> 0,
  167. x: position.x,
  168. y: position.y,
  169. radius: radius,
  170. mass: c.foodStruct.dotCircle.centerMass,
  171. hue: Math.round(Math.random() * 360)
  172. });
  173. var addBorderDots = 25;
  174. var addBorderFood = 5;
  175. for(var i=0;i<addBorderDots;i++) {
  176. var dotPosition = util.relativePosition(position,radius+15,(360/addBorderDots*i)-90);
  177. foodStructDotCircleFood.push({
  178. // Make IDs unique.
  179. id: ((new Date()).getTime() + '' + foodStructDotCircleFood.length) >>> 0,
  180. x: dotPosition.x,
  181. y: dotPosition.y,
  182. radius: dotRadius,
  183. mass: c.foodStruct.dotCircle.borderDotMass,
  184. hue: dotHue
  185. });
  186. }
  187. for(i=0;i<addBorderFood;i++) {
  188. var foodPosition = util.relativePosition(position,radius+15,(360/addBorderFood*i)-90);
  189. foodStructDotCircleFood.push({
  190. // Make IDs unique.
  191. id: ((new Date()).getTime() + '' + foodStructDotCircleFood.length) >>> 0,
  192. x: foodPosition.x,
  193. y: foodPosition.y,
  194. radius: foodRadius,
  195. mass: c.foodStruct.dotCircle.borderMass,
  196. hue: foodHue
  197. });
  198. }
  199. }
  200. }
  201. function removeFood(toRem) {
  202. while (toRem--) {
  203. food.pop();
  204. }
  205. }
  206. function movePlayer(player) {
  207. var x =0,y =0;
  208. for(var i=0; i<player.cells.length; i++)
  209. {
  210. var target = {
  211. x: player.x - player.cells[i].x + player.target.x,
  212. y: player.y - player.cells[i].y + player.target.y
  213. };
  214. var dist = Math.sqrt(Math.pow(target.y, 2) + Math.pow(target.x, 2));
  215. var deg = Math.atan2(target.y, target.x);
  216. var slowDown = 1;
  217. if(player.cells[i].speed <= 6.25) {
  218. slowDown = util.log(player.cells[i].mass, c.slowBase) - initMassLog + 1;
  219. }
  220. var deltaY = player.cells[i].speed * Math.sin(deg)/ slowDown;
  221. var deltaX = player.cells[i].speed * Math.cos(deg)/ slowDown;
  222. if(player.cells[i].speed > 6.25) {
  223. player.cells[i].speed -= 0.5;
  224. }
  225. if (dist < (50 + player.cells[i].radius)) {
  226. deltaY *= dist / (50 + player.cells[i].radius);
  227. deltaX *= dist / (50 + player.cells[i].radius);
  228. }
  229. if (!isNaN(deltaY)) {
  230. player.cells[i].y += deltaY;
  231. }
  232. if (!isNaN(deltaX)) {
  233. player.cells[i].x += deltaX;
  234. }
  235. // Find best solution.
  236. for(var j=0; j<player.cells.length; j++) {
  237. if(j != i && player.cells[i] !== undefined) {
  238. var distance = Math.sqrt(Math.pow(player.cells[j].y-player.cells[i].y,2) + Math.pow(player.cells[j].x-player.cells[i].x,2));
  239. var radiusTotal = (player.cells[i].radius + player.cells[j].radius);
  240. if(distance < radiusTotal) {
  241. if(player.lastSplit > new Date().getTime() - 1000 * c.mergeTimer) {
  242. if(player.cells[i].x < player.cells[j].x) {
  243. player.cells[i].x--;
  244. } else if(player.cells[i].x > player.cells[j].x) {
  245. player.cells[i].x++;
  246. }
  247. if(player.cells[i].y < player.cells[j].y) {
  248. player.cells[i].y--;
  249. } else if((player.cells[i].y > player.cells[j].y)) {
  250. player.cells[i].y++;
  251. }
  252. }
  253. else if(distance < radiusTotal / 1.75) {
  254. player.cells[i].mass += player.cells[j].mass;
  255. player.cells[i].radius = util.massToRadius(player.cells[i].mass);
  256. player.cells.splice(j, 1);
  257. }
  258. }
  259. }
  260. }
  261. if(player.cells.length > i) {
  262. var borderCalc = player.cells[i].radius / 3;
  263. if (player.cells[i].x > c.gameWidth - borderCalc) {
  264. player.cells[i].x = c.gameWidth - borderCalc;
  265. }
  266. if (player.cells[i].y > c.gameHeight - borderCalc) {
  267. player.cells[i].y = c.gameHeight - borderCalc;
  268. }
  269. if (player.cells[i].x < borderCalc) {
  270. player.cells[i].x = borderCalc;
  271. }
  272. if (player.cells[i].y < borderCalc) {
  273. player.cells[i].y = borderCalc;
  274. }
  275. x += player.cells[i].x;
  276. y += player.cells[i].y;
  277. }
  278. }
  279. player.x = x/player.cells.length;
  280. player.y = y/player.cells.length;
  281. }
  282. function moveMass(mass) {
  283. var deg = Math.atan2(mass.target.y, mass.target.x);
  284. var deltaY = mass.speed * Math.sin(deg);
  285. var deltaX = mass.speed * Math.cos(deg);
  286. mass.speed -= 0.5;
  287. if(mass.speed < 0) {
  288. mass.speed = 0;
  289. }
  290. if (!isNaN(deltaY)) {
  291. mass.y += deltaY;
  292. }
  293. if (!isNaN(deltaX)) {
  294. mass.x += deltaX;
  295. }
  296. var borderCalc = mass.radius + 5;
  297. if (mass.x > c.gameWidth - borderCalc) {
  298. mass.x = c.gameWidth - borderCalc;
  299. }
  300. if (mass.y > c.gameHeight - borderCalc) {
  301. mass.y = c.gameHeight - borderCalc;
  302. }
  303. if (mass.x < borderCalc) {
  304. mass.x = borderCalc;
  305. }
  306. if (mass.y < borderCalc) {
  307. mass.y = borderCalc;
  308. }
  309. }
  310. function balanceMass() {
  311. var totalMass = food.length * (c.foodMass.from+c.foodMass.to)/2 +
  312. users
  313. .map(function(u) {return u.massTotal; })
  314. .reduce(function(pu,cu) { return pu+cu;}, 0);
  315. var massDiff = c.gameMass - totalMass;
  316. var maxFoodDiff = c.maxFood - food.length;
  317. var foodDiff = parseInt(massDiff / (c.foodMass.from+c.foodMass.to)/2) - maxFoodDiff;
  318. var foodToAdd = Math.min(foodDiff, maxFoodDiff);
  319. var foodToRemove = -Math.max(foodDiff, maxFoodDiff);
  320. if (foodToAdd > 0) {
  321. //console.log('[DEBUG] Adding ' + foodToAdd + ' food to level!');
  322. addFood(foodToAdd);
  323. //console.log('[DEBUG] Mass rebalanced!');
  324. }
  325. else if (foodToRemove > 0) {
  326. //console.log('[DEBUG] Removing ' + foodToRemove + ' food from level!');
  327. removeFood(foodToRemove);
  328. //console.log('[DEBUG] Mass rebalanced!');
  329. }
  330. var virusToAdd = c.maxVirus - virus.length;
  331. if (virusToAdd > 0) {
  332. addVirus(virusToAdd);
  333. }
  334. var mothercellToAdd = c.maxMothercell - mothercell.length;
  335. if (mothercellToAdd > 0) {
  336. addMothercell(mothercellToAdd);
  337. }
  338. var foodStructSpiralToAdd = c.foodStruct.spiral.limit - foodStructSpiral.length;
  339. if (foodStructSpiralToAdd > 0) {
  340. addFoodStructSpiral(foodStructSpiralToAdd);
  341. }
  342. var foodStructDotCircleToAdd = c.foodStruct.dotCircle.limit - foodStructDotCircle.length;
  343. if (foodStructDotCircleToAdd > 0) {
  344. addFoodStructDotCircle(foodStructDotCircleToAdd);
  345. }
  346. }
  347. io.on('connection', function (socket) {
  348. console.log('A user connected!', socket.handshake.query.type);
  349. var type = socket.handshake.query.type;
  350. var radius = util.massToRadius(c.defaultPlayerMass);
  351. var position = c.newPlayerInitialPosition == 'farthest' ? util.uniformPosition(users, radius) : util.randomPosition(radius);
  352. var cells = [];
  353. var massTotal = 0;
  354. if(type === 'player') {
  355. cells = [{
  356. mass: c.defaultPlayerMass,
  357. x: position.x,
  358. y: position.y,
  359. radius: radius
  360. }];
  361. massTotal = c.defaultPlayerMass;
  362. }
  363. var currentPlayer = {
  364. id: socket.id,
  365. x: position.x,
  366. y: position.y,
  367. w: c.defaultPlayerMass,
  368. h: c.defaultPlayerMass,
  369. cells: cells,
  370. massTotal: massTotal,
  371. hue: Math.round(Math.random() * 360),
  372. type: type,
  373. lastHeartbeat: new Date().getTime(),
  374. target: {
  375. x: 0,
  376. y: 0
  377. }
  378. };
  379. socket.on('gotit', function (player) {
  380. player.name = player.name.substr(0,25);
  381. console.log('[INFO] Player ' + player.name + ' connecting!');
  382. if (util.findIndex(users, player.id) > -1) {
  383. console.log('[INFO] Player ID is already connected, kicking.');
  384. socket.disconnect();
  385. } else if (!util.validNick(player.name)) {
  386. socket.emit('kick', 'Invalid username.');
  387. socket.disconnect();
  388. } else {
  389. console.log('[INFO] Player ' + player.name + ' connected!');
  390. sockets[player.id] = socket;
  391. var radius = util.massToRadius(c.defaultPlayerMass);
  392. var position = c.newPlayerInitialPosition == 'farthest' ? util.uniformPosition(users, radius) : util.randomPosition(radius);
  393. player.x = position.x;
  394. player.y = position.y;
  395. player.target.x = 0;
  396. player.target.y = 0;
  397. if(type === 'player') {
  398. player.cells = [{
  399. mass: c.defaultPlayerMass,
  400. x: position.x,
  401. y: position.y,
  402. radius: radius
  403. }];
  404. player.massTotal = c.defaultPlayerMass;
  405. }
  406. else {
  407. player.cells = [];
  408. player.massTotal = 0;
  409. }
  410. player.hue = Math.round(Math.random() * 360);
  411. currentPlayer = player;
  412. currentPlayer.lastHeartbeat = new Date().getTime();
  413. users.push(currentPlayer);
  414. io.emit('playerJoin', { name: currentPlayer.name });
  415. socket.emit('gameSetup', {
  416. gameWidth: c.gameWidth,
  417. gameHeight: c.gameHeight,
  418. virus:{
  419. fill: c.virus.fill,
  420. stroke: c.virus.stroke,
  421. strokeWidth: c.virus.strokeWidth
  422. },
  423. mothercell:{
  424. fill: c.mothercell.fill,
  425. stroke: c.mothercell.stroke,
  426. strokeWidth: c.mothercell.strokeWidth
  427. }
  428. });
  429. console.log('Total players: ' + users.length);
  430. }
  431. });
  432. socket.on('pingcheck', function () {
  433. socket.emit('pongcheck');
  434. });
  435. socket.on('windowResized', function (data) {
  436. currentPlayer.screenWidth = data.screenWidth;
  437. currentPlayer.screenHeight = data.screenHeight;
  438. });
  439. socket.on('respawn', function () {
  440. if (util.findIndex(users, currentPlayer.id) > -1)
  441. users.splice(util.findIndex(users, currentPlayer.id), 1);
  442. socket.emit('welcome', currentPlayer);
  443. console.log('[INFO] User ' + currentPlayer.name + ' respawned!');
  444. });
  445. socket.on('disconnect', function () {
  446. if (util.findIndex(users, currentPlayer.id) > -1)
  447. users.splice(util.findIndex(users, currentPlayer.id), 1);
  448. console.log('[INFO] User ' + currentPlayer.name + ' disconnected!');
  449. socket.broadcast.emit('playerDisconnect', { name: currentPlayer.name });
  450. });
  451. socket.on('playerChat', function(data) {
  452. if (c.logChat === 1) {
  453. console.log('[CHAT] [' + (new Date()).getHours() + ':' + (new Date()).getMinutes() + '] ' + currentPlayer.name + ': ' + data);
  454. }
  455. socket.broadcast.emit('serverSendPlayerChat', {sender: currentPlayer.name, hue: currentPlayer.hue, message: data});
  456. });
  457. socket.on('pass', function(data) {
  458. if (data[0] === c.adminPass) {
  459. console.log('[ADMIN] ' + currentPlayer.name + ' just logged in as an admin!');
  460. socket.emit('serverMSG', 'Welcome back ' + currentPlayer.name);
  461. socket.broadcast.emit('serverMSG', currentPlayer.name + ' just logged in as admin!');
  462. currentPlayer.admin = true;
  463. } else {
  464. // TODO: Actually log incorrect passwords.
  465. console.log('[ADMIN] ' + currentPlayer.name + ' attempted to log in with incorrect password.');
  466. socket.emit('serverMSG', 'Password incorrect, attempt logged.');
  467. //pool.query('INSERT INTO logging SET name=' + currentPlayer.name + ', reason="Invalid login attempt as admin"');
  468. }
  469. });
  470. socket.on('whisper', function(data) {
  471. var userFound = false;
  472. if(data.length > 1) {
  473. for(var e = 0; e < users.length; e++) {
  474. if(currentPlayer.name === data[0]) {
  475. socket.emit('serverMSG', 'You cannot whisper to yourself.');
  476. return;
  477. }
  478. if(users[e].name === data[0]) {
  479. userFound = true;
  480. var message = "";
  481. for(var f = 1; f < data.length; f++)
  482. message = message + data[f] + " ";
  483. sockets[users[e].id].emit('serverSendPlayerChat', {sender: currentPlayer.name, hue: currentPlayer.hue, message: message, prefix: 'WHISPER <-'});
  484. socket.emit('serverSendPlayerChat', {sender: currentPlayer.name, hue: currentPlayer.hue, message: message, prefix: 'WHISPER ->'});
  485. }
  486. }
  487. if(!userFound) {
  488. socket.emit('serverMSG', 'This user is not online.');
  489. }
  490. } else {
  491. socket.emit('serverMSG', 'Please enter a name and a message.');
  492. }
  493. });
  494. socket.on('kick', function(data) {
  495. if (currentPlayer.admin) {
  496. var reason = '';
  497. var worked = false;
  498. for (var e = 0; e < users.length; e++) {
  499. if (users[e].name === data[0] && !users[e].admin && !worked) {
  500. if (data.length > 1) {
  501. for (var f = 1; f < data.length; f++) {
  502. if (f === data.length) {
  503. reason = reason + data[f];
  504. }
  505. else {
  506. reason = reason + data[f] + ' ';
  507. }
  508. }
  509. }
  510. if (reason !== '') {
  511. console.log('[ADMIN] User ' + users[e].name + ' kicked successfully by ' + currentPlayer.name + ' for reason ' + reason);
  512. }
  513. else {
  514. console.log('[ADMIN] User ' + users[e].name + ' kicked successfully by ' + currentPlayer.name);
  515. }
  516. socket.emit('serverMSG', 'User ' + users[e].name + ' was kicked by ' + currentPlayer.name);
  517. sockets[users[e].id].emit('kick', reason);
  518. sockets[users[e].id].disconnect();
  519. users.splice(e, 1);
  520. worked = true;
  521. }
  522. }
  523. if (!worked) {
  524. socket.emit('serverMSG', 'Could not locate user or user is an admin.');
  525. }
  526. } else {
  527. console.log('[ADMIN] ' + currentPlayer.name + ' is trying to use -kick but isn\'t an admin.');
  528. socket.emit('serverMSG', 'You are not permitted to use this command.');
  529. }
  530. });
  531. // Heartbeat function, update everytime.
  532. socket.on('0', function(target) {
  533. currentPlayer.lastHeartbeat = new Date().getTime();
  534. if (target.x !== currentPlayer.x || target.y !== currentPlayer.y) {
  535. currentPlayer.target = target;
  536. }
  537. });
  538. socket.on('1', function() {
  539. // Fire food.
  540. for(var i=0; i<currentPlayer.cells.length; i++)
  541. {
  542. if(((currentPlayer.cells[i].mass >= c.defaultPlayerMass + c.fireFood) && c.fireFood > 0) || (currentPlayer.cells[i].mass >= 20 && c.fireFood === 0)){
  543. var masa = 1;
  544. if(c.fireFood > 0)
  545. masa = c.fireFood;
  546. else
  547. masa = currentPlayer.cells[i].mass*0.1;
  548. currentPlayer.cells[i].mass -= masa;
  549. currentPlayer.massTotal -=masa;
  550. massFood.push({
  551. id: currentPlayer.id,
  552. num: i,
  553. masa: masa,
  554. hue: currentPlayer.hue,
  555. target: {
  556. x: currentPlayer.x - currentPlayer.cells[i].x + currentPlayer.target.x,
  557. y: currentPlayer.y - currentPlayer.cells[i].y + currentPlayer.target.y
  558. },
  559. x: currentPlayer.cells[i].x,
  560. y: currentPlayer.cells[i].y,
  561. radius: util.massToRadius(masa),
  562. speed: 25
  563. });
  564. }
  565. }
  566. });
  567. socket.on('2', function() {
  568. function splitCell(cell) {
  569. if(cell && cell.mass && cell.mass >= c.defaultPlayerMass*2) {
  570. cell.mass = cell.mass/2;
  571. cell.radius = util.massToRadius(cell.mass);
  572. currentPlayer.cells.push({
  573. mass: cell.mass,
  574. x: cell.x,
  575. y: cell.y,
  576. radius: cell.radius,
  577. speed: 25
  578. });
  579. }
  580. }
  581. if(currentPlayer.cells.length < c.limitSplit && currentPlayer.massTotal >= c.defaultPlayerMass*2) {
  582. //Split all cells
  583. if(currentPlayer.cells.length < c.limitSplit && currentPlayer.massTotal >= c.defaultPlayerMass*2) {
  584. var numMax = currentPlayer.cells.length;
  585. for(var d=0; d<numMax; d++) {
  586. splitCell(currentPlayer.cells[d]);
  587. }
  588. }
  589. currentPlayer.lastSplit = new Date().getTime();
  590. }
  591. });
  592. });
  593. function tickPlayer(currentPlayer) {
  594. if(currentPlayer.lastHeartbeat < new Date().getTime() - c.maxHeartbeatInterval) {
  595. sockets[currentPlayer.id].emit('kick', 'Last heartbeat received over ' + c.maxHeartbeatInterval + ' ago.');
  596. sockets[currentPlayer.id].disconnect();
  597. }
  598. movePlayer(currentPlayer);
  599. function funcFood(f) {
  600. return SAT.pointInCircle(new V(f.x, f.y), playerCircle);
  601. }
  602. function deleteFood(f) {
  603. if(currentCell.mass > food[f].mass) {
  604. currentCell.mass += food[f].mass;
  605. currentPlayer.massTotal += food[f].mass;
  606. food.splice(f, 1);
  607. }
  608. }
  609. function deleteMothercellFood(f) {
  610. if(currentCell.mass > mothercellFood[f].mass) {
  611. currentCell.mass += mothercellFood[f].mass;
  612. currentPlayer.massTotal += mothercellFood[f].mass;
  613. mothercellFood.splice(f, 1);
  614. }
  615. }
  616. function deleteFoodStructSpiral(f) {
  617. if(currentCell.mass > foodStructSpiral[f].mass) {
  618. currentCell.mass += foodStructSpiral[f].mass;
  619. currentPlayer.massTotal += foodStructSpiral[f].mass;
  620. foodStructSpiral.splice(f, 1);
  621. }
  622. }
  623. function deleteFoodStructSpiralFood(f) {
  624. if(currentCell.mass > foodStructSpiralFood[f].mass) {
  625. currentCell.mass += foodStructSpiralFood[f].mass;
  626. currentPlayer.massTotal += foodStructSpiralFood[f].mass;
  627. foodStructSpiralFood.splice(f, 1);
  628. }
  629. }
  630. function deleteFoodStructDotCircle(f) {
  631. if(currentCell.mass > foodStructDotCircle[f].mass) {
  632. currentCell.mass += foodStructDotCircle[f].mass;
  633. currentPlayer.massTotal += foodStructDotCircle[f].mass;
  634. foodStructDotCircle.splice(f, 1);
  635. }
  636. }
  637. function deleteFoodStructDotCircleFood(f) {
  638. if(currentCell.mass > foodStructDotCircleFood[f].mass) {
  639. currentCell.mass += foodStructDotCircleFood[f].mass;
  640. currentPlayer.massTotal += foodStructDotCircleFood[f].mass;
  641. foodStructDotCircleFood.splice(f, 1);
  642. }
  643. }
  644. function eatMass(m) {
  645. if(SAT.pointInCircle(new V(m.x, m.y), playerCircle)){
  646. if(m.id == currentPlayer.id && m.speed > 0 && z == m.num)
  647. return false;
  648. if(currentCell.mass > m.masa * 1.1)
  649. return true;
  650. }
  651. return false;
  652. }
  653. function check(user) {
  654. for(var i=0; i<user.cells.length; i++) {
  655. if(user.cells[i].mass > 10 && user.id !== currentPlayer.id) {
  656. var response = new SAT.Response();
  657. var collided = SAT.testCircleCircle(playerCircle,
  658. new C(new V(user.cells[i].x, user.cells[i].y), user.cells[i].radius),
  659. response);
  660. if (collided) {
  661. response.aUser = currentCell;
  662. response.bUser = {
  663. id: user.id,
  664. name: user.name,
  665. x: user.cells[i].x,
  666. y: user.cells[i].y,
  667. num: i,
  668. mass: user.cells[i].mass
  669. };
  670. playerCollisions.push(response);
  671. }
  672. }
  673. }
  674. return true;
  675. }
  676. function checkMothercell(mCell) {
  677. var response = new SAT.Response();
  678. var collided = SAT.testCircleCircle(playerCircle,
  679. new C(new V(mCell.x, mCell.y), mCell.radius),
  680. response);
  681. if (collided) {
  682. response.bUser = {
  683. id: currentPlayer.id,
  684. name: currentPlayer.name,
  685. x: currentCell.x,
  686. y: currentCell.y,
  687. mass: currentCell.mass,
  688. num: currentPlayer.cells.indexOf(currentCell)
  689. };
  690. response.aUser = {
  691. id: mCell.id,
  692. x: mCell.x,
  693. y: mCell.y,
  694. mass: mCell.mass,
  695. radius: mCell.radius
  696. };
  697. return response;
  698. }
  699. return false;
  700. }
  701. function collisionCheck(collision) {
  702. if (collision.aUser.mass > collision.bUser.mass * 1.1 && collision.aUser.radius > Math.sqrt(Math.pow(collision.aUser.x - collision.bUser.x, 2) + Math.pow(collision.aUser.y - collision.bUser.y, 2))*1.75) {
  703. console.log('[DEBUG] Killing user: ' + collision.bUser.name);
  704. console.log('[DEBUG] Collision info:');
  705. console.log(collision);
  706. var numUser = util.findIndex(users, collision.bUser.id);
  707. if (numUser > -1) {
  708. if(users[numUser].cells.length > 1) {
  709. users[numUser].massTotal -= collision.bUser.mass;
  710. users[numUser].cells.splice(collision.bUser.num, 1);
  711. } else {
  712. users.splice(numUser, 1);
  713. io.emit('playerDied', { name: collision.bUser.name });
  714. sockets[collision.bUser.id].emit('RIP');
  715. }
  716. }
  717. currentPlayer.massTotal += collision.bUser.mass;
  718. collision.aUser.mass += collision.bUser.mass;
  719. }
  720. }
  721. function mothercellCollisionCheck(collision) {
  722. if (collision.aUser.mass > collision.bUser.mass * 1.1 && collision.aUser.radius > Math.sqrt(Math.pow(collision.aUser.x - collision.bUser.x, 2) + Math.pow(collision.aUser.y - collision.bUser.y, 2))*1.75) {
  723. console.log('[DEBUG] Killing user: ' + collision.bUser.name);
  724. console.log('[DEBUG] Collision info:');
  725. console.log(collision);
  726. var numUser = util.findIndex(users, collision.bUser.id);
  727. if (numUser > -1) {
  728. if(users[numUser].cells.length > 1) {
  729. users[numUser].massTotal -= collision.bUser.mass;
  730. users[numUser].cells.splice(collision.bUser.num, 1);
  731. } else {
  732. users.splice(numUser, 1);
  733. io.emit('playerSuicide', { name: collision.bUser.name });
  734. sockets[collision.bUser.id].emit('RIP');
  735. }
  736. }
  737. mothercellAddFood(collision.aUser,Math.round(collision.bUser.mass),false);
  738. }
  739. }
  740. function splitCell(cell) {
  741. if(cell && cell.mass) {
  742. var thisUser = util.findIndex(users,currentPlayer.id);
  743. var parts = c.virus.splitParts-1;
  744. for(var i=0;i<c.virus.splitParts-1;i++) {
  745. if(cell.mass < c.defaultPlayerMass * parts || currentPlayer.cells.length + parts > c.limitSplit) parts--;
  746. }
  747. if(parts > 1) {
  748. cell.mass = cell.mass/parts;
  749. cell.radius = util.massToRadius(cell.mass);
  750. for(var a=0;a<parts;a++) {
  751. currentPlayer.cells.push({
  752. mass: cell.mass,
  753. x: cell.x,
  754. y: cell.y,
  755. radius: cell.radius,
  756. speed: 25
  757. });
  758. }
  759. }
  760. }
  761. }
  762. for(var z=0; z<currentPlayer.cells.length; z++) {
  763. var currentCell = currentPlayer.cells[z];
  764. var playerCircle = new C(
  765. new V(currentCell.x, currentCell.y),
  766. currentCell.radius
  767. );
  768. var foodEaten = food.map(funcFood)
  769. .reduce( function(a, b, c) { return b ? a.concat(c) : a; }, []);
  770. foodEaten.forEach(deleteFood);
  771. var mothercellFoodEaten = mothercellFood.map(funcFood)
  772. .reduce( function(a, b, c) { return b ? a.concat(c) : a; }, []);
  773. mothercellFoodEaten.forEach(deleteMothercellFood);
  774. var foodStructSpiralEaten = foodStructSpiral.map(funcFood)
  775. .reduce( function(a, b, c) { return b ? a.concat(c) : a; }, []);
  776. foodStructSpiralEaten.forEach(deleteFoodStructSpiral);
  777. var foodStructSpiralFoodEaten = foodStructSpiralFood.map(funcFood)
  778. .reduce( function(a, b, c) { return b ? a.concat(c) : a; }, []);
  779. foodStructSpiralFoodEaten.forEach(deleteFoodStructSpiralFood);
  780. var foodStructDotCircleEaten = foodStructDotCircle.map(funcFood)
  781. .reduce( function(a, b, c) { return b ? a.concat(c) : a; }, []);
  782. foodStructDotCircleEaten.forEach(deleteFoodStructDotCircle);
  783. var foodStructDotCircleFoodEaten = foodStructDotCircleFood.map(funcFood)
  784. .reduce( function(a, b, c) { return b ? a.concat(c) : a; }, []);
  785. foodStructDotCircleFoodEaten.forEach(deleteFoodStructDotCircleFood);
  786. var massEaten = massFood.map(eatMass)
  787. .reduce(function(a, b, c) {return b ? a.concat(c) : a; }, []);
  788. var virusCollision = virus.map(funcFood)
  789. .reduce( function(a, b, c) { return b ? a.concat(c) : a; }, []);
  790. if(virusCollision > 0 && currentCell.mass > virus[virusCollision].mass) {
  791. currentCell.mass += virus[virusCollision].mass;
  792. currentPlayer.massTotal += virus[virusCollision].mass;
  793. virus.splice(virusCollision, 1);
  794. splitCell(currentPlayer.cells[z]);
  795. currentPlayer.lastSplit = new Date().getTime();
  796. }
  797. var mothercellCollision = mothercell.map(funcFood)
  798. .reduce( function(a, b, c) { return b ? a.concat(c) : a; }, []);
  799. if(mothercellCollision > 0 && currentCell.mass > mothercell[mothercellCollision].mass) {
  800. currentCell.mass += mothercell[mothercellCollision].mass;
  801. currentPlayer.massTotal += mothercell[mothercellCollision].mass;
  802. mothercell.splice(mothercellCollision, 1);
  803. splitCell(currentPlayer.cells[z]);
  804. currentPlayer.lastSplit = new Date().getTime();
  805. }
  806. var masaGanada = 0;
  807. for(var m=0; m<massEaten.length; m++) {
  808. masaGanada += massFood[massEaten[m]].masa;
  809. massFood[massEaten[m]] = {};
  810. massFood.splice(massEaten[m],1);
  811. for(var n=0; n<massEaten.length; n++) {
  812. if(massEaten[m] < massEaten[n]) {
  813. massEaten[n]--;
  814. }
  815. }
  816. }
  817. if(typeof(currentCell.speed) == "undefined")
  818. currentCell.speed = 6.25;
  819. currentCell.mass += masaGanada;
  820. currentPlayer.massTotal += masaGanada;
  821. currentCell.radius = util.massToRadius(currentCell.mass);
  822. playerCircle.r = currentCell.radius;
  823. tree.clear();
  824. users.forEach(tree.put);
  825. var playerCollisions = [];
  826. var otherUsers = tree.get({x:currentCell.x, y: currentCell.y, w: currentCell.radius * 2, h: currentCell.radius * 2}, check);
  827. playerCollisions.forEach(collisionCheck);
  828. if(mothercellCollision > 0 && currentCell.mass < mothercell[mothercellCollision].mass) {
  829. var mCellCheck = checkMothercell(mothercell[mothercellCollision]);
  830. if(mCellCheck) mothercellCollisionCheck(mCellCheck);
  831. }
  832. }
  833. }
  834. function moveloop() {
  835. for (var i = 0; i < users.length; i++) {
  836. tickPlayer(users[i]);
  837. }
  838. for (i=0; i < massFood.length; i++) {
  839. if(massFood[i].speed > 0) moveMass(massFood[i]);
  840. }
  841. }
  842. function gameloop() {
  843. if (users.length > 0) {
  844. users.sort( function(a, b) { return b.massTotal - a.massTotal; });
  845. var topUsers = [];
  846. for (var i = 0; i < Math.min(10, users.length); i++) {
  847. if(users[i].type == 'player') {
  848. topUsers.push({
  849. id: users[i].id,
  850. hue: users[i].hue,
  851. name: users[i].name,
  852. mass: Math.round(users[i].massTotal)
  853. });
  854. }
  855. }
  856. if (isNaN(leaderboard) || leaderboard.length !== topUsers.length) {
  857. leaderboard = topUsers;
  858. leaderboardChanged = true;
  859. }
  860. else {
  861. for (i = 0; i < leaderboard.length; i++) {
  862. if (leaderboard[i].id !== topUsers[i].id) {
  863. leaderboard = topUsers;
  864. leaderboardChanged = true;
  865. break;
  866. }
  867. }
  868. }
  869. for (i = 0; i < users.length; i++) {
  870. for(var z=0; z < users[i].cells.length; z++) {
  871. if (users[i].cells[z].mass * (1 - (c.massLossRate / 1000)) > c.defaultPlayerMass && users[i].massTotal > c.minMassLoss) {
  872. var massLoss = users[i].cells[z].mass * (1 - (c.massLossRate / 1000));
  873. users[i].massTotal -= users[i].cells[z].mass - massLoss;
  874. users[i].cells[z].mass = massLoss;
  875. }
  876. }
  877. }
  878. }
  879. balanceMass();
  880. }
  881. function sendUpdates() {
  882. users.forEach( function(u) {
  883. // center the view if x/y is undefined, this will happen for spectators
  884. u.x = u.x || c.gameWidth / 2;
  885. u.y = u.y || c.gameHeight / 2;
  886. var visibleFood = food
  887. .concat(mothercellFood)
  888. .concat(foodStructSpiral)
  889. .concat(foodStructSpiralFood)
  890. .concat(foodStructDotCircle)
  891. .concat(foodStructDotCircleFood)
  892. .map(function(f) {
  893. if ( f.x > u.x - u.screenWidth/2 - 20 &&
  894. f.x < u.x + u.screenWidth/2 + 20 &&
  895. f.y > u.y - u.screenHeight/2 - 20 &&
  896. f.y < u.y + u.screenHeight/2 + 20) {
  897. return {
  898. i: f.id,
  899. x: util.round(f.x,1),
  900. y: util.round(f.y,1),
  901. r: util.round(f.radius,2),
  902. m: util.round(f.mass,2),
  903. h: f.hue
  904. };
  905. }
  906. })
  907. .filter(function(f) { return f; });
  908. var visibleVirus = virus
  909. .map(function(f) {
  910. if ( f.x > u.x - u.screenWidth/2 - f.radius &&
  911. f.x < u.x + u.screenWidth/2 + f.radius &&
  912. f.y > u.y - u.screenHeight/2 - f.radius &&
  913. f.y < u.y + u.screenHeight/2 + f.radius) {
  914. return {
  915. i: f.id,
  916. x: util.round(f.x,1),
  917. y: util.round(f.y,1),
  918. r: util.round(f.radius,2),
  919. m: util.round(f.mass,2)
  920. };
  921. }
  922. })
  923. .filter(function(f) { return f; });
  924. var visibleMothercell = mothercell
  925. .map(function(f) {
  926. if ( f.x > u.x - u.screenWidth/2 - f.radius &&
  927. f.x < u.x + u.screenWidth/2 + f.radius &&
  928. f.y > u.y - u.screenHeight/2 - f.radius &&
  929. f.y < u.y + u.screenHeight/2 + f.radius) {
  930. return {
  931. i: f.id,
  932. x: util.round(f.x,1),
  933. y: util.round(f.y,1),
  934. r: util.round(f.radius,2),
  935. m: util.round(f.mass,2)
  936. };
  937. }
  938. })
  939. .filter(function(f) { return f; });
  940. var visibleMass = massFood
  941. .map(function(f) {
  942. if ( f.x+f.radius > u.x - u.screenWidth/2 - 20 &&
  943. f.x-f.radius < u.x + u.screenWidth/2 + 20 &&
  944. f.y+f.radius > u.y - u.screenHeight/2 - 20 &&
  945. f.y-f.radius < u.y + u.screenHeight/2 + 20) {
  946. return {
  947. i: f.id,
  948. n: f.num,
  949. m: f.masa,
  950. h: f.hue,
  951. t: {
  952. x:util.round(f.target.x,1),
  953. y:util.round(f.target.y,1)
  954. },
  955. x: util.round(f.x,1),
  956. y: util.round(f.y,1),
  957. r: util.round(f.radius,2),
  958. s: f.speed
  959. };
  960. }
  961. })
  962. .filter(function(f) { return f; });
  963. var visibleCells = users
  964. .map(function(f) {
  965. var rcells = f.cells.map(function(c) {
  966. return {
  967. m: Math.round(c.mass),
  968. x: util.round(c.x,1),
  969. y: util.round(c.y,1),
  970. r: util.round(c.radius,2),
  971. s: c.speed
  972. };
  973. });
  974. for(var z=0; z<f.cells.length; z++)
  975. {
  976. if ( f.cells[z].x+f.cells[z].radius > u.x - u.screenWidth/2 - 20 &&
  977. f.cells[z].x-f.cells[z].radius < u.x + u.screenWidth/2 + 20 &&
  978. f.cells[z].y+f.cells[z].radius > u.y - u.screenHeight/2 - 20 &&
  979. f.cells[z].y-f.cells[z].radius < u.y + u.screenHeight/2 + 20) {
  980. z = f.cells.lenth;
  981. if(f.id !== u.id) {
  982. return {
  983. i: f.id,
  984. x: util.round(f.x,1),
  985. y: util.round(f.y,1),
  986. c: rcells,
  987. m: Math.round(f.massTotal),
  988. h: f.hue,
  989. n: f.name
  990. };
  991. } else {
  992. //console.log("Nombre: " + f.name + " Es Usuario");
  993. return {
  994. x: util.round(f.x,1),
  995. y: util.round(f.y,1),
  996. c: rcells,
  997. m: Math.round(f.massTotal),
  998. h: f.hue,
  999. };
  1000. }
  1001. }
  1002. }
  1003. })
  1004. .filter(function(f) { return f; });
  1005. sockets[u.id].emit('m', visibleCells, visibleFood, visibleMass, visibleVirus, visibleMothercell);
  1006. if (leaderboardChanged) {
  1007. sockets[u.id].emit('leaderboard', {
  1008. players: users.length,
  1009. leaderboard: leaderboard
  1010. });
  1011. }
  1012. });
  1013. leaderboardChanged = false;
  1014. }
  1015. function mothercellFeed() {
  1016. for(var i=0;i<mothercell.length;i++) {
  1017. mothercellAddFood(mothercell[i],1);
  1018. }
  1019. }
  1020. setInterval(moveloop, 1000 / 60);
  1021. setInterval(gameloop, 1000);
  1022. setInterval(sendUpdates, 1000 / c.networkUpdateFactor);
  1023. setInterval(mothercellFeed, 60000 / c.mothercell.foodSpeed);
  1024. // Don't touch, IP configurations.
  1025. var ipaddress = process.env.OPENSHIFT_NODEJS_IP || process.env.IP || c.host;
  1026. var serverport = process.env.OPENSHIFT_NODEJS_PORT || process.env.PORT || c.port;
  1027. http.listen( serverport, ipaddress, function() {
  1028. console.log('[DEBUG] Listening on ' + ipaddress + ':' + serverport);
  1029. });