浏览代码

AQ update

master
peter 5 年前
父节点
当前提交
7c1b0b73ed
共有 35 个文件被更改,包括 1342 次插入880 次删除
  1. 2
    0
      .gitignore
  2. 二进制
      live_db/frontworkAdmin.sqlite
  3. 14
    22
      migrations/000_signups_addColumn_memo_timestamp.js
  4. 1
    1
      migrations/001_items_addColumn_stats.js
  5. 359
    289
      package-lock.json
  6. 3
    2
      package.json
  7. 9
    5
      src/backend/Admin/Admin.ts
  8. 2
    3
      src/backend/Components/Character/CharacterManager.ts
  9. 3
    3
      src/backend/Components/Guild/GuildManager.ts
  10. 3
    1
      src/backend/Components/Item/Interface.ts
  11. 43
    17
      src/backend/Components/Item/ItemManager.ts
  12. 1
    1
      src/backend/Components/PluginLoader.ts
  13. 1
    1
      src/backend/Components/PubSub/PubSub.ts
  14. 1
    1
      src/backend/Components/RPCConfigLoader.ts
  15. 3
    2
      src/backend/Components/Raid/Interface.ts
  16. 2
    1
      src/backend/Components/Raid/RPCInterface.ts
  17. 75
    39
      src/backend/Components/Raid/RaidManager.ts
  18. 24
    25
      src/backend/Components/User/UserManager.ts
  19. 2
    2
      src/backend/Types/FrontworkComponent.ts
  20. 417
    281
      src/backend/Types/Items.ts
  21. 1
    1
      src/backend/Types/Plugin.ts
  22. 1
    1
      src/backend/Types/PrivilegedRPCExporter.ts
  23. 3
    1
      src/backend/Types/Types.ts
  24. 239
    56
      src/frontend/package-lock.json
  25. 2
    2
      src/frontend/package.json
  26. 1
    0
      src/frontend/src/app/frontcraft/pages/armory/armory.component.ts
  27. 0
    19
      src/frontend/src/app/frontcraft/pages/character/character.component.html
  28. 15
    6
      src/frontend/src/app/frontcraft/pages/raid/characterpicker.component.html
  29. 2
    2
      src/frontend/src/app/frontcraft/pages/raid/characterpicker.component.ts
  30. 32
    50
      src/frontend/src/app/frontcraft/pages/raid/raid.component.html
  31. 40
    8
      src/frontend/src/app/frontcraft/pages/raid/raid.component.ts
  32. 5
    5
      src/frontend/src/app/frontcraft/pages/shop/buytoken.component.ts
  33. 3
    3
      src/frontend/src/app/frontcraft/services/ApiService/client-login-api.ts
  34. 2
    2
      src/frontend/src/app/frontcraft/services/ApiService/server-login-api.ts
  35. 31
    28
      test/backendTest.ts

+ 2
- 0
.gitignore 查看文件

@@ -13,6 +13,8 @@ log.txt
13 13
 *.d.ts
14 14
 *.js
15 15
 *.ts
16
+log.txt
17
+
16 18
 
17 19
 !src/**/*
18 20
 node_modules

二进制
live_db/frontworkAdmin.sqlite 查看文件


+ 14
- 22
migrations/000_signups_addColumn_memo_timestamp.js 查看文件

@@ -11,28 +11,20 @@ exports.up = function (knex) {
11 11
                     output: process.stdout
12 12
                 })
13 13
                 return new Promise((res, rej) => {
14
-                    r1.question('WARNING\n\nAbout to update your signups table. Please make sure there are no active signups. \nContinue? y/N', answer => {
15
-                        if(!answer || answer === "" || answer === "n" || answer === "N"){
16
-                            rej(new Error("User aborted"))
17
-                        }else{
18
-                            knex('signups').select('*').then(rows => {
19
-                                return knex.schema.dropTable('signups').then(_ => {
20
-                                    return knex.schema.createTable('signups', function (table) {
21
-                                        table.increments('id').primary()
22
-                                        table.unique(['raidid', 'characterid'])
23
-                                        table.integer('raidid')
24
-                                        table.foreign('raidid').references('id').inTable('raids').onDelete('CASCADE')
25
-                                        table.integer('characterid')
26
-                                        table.foreign('characterid').references('id').inTable('characters').onDelete('CASCADE')
27
-                                        table.boolean('benched').defaultTo('false')
28
-                                        table.boolean('late')
29
-                                        table.timestamp('timestamp').defaultTo(knex.fn.now())
30
-                                        table.string('memo').nullable()
31
-                                        res()
32
-                                    }).catch(rej)
33
-                                })
34
-                            })
35
-                        }
14
+                    return knex.schema.dropTable('signups').then(_ => {
15
+                        return knex.schema.createTable('signups', function (table) {
16
+                            table.increments('id').primary()
17
+                            table.unique(['raidid', 'characterid'])
18
+                            table.integer('raidid')
19
+                            table.foreign('raidid').references('id').inTable('raids').onDelete('CASCADE')
20
+                            table.integer('characterid')
21
+                            table.foreign('characterid').references('id').inTable('characters').onDelete('CASCADE')
22
+                            table.boolean('benched').defaultTo('false')
23
+                            table.boolean('late')
24
+                            table.timestamp('timestamp').defaultTo(knex.fn.now())
25
+                            table.string('memo').nullable()
26
+                            res()
27
+                        }).catch(rej)
36 28
                     })
37 29
                 })
38 30
             }

+ 1
- 1
migrations/001_items_addColumn_stats.js 查看文件

@@ -12,7 +12,7 @@ exports.up = function (knex) {
12 12
 
13 13
 
14 14
 exports.down = function (knex) {
15
-    return knex.schema.table('signups', function (table) {
15
+    return knex.schema.table('items', function (table) {
16 16
         table.dropColumn('stats')
17 17
     })
18 18
 }

+ 359
- 289
package-lock.json
文件差异内容过多而无法显示
查看文件


+ 3
- 2
package.json 查看文件

@@ -26,6 +26,7 @@
26 26
   "license": "ISC",
27 27
   "dependencies": {
28 28
     "@types/mocha": "^5.2.7",
29
+    "@types/node": "^14.0.27",
29 30
     "bsert": "0.0.10",
30 31
     "bsock": "^0.1.9",
31 32
     "child-process-promise": "^2.2.1",
@@ -47,10 +48,10 @@
47 48
     "path": "^0.12.7",
48 49
     "reflect-metadata": "^0.1.13",
49 50
     "rimraf": "^3.0.0",
50
-    "rpclibrary": "^1.10.2",
51
+    "rpclibrary": "^2.0.1",
51 52
     "simple-git": "^1.124.0",
52 53
     "spawn-sync": "^2.0.0",
53
-    "sqlite3": "^4.1.1",
54
+    "sqlite3": "^4.2.0",
54 55
     "trash": "^6.0.0",
55 56
     "tsyringe": "^4.0.1",
56 57
     "upgiter": "^1.0.4",

+ 9
- 5
src/backend/Admin/Admin.ts 查看文件

@@ -79,7 +79,7 @@ export class FrontworkAdmin
79 79
         ])
80 80
             .catch(e => getLogger('Admin#stop').warn(e))
81 81
             .finally(() => {
82
-                this.rpcServer.destroy()
82
+                this.rpcServer.close()
83 83
                 process.exit(0)
84 84
             })
85 85
     }
@@ -107,18 +107,22 @@ export class FrontworkAdmin
107 107
     }
108 108
 
109 109
     private startWebsocket() {
110
-        this.rpcServer = new RPCServer(20000, [
110
+        this.rpcServer = new RPCServer([
111 111
             ...this.frontworkComponents,
112 112
             {
113 113
                 name: "debug",
114
-                exportRPCs: () => [{
114
+                RPCs: () => [{
115 115
                     name: 'dumpDb',
116 116
                     call: async (table) => await this.knex(table).select('*')
117 117
                 }]
118 118
             }
119 119
         ], {
120
-            visibility: '0.0.0.0'
121
-        })
120
+            errorHandler: (sock, err, rpc, args) => {
121
+                console.log(rpc, err);
122
+                
123
+            }
124
+
125
+        }).listen(20000)
122 126
         getLogger('Admin#startWebsocket').debug("Websocket up on", 20000)
123 127
     }
124 128
 

+ 2
- 3
src/backend/Components/Character/CharacterManager.ts 查看文件

@@ -20,7 +20,7 @@ implements FrontworkComponent<CharacterManagerIfc, RPCInterface>, ICharacterMana
20 20
     @Inject(IUserManager)
21 21
     private loginManager : IUserManager
22 22
 
23
-    exportRPCs = () => [
23
+    RPCs = () => [
24 24
             this.getSpecId,
25 25
             this.getCharacterByName,
26 26
             this.getCharacters,
@@ -30,8 +30,7 @@ implements FrontworkComponent<CharacterManagerIfc, RPCInterface>, ICharacterMana
30 30
             this.getHeadCount
31 31
     ]
32 32
 
33
-    exportRPCFeatures = () => [
34
-    ]
33
+    RPCFeatures = () => []
35 34
     
36 35
     getTableDefinitions = (): TableDefiniton[] => [
37 36
         {

+ 3
- 3
src/backend/Components/Guild/GuildManager.ts 查看文件

@@ -28,14 +28,14 @@ implements FrontworkComponent<GuildManagerIfc, GuildManagerFeatureIfc>, IGuildMa
28 28
         }
29 29
     }, "./config")
30 30
 
31
-    exportRPCs = () => [
31
+    RPCs = () => [
32 32
         this.getHeadCount,
33 33
         this.getGuildInfo
34 34
     ]
35 35
 
36
-    exportRPCFeatures = () => [{
36
+    RPCFeatures = () => [{
37 37
         name: 'manageGuild' as 'manageGuild',
38
-        exportRPCs: () => [
38
+        RPCs: () => [
39 39
             this.setName,
40 40
             this.setRealm,
41 41
             this.setDescription

+ 3
- 1
src/backend/Components/Item/Interface.ts 查看文件

@@ -9,8 +9,10 @@ export class IItemManager{
9 9
     setPriority: (itemname:string, priority: any) => Promise<void>
10 10
     calculatePriorities: (itemname: string, character:Character) => Promise<number>
11 11
     deletePriority: (priority:SRPriority) => Promise<void>
12
-    getTokens: (character:Character, tiers: Tiers[], valid?:boolean) => Promise<(SRToken & Character & Item)[] | undefined>
12
+    getTokens: (character:Character, tiers: Tiers[], valid:boolean) => Promise<(SRToken & Character & Item)[] | undefined>
13 13
     getToken: (character:Character, item:Item, valid?:boolean) => Promise<(SRToken & Character & Item) | undefined>
14 14
     getAllPriorities: () => Promise<(SRPriority & Spec & Item)[]>
15 15
     wipeCurrencyAndItems: () => Promise<void>
16
+    decayTokens: (tier: Tiers, amount?:number) => Promise<void>
17
+    decayTokensOfCharacter: (tier: Tiers, character:Character, amount?:number) => Promise<void>
16 18
 }

+ 43
- 17
src/backend/Components/Item/ItemManager.ts 查看文件

@@ -10,7 +10,7 @@ import { IUserManager } from "../User/Interface";
10 10
 import { ICharacterManager } from "../Character/Interface";
11 11
 import { IPubSub } from "../PubSub/Interface";
12 12
 import { IRaidManager } from "../Raid/Interface";
13
-import { getLogger } from "frontblock-generic/Types";
13
+import { getLogger } from "log4js";
14 14
 
15 15
 const fetch = require('node-fetch')
16 16
 const xml2js = require('xml2js');
@@ -36,7 +36,7 @@ export class ItemManager
36 36
     @Inject(IRaidManager)
37 37
     private raidManager: IRaidManager
38 38
 
39
-    exportRPCs = () => [
39
+    RPCs = () => [
40 40
         this.getItems,
41 41
         this.getItem,
42 42
         this.buyToken,
@@ -46,15 +46,15 @@ export class ItemManager
46 46
         this.getAllPriorities
47 47
     ]
48 48
 
49
-    exportRPCFeatures = () => [{
49
+    RPCFeatures = () => [{
50 50
         name: 'managePriorities' as 'managePriorities',
51
-        exportRPCs: () => [
51
+        RPCs: () => [
52 52
             this.setPriority,
53 53
             this.deletePriority
54 54
         ],
55 55
     }, {
56 56
         name: 'reset' as 'reset',
57
-        exportRPCs: () => [
57
+        RPCs: () => [
58 58
             this.wipeCurrencyAndItems
59 59
         ]
60 60
     }]
@@ -129,7 +129,7 @@ export class ItemManager
129 129
             return item
130 130
 
131 131
         }catch(e){
132
-            getLogger('ItemManager', 'error').error(name, e);
132
+            getLogger('ItemManager').error(name, e);
133 133
         }
134 134
     }
135 135
 
@@ -191,14 +191,14 @@ export class ItemManager
191 191
         const streaks = await this.getTokens(character, [item.tier], false)
192 192
         const activeTokens = await this.getTokens(character, [item.tier], true)
193 193
 
194
-        await this.userManager.decrementCurrency(record.user, item.tier, 1)
194
+        await this.userManager.decrementCurrency(record.user, item.tier, currency)
195 195
         const modifier = await this.calculatePriorities(itemname, character)
196 196
 
197 197
         const tx = await this.admin.knex.transaction()
198 198
 
199
-        if (streaks.length > 0) {
199
+        if (streaks.length > 0) { //if an old streak already exists
200 200
             const myStreak = streaks.find(token => token.itemname === itemname)
201
-            if (myStreak) {
201
+            if (myStreak) { //and the streak is for this item
202 202
                 //getLogger('ItemManager').debug('update signupid and increment level')
203 203
                 await this.admin
204 204
                     .knex(item.tier + 'tokens')
@@ -209,12 +209,12 @@ export class ItemManager
209 209
                     })
210 210
                     .update({
211 211
                         signupid: signup.id,
212
-                        level: myStreak.level + 1
212
+                        level: myStreak.level + currency + 1
213 213
                     })
214 214
 
215 215
             }
216 216
 
217
-            //getLogger('ItemManager').debug('delete streaks')
217
+            //getLogger('ItemManager').debug('delete other streaks')
218 218
             await Promise.all(streaks.map(async s => await this.admin
219 219
                 .knex(item.tier + 'tokens')
220 220
                 .transacting(tx)
@@ -226,35 +226,36 @@ export class ItemManager
226 226
                 .del()
227 227
             ))
228 228
 
229
+            //If there was a streak for this item we're done
229 230
             if (myStreak) {
230 231
                 await tx.commit()
231 232
                 await this.notifyRaid({ id: signup.raidid })
232
-
233 233
                 return await this.getToken(character, item)
234 234
             }
235 235
         }
236 236
 
237
+        //check if there is an active reserve for this item
237 238
         const matchingReserve = activeTokens.find(token => token.itemname === itemname)
238 239
         if (matchingReserve) {
239
-            //getLogger('ItemManager').debug('upgrade reserve')
240
+            getLogger('ItemManager').debug('upgrade reserve')
240 241
             await this.admin
241 242
                 .knex(item.tier + 'tokens')
242 243
                 .transacting(tx)
243
-                .increment('level')
244
+                .increment('level', currency + 1)
244 245
                 .where({
245 246
                     signupid: signup.id,
246 247
                     characterid: character.id,
247 248
                     itemname: item.itemname
248 249
                 })
249 250
         } else {
250
-            //getLogger('ItemManager').debug('new reserve')
251
+            getLogger('ItemManager').debug('new reserve')
251 252
             await this.admin
252 253
                 .knex(item.tier + 'tokens')
253 254
                 .transacting(tx)
254 255
                 .insert({
255 256
                     characterid: character.id,
256 257
                     itemname: item.itemname,
257
-                    level: 1 + modifier,
258
+                    level: 1 + modifier + currency,
258 259
                     signupid: signup.id
259 260
                 })
260 261
         }
@@ -346,7 +347,7 @@ export class ItemManager
346 347
             .first()
347 348
     }
348 349
 
349
-    getTokens = async (character: Character, tiers: Tiers[], valid = true): Promise<(SRToken & Character & Item)[]> => {
350
+    getTokens = async (character: Character, tiers: Tiers[], valid:boolean): Promise<(SRToken & Character & Item)[]> => {
350 351
         const ret = await Promise.all(tiers.map(async tier => {
351 352
             return await this.admin
352 353
                 .knex(tier + 'tokens as t')
@@ -367,6 +368,31 @@ export class ItemManager
367 368
         return ret.flat()
368 369
     }
369 370
 
371
+    decayTokens = async (tier: Tiers, amount = 1) => {
372
+        await this.admin
373
+        .knex(tier + 'tokens')
374
+        .decrement('level', Math.max(amount, 1))
375
+
376
+        await this.admin
377
+        .knex(tier + 'tokens as t')
378
+        .where('level', '<=', 0)
379
+        .del()
380
+    }
381
+
382
+    decayTokensOfCharacter = async (tier: Tiers, character:Character, amount = 1) => {
383
+        await this.admin
384
+        .knex(tier + 'tokens')
385
+        .where({
386
+            characterid: character.id
387
+        })
388
+        .decrement('level', Math.max(amount, 1))
389
+
390
+        await this.admin
391
+        .knex(tier + 'tokens as t')
392
+        .where('level', '<=', 0)
393
+        .del()
394
+    }
395
+
370 396
     countItems = async (): Promise<number> => {
371 397
         const count = await this.admin
372 398
             .knex('items')

+ 1
- 1
src/backend/Components/PluginLoader.ts 查看文件

@@ -143,7 +143,7 @@ implements RPCExporter<PluginLoaderIfc, "PluginLoader">{
143 143
 
144 144
     name = "PluginLoader" as "PluginLoader";
145 145
 
146
-    exportRPCs(){
146
+    RPCs(){
147 147
         return [{
148 148
             name: "installPlugin" as "installPlugin",
149 149
             call: this.installPlugin,

+ 1
- 1
src/backend/Components/PubSub/PubSub.ts 查看文件

@@ -13,7 +13,7 @@ implements RPCExporter{
13 13
         }
14 14
     }= {}
15 15
 
16
-    exportRPCs = () => [
16
+    RPCs = () => [
17 17
         this.unsubscribe,
18 18
         {
19 19
             name: 'subscribe',

+ 1
- 1
src/backend/Components/RPCConfigLoader.ts 查看文件

@@ -18,7 +18,7 @@ implements RPCExporter<ConfigLoaderIfc<ConfT>, "Config">{
18 18
 
19 19
     name = "Config" as "Config"
20 20
 
21
-    exportRPCs = () => [
21
+    RPCs = () => [
22 22
         this.getConfig,
23 23
         this.resetConfig,
24 24
         this.setConfig,

+ 3
- 2
src/backend/Components/Raid/Interface.ts 查看文件

@@ -6,13 +6,14 @@ export class IRaidManager{
6 6
     addSignup: (signup: Signup) => Promise<Signup>
7 7
     removeSignup: (signup: Signup) => Promise<any>    
8 8
     getSignups: (raid:Raid) => Promise<(Signup & Character & Spec & User)[]>
9
-    sign: (userToken: string, character:Character, raid:Raid, late:boolean, memo?: string) => Promise<any>
9
+    sign: (userToken: string, character:Character, raid:Raid, late:boolean, absent:boolean, memo?: string) => Promise<any>
10 10
     unsign: (userToken: string, character:Character, raid:Raid,) => Promise<any>
11 11
     archiveRaid: (raid:Raid) => Promise<RaidData>
12 12
     getRaidData: (raid:Raid) => Promise<RaidData>
13
-    setBenched: (signup: Signup) => Promise<void>
13
+    updateSignup: (signup: Signup) => Promise<void>
14 14
     getPastRaids: (limit: number) => Promise<RaidData[]>
15 15
     getArchiveRaid: (id:number) => Promise<RaidData>
16 16
     startRaid: (raid:Raid) => Promise<RaidData>
17 17
     adminUnsign: (character:Character, raid:Raid) => Promise<any>
18
+    cancelRaid: (raid: Raid) => Promise<void>
18 19
 }

+ 2
- 1
src/backend/Components/Raid/RPCInterface.ts 查看文件

@@ -15,9 +15,10 @@ export type RaidManagerFeatureIfc = {
15 15
         addSignup: IRaidManager['addSignup']
16 16
         removeSignup: IRaidManager['removeSignup'] 
17 17
         archiveRaid: IRaidManager['archiveRaid']
18
-        setBenched: IRaidManager['setBenched']
18
+        updateSignup: IRaidManager['updateSignup']
19 19
         startRaid: IRaidManager['startRaid']
20 20
         adminUnsign: IRaidManager['adminUnsign']
21
+        cancelRaid: IRaidManager['cancelRaid']
21 22
     }
22 23
     signup: {
23 24
         getSignups: IRaidManager['getSignups']

+ 75
- 39
src/backend/Components/Raid/RaidManager.ts 查看文件

@@ -33,28 +33,29 @@ export class RaidManager
33 33
     @Inject(IPubSub)
34 34
     private pubsub: IPubSub<any>
35 35
 
36
-    exportRPCs = () => [
36
+    RPCs = () => [
37 37
         this.getRaids,
38 38
         this.getRaidData,
39 39
         this.getPastRaids,
40 40
         this.getArchiveRaid
41 41
     ]
42 42
 
43
-    exportRPCFeatures() {
43
+    RPCFeatures() {
44 44
         return [{
45 45
             name: 'manageRaid' as 'manageRaid',
46
-            exportRPCs: () => [
46
+            RPCs: () => [
47 47
                 this.createRaid,
48 48
                 this.addSignup,
49 49
                 this.removeSignup,
50 50
                 this.archiveRaid,
51
-                this.setBenched,
51
+                this.updateSignup,
52 52
                 this.startRaid,
53
-                this.adminUnsign
53
+                this.adminUnsign,
54
+                this.cancelRaid
54 55
             ]
55 56
         }, {
56 57
             name: 'signup' as 'signup',
57
-            exportRPCs: () => [
58
+            RPCs: () => [
58 59
                 this.getSignups,
59 60
                 this.sign,
60 61
                 this.unsign
@@ -98,20 +99,20 @@ export class RaidManager
98 99
         ]
99 100
     }
100 101
 
101
-    notifyRaid = async (raid:Raid | {id:number}) => {
102
+    notifyRaid = async (raid: Raid | { id: number }) => {
102 103
         const data = await this.getRaidData(<Raid>raid)
103
-        try{
104
+        try {
104 105
             await this.pubsub.publish(String(raid.id), data)
105 106
             await this.notifyRaids()
106
-        }catch(e){
107
+        } catch (e) {
107 108
             getLogger('RaidManager#notifyRaid').debug(e);
108 109
         }
109 110
     }
110 111
 
111 112
     notifyRaids = async () => {
112
-        try{
113
+        try {
113 114
             await this.pubsub.publish('raids', undefined)
114
-        }catch(e){
115
+        } catch (e) {
115 116
             getLogger('RaidManager#notifyRaids').debug(e);
116 117
         }
117 118
     }
@@ -171,6 +172,9 @@ export class RaidManager
171 172
     }
172 173
 
173 174
     startRaid = async (raid: Raid): Promise<RaidData> => {
175
+        const data = await this.getRaidData(raid)
176
+        data.participants.absent.map(p => this.itemManager.decayTokensOfCharacter(raid.tier, p, 1))
177
+        await this.itemManager.decayTokens(data.tier)      
174 178
         const archived = await this.archiveRaid(raid)
175 179
 
176 180
         const giveCurrency = async (b: Character) => {
@@ -180,17 +184,15 @@ export class RaidManager
180 184
 
181 185
         await Promise.all([
182 186
             ...archived.participants.bench.map(giveCurrency),
183
-            ...Object.values(archived.participants).flat().map((b:Signup & Character & Spec) => giveCurrency(b))
187
+            ...Object.values(archived.participants).flat().map((b: Signup & Character & Spec) => giveCurrency(b))
184 188
         ])
185 189
 
186 190
         await this.notifyRaids()
187
-
188 191
         return archived
189 192
     }
190 193
 
191 194
     archiveRaid = async (raid: Raid): Promise<RaidData> => {
192 195
         const raidData = await this.getRaidData(raid)
193
-
194 196
         const tx = await this.admin.knex.transaction()
195 197
 
196 198
         await this.admin.knex('archive')
@@ -201,15 +203,15 @@ export class RaidManager
201 203
             })
202 204
 
203 205
         await Promise.all(
204
-            Object.values(raidData.participants).flat().flatMap((p: (Signup & Character & Spec)) => 
206
+            Object.values(raidData.participants).flat().flatMap((p: (Signup & Character & Spec)) =>
205 207
                 this.admin
206
-                .knex(raid.tier + 'tokens')
207
-                .transacting(tx)
208
-                .where({
209
-                    characterid: p.characterid,
210
-                    signupid: null
211
-                })
212
-                .del()
208
+                    .knex(raid.tier + 'tokens')
209
+                    .transacting(tx)
210
+                    .where({
211
+                        characterid: p.characterid,
212
+                        signupid: null
213
+                    })
214
+                    .del()
213 215
             ))
214 216
 
215 217
         await this.admin.knex('raids')
@@ -225,15 +227,30 @@ export class RaidManager
225 227
                 id: raidData.id,
226 228
             })
227 229
             .first()
228
-        return JSON.parse(row.raiddata)
230
+
231
+        try {
232
+            return JSON.parse(row.raiddata)
233
+        } catch (e) {
234
+            getLogger('RaidManager#archiveRaid').error(row.id + " could not get parsed")
235
+            return {} as RaidData
236
+        }
229 237
     }
230 238
 
231 239
     getArchiveRaid = async (id: number): Promise<RaidData> => {
232
-        const data = await this.admin.knex('archive').select('raiddata').where({
233
-            id: id
234
-        }).first()
240
+        const data = await this.admin
241
+            .knex('archive')
242
+            .select('raiddata')
243
+            .where({
244
+                id: id
245
+            })
246
+            .first()
235 247
 
236
-        return JSON.parse(data.raiddata)
248
+        try {
249
+            return JSON.parse(data.raiddata)
250
+        } catch (e) {
251
+            getLogger('RaidManager#getArchiveRaid').error(id + " could not get parsed")
252
+            return {} as RaidData
253
+        }
237 254
     }
238 255
 
239 256
     getPastRaids = async (limit: number): Promise<RaidData[]> => {
@@ -259,6 +276,7 @@ export class RaidManager
259 276
                 Warrior: <(Signup & Character & Spec)[]>[],
260 277
                 late: <(Signup & Character & Spec)[]>[],
261 278
                 bench: <(Signup & Character & Spec)[]>[],
279
+                absent: <(Signup & Character & Spec)[]>[]
262 280
             },
263 281
             tokens: {},
264 282
             healers: <(Signup & Character & Spec)[]>[],
@@ -271,7 +289,8 @@ export class RaidManager
271 289
             .where({
272 290
                 raidid: this.admin.knex.ref('raids.id'),
273 291
                 benched: false,
274
-                late: false
292
+                late: false,
293
+                absent: false
275 294
             })
276 295
             .as('signupcount')
277 296
 
@@ -283,7 +302,7 @@ export class RaidManager
283 302
 
284 303
         const characterData: (Signup & Character & Spec)[] = await this.admin
285 304
             .knex('signups as s')
286
-            .select('s.id as id', 'charactername', 'rank', 'class', 'specid', 'specname', 'race', 'userid', 'benched', 'late', 'raidid', 'characterid', 'specid', 'memo', 'timestamp')
305
+            .select('s.id as id', 'charactername', 'rank', 'class', 'specid', 'specname', 'race', 'userid', 'benched', 'late', 'raidid', 'characterid', 'specid', 'memo', 'timestamp', 'absent')
287 306
             .join('raids as r', 's.raidid', '=', 'r.id')
288 307
             .join('characters as c', 's.characterid', '=', 'c.id')
289 308
             .join('users as u', 'c.userid', '=', 'u.id')
@@ -291,6 +310,10 @@ export class RaidManager
291 310
             .where('r.id', '=', raid.id)
292 311
 
293 312
         characterData.forEach(data => {
313
+            if(data.absent){
314
+                raiddata.participants.absent.push(data)
315
+                return
316
+            }
294 317
             if (data.benched) {
295 318
                 raiddata.participants.bench.push(data)
296 319
                 return
@@ -307,7 +330,7 @@ export class RaidManager
307 330
             .select('*', 's.id as id')
308 331
             .join('raids as r', 's.raidid', '=', 'r.id')
309 332
             .join('characters as c', 's.characterid', '=', 'c.id')
310
-            .join('users as u', 'u.id', '=' ,'c.userid')
333
+            .join('users as u', 'u.id', '=', 'c.userid')
311 334
             .join(raidInDb.tier + 'tokens as t', 't.characterid', '=', 'c.id')
312 335
             .join('items as i', 'i.itemname', '=', 't.itemname')
313 336
             .where({
@@ -327,6 +350,7 @@ export class RaidManager
327 350
         raiddata.tanks = Object.values(raiddata.participants).flatMap(
328 351
             (tanks: any[]) => tanks.filter((p: any) =>
329 352
                 !p.benched
353
+                && !p.absent
330 354
                 && !p.late
331 355
                 && (p.specname === "Protection"
332 356
                     || p.specname === "Feral (Tank)"))
@@ -334,6 +358,7 @@ export class RaidManager
334 358
         raiddata.healers = Object.values(raiddata.participants).flatMap(
335 359
             (healers: any[]) => healers.filter((p: any) =>
336 360
                 !p.benched
361
+                && !p.absent
337 362
                 && !p.late
338 363
                 && (p.specname === "Holy"
339 364
                     || p.specname === "Discipline"
@@ -354,13 +379,13 @@ export class RaidManager
354 379
         .select('*', 'si.id as id')
355 380
         .where('raidid', '=', raid.id!)
356 381
 
357
-    sign = async (usertoken: string, character: Character, raid: Raid, late: boolean, memo?: string) => {
382
+    sign = async (usertoken: string, character: Character, raid: Raid, late: boolean, absent:boolean, memo?: string) => {
358 383
         const maybeUserRecord = this.userManager.getUserRecordByToken(usertoken)
359 384
         if (!maybeUserRecord || maybeUserRecord.user.id != character.userid) {
360 385
             throw new Error("Bad Usertoken")
361 386
         }
362 387
 
363
-        if(memo) memo = memo.substring(0, MAX_MEMO_LENGTH)
388
+        if (memo) memo = memo.substring(0, MAX_MEMO_LENGTH)
364 389
         const exists = await this.admin
365 390
             .knex('signups')
366 391
             .select('*')
@@ -377,6 +402,7 @@ export class RaidManager
377 402
                     raidid: raid.id!,
378 403
                     characterid: character.id!,
379 404
                     late: late,
405
+                    absent: absent,
380 406
                     benched: false,
381 407
                     memo: memo
382 408
                 })
@@ -390,13 +416,14 @@ export class RaidManager
390 416
                     raidid: raid.id!,
391 417
                     characterid: character.id!,
392 418
                     late: late,
419
+                    absent: absent,
393 420
                     benched: false,
394 421
                     memo: memo
395 422
                 })
396 423
         }
397 424
 
398 425
         await this.notifyRaid(raid)
399
-        
426
+
400 427
         return await this.admin
401 428
             .knex('signups')
402 429
             .select('*')
@@ -407,6 +434,16 @@ export class RaidManager
407 434
             .first()
408 435
     }
409 436
 
437
+    cancelRaid = async (raid: Raid) => {
438
+        const data = await this.getRaidData(raid)
439
+        const participants = Object.values(data.participants).flat()
440
+        await Promise.all(
441
+            participants.map(async p => await this.adminUnsign({...p, id: p.characterid}, raid))
442
+        )
443
+        await this.admin.knex('raids').where({id: raid.id}).del()
444
+        await this.notifyRaids()
445
+    }
446
+
410 447
     unsign = async (usertoken: string, character: Character, raid: Raid) => {
411 448
         const maybeUserRecord = this.userManager.getUserRecordByToken(usertoken)
412 449
         if (!maybeUserRecord || maybeUserRecord.user.id != character.userid) {
@@ -417,17 +454,16 @@ export class RaidManager
417 454
     }
418 455
 
419 456
     adminUnsign = async (character: Character, raid: Raid) => {
420
-
421 457
         const user = await this.characterManager.getUserOfCharacter(character)
422 458
         const tokens = await this.itemManager.getTokens(character, [raid.tier], true)
423 459
 
424 460
         //check if token has to be deleted
425 461
         if (tokens) {
426
-            Promise.all(
462
+            await Promise.all(
427 463
                 tokens.map(async token => {
428 464
                     await this.userManager.incrementCurrency(user, raid.tier, 1)
429 465
                     const prio = await this.itemManager.calculatePriorities(token.itemname, character)
430
-                    if (token.level <= prio + 1) {
466
+                    if (token.level <= prio + 2) {
431 467
                         await this.admin
432 468
                             .knex(raid.tier + 'tokens')
433 469
                             .where({
@@ -442,7 +478,7 @@ export class RaidManager
442 478
                                 itemname: token.itemname
443 479
                             }).update({
444 480
                                 signupid: null,
445
-                                level: token.level - 1
481
+                                level: token.level - 2
446 482
                             })
447 483
                     }
448 484
                 })
@@ -459,14 +495,14 @@ export class RaidManager
459 495
         await this.notifyRaid(raid)
460 496
     }
461 497
 
462
-    setBenched = async (signup: Signup): Promise<void> => {
498
+    updateSignup = async (signup: Signup): Promise<void> => {
463 499
         await this.admin.knex('signups')
464 500
             .where({
465 501
                 raidid: signup.raidid,
466 502
                 characterid: signup.characterid
467 503
             })
468 504
             .update(signup)
469
-    
505
+
470 506
         await this.notifyRaid({ id: signup.raidid })
471 507
     }
472 508
 }

+ 24
- 25
src/backend/Components/User/UserManager.ts 查看文件

@@ -53,7 +53,7 @@ export class UserManager
53 53
     userLogins: { [username in string]: UserRecord } = {}
54 54
     allowed: string[] = []
55 55
 
56
-    exportRPCs = () => [
56
+    RPCs = () => [
57 57
         this.login,
58 58
         this.logout,
59 59
         this.getAuth,
@@ -63,23 +63,23 @@ export class UserManager
63 63
         this.changePassword
64 64
     ]
65 65
 
66
-    exportRPCFeatures = () => [
66
+    RPCFeatures = () => [
67 67
         {
68 68
             name: 'manageUser' as 'manageUser',
69
-            exportRPCs: () => [
69
+            RPCs: () => [
70 70
                 this.adminLogout,
71 71
                 this.changeRank,
72 72
                 this.adminChangePassword
73 73
             ]
74 74
         }, {
75 75
             name: 'modifyPermissions' as 'modifyPermissions',
76
-            exportRPCs: () => [
76
+            RPCs: () => [
77 77
                 this.getPermissions,
78 78
                 this.setPermission,
79 79
             ]
80 80
         }, {
81 81
             name: 'softreserveCurrency' as 'softreserveCurrency',
82
-            exportRPCs: () => [
82
+            RPCs: () => [
83 83
                 this.incrementCurrency,
84 84
                 this.decrementCurrency,
85 85
                 this.setCurrency
@@ -137,7 +137,7 @@ export class UserManager
137 137
         getLogger('UserManager#initialize').debug('setting up permissions')
138 138
 
139 139
         await Promise.all(
140
-            [this, ...this.exporters].flatMap(exp => exp.exportRPCFeatures().map(async (feature) => {
140
+            [this, ...this.exporters].flatMap(exp => exp.RPCFeatures().map(async (feature) => {
141 141
                 try {
142 142
                     await this.admin.knex.insert({ rpcname: feature.name }).into('rpcpermissions')
143 143
                 } catch (e) {
@@ -149,16 +149,16 @@ export class UserManager
149 149
             server: rankServer,
150 150
             port: 20001
151 151
         }
152
-        setInterval(this.checkExpiredSessions, 600_000)
152
+        //setInterval(this.checkExpiredSessions, 600_000)
153 153
     }
154 154
 
155 155
     stop = async () => {
156 156
         Object.values(this.userLogins).forEach(x => {
157
-            Object.values(x.connections).forEach(c => c.destroy())
157
+            Object.values(x.connections).forEach(c => c.close())
158 158
         })
159 159
 
160 160
         try {
161
-            return this.rankServer.server.destroy()
161
+            return this.rankServer.server.close()
162 162
         } catch (e) {
163 163
             getLogger('UserManager#stop').debug(String(e))
164 164
         }
@@ -200,7 +200,7 @@ export class UserManager
200 200
             return false
201 201
         }
202 202
 
203
-        this.userLogins[data.user.username].connections[socket.port] = socket
203
+        this.userLogins[data.user.username].connections[socket.id] = socket
204 204
         //getLogger('UserManager#checkConnection').debug(this.userLogins, this.allowed);
205 205
         
206 206
         return true
@@ -376,28 +376,28 @@ export class UserManager
376 376
 
377 377
     startRankServer = async (port: number): Promise<RPCServer<FrontcraftFeatureIfc>> => {
378 378
         let rpcs = [
379
-            ...this.exportRPCFeatures(),
380
-            ...this.exporters.flatMap((exp) => exp.exportRPCFeatures())
379
+            ...this.RPCFeatures(),
380
+            ...this.exporters.flatMap((exp) => exp.RPCFeatures())
381 381
         ]
382
-        let rpcServer = new RPCServer<FrontcraftFeatureIfc>(port, rpcs, {
382
+        let rpcServer = new RPCServer<FrontcraftFeatureIfc>(rpcs, {
383 383
             accessFilter: async (sesame, exporter) => {
384 384
                 const record = this.getUserRecordByToken(sesame!)
385 385
                 if (!record) return false
386 386
                 return await this.getPermission(exporter.name, record.user.rank)
387 387
             },
388 388
             closeHandler: (socket) => {
389
-                Object.values(this.userLogins).forEach(login => delete login.connections[socket.port])
390
-                getLogger('UserManager#closeHandler').debug(this.userLogins, this.allowed)
389
+                Object.values(this.userLogins).forEach(login => delete login.connections[socket.id])
390
+                //getLogger('UserManager#closeHandler').debug(this.userLogins, this.allowed)
391 391
             },
392 392
             connectionHandler: (socket) => {
393 393
                 this.checkConnection(socket, 10).then(res => {
394 394
                     if (!res) {
395
-                        getLogger('UserManager#connectionHandler').debug("Connection on "+socket.port+" failed to authenticate")
396
-                        socket.destroy();
395
+                        getLogger('UserManager#connectionHandler').debug("Connection "+socket.id+" failed to authenticate")
396
+                        socket.close();
397 397
                     }
398 398
                 }).catch((e) => {
399 399
                     getLogger('UserManager#connectionHandler').debug(String(e))
400
-                    socket.destroy();
400
+                    socket.close();
401 401
                 })
402 402
             },
403 403
             errorHandler: (socket, e, rpcName, args) => {
@@ -405,8 +405,7 @@ export class UserManager
405 405
                 throw(new Error("RPC failed"))
406 406
             },
407 407
             sesame: (sesame) => this.checkToken(sesame),
408
-            visibility: '0.0.0.0'
409
-        })
408
+        }).listen(port)
410 409
 
411 410
         return rpcServer
412 411
     }
@@ -451,8 +450,8 @@ export class UserManager
451 450
         return usr[tier]!
452 451
     }
453 452
 
454
-    decrementCurrency = async (user: User, tier?: Tiers, value = 1) => {
455
-        if (!tier || value < 1) return
453
+    decrementCurrency = async (user: User, tier: Tiers, value = 1) => {
454
+        if (value < 1) return
456 455
 
457 456
         const usr: User = await this.admin
458 457
             .knex('users')
@@ -460,7 +459,7 @@ export class UserManager
460 459
             .select('*')
461 460
             .first()
462 461
 
463
-        if (!usr || usr[tier]! <= 0) return
462
+        if (!usr || usr[tier]!-value < 0) return
464 463
 
465 464
         await this.admin
466 465
             .knex('users')
@@ -476,8 +475,8 @@ export class UserManager
476 475
             .increment(tier, value)
477 476
     }
478 477
 
479
-    setCurrency = async (user: User, tier?: Tiers, value = 0) => {
480
-        if (!tier || value < 0) return
478
+    setCurrency = async (user: User, tier: Tiers, value = 0) => {
479
+        if (value < 0) return
481 480
         await this.admin
482 481
             .knex('users')
483 482
             .where('id', '=', user.id)

+ 2
- 2
src/backend/Types/FrontworkComponent.ts 查看文件

@@ -14,8 +14,8 @@ export interface FrontworkComponent<
14 14
 {
15 15
     name: Name;
16 16
 
17
-    exportRPCFeatures(): RPCExporter<FeatureIfc, FeatureName>[] 
18
-    exportRPCs(): RPCDefinitions<Ifc>[Name]
17
+    RPCFeatures(): RPCExporter<FeatureIfc, FeatureName>[] 
18
+    RPCs(): RPCDefinitions<Ifc>[Name]
19 19
     getTableDefinitions(): TableDefiniton[] 
20 20
 
21 21
     initialize?(): Promise<any>

+ 417
- 281
src/backend/Types/Items.ts 查看文件

@@ -1,301 +1,437 @@
1
-export type Tiers =  "MC" | 'BWL' | 'ZG' | 'AQ20' | 'AQ40' | 'Naxx'
2
-export const _Tiers:Tiers[] = ["MC", 'BWL', 'ZG', 'AQ20', 'AQ40', 'Naxx']
1
+export type Tiers = "MC" | 'BWL' | 'ZG' | 'AQ20' | 'AQ40' | 'Naxx'
2
+export const _Tiers: Tiers[] = ["MC", 'BWL', 'ZG', 'AQ20', 'AQ40', 'Naxx']
3 3
 
4 4
 export const T1 = [
5
-"Robe of Volatile Power",
6
-"Salamander Scale Pants",
7
-"Heavy Dark Iron Ring",
8
-"Ring of Spell Power",
9
-"Sorcerous Dagger",
10
-"Wristguards of Stability",
11
-"Helm of the Lifegiver",
12
-"Fire Runed Grimoire",
13
-"Crimson Shocker",
14
-"Mana Igniting Cord",
15
-"Quick Strike Ring",
16
-"Seal of the Archmagus",
17
-"Talisman of Ephemeral Power",
18
-"Flameguard Gauntlets",
19
-"Magma Tempered Boots",
20
-"Obsidian Edged Blade",
21
-"Aged Core Leather Gloves",
22
-"Sabatons of the Flamewalker",
23
-"Striker's Mark",
24
-"Medallion of Steadfast Might",
25
-"Earthshaker",
26
-"Brutality Blade",
27
-"Aurastone Hammer",
28
-"Gutgore Ripper",
29
-"Drillborer Disk",
30
-"Azuresong Mageblade",
31
-"Staff of Dominance",
32
-"Blastershot Launcher",
33
-"The Eye of Divinity",
34
-"Ancient Petrified Leaf",
35
-"Finkle's Lava Dredger",
36
-"Core Hound Tooth",
37
-"Core Forged Greaves",
38
-"Gloves of the Hypnotic Flame",
39
-"Sash of Whispered Secrets",
40
-"Wild Growth Spaulders",
41
-"Fireproof Cloak",
42
-"Wristguards of True Flight",
43
-"Fireguard Shoulders",
44
-"Cauterizing Band",
45
-"Onslaught Girdle",
46
-"Perdition's Blade",
47
-"Cloak of the Shrouded Mists",
48
-"Band of Accuria",
49
-"Crown of Destruction",
50
-"Choker of the Fire Lord",
51
-"Band of Sulfuras",
52
-"Dragon's Blood Cape",
53
-"Malistar's Defender",
54
-"Spinal Reaper",
55
-"Bonereaver's Edge",
56
-"Shard of the Flame",
57
-"Essence of the Pure Flame",
58
-"Eye of Sulfuras",
59
-"Deathbringer",
60
-"Vis'kag the Bloodletter",
61
-"Sapphiron Drape",
62
-"Ancient Cornerstone Grimoire",
63
-"Eskhandar's Collar",
64
-"Ring of Binding",
65
-"Shard of the Scale",
66
-"Head of Onyxia",
67
-"Bindings of the Windseeker",
68
-"Cenarion Belt",
69
-"Cenarion Boots",
70
-"Cenarion Bracers",
71
-"Cenarion Vestments",
72
-"Cenarion Helm",
73
-"Cenarion Leggings",
74
-"Cenarion Spaulders",
75
-"Giantstalker's Belt",
76
-"Giantstalker's Boots",
77
-"Giantstalker's Bracers",
78
-"Giantstalker's Breastplate",
79
-"Giantstalker's Epaulets",
80
-"Giantstalker's Gloves",
81
-"Giantstalker's Helmet",
82
-"Giantstalker's Leggings",
83
-"Arcanist Belt",
84
-"Arcanist Bindings",
85
-"Arcanist Crown",
86
-"Arcanist Boots",
87
-"Arcanist Gloves",
88
-"Arcanist Leggings",
89
-"Arcanist Mantle",
90
-"Arcanist Robes",
91
-"Lawbringer Belt",
92
-"Lawbringer Boots",
93
-"Lawbringer Bracers",
94
-"Lawbringer Chestguard",
95
-"Lawbringer Gauntlets",
96
-"Lawbringer Helm",
97
-"Lawbringer Legplates",
98
-"Lawbringer Spaulders",
99
-"Boots of Prophecy",
100
-"Circlet of Prophecy",
101
-"Girdle of Prophecy",
102
-"Gloves of Prophecy",
103
-"Pants of Prophecy",
104
-"Mantle of Prophecy",
105
-"Robes of Prophecy",
106
-"Vambraces of Prophecy",
107
-"Nightslayer Belt",
108
-"Nightslayer Bracelets",
109
-"Nightslayer Chestpiece",
110
-"Nightslayer Cover",
111
-"Nightslayer Gloves",
112
-"Nightslayer Pants",
113
-"Nightslayer Shoulder Pads",
114
-"Felheart Belt",
115
-"Felheart Bracers",
116
-"Felheart Gloves",
117
-"Felheart Pants",
118
-"Felheart Robes",
119
-"Felheart Shoulder Pads",
120
-"Felheart Horns",
121
-"Belt of Might",
122
-"Bracers of Might",
123
-"Breastplate of Might",
124
-"Gauntlets of Might",
125
-"Helm of Might",
126
-"Legplates of Might",
127
-"Pauldrons of Might",
128
-"Sabatons of Might",
129
-"Earthfury Belt",
130
-"Earthfury Boots",
131
-"Earthfury Bracers",
132
-"Earthfury Vestments",
133
-"Earthfury Epaulets",
134
-"Earthfury Gauntlets",
135
-"Earthfury Helmet",
136
-"Earthfury Legguards",
137
-"Stormrage Cover",
138
-"Stormrage Legguards",
139
-"Dragonstalker's Helm",
140
-"Dragonstalker's Legguards",
141
-"Netherwind Crown",
142
-"Netherwind Pants",
143
-"Judgement Crown",
144
-"Judgement Legplates",
145
-"Halo of Transcendence",
146
-"Leggings of Transcendence",
147
-"Bloodfang Hood",
148
-"Bloodfang Pants",
149
-"Helmet of Ten Storms",
150
-"Nemesis Skullcap",
151
-"Nemesis Leggings",
152
-"Helm of Wrath",
153
-"Legplates of Wrath",
5
+    "Robe of Volatile Power",
6
+    "Salamander Scale Pants",
7
+    "Heavy Dark Iron Ring",
8
+    "Ring of Spell Power",
9
+    "Sorcerous Dagger",
10
+    "Wristguards of Stability",
11
+    "Helm of the Lifegiver",
12
+    "Fire Runed Grimoire",
13
+    "Crimson Shocker",
14
+    "Mana Igniting Cord",
15
+    "Quick Strike Ring",
16
+    "Seal of the Archmagus",
17
+    "Talisman of Ephemeral Power",
18
+    "Flameguard Gauntlets",
19
+    "Magma Tempered Boots",
20
+    "Obsidian Edged Blade",
21
+    "Aged Core Leather Gloves",
22
+    "Sabatons of the Flamewalker",
23
+    "Striker's Mark",
24
+    "Medallion of Steadfast Might",
25
+    "Earthshaker",
26
+    "Brutality Blade",
27
+    "Aurastone Hammer",
28
+    "Gutgore Ripper",
29
+    "Drillborer Disk",
30
+    "Azuresong Mageblade",
31
+    "Staff of Dominance",
32
+    "Blastershot Launcher",
33
+    "The Eye of Divinity",
34
+    "Ancient Petrified Leaf",
35
+    "Finkle's Lava Dredger",
36
+    "Core Hound Tooth",
37
+    "Core Forged Greaves",
38
+    "Gloves of the Hypnotic Flame",
39
+    "Sash of Whispered Secrets",
40
+    "Wild Growth Spaulders",
41
+    "Fireproof Cloak",
42
+    "Wristguards of True Flight",
43
+    "Fireguard Shoulders",
44
+    "Cauterizing Band",
45
+    "Onslaught Girdle",
46
+    "Perdition's Blade",
47
+    "Cloak of the Shrouded Mists",
48
+    "Band of Accuria",
49
+    "Crown of Destruction",
50
+    "Choker of the Fire Lord",
51
+    "Band of Sulfuras",
52
+    "Dragon's Blood Cape",
53
+    "Malistar's Defender",
54
+    "Spinal Reaper",
55
+    "Bonereaver's Edge",
56
+    "Shard of the Flame",
57
+    "Essence of the Pure Flame",
58
+    "Eye of Sulfuras",
59
+    "Deathbringer",
60
+    "Vis'kag the Bloodletter",
61
+    "Sapphiron Drape",
62
+    "Ancient Cornerstone Grimoire",
63
+    "Eskhandar's Collar",
64
+    "Ring of Binding",
65
+    "Shard of the Scale",
66
+    "Head of Onyxia",
67
+    "Bindings of the Windseeker",
68
+    "Cenarion Belt",
69
+    "Cenarion Boots",
70
+    "Cenarion Bracers",
71
+    "Cenarion Vestments",
72
+    "Cenarion Helm",
73
+    "Cenarion Leggings",
74
+    "Cenarion Spaulders",
75
+    "Giantstalker's Belt",
76
+    "Giantstalker's Boots",
77
+    "Giantstalker's Bracers",
78
+    "Giantstalker's Breastplate",
79
+    "Giantstalker's Epaulets",
80
+    "Giantstalker's Gloves",
81
+    "Giantstalker's Helmet",
82
+    "Giantstalker's Leggings",
83
+    "Arcanist Belt",
84
+    "Arcanist Bindings",
85
+    "Arcanist Crown",
86
+    "Arcanist Boots",
87
+    "Arcanist Gloves",
88
+    "Arcanist Leggings",
89
+    "Arcanist Mantle",
90
+    "Arcanist Robes",
91
+    "Lawbringer Belt",
92
+    "Lawbringer Boots",
93
+    "Lawbringer Bracers",
94
+    "Lawbringer Chestguard",
95
+    "Lawbringer Gauntlets",
96
+    "Lawbringer Helm",
97
+    "Lawbringer Legplates",
98
+    "Lawbringer Spaulders",
99
+    "Boots of Prophecy",
100
+    "Circlet of Prophecy",
101
+    "Girdle of Prophecy",
102
+    "Gloves of Prophecy",
103
+    "Pants of Prophecy",
104
+    "Mantle of Prophecy",
105
+    "Robes of Prophecy",
106
+    "Vambraces of Prophecy",
107
+    "Nightslayer Belt",
108
+    "Nightslayer Bracelets",
109
+    "Nightslayer Chestpiece",
110
+    "Nightslayer Cover",
111
+    "Nightslayer Gloves",
112
+    "Nightslayer Pants",
113
+    "Nightslayer Shoulder Pads",
114
+    "Felheart Belt",
115
+    "Felheart Bracers",
116
+    "Felheart Gloves",
117
+    "Felheart Pants",
118
+    "Felheart Robes",
119
+    "Felheart Shoulder Pads",
120
+    "Felheart Horns",
121
+    "Belt of Might",
122
+    "Bracers of Might",
123
+    "Breastplate of Might",
124
+    "Gauntlets of Might",
125
+    "Helm of Might",
126
+    "Legplates of Might",
127
+    "Pauldrons of Might",
128
+    "Sabatons of Might",
129
+    "Earthfury Belt",
130
+    "Earthfury Boots",
131
+    "Earthfury Bracers",
132
+    "Earthfury Vestments",
133
+    "Earthfury Epaulets",
134
+    "Earthfury Gauntlets",
135
+    "Earthfury Helmet",
136
+    "Earthfury Legguards",
137
+    "Stormrage Cover",
138
+    "Stormrage Legguards",
139
+    "Dragonstalker's Helm",
140
+    "Dragonstalker's Legguards",
141
+    "Netherwind Crown",
142
+    "Netherwind Pants",
143
+    "Judgement Crown",
144
+    "Judgement Legplates",
145
+    "Halo of Transcendence",
146
+    "Leggings of Transcendence",
147
+    "Bloodfang Hood",
148
+    "Bloodfang Pants",
149
+    "Helmet of Ten Storms",
150
+    "Nemesis Skullcap",
151
+    "Nemesis Leggings",
152
+    "Helm of Wrath",
153
+    "Legplates of Wrath",
154 154
 ]
155 155
 
156
-export const T2:string[] = [
157
-"Gloves of Rapid Evolution",
158
-"Mantle of the Blackwing Cabal",
159
-"Spineshatter",
160
-"The Untamed Blade",
161
-"Pendant of the Fallen Dragon",
162
-"Helm of Endless Rage",
163
-"Dragonfang Blade",
164
-"Red Dragonscale Protector",
165
-"Black Brood Pauldrons",
166
-"Bracers of Arcane Accuracy",
167
-"Heartstriker",
168
-"Maladath, Runed Blade of the Black Flight",
169
-"Black Ash Robe",
170
-"Firemaw's Clutch",
171
-"Claw of the Black Drake",
172
-"Cloak of Firemaw",
173
-"Legguards of the Fallen Crusader",
174
-"Taut Dragonhide Belt",
175
-"Drake Talon Pauldrons",
176
-"Rejuvenating Gem",
177
-"Drake Talon Cleaver",
178
-"Band of Forced Concentration",
179
-"Drake Fang Talisman",
180
-"Ebony Flame Gloves",
181
-"Malfurion's Blessed Bulwark",
182
-"Dragonbreath Hand Cannon",
183
-"Shadow Wing Focus Staff",
184
-"Shroud of Pure Thought",
185
-"Circle of Applied Force",
186
-"Emberweave Leggings",
187
-"Styleen's Impeding Scarab",
188
-"Dragon's Touch",
189
-"Herald of Woe",
190
-"Taut Dragonhide Shoulderpads",
191
-"Chromatic Boots",
192
-"Taut Dragonhide Gloves",
193
-"Shimmering Geta",
194
-"Ring of Blackrock",
195
-"Elementium Threaded Cloak",
196
-"Angelista's Grasp",
197
-"Girdle of the Fallen Crusader",
198
-"Empowered Leggings",
199
-"Chromatically Tempered Sword",
200
-"Claw of Chromaggus",
201
-"Primalist's Linked Waistguard",
202
-"Ashjre'thul, Crossbow of Smiting",
203
-"Elementium Reinforced Bulwark",
204
-"Head of Nefarian",
205
-"Cloak of the Brood Lord",
206
-"Boots of the Shadow Flame",
207
-"Therazane's Link",
208
-"Archimtiros' Ring of Reckoning",
209
-"Neltharion's Tear",
210
-"Pure Elementium Band",
211
-"Mish'undare, Circlet of the Mind Flayer",
212
-"Prestor's Talisman of Connivery",
213
-"Staff of the Shadow Flame",
214
-"Ashkandi, Greatsword of the Brotherhood",
215
-"Crul'shorukh, Edge of Chaos",
216
-"Lok'amir il Romathis",
217
-"Boots of Pure Thought",
218
-"Cloak of Draconic Might",
219
-"Draconic Maul",
220
-"Doom's Edge",
221
-"Essence Gatherer",
222
-"Band of Dark Dominion",
223
-"Draconic Avenger",
224
-"Interlaced Shadow Jerkin",
225
-"Ringo's Blizzard Boots",
226
-"Scrolls of Blinding Light",
227
-"Rune of Metamorphosis",
228
-"Natural Alignment Crystal",
229
-"Mind Quickening Gem",
230
-"Lifegiving Gem",
231
-"Arcane Infused Gem",
232
-"The Black Book",
233
-"Venomous Totem",
234
-"Nemesis Spaulders",
235
-"Nemesis Robes",
236
-"Nemesis Bracers",
237
-"Nemesis Gloves",
238
-"Nemesis Belt",
239
-"Nemesis Boots",
240
-"Netherwind Mantle",
241
-"Netherwind Robes",
242
-"Netherwind Bindings",
243
-"Netherwind Gloves",
244
-"Netherwind Belt",
245
-"Netherwind Boots",
246
-"Bloodfang Spaulders",
247
-"Bloodfang Chestpiece",
248
-"Bloodfang Bracers",
249
-"Bloodfang Gloves",
250
-"Bloodfang Belt",
251
-"Bloodfang Boots",
252
-"Stormrage Pauldrons",
253
-"Stormrage Chestguard",
254
-"Stormrage Bracers",
255
-"Stormrage Handguards",
256
-"Stormrage Belt",
257
-"Stormrage Boots",
258
-"Dragonstalker's Spaulders",
259
-"Dragonstalker's Breastplate",
260
-"Dragonstalker's Bracers",
261
-"Dragonstalker's Gauntlets",
262
-"Dragonstalker's Belt",
263
-"Dragonstalker's Greaves",
264
-"Judgement Spaulders",
265
-"Judgement Breastplate",
266
-"Judgement Bindings",
267
-"Judgement Gauntlets",
268
-"Judgement Belt",
269
-"Judgement Sabatons",
270
-"Pauldrons of Transcendence",
271
-"Robes of Transcendence",
272
-"Bindings of Transcendence",
273
-"Handguards of Transcendence",
274
-"Belt of Transcendence",
275
-"Boots of Transcendence",
276
-"Pauldrons of Wrath",
277
-"Breastplate of Wrath",
278
-"Bracelets of Wrath",
279
-"Gauntlets of Wrath",
280
-"Waistband of Wrath",
281
-"Sabatons of Wrath"
156
+export const T2: string[] = [
157
+    "Gloves of Rapid Evolution",
158
+    "Mantle of the Blackwing Cabal",
159
+    "Spineshatter",
160
+    "The Untamed Blade",
161
+    "Pendant of the Fallen Dragon",
162
+    "Helm of Endless Rage",
163
+    "Dragonfang Blade",
164
+    "Red Dragonscale Protector",
165
+    "Black Brood Pauldrons",
166
+    "Bracers of Arcane Accuracy",
167
+    "Heartstriker",
168
+    "Maladath, Runed Blade of the Black Flight",
169
+    "Black Ash Robe",
170
+    "Firemaw's Clutch",
171
+    "Claw of the Black Drake",
172
+    "Cloak of Firemaw",
173
+    "Legguards of the Fallen Crusader",
174
+    "Taut Dragonhide Belt",
175
+    "Drake Talon Pauldrons",
176
+    "Rejuvenating Gem",
177
+    "Drake Talon Cleaver",
178
+    "Band of Forced Concentration",
179
+    "Drake Fang Talisman",
180
+    "Ebony Flame Gloves",
181
+    "Malfurion's Blessed Bulwark",
182
+    "Dragonbreath Hand Cannon",
183
+    "Shadow Wing Focus Staff",
184
+    "Shroud of Pure Thought",
185
+    "Circle of Applied Force",
186
+    "Emberweave Leggings",
187
+    "Styleen's Impeding Scarab",
188
+    "Dragon's Touch",
189
+    "Herald of Woe",
190
+    "Taut Dragonhide Shoulderpads",
191
+    "Chromatic Boots",
192
+    "Taut Dragonhide Gloves",
193
+    "Shimmering Geta",
194
+    "Ring of Blackrock",
195
+    "Elementium Threaded Cloak",
196
+    "Angelista's Grasp",
197
+    "Girdle of the Fallen Crusader",
198
+    "Empowered Leggings",
199
+    "Chromatically Tempered Sword",
200
+    "Claw of Chromaggus",
201
+    "Primalist's Linked Waistguard",
202
+    "Ashjre'thul, Crossbow of Smiting",
203
+    "Elementium Reinforced Bulwark",
204
+    "Head of Nefarian",
205
+    "Cloak of the Brood Lord",
206
+    "Boots of the Shadow Flame",
207
+    "Therazane's Link",
208
+    "Archimtiros' Ring of Reckoning",
209
+    "Neltharion's Tear",
210
+    "Pure Elementium Band",
211
+    "Mish'undare, Circlet of the Mind Flayer",
212
+    "Prestor's Talisman of Connivery",
213
+    "Staff of the Shadow Flame",
214
+    "Ashkandi, Greatsword of the Brotherhood",
215
+    "Crul'shorukh, Edge of Chaos",
216
+    "Lok'amir il Romathis",
217
+    "Boots of Pure Thought",
218
+    "Cloak of Draconic Might",
219
+    "Draconic Maul",
220
+    "Doom's Edge",
221
+    "Essence Gatherer",
222
+    "Band of Dark Dominion",
223
+    "Draconic Avenger",
224
+    "Interlaced Shadow Jerkin",
225
+    "Ringo's Blizzard Boots",
226
+    "Scrolls of Blinding Light",
227
+    "Rune of Metamorphosis",
228
+    "Natural Alignment Crystal",
229
+    "Mind Quickening Gem",
230
+    "Lifegiving Gem",
231
+    "Arcane Infused Gem",
232
+    "The Black Book",
233
+    "Venomous Totem",
234
+    "Nemesis Spaulders",
235
+    "Nemesis Robes",
236
+    "Nemesis Bracers",
237
+    "Nemesis Gloves",
238
+    "Nemesis Belt",
239
+    "Nemesis Boots",
240
+    "Netherwind Mantle",
241
+    "Netherwind Robes",
242
+    "Netherwind Bindings",
243
+    "Netherwind Gloves",
244
+    "Netherwind Belt",
245
+    "Netherwind Boots",
246
+    "Bloodfang Spaulders",
247
+    "Bloodfang Chestpiece",
248
+    "Bloodfang Bracers",
249
+    "Bloodfang Gloves",
250
+    "Bloodfang Belt",
251
+    "Bloodfang Boots",
252
+    "Stormrage Pauldrons",
253
+    "Stormrage Chestguard",
254
+    "Stormrage Bracers",
255
+    "Stormrage Handguards",
256
+    "Stormrage Belt",
257
+    "Stormrage Boots",
258
+    "Dragonstalker's Spaulders",
259
+    "Dragonstalker's Breastplate",
260
+    "Dragonstalker's Bracers",
261
+    "Dragonstalker's Gauntlets",
262
+    "Dragonstalker's Belt",
263
+    "Dragonstalker's Greaves",
264
+    "Judgement Spaulders",
265
+    "Judgement Breastplate",
266
+    "Judgement Bindings",
267
+    "Judgement Gauntlets",
268
+    "Judgement Belt",
269
+    "Judgement Sabatons",
270
+    "Pauldrons of Transcendence",
271
+    "Robes of Transcendence",
272
+    "Bindings of Transcendence",
273
+    "Handguards of Transcendence",
274
+    "Belt of Transcendence",
275
+    "Boots of Transcendence",
276
+    "Pauldrons of Wrath",
277
+    "Breastplate of Wrath",
278
+    "Bracelets of Wrath",
279
+    "Gauntlets of Wrath",
280
+    "Waistband of Wrath",
281
+    "Sabatons of Wrath"
282
+]
283
+
284
+const AQ40 = [
285
+    //Tokens
286
+    "Qiraji Bindings of Command",
287
+    "Qiraji Bindings of Dominance",
288
+    "Vek'lor's Diadem",
289
+    "Vek'nilash's Circlet",
290
+    "Ouro's Intact Hide",
291
+    "Skin of the Great Sandworm",
292
+    "Carapace of the Old God",
293
+    "Husk of the Old God",
294
+    "Imperial Qiraji Armaments",
295
+    "Imperial Qiraji Regalia",
296
+
297
+    //Prophet Skeram
298
+    "Beetle Scaled Wristguards",
299
+    "Boots of the Unwavering Will",
300
+    "Breastplate of Annihilation",
301
+    "Cloak of Concentrated Hatred",
302
+    "Leggings of Immersion",
303
+    "Barrage Shoulders",
304
+    "Pendant of the Qiraji Guardian",
305
+    "Ring of Swarming Thought",
306
+    "Amulet of Foul Warding",
307
+    "Boots of the Redeemed Prophecy",
308
+    "Hammer of Ji'zhi",
309
+    "Staff of the Qiraji Prophets",
310
+    "Boots of the Fallen Prophet",
311
+
312
+    //Battleguard Sartura
313
+    "Robes of the Battleguard",
314
+    "Thick Qirajihide Belt",
315
+    "Creeping Vine Helm",
316
+    "Gauntlets of Steadfast Determination",
317
+    "Gloves of Enforcement",
318
+    "Leggings of the Festering Swarm",
319
+    "Necklace of Purity",
320
+    "Recomposed Boots",
321
+    "Badge of the Swarmguard",
322
+    "Legplates of Blazing Light",
323
+    "Sartura's Might",
324
+    "Scaled Leggings of Qiraji Fury",
325
+    "Silithid Claw",
326
+
327
+    //Fankriss the Unyielding
328
+    "Hive Tunneler's Boots",
329
+    "Pauldrons of the Unrelenting",
330
+    "Robes of the Guardian Saint",
331
+    "Barbed Choker",
332
+    "Fetish of the Sand Reaver",
333
+    "Mantle of Wicked Revenge",
334
+    "Scaled Sand Reaver Leggings",
335
+    "Silithid Carapace Chestguard",
336
+    "Cloak of Untold Secrets",
337
+    "Barb of the Sand Reaver",
338
+    "Libram of Grace",
339
+    "Ancient Qiraji Ripper",
340
+    "Totem of Life",
341
+
342
+    //Princess Huhuran
343
+    "Hive Defiler Wristguards",
344
+    "Wasphide Gauntlets",
345
+    "Cloak of the Golden Hive",
346
+    "Ring of the Martyr",
347
+    "Gloves of the Messiah",
348
+    "Huhuran's Stinger",
349
+
350
+    //Twin Emperors
351
+    "Boots of Epiphany",
352
+    "Vek'lor's Gloves of Devastation",
353
+    "Royal Qiraji Belt",
354
+    "Royal Scepter of Vek'lor",
355
+    "Ring of Emperor Vek'lor",
356
+    "Gloves of the Hidden Temple",
357
+    "Amulet of Vek'nilash",
358
+    "Regenerating Belt of Vek'nilash",
359
+    "Belt of the Fallen Emperor",
360
+    "Bracelets of Royal Redemption",
361
+    "Kalimdor's Revenge",
362
+    "Grasp of the Fallen Emperor",
363
+    "Qiraji Execution Bracers",
364
+
365
+    //C'Thun
366
+    "Eye of C'Thun",
367
+    "Ring of the Godslayer",
368
+    "Eyestalk Waist Cord",
369
+    "Cloak of the Devoured",
370
+    "Gauntlets of Annihilation",
371
+    "Scepter of the False Prophet",
372
+    "Mark of C'Thun",
373
+    "Cloak of Clarity",
374
+    "Dark Storm Gauntlets",
375
+    "Grasp of the Old God",
376
+    "Belt of Never-ending Agony",
377
+    "Vanquished Tentacle of C'Thun",
378
+    "Dark Edge of Insanity",
379
+    "Death's Sting",
380
+
381
+    //Viscidus
382
+    "Gauntlets of Kalimdor",
383
+    "Gauntlets of the Righteous Champion",
384
+    "Idol of Health",
385
+    "Ring of the Qiraji Fury",
386
+    "Sharpened Silithid Femur",
387
+    "Slime-coated Leggings",
388
+    "Scarab Brooch",
389
+
390
+    //Ouro
391
+    "Don Rigoberto's Lost Hat",
392
+    "Larvae of the Great Worm",
393
+    "The Burrower's Shell",
394
+    "Wormscale Blocker",
395
+    "Burrower Bracers",
396
+    "Jom Gabbar",
397
+
398
+    //3 Bugs
399
+    "Guise of the Devourer",
400
+    "Ternary Mantle",
401
+    "Cape of the Trinity",
402
+    "Robes of the Triumvirate",
403
+    "Triad Girdle",
404
+    "Angelista's Touch",
405
+    "Vest of Swift Execution",
406
+    "Ring of the Devoured",
407
+    "Petrified Scarab",
408
+    "Wand of Qiraji Nobility",
409
+    "Angelista's Charm",
410
+    "Gloves of Ebru",
411
+    "Ooze-ridden Gauntlets",
412
+    "Boots of the Fallen Hero",
413
+    "Mantle of Phrenic Power",
414
+    "Mantle of the Desert's Fury",
415
+    "Mantle of the Desert Crusade",
416
+    "Bile-Covered Gauntlets",
417
+    "Ukko's Ring of Darkness",
282 418
 ]
283 419
 
284 420
 export const ZGMounts = [
285 421
     "Swift Zulian Tiger",
286 422
     "Swift Razzashi Raptor",
287 423
     "Primal Hakkari Idol"
288
-] 
424
+]
289 425
 
290 426
 export type AllItems = {
291
-    [tier in Tiers] : string[]
427
+    [tier in Tiers]: string[]
292 428
 }
293 429
 
294
-export const allItems : AllItems = {
430
+export const allItems: AllItems = {
295 431
     MC: T1,
296 432
     BWL: T2,
297 433
     ZG: ZGMounts,
298 434
     AQ20: [],
299
-    AQ40: [],
435
+    AQ40: AQ40,
300 436
     Naxx: []
301 437
 }

+ 1
- 1
src/backend/Types/Plugin.ts 查看文件

@@ -18,5 +18,5 @@ implements ConfigExporter<ConfType>, RPCExporter, TableDefinitionExporter{
18 18
 
19 19
     abstract getTableDefinitions(): TableDefiniton[]
20 20
     abstract getDefaultConfig(): ConfType
21
-    abstract exportRPCs(): RPC<any,any>[]
21
+    abstract RPCs(): RPC<any,any>[]
22 22
 }

+ 1
- 1
src/backend/Types/PrivilegedRPCExporter.ts 查看文件

@@ -6,5 +6,5 @@ export interface PrivilegedRPCExporter <
6 6
     Name extends keyof Ifc = keyof Ifc, 
7 7
     FeatureName extends keyof FeatureIfc = keyof FeatureIfc, 
8 8
 >extends RPCExporter<Ifc, Name>{
9
-    exportRPCFeatures() : RPCExporter<FeatureIfc, FeatureName>[]
9
+    RPCFeatures() : RPCExporter<FeatureIfc, FeatureName>[]
10 10
 }

+ 3
- 1
src/backend/Types/Types.ts 查看文件

@@ -54,7 +54,8 @@ export type RaidData = Raid & {
54 54
         [clazz in Class] : (Signup & Character & Spec)[] 
55 55
     } & {
56 56
         late: (Signup & Character & Spec)[]
57
-        bench: (Signup & Character & Spec)[] 
57
+        bench: (Signup & Character & Spec)[]
58
+        absent: (Signup & Character & Spec)[]
58 59
     }
59 60
     tokens: {
60 61
         [itemname in string]: (Character & SRToken & Item)[]
@@ -163,6 +164,7 @@ export type Signup = {
163 164
     characterid: number
164 165
     benched: boolean
165 166
     late: boolean
167
+    absent:boolean
166 168
     timestamp: string
167 169
     memo?: string
168 170
 }

+ 239
- 56
src/frontend/package-lock.json 查看文件

@@ -2645,6 +2645,14 @@
2645 2645
       "integrity": "sha1-ytdV8Pxt57cPpuXgivqB70wiSN4=",
2646 2646
       "dev": true
2647 2647
     },
2648
+    "@types/engine.io": {
2649
+      "version": "3.1.4",
2650
+      "resolved": "https://registry.npmjs.org/@types/engine.io/-/engine.io-3.1.4.tgz",
2651
+      "integrity": "sha512-98rXVukLD6/ozrQ2O80NAlWDGA4INg+tqsEReWJldqyi2fulC9V7Use/n28SWgROXKm6003ycWV4gZHoF8GA6w==",
2652
+      "requires": {
2653
+        "@types/node": "*"
2654
+      }
2655
+    },
2648 2656
     "@types/events": {
2649 2657
       "version": "3.0.0",
2650 2658
       "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz",
@@ -2705,10 +2713,9 @@
2705 2713
       "dev": true
2706 2714
     },
2707 2715
     "@types/node": {
2708
-      "version": "6.0.90",
2709
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-6.0.90.tgz",
2710
-      "integrity": "sha512-tXoGRVdi7wZX7P1VWoV9Wfk0uYDOAHdEYXAttuWgSrN76Q32wQlSrMX0Rgyv3RTEaQY2ZLQrzYHVM2e8rfo8sA==",
2711
-      "dev": true
2716
+      "version": "14.0.27",
2717
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.27.tgz",
2718
+      "integrity": "sha512-kVrqXhbclHNHGu9ztnAwSncIgJv/FaxmzXJvGXNdcCpV1b8u1/Mi6z6m0vwy0LzKeXFTPLH0NzwmoJ3fNCIq0g=="
2712 2719
     },
2713 2720
     "@types/q": {
2714 2721
       "version": "0.0.32",
@@ -2722,6 +2729,20 @@
2722 2729
       "integrity": "sha512-/DJGXtMuklLQef49qDsmjQofzaGU6D5To2xJZRLBNZO4GLAi30IjxLy6hHRQdycXP+IZKKXqJhYyNJ53SglJ8w==",
2723 2730
       "dev": true
2724 2731
     },
2732
+    "@types/socket.io": {
2733
+      "version": "2.1.10",
2734
+      "resolved": "https://registry.npmjs.org/@types/socket.io/-/socket.io-2.1.10.tgz",
2735
+      "integrity": "sha512-1fQMaDU/x2LPljEI/QI5IKl8sBYHM/zv32YYKvNrVEor7/1+MLqMqmWt8Bb8Vpf+PlIPBiTTC0BnrRx7ju3xOw==",
2736
+      "requires": {
2737
+        "@types/engine.io": "*",
2738
+        "@types/node": "*"
2739
+      }
2740
+    },
2741
+    "@types/socket.io-client": {
2742
+      "version": "1.4.33",
2743
+      "resolved": "https://registry.npmjs.org/@types/socket.io-client/-/socket.io-client-1.4.33.tgz",
2744
+      "integrity": "sha512-m4LnxkljsI9fMsjwpW5QhRpMixo2BeeLpFmg0AE+sS4H1pzAd/cs/ftTiL60FLZgfFa8PFRPx5KsHu8O0bADKQ=="
2745
+    },
2725 2746
     "@types/source-list-map": {
2726 2747
       "version": "0.1.2",
2727 2748
       "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz",
@@ -2952,7 +2973,6 @@
2952 2973
       "version": "1.3.7",
2953 2974
       "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
2954 2975
       "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
2955
-      "dev": true,
2956 2976
       "requires": {
2957 2977
         "mime-types": "~2.1.24",
2958 2978
         "negotiator": "0.6.2"
@@ -3004,8 +3024,7 @@
3004 3024
     "after": {
3005 3025
       "version": "0.8.2",
3006 3026
       "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz",
3007
-      "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=",
3008
-      "dev": true
3027
+      "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8="
3009 3028
     },
3010 3029
     "agent-base": {
3011 3030
       "version": "4.3.0",
@@ -3437,8 +3456,7 @@
3437 3456
     "async-limiter": {
3438 3457
       "version": "1.0.1",
3439 3458
       "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
3440
-      "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==",
3441
-      "dev": true
3459
+      "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ=="
3442 3460
     },
3443 3461
     "asynckit": {
3444 3462
       "version": "0.4.0",
@@ -3683,8 +3701,7 @@
3683 3701
     "backo2": {
3684 3702
       "version": "1.0.2",
3685 3703
       "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
3686
-      "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=",
3687
-      "dev": true
3704
+      "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
3688 3705
     },
3689 3706
     "balanced-match": {
3690 3707
       "version": "1.0.0",
@@ -3749,8 +3766,7 @@
3749 3766
     "base64-arraybuffer": {
3750 3767
       "version": "0.1.5",
3751 3768
       "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz",
3752
-      "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=",
3753
-      "dev": true
3769
+      "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg="
3754 3770
     },
3755 3771
     "base64-js": {
3756 3772
       "version": "1.3.1",
@@ -3797,7 +3813,6 @@
3797 3813
       "version": "1.0.2",
3798 3814
       "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz",
3799 3815
       "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=",
3800
-      "dev": true,
3801 3816
       "requires": {
3802 3817
         "callsite": "1.0.0"
3803 3818
       }
@@ -4076,19 +4091,6 @@
4076 4091
         "node-releases": "^1.1.47"
4077 4092
       }
4078 4093
     },
4079
-    "bsert": {
4080
-      "version": "0.0.10",
4081
-      "resolved": "https://registry.npmjs.org/bsert/-/bsert-0.0.10.tgz",
4082
-      "integrity": "sha512-NHNwlac+WPy4t2LoNh8pXk8uaIGH3NSaIUbTTRXGpE2WEbq0te/tDykYHkFK57YKLPjv/aGHmbqvnGeVWDz57Q=="
4083
-    },
4084
-    "bsock": {
4085
-      "version": "0.1.9",
4086
-      "resolved": "https://registry.npmjs.org/bsock/-/bsock-0.1.9.tgz",
4087
-      "integrity": "sha512-/l9Kg/c5o+n/0AqreMxh2jpzDMl1ikl4gUxT7RFNe3A3YRIyZkiREhwcjmqxiymJSRI/Qhew357xGn1SLw/xEw==",
4088
-      "requires": {
4089
-        "bsert": "~0.0.10"
4090
-      }
4091
-    },
4092 4094
     "buffer": {
4093 4095
       "version": "4.9.2",
4094 4096
       "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz",
@@ -4277,8 +4279,7 @@
4277 4279
     "callsite": {
4278 4280
       "version": "1.0.0",
4279 4281
       "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz",
4280
-      "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=",
4281
-      "dev": true
4282
+      "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA="
4282 4283
     },
4283 4284
     "callsites": {
4284 4285
       "version": "2.0.0",
@@ -4752,8 +4753,7 @@
4752 4753
     "component-bind": {
4753 4754
       "version": "1.0.0",
4754 4755
       "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
4755
-      "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=",
4756
-      "dev": true
4756
+      "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E="
4757 4757
     },
4758 4758
     "component-emitter": {
4759 4759
       "version": "1.3.0",
@@ -4764,8 +4764,7 @@
4764 4764
     "component-inherit": {
4765 4765
       "version": "0.0.3",
4766 4766
       "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz",
4767
-      "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=",
4768
-      "dev": true
4767
+      "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM="
4769 4768
     },
4770 4769
     "compressible": {
4771 4770
       "version": "2.0.18",
@@ -9131,11 +9130,25 @@
9131 9130
         }
9132 9131
       }
9133 9132
     },
9133
+    "has-binary2": {
9134
+      "version": "1.0.3",
9135
+      "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz",
9136
+      "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==",
9137
+      "requires": {
9138
+        "isarray": "2.0.1"
9139
+      },
9140
+      "dependencies": {
9141
+        "isarray": {
9142
+          "version": "2.0.1",
9143
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
9144
+          "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
9145
+        }
9146
+      }
9147
+    },
9134 9148
     "has-cors": {
9135 9149
       "version": "1.1.0",
9136 9150
       "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
9137
-      "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=",
9138
-      "dev": true
9151
+      "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk="
9139 9152
     },
9140 9153
     "has-flag": {
9141 9154
       "version": "3.0.0",
@@ -9571,8 +9584,7 @@
9571 9584
     "indexof": {
9572 9585
       "version": "0.0.1",
9573 9586
       "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
9574
-      "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=",
9575
-      "dev": true
9587
+      "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10="
9576 9588
     },
9577 9589
     "infer-owner": {
9578 9590
       "version": "1.0.4",
@@ -13468,8 +13480,7 @@
13468 13480
     "negotiator": {
13469 13481
       "version": "0.6.2",
13470 13482
       "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
13471
-      "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==",
13472
-      "dev": true
13483
+      "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
13473 13484
     },
13474 13485
     "neo-async": {
13475 13486
       "version": "2.6.1",
@@ -13925,8 +13936,7 @@
13925 13936
     "object-component": {
13926 13937
       "version": "0.0.3",
13927 13938
       "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz",
13928
-      "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=",
13929
-      "dev": true
13939
+      "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE="
13930 13940
     },
13931 13941
     "object-copy": {
13932 13942
       "version": "0.1.0",
@@ -14440,7 +14450,6 @@
14440 14450
       "version": "0.0.5",
14441 14451
       "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz",
14442 14452
       "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=",
14443
-      "dev": true,
14444 14453
       "requires": {
14445 14454
         "better-assert": "~1.0.0"
14446 14455
       }
@@ -14449,7 +14458,6 @@
14449 14458
       "version": "0.0.5",
14450 14459
       "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz",
14451 14460
       "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=",
14452
-      "dev": true,
14453 14461
       "requires": {
14454 14462
         "better-assert": "~1.0.0"
14455 14463
       }
@@ -15043,6 +15051,12 @@
15043 15051
         "webdriver-manager": "^12.0.6"
15044 15052
       },
15045 15053
       "dependencies": {
15054
+        "@types/node": {
15055
+          "version": "6.14.10",
15056
+          "resolved": "https://registry.npmjs.org/@types/node/-/node-6.14.10.tgz",
15057
+          "integrity": "sha512-pF4HjZGSog75kGq7B1InK/wt/N08BuPATo+7HRfv7gZUzccebwv/fmWVGs/j6LvSiLWpCuGGhql51M/wcQsNzA==",
15058
+          "dev": true
15059
+        },
15046 15060
         "del": {
15047 15061
           "version": "2.2.2",
15048 15062
           "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz",
@@ -15775,20 +15789,191 @@
15775 15789
       "integrity": "sha512-ZYzRkETgBrdEGzL5JSKimvjI2CX7ioyZCkX2BpcfyjqI+079W0wHAyj5W4rIZMcDSOHgLZtgz1IdDi/vU77KEQ=="
15776 15790
     },
15777 15791
     "rpclibrary": {
15778
-      "version": "1.10.2",
15779
-      "resolved": "https://registry.npmjs.org/rpclibrary/-/rpclibrary-1.10.2.tgz",
15780
-      "integrity": "sha512-uf4FtylK+tyVo1O8YVihxYxaI4KHnKBzq05SsLYe8ZPXdO8Hqrv7Je5cwv3bfx0FBCO1vVrSc8DT1P6ypGY6uQ==",
15792
+      "version": "2.0.1",
15793
+      "resolved": "https://registry.npmjs.org/rpclibrary/-/rpclibrary-2.0.1.tgz",
15794
+      "integrity": "sha512-phdU0TkrnNVXdFa5PKkbWvn+MxocowvqalEUG1jfpkc1CoQkJ9AXa4OXSEu5r2a2IFhVauYva/qXv1em5yrHSg==",
15781 15795
       "requires": {
15782
-        "bsock": "^0.1.9",
15783
-        "crypto-js": "^4.0.0",
15796
+        "@types/socket.io": "^2.1.8",
15797
+        "@types/socket.io-client": "^1.4.33",
15784 15798
         "http": "0.0.0",
15799
+        "socket.io": "^2.3.0",
15800
+        "socket.io-client": "^2.3.0",
15785 15801
         "uuid": "^3.3.3"
15786 15802
       },
15787 15803
       "dependencies": {
15788
-        "crypto-js": {
15789
-          "version": "4.0.0",
15790
-          "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.0.0.tgz",
15791
-          "integrity": "sha512-bzHZN8Pn+gS7DQA6n+iUmBfl0hO5DJq++QP3U6uTucDtk/0iGpXd/Gg7CGR0p8tJhofJyaKoWBuJI4eAO00BBg=="
15804
+        "arraybuffer.slice": {
15805
+          "version": "0.0.7",
15806
+          "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz",
15807
+          "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog=="
15808
+        },
15809
+        "base64id": {
15810
+          "version": "2.0.0",
15811
+          "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
15812
+          "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog=="
15813
+        },
15814
+        "blob": {
15815
+          "version": "0.0.5",
15816
+          "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz",
15817
+          "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig=="
15818
+        },
15819
+        "component-emitter": {
15820
+          "version": "1.2.1",
15821
+          "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
15822
+          "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY="
15823
+        },
15824
+        "cookie": {
15825
+          "version": "0.3.1",
15826
+          "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
15827
+          "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
15828
+        },
15829
+        "engine.io": {
15830
+          "version": "3.4.2",
15831
+          "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.4.2.tgz",
15832
+          "integrity": "sha512-b4Q85dFkGw+TqgytGPrGgACRUhsdKc9S9ErRAXpPGy/CXKs4tYoHDkvIRdsseAF7NjfVwjRFIn6KTnbw7LwJZg==",
15833
+          "requires": {
15834
+            "accepts": "~1.3.4",
15835
+            "base64id": "2.0.0",
15836
+            "cookie": "0.3.1",
15837
+            "debug": "~4.1.0",
15838
+            "engine.io-parser": "~2.2.0",
15839
+            "ws": "^7.1.2"
15840
+          }
15841
+        },
15842
+        "engine.io-client": {
15843
+          "version": "3.4.3",
15844
+          "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.3.tgz",
15845
+          "integrity": "sha512-0NGY+9hioejTEJCaSJZfWZLk4FPI9dN+1H1C4+wj2iuFba47UgZbJzfWs4aNFajnX/qAaYKbe2lLTfEEWzCmcw==",
15846
+          "requires": {
15847
+            "component-emitter": "~1.3.0",
15848
+            "component-inherit": "0.0.3",
15849
+            "debug": "~4.1.0",
15850
+            "engine.io-parser": "~2.2.0",
15851
+            "has-cors": "1.1.0",
15852
+            "indexof": "0.0.1",
15853
+            "parseqs": "0.0.5",
15854
+            "parseuri": "0.0.5",
15855
+            "ws": "~6.1.0",
15856
+            "xmlhttprequest-ssl": "~1.5.4",
15857
+            "yeast": "0.1.2"
15858
+          },
15859
+          "dependencies": {
15860
+            "component-emitter": {
15861
+              "version": "1.3.0",
15862
+              "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
15863
+              "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg=="
15864
+            },
15865
+            "ws": {
15866
+              "version": "6.1.4",
15867
+              "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz",
15868
+              "integrity": "sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==",
15869
+              "requires": {
15870
+                "async-limiter": "~1.0.0"
15871
+              }
15872
+            }
15873
+          }
15874
+        },
15875
+        "engine.io-parser": {
15876
+          "version": "2.2.0",
15877
+          "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.0.tgz",
15878
+          "integrity": "sha512-6I3qD9iUxotsC5HEMuuGsKA0cXerGz+4uGcXQEkfBidgKf0amsjrrtwcbwK/nzpZBxclXlV7gGl9dgWvu4LF6w==",
15879
+          "requires": {
15880
+            "after": "0.8.2",
15881
+            "arraybuffer.slice": "~0.0.7",
15882
+            "base64-arraybuffer": "0.1.5",
15883
+            "blob": "0.0.5",
15884
+            "has-binary2": "~1.0.2"
15885
+          }
15886
+        },
15887
+        "isarray": {
15888
+          "version": "2.0.1",
15889
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
15890
+          "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
15891
+        },
15892
+        "ms": {
15893
+          "version": "2.0.0",
15894
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
15895
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
15896
+        },
15897
+        "socket.io": {
15898
+          "version": "2.3.0",
15899
+          "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.3.0.tgz",
15900
+          "integrity": "sha512-2A892lrj0GcgR/9Qk81EaY2gYhCBxurV0PfmmESO6p27QPrUK1J3zdns+5QPqvUYK2q657nSj0guoIil9+7eFg==",
15901
+          "requires": {
15902
+            "debug": "~4.1.0",
15903
+            "engine.io": "~3.4.0",
15904
+            "has-binary2": "~1.0.2",
15905
+            "socket.io-adapter": "~1.1.0",
15906
+            "socket.io-client": "2.3.0",
15907
+            "socket.io-parser": "~3.4.0"
15908
+          }
15909
+        },
15910
+        "socket.io-adapter": {
15911
+          "version": "1.1.2",
15912
+          "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz",
15913
+          "integrity": "sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g=="
15914
+        },
15915
+        "socket.io-client": {
15916
+          "version": "2.3.0",
15917
+          "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.3.0.tgz",
15918
+          "integrity": "sha512-cEQQf24gET3rfhxZ2jJ5xzAOo/xhZwK+mOqtGRg5IowZsMgwvHwnf/mCRapAAkadhM26y+iydgwsXGObBB5ZdA==",
15919
+          "requires": {
15920
+            "backo2": "1.0.2",
15921
+            "base64-arraybuffer": "0.1.5",
15922
+            "component-bind": "1.0.0",
15923
+            "component-emitter": "1.2.1",
15924
+            "debug": "~4.1.0",
15925
+            "engine.io-client": "~3.4.0",
15926
+            "has-binary2": "~1.0.2",
15927
+            "has-cors": "1.1.0",
15928
+            "indexof": "0.0.1",
15929
+            "object-component": "0.0.3",
15930
+            "parseqs": "0.0.5",
15931
+            "parseuri": "0.0.5",
15932
+            "socket.io-parser": "~3.3.0",
15933
+            "to-array": "0.1.4"
15934
+          },
15935
+          "dependencies": {
15936
+            "socket.io-parser": {
15937
+              "version": "3.3.0",
15938
+              "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.0.tgz",
15939
+              "integrity": "sha512-hczmV6bDgdaEbVqhAeVMM/jfUfzuEZHsQg6eOmLgJht6G3mPKMxYm75w2+qhAQZ+4X+1+ATZ+QFKeOZD5riHng==",
15940
+              "requires": {
15941
+                "component-emitter": "1.2.1",
15942
+                "debug": "~3.1.0",
15943
+                "isarray": "2.0.1"
15944
+              },
15945
+              "dependencies": {
15946
+                "debug": {
15947
+                  "version": "3.1.0",
15948
+                  "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
15949
+                  "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
15950
+                  "requires": {
15951
+                    "ms": "2.0.0"
15952
+                  }
15953
+                }
15954
+              }
15955
+            }
15956
+          }
15957
+        },
15958
+        "socket.io-parser": {
15959
+          "version": "3.4.1",
15960
+          "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.4.1.tgz",
15961
+          "integrity": "sha512-11hMgzL+WCLWf1uFtHSNvliI++tcRUWdoeYuwIl+Axvwy9z2gQM+7nJyN3STj1tLj5JyIUH8/gpDGxzAlDdi0A==",
15962
+          "requires": {
15963
+            "component-emitter": "1.2.1",
15964
+            "debug": "~4.1.0",
15965
+            "isarray": "2.0.1"
15966
+          }
15967
+        },
15968
+        "ws": {
15969
+          "version": "7.3.1",
15970
+          "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz",
15971
+          "integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA=="
15972
+        },
15973
+        "xmlhttprequest-ssl": {
15974
+          "version": "1.5.5",
15975
+          "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz",
15976
+          "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4="
15792 15977
         }
15793 15978
       }
15794 15979
     },
@@ -17979,8 +18164,7 @@
17979 18164
     "to-array": {
17980 18165
       "version": "0.1.4",
17981 18166
       "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz",
17982
-      "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=",
17983
-      "dev": true
18167
+      "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA="
17984 18168
     },
17985 18169
     "to-arraybuffer": {
17986 18170
       "version": "1.0.1",
@@ -21030,8 +21214,7 @@
21030 21214
     "yeast": {
21031 21215
       "version": "0.1.2",
21032 21216
       "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
21033
-      "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=",
21034
-      "dev": true
21217
+      "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
21035 21218
     },
21036 21219
     "yn": {
21037 21220
       "version": "3.1.1",

+ 2
- 2
src/frontend/package.json 查看文件

@@ -79,7 +79,7 @@
79 79
     "normalize.css": "6.0.0",
80 80
     "pace-js": "1.0.2",
81 81
     "roboto-fontface": "0.8.0",
82
-    "rpclibrary": "^1.10.2",
82
+    "rpclibrary": "^2.0.1",
83 83
     "rxjs": "6.5.2",
84 84
     "rxjs-compat": "6.3.0",
85 85
     "socicon": "3.0.5",
@@ -102,7 +102,7 @@
102 102
     "@types/jasmine": "2.5.54",
103 103
     "@types/jasminewd2": "2.0.3",
104 104
     "@types/leaflet": "1.2.3",
105
-    "@types/node": "6.0.90",
105
+    "@types/node": "^14.0.27",
106 106
     "codelyzer": "^5.0.1",
107 107
     "conventional-changelog-cli": "1.3.4",
108 108
     "jasmine-core": "2.6.4",

+ 1
- 0
src/frontend/src/app/frontcraft/pages/armory/armory.component.ts 查看文件

@@ -51,6 +51,7 @@ export class FrontcraftArmoryComponent implements OnInit {
51 51
       const geardata = maybeItems.filter(maybeItem => maybeItem != null)
52 52
 
53 53
       this.gear = geardata
54
+      console.log(this.gear)
54 55
       console.log(this.gear.map(g => g.stats).reduce(sumStats))
55 56
     }))
56 57
   }

+ 0
- 19
src/frontend/src/app/frontcraft/pages/character/character.component.html 查看文件

@@ -21,24 +21,5 @@ class = "col-12 col-xl-9">
21 21
         <span *ngIf="link === 'owner'">
22 22
             Owned by <a [routerLink]="'/frontcraft/user/'+char.username"> {{char.username}} ({{char.rank}})</a>
23 23
         </span>
24
-        <br/>
25
-        <br />
26
-        <br />
27
-
28
-        <h3>Tokens</h3>
29
-        <span *ngFor="let token of tokens">
30
-            [ {{token.level}} ]
31
-            <wowhead [item]="token"></wowhead><br />
32
-        </span>
33
-
34
-        <br />
35
-        <br />
36
-        <br />
37
-        <h3>Gear of the last reported kill <a target="_blank" *ngIf="dataLink != null && dataLink != ''" style="size: 0.5em;" [href]="dataLink">(data)</a></h3>
38
-        <h5>{{boss2.name}} {{boss2.date | date : 'MMM d @ HH : mm'}}</h5>
39
-        <ng-container *ngFor="let item of gear2">
40
-            <wowhead [item]="item"></wowhead><br />
41
-        </ng-container>
42
-
43 24
     </nb-card-body>
44 25
 </nb-card>

+ 15
- 6
src/frontend/src/app/frontcraft/pages/raid/characterpicker.component.html 查看文件

@@ -4,6 +4,7 @@
4 4
     </nb-card-header>
5 5
     <nb-card-body>
6 6
         <textarea 
7
+            fullWidth
7 8
             placeholder="Add a note (optional)"
8 9
             rows="7" 
9 10
             cols="50" 
@@ -13,30 +14,38 @@
13 14
         <br />
14 15
         <br />
15 16
         <nb-card *ngFor="let character of characters">
16
-            <nb-card-header [ngStyle]="{'color': character.color}">
17
+            <nb-card-header [ngStyle]="{'color': character.color}" style="text-transform: capitalize;">
17 18
                 <img [src]="'../../../../assets/images/'+character.class.toLowerCase()+'.png'" />
18 19
                 {{character.charactername}}
19 20
             </nb-card-header>
20 21
             <nb-card-body>
21 22
                 <button
22
-                    (click)="signup(character, false)"
23
+                    (click)="signup(character, false, false)"
23 24
                     nbButton 
24 25
                     outline 
26
+                    fullWidth
25 27
                     status="success" 
26 28
                     size="medium">
27 29
                     <nb-icon icon="checkmark-outline"></nb-icon> On Time
28 30
                 </button>
29
-                &nbsp;
30
-                &nbsp;
31
-                &nbsp;
32 31
                 <button
33
-                    (click)="signup(character, true)"
32
+                    (click)="signup(character, true, false)"
34 33
                     nbButton 
35 34
                     outline 
35
+                    fullWidth
36 36
                     status="warning" 
37 37
                     size="medium">
38 38
                     <nb-icon icon="clock-outline"></nb-icon> Late
39 39
                 </button>
40
+                <button
41
+                    (click)="signup(character, false, true)"
42
+                    nbButton 
43
+                    outline 
44
+                    fullWidth
45
+                    status="danger" 
46
+                    size="medium">
47
+                    <nb-icon icon="person-delete-outline"></nb-icon> Absent
48
+                </button>
40 49
             </nb-card-body>
41 50
         </nb-card>
42 51
     </nb-card-body>

+ 2
- 2
src/frontend/src/app/frontcraft/pages/raid/characterpicker.component.ts 查看文件

@@ -20,12 +20,12 @@ export class FrontcraftCharacerpickerComponent implements OnInit{
20 20
         private toast : NbToastrService
21 21
     ){}
22 22
 
23
-    signup = async (character: Character, late: boolean) => {
23
+    signup = async (character: Character, late: boolean, attending: boolean = true) => {
24 24
         const auth = this.api.getAuth()
25 25
         const signup = this.api.get('signup')
26 26
         if(!signup) return
27 27
 
28
-        await signup.sign(auth.token.value, character, this.raid, late, this.memo)
28
+        await signup.sign(auth.token.value, character, this.raid, late, attending, this.memo)
29 29
         this.toast.show('Signup', 'Success', { status: 'success' })
30 30
         this.dialogRef.close()
31 31
     }

+ 32
- 50
src/frontend/src/app/frontcraft/pages/raid/raid.component.html 查看文件

@@ -26,35 +26,40 @@
26 26
                                     ({{mySignup.race}} {{mySignup.specname}} {{mySignup.class}})
27 27
                                     <br />
28 28
                                     Status: {{mySignup.status}}<br /><br />
29
-                                    <textarea [status]="mySignup.status === 'Attending'?'success':'warning'"
29
+                                    <textarea fullWidth *ngIf="mySignup.status !== 'Absent'"
30
+                                        [status]="mySignup.status === 'Attending'?'success':'warning'"
30 31
                                         placeholder="Add a note (optional)" rows="10" cols="50" nbInput
31 32
                                         style="white-space: pre-line;" [(ngModel)]="mySignup.memo">
32 33
                                     </textarea>
33 34
                                 </p>
34 35
 
35
-                                <button *ngIf="mySignup.status === 'Late'" (click)="setLate(false)" nbButton outline
36
-                                    status="success" size="medium">
36
+                                <button *ngIf="mySignup.status !== 'Attending'" (click)="setLate(false)" nbButton
37
+                                    fullWidth outline status="success" size="medium">
37 38
                                     <nb-icon icon="checkmark-outline"></nb-icon> On Time
38 39
                                 </button>
39 40
                                 <button *ngIf="mySignup.status === 'Attending'" (click)="setLate(false)" nbButton
40
-                                    outline status="success" size="medium">
41
+                                    fullWidth outline status="success" size="medium">
41 42
                                     <nb-icon icon="checkmark-outline"></nb-icon> Update
42 43
                                 </button>
43
-                                <button *ngIf="mySignup.status === 'Late'" (click)="setLate(true)" nbButton outline
44
-                                    status="warning" size="medium">
44
+                                <button *ngIf="mySignup.status === 'Late'" (click)="setLate(true)" nbButton fullWidth
45
+                                    outline status="warning" size="medium">
45 46
                                     <nb-icon icon="clock-outline"></nb-icon> Update
46 47
                                 </button>
47
-                                <button *ngIf="mySignup.status === 'Attending'" (click)="setLate(true)" nbButton outline
48
-                                    status="warning" size="medium">
48
+                                <button *ngIf="mySignup.status !== 'Late'" (click)="setLate(true)" nbButton fullWidth
49
+                                    outline status="warning" size="medium">
49 50
                                     <nb-icon icon="clock-outline"></nb-icon> Late
50 51
                                 </button>
51
-
52
-                                <button (click)="unsign()" nbButton outline status="danger" size="medium">
52
+                                <button *ngIf="mySignup.status !== 'Absent'" (click)="setAbsent()" nbButton fullWidth
53
+                                    outline status="danger" size="medium">
54
+                                    <nb-icon icon="person-delete-outline"></nb-icon> Absent
55
+                                </button>
56
+                                <p>&nbsp;</p>
57
+                                <button (click)="unsign()" nbButton fullWidth status="danger" size="medium">
53 58
                                     <nb-icon icon="close"></nb-icon>unsign
54 59
                                 </button>
55 60
                             </div>
56 61
                             <div *ngIf="!isSignedup">
57
-                                <button (click)="signup()" nbButton outline status="success" size="medium">
62
+                                <button (click)="signup()" fullWidth nbButton outline status="success" size="medium">
58 63
                                     <nb-icon icon="person-done-outline"></nb-icon> sign up
59 64
                                 </button>
60 65
                             </div>
@@ -83,21 +88,26 @@
83 88
                                                 [src]="'/assets/images/'+participant.class.toLowerCase()+'.png'" />
84 89
                                             {{ participant.charactername }}
85 90
                                         </a>
86
-                                        <ng-template #template>
91
+                                        <ng-template #timeInfo>
92
+                                            <div style="padding: 10px">
93
+                                                {{participant.timestamp | date : 'HH:mm EEE MMM d'}}
94
+                                                ({{participant.before | date : 'dd'}} days before start)
95
+                                            </div>
96
+                                        </ng-template>
97
+                                        <nb-icon [nbPopover]="timeInfo" nbPopoverTrigger="hover"
98
+                                            style="width: 0.75em; height: 0.75em;" icon="clock-outline"
99
+                                            [status]="participant.before>8.64e+7?(participant.before>2.592e+8?'success':'warning'):'danger'">
100
+                                        </nb-icon>
101
+                                        <ng-template #memoBox>
87 102
                                             <div style="padding: 10px">
88 103
                                                 <p
89 104
                                                     style="white-space: pre-line; max-width: 50vw; word-wrap: break-word;">
90 105
                                                     {{participant.memo}}
91 106
                                                 </p>
92
-                                                {{participant.timestamp | date : 'HH:mm EEE MMM d'}} ({{participant.before | date : 'dd'}} days before start)
93 107
                                             </div>
94 108
                                         </ng-template>
95
-                                        <nb-icon
96
-                                        [nbPopover]="template" 
97
-                                        nbPopoverTrigger="hover"
98
-                                        style="width: 0.75em; height: 0.75em;" 
99
-                                        [icon]="participant.memo != null && participant.memo != ''?'message-circle-outline':'clock-outline'"
100
-                                        [status]="participant.before>8.64e+7?(participant.before>2.592e+8?'success':'warning'):'danger'">
109
+                                        <nb-icon *ngIf="participant.memo" [nbPopover]="memoBox" nbPopoverTrigger="hover"
110
+                                            style="width: 0.75em; height: 0.75em;" icon="message-circle-outline">
101 111
                                         </nb-icon>
102 112
 
103 113
                                         <span *ngIf="participant.rank=='Trial'" style="font-size: 9px;">
@@ -127,21 +137,6 @@
127 137
                                                 [src]="'/assets/images/'+participant.class.toLowerCase()+'.png'" />
128 138
                                             {{ participant.charactername }}
129 139
                                         </a>
130
-                                        <ng-template #template>
131
-                                            <div style="padding: 10px">
132
-                                                <p style="white-space: pre-line;">
133
-                                                    {{participant.memo}}
134
-                                                </p>
135
-                                                {{participant.timestamp | date : 'HH:mm EEE MMM d'}} ({{participant.before | date : 'dd'}} days before start)
136
-                                            </div>
137
-                                        </ng-template>
138
-                                        <nb-icon
139
-                                            [nbPopover]="template" 
140
-                                            nbPopoverTrigger="hover"
141
-                                            style="width: 0.75em; height: 0.75em;" 
142
-                                            [icon]="participant.memo != null && participant.memo != ''?'message-circle-outline':'clock-outline'"
143
-                                            [status]="participant.before>8.64e+7?(participant.before>2.592e+8?'success':'warning'):'danger'">
144
-                                        </nb-icon>
145 140
                                         <span *ngIf="participant.rank=='Trial'" style="font-size: 9px;">
146 141
                                             Trial
147 142
                                         </span>
@@ -171,22 +166,6 @@
171 166
                                                     [src]="'/assets/images/'+participant.class.toLowerCase()+'.png'" />
172 167
                                                 {{ participant.charactername }}
173 168
                                             </a>
174
-                                            <ng-template #template>
175
-                                                <div style="padding: 10px">
176
-                                                    <p style="white-space: pre-line;">
177
-                                                        {{participant.memo}}
178
-                                                    </p>
179
-                                                    {{participant.timestamp | date : 'HH:mm EEE MMM d'}} ({{participant.before | date : 'dd'}} days before start)
180
-                                                </div>
181
-                                            </ng-template>
182
-                                            <nb-icon
183
-                                                [nbPopover]="template" 
184
-                                                nbPopoverTrigger="hover"
185
-                                                style="width: 0.75em; height: 0.75em;" 
186
-                                                [icon]="participant.memo != null && participant.memo != ''?'message-circle-outline':'clock-outline'"
187
-                                                [status]="participant.before>8.64e+7?(participant.before>2.592e+8?'success':'warning'):'danger'">
188
-                                            </nb-icon>
189
-
190 169
                                             <span *ngIf="participant.rank=='Trial'" style="font-size: 9px;">
191 170
                                                 Trial
192 171
                                             </span>
@@ -200,6 +179,9 @@
200 179
                         <button (click)="startRaid(raid)" nbButton outline status="success" size="medium">
201 180
                             start
202 181
                         </button>
182
+                        <button (click)="cancelRaid(raid)" nbButton outline status="danger" size="medium">
183
+                            cancel
184
+                        </button>
203 185
                     </nb-tab>
204 186
                 </nb-tabset>
205 187
             </nb-card-body>

+ 40
- 8
src/frontend/src/app/frontcraft/pages/raid/raid.component.ts 查看文件

@@ -120,6 +120,12 @@ export class FrontcraftRaidComponent
120 120
     this.router.navigateByUrl('/frontcraft/archive/' + raid.id)
121 121
   }
122 122
 
123
+  async cancelRaid(raid: Raid) {
124
+    await this.manageRaid!.cancelRaid(raid)
125
+    this.toast.show('Raid cancelled', 'Success', { status: 'success' })
126
+    this.router.navigateByUrl('/frontcraft/raids')
127
+  }
128
+
123 129
   unsign = async () => {
124 130
     const signupFeature = this.api.get('signup')
125 131
     if (!signupFeature) return
@@ -139,7 +145,21 @@ export class FrontcraftRaidComponent
139 145
     await signup.sign(auth.token.value, {
140 146
       ...this.mySignup,
141 147
       id: this.mySignup.characterid,
142
-    }, this.raid, value, this.mySignup.memo)
148
+    }, this.raid, value, false, this.mySignup.memo)
149
+
150
+    this.toast.show('Signup', 'Success', { status: 'success' })
151
+  }
152
+
153
+  setAbsent = async () => {
154
+    const auth = this.api.getAuth()
155
+    const signup = this.api.get('signup')
156
+    if (!signup) return
157
+
158
+    await signup.sign(auth.token.value, {
159
+      ...this.mySignup,
160
+      id: this.mySignup.characterid,
161
+    }, this.raid, false, true, this.mySignup.memo)
162
+    
143 163
     this.toast.show('Signup', 'Success', { status: 'success' })
144 164
   }
145 165
 
@@ -184,7 +204,9 @@ export class FrontcraftRaidComponent
184 204
     if (matchingSignup) {
185 205
       this.mySignup = matchingSignup
186 206
       this.isSignedup = true
187
-      this.mySignup['status'] = matchingSignup['benched'] ? 'Bench' : matchingSignup['late'] ? 'Late' : 'Attending'
207
+      console.log(this.mySignup);
208
+      
209
+      this.mySignup['status'] = matchingSignup['absent'] ? 'Absent' : matchingSignup['benched'] ? 'Bench' : matchingSignup['late'] ? 'Late' : 'Attending'
188 210
     } else {
189 211
       this.isSignedup = false
190 212
       this.mySignup = null
@@ -209,11 +231,21 @@ export class FrontcraftRaidComponent
209 231
   }
210 232
 
211 233
   setBench(signup: Signup) {
212
-    this.manageRaid.setBenched({
213
-      characterid: signup.characterid,
214
-      raidid: signup.raidid,
215
-      late: false,
216
-      benched: !signup.benched
217
-    }).then(_ => this.refresh())
234
+
235
+    const manageRaid = this.api.get('manageRaid')
236
+    if(manageRaid){
237
+      const signupUpdated:Signup = {
238
+        benched: !signup.benched,
239
+        absent: false,
240
+        late: false,
241
+        characterid: signup.characterid,
242
+        raidid: signup.raidid,
243
+        timestamp: signup.timestamp,
244
+        id: signup.id,
245
+        memo: signup.memo
246
+      }
247
+      manageRaid.updateSignup(signupUpdated)
248
+      .then(_ => this.refresh())
249
+    }
218 250
   }
219 251
 }

+ 5
- 5
src/frontend/src/app/frontcraft/pages/shop/buytoken.component.ts 查看文件

@@ -14,7 +14,7 @@ import { IApiService } from '../../services/ApiService/ApiService';
14 14
       </nb-card-header>
15 15
       <nb-card-body>
16 16
         <p>
17
-          {{currency}} softreserves remaining
17
+          You have {{currency}} softreserve(s) remaining
18 18
         </p>  
19 19
         <div *ngIf="currency>0">
20 20
             <nb-alert accent="danger" *ngIf="invalidatedTokens.length > 0">
@@ -37,7 +37,7 @@ import { IApiService } from '../../services/ApiService/ApiService';
37 37
                 The following reserve will be created<br />
38 38
                 <ul>
39 39
                     <li>
40
-                        [ {{1+modifier}} ] 
40
+                        [ {{1+currency+modifier}} ] 
41 41
                         <img style="min-width: 20px; width: 2.25vw; max-width: 35px" [src]="'https://wow.zamimg.com/images/wow/icons/large/'+item.iconname+'.jpg'" />
42 42
                         <span [ngStyle]="{'color':item.quality=='Epic'?'#a335ee':'#ff8000'}">
43 43
                         {{item.itemname}}
@@ -49,10 +49,10 @@ import { IApiService } from '../../services/ApiService/ApiService';
49 49
             </nb-alert>
50 50
 
51 51
             <nb-alert accent="info" *ngIf="upgradableToken != null">
52
-                The following reserve will be created<br />
52
+                The following reserve will be upgraded<br />
53 53
                 <ul>
54 54
                     <li>
55
-                        [ {{upgradableToken.level}} ] => [ {{upgradableToken.level+1}} ] <img style="min-width: 20px; width: 2.25vw; max-width: 35px" [src]="'https://wow.zamimg.com/images/wow/icons/large/'+upgradableToken.iconname+'.jpg'" />
55
+                        [ {{upgradableToken.level}} ] => [ {{upgradableToken.level+1+currency}} ] <img style="min-width: 20px; width: 2.25vw; max-width: 35px" [src]="'https://wow.zamimg.com/images/wow/icons/large/'+upgradableToken.iconname+'.jpg'" />
56 56
                         <span [ngStyle]="{'color':upgradableToken.quality=='Epic'?'#a335ee':'#ff8000'}">{{upgradableToken.itemname}}</span>
57 57
                     </li>
58 58
                 </ul>
@@ -65,7 +65,7 @@ import { IApiService } from '../../services/ApiService/ApiService';
65 65
                   outline 
66 66
                   status="success" 
67 67
                   size="small">
68
-                  <nb-icon icon="checkmark-outline"></nb-icon> claim
68
+                  <nb-icon icon="checkmark-outline"></nb-icon> okay
69 69
             </button>
70 70
       </nb-card-body>
71 71
   </nb-card>

+ 3
- 3
src/frontend/src/app/frontcraft/services/ApiService/client-login-api.ts 查看文件

@@ -59,7 +59,7 @@ export class ClientApiService implements IApiService{
59 59
 //            document.cookie = JSON.stringify(auth)
60 60
             return authSock
61 61
         }catch(e){ 
62
-            if(this.privSocket) this.privSocket.destroy()
62
+            if(this.privSocket) this.privSocket.close()
63 63
             return;
64 64
         }
65 65
     }
@@ -120,14 +120,14 @@ export class ClientApiService implements IApiService{
120 120
             }
121 121
         } 
122 122
 
123
-        if(this.privSocket) this.privSocket.destroy()
123
+        if(this.privSocket) this.privSocket.close()
124 124
         this.privSocket = null
125 125
         this.auth  = null
126 126
     }
127 127
 
128 128
     initialize = async (force = false) : Promise<any> => {
129 129
         if(this.socket){
130
-            this.socket.destroy()
130
+            this.socket.close()
131 131
             this.socket = null
132 132
         }
133 133
 

+ 2
- 2
src/frontend/src/app/frontcraft/services/ApiService/server-login-api.ts 查看文件

@@ -71,14 +71,14 @@ export class ServerApiService implements IApiService {
71 71
             }
72 72
         }
73 73
 
74
-        if (this.privSocket) this.privSocket.destroy()
74
+        if (this.privSocket) this.privSocket.close()
75 75
         this.privSocket = null
76 76
         this.auth = null
77 77
     }
78 78
 
79 79
     initialize = async (): Promise<any> => {
80 80
         if (this.socket) {
81
-            this.socket.destroy()
81
+            this.socket.close()
82 82
             this.socket = null
83 83
         }
84 84
 

+ 31
- 28
test/backendTest.ts 查看文件

@@ -5,6 +5,7 @@ import { T2, Tiers } from "../src/backend/Types/Items";
5 5
 import { RPCSocket, ConnectedSocket } from "rpclibrary";
6 6
 import { FrontcraftIfc, Auth, User, FrontcraftFeatureIfc, Raid, Character, Rank, Class, Race, Spec, Signup } from "../src/backend/Types/Types";
7 7
 import { SpecT } from "../src/backend/Types/PlayerSpecs";
8
+import { ItemManager } from "src/backend/Components/Item/ItemManager";
8 9
 
9 10
 
10 11
 type protoAccount<C extends Class = Class> = {
@@ -166,7 +167,6 @@ describe('Frontcraft', () => {
166 167
                     username: 'a',
167 168
                     MC: 2
168 169
                 }).then(adminUser => {
169
-
170 170
                     client.UserManager.login(adminUser.username, 'ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb').then(auth => {
171 171
                         const sock = new RPCSocket<FrontcraftFeatureIfc>(
172 172
                             auth.port,
@@ -194,8 +194,8 @@ describe('Frontcraft', () => {
194 194
     })
195 195
 
196 196
     after(() => {
197
-        client.destroy()
198
-        adminClient.destroy()
197
+        client.close()
198
+        adminClient.close()
199 199
         server.stop()
200 200
     })
201 201
 
@@ -240,8 +240,8 @@ describe('Frontcraft', () => {
240 240
 
241 241
     it('should sign up', (done) => {
242 242
         Promise.all(Object.values(users).map((user) =>
243
-            adminClient.signup.sign(user.auth.token.value, user.character, raids[0], false, ""+Math.floor(Math.random()*10000)).catch(done)
244
-        )).then(x => {
243
+            adminClient.signup.sign(user.auth.token.value, user.character, raids[0], false, true, ""+Math.floor(Math.random()*10000)).catch(done)
244
+        )).then(_ => {
245 245
             adminClient.signup.getSignups(raids[0]).then(s => {
246 246
                 if (s.length == testAccounts.length) {
247 247
                     s.forEach(sign => {
@@ -539,7 +539,7 @@ describe('Frontcraft', () => {
539 539
             users[user.account.username].item = itemname
540 540
 
541 541
             if (!token) return false
542
-            return modifier + 1 === token.level
542
+            return modifier + 2 === token.level
543 543
         })).then(success => {
544 544
             if (success.reduce((prev, curr) => prev && curr, true)) done()
545 545
         })
@@ -564,16 +564,8 @@ describe('Frontcraft', () => {
564 564
             await adminClient.softreserveCurrency.incrementCurrency(user.account, item!.tier, 2)
565 565
             const before = await client.ItemManager.getToken(user.character, item!)
566 566
 
567
-            if (!before || before.level !== 4) {
568
-                console.log("expected level to be 4", before ? before.level : '?');
569
-                return
570
-            }
571
-
572
-            await client.ItemManager.buyToken(user.auth.token.value, user.character.charactername, itemname, user.signup!)
573
-            const after = await client.ItemManager.buyToken(user.auth.token.value, user.character.charactername, itemname, user.signup!)
574
-
575
-            if (!after || after.level !== 6) {
576
-                console.log("expected level after to be 6", after ? after.level : '?');
567
+            if (!before || before.level !== 5) {
568
+                console.log("expected level to be 5", before ? before.level : '?');
577 569
                 return
578 570
             }
579 571
             done()
@@ -625,7 +617,7 @@ describe('Frontcraft', () => {
625 617
         adminClient.reset.wipeCurrencyAndItems().then(() => {
626 618
             client.UserManager.getUser(testAccounts[0].name).then(user => {
627 619
                 if (user && user.MC === 1) {
628
-                    client.ItemManager.getTokens(users[testAccounts[0].name.toLowerCase()].character, ['BWL']).then(tokens => {
620
+                    client.ItemManager.getTokens(users[testAccounts[0].name.toLowerCase()].character, ['BWL'], true).then(tokens => {
629 621
                         if (tokens!.length === 0) {
630 622
                             done()
631 623
                         } else {
@@ -652,7 +644,7 @@ describe('Frontcraft', () => {
652 644
         }
653 645
         const user = Object.values(users)[0]
654 646
         const createRaid = adminClient.manageRaid.createRaid
655
-        const sign = async (raid: Raid, late = false) => await adminClient.signup.sign(user.auth.token.value, user.character, raid, late)
647
+        const sign = async (raid: Raid, late = false) => await adminClient.signup.sign(user.auth.token.value, user.character, raid, late, false)
656 648
         const buyToken = async (signup: Signup, itemname: string) => await client.ItemManager.buyToken(user.auth.token.value, user.character.charactername, itemname, signup)
657 649
         const getCurrency = async (tier: Tiers) => {
658 650
             const dbUser = await client.UserManager.getUser(user.account.username)
@@ -661,7 +653,6 @@ describe('Frontcraft', () => {
661 653
 
662 654
         createRaid(Raid(0, 'BWL')).then(async (BWL0: Raid) => {
663 655
             const T2_0 = await client.ItemManager.getItem(T2[0])
664
-            //const BWL0 = await createRaid(Raid(0, 'BWL'))
665 656
             const signupBWL0 = await sign(BWL0)
666 657
             let BWL = await getCurrency(BWL0.tier)
667 658
             let token = await buyToken(signupBWL0, T2[0])
@@ -676,14 +667,26 @@ describe('Frontcraft', () => {
676 667
 
677 668
             let reserves = await client.ItemManager.getTokens(user.character, [BWL0.tier], true)
678 669
             let streaks = await client.ItemManager.getTokens(user.character, [BWL0.tier], false)
679
-            if (reserves!.length != 1
680
-                || streaks!.length != 0
681
-                || reserves![0].itemname !== T2_0!.itemname
682
-                || reserves![0].level !== 1) {
683
-                console.log("Bad Token status 1", reserves, streaks);
670
+            if (reserves!.length != 1){
671
+                console.log("Expected reserves to be of length 1", reserves);
672
+                done(new Error("Bad token status"))
673
+            }
674
+
675
+            if(streaks!.length != 0){
676
+                console.log("Expected streaks to be of length 0", streaks);
677
+                done(new Error("Bad token status"))
678
+            }
679
+
680
+            if(reserves![0].itemname !== T2_0!.itemname){
681
+                console.log("Expected reserve to to be of item "+T2_0!.itemname, reserves![0]);
682
+                done(new Error("Bad token status"))
683
+            }
684
+            
685
+            if(reserves![0].level !== 2){
686
+                console.log("Expected reserve to to be of level "+2, reserves![0].level);
684 687
                 done(new Error("Bad Token status"))
685
-                return
686 688
             }
689
+
687 690
             await adminClient.manageRaid.startRaid(BWL0)
688 691
             reserves = await client.ItemManager.getTokens(user.character, [BWL0.tier], true)
689 692
             streaks = await client.ItemManager.getTokens(user.character, [BWL0.tier], false)
@@ -691,7 +694,7 @@ describe('Frontcraft', () => {
691 694
                 || streaks!.length != 1
692 695
                 || streaks![0].itemname !== T2[0]
693 696
                 || streaks![0].level !== 1) {
694
-                console.log("Bad Token status", reserves, streaks);
697
+                console.log("Bad Token status 2", reserves, streaks);
695 698
                 done(new Error("Bad Token status 2"))
696 699
                 return
697 700
             }
@@ -726,8 +729,8 @@ describe('Frontcraft', () => {
726 729
             if (reserves!.length != 1
727 730
                 || streaks!.length != 0
728 731
                 || reserves![0].itemname !== T2[1]
729
-                || reserves![0].level !== 1) {
730
-                console.log("Bad Token status", reserves, streaks);
732
+                || reserves![0].level !== 2) {
733
+                console.log("Bad Token status 3", reserves, streaks);
731 734
                 done(new Error("Bad Token status 3"))
732 735
                 return
733 736
             }

正在加载...
取消
保存