Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

650 строки
15 KiB

  1. define([
  2. 'bcrypt-nodejs',
  3. 'security/io',
  4. 'misc/messages',
  5. 'security/connections',
  6. 'leaderboard/leaderboard',
  7. 'config/skins',
  8. 'config/roles',
  9. 'misc/profanities',
  10. 'fixes/fixes',
  11. 'config/loginRewards',
  12. 'misc/mail'
  13. ], function (
  14. bcrypt,
  15. io,
  16. messages,
  17. connections,
  18. leaderboard,
  19. skins,
  20. roles,
  21. profanities,
  22. fixes,
  23. loginRewards,
  24. mail
  25. ) {
  26. return {
  27. type: 'auth',
  28. username: null,
  29. charname: null,
  30. characters: {},
  31. characterList: [],
  32. stash: null,
  33. accountInfo: null,
  34. customChannels: [],
  35. play: function (data) {
  36. if (this.username == null)
  37. return;
  38. var character = this.characters[data.data.name];
  39. if (!character)
  40. return;
  41. if (character.permadead)
  42. return;
  43. character.stash = this.stash;
  44. character.account = this.username;
  45. this.charname = character.name;
  46. this.checkLoginReward(data, character);
  47. },
  48. checkLoginReward: function (data, character) {
  49. var accountInfo = this.accountInfo;
  50. var scheduler = require('misc/scheduler');
  51. var time = scheduler.getTime();
  52. var lastLogin = accountInfo.lastLogin;
  53. if ((!lastLogin) || (lastLogin.day != time.day)) {
  54. var daysSkipped = 1;
  55. if (lastLogin) {
  56. if (time.day > lastLogin.day)
  57. daysSkipped = time.day - lastLogin.day;
  58. else {
  59. var daysInMonth = scheduler.daysInMonth(lastLogin.month);
  60. daysSkipped = (daysInMonth - lastLogin.day) + time.day;
  61. for (var i = lastLogin.month + 1; i < time.month - 1; i++) {
  62. daysSkipped += scheduler.daysInMonth(i);
  63. }
  64. }
  65. }
  66. if (daysSkipped == 1) {
  67. accountInfo.loginStreak++;
  68. if (accountInfo.loginStreak > 21)
  69. accountInfo.loginStreak = 21;
  70. } else {
  71. accountInfo.loginStreak -= (daysSkipped - 1);
  72. if (accountInfo.loginStreak < 1)
  73. accountInfo.loginStreak = 1;
  74. }
  75. var rewards = loginRewards.generate(accountInfo.loginStreak);
  76. mail.sendMail(character.name, rewards, this.onSendRewards.bind(this, data, character));
  77. } else
  78. this.onSendRewards(data, character);
  79. accountInfo.lastLogin = time;
  80. },
  81. onSendRewards: function (data, character) {
  82. delete mail.busy[character.name];
  83. io.set({
  84. ent: this.username,
  85. field: 'accountInfo',
  86. value: JSON.stringify(this.accountInfo),
  87. callback: this.onUpdateAccountInfo.bind(this, data, character)
  88. });
  89. },
  90. onUpdateAccountInfo: function (data, character) {
  91. this.obj.player.sessionStart = +new Date;
  92. this.obj.player.spawn(character, data.callback);
  93. var prophecies = this.obj.prophecies ? this.obj.prophecies.simplify().list : [];
  94. leaderboard.setLevel(character.name, this.obj.stats.values.level, prophecies);
  95. },
  96. doSave: function (extensionObj) {
  97. var simple = this.obj.getSimple(true, true);
  98. simple.components.spliceWhere(c => c.type == 'stash');
  99. var stats = simple.components.find(c => c.type == 'stats');
  100. stats.values = extend(true, {}, stats.values);
  101. var social = simple.components.find(c => c.type == 'social');
  102. delete social.party;
  103. delete social.customChannels;
  104. var statKeys = Object.keys(stats.values);
  105. var sLen = statKeys.length;
  106. for (var i = 0; i < sLen; i++) {
  107. var s = statKeys[i];
  108. if (
  109. (
  110. (s.indexOf('xp') > -1) &&
  111. (s != 'xpIncrease')
  112. ) ||
  113. (s == 'level') ||
  114. (s == 'hp') ||
  115. (s == 'mana')
  116. )
  117. continue;
  118. delete stats.values[s];
  119. }
  120. //Calculate and store the ttl for effects
  121. var time = +new Date;
  122. simple.components.find(e => e.type == 'effects').effects.forEach(function (e) {
  123. if (e.expire)
  124. return;
  125. e.expire = time + (e.ttl * 350);
  126. });
  127. var callback = null;
  128. if (extensionObj) {
  129. callback = extensionObj.callback;
  130. delete extensionObj.callback;
  131. }
  132. extend(true, simple, extensionObj);
  133. io.set({
  134. ent: this.charname,
  135. field: 'character',
  136. value: JSON.stringify(simple).split(`'`).join('`'),
  137. callback: callback
  138. });
  139. //Save stash
  140. io.set({
  141. ent: this.username,
  142. field: 'stash',
  143. value: JSON.stringify(this.obj.stash.items).split(`'`).join('`')
  144. });
  145. },
  146. simplify: function () {
  147. return {
  148. type: 'auth',
  149. username: this.username,
  150. charname: this.charname,
  151. skins: this.skins
  152. };
  153. },
  154. getCharacterList: function (data) {
  155. if (this.username == null)
  156. return;
  157. io.get({
  158. ent: this.username,
  159. field: 'characterList',
  160. callback: this.onGetCharacterList.bind(this, data)
  161. });
  162. },
  163. onGetCharacterList: function (data, result) {
  164. var characters = JSON.parse(result || '[]');
  165. this.characterList = characters;
  166. var result = characters
  167. .map(c => ({
  168. name: c.name ? c.name : c,
  169. level: leaderboard.getLevel(c.name ? c.name : c)
  170. }));
  171. data.callback(result);
  172. },
  173. getCharacter: function (data) {
  174. var name = data.data.name;
  175. if (!this.characterList.some(c => ((c.name == name) || (c == name))))
  176. return;
  177. io.get({
  178. ent: name,
  179. field: 'character',
  180. callback: this.onGetCharacter.bind(this, data)
  181. });
  182. },
  183. onGetCharacter: function (data, result) {
  184. if (result) {
  185. result = result.split('`').join(`'`);
  186. result = result.replace(/''+/g, '\'');
  187. } else {
  188. console.log('char not found');
  189. console.log(data);
  190. }
  191. var character = JSON.parse(result || '{}');
  192. fixes.fixCharacter(character);
  193. //Hack for old characters
  194. if (!character.skinId)
  195. character.skinId = character.class + ' 1';
  196. character.cell = skins.getCell(character.skinId);
  197. character.sheetName = skins.getSpritesheet(character.skinId);
  198. this.characters[data.data.name] = character;
  199. this.getCustomChannels(data, character);
  200. },
  201. getCustomChannels: function (data, character) {
  202. io.get({
  203. ent: character.name,
  204. field: 'customChannels',
  205. callback: this.onGetCustomChannels.bind(this, data, character)
  206. });
  207. },
  208. onGetCustomChannels: function (data, character, result) {
  209. this.customChannels = JSON
  210. .parse(result || '[]')
  211. .filter(c => (typeof (c) == 'string'))
  212. .map(c => c.split(' ').join(''))
  213. .filter(c => (c.length > 0));
  214. this.customChannels = this.customChannels
  215. .filter((c, i) => (this.customChannels.indexOf(c) == i));
  216. var social = character.components.find(c => (c.type == 'social'));
  217. if (social)
  218. social.customChannels = this.customChannels;
  219. this.getStash(data, character);
  220. },
  221. getStash: function (data, character) {
  222. io.get({
  223. ent: this.username,
  224. field: 'stash',
  225. callback: this.onGetStash.bind(this, data, character)
  226. });
  227. },
  228. onGetStash: function (data, character, result) {
  229. if (result) {
  230. result = result.split('`').join(`'`);
  231. result = result.replace(/''+/g, '\'');
  232. }
  233. this.stash = JSON.parse(result || '[]');
  234. fixes.fixStash(this.stash);
  235. if (this.skins != null) {
  236. this.verifySkin(character);
  237. data.callback(character);
  238. } else {
  239. data.callback = data.callback.bind(null, character);
  240. this.getSkins(data, character);
  241. }
  242. },
  243. getSkins: function (msg, character) {
  244. io.get({
  245. ent: this.username,
  246. field: 'skins',
  247. callback: this.onGetSkins.bind(this, msg, character)
  248. });
  249. },
  250. onGetSkins: function (msg, character, result) {
  251. this.skins = JSON.parse(result || '[]');
  252. fixes.fixSkins(this.username, this.skins);
  253. var list = [...this.skins, ...roles.getSkins(this.username)];
  254. var skinList = skins.getSkinList(list);
  255. this.verifySkin(character);
  256. msg.callback(skinList);
  257. },
  258. saveSkin: function (skinId) {
  259. if (!this.skins) {
  260. this.getSkins({
  261. callback: this.saveSkin.bind(this, skinId)
  262. });
  263. return;
  264. }
  265. this.skins.push(skinId);
  266. io.set({
  267. ent: this.username,
  268. field: 'skins',
  269. value: JSON.stringify(this.skins),
  270. callback: this.onSaveSkin.bind(this)
  271. });
  272. },
  273. onSaveSkin: function () {
  274. },
  275. verifySkin: function (character) {
  276. if (!character)
  277. return;
  278. var list = [...this.skins, ...roles.getSkins(this.username)];
  279. var skinList = skins.getSkinList(list);
  280. if (!skinList.some(s => (s.id == character.skinId))) {
  281. character.skinId = '1.0';
  282. character.cell = skins.getCell(character.skinId);
  283. character.sheetName = skins.getSpritesheet(character.skinId);
  284. }
  285. },
  286. doesOwnSkin: function (skinId) {
  287. return this.skins.some(s => s == skinId);
  288. },
  289. login: function (msg) {
  290. var credentials = msg.data;
  291. if ((credentials.username == '') | (credentials.password == '')) {
  292. msg.callback(messages.login.allFields);
  293. return;
  294. }
  295. this.username = credentials.username;
  296. io.get({
  297. ent: credentials.username,
  298. field: 'login',
  299. callback: this.onHashCompare.bind(this, msg)
  300. });
  301. },
  302. onHashCompare: function (msg, storedPassword) {
  303. var credentials = msg.data;
  304. bcrypt.compare(credentials.password, storedPassword, this.onLogin.bind(this, msg, storedPassword));
  305. },
  306. onLogin: function (msg, storedPassword, err, compareResult) {
  307. if (!storedPassword)
  308. msg.callback(messages.login.incorrect);
  309. else {
  310. if (compareResult) { //If stored password matches the hashed password entered by the user, log them in directly
  311. this.onLoginVerified(msg);
  312. } else if (msg.data.password == storedPassword) { //If the stored password matches a plaintext password entered by the user; In that case the password gets hashed for the future
  313. this.onUnhashedLogin(msg);
  314. } else
  315. msg.callback(messages.login.incorrect);
  316. }
  317. },
  318. onUnhashedLogin: function (msg) {
  319. bcrypt.hash(msg.data.password, null, null, this.onPasswordHashed.bind(this, msg));
  320. },
  321. onPasswordHashed: function (msg, err, hashedPassword) {
  322. io.set({
  323. ent: msg.data.username,
  324. field: 'login',
  325. value: hashedPassword,
  326. callback: this.onLoginVerified.bind(this, msg)
  327. });
  328. },
  329. onLoginVerified: function (msg) {
  330. this.username = msg.data.username;
  331. connections.logOut(this.obj);
  332. io.get({
  333. ent: msg.data.username,
  334. field: 'accountInfo',
  335. callback: this.onGetAccountInfo.bind(this, msg)
  336. });
  337. },
  338. onGetAccountInfo: function (msg, info) {
  339. if (!info) {
  340. info = {
  341. loginStreak: 0
  342. };
  343. } else
  344. info = JSON.parse(info);
  345. this.accountInfo = info;
  346. msg.callback();
  347. },
  348. register: function (msg) {
  349. var credentials = msg.data;
  350. if ((credentials.username == '') || (credentials.password == '')) {
  351. msg.callback(messages.login.allFields);
  352. return;
  353. }
  354. var illegal = ["'", '"', '/', '(', ')', '[', ']', '{', '}', ':', ';', '<', '>'];
  355. for (var i = 0; i < illegal.length; i++) {
  356. if ((credentials.username.indexOf(illegal[i]) > -1) || (credentials.password.indexOf(illegal[i]) > -1)) {
  357. msg.callback(messages.login.illegal);
  358. return;
  359. }
  360. }
  361. io.get({
  362. ent: credentials.username,
  363. field: 'login',
  364. callback: this.onCheckExists.bind(this, msg)
  365. });
  366. },
  367. onCheckExists: function (msg, result) {
  368. if (result) {
  369. msg.callback(messages.login.exists);
  370. return;
  371. }
  372. var credentials = msg.data;
  373. bcrypt.hash(credentials.password, null, null, this.onHashGenerated.bind(this, msg));
  374. },
  375. onHashGenerated: function (msg, err, hashedPassword) {
  376. io.set({
  377. ent: msg.data.username,
  378. field: 'login',
  379. value: hashedPassword,
  380. callback: this.onRegister.bind(this, msg)
  381. });
  382. },
  383. onRegister: function (msg, result) {
  384. this.accountInfo = {
  385. loginStreak: 0
  386. };
  387. io.set({
  388. ent: msg.data.username,
  389. field: 'characterList',
  390. value: '[]',
  391. callback: this.onCreateCharacterList.bind(this, msg)
  392. });
  393. },
  394. onCreateCharacterList: function (msg, result) {
  395. this.username = msg.data.username;
  396. connections.logOut(this.obj);
  397. msg.callback();
  398. },
  399. createCharacter: function (msg) {
  400. var data = msg.data;
  401. if ((data.name.length < 3) || (data.name.length > 12)) {
  402. msg.callback(messages.createCharacter.nameLength);
  403. return;
  404. }
  405. if (!profanities.isClean(data.name)) {
  406. msg.callback(messages.login.invalid);
  407. return;
  408. } else {
  409. var name = data.name;
  410. if (name.indexOf(' ') > -1) {
  411. msg.callback(messages.login.invalid);
  412. return;
  413. }
  414. var nLen = name.length;
  415. for (var i = 0; i < nLen; i++) {
  416. var char = name[i].toLowerCase();
  417. var valid = [
  418. 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
  419. ];
  420. if (valid.indexOf(char) == -1) {
  421. msg.callback(messages.login.invalid);
  422. return;
  423. }
  424. }
  425. }
  426. io.get({
  427. ent: data.name,
  428. field: 'character',
  429. callback: this.onCheckCharacterExists.bind(this, msg)
  430. });
  431. },
  432. onCheckCharacterExists: function (msg, result) {
  433. if (result) {
  434. msg.callback(messages.login.charExists);
  435. return;
  436. }
  437. var data = msg.data;
  438. this.obj.name = data.name;
  439. this.obj.skinId = data.skinId;
  440. this.obj.class = data.class;
  441. this.obj.cell = skins.getCell(this.obj.skinId);
  442. this.obj.sheetName = skins.getSpritesheet(this.obj.skinId);
  443. this.verifySkin(this.obj);
  444. var simple = this.obj.getSimple(true);
  445. var prophecies = data.prophecies || [];
  446. prophecies = prophecies.filter((p, i) => (prophecies.indexOf(p) == i));
  447. simple.components.push({
  448. type: 'prophecies',
  449. list: prophecies
  450. });
  451. io.set({
  452. ent: data.name,
  453. field: 'character',
  454. value: JSON.stringify(simple),
  455. callback: this.onCreateCharacter.bind(this, msg)
  456. });
  457. },
  458. onCreateCharacter: function (msg, result) {
  459. var name = msg.data.name;
  460. var simple = this.obj.getSimple(true);
  461. simple.components.push({
  462. type: 'prophecies',
  463. list: msg.data.prophecies || []
  464. });
  465. simple.components.push({
  466. type: 'social',
  467. customChannels: this.customChannels
  468. });
  469. this.characters[name] = simple;
  470. this.characterList.push(name);
  471. io.set({
  472. ent: this.username,
  473. field: 'characterList',
  474. value: JSON.stringify(this.characterList),
  475. callback: this.onAppendList.bind(this, msg)
  476. });
  477. },
  478. deleteCharacter: function (msg) {
  479. var data = msg.data;
  480. if ((!data.name) || (!this.username))
  481. return;
  482. if (!this.characterList.some(c => ((c.name == data.name) || (c == data.name)))) {
  483. msg.callback([]);
  484. return;
  485. }
  486. io.delete({
  487. ent: data.name,
  488. field: 'character',
  489. callback: this.onDeleteCharacter.bind(this, msg)
  490. });
  491. },
  492. onDeleteCharacter: function (msg, result) {
  493. this.characterList.spliceWhere(c => ((c.name == msg.data.name) || (c == msg.data.name)));
  494. var characterList = this.characterList
  495. .map(c => ({
  496. name: c.name ? c.name : c,
  497. level: leaderboard.getLevel(c.name ? c.name : c)
  498. }));
  499. io.set({
  500. ent: this.username,
  501. field: 'characterList',
  502. value: JSON.stringify(characterList),
  503. callback: this.onRemoveFromList.bind(this, msg)
  504. });
  505. leaderboard.deleteCharacter(msg.data.name);
  506. },
  507. onRemoveFromList: function (msg, result) {
  508. var result = this.characterList
  509. .map(c => ({
  510. name: c.name ? c.name : c,
  511. level: leaderboard.getLevel(c.name ? c.name : c)
  512. }));
  513. msg.callback(result);
  514. },
  515. onAppendList: function (msg, result) {
  516. this.play({
  517. data: {
  518. name: msg.data.name
  519. },
  520. callback: msg.callback
  521. });
  522. },
  523. permadie: function () {
  524. this.obj.permadead = true;
  525. this.doSave({
  526. permadead: true,
  527. callback: this.onPermadie.bind(this)
  528. });
  529. },
  530. onPermadie: function () {
  531. process.send({
  532. method: 'object',
  533. serverId: this.obj.serverId,
  534. obj: {
  535. dead: true
  536. }
  537. });
  538. }
  539. };
  540. });