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.
 
 
 

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