Não pode escolher mais do que 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 

582 linhas
12 KiB

  1. //Imports
  2. const bcrypt = require('bcrypt-nodejs');
  3. const messages = require('../misc/messages');
  4. const skins = require('../config/skins');
  5. const profanities = require('../misc/profanities');
  6. const fixes = require('../fixes/fixes');
  7. const spirits = require('../config/spirits');
  8. const ga = require('../security/ga');
  9. const eventEmitter = require('../misc/events');
  10. const checkLoginRewards = require('./auth/checkLoginRewards');
  11. //This section of code is in charge of ensuring that we only ever create one account at a time,
  12. // since we don't have a read/write lock on the characters table, we have to address it in code
  13. const createLockBuffer = [];
  14. const getCreateLock = async () => {
  15. const releaseLock = lockEntry => {
  16. createLockBuffer.spliceWhere(c => c === lockEntry);
  17. const nextEntry = createLockBuffer[0];
  18. if (!nextEntry)
  19. return;
  20. nextEntry.takeLock();
  21. };
  22. const promise = new Promise(async res => {
  23. let lockEntry = {};
  24. lockEntry.takeLock = res.bind(null, releaseLock.bind(null, lockEntry));
  25. if (!createLockBuffer.length) {
  26. createLockBuffer.push(lockEntry);
  27. lockEntry.takeLock();
  28. return;
  29. }
  30. createLockBuffer.push(lockEntry);
  31. });
  32. return promise;
  33. };
  34. //Component Definition
  35. module.exports = {
  36. type: 'auth',
  37. username: null,
  38. charname: null,
  39. characters: {},
  40. characterList: [],
  41. stash: null,
  42. accountInfo: null,
  43. customChannels: [],
  44. play: async function (data) {
  45. if (!this.username)
  46. return;
  47. let character = this.characters[data.data.name];
  48. if (!character)
  49. return;
  50. else if (character.permadead)
  51. return;
  52. character.stash = this.stash;
  53. character.account = this.username;
  54. this.charname = character.name;
  55. checkLoginRewards(this, data, character, this.onSendRewards.bind(this, data, character));
  56. cons.modifyPlayerCount(1);
  57. },
  58. onSendRewards: async function (data, character) {
  59. await io.setAsync({
  60. key: this.username,
  61. table: 'accountInfo',
  62. value: this.accountInfo,
  63. serialize: true
  64. });
  65. this.obj.player.sessionStart = +new Date();
  66. this.obj.player.spawn(character, data.callback);
  67. let prophecies = this.obj.prophecies ? this.obj.prophecies.simplify().list : [];
  68. await leaderboard.setLevel(character.name, this.obj.stats.values.level, prophecies);
  69. },
  70. doSave: async function (callback, saveStash = true) {
  71. const simple = this.obj.getSimple(true, true);
  72. delete simple.destroyed;
  73. delete simple.forceDestroy;
  74. simple.components.spliceWhere(f => (f.type === 'stash'));
  75. await io.setAsync({
  76. key: this.charname,
  77. table: 'character',
  78. value: simple,
  79. clean: true,
  80. serialize: true
  81. });
  82. if (saveStash)
  83. await this.doSaveStash();
  84. if (callback)
  85. callback();
  86. },
  87. doSaveStash: async function () {
  88. await io.setAsync({
  89. key: this.username,
  90. table: 'stash',
  91. value: this.obj.stash.serialize(),
  92. clean: true,
  93. serialize: true
  94. });
  95. },
  96. simplify: function (self) {
  97. if (!self)
  98. return;
  99. return {
  100. type: 'auth',
  101. username: this.username,
  102. charname: this.charname,
  103. accountInfo: this.accountInfo
  104. };
  105. },
  106. getCharacterList: async function (data) {
  107. if (!this.username)
  108. return;
  109. this.characterList = await io.getAsync({
  110. key: this.username,
  111. table: 'characterList',
  112. isArray: true
  113. });
  114. let res = this.characterList.map(c => ({
  115. name: c.name ? c.name : c,
  116. level: leaderboard.getLevel(c.name ? c.name : c)
  117. }));
  118. data.callback(res);
  119. },
  120. getCharacter: async function (data) {
  121. let charName = data.data.name;
  122. if (!this.characterList.some(c => (c.name === charName || c === charName)))
  123. return;
  124. let character = await io.getAsync({
  125. key: charName,
  126. table: 'character',
  127. clean: true
  128. });
  129. eventEmitter.emit('onAfterGetCharacter', {
  130. obj: this.obj,
  131. character
  132. });
  133. fixes.fixCharacter(character);
  134. character.cell = skins.getCell(character.skinId);
  135. character.sheetName = skins.getSpritesheet(character.skinId);
  136. this.characters[charName] = character;
  137. await this.getCustomChannels(character);
  138. await this.getStash();
  139. await this.verifySkin(character);
  140. data.callback(character);
  141. },
  142. getCustomChannels: async function (character) {
  143. this.customChannels = await io.getAsync({
  144. key: character.name,
  145. table: 'customChannels',
  146. isArray: true
  147. });
  148. let social = character.components.find(c => (c.type === 'social'));
  149. this.customChannels = fixes.fixCustomChannels(this.customChannels);
  150. if (social)
  151. social.customChannels = this.customChannels;
  152. },
  153. getStash: async function (data, character) {
  154. this.stash = await io.getAsync({
  155. key: this.username,
  156. table: 'stash',
  157. isArray: true,
  158. clean: true
  159. });
  160. fixes.fixStash(this.stash);
  161. await eventEmitter.emit('onAfterGetStash', {
  162. obj: this.obj,
  163. stash: this.stash
  164. });
  165. },
  166. verifySkin: async function (character) {
  167. const doesOwn = await this.doesOwnSkin(character.skinId);
  168. if (doesOwn)
  169. return;
  170. const defaultTo = 'wizard';
  171. character.skinId = defaultTo;
  172. character.cell = skins.getCell(defaultTo);
  173. character.sheetName = skins.getSpritesheet(defaultTo);
  174. },
  175. doesOwnSkin: async function (skinId) {
  176. const allSkins = skins.getList();
  177. const filteredSkins = allSkins.filter(({ default: isDefaultSkin }) => isDefaultSkin);
  178. const msgSkinList = {
  179. obj: this,
  180. allSkins,
  181. filteredSkins
  182. };
  183. await eventEmitter.emit('onBeforeGetAccountSkins', msgSkinList);
  184. const result = filteredSkins.some(f => f.id === skinId);
  185. return result;
  186. },
  187. getSkinList: async function ({ callback }) {
  188. const allSkins = skins.getList();
  189. const filteredSkins = allSkins.filter(({ default: isDefaultSkin }) => isDefaultSkin);
  190. const msgSkinList = {
  191. obj: this,
  192. allSkins,
  193. filteredSkins
  194. };
  195. await eventEmitter.emit('onBeforeGetAccountSkins', msgSkinList);
  196. callback(filteredSkins);
  197. },
  198. login: async function (msg) {
  199. let credentials = msg.data;
  200. if (credentials.username === '' || credentials.password === '') {
  201. msg.callback(messages.login.allFields);
  202. return;
  203. }
  204. let storedPassword = await io.getAsync({
  205. key: credentials.username,
  206. table: 'login',
  207. noParse: true
  208. });
  209. bcrypt.compare(credentials.password, storedPassword, this.onLogin.bind(this, msg, storedPassword));
  210. },
  211. onLogin: async function (msg, storedPassword, err, compareResult) {
  212. const { data: { username } } = msg;
  213. if (!compareResult) {
  214. msg.callback(messages.login.incorrect);
  215. return;
  216. }
  217. const emBeforeLogin = {
  218. obj: this.obj,
  219. success: true,
  220. msg: null
  221. };
  222. await eventEmitter.emit('onBeforeLogin', emBeforeLogin);
  223. if (!emBeforeLogin.success) {
  224. msg.callback(emBeforeLogin.msg);
  225. return;
  226. }
  227. this.username = username;
  228. await cons.logOut(this.obj);
  229. this.initTracker();
  230. const accountInfo = await io.getAsync({
  231. key: username,
  232. table: 'accountInfo',
  233. noDefault: true
  234. }) || {
  235. loginStreak: 0,
  236. level: 0
  237. };
  238. const msgAccountInfo = {
  239. username,
  240. accountInfo
  241. };
  242. await eventEmitter.emit('onBeforeGetAccountInfo', msgAccountInfo);
  243. await eventEmitter.emit('onAfterLogin', { username });
  244. this.accountInfo = msgAccountInfo.accountInfo;
  245. msg.callback();
  246. },
  247. initTracker: function () {
  248. this.gaTracker = ga.connect(this.username);
  249. },
  250. track: function (category, action, label, value = 1) {
  251. process.send({
  252. method: 'track',
  253. serverId: this.obj.serverId,
  254. obj: {
  255. category,
  256. action,
  257. label,
  258. value
  259. }
  260. });
  261. },
  262. register: async function (msg) {
  263. let credentials = msg.data;
  264. if ((credentials.username === '') || (credentials.password === '')) {
  265. msg.callback(messages.login.allFields);
  266. return;
  267. }
  268. let illegal = ["'", '"', '/', '\\', '(', ')', '[', ']', '{', '}', ':', ';', '<', '>', '+', '?', '*'];
  269. for (let i = 0; i < illegal.length; i++) {
  270. if (credentials.username.indexOf(illegal[i]) > -1) {
  271. msg.callback(messages.login.illegal);
  272. return;
  273. }
  274. }
  275. const emBeforeRegisterAccount = {
  276. obj: this.obj,
  277. success: true,
  278. msg: null
  279. };
  280. await eventEmitter.emit('onBeforeRegisterAccount', emBeforeRegisterAccount);
  281. if (!emBeforeRegisterAccount.success) {
  282. msg.callback(emBeforeRegisterAccount.msg);
  283. return;
  284. }
  285. let exists = await io.getAsync({
  286. key: credentials.username,
  287. ignoreCase: true,
  288. table: 'login',
  289. noDefault: true,
  290. noParse: true
  291. });
  292. if (exists) {
  293. msg.callback(messages.login.exists);
  294. return;
  295. }
  296. bcrypt.hash(credentials.password, null, null, this.onHashGenerated.bind(this, msg));
  297. },
  298. onHashGenerated: async function (msg, err, hashedPassword) {
  299. await io.setAsync({
  300. key: msg.data.username,
  301. table: 'login',
  302. value: hashedPassword
  303. });
  304. this.accountInfo = {
  305. loginStreak: 0,
  306. level: 0
  307. };
  308. await io.setAsync({
  309. key: msg.data.username,
  310. table: 'characterList',
  311. value: [],
  312. serialize: true
  313. });
  314. this.username = msg.data.username;
  315. cons.logOut(this.obj);
  316. msg.callback();
  317. },
  318. createCharacter: async function (msg) {
  319. let data = msg.data;
  320. let name = data.name;
  321. let error = null;
  322. if (name.length < 3 || name.length > 12)
  323. error = messages.createCharacter.nameLength;
  324. else if (!profanities.isClean(name))
  325. error = messages.login.invalid;
  326. else if (name.indexOf(' ') > -1)
  327. msg.callback(messages.login.invalid);
  328. else if (!spirits.list.includes(data.class))
  329. return;
  330. let nLen = name.length;
  331. for (let i = 0; i < nLen; i++) {
  332. let char = name[i].toLowerCase();
  333. let valid = [
  334. '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'
  335. ];
  336. if (!valid.includes(char)) {
  337. error = messages.login.invalid;
  338. break;
  339. }
  340. }
  341. if (error) {
  342. msg.callback(error);
  343. return;
  344. }
  345. const releaseCreateLock = await getCreateLock();
  346. let exists = await io.getAsync({
  347. key: name,
  348. ignoreCase: true,
  349. table: 'character',
  350. noDefault: true
  351. });
  352. if (exists) {
  353. releaseCreateLock();
  354. msg.callback(messages.login.charExists);
  355. return;
  356. }
  357. let obj = this.obj;
  358. extend(obj, {
  359. name: name,
  360. skinId: data.skinId,
  361. class: data.class,
  362. cell: skins.getCell(data.skinId),
  363. sheetName: skins.getSpritesheet(data.skinId),
  364. x: null,
  365. y: null
  366. });
  367. let simple = this.obj.getSimple(true);
  368. await this.verifySkin(simple);
  369. let prophecies = (data.prophecies || []).filter(p => p);
  370. simple.components.push({
  371. type: 'prophecies',
  372. list: prophecies
  373. }, {
  374. type: 'social',
  375. customChannels: this.customChannels
  376. });
  377. await io.setAsync({
  378. key: name,
  379. table: 'character',
  380. value: simple,
  381. serialize: true
  382. });
  383. this.characters[name] = simple;
  384. this.characterList.push(name);
  385. await io.setAsync({
  386. key: this.username,
  387. table: 'characterList',
  388. value: this.characterList,
  389. serialize: true
  390. });
  391. releaseCreateLock();
  392. this.initTracker();
  393. this.play({
  394. data: {
  395. name: name
  396. },
  397. callback: msg.callback
  398. });
  399. },
  400. deleteCharacter: async function (msg) {
  401. let data = msg.data;
  402. if ((!data.name) || (!this.username))
  403. return;
  404. if (!this.characterList.some(c => ((c.name === data.name) || (c === data.name)))) {
  405. msg.callback([]);
  406. return;
  407. }
  408. await io.deleteAsync({
  409. key: data.name,
  410. table: 'character'
  411. });
  412. let name = data.name;
  413. this.characterList.spliceWhere(c => (c.name === name || c === name));
  414. let characterList = this.characterList
  415. .map(c => ({
  416. name: c.name ? c.name : c,
  417. level: leaderboard.getLevel(c.name ? c.name : c)
  418. }));
  419. await io.setAsync({
  420. key: this.username,
  421. table: 'characterList',
  422. value: characterList,
  423. serialize: true
  424. });
  425. await leaderboard.deleteCharacter(name);
  426. let result = this.characterList
  427. .map(c => ({
  428. name: c.name ? c.name : c,
  429. level: leaderboard.getLevel(c.name ? c.name : c)
  430. }));
  431. msg.callback(result);
  432. },
  433. permadie: function () {
  434. this.obj.permadead = true;
  435. this.doSave(this.onPermadie.bind(this));
  436. },
  437. onPermadie: function () {
  438. process.send({
  439. method: 'object',
  440. serverId: this.obj.serverId,
  441. obj: {
  442. dead: true
  443. }
  444. });
  445. },
  446. getAccountLevel: function () {
  447. return this.accountInfo.level;
  448. }
  449. };