lavalink-client 2.10.2 → 2.10.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +290 -52
- package/dist/index.d.cts +36 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.js +290 -52
- package/dist/index.mjs +290 -52
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1204,6 +1204,11 @@ function safeStringify(obj, padding = 0) {
|
|
|
1204
1204
|
}
|
|
1205
1205
|
|
|
1206
1206
|
// src/structures/Node.ts
|
|
1207
|
+
var normalizeInt = (value, fallback, min, max) => {
|
|
1208
|
+
const parsed = typeof value === "number" ? value : Number(value);
|
|
1209
|
+
if (!Number.isFinite(parsed)) return fallback;
|
|
1210
|
+
return Math.min(max, Math.max(min, Math.floor(parsed)));
|
|
1211
|
+
};
|
|
1207
1212
|
var LavalinkNode = class _LavalinkNode {
|
|
1208
1213
|
heartBeatPingTimestamp = 0;
|
|
1209
1214
|
heartBeatPongTimestamp = 0;
|
|
@@ -1638,7 +1643,12 @@ var LavalinkNode = class _LavalinkNode {
|
|
|
1638
1643
|
{ headers }
|
|
1639
1644
|
);
|
|
1640
1645
|
this.socket.on("open", this.open.bind(this));
|
|
1641
|
-
this.socket.on(
|
|
1646
|
+
this.socket.on(
|
|
1647
|
+
"close",
|
|
1648
|
+
(code, reason) => void this.close(code, reason?.toString()).catch((error) => {
|
|
1649
|
+
this.NodeManager.emit("error", this, error);
|
|
1650
|
+
})
|
|
1651
|
+
);
|
|
1642
1652
|
this.socket.on("message", this.message.bind(this));
|
|
1643
1653
|
this.socket.on("error", this.error.bind(this));
|
|
1644
1654
|
}
|
|
@@ -1730,43 +1740,7 @@ var LavalinkNode = class _LavalinkNode {
|
|
|
1730
1740
|
)
|
|
1731
1741
|
);
|
|
1732
1742
|
}
|
|
1733
|
-
|
|
1734
|
-
(n) => n.connected && n.options.id !== this.id
|
|
1735
|
-
);
|
|
1736
|
-
if (!nodeToMove) {
|
|
1737
|
-
return Promise.allSettled(
|
|
1738
|
-
Array.from(players.values()).map(
|
|
1739
|
-
(player) => player.destroy("PlayerChangeNodeFailNoEligibleNode" /* PlayerChangeNodeFailNoEligibleNode */).catch((error) => {
|
|
1740
|
-
this._emitDebugEvent("PlayerChangeNodeFailNoEligibleNode" /* PlayerChangeNodeFailNoEligibleNode */, {
|
|
1741
|
-
state: "error",
|
|
1742
|
-
message: `Failed to destroy player ${player.guildId}: ${error.message}`,
|
|
1743
|
-
error,
|
|
1744
|
-
functionLayer: "Node > destroy() > movePlayers"
|
|
1745
|
-
});
|
|
1746
|
-
})
|
|
1747
|
-
)
|
|
1748
|
-
);
|
|
1749
|
-
}
|
|
1750
|
-
return Promise.allSettled(
|
|
1751
|
-
Array.from(players.values()).map(
|
|
1752
|
-
(player) => player.changeNode(nodeToMove.options.id).catch((error) => {
|
|
1753
|
-
this._emitDebugEvent("PlayerChangeNodeFail" /* PlayerChangeNodeFail */, {
|
|
1754
|
-
state: "error",
|
|
1755
|
-
message: `Failed to move player ${player.guildId}: ${error.message}`,
|
|
1756
|
-
error,
|
|
1757
|
-
functionLayer: "Node > destroy() > movePlayers"
|
|
1758
|
-
});
|
|
1759
|
-
return player.destroy(error.message ?? "PlayerChangeNodeFail" /* PlayerChangeNodeFail */).catch((destroyError) => {
|
|
1760
|
-
this._emitDebugEvent("PlayerDestroyFail" /* PlayerDestroyFail */, {
|
|
1761
|
-
state: "error",
|
|
1762
|
-
message: `Failed to destroy player ${player.guildId} after move failure: ${destroyError.message}`,
|
|
1763
|
-
error: destroyError,
|
|
1764
|
-
functionLayer: "Node > destroy() > movePlayers"
|
|
1765
|
-
});
|
|
1766
|
-
});
|
|
1767
|
-
})
|
|
1768
|
-
)
|
|
1769
|
-
);
|
|
1743
|
+
return this.movePlayersToReplacementNode(Array.from(players.values()), "destroy");
|
|
1770
1744
|
};
|
|
1771
1745
|
return void handlePlayerOperations().finally(() => {
|
|
1772
1746
|
this.socket?.close(1e3, "Node-Destroy");
|
|
@@ -1805,6 +1779,247 @@ var LavalinkNode = class _LavalinkNode {
|
|
|
1805
1779
|
this.resetReconnectionAttempts();
|
|
1806
1780
|
this.NodeManager.emit("disconnect", this, { code: 1e3, reason: disconnectReason });
|
|
1807
1781
|
}
|
|
1782
|
+
/**
|
|
1783
|
+
* Pick the best ready node to move players to.
|
|
1784
|
+
* A node must be connected and have a sessionId before a player can be migrated to it.
|
|
1785
|
+
*/
|
|
1786
|
+
getReadyMigrationNode(excludeNodeId) {
|
|
1787
|
+
return this.selectMigrationTarget(/* @__PURE__ */ new Set([excludeNodeId]), Number.MAX_SAFE_INTEGER);
|
|
1788
|
+
}
|
|
1789
|
+
getMigrationErrorMessage(error) {
|
|
1790
|
+
if (error instanceof Error) return error.message;
|
|
1791
|
+
if (typeof error === "string") return error;
|
|
1792
|
+
try {
|
|
1793
|
+
return safeStringify(error);
|
|
1794
|
+
} catch {
|
|
1795
|
+
return String(error);
|
|
1796
|
+
}
|
|
1797
|
+
}
|
|
1798
|
+
getMigrationConfig() {
|
|
1799
|
+
const migration = this._LManager.options?.advancedOptions?.playerMigration;
|
|
1800
|
+
return {
|
|
1801
|
+
concurrency: normalizeInt(migration?.concurrency, 5, 1, 32),
|
|
1802
|
+
perTargetConcurrency: normalizeInt(migration?.perTargetConcurrency, 2, 1, 8),
|
|
1803
|
+
backpressureDelayMs: normalizeInt(migration?.backpressureDelayMs, 50, 0, 1e3)
|
|
1804
|
+
};
|
|
1805
|
+
}
|
|
1806
|
+
getMigrationReservationCount(nodeId) {
|
|
1807
|
+
return this.NodeManager.playerMigration.reservations.get(nodeId) || 0;
|
|
1808
|
+
}
|
|
1809
|
+
reserveMigrationTarget(nodeId) {
|
|
1810
|
+
this.NodeManager.playerMigration.reservations.set(nodeId, this.getMigrationReservationCount(nodeId) + 1);
|
|
1811
|
+
}
|
|
1812
|
+
releaseMigrationTarget(nodeId) {
|
|
1813
|
+
const current = this.getMigrationReservationCount(nodeId);
|
|
1814
|
+
if (current <= 1) this.NodeManager.playerMigration.reservations.delete(nodeId);
|
|
1815
|
+
else this.NodeManager.playerMigration.reservations.set(nodeId, current - 1);
|
|
1816
|
+
this.NodeManager.signalPlayerMigrationCapacityChange();
|
|
1817
|
+
}
|
|
1818
|
+
getEffectiveMigrationLoad(node) {
|
|
1819
|
+
return (node.stats?.players || 0) + this.getMigrationReservationCount(node.id);
|
|
1820
|
+
}
|
|
1821
|
+
selectMigrationTarget(excludeNodeIds, perTargetConcurrency) {
|
|
1822
|
+
let selectedNode = null;
|
|
1823
|
+
let selectedEffectiveLoad = Number.POSITIVE_INFINITY;
|
|
1824
|
+
let selectedLiveLoad = Number.POSITIVE_INFINITY;
|
|
1825
|
+
for (const node of this.NodeManager.nodes.values()) {
|
|
1826
|
+
if (!node.connected || !node.sessionId) continue;
|
|
1827
|
+
if (excludeNodeIds.has(node.id)) continue;
|
|
1828
|
+
const reservationCount = this.getMigrationReservationCount(node.id);
|
|
1829
|
+
if (reservationCount >= perTargetConcurrency) continue;
|
|
1830
|
+
const liveLoad = node.stats?.players || 0;
|
|
1831
|
+
const effectiveLoad = this.getEffectiveMigrationLoad(node);
|
|
1832
|
+
if (!selectedNode || effectiveLoad < selectedEffectiveLoad || effectiveLoad === selectedEffectiveLoad && (liveLoad < selectedLiveLoad || liveLoad === selectedLiveLoad && node.id.localeCompare(selectedNode.id) < 0)) {
|
|
1833
|
+
selectedNode = node;
|
|
1834
|
+
selectedEffectiveLoad = effectiveLoad;
|
|
1835
|
+
selectedLiveLoad = liveLoad;
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
return selectedNode;
|
|
1839
|
+
}
|
|
1840
|
+
hasAnyHealthyMigrationNode(excludeNodeIds) {
|
|
1841
|
+
for (const node of this.NodeManager.nodes.values()) {
|
|
1842
|
+
if (!node.connected || !node.sessionId) continue;
|
|
1843
|
+
if (excludeNodeIds.has(node.id)) continue;
|
|
1844
|
+
return true;
|
|
1845
|
+
}
|
|
1846
|
+
return false;
|
|
1847
|
+
}
|
|
1848
|
+
isMigrationNodeHealthy(nodeId) {
|
|
1849
|
+
const node = this.NodeManager.nodes.get(nodeId);
|
|
1850
|
+
return !!node?.connected && !!node.sessionId;
|
|
1851
|
+
}
|
|
1852
|
+
async waitForMigrationCapacity(excludeNodeIds, perTargetConcurrency, timeoutMs) {
|
|
1853
|
+
if (this.selectMigrationTarget(excludeNodeIds, perTargetConcurrency)) return;
|
|
1854
|
+
if (timeoutMs <= 0) {
|
|
1855
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
1856
|
+
return;
|
|
1857
|
+
}
|
|
1858
|
+
await new Promise((resolve) => {
|
|
1859
|
+
let settled = false;
|
|
1860
|
+
let timer;
|
|
1861
|
+
const onSignal = () => {
|
|
1862
|
+
if (settled) return;
|
|
1863
|
+
settled = true;
|
|
1864
|
+
if (timer) clearTimeout(timer);
|
|
1865
|
+
this.NodeManager.playerMigration.capacityWaiters.delete(onSignal);
|
|
1866
|
+
resolve();
|
|
1867
|
+
};
|
|
1868
|
+
timer = setTimeout(() => {
|
|
1869
|
+
if (settled) return;
|
|
1870
|
+
settled = true;
|
|
1871
|
+
this.NodeManager.playerMigration.capacityWaiters.delete(onSignal);
|
|
1872
|
+
resolve();
|
|
1873
|
+
}, timeoutMs);
|
|
1874
|
+
this.NodeManager.playerMigration.capacityWaiters.add(onSignal);
|
|
1875
|
+
});
|
|
1876
|
+
}
|
|
1877
|
+
/**
|
|
1878
|
+
* Move a batch of players to a ready node with bounded concurrency.
|
|
1879
|
+
* This keeps migration responsive without flooding the replacement node with requests.
|
|
1880
|
+
*/
|
|
1881
|
+
async movePlayersToReplacementNode(players, onNoEligibleNode) {
|
|
1882
|
+
const migrationQueue = players.filter((player) => {
|
|
1883
|
+
if (player.getData("internal_nodeMigrating") === true) return false;
|
|
1884
|
+
if (player.getData("internal_destroystatus") === true) return false;
|
|
1885
|
+
player.setData("internal_nodeMigrating", true);
|
|
1886
|
+
return true;
|
|
1887
|
+
});
|
|
1888
|
+
const { concurrency, perTargetConcurrency, backpressureDelayMs } = this.getMigrationConfig();
|
|
1889
|
+
let nextQueueIndex = 0;
|
|
1890
|
+
const workerCount = Math.min(concurrency, migrationQueue.length);
|
|
1891
|
+
const migratePlayer = async (player) => {
|
|
1892
|
+
const excludedNodes = /* @__PURE__ */ new Set();
|
|
1893
|
+
try {
|
|
1894
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
1895
|
+
if (player.getData("internal_destroystatus") === true) return;
|
|
1896
|
+
if (player.node?.options?.id !== this.id) return;
|
|
1897
|
+
let targetNode = null;
|
|
1898
|
+
while (!(targetNode = this.selectMigrationTarget(excludedNodes, perTargetConcurrency))) {
|
|
1899
|
+
if (!this.hasAnyHealthyMigrationNode(excludedNodes)) {
|
|
1900
|
+
this._emitDebugEvent("PlayerChangeNodeFailNoEligibleNode" /* PlayerChangeNodeFailNoEligibleNode */, {
|
|
1901
|
+
state: "warn",
|
|
1902
|
+
message: `No eligible ready node was available for player ${player.guildId}`,
|
|
1903
|
+
functionLayer: "Node > movePlayersToReplacementNode()"
|
|
1904
|
+
});
|
|
1905
|
+
if (onNoEligibleNode === "destroy") {
|
|
1906
|
+
await player.destroy("PlayerChangeNodeFailNoEligibleNode" /* PlayerChangeNodeFailNoEligibleNode */).catch((destroyError) => {
|
|
1907
|
+
this._emitDebugEvent("PlayerDestroyFail" /* PlayerDestroyFail */, {
|
|
1908
|
+
state: "error",
|
|
1909
|
+
message: `Failed to destroy player ${player.guildId}: ${this.getMigrationErrorMessage(destroyError)}`,
|
|
1910
|
+
error: destroyError,
|
|
1911
|
+
functionLayer: "Node > movePlayersToReplacementNode()"
|
|
1912
|
+
});
|
|
1913
|
+
});
|
|
1914
|
+
} else {
|
|
1915
|
+
player.playing = false;
|
|
1916
|
+
}
|
|
1917
|
+
return;
|
|
1918
|
+
}
|
|
1919
|
+
await this.waitForMigrationCapacity(excludedNodes, perTargetConcurrency, backpressureDelayMs);
|
|
1920
|
+
}
|
|
1921
|
+
const targetNodeId = targetNode.id;
|
|
1922
|
+
if (player.getData("internal_destroystatus") === true) return;
|
|
1923
|
+
if (player.node?.options?.id !== this.id) return;
|
|
1924
|
+
this.reserveMigrationTarget(targetNodeId);
|
|
1925
|
+
try {
|
|
1926
|
+
await player.changeNode(targetNodeId);
|
|
1927
|
+
return;
|
|
1928
|
+
} catch (error) {
|
|
1929
|
+
const errorMessage = this.getMigrationErrorMessage(error);
|
|
1930
|
+
this._emitDebugEvent("PlayerChangeNodeFail" /* PlayerChangeNodeFail */, {
|
|
1931
|
+
state: "error",
|
|
1932
|
+
message: `Failed to move player ${player.guildId}: ${errorMessage}`,
|
|
1933
|
+
error,
|
|
1934
|
+
functionLayer: "Node > movePlayersToReplacementNode()"
|
|
1935
|
+
});
|
|
1936
|
+
const targetStillHealthy = this.isMigrationNodeHealthy(targetNodeId);
|
|
1937
|
+
excludedNodes.add(targetNodeId);
|
|
1938
|
+
if (attempt === 0 && !targetStillHealthy) {
|
|
1939
|
+
this._emitDebugEvent("PlayerChangeNodeFail" /* PlayerChangeNodeFail */, {
|
|
1940
|
+
state: "warn",
|
|
1941
|
+
message: `Migration target ${targetNodeId} became unavailable while moving player ${player.guildId}; retrying on another ready node`,
|
|
1942
|
+
error,
|
|
1943
|
+
functionLayer: "Node > movePlayersToReplacementNode()"
|
|
1944
|
+
});
|
|
1945
|
+
if (player.node?.options?.id === targetNodeId) player.node = this;
|
|
1946
|
+
continue;
|
|
1947
|
+
}
|
|
1948
|
+
if (attempt === 0 && this.hasAnyHealthyMigrationNode(excludedNodes)) {
|
|
1949
|
+
this._emitDebugEvent("PlayerChangeNodeFail" /* PlayerChangeNodeFail */, {
|
|
1950
|
+
state: "warn",
|
|
1951
|
+
message: `Retrying migration for player ${player.guildId} on a different ready node`,
|
|
1952
|
+
error,
|
|
1953
|
+
functionLayer: "Node > movePlayersToReplacementNode()"
|
|
1954
|
+
});
|
|
1955
|
+
if (player.node?.options?.id === targetNodeId) player.node = this;
|
|
1956
|
+
continue;
|
|
1957
|
+
}
|
|
1958
|
+
if (onNoEligibleNode === "destroy") {
|
|
1959
|
+
await player.destroy("PlayerChangeNodeFail" /* PlayerChangeNodeFail */).catch((destroyError) => {
|
|
1960
|
+
this._emitDebugEvent("PlayerDestroyFail" /* PlayerDestroyFail */, {
|
|
1961
|
+
state: "error",
|
|
1962
|
+
message: `Failed to destroy player ${player.guildId} after unexpected migration failure: ${this.getMigrationErrorMessage(destroyError)}`,
|
|
1963
|
+
error: destroyError,
|
|
1964
|
+
functionLayer: "Node > movePlayersToReplacementNode()"
|
|
1965
|
+
});
|
|
1966
|
+
});
|
|
1967
|
+
} else {
|
|
1968
|
+
player.playing = false;
|
|
1969
|
+
}
|
|
1970
|
+
return;
|
|
1971
|
+
} finally {
|
|
1972
|
+
this.releaseMigrationTarget(targetNodeId);
|
|
1973
|
+
}
|
|
1974
|
+
}
|
|
1975
|
+
} catch (error) {
|
|
1976
|
+
this._emitDebugEvent("PlayerChangeNodeFail" /* PlayerChangeNodeFail */, {
|
|
1977
|
+
state: "error",
|
|
1978
|
+
message: `Unexpected error while migrating player ${player.guildId}: ${this.getMigrationErrorMessage(error)}`,
|
|
1979
|
+
error,
|
|
1980
|
+
functionLayer: "Node > movePlayersToReplacementNode()"
|
|
1981
|
+
});
|
|
1982
|
+
if (onNoEligibleNode === "destroy") {
|
|
1983
|
+
await player.destroy("PlayerChangeNodeFail" /* PlayerChangeNodeFail */).catch((destroyError) => {
|
|
1984
|
+
this._emitDebugEvent("PlayerDestroyFail" /* PlayerDestroyFail */, {
|
|
1985
|
+
state: "error",
|
|
1986
|
+
message: `Failed to destroy player ${player.guildId} after unexpected migration failure: ${this.getMigrationErrorMessage(destroyError)}`,
|
|
1987
|
+
error: destroyError,
|
|
1988
|
+
functionLayer: "Node > movePlayersToReplacementNode()"
|
|
1989
|
+
});
|
|
1990
|
+
});
|
|
1991
|
+
} else {
|
|
1992
|
+
player.playing = false;
|
|
1993
|
+
}
|
|
1994
|
+
}
|
|
1995
|
+
};
|
|
1996
|
+
const workers = Array.from({ length: workerCount }, async () => {
|
|
1997
|
+
while (true) {
|
|
1998
|
+
const queueIndex = nextQueueIndex++;
|
|
1999
|
+
if (queueIndex >= migrationQueue.length) return;
|
|
2000
|
+
const player = migrationQueue[queueIndex];
|
|
2001
|
+
if (!player) continue;
|
|
2002
|
+
try {
|
|
2003
|
+
if (player.getData("internal_nodeMigrating") !== true) {
|
|
2004
|
+
player.setData("internal_nodeMigrating", true);
|
|
2005
|
+
}
|
|
2006
|
+
if (player.getData("internal_destroystatus") === true) continue;
|
|
2007
|
+
if (player.node?.options?.id !== this.id) continue;
|
|
2008
|
+
await migratePlayer(player);
|
|
2009
|
+
} catch (error) {
|
|
2010
|
+
this._emitDebugEvent("PlayerChangeNodeFail" /* PlayerChangeNodeFail */, {
|
|
2011
|
+
state: "error",
|
|
2012
|
+
message: `Unexpected worker error while migrating player ${player.guildId}: ${this.getMigrationErrorMessage(error)}`,
|
|
2013
|
+
error,
|
|
2014
|
+
functionLayer: "Node > movePlayersToReplacementNode()"
|
|
2015
|
+
});
|
|
2016
|
+
} finally {
|
|
2017
|
+
player.setData("internal_nodeMigrating", void 0);
|
|
2018
|
+
}
|
|
2019
|
+
}
|
|
2020
|
+
});
|
|
2021
|
+
await Promise.all(workers);
|
|
2022
|
+
}
|
|
1808
2023
|
/**
|
|
1809
2024
|
* Gets all Players of a Node
|
|
1810
2025
|
* @returns array of players inside of lavalink
|
|
@@ -2517,9 +2732,10 @@ var LavalinkNode = class _LavalinkNode {
|
|
|
2517
2732
|
}
|
|
2518
2733
|
this.info.isNodelink = !!this.info.isNodelink;
|
|
2519
2734
|
this.NodeManager.emit("connect", this);
|
|
2735
|
+
this.NodeManager.signalPlayerMigrationCapacityChange();
|
|
2520
2736
|
}
|
|
2521
2737
|
/** @private util function for handling closing events from websocket */
|
|
2522
|
-
close(code, reason) {
|
|
2738
|
+
async close(code, reason) {
|
|
2523
2739
|
this.resetAckTimeouts(true, true);
|
|
2524
2740
|
try {
|
|
2525
2741
|
if (this.socket) {
|
|
@@ -2538,19 +2754,22 @@ var LavalinkNode = class _LavalinkNode {
|
|
|
2538
2754
|
this.isAlive = false;
|
|
2539
2755
|
if (code === 1006 && !reason) reason = "Socket got terminated due to no ping connection";
|
|
2540
2756
|
if (code === 1e3 && reason === "Node-Disconnect") return;
|
|
2757
|
+
const players = Array.from(
|
|
2758
|
+
this._LManager.players.filter((p) => p?.node?.options?.id === this?.options?.id).values()
|
|
2759
|
+
);
|
|
2541
2760
|
this.NodeManager.emit("disconnect", this, { code, reason });
|
|
2542
2761
|
if (code !== 1e3 || reason !== "Node-Destroy") {
|
|
2543
2762
|
if (this.NodeManager.nodes.has(this.id)) {
|
|
2544
2763
|
this.reconnect();
|
|
2545
2764
|
}
|
|
2546
2765
|
}
|
|
2547
|
-
this._LManager.
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
}
|
|
2766
|
+
if (this._LManager.options.autoMove && players.length) {
|
|
2767
|
+
await this.movePlayersToReplacementNode(players, "freeze");
|
|
2768
|
+
} else if (!this._LManager.options.autoMove) {
|
|
2769
|
+
players.forEach((p) => p.playing = false);
|
|
2770
|
+
} else if (this.NodeManager.nodes.filter((n) => n.connected && !!n.sessionId).size === 0) {
|
|
2771
|
+
players.forEach((p) => p.playing = false);
|
|
2772
|
+
}
|
|
2554
2773
|
}
|
|
2555
2774
|
/** @private util function for handling error events from websocket */
|
|
2556
2775
|
error(error) {
|
|
@@ -3628,6 +3847,16 @@ var NodeManager = class extends EventEmitter {
|
|
|
3628
3847
|
* A map of all nodes in the nodeManager
|
|
3629
3848
|
*/
|
|
3630
3849
|
nodes = new MiniMap();
|
|
3850
|
+
/** @internal Coordinates player migrations between nodes owned by this manager. */
|
|
3851
|
+
playerMigration = {
|
|
3852
|
+
reservations: /* @__PURE__ */ new Map(),
|
|
3853
|
+
capacityWaiters: /* @__PURE__ */ new Set()
|
|
3854
|
+
};
|
|
3855
|
+
/** @internal Wakes migrations waiting for capacity on one of this manager's nodes. */
|
|
3856
|
+
signalPlayerMigrationCapacityChange() {
|
|
3857
|
+
for (const resolve of this.playerMigration.capacityWaiters) resolve();
|
|
3858
|
+
this.playerMigration.capacityWaiters.clear();
|
|
3859
|
+
}
|
|
3631
3860
|
/**
|
|
3632
3861
|
* @param LavalinkManager The LavalinkManager that created this NodeManager
|
|
3633
3862
|
*/
|
|
@@ -6007,8 +6236,10 @@ var Player = class {
|
|
|
6007
6236
|
* @returns
|
|
6008
6237
|
*/
|
|
6009
6238
|
async connect() {
|
|
6010
|
-
if (!this.options.voiceChannelId)
|
|
6011
|
-
|
|
6239
|
+
if (!this.options.voiceChannelId) {
|
|
6240
|
+
if (this.voiceChannelId) this.options.voiceChannelId = this.voiceChannelId;
|
|
6241
|
+
else throw new RangeError("No Voice Channel id has been set. (player.options.voiceChannelId)");
|
|
6242
|
+
}
|
|
6012
6243
|
await this.LavalinkManager.options.sendToShard(this.guildId, {
|
|
6013
6244
|
op: 4,
|
|
6014
6245
|
d: {
|
|
@@ -6045,8 +6276,10 @@ var Player = class {
|
|
|
6045
6276
|
* @returns
|
|
6046
6277
|
*/
|
|
6047
6278
|
async disconnect(force = false) {
|
|
6048
|
-
if (!force && !this.options.voiceChannelId)
|
|
6049
|
-
|
|
6279
|
+
if (!force && !this.options.voiceChannelId) {
|
|
6280
|
+
if (this.voiceChannelId) this.options.voiceChannelId = this.voiceChannelId;
|
|
6281
|
+
else throw new RangeError("No Voice Channel id has been set. (player.options.voiceChannelId)");
|
|
6282
|
+
}
|
|
6050
6283
|
await this.LavalinkManager.options.sendToShard(this.guildId, {
|
|
6051
6284
|
op: 4,
|
|
6052
6285
|
d: {
|
|
@@ -6275,8 +6508,8 @@ var Player = class {
|
|
|
6275
6508
|
try {
|
|
6276
6509
|
if (!node)
|
|
6277
6510
|
node = Array.from(this.LavalinkManager.nodeManager.leastUsedNodes("playingPlayers")).find(
|
|
6278
|
-
(n) => n.connected && n.options.id !== this.node.options.id
|
|
6279
|
-
)
|
|
6511
|
+
(n) => n.connected && !!n.sessionId && n.options.id !== this.node.options.id
|
|
6512
|
+
)?.id;
|
|
6280
6513
|
if (!node || !this.LavalinkManager.nodeManager.nodes.get(node))
|
|
6281
6514
|
throw new RangeError("No nodes are available.");
|
|
6282
6515
|
if (this.node.options.id === node) return this;
|
|
@@ -6431,6 +6664,11 @@ var LavalinkManager = class _LavalinkManager extends EventEmitter2 {
|
|
|
6431
6664
|
advancedOptions: {
|
|
6432
6665
|
enableDebugEvents: options?.advancedOptions?.enableDebugEvents ?? false,
|
|
6433
6666
|
maxFilterFixDuration: options?.advancedOptions?.maxFilterFixDuration ?? 6e5,
|
|
6667
|
+
playerMigration: {
|
|
6668
|
+
concurrency: options?.advancedOptions?.playerMigration?.concurrency ?? 5,
|
|
6669
|
+
perTargetConcurrency: options?.advancedOptions?.playerMigration?.perTargetConcurrency ?? 2,
|
|
6670
|
+
backpressureDelayMs: options?.advancedOptions?.playerMigration?.backpressureDelayMs ?? 50
|
|
6671
|
+
},
|
|
6434
6672
|
debugOptions: {
|
|
6435
6673
|
logCustomSearches: options?.advancedOptions?.debugOptions?.logCustomSearches ?? false,
|
|
6436
6674
|
noAudio: options?.advancedOptions?.debugOptions?.noAudio ?? false,
|