Skip to content

Commit

Permalink
feat: optimize and reduce memory footprint a bit
Browse files Browse the repository at this point in the history
* These are breaking changes
  • Loading branch information
Deivu committed Nov 7, 2023
1 parent 31ed644 commit e5ed32f
Show file tree
Hide file tree
Showing 8 changed files with 138 additions and 190 deletions.
41 changes: 23 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
![NPM](https://img.shields.io/npm/l/shoukaku?style=flat-square)

<p align="center">
<img src="https://azurlane.netojuu.com/images/6/69/Shoukaku.png">
<img src="https://safe.saya.moe/lhvaWz3iP67f.webp">
</p>

> The ShipGirl Project, feat Shoukaku; ⓒ Azur Lane
Expand All @@ -35,17 +35,21 @@ Refer to [/src/connectors](https://github.com/Deivu/Shoukaku/tree/master/src/con

### Installation

* Stable (3.x.x) | Needs Lavalink Versions: `"3.5.x" < "3.9.x" >`
* Shooukaku Version: Stable (3.x.x)

* Lavalink Version: `3.5.x < to 3.9.x >`

* Node Version: `> 16.0.0 `

> `npm install shoukaku`
* Dev (4.0.0-dev) | Needs Lavalink Versions: `"4.x.x <"`
* Version: Dev (4.0.0-dev)

> `npm install https://github.com/Deivu/Shoukaku.git`
* Needs Lavalink Versions: `4.x.x <`

> Lavalink v4 support is currently deployed on master branch, do `npm install https://github.com/Deivu/Shoukaku.git`
* Node Version: `> 18.0.0`

> Dev versions are not guaranteed to stay the same api wise, and even with last known stable, I won't say it's 100% stable
> `npm install https://github.com/shipgirlproject/Shoukaku.git`
### Documentation

Expand All @@ -64,7 +68,7 @@ const { Shoukaku, Connectors } = require('shoukaku');
const Nodes = [{
name: 'Localhost',
url: 'localhost:6969',
auth: 'marin_kitagawa'
auth: 're_aoharu'
}];
const client = new Client();
const shoukaku = new Shoukaku(new Connectors.DiscordJS(client), Nodes);
Expand Down Expand Up @@ -141,7 +145,7 @@ shoukaku.on('error', (_, error) => console.error(error));
client.login('token');
client.once('ready', async () => {
// get a node with least load to resolve a track
const node = shoukaku.getIdealNode();
const node = shoukaku.options.nodeResolver(shoukaku.nodes);
const result = await node.rest.resolve('scsearch:snowhalation');
if (!result?.tracks.length) return;
// we now have a track metadata, we can use this to play tracks
Expand Down Expand Up @@ -187,18 +191,19 @@ console.log(player.filters.volume)
```js
// new variable in shoukaku class, which handles the "connection data" of discord only
console.log(shoukaku.connections);
// getNode() is removed in favor of joinVoiceChannel custom get node function, example:
// players are moved from `node.players` to `shoukaku.players`
console.log(shoukaku.players);
// getNode() is removed in favor of joinVoiceChannel, you can still get the default least loaded node via `shoukaku.options.nodeResolver()`
const player = await shoukaku.joinVoiceChannel({
guildId: 'your_guild_id',
channelId: 'your_channel_id',
shardId: 0,
getNode: (nodes, connection) => {
nodes = [ ...nodes.values() ];
return nodes.find(node => node.group === connection.region);
}
});
// you can still get the least loaded node to resolve tracks via getIdealNode();
console.log(shoukaku.getIdealNode());
// you can supply a custom node resolver for your own way of getting an ideal node by supplying the nodeResolver option in Shoukaku options
const ShoukakuOptions = {
...yourShoukakuOptions,
nodeResolver: (nodes, connection) => getYourIdealNode(nodes, connection)
};
// and other changes I'm not able to document(?);
```
Expand All @@ -214,8 +219,8 @@ console.log(shoukaku.getIdealNode());
| moveOnDisconnect | boolean | Whether to move players to a different Lavalink node when a node disconnects |
| userAgent | string | User Agent to use when making requests to Lavalink |
| structures | Object{rest?, player?} | Custom structures for shoukaku to use |
| voiceConnectionTimeout | number | Timeout before abort connection **in seconds** |
| voiceConnectionTimeout | number | Timeout before abort connection **in seconds** |\
| nodeResolver | function(nodes, con) | Custom node resolver if you want to have your own method of getting the ideal node
### Plugins list
> Open a pr to add your plugin here
Expand All @@ -233,4 +238,4 @@ console.log(shoukaku.getIdealNode());
> [Kongou](https://github.com/Deivu/Kongou)
### Made with ❤ by
> @Sāya#0113
> @ichimakase
8 changes: 6 additions & 2 deletions src/Constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export enum Versions {
WEBSOCKET_VERSION = 4
}

export const ShoukakuDefaults: ShoukakuOptions = {
export const ShoukakuDefaults: Required<ShoukakuOptions> = {
resume: false,
resumeTimeout: 30,
resumeByLibrary: false,
Expand All @@ -39,7 +39,11 @@ export const ShoukakuDefaults: ShoukakuOptions = {
moveOnDisconnect: false,
userAgent: `${Info.name}bot/${Info.version} (${Info.repository.url})`,
structures: {},
voiceConnectionTimeout: 15
voiceConnectionTimeout: 15,
nodeResolver: (nodes) => [ ...nodes.values() ]
.filter(node => node.state === State.CONNECTED)
.sort((a, b) => a.penalties - b.penalties)
.shift()
};

export const NodeDefaults: NodeOption = {
Expand Down
87 changes: 29 additions & 58 deletions src/Shoukaku.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { EventEmitter } from 'events';
import { State, ShoukakuDefaults } from './Constants';
import { State, ShoukakuDefaults, VoiceState } from './Constants';
import { Node } from './node/Node';
import { Connector } from './connectors/Connector';
import { Constructor, mergeDefault } from './Utils';
Expand Down Expand Up @@ -82,6 +82,10 @@ export interface ShoukakuOptions {
* Timeout before abort connection
*/
voiceConnectionTimeout?: number;
/**
* Node Resolver to use if you want to customize it
*/
nodeResolver?: (node: Map<string, Node>, connection: Connection) => Node|undefined;
}

export interface VoiceChannelOptions {
Expand All @@ -90,20 +94,6 @@ export interface VoiceChannelOptions {
channelId: string;
deaf?: boolean;
mute?: boolean;
getNode?: (node: Map<string, Node>, connection: Connection) => Node|undefined;
}

export interface MergedShoukakuOptions {
resume: boolean;
resumeTimeout: number;
resumeByLibrary: boolean;
reconnectTries: number;
reconnectInterval: number;
restTimeout: number;
moveOnDisconnect: boolean;
userAgent: string;
structures: Structures;
voiceConnectionTimeout: number;
}

export declare interface Shoukaku {
Expand Down Expand Up @@ -136,7 +126,7 @@ export declare interface Shoukaku {
* Emitted when a websocket connection to Lavalink disconnects
* @eventProperty
*/
on(event: 'disconnect', listener: (name: string, moved: boolean, count: number) => void): this;
on(event: 'disconnect', listener: (name: string, count: number) => void): this;
/**
* Emitted when a raw message is received from Lavalink
* @eventProperty
Expand All @@ -147,14 +137,14 @@ export declare interface Shoukaku {
once(event: 'error', listener: (name: string, error: Error) => void): this;
once(event: 'ready', listener: (name: string, reconnected: boolean) => void): this;
once(event: 'close', listener: (name: string, code: number, reason: string) => void): this;
once(event: 'disconnect', listener: (name: string, moved: boolean, count: number) => void): this;
once(event: 'disconnect', listener: (name: string, count: number) => void): this;
once(event: 'raw', listener: (name: string, json: unknown) => void): this;
off(event: 'reconnecting', listener: (name: string, reconnectsLeft: number, reconnectInterval: number) => void): this;
off(event: 'debug', listener: (name: string, info: string) => void): this;
off(event: 'error', listener: (name: string, error: Error) => void): this;
off(event: 'ready', listener: (name: string, reconnected: boolean) => void): this;
off(event: 'close', listener: (name: string, code: number, reason: string) => void): this;
off(event: 'disconnect', listener: (name: string, moved: boolean, count: number) => void): this;
off(event: 'disconnect', listener: (name: string, count: number) => void): this;
off(event: 'raw', listener: (name: string, json: unknown) => void): this;
}

Expand All @@ -169,7 +159,7 @@ export class Shoukaku extends EventEmitter {
/**
* Shoukaku options
*/
public readonly options: MergedShoukakuOptions;
public readonly options: Required<ShoukakuOptions>;
/**
* Connected Lavalink nodes
*/
Expand All @@ -178,6 +168,10 @@ export class Shoukaku extends EventEmitter {
* Voice connections being handled
*/
public readonly connections: Map<string, Connection>;
/**
* Players being handled
*/
public readonly players: Map<string, Player>;
/**
* Shoukaku instance identifier
*/
Expand All @@ -195,30 +189,19 @@ export class Shoukaku extends EventEmitter {
* @param options.moveOnDisconnect Whether to move players to a different Lavalink node when a node disconnects
* @param options.userAgent User Agent to use when making requests to Lavalink
* @param options.structures Custom structures for shoukaku to use
* @param options.nodeResolver Used if you have custom lavalink node resolving
*/
constructor(connector: Connector, nodes: NodeOption[], options: ShoukakuOptions = {}) {
super();
this.connector = connector.set(this);
this.options = mergeDefault(ShoukakuDefaults, options);
this.nodes = new Map();
this.connections = new Map();
this.players = new Map();
this.id = null;
this.connector.listen(nodes);
}

/**
* Get a list of players
* @returns A map of guild IDs and players
* @readonly
*/
get players(): Map<string, Player> {
const players = new Map();
for (const node of this.nodes.values()) {
for (const [ id, player ] of node.players) players.set(id, player);
}
return players;
}

/**
* Add a Lavalink node to the pool of available nodes
* @param options.name Name of this node
Expand Down Expand Up @@ -258,14 +241,12 @@ export class Shoukaku extends EventEmitter {
* @param options.channelId ChannelId of the voice channel you want to connect to
* @param options.deaf Optional boolean value to specify whether to deafen or undeafen the current bot user
* @param options.mute Optional boolean value to specify whether to mute or unmute the current bot user
* @param options.getNode Optional getter function if you have custom node resolving
* @returns The created player
* @internal
*/
public async joinVoiceChannel(options: VoiceChannelOptions): Promise<Player> {
if (this.connections.has(options.guildId))
throw new Error('This guild already have an existing connection');
if (!options.getNode) options.getNode = this.getIdealNode.bind(this);
const connection = new Connection(this, options);
this.connections.set(connection.guildId, connection);
try {
Expand All @@ -275,21 +256,21 @@ export class Shoukaku extends EventEmitter {
throw error;
}
try {
const node = options.getNode(this.nodes, connection);
const node = this.options.nodeResolver(this.nodes, connection);
if (!node)
throw new Error('Can\'t find any nodes to connect on');
const player = this.options.structures.player ? new this.options.structures.player(node, connection) : new Player(node, connection);
node.players.set(player.guildId, player);
try {
await player.sendServerUpdate();
} catch (error) {
node.players.delete(options.guildId);
throw error;
}
connection.established = true;
const player = this.options.structures.player ? new this.options.structures.player(connection.guildId, node) : new Player(connection.guildId, node);
const onUpdate = (state: VoiceState) => {
if (state !== VoiceState.SESSION_READY) return;
player.sendServerUpdate(connection);
};
await player.sendServerUpdate(connection);
connection.on('connectionUpdate', onUpdate);
this.players.set(player.guildId, player);
return player;
} catch (error) {
connection.disconnect();
this.connections.delete(options.guildId);
throw error;
}
}
Expand All @@ -301,24 +282,14 @@ export class Shoukaku extends EventEmitter {
* @internal
*/
public async leaveVoiceChannel(guildId: string): Promise<Player|undefined> {
const connection = this.connections.get(guildId);
if (connection) connection.disconnect();
this.connections.get(guildId)?.disconnect();
this.connections.delete(guildId);
const player = this.players.get(guildId);
if (player) await player.destroyPlayer(true);
if (player) await player.destroy(true);
this.players.delete(guildId);
return player;
}

/**
* Gets the Lavalink node the least penalty score
* @returns A Lavalink node or undefined if there are no nodes ready
*/
public getIdealNode(): Node|undefined {
return [ ...this.nodes.values() ]
.filter(node => node.state === State.CONNECTED)
.sort((a, b) => a.penalties - b.penalties)
.shift();
}

/**
* Cleans the disconnected lavalink node
* @param node The node to clean
Expand Down
10 changes: 1 addition & 9 deletions src/connectors/Connector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,7 @@ export abstract class Connector {
const guildId = packet.d.guild_id;
const connection = this.manager!.connections.get(guildId);
if (!connection) return;
if (packet.t === 'VOICE_SERVER_UPDATE') {
connection.setServerUpdate(packet.d);
if (!connection.established) return;
const player = this.manager!.players.get(guildId);
if (!player) return;
player.sendServerUpdate()
.catch(error => this.manager!.on('error', error));
return;
}
if (packet.t === 'VOICE_SERVER_UPDATE') return connection.setServerUpdate(packet.d);
const userId = packet.d.user_id;
if (userId !== this.manager!.id) return;
connection.setStateUpdate(packet.d);
Expand Down
Loading

0 comments on commit e5ed32f

Please sign in to comment.