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.
 
 
 

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