Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.
 
 
 

551 rader
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. await 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: async function (character) {
  166. const doesOwn = await 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: async 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. await 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. await 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. await 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. await eventEmitter.emit('onBeforeGetAccountInfo', msgAccountInfo);
  231. await eventEmitter.emit('onAfterLogin', { username });
  232. this.accountInfo = msgAccountInfo.accountInfo;
  233. msg.callback();
  234. },
  235. initTracker: function () {
  236. this.gaTracker = ga.connect(this.username);
  237. },
  238. track: function (category, action, label, value = 1) {
  239. process.send({
  240. method: 'track',
  241. serverId: this.obj.serverId,
  242. obj: {
  243. category,
  244. action,
  245. label,
  246. value
  247. }
  248. });
  249. },
  250. register: async function (msg) {
  251. let credentials = msg.data;
  252. if ((credentials.username === '') || (credentials.password === '')) {
  253. msg.callback(messages.login.allFields);
  254. return;
  255. }
  256. let illegal = ["'", '"', '/', '\\', '(', ')', '[', ']', '{', '}', ':', ';', '<', '>', '+', '?', '*'];
  257. for (let i = 0; i < illegal.length; i++) {
  258. if (credentials.username.indexOf(illegal[i]) > -1) {
  259. msg.callback(messages.login.illegal);
  260. return;
  261. }
  262. }
  263. let exists = await io.getAsync({
  264. key: credentials.username,
  265. ignoreCase: true,
  266. table: 'login',
  267. noDefault: true,
  268. noParse: true
  269. });
  270. if (exists) {
  271. msg.callback(messages.login.exists);
  272. return;
  273. }
  274. bcrypt.hash(credentials.password, null, null, this.onHashGenerated.bind(this, msg));
  275. },
  276. onHashGenerated: async function (msg, err, hashedPassword) {
  277. await io.setAsync({
  278. key: msg.data.username,
  279. table: 'login',
  280. value: hashedPassword
  281. });
  282. this.accountInfo = {
  283. loginStreak: 0
  284. };
  285. await io.setAsync({
  286. key: msg.data.username,
  287. table: 'characterList',
  288. value: [],
  289. serialize: true
  290. });
  291. this.username = msg.data.username;
  292. cons.logOut(this.obj);
  293. msg.callback();
  294. },
  295. createCharacter: async function (msg) {
  296. let data = msg.data;
  297. let name = data.name;
  298. let error = null;
  299. if (name.length < 3 || name.length > 12)
  300. error = messages.createCharacter.nameLength;
  301. else if (!profanities.isClean(name))
  302. error = messages.login.invalid;
  303. else if (name.indexOf(' ') > -1)
  304. msg.callback(messages.login.invalid);
  305. else if (!spirits.list.includes(data.class))
  306. return;
  307. let nLen = name.length;
  308. for (let i = 0; i < nLen; i++) {
  309. let char = name[i].toLowerCase();
  310. let valid = [
  311. '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'
  312. ];
  313. if (!valid.includes(char)) {
  314. error = messages.login.invalid;
  315. break;
  316. }
  317. }
  318. if (error) {
  319. msg.callback(error);
  320. return;
  321. }
  322. const releaseCreateLock = await getCreateLock();
  323. let exists = await io.getAsync({
  324. key: name,
  325. ignoreCase: true,
  326. table: 'character',
  327. noDefault: true
  328. });
  329. if (exists) {
  330. releaseCreateLock();
  331. msg.callback(messages.login.charExists);
  332. return;
  333. }
  334. let obj = this.obj;
  335. extend(obj, {
  336. name: name,
  337. skinId: data.skinId,
  338. class: data.class,
  339. cell: skins.getCell(data.skinId),
  340. sheetName: skins.getSpritesheet(data.skinId),
  341. x: null,
  342. y: null
  343. });
  344. let simple = this.obj.getSimple(true);
  345. await this.verifySkin(simple);
  346. let prophecies = (data.prophecies || []).filter(p => p);
  347. simple.components.push({
  348. type: 'prophecies',
  349. list: prophecies
  350. }, {
  351. type: 'social',
  352. customChannels: this.customChannels
  353. });
  354. await io.setAsync({
  355. key: name,
  356. table: 'character',
  357. value: simple,
  358. serialize: true
  359. });
  360. this.characters[name] = simple;
  361. this.characterList.push(name);
  362. await io.setAsync({
  363. key: this.username,
  364. table: 'characterList',
  365. value: this.characterList,
  366. serialize: true
  367. });
  368. releaseCreateLock();
  369. this.initTracker();
  370. this.play({
  371. data: {
  372. name: name
  373. },
  374. callback: msg.callback
  375. });
  376. },
  377. deleteCharacter: async function (msg) {
  378. let data = msg.data;
  379. if ((!data.name) || (!this.username))
  380. return;
  381. if (!this.characterList.some(c => ((c.name === data.name) || (c === data.name)))) {
  382. msg.callback([]);
  383. return;
  384. }
  385. await io.deleteAsync({
  386. key: data.name,
  387. table: 'character'
  388. });
  389. let name = data.name;
  390. this.characterList.spliceWhere(c => (c.name === name || c === name));
  391. let characterList = this.characterList
  392. .map(c => ({
  393. name: c.name ? c.name : c,
  394. level: leaderboard.getLevel(c.name ? c.name : c)
  395. }));
  396. await io.setAsync({
  397. key: this.username,
  398. table: 'characterList',
  399. value: characterList,
  400. serialize: true
  401. });
  402. await leaderboard.deleteCharacter(name);
  403. let result = this.characterList
  404. .map(c => ({
  405. name: c.name ? c.name : c,
  406. level: leaderboard.getLevel(c.name ? c.name : c)
  407. }));
  408. msg.callback(result);
  409. },
  410. permadie: function () {
  411. this.obj.permadead = true;
  412. this.doSave(this.onPermadie.bind(this));
  413. },
  414. onPermadie: function () {
  415. process.send({
  416. method: 'object',
  417. serverId: this.obj.serverId,
  418. obj: {
  419. dead: true
  420. }
  421. });
  422. },
  423. getAccountLevel: function () {
  424. return this.accountInfo.level;
  425. }
  426. };