You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

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