2020-12-16 10:00:13 +00:00
|
|
|
import { Collection } from '../utils/collection.ts'
|
2021-01-24 16:46:10 +00:00
|
|
|
import type { Client } from './client.ts'
|
2020-12-16 10:00:13 +00:00
|
|
|
import { RESTManager } from './rest.ts'
|
2021-01-20 10:05:15 +00:00
|
|
|
import { Gateway } from '../gateway/index.ts'
|
|
|
|
import { HarmonyEventEmitter } from '../utils/events.ts'
|
|
|
|
import { GatewayEvents } from '../types/gateway.ts'
|
2021-01-24 16:46:10 +00:00
|
|
|
import { delay } from '../utils/delay.ts'
|
2020-12-16 10:00:13 +00:00
|
|
|
|
2021-01-20 10:05:15 +00:00
|
|
|
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
|
|
|
|
export type ShardManagerEvents = {
|
|
|
|
launch: [number]
|
|
|
|
shardReady: [number]
|
|
|
|
shardDisconnect: [number, number | undefined, string | undefined]
|
|
|
|
shardError: [number, Error, ErrorEvent]
|
|
|
|
shardResume: [number]
|
2020-12-16 10:00:13 +00:00
|
|
|
}
|
|
|
|
|
2021-01-20 10:05:15 +00:00
|
|
|
export class ShardManager extends HarmonyEventEmitter<ShardManagerEvents> {
|
|
|
|
list: Collection<string, Gateway> = new Collection()
|
|
|
|
client: Client
|
|
|
|
cachedShardCount?: number
|
2021-01-24 16:46:10 +00:00
|
|
|
queueProcessing: boolean = false
|
|
|
|
queue: CallableFunction[] = []
|
2020-12-16 10:00:13 +00:00
|
|
|
|
|
|
|
get rest(): RESTManager {
|
2021-01-20 10:05:15 +00:00
|
|
|
return this.client.rest
|
2020-12-16 10:00:13 +00:00
|
|
|
}
|
|
|
|
|
2021-01-20 10:05:15 +00:00
|
|
|
constructor(client: Client) {
|
2020-12-16 10:00:13 +00:00
|
|
|
super()
|
2021-01-20 10:05:15 +00:00
|
|
|
this.client = client
|
|
|
|
}
|
2020-12-16 10:00:13 +00:00
|
|
|
|
2021-01-24 16:46:10 +00:00
|
|
|
debug(msg: string): void {
|
|
|
|
this.client.debug('Shards', msg)
|
|
|
|
}
|
|
|
|
|
|
|
|
enqueueIdentify(fn: CallableFunction): ShardManager {
|
|
|
|
this.queue.push(fn)
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
|
|
if (!this.queueProcessing) this.processQueue()
|
|
|
|
return this
|
|
|
|
}
|
|
|
|
|
|
|
|
private async processQueue(): Promise<void> {
|
|
|
|
if (this.queueProcessing || this.queue.length === 0) return
|
|
|
|
this.queueProcessing = true
|
|
|
|
const item = this.queue[0]
|
|
|
|
await item()
|
|
|
|
this.queue.shift()
|
|
|
|
await delay(5000)
|
|
|
|
this.queueProcessing = false
|
|
|
|
if (this.queue.length === 0) {
|
|
|
|
this.queueProcessing = false
|
|
|
|
} else {
|
|
|
|
await this.processQueue()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-20 10:05:15 +00:00
|
|
|
async getShardCount(): Promise<number> {
|
|
|
|
let shardCount: number
|
|
|
|
if (this.cachedShardCount !== undefined) shardCount = this.cachedShardCount
|
|
|
|
else {
|
|
|
|
if (this.client.shardCount === 'auto') {
|
|
|
|
const info = await this.client.rest.api.gateway.bot.get()
|
|
|
|
shardCount = info.shards as number
|
|
|
|
} else shardCount = this.client.shardCount ?? 1
|
|
|
|
}
|
|
|
|
this.cachedShardCount = shardCount
|
|
|
|
return this.cachedShardCount
|
2020-12-16 10:00:13 +00:00
|
|
|
}
|
|
|
|
|
2021-01-20 10:05:15 +00:00
|
|
|
/** Launches a new Shard */
|
|
|
|
async launch(id: number): Promise<ShardManager> {
|
|
|
|
if (this.list.has(id.toString()) === true)
|
|
|
|
throw new Error(`Shard ${id} already launched`)
|
2020-12-16 10:00:13 +00:00
|
|
|
|
2021-01-24 16:46:10 +00:00
|
|
|
this.debug(`Launching Shard: ${id}`)
|
2021-01-20 10:05:15 +00:00
|
|
|
const shardCount = await this.getShardCount()
|
2020-12-16 10:00:13 +00:00
|
|
|
|
2021-01-20 10:05:15 +00:00
|
|
|
const gw = new Gateway(this.client, [Number(id), shardCount])
|
|
|
|
this.list.set(id.toString(), gw)
|
2021-01-24 16:46:10 +00:00
|
|
|
|
2021-01-20 10:05:15 +00:00
|
|
|
gw.initWebsocket()
|
|
|
|
this.emit('launch', id)
|
2020-12-16 10:00:13 +00:00
|
|
|
|
2021-01-20 10:05:15 +00:00
|
|
|
gw.on(GatewayEvents.Ready, () => this.emit('shardReady', id))
|
|
|
|
gw.on('error', (err: Error, evt: ErrorEvent) =>
|
|
|
|
this.emit('shardError', id, err, evt)
|
|
|
|
)
|
|
|
|
gw.on(GatewayEvents.Resumed, () => this.emit('shardResume', id))
|
|
|
|
gw.on('close', (code: number, reason: string) =>
|
|
|
|
this.emit('shardDisconnect', id, code, reason)
|
|
|
|
)
|
|
|
|
|
|
|
|
return gw.waitFor(GatewayEvents.Ready, () => true).then(() => this)
|
|
|
|
}
|
|
|
|
|
2021-01-24 16:46:10 +00:00
|
|
|
/** Launches all Shards */
|
|
|
|
async connect(): Promise<ShardManager> {
|
2021-01-20 10:05:15 +00:00
|
|
|
const shardCount = await this.getShardCount()
|
2021-01-24 16:46:10 +00:00
|
|
|
this.client.shardCount = shardCount
|
|
|
|
this.debug(`Launching ${shardCount} shard${shardCount === 1 ? '' : 's'}...`)
|
|
|
|
const startTime = Date.now()
|
|
|
|
for (let i = 0; i < shardCount; i++) {
|
2021-01-20 10:05:15 +00:00
|
|
|
await this.launch(i)
|
|
|
|
}
|
2021-01-24 16:46:10 +00:00
|
|
|
const endTime = Date.now()
|
|
|
|
const diff = endTime - startTime
|
|
|
|
this.debug(
|
|
|
|
`Launched ${shardCount} shards! Time taken: ${Math.floor(diff / 1000)}s`
|
|
|
|
)
|
2021-01-20 10:05:15 +00:00
|
|
|
return this
|
|
|
|
}
|
2021-01-24 16:46:10 +00:00
|
|
|
|
|
|
|
get(id: number): Gateway | undefined {
|
|
|
|
return this.list.get(id.toString())
|
|
|
|
}
|
2020-12-16 10:00:13 +00:00
|
|
|
}
|