您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 

550 行
11 KiB

  1. //Imports
  2. const bcrypt = require('bcrypt-nodejs');
  3. const messages = require('../misc/messages');
  4. const skins = require('../config/skins');
  5. const roles = require('../config/roles');
  6. const profanities = require('../misc/profanities');
  7. const fixes = require('../fixes/fixes');
  8. const spirits = require('../config/spirits');
  9. const ga = require('../security/ga');
  10. const events = require('../misc/events');
  11. const checkLoginRewards = require('./auth/checkLoginRewards');
  12. //This section of code is in charge of ensuring that we only ever create one account at a time,
  13. // since we don't have a read/write lock on the characters table, we have to address it in code
  14. const createLockBuffer = [];
  15. const getCreateLock = async () => {
  16. const releaseLock = lockEntry => {
  17. createLockBuffer.spliceWhere(c => c === lockEntry);
  18. const nextEntry = createLockBuffer[0];
  19. if (!nextEntry)
  20. return;
  21. nextEntry.takeLock();
  22. };
  23. const promise = new Promise(async res => {
  24. let lockEntry = {};
  25. lockEntry.takeLock = res.bind(null, releaseLock.bind(null, lockEntry));
  26. if (!createLockBuffer.length) {
  27. createLockBuffer.push(lockEntry);
  28. lockEntry.takeLock();
  29. return;
  30. }
  31. createLockBuffer.push(lockEntry);
  32. });
  33. return promise;
  34. };
  35. //Component Definition
  36. module.exports = {
  37. type: 'auth',
  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. skins: this.skins
  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. events.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. events.emit('onAfterGetStash', {
  161. obj: this.obj,
  162. stash: this.stash
  163. });
  164. },
  165. getSkins: async function (character) {
  166. this.skins = await io.getAsync({
  167. key: this.username,
  168. table: 'skins',
  169. isArray: true
  170. });
  171. fixes.fixSkins(this.username, this.skins);
  172. },
  173. getSkinList: function (msg) {
  174. let list = [...this.skins, ...roles.getSkins(this.username)];
  175. let skinList = skins.getSkinList(list);
  176. msg.callback(skinList);
  177. },
  178. saveSkin: async function (skinId) {
  179. if (!this.skins) {
  180. this.getSkins({
  181. callback: this.saveSkin.bind(this, skinId)
  182. });
  183. return;
  184. }
  185. this.skins.push(skinId);
  186. await io.setAsync({
  187. key: this.username,
  188. table: 'skins',
  189. value: this.skins,
  190. serialize: true
  191. });
  192. },
  193. onSaveSkin: function () {
  194. },
  195. verifySkin: function (character) {
  196. let list = [...this.skins, ...roles.getSkins(this.username)];
  197. let skinList = skins.getSkinList(list);
  198. if (!skinList.some(s => (s.id === character.skinId))) {
  199. character.skinId = '1.0';
  200. character.cell = skins.getCell(character.skinId);
  201. character.sheetName = skins.getSpritesheet(character.skinId);
  202. }
  203. },
  204. doesOwnSkin: function (skinId) {
  205. let list = [...this.skins, ...roles.getSkins(this.username)];
  206. let skinList = skins.getSkinList(list);
  207. return skinList.some(s => s.id === (skinId + '') || s === '*');
  208. },
  209. login: async function (msg) {
  210. let credentials = msg.data;
  211. if (credentials.username === '' || credentials.password === '') {
  212. msg.callback(messages.login.allFields);
  213. return;
  214. }
  215. let storedPassword = await io.getAsync({
  216. key: credentials.username,
  217. table: 'login',
  218. noParse: true
  219. });
  220. bcrypt.compare(credentials.password, storedPassword, this.onLogin.bind(this, msg, storedPassword));
  221. },
  222. onLogin: async function (msg, storedPassword, err, compareResult) {
  223. if (!compareResult) {
  224. msg.callback(messages.login.incorrect);
  225. return;
  226. }
  227. this.username = msg.data.username;
  228. await cons.logOut(this.obj);
  229. this.initTracker();
  230. await this.getSkins();
  231. this.accountInfo = await io.getAsync({
  232. key: msg.data.username,
  233. table: 'accountInfo',
  234. noDefault: true
  235. }) || {
  236. loginStreak: 0
  237. };
  238. msg.callback();
  239. },
  240. initTracker: function () {
  241. this.gaTracker = ga.connect(this.username);
  242. },
  243. track: function (category, action, label, value = 1) {
  244. process.send({
  245. method: 'track',
  246. serverId: this.obj.serverId,
  247. obj: {
  248. category,
  249. action,
  250. label,
  251. value
  252. }
  253. });
  254. },
  255. register: async function (msg) {
  256. let credentials = msg.data;
  257. if ((credentials.username === '') || (credentials.password === '')) {
  258. msg.callback(messages.login.allFields);
  259. return;
  260. }
  261. let illegal = ["'", '"', '/', '(', ')', '[', ']', '{', '}', ':', ';', '<', '>', '+', '?', '*'];
  262. for (let i = 0; i < illegal.length; i++) {
  263. if (credentials.username.indexOf(illegal[i]) > -1) {
  264. msg.callback(messages.login.illegal);
  265. return;
  266. }
  267. }
  268. let exists = await io.getAsync({
  269. key: credentials.username,
  270. ignoreCase: true,
  271. table: 'login',
  272. noDefault: true,
  273. noParse: true
  274. });
  275. if (exists) {
  276. msg.callback(messages.login.exists);
  277. return;
  278. }
  279. bcrypt.hash(credentials.password, null, null, this.onHashGenerated.bind(this, msg));
  280. },
  281. onHashGenerated: async function (msg, err, hashedPassword) {
  282. await io.setAsync({
  283. key: msg.data.username,
  284. table: 'login',
  285. value: hashedPassword
  286. });
  287. this.accountInfo = {
  288. loginStreak: 0
  289. };
  290. await io.setAsync({
  291. key: msg.data.username,
  292. table: 'characterList',
  293. value: [],
  294. serialize: true
  295. });
  296. this.username = msg.data.username;
  297. cons.logOut(this.obj);
  298. await this.getSkins();
  299. msg.callback();
  300. },
  301. createCharacter: async function (msg) {
  302. let data = msg.data;
  303. let name = data.name;
  304. let error = null;
  305. if (name.length < 3 || name.length > 12)
  306. error = messages.createCharacter.nameLength;
  307. else if (!profanities.isClean(name))
  308. error = messages.login.invalid;
  309. else if (name.indexOf(' ') > -1)
  310. msg.callback(messages.login.invalid);
  311. else if (!spirits.list.includes(data.class))
  312. return;
  313. let nLen = name.length;
  314. for (let i = 0; i < nLen; i++) {
  315. let char = name[i].toLowerCase();
  316. let valid = [
  317. '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'
  318. ];
  319. if (!valid.includes(char)) {
  320. error = messages.login.invalid;
  321. break;
  322. }
  323. }
  324. if (error) {
  325. msg.callback(error);
  326. return;
  327. }
  328. const releaseCreateLock = await getCreateLock();
  329. let exists = await io.getAsync({
  330. key: name,
  331. ignoreCase: true,
  332. table: 'character',
  333. noDefault: true
  334. });
  335. if (exists) {
  336. releaseCreateLock();
  337. msg.callback(messages.login.charExists);
  338. return;
  339. }
  340. let obj = this.obj;
  341. extend(obj, {
  342. name: name,
  343. skinId: data.skinId,
  344. class: data.class,
  345. cell: skins.getCell(data.skinId),
  346. sheetName: skins.getSpritesheet(data.skinId),
  347. x: null,
  348. y: null
  349. });
  350. let simple = this.obj.getSimple(true);
  351. this.verifySkin(simple);
  352. let prophecies = (data.prophecies || []).filter(p => p);
  353. simple.components.push({
  354. type: 'prophecies',
  355. list: prophecies
  356. }, {
  357. type: 'social',
  358. customChannels: this.customChannels
  359. });
  360. await io.setAsync({
  361. key: name,
  362. table: 'character',
  363. value: simple,
  364. serialize: true
  365. });
  366. this.characters[name] = simple;
  367. this.characterList.push(name);
  368. await io.setAsync({
  369. key: this.username,
  370. table: 'characterList',
  371. value: this.characterList,
  372. serialize: true
  373. });
  374. releaseCreateLock();
  375. this.initTracker();
  376. this.play({
  377. data: {
  378. name: name
  379. },
  380. callback: msg.callback
  381. });
  382. },
  383. deleteCharacter: async function (msg) {
  384. let data = msg.data;
  385. if ((!data.name) || (!this.username))
  386. return;
  387. if (!this.characterList.some(c => ((c.name === data.name) || (c === data.name)))) {
  388. msg.callback([]);
  389. return;
  390. }
  391. await io.deleteAsync({
  392. key: data.name,
  393. table: 'character'
  394. });
  395. let name = data.name;
  396. this.characterList.spliceWhere(c => (c.name === name || c === name));
  397. let characterList = this.characterList
  398. .map(c => ({
  399. name: c.name ? c.name : c,
  400. level: leaderboard.getLevel(c.name ? c.name : c)
  401. }));
  402. await io.setAsync({
  403. key: this.username,
  404. table: 'characterList',
  405. value: characterList,
  406. serialize: true
  407. });
  408. await leaderboard.deleteCharacter(name);
  409. let result = this.characterList
  410. .map(c => ({
  411. name: c.name ? c.name : c,
  412. level: leaderboard.getLevel(c.name ? c.name : c)
  413. }));
  414. msg.callback(result);
  415. },
  416. permadie: function () {
  417. this.obj.permadead = true;
  418. this.doSave(this.onPermadie.bind(this));
  419. },
  420. onPermadie: function () {
  421. process.send({
  422. method: 'object',
  423. serverId: this.obj.serverId,
  424. obj: {
  425. dead: true
  426. }
  427. });
  428. }
  429. };