import { Inject, Injectable } from "../../Injector/ServiceDecorator"; import { RaidManagerIfc, RaidManagerFeatureIfc } from "./RPCInterface"; import { FrontworkComponent } from "../../Types/FrontworkComponent"; import { TableDefiniton, Signup, Raid, Character, RaidData, Spec, SRToken, Item, User } from "../../Types/Types"; import { IAdmin } from "../../Admin/Interface"; import { IRaidManager } from "./Interface"; import { IUserManager } from "../User/Interface"; import { ICharacterManager } from "../Character/Interface"; import { _Tiers } from "../../Types/Items"; import { IItemManager } from "../Item/Interface"; import { ItemManager } from "../Item/ItemManager"; import { IPubSub } from "../PubSub/Interface"; @Injectable(IRaidManager) export class RaidManager implements FrontworkComponent, IRaidManager { name = "RaidManager" as "RaidManager"; @Inject(IAdmin) private admin: IAdmin @Inject(IUserManager) private userManager: IUserManager @Inject(ICharacterManager) private characterManager: ICharacterManager @Inject(ItemManager) private itemManager: IItemManager @Inject(IPubSub) private pubsub: IPubSub exportRPCs = () => [ this.getRaids, this.getRaidData, this.getPastRaids, this.getArchiveRaid ] exportRPCFeatures() { return [{ name: 'manageRaid' as 'manageRaid', exportRPCs: () => [ this.createRaid, this.addSignup, this.removeSignup, this.archiveRaid, this.setBenched, this.startRaid, this.adminUnsign ] }, { name: 'signup' as 'signup', exportRPCs: () => [ this.getSignups, this.sign, this.unsign ] }] } getTableDefinitions(): TableDefiniton[] { return [ { name: 'raids', tableBuilder: (table) => { table.increments('id').primary() table.dateTime('start').notNullable() table.string('description').notNullable() table.string('title').notNullable() table.integer('size').defaultTo(40) table.string('tier').defaultTo('null') } }, { name: 'archive', tableBuilder: (table) => { table.integer('id').primary() table.json('raiddata').notNullable() } }, { name: 'signups', tableBuilder: (table) => { table.increments('id').primary() table.unique(['raidid', 'characterid']) table.integer('raidid') table.foreign('raidid').references('id').inTable('raids').onDelete('CASCADE') table.integer('characterid') table.foreign('characterid').references('id').inTable('characters').onDelete('CASCADE') table.boolean('benched').defaultTo('false') table.boolean('late') } } ] } notifyRaid = async (raid:Raid | {id:number}) => { const data = await this.getRaidData(raid) this.pubsub.publish(""+raid.id, data) await this.notifyRaids() } notifyRaids = async () => { this.pubsub.publish('raids', undefined) } createRaid = async (raid: Raid): Promise => { const ids: number[] = await this.admin .knex('raids') .insert(raid) await this.notifyRaid({ id: ids[0] }) return await this.admin.knex('raids').where({ id: ids[0] }).first() } addSignup = async (signup: Signup) => { const ids: number[] = await this.admin .knex('signups') .insert(signup) return await this.admin.knex('signups').where({ id: ids[0] }).first() } removeSignup = async (signup: Signup) => await this.admin .knex('signups') .where({ raid_id: signup.raidid, character_id: signup.characterid }) .del() getRaids = async (): Promise => { const subQuery = this.admin .knex('signups') .count('*') .where({ raidid: this.admin.knex.ref('raids.id'), benched: false, late: false }) .as('signupcount') return await this.admin.knex('raids') .select('*', subQuery) .orderBy('start', 'asc') } startRaid = async (raid: Raid): Promise => { const archived = await this.archiveRaid(raid) const giveCurrency = async (b: Character) => { const usr = await this.characterManager.getUserOfCharacter(b) await this.userManager.incrementCurrency(usr, raid.tier, 1) } await Promise.all([ ...archived.participants.bench.map(giveCurrency), ...Object.values(archived.participants).flat().flatMap((b:Signup & Character & Spec) => giveCurrency(b)) ]) await this.notifyRaids() return archived } archiveRaid = async (raid: Raid): Promise => { const raidData = await this.getRaidData(raid) //const tx = await this.admin.knex.transaction() await this.admin.knex('archive') //.transacting(tx) .insert({ id: raidData.id, raiddata: JSON.stringify(raidData) }) await Promise.all( Object.values(raidData.participants).flat().flatMap((p: (Signup & Character & Spec)) => this.admin .knex(raid.tier + 'tokens') //.transacting(tx) .where({ characterid: p.characterid, signupid: null }) .del() )) await this.admin.knex('raids') //.transacting(tx) .where('id', '=', raid.id) .del() //await tx.commit() const row = await this.admin.knex('archive') .select('*') .where({ id: raidData.id, }) .first() return JSON.parse(row.raiddata) } getArchiveRaid = async (id: number): Promise => { const data = await this.admin.knex('archive').select('raiddata').where({ id: id }).first() return JSON.parse(data.raiddata) } getPastRaids = async (limit: number): Promise => { const raids = await this.admin.knex('archive') .select('*') .orderBy('id', 'desc') .limit(limit) return raids.map(raid => JSON.parse(raid.raiddata)) } getRaidData = async (raid: Raid): Promise => { const raiddata = { participants: { Druid: <(Signup & Character & Spec)[]>[], Hunter: <(Signup & Character & Spec)[]>[], Mage: <(Signup & Character & Spec)[]>[], Paladin: <(Signup & Character & Spec)[]>[], Priest: <(Signup & Character & Spec)[]>[], Rogue: <(Signup & Character & Spec)[]>[], Shaman: <(Signup & Character & Spec)[]>[], Warlock: <(Signup & Character & Spec)[]>[], Warrior: <(Signup & Character & Spec)[]>[], late: <(Signup & Character & Spec)[]>[], bench: <(Signup & Character & Spec)[]>[], }, tokens: {}, healers: <(Signup & Character & Spec)[]>[], tanks: <(Signup & Character & Spec)[]>[] } //const tx = await this.admin.knex.transaction() const subQuery = this.admin .knex('signups') .count('*') .where({ raidid: this.admin.knex.ref('raids.id'), benched: false, late: false }) .as('signupcount') const raidInDb: Raid = await this.admin.knex('raids') .select('*', subQuery) //.transacting(tx) .where('id', '=', raid.id) .first() const characterData: (Signup & Character & Spec)[] = await this.admin .knex('signups as s') //.transacting(tx) .select('s.id as id', 'charactername', 'class', 'specid', 'specname', 'race', 'userid', 'benched', 'late', 'raidid', 'characterid', 'specid') .join('raids as r', 's.raidid', '=', 'r.id') .join('characters as c', 's.characterid', '=', 'c.id') .join('users as u', 'c.userid', '=', 'u.id') .join('specs as sp', 'specid', '=', 'sp.id') .where('r.id', '=', raid.id) characterData.forEach(data => { if (data.benched) { raiddata.participants.bench.push(data) return } if (data.late) { raiddata.participants.late.push(data) return } raiddata.participants[data.class].push(data) }) const tokenData: (Character & SRToken & Item)[] = await this.admin .knex('signups as s') //.transacting(tx) .select('*', 's.id as id') .join('raids as r', 's.raidid', '=', 'r.id') .join('characters as c', 's.characterid', '=', 'c.id') .join(raidInDb.tier + 'tokens as t', 't.characterid', '=', 'c.id') .join('items as i', 'i.itemname', '=', 't.itemname') .where({ 'r.id': raid.id, }) .andWhere(function () { this.whereNotNull('t.signupid') }) //await tx.commit() tokenData.forEach(data => { if (!raiddata.tokens[data.itemname]) raiddata.tokens[data.itemname] = [] raiddata.tokens[data.itemname].push(data) }) raiddata.tanks = Object.values(raiddata.participants).flatMap( (tanks: any[]) => tanks.filter((p: any) => !p.benched && !p.late && (p.specname === "Protection" || p.specname === "Feral (Tank)")) ) raiddata.healers = Object.values(raiddata.participants).flatMap( (healers: any[]) => healers.filter((p: any) => !p.benched && !p.late && (p.specname === "Holy" || p.specname === "Discipline" || p.specname === "Restoration")) ) return { ...raidInDb, ...raiddata } } getSignups = async (raid: Raid): Promise<(Signup & Character & Spec & User)[]> => await this.admin .knex('signups as si') .join('characters as c', 'c.id', '=', 'characterid') .join('specs as s', 's.id', '=', 'specid') .join('users as u', 'u.id', '=', 'userid') .select('*', 'si.id as id') .where('raidid', '=', raid.id!) sign = async (usertoken: string, character: Character, raid: Raid, late: boolean) => { const maybeUserRecord = this.userManager.getUserRecordByToken(usertoken) if (!maybeUserRecord || maybeUserRecord.user.id != character.userid) { throw new Error("Bad Usertoken") } //const tx = await this.admin.knex.transaction() const exists = await this.admin .knex('signups') //.transacting(tx) .select('*') .where({ raidid: raid.id!, characterid: character.id!, }) .first() if (!exists) { await this.admin .knex('signups') //.transacting(tx) .insert({ raidid: raid.id!, characterid: character.id!, late: late, benched: false, }) } else { await this.admin .knex('signups') //.transacting(tx) .where({ id: exists.id }) .update({ raidid: raid.id!, characterid: character.id!, late: late, benched: false, }) } //await tx.commit() await this.notifyRaid(raid) return await this.admin .knex('signups') .select('*') .where({ raidid: raid.id!, characterid: character.id!, }) .first() } unsign = async (usertoken: string, character: Character, raid: Raid) => { const maybeUserRecord = this.userManager.getUserRecordByToken(usertoken) if (!maybeUserRecord || maybeUserRecord.user.id != character.userid) { throw new Error("Bad Usertoken") } return await this.adminUnsign(character, raid) } adminUnsign = async (character: Character, raid: Raid) => { const user = await this.characterManager.getUserOfCharacter(character) const signup = await this.admin.knex('signups as si') .where({ "si.raidid": raid.id!, "si.characterid": character.id!, }).first() const tokens = await this.itemManager.getTokens(character, [raid.tier], true) //check if token has to be deleted if (tokens) { Promise.all( tokens.map(async token => { await this.userManager.incrementCurrency(user, raid.tier, 1) const prio = await this.itemManager.calculatePriorities(token.itemname, character) if (token.level <= prio + 1) { await this.admin .knex(raid.tier + 'tokens') .where({ characterid: character.id, itemname: token.itemname }).del() } else { await this.admin .knex(raid.tier + 'tokens') .where({ characterid: character.id, itemname: token.itemname }).update({ signupid: null, level: token.level - 1 }) } }) ) } await this.admin.knex('signups') .where({ raidid: raid.id!, characterid: character.id!, }) .del() await this.notifyRaid(raid) } setBenched = async (signup: Signup): Promise => { await this.admin.knex('signups') .where({ raidid: signup.raidid, characterid: signup.characterid }) .update(signup) await this.notifyRaid({ id: signup.raidid }) } }