Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.
 
 
 

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