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.
 
 
 

460 lines
10 KiB

  1. define([
  2. 'bcrypt-nodejs',
  3. 'security/io',
  4. 'misc/messages',
  5. 'security/connections',
  6. 'leaderboard/leaderboard',
  7. 'config/skins',
  8. 'misc/profanities'
  9. ], function(
  10. bcrypt,
  11. io,
  12. messages,
  13. connections,
  14. leaderboard,
  15. skins,
  16. profanities
  17. ) {
  18. return {
  19. type: 'auth',
  20. username: null,
  21. charname: null,
  22. characters: {},
  23. characterList: [],
  24. stash: null,
  25. play: function(data) {
  26. if (this.username == null)
  27. return;
  28. var character = this.characters[data.data.name];
  29. if (!character)
  30. return;
  31. if (character.permadead)
  32. return;
  33. character.stash = this.stash;
  34. character.account = this.username;
  35. this.charname = character.name;
  36. data.callback();
  37. this.obj.player.sessionStart = +new Date;
  38. this.obj.player.spawn(character);
  39. var prophecies = this.obj.prophecies ? this.obj.prophecies.simplify().list : [];
  40. leaderboard.setLevel(character.name, this.obj.stats.values.level, prophecies);
  41. },
  42. doSave: function(extensionObj) {
  43. var simple = this.obj.getSimple(true, true);
  44. simple.components.spliceWhere(c => c.type == 'stash');
  45. //Don't save modified stat values
  46. var stats = simple.components.find(c => c.type == 'stats');
  47. stats.values = extend(true, {}, stats.values);
  48. var statKeys = Object.keys(stats.values);
  49. var sLen = statKeys.length;
  50. for (var i = 0; i < sLen; i++) {
  51. var s = statKeys[i];
  52. if (
  53. (
  54. (s.indexOf('xp') > -1) &&
  55. (s != 'xpIncrease')
  56. ) ||
  57. (s == 'level') ||
  58. (s == 'hp') ||
  59. (s == 'mana')
  60. )
  61. continue;
  62. delete stats.values[s];
  63. }
  64. //Calculate and store the ttl for effects
  65. var time = +new Date;
  66. simple.components.find(e => e.type == 'effects').effects.forEach(function(e) {
  67. e.expire = time + (e.ttl * 350);
  68. });
  69. var callback = null;
  70. if (extensionObj) {
  71. callback = extensionObj.callback;
  72. delete extensionObj.callback;
  73. }
  74. extend(true, simple, extensionObj);
  75. try {
  76. var a = JSON.stringify(simple);
  77. }
  78. catch (e) {
  79. console.log(simple.components.find(c => (c.type == 'spellbook')).spells);
  80. }
  81. io.set({
  82. ent: this.charname,
  83. field: 'character',
  84. value: JSON.stringify(simple),
  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)
  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. io.get({
  123. ent: data.data.name,
  124. field: 'character',
  125. callback: this.onGetCharacter.bind(this, data)
  126. });
  127. },
  128. onGetCharacter: function(data, result) {
  129. var character = JSON.parse(result || '{}');
  130. this.characters[data.data.name] = character;
  131. this.getStash(data, character);
  132. },
  133. getStash: function(data, character) {
  134. io.get({
  135. ent: this.username,
  136. field: 'stash',
  137. callback: this.onGetStash.bind(this, data, character)
  138. });
  139. },
  140. onGetStash: function(data, character, result) {
  141. this.stash = JSON.parse(result || '[]');
  142. if (this.skins != null)
  143. data.callback(character);
  144. else {
  145. data.callback = data.callback.bind(null, character);
  146. this.getSkins(data);
  147. }
  148. },
  149. getSkins: function(msg) {
  150. io.get({
  151. ent: this.username,
  152. field: 'skins',
  153. callback: this.onGetSkins.bind(this, msg)
  154. });
  155. },
  156. onGetSkins: function(msg, result) {
  157. this.skins = JSON.parse(result || '[]');
  158. var skinList = skins.getSkinList(this.skins);
  159. msg.callback(skinList);
  160. },
  161. saveSkin: function(skinId) {
  162. if (!this.skins) {
  163. this.getSkins({
  164. callback: this.saveSkin.bind(this, skinId)
  165. });
  166. return;
  167. }
  168. this.skins.push(skinId);
  169. io.set({
  170. ent: this.username,
  171. field: 'skins',
  172. value: JSON.stringify(this.skins),
  173. callback: this.onSaveSkin.bind(this)
  174. });
  175. },
  176. onSaveSkin: function() {
  177. },
  178. doesOwnSkin: function(skinId) {
  179. return this.skins.some(s => s == skinId);
  180. },
  181. login: function(msg) {
  182. var credentials = msg.data;
  183. if ((credentials.username == '') | (credentials.password == '')) {
  184. msg.callback(messages.login.allFields);
  185. return;
  186. }
  187. this.username = credentials.username;
  188. io.get({
  189. ent: credentials.username,
  190. field: 'login',
  191. callback: this.onHashCompare.bind(this, msg)
  192. });
  193. },
  194. onHashCompare: function(msg, storedPassword) {
  195. var credentials = msg.data;
  196. bcrypt.compare(credentials.password, storedPassword, this.onLogin.bind(this, msg, storedPassword));
  197. },
  198. onLogin: function(msg, storedPassword, err, compareResult) {
  199. if (!storedPassword)
  200. msg.callback(messages.login.incorrect);
  201. else {
  202. if (compareResult) { //If stored password matches the hashed password entered by the user, log them in directly
  203. this.onLoginVerified(msg);
  204. } 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
  205. this.onUnhashedLogin(msg);
  206. } else
  207. msg.callback(messages.login.incorrect);
  208. }
  209. },
  210. onUnhashedLogin: function(msg) {
  211. bcrypt.hash(msg.data.password, null, null, this.onPasswordHashed.bind(this, msg));
  212. },
  213. onPasswordHashed: function(msg, err, hashedPassword) {
  214. io.set({
  215. ent: msg.data.username,
  216. field: 'login',
  217. value: hashedPassword,
  218. callback: this.onLoginVerified.bind(this, msg)
  219. });
  220. },
  221. onLoginVerified: function(msg) {
  222. this.username = msg.data.username;
  223. connections.logOut(this.obj);
  224. msg.callback();
  225. },
  226. register: function(msg) {
  227. var credentials = msg.data;
  228. if ((credentials.username == '') || (credentials.password == '')) {
  229. msg.callback(messages.login.allFields);
  230. return;
  231. }
  232. var illegal = ["'", '"', '/', '(', ')', '[', ']', '{', '}', ':', ';', '<', '>'];
  233. for (var i = 0; i < illegal.length; i++) {
  234. if ((credentials.username.indexOf(illegal[i]) > -1) || (credentials.password.indexOf(illegal[i]) > -1)) {
  235. msg.callback(messages.login.illegal);
  236. return;
  237. }
  238. }
  239. if (!profanities.isClean(credentials.username)) {
  240. msg.callback(messages.login.invalid);
  241. return;
  242. }
  243. io.get({
  244. ent: credentials.username,
  245. field: 'login',
  246. callback: this.onCheckExists.bind(this, msg)
  247. });
  248. },
  249. onCheckExists: function(msg, result) {
  250. if (result) {
  251. msg.callback(messages.login.exists);
  252. return;
  253. }
  254. var credentials = msg.data;
  255. bcrypt.hash(credentials.password, null, null, this.onHashGenerated.bind(this, msg));
  256. },
  257. onHashGenerated: function(msg, err, hashedPassword) {
  258. io.set({
  259. ent: msg.data.username,
  260. field: 'login',
  261. value: hashedPassword,
  262. callback: this.onRegister.bind(this, msg)
  263. });
  264. },
  265. onRegister: function(msg, result) {
  266. io.set({
  267. ent: msg.data.username,
  268. field: 'characterList',
  269. value: '[]',
  270. callback: this.onCreateCharacterList.bind(this, msg)
  271. });
  272. },
  273. onCreateCharacterList: function(msg, result) {
  274. this.username = msg.data.username;
  275. connections.logOut(this.obj);
  276. msg.callback();
  277. },
  278. createCharacter: function(msg) {
  279. var data = msg.data;
  280. if ((data.name.length < 3) || (data.name.length > 12)) {
  281. msg.callback(messages.createCharacter.nameLength);
  282. return;
  283. }
  284. if (!profanities.isClean(data.name)) {
  285. msg.callback(messages.login.invalid);
  286. return;
  287. }
  288. io.get({
  289. ent: data.name,
  290. field: 'character',
  291. callback: this.onCheckCharacterExists.bind(this, msg)
  292. });
  293. },
  294. onCheckCharacterExists: function(msg, result) {
  295. if (result) {
  296. msg.callback(messages.login.charExists);
  297. return;
  298. }
  299. var data = msg.data;
  300. this.obj.name = data.name;
  301. this.obj.class = data.class;
  302. this.obj.costume = data.costume;
  303. this.obj.cell = skins.getCell(this.obj.class, this.obj.costume);
  304. this.obj.previewSpritesheet = skins.getSpritesheet(this.obj.class);
  305. var simple = this.obj.getSimple(true);
  306. simple.components.push({
  307. type: 'prophecies',
  308. list: data.prophecies || []
  309. });
  310. io.set({
  311. ent: data.name,
  312. field: 'character',
  313. value: JSON.stringify(simple),
  314. callback: this.onCreateCharacter.bind(this, msg)
  315. });
  316. },
  317. onCreateCharacter: function(msg, result) {
  318. var name = msg.data.name;
  319. var simple = this.obj.getSimple(true);
  320. simple.components.push({
  321. type: 'prophecies',
  322. list: msg.data.prophecies || []
  323. });
  324. this.characters[name] = simple;
  325. this.characterList.push(name);
  326. io.set({
  327. ent: this.username,
  328. field: 'characterList',
  329. value: JSON.stringify(this.characterList),
  330. callback: this.onAppendList.bind(this, msg)
  331. });
  332. },
  333. deleteCharacter: function(msg) {
  334. var data = msg.data;
  335. if ((!data.name) || (!this.username))
  336. return;
  337. if (this.characterList.indexOf(data.name) == -1) {
  338. msg.callback([]);
  339. return;
  340. }
  341. io.delete({
  342. ent: data.name,
  343. field: 'character',
  344. callback: this.onDeleteCharacter.bind(this, msg)
  345. });
  346. },
  347. onDeleteCharacter: function(msg, result) {
  348. this.characterList.spliceWhere(c => c == msg.data.name);
  349. var characterList = this.characterList
  350. .map(c => ({
  351. name: c.name ? c.name : c,
  352. level: leaderboard.getLevel(c.name ? c.name : c)
  353. }));
  354. io.set({
  355. ent: this.username,
  356. field: 'characterList',
  357. value: JSON.stringify(characterList),
  358. callback: this.onRemoveFromList.bind(this, msg)
  359. });
  360. leaderboard.deleteCharacter(msg.data.name);
  361. },
  362. onRemoveFromList: function(msg, result) {
  363. msg.callback(this.characterList);
  364. },
  365. onAppendList: function(msg, result) {
  366. this.play({
  367. data: {
  368. name: msg.data.name
  369. },
  370. callback: msg.callback
  371. });
  372. },
  373. permadie: function() {
  374. this.obj.permadead = true;
  375. this.doSave({
  376. permadead: true,
  377. callback: this.onPermadie.bind(this)
  378. });
  379. },
  380. onPermadie: function() {
  381. process.send({
  382. method: 'object',
  383. serverId: this.obj.serverId,
  384. obj: {
  385. dead: true
  386. }
  387. });
  388. }
  389. };
  390. });