diff --git a/IMPLEMENTATION.md b/IMPLEMENTATION.md
index c0a85cee1..674c496a6 100644
--- a/IMPLEMENTATION.md
+++ b/IMPLEMENTATION.md
@@ -173,7 +173,7 @@ Server emitted an event. See the client implementation below.
```json
{
"op": "event",
- ...
+ "type": "..."
}
```
@@ -185,7 +185,7 @@ Server emitted an event. See the client implementation below.
* 2. TrackExceptionEvent
* 3. TrackStuckEvent
*
- * The remaining are caused by the client
+ * The remaining lavaplayer events are caused by client actions, and are therefore not forwarded via WS.
*/
private void handleEvent(JSONObject json) throws IOException {
LavalinkPlayer player = (LavalinkPlayer) lavalink.getPlayer(json.getString("guildId"));
@@ -221,6 +221,22 @@ private void handleEvent(JSONObject json) throws IOException {
See also: [AudioTrackEndReason.java](https://github.com/sedmelluq/lavaplayer/blob/master/main/src/main/java/com/sedmelluq/discord/lavaplayer/track/AudioTrackEndReason.java)
+Additionally there is also the `WebSocketClosedEvent`, which signals when an audio web socket (to Discord) is closed.
+This can happen for various reasons (normal and abnormal), e.g when using an expired voice server update.
+4xxx codes are usually bad.
+See the [Discord docs](https://discordapp.com/developers/docs/topics/opcodes-and-status-codes#voice-voice-close-event-codes).
+
+```json
+{
+ "op": "event",
+ "type": "WebSocketClosedEvent",
+ "guildId": "...",
+ "code": 4006,
+ "reason": "Your session is no longer valid.",
+ "byRemote": true
+}
+```
+
### REST API
The REST api is used to resolve audio tracks for use with the `play` op.
```
diff --git a/LavalinkServer/application.yml.example b/LavalinkServer/application.yml.example
index f475eeba7..f7152c23d 100644
--- a/LavalinkServer/application.yml.example
+++ b/LavalinkServer/application.yml.example
@@ -1,4 +1,4 @@
-server: # REST server
+server: # REST and WS server
port: 2333
address: 0.0.0.0
spring:
@@ -7,9 +7,6 @@ spring:
lavalink:
server:
password: "youshallnotpass"
- ws:
- port: 80
- host: 0.0.0.0
sources:
youtube: true
bandcamp: true
diff --git a/LavalinkServer/build.gradle b/LavalinkServer/build.gradle
index 8f99df7c5..dc1b296c6 100644
--- a/LavalinkServer/build.gradle
+++ b/LavalinkServer/build.gradle
@@ -36,16 +36,22 @@ test {
}
dependencies {
+ compile group: 'space.npstr', name: 'Magma', version: magmaVersion
compile group: 'com.sedmelluq', name: 'lavaplayer', version: lavaplayerVersion
- compile group: 'com.github.DV8FromTheWorld', name: 'JDA-Audio', version: jdaAudioVersion
- compile group: 'com.github.FredBoat.jda-nas', name: 'jda-nas', version: jdaNasVersion
+ compile group: 'com.sedmelluq', name: 'jda-nas', version: jdaNasVersion
+
compile group: 'com.github.shredder121', name: 'jda-async-packetprovider', version: jappVersion
- compile group: 'org.java-websocket', name: 'Java-WebSocket', version: javaWebSocketVersion
+ //required by japp
+ compile group: 'org.apache.commons', name: 'commons-lang3', version: commonsLangVersion
+ compile group: 'org.springframework', name: 'spring-websocket', version: springWebSocketVersion
compile group: 'ch.qos.logback', name: 'logback-classic', version: logbackVersion
compile group: 'io.sentry', name: 'sentry-logback', version: sentryLogbackVersion
compile group: 'com.github.oshi', name: 'oshi-core', version: oshiVersion
compile group: 'org.json', name: 'json', version: jsonOrgVersion
- compile group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: springBootVersion
+ compile(group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: springBootVersion) {
+ exclude group: 'org.springframework.boot', module: 'spring-boot-starter-tomcat'
+ }
+ compile group: 'org.springframework.boot', name: 'spring-boot-starter-undertow', version: springBootVersion
compileOnly group: 'com.github.spotbugs', name: 'spotbugs-annotations', version: spotbugsAnnotationsVersion
compile group: 'io.prometheus', name: 'simpleclient', version: prometheusVersion
diff --git a/LavalinkServer/src/main/java/lavalink/server/Launcher.java b/LavalinkServer/src/main/java/lavalink/server/Launcher.java
index f00ca914d..5b4ba9a6d 100644
--- a/LavalinkServer/src/main/java/lavalink/server/Launcher.java
+++ b/LavalinkServer/src/main/java/lavalink/server/Launcher.java
@@ -25,9 +25,6 @@
import com.sedmelluq.discord.lavaplayer.tools.PlayerLibrary;
import lavalink.server.info.AppInfo;
import lavalink.server.info.GitRepoState;
-import lavalink.server.io.SocketServer;
-import lavalink.server.util.SimpleLogToSLF4JAdapter;
-import net.dv8tion.jda.utils.SimpleLog;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
@@ -35,14 +32,12 @@
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.boot.context.event.ApplicationFailedEvent;
-import org.springframework.context.annotation.ComponentScan;
import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
@SpringBootApplication
-@ComponentScan
public class Launcher {
private static final Logger log = LoggerFactory.getLogger(Launcher.class);
@@ -76,20 +71,6 @@ public static void main(String[] args) {
sa.run(args);
}
- public Launcher(SocketServer socketServer) {
- Runtime.getRuntime().addShutdownHook(new Thread(() -> {
- log.info("Shutdown hook triggered");
- try {
- socketServer.stop(30);
- } catch (InterruptedException e) {
- log.warn("Interrupted while stopping socket server", e);
- }
- }, "shutdown hook"));
-
- SimpleLog.LEVEL = SimpleLog.Level.OFF;
- SimpleLog.addListener(new SimpleLogToSLF4JAdapter());
- }
-
private static String getVersionInfo() {
AppInfo appInfo = new AppInfo();
GitRepoState gitRepoState = new GitRepoState();
diff --git a/LavalinkServer/src/main/java/lavalink/server/config/WebsocketConfig.java b/LavalinkServer/src/main/java/lavalink/server/config/WebsocketConfig.java
index 838619006..105de19fd 100644
--- a/LavalinkServer/src/main/java/lavalink/server/config/WebsocketConfig.java
+++ b/LavalinkServer/src/main/java/lavalink/server/config/WebsocketConfig.java
@@ -1,31 +1,29 @@
package lavalink.server.config;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.stereotype.Component;
+import lavalink.server.io.HandshakeInterceptorImpl;
+import lavalink.server.io.SocketServer;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.socket.config.annotation.EnableWebSocket;
+import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
+import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
-/**
- * Created by napster on 05.03.18.
- */
-@ConfigurationProperties(prefix = "lavalink.server.ws")
-@Component
-public class WebsocketConfig {
+@Configuration
+@EnableWebSocket
+public class WebsocketConfig implements WebSocketConfigurer {
- private int port = 80;
- private String host = "0.0.0.0";
+ private final SocketServer server;
+ private final HandshakeInterceptorImpl handshakeInterceptor;
- public int getPort() {
- return port;
+ @Autowired
+ public WebsocketConfig(SocketServer server, HandshakeInterceptorImpl handshakeInterceptor) {
+ this.server = server;
+ this.handshakeInterceptor = handshakeInterceptor;
}
- public void setPort(int port) {
- this.port = port;
- }
-
- public String getHost() {
- return host;
- }
-
- public void setHost(String host) {
- this.host = host;
+ @Override
+ public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
+ registry.addHandler(server, "/")
+ .addInterceptors(handshakeInterceptor);
}
}
diff --git a/LavalinkServer/src/main/java/lavalink/server/info/AppInfo.java b/LavalinkServer/src/main/java/lavalink/server/info/AppInfo.java
index 0143e13de..1d61c257a 100644
--- a/LavalinkServer/src/main/java/lavalink/server/info/AppInfo.java
+++ b/LavalinkServer/src/main/java/lavalink/server/info/AppInfo.java
@@ -36,7 +36,11 @@ public AppInfo() {
this.groupId = prop.getProperty("groupId");
this.artifactId = prop.getProperty("artifactId");
this.buildNumber = prop.getProperty("buildNumber");
- this.buildTime = Long.parseLong(prop.getProperty("buildTime"));
+ long bTime = -1L;
+ try {
+ bTime = Long.parseLong(prop.getProperty("buildTime"));
+ } catch (NumberFormatException ignored) { }
+ this.buildTime = bTime;
}
public String getVersion() {
diff --git a/LavalinkServer/src/main/java/lavalink/server/info/GitRepoState.java b/LavalinkServer/src/main/java/lavalink/server/info/GitRepoState.java
index 0c1dcdf01..5753e4d9f 100644
--- a/LavalinkServer/src/main/java/lavalink/server/info/GitRepoState.java
+++ b/LavalinkServer/src/main/java/lavalink/server/info/GitRepoState.java
@@ -50,7 +50,7 @@ public GitRepoState() {
this.commitMessageFull = String.valueOf(properties.getOrDefault("git.commit.message.full", ""));
this.commitMessageShort = String.valueOf(properties.getOrDefault("git.commit.message.short", ""));
final String time = String.valueOf(properties.get("git.commit.time"));
- if (time == null) {
+ if (time == null || time.equals("null")) {
this.commitTime = 0;
} else {
// https://github.com/n0mer/gradle-git-properties/issues/71
diff --git a/LavalinkServer/src/main/java/lavalink/server/io/ConnectionManagerImpl.java b/LavalinkServer/src/main/java/lavalink/server/io/ConnectionManagerImpl.java
deleted file mode 100644
index 22c101672..000000000
--- a/LavalinkServer/src/main/java/lavalink/server/io/ConnectionManagerImpl.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package lavalink.server.io;
-
-import net.dv8tion.jda.manager.ConnectionManager;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Created by Repulser
- * https://github.com/Repulser
- */
-public class ConnectionManagerImpl implements ConnectionManager {
- private static final Logger log = LoggerFactory.getLogger(ConnectionManagerImpl.class);
-
- @Override
- public void removeAudioConnection(String guildId) {
- }
-
- @Override
- public void queueAudioConnect(String guildId, String channelId) {
- log.warn("queueAudioConnect was requested, this shouldn't happen, guildId:" + guildId + " channelId:" + channelId, new RuntimeException());
- }
-
-}
diff --git a/LavalinkServer/src/main/java/lavalink/server/io/CoreClientImpl.java b/LavalinkServer/src/main/java/lavalink/server/io/CoreClientImpl.java
deleted file mode 100644
index 1cb045e46..000000000
--- a/LavalinkServer/src/main/java/lavalink/server/io/CoreClientImpl.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (c) 2017 Frederik Ar. Mikkelsen & NoobLance
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-package lavalink.server.io;
-
-import net.dv8tion.jda.CoreClient;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class CoreClientImpl implements CoreClient {
-
- private static final Logger log = LoggerFactory.getLogger(CoreClientImpl.class);
-
- @Override
- public void sendWS(String message) {
- }
-
- @Override
- public boolean isConnected() {
- log.warn("isConnected was requested, this shouldn't happen", new RuntimeException());
- return true;
- }
-
- @Override
- public boolean inGuild(String guildId) {
- log.warn("inGuild was requested, this shouldn't happen, guildId:" + guildId, new RuntimeException());
- return true;
- }
-
- @Override
- public boolean voiceChannelExists(String guildId, String channelId) {
- log.warn("voiceChannelExists was requested, this shouldn't happen, guildId:" + guildId + " channelId:" + channelId, new RuntimeException());
- return true;
- }
-
- @Override
- public boolean hasPermissionInChannel(String guildId, String channelId, long l) {
- log.warn("hasPermissionInChannel was requested, this shouldn't happen, guildId:" + guildId + " channelId:" + channelId + " l:" + l, new RuntimeException());
- return true;
- }
-
-}
diff --git a/LavalinkServer/src/main/java/lavalink/server/io/HandshakeInterceptorImpl.java b/LavalinkServer/src/main/java/lavalink/server/io/HandshakeInterceptorImpl.java
new file mode 100644
index 000000000..aa0b80341
--- /dev/null
+++ b/LavalinkServer/src/main/java/lavalink/server/io/HandshakeInterceptorImpl.java
@@ -0,0 +1,53 @@
+package lavalink.server.io;
+
+import lavalink.server.config.ServerConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.server.ServerHttpRequest;
+import org.springframework.http.server.ServerHttpResponse;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.socket.WebSocketHandler;
+import org.springframework.web.socket.server.HandshakeInterceptor;
+
+import java.util.Map;
+import java.util.Objects;
+
+@Controller
+public class HandshakeInterceptorImpl implements HandshakeInterceptor {
+
+ private static final Logger log = LoggerFactory.getLogger(HandshakeInterceptorImpl.class);
+ private final ServerConfig serverConfig;
+
+ @Autowired
+ public HandshakeInterceptorImpl(ServerConfig serverConfig) {
+ this.serverConfig = serverConfig;
+ }
+
+ /**
+ * Checks credentials and sets the Lavalink version header
+ *
+ * @return true if authenticated
+ */
+ @Override
+ public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
+ Map attributes) {
+ response.getHeaders().add("Lavalink-Major-Version", "3");
+
+ String password = request.getHeaders().getFirst("Authorization");
+ boolean matches = Objects.equals(password, serverConfig.getPassword());
+
+ if (matches) {
+ log.info("Incoming connection from " + request.getRemoteAddress());
+ } else {
+ log.error("Authentication failed from " + request.getRemoteAddress());
+ }
+
+ return matches;
+ }
+
+ // No action required
+ @Override
+ public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
+ Exception exception) {}
+}
diff --git a/LavalinkServer/src/main/java/lavalink/server/io/SocketContext.java b/LavalinkServer/src/main/java/lavalink/server/io/SocketContext.java
index da276ef14..bfa2a3b43 100644
--- a/LavalinkServer/src/main/java/lavalink/server/io/SocketContext.java
+++ b/LavalinkServer/src/main/java/lavalink/server/io/SocketContext.java
@@ -22,22 +22,19 @@
package lavalink.server.io;
-import com.github.shredder121.asyncaudio.jdaaudio.AsyncPacketProviderFactory;
-import com.sedmelluq.discord.lavaplayer.jdaudp.NativeAudioSendFactory;
import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager;
-import lavalink.server.config.AudioSendFactoryConfiguration;
-import lavalink.server.config.ServerConfig;
import lavalink.server.player.Player;
-import lavalink.server.util.Util;
-import net.dv8tion.jda.Core;
-import net.dv8tion.jda.audio.factory.IAudioSendFactory;
-import net.dv8tion.jda.manager.AudioManager;
-import net.dv8tion.jda.manager.ConnectionManagerBuilder;
-import org.java_websocket.WebSocket;
+import lavalink.server.util.Ws;
+import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.web.socket.WebSocketSession;
+import space.npstr.magma.MagmaApi;
+import space.npstr.magma.MagmaMember;
+import space.npstr.magma.Member;
+import space.npstr.magma.events.api.MagmaEvent;
+import space.npstr.magma.events.api.WebSocketClosed;
-import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -52,26 +49,21 @@ public class SocketContext {
private static final Logger log = LoggerFactory.getLogger(SocketContext.class);
private final AudioPlayerManager audioPlayerManager;
- private final ServerConfig serverConfig;
- private final WebSocket socket;
- private final AudioSendFactoryConfiguration audioSendFactoryConfiguration;
+ private final WebSocketSession socket;
private String userId;
- private int shardCount;
- private final Map cores = new HashMap<>();
+ private final MagmaApi magmaApi;
+ //guildId <-> Player
private final Map players = new ConcurrentHashMap<>();
private ScheduledExecutorService statsExecutor;
public final ScheduledExecutorService playerUpdateService;
- private final ConcurrentHashMap sendFactories = new ConcurrentHashMap<>();
- SocketContext(Supplier audioPlayerManagerSupplier, ServerConfig serverConfig, WebSocket socket,
- AudioSendFactoryConfiguration audioSendFactoryConfiguration, SocketServer socketServer,
- String userId, int shardCount) {
+ SocketContext(Supplier audioPlayerManagerSupplier, WebSocketSession socket,
+ SocketServer socketServer, String userId) {
this.audioPlayerManager = audioPlayerManagerSupplier.get();
- this.serverConfig = serverConfig;
this.socket = socket;
- this.audioSendFactoryConfiguration = audioSendFactoryConfiguration;
this.userId = userId;
- this.shardCount = shardCount;
+ this.magmaApi = MagmaApi.of(socketServer::getAudioSendFactory);
+ magmaApi.getEventStream().subscribe(this::handleMagmaEvent);
statsExecutor = Executors.newSingleThreadScheduledExecutor();
statsExecutor.scheduleAtFixedRate(new StatsTask(this, socketServer), 0, 1, TimeUnit.MINUTES);
@@ -84,15 +76,8 @@ public class SocketContext {
});
}
- Core getCore(int shardId) {
- return cores.computeIfAbsent(shardId,
- __ -> {
- if (audioSendFactoryConfiguration.isNasSupported())
- return new Core(userId, new CoreClientImpl(), core -> new ConnectionManagerImpl(), getAudioSendFactory(shardId));
- else
- return new Core(userId, new CoreClientImpl(), (ConnectionManagerBuilder) core -> new ConnectionManagerImpl());
- }
- );
+ public String getUserId() {
+ return userId;
}
Player getPlayer(String guildId) {
@@ -101,11 +86,7 @@ Player getPlayer(String guildId) {
);
}
- int getShardCount() {
- return shardCount;
- }
-
- public WebSocket getSocket() {
+ public WebSocketSession getSession() {
return socket;
}
@@ -121,37 +102,41 @@ List getPlayingPlayers() {
return newList;
}
+ MagmaApi getMagma() {
+ return magmaApi;
+ }
+
+ private void handleMagmaEvent(MagmaEvent magmaEvent) {
+ if (magmaEvent instanceof WebSocketClosed) {
+ WebSocketClosed event = (WebSocketClosed) magmaEvent;
+ JSONObject out = new JSONObject();
+ out.put("op", "event");
+ out.put("type", "WebSocketClosedEvent");
+ out.put("guildId", event.getMember().getGuildId());
+ out.put("reason", event.getReason());
+ out.put("code", event.getCloseCode());
+ out.put("byRemote", event.isByRemote());
+
+ Ws.send(socket, out);
+ }
+ }
+
void shutdown() {
- log.info("Shutting down " + cores.size() + " cores and " + getPlayingPlayers().size() + " playing players.");
+ log.info("Shutting down " + getPlayingPlayers().size() + " playing players.");
statsExecutor.shutdown();
audioPlayerManager.shutdown();
playerUpdateService.shutdown();
- players.keySet().forEach(s -> {
- Core core = cores.get(Util.getShardFromSnowflake(s, shardCount));
- if (core != null) {
- AudioManager audioManager = core.getAudioManager(s);
- if (audioManager != null) {
- audioManager.closeAudioConnection();
- }
- }
+ players.keySet().forEach(guildId -> {
+ Member member = MagmaMember.builder()
+ .userId(userId)
+ .guildId(guildId)
+ .build();
+ magmaApi.removeSendHandler(member);
+ magmaApi.closeConnection(member);
});
players.values().forEach(Player::stop);
- }
-
- private IAudioSendFactory getAudioSendFactory(int shardId) {
- return sendFactories.computeIfAbsent(shardId % audioSendFactoryConfiguration.getAudioSendFactoryCount(),
- integer -> {
- Integer customBuffer = serverConfig.getBufferDurationMs();
- NativeAudioSendFactory nativeAudioSendFactory;
- if (customBuffer != null) {
- nativeAudioSendFactory = new NativeAudioSendFactory(customBuffer);
- } else {
- nativeAudioSendFactory = new NativeAudioSendFactory();
- }
-
- return AsyncPacketProviderFactory.adapt(nativeAudioSendFactory);
- });
+ magmaApi.shutdown();
}
public AudioPlayerManager getAudioPlayerManager() {
diff --git a/LavalinkServer/src/main/java/lavalink/server/io/SocketServer.java b/LavalinkServer/src/main/java/lavalink/server/io/SocketServer.java
index 8cd7578b3..460657232 100644
--- a/LavalinkServer/src/main/java/lavalink/server/io/SocketServer.java
+++ b/LavalinkServer/src/main/java/lavalink/server/io/SocketServer.java
@@ -22,142 +22,127 @@
package lavalink.server.io;
+import com.github.shredder121.asyncaudio.jda.AsyncPacketProviderFactory;
+import com.sedmelluq.discord.lavaplayer.jdaudp.NativeAudioSendFactory;
import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager;
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
import com.sedmelluq.discord.lavaplayer.track.TrackMarker;
import lavalink.server.config.AudioSendFactoryConfiguration;
import lavalink.server.config.ServerConfig;
-import lavalink.server.config.WebsocketConfig;
import lavalink.server.player.Player;
import lavalink.server.player.TrackEndMarkerHandler;
import lavalink.server.util.Util;
-import net.dv8tion.jda.Core;
-import net.dv8tion.jda.manager.AudioManager;
-import org.java_websocket.WebSocket;
-import org.java_websocket.drafts.Draft;
-import org.java_websocket.exceptions.InvalidDataException;
-import org.java_websocket.handshake.ClientHandshake;
-import org.java_websocket.handshake.ServerHandshakeBuilder;
-import org.java_websocket.server.WebSocketServer;
+import lavalink.server.util.Ws;
+import net.dv8tion.jda.core.audio.factory.IAudioSendFactory;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.springframework.stereotype.Component;
+import org.springframework.stereotype.Service;
+import org.springframework.web.socket.CloseStatus;
+import org.springframework.web.socket.TextMessage;
+import org.springframework.web.socket.WebSocketSession;
+import org.springframework.web.socket.handler.TextWebSocketHandler;
+import space.npstr.magma.*;
-import javax.annotation.PostConstruct;
import java.io.IOException;
-import java.net.InetSocketAddress;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
-import static lavalink.server.io.WSCodes.AUTHORIZATION_REJECTED;
-import static lavalink.server.io.WSCodes.INTERNAL_ERROR;
-
-@Component
-public class SocketServer extends WebSocketServer {
+@Service
+public class SocketServer extends TextWebSocketHandler {
private static final Logger log = LoggerFactory.getLogger(SocketServer.class);
- private final Map contextMap = new HashMap<>();
+ // userId <-> shardCount
+ private final Map shardCounts = new ConcurrentHashMap<>();
+ private final Map contextMap = new HashMap<>();
private final ServerConfig serverConfig;
private final Supplier audioPlayerManagerSupplier;
private final AudioSendFactoryConfiguration audioSendFactoryConfiguration;
+ private final ConcurrentHashMap sendFactories = new ConcurrentHashMap<>();
- public SocketServer(WebsocketConfig websocketConfig, ServerConfig serverConfig, Supplier audioPlayerManagerSupplier,
+ public SocketServer(ServerConfig serverConfig, Supplier audioPlayerManagerSupplier,
AudioSendFactoryConfiguration audioSendFactoryConfiguration) {
- super(new InetSocketAddress(websocketConfig.getHost(), websocketConfig.getPort()));
- this.setReuseAddr(true);
this.serverConfig = serverConfig;
this.audioPlayerManagerSupplier = audioPlayerManagerSupplier;
this.audioSendFactoryConfiguration = audioSendFactoryConfiguration;
}
+ @SuppressWarnings("ConstantConditions")
@Override
- @PostConstruct
- public void start() {
- super.start();
- }
+ public void afterConnectionEstablished(WebSocketSession session) {
+ int shardCount = Integer.parseInt(session.getHandshakeHeaders().getFirst("Num-Shards"));
+ String userId = session.getHandshakeHeaders().getFirst("User-Id");
- @Override
- public ServerHandshakeBuilder onWebsocketHandshakeReceivedAsServer(WebSocket conn, Draft draft, ClientHandshake request) throws InvalidDataException {
- ServerHandshakeBuilder builder = super.onWebsocketHandshakeReceivedAsServer(conn, draft, request);
- builder.put("Lavalink-Major-Version", "3");
- return builder;
- }
+ shardCounts.put(userId, shardCount);
- @Override
- public void onOpen(WebSocket webSocket, ClientHandshake clientHandshake) {
- try {
- int shardCount = Integer.parseInt(clientHandshake.getFieldValue("Num-Shards"));
- String userId = clientHandshake.getFieldValue("User-Id");
-
- if (clientHandshake.getFieldValue("Authorization").equals(serverConfig.getPassword())) {
- log.info("Connection opened from " + webSocket.getRemoteSocketAddress() + " with protocol " + webSocket.getDraft());
- contextMap.put(webSocket, new SocketContext(audioPlayerManagerSupplier, serverConfig, webSocket,
- audioSendFactoryConfiguration, this, userId, shardCount));
- } else {
- log.error("Authentication failed from " + webSocket.getRemoteSocketAddress() + " with protocol " + webSocket.getDraft());
- webSocket.close(AUTHORIZATION_REJECTED, "Authorization rejected");
- }
- } catch (Exception e) {
- log.error("Error when opening websocket", e);
- webSocket.close(INTERNAL_ERROR, e.getMessage());
- }
+ contextMap.put(session.getId(), new SocketContext(audioPlayerManagerSupplier, session, this, userId));
+ log.info("Connection successfully established from " + session.getRemoteAddress());
}
@Override
- public void onCloseInitiated(WebSocket webSocket, int code, String reason) {
- close(webSocket, code, reason);
- }
-
- @Override
- public void onClosing(WebSocket webSocket, int code, String reason, boolean remote) {
- close(webSocket, code, reason);
- }
-
- @Override
- public void onClose(WebSocket webSocket, int code, String reason, boolean remote) {
- close(webSocket, code, reason);
- }
-
- // WebSocketServer has a very questionable attitude towards communicating close events, so we override ALL the closing methods
- private void close(WebSocket webSocket, int code, String reason) {
- SocketContext context = contextMap.remove(webSocket);
+ public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
+ SocketContext context = contextMap.remove(session.getId());
if (context != null) {
- log.info("Connection closed from {} with protocol {} with reason {} with code {}",
- webSocket.getRemoteSocketAddress().toString(), webSocket.getDraft(), reason, code);
+ log.info("Connection closed from {} -- {}", session.getRemoteAddress(), status);
context.shutdown();
}
}
@Override
- public void onMessage(WebSocket webSocket, String s) {
- JSONObject json = new JSONObject(s);
+ protected void handleTextMessage(WebSocketSession session, TextMessage message) {
+ try {
+ handleTextMessageSafe(session, message);
+ } catch (Exception e) {
+ log.error("Exception while handling websocket message", e);
+ }
+ }
+
+ private void handleTextMessageSafe(WebSocketSession session, TextMessage message) {
+ JSONObject json = new JSONObject(message.getPayload());
- log.info(s);
+ log.info(message.getPayload());
- if (webSocket.isClosing()) {
- log.error("Ignoring closing websocket: " + webSocket.getRemoteSocketAddress().toString());
+ if (!session.isOpen()) {
+ log.error("Ignoring closing websocket: " + session.getRemoteAddress());
return;
}
switch (json.getString("op")) {
/* JDAA ops */
case "voiceUpdate":
- Core core = contextMap.get(webSocket).getCore(getShardId(webSocket, json));
- core.provideVoiceServerUpdate(
- json.getString("sessionId"),
- json.getJSONObject("event")
- );
- core.getAudioManager(json.getJSONObject("event").getString("guild_id")).setAutoReconnect(false);
+ String sessionId = json.getString("sessionId");
+ String guildId = json.getString("guildId");
+
+ JSONObject event = json.getJSONObject("event");
+ String endpoint = event.optString("endpoint");
+ String token = event.getString("token");
+
+ //discord sometimes send a partial server update missing the endpoint, which can be ignored.
+ if (endpoint == null || endpoint.isEmpty()) {
+ return;
+ }
+
+ SocketContext sktContext = contextMap.get(session.getId());
+ Member member = MagmaMember.builder()
+ .userId(sktContext.getUserId())
+ .guildId(guildId)
+ .build();
+ ServerUpdate serverUpdate = MagmaServerUpdate.builder()
+ .sessionId(sessionId)
+ .endpoint(endpoint)
+ .token(token)
+ .build();
+ sktContext.getMagma().provideVoiceServerUpdate(member, serverUpdate);
break;
/* Player ops */
case "play":
try {
- SocketContext ctx = contextMap.get(webSocket);
+ SocketContext ctx = contextMap.get(session.getId());
Player player = ctx.getPlayer(json.getString("guildId"));
AudioTrack track = Util.toAudioTrack(ctx.getAudioPlayerManager(), json.getString("track"));
if (json.has("startTime")) {
@@ -174,41 +159,47 @@ public void onMessage(WebSocket webSocket, String s) {
player.play(track);
- SocketContext context = contextMap.get(webSocket);
+ SocketContext context = contextMap.get(session.getId());
+
+ Member m = MagmaMember.builder()
+ .userId(context.getUserId())
+ .guildId(json.getString("guildId"))
+ .build();
+ context.getMagma().setSendHandler(m, context.getPlayer(json.getString("guildId")));
- context.getCore(getShardId(webSocket, json)).getAudioManager(json.getString("guildId"))
- .setSendingHandler(context.getPlayer(json.getString("guildId")));
- sendPlayerUpdate(webSocket, player);
+ sendPlayerUpdate(session, player);
} catch (IOException e) {
throw new RuntimeException(e);
}
break;
case "stop":
- Player player = contextMap.get(webSocket).getPlayer(json.getString("guildId"));
+ Player player = contextMap.get(session.getId()).getPlayer(json.getString("guildId"));
player.stop();
break;
case "pause":
- Player player2 = contextMap.get(webSocket).getPlayer(json.getString("guildId"));
+ Player player2 = contextMap.get(session.getId()).getPlayer(json.getString("guildId"));
player2.setPause(json.getBoolean("pause"));
- sendPlayerUpdate(webSocket, player2);
+ sendPlayerUpdate(session, player2);
break;
case "seek":
- Player player3 = contextMap.get(webSocket).getPlayer(json.getString("guildId"));
+ Player player3 = contextMap.get(session.getId()).getPlayer(json.getString("guildId"));
player3.seekTo(json.getLong("position"));
- sendPlayerUpdate(webSocket, player3);
+ sendPlayerUpdate(session, player3);
break;
case "volume":
- Player player4 = contextMap.get(webSocket).getPlayer(json.getString("guildId"));
+ Player player4 = contextMap.get(session.getId()).getPlayer(json.getString("guildId"));
player4.setVolume(json.getInt("volume"));
break;
case "destroy":
- Player player5 = contextMap.get(webSocket).getPlayers().remove(json.getString("guildId"));
+ SocketContext socketContext = contextMap.get(session.getId());
+ Player player5 = socketContext.getPlayers().remove(json.getString("guildId"));
if (player5 != null) player5.stop();
- AudioManager audioManager = contextMap.get(webSocket)
- .getCore(getShardId(webSocket, json))
- .getAudioManager(json.getString("guildId"));
- audioManager.setSendingHandler(null);
- audioManager.closeAudioConnection();
+ Member mem = MagmaMember.builder()
+ .userId(socketContext.getUserId())
+ .guildId(json.getString("guildId"))
+ .build();
+ socketContext.getMagma().removeSendHandler(mem);
+ socketContext.getMagma().closeConnection(mem);
break;
default:
log.warn("Unexpected operation: " + json.getString("op"));
@@ -216,32 +207,34 @@ public void onMessage(WebSocket webSocket, String s) {
}
}
- @Override
- public void onError(WebSocket webSocket, Exception e) {
- log.error("Caught exception in websocket", e);
- }
-
- @Override
- public void onStart() {
- log.info("Started WS server with port " + getPort());
- }
-
- public static void sendPlayerUpdate(WebSocket webSocket, Player player) {
+ public static void sendPlayerUpdate(WebSocketSession session, Player player) {
JSONObject json = new JSONObject();
json.put("op", "playerUpdate");
json.put("guildId", player.getGuildId());
json.put("state", player.getState());
- webSocket.send(json.toString());
- }
-
- //Shorthand method
- private int getShardId(WebSocket webSocket, JSONObject json) {
- return Util.getShardFromSnowflake(json.getString("guildId"), contextMap.get(webSocket).getShardCount());
+ Ws.send(session, json);
}
Collection getContexts() {
return contextMap.values();
}
+ IAudioSendFactory getAudioSendFactory(Member member) {
+ int shardCount = shardCounts.getOrDefault(member.getUserId(), 1);
+ int shardId = Util.getShardFromSnowflake(member.getGuildId(), shardCount);
+
+ return sendFactories.computeIfAbsent(shardId % audioSendFactoryConfiguration.getAudioSendFactoryCount(),
+ integer -> {
+ Integer customBuffer = serverConfig.getBufferDurationMs();
+ NativeAudioSendFactory nativeAudioSendFactory;
+ if (customBuffer != null) {
+ nativeAudioSendFactory = new NativeAudioSendFactory(customBuffer);
+ } else {
+ nativeAudioSendFactory = new NativeAudioSendFactory();
+ }
+
+ return AsyncPacketProviderFactory.adapt(nativeAudioSendFactory);
+ });
+ }
}
diff --git a/LavalinkServer/src/main/java/lavalink/server/io/StatsTask.java b/LavalinkServer/src/main/java/lavalink/server/io/StatsTask.java
index 3f97f3cec..d29f489f4 100644
--- a/LavalinkServer/src/main/java/lavalink/server/io/StatsTask.java
+++ b/LavalinkServer/src/main/java/lavalink/server/io/StatsTask.java
@@ -25,6 +25,7 @@
import lavalink.server.Launcher;
import lavalink.server.player.AudioLossCounter;
import lavalink.server.player.Player;
+import lavalink.server.util.Ws;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -33,6 +34,8 @@
import oshi.software.os.OSProcess;
import oshi.software.os.OperatingSystem;
+import java.io.IOException;
+
public class StatsTask implements Runnable {
private static final Logger log = LoggerFactory.getLogger(StatsTask.class);
@@ -56,7 +59,7 @@ public void run() {
}
}
- public void sendStats() {
+ private void sendStats() throws IOException {
JSONObject out = new JSONObject();
final int[] playersTotal = {0};
@@ -116,14 +119,14 @@ public void sendStats() {
out.put("frameStats", frames);
}
- context.getSocket().send(out.toString());
+ Ws.send(context.getSession(), out);
}
private double uptime = 0;
private double cpuTime = 0;
private double getProcessRecentCpuUsage() {
- double output = 0d;
+ double output;
HardwareAbstractionLayer hal = si.getHardware();
OperatingSystem os = si.getOperatingSystem();
OSProcess p = os.getProcess(os.getProcessId());
diff --git a/LavalinkServer/src/main/java/lavalink/server/player/EventEmitter.java b/LavalinkServer/src/main/java/lavalink/server/player/EventEmitter.java
index 336bf59c0..6ac253202 100644
--- a/LavalinkServer/src/main/java/lavalink/server/player/EventEmitter.java
+++ b/LavalinkServer/src/main/java/lavalink/server/player/EventEmitter.java
@@ -30,6 +30,7 @@
import com.sedmelluq.discord.lavaplayer.track.AudioTrackEndReason;
import lavalink.server.io.SocketServer;
import lavalink.server.util.Util;
+import lavalink.server.util.Ws;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -61,7 +62,7 @@ public void onTrackEnd(AudioPlayer player, AudioTrack track, AudioTrackEndReason
out.put("reason", endReason.toString());
- linkPlayer.getSocket().getSocket().send(out.toString());
+ Ws.sendIfOpen(linkPlayer.getSocket().getSession(), out);
}
// These exceptions are already logged by Lavaplayer
@@ -79,7 +80,7 @@ public void onTrackException(AudioPlayer player, AudioTrack track, FriendlyExcep
out.put("error", exception.getMessage());
- linkPlayer.getSocket().getSocket().send(out.toString());
+ Ws.sendIfOpen(linkPlayer.getSocket().getSession(), out);
}
@Override
@@ -98,8 +99,8 @@ public void onTrackStuck(AudioPlayer player, AudioTrack track, long thresholdMs)
out.put("thresholdMs", thresholdMs);
- linkPlayer.getSocket().getSocket().send(out.toString());
- SocketServer.sendPlayerUpdate(linkPlayer.getSocket().getSocket(), linkPlayer);
+ Ws.sendIfOpen(linkPlayer.getSocket().getSession(), out);
+ SocketServer.sendPlayerUpdate(linkPlayer.getSocket().getSession(), linkPlayer);
}
}
diff --git a/LavalinkServer/src/main/java/lavalink/server/player/Player.java b/LavalinkServer/src/main/java/lavalink/server/player/Player.java
index 67af5313c..3c7201745 100644
--- a/LavalinkServer/src/main/java/lavalink/server/player/Player.java
+++ b/LavalinkServer/src/main/java/lavalink/server/player/Player.java
@@ -30,7 +30,7 @@
import com.sedmelluq.discord.lavaplayer.track.playback.AudioFrame;
import lavalink.server.io.SocketContext;
import lavalink.server.io.SocketServer;
-import net.dv8tion.jda.audio.AudioSendHandler;
+import net.dv8tion.jda.core.audio.AudioSendHandler;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -75,7 +75,11 @@ public String getGuildId() {
}
public void seekTo(long position) {
- player.getPlayingTrack().setPosition(position);
+ AudioTrack track = player.getPlayingTrack();
+
+ if (track == null) throw new RuntimeException("Can't seek when not playing anything");
+
+ track.setPosition(position);
}
public void setVolume(int volume) {
@@ -136,7 +140,7 @@ public void onTrackEnd(AudioPlayer player, AudioTrack track, AudioTrackEndReason
public void onTrackStart(AudioPlayer player, AudioTrack track) {
if (myFuture == null || myFuture.isCancelled()) {
myFuture = socketContext.playerUpdateService.scheduleAtFixedRate(() -> {
- SocketServer.sendPlayerUpdate(socketContext.getSocket(), this);
+ SocketServer.sendPlayerUpdate(socketContext.getSession(), this);
}, 0, 5, TimeUnit.SECONDS);
}
}
diff --git a/LavalinkServer/src/main/java/lavalink/server/util/DebugConnectionListener.java b/LavalinkServer/src/main/java/lavalink/server/util/DebugConnectionListener.java
deleted file mode 100644
index 2b5b10959..000000000
--- a/LavalinkServer/src/main/java/lavalink/server/util/DebugConnectionListener.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (c) 2017 Frederik Ar. Mikkelsen & NoobLance
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-package lavalink.server.util;
-
-
-import net.dv8tion.jda.audio.hooks.ConnectionListener;
-import net.dv8tion.jda.audio.hooks.ConnectionStatus;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * 65,42% copy pasta from the DebugConnectionListener in FredBoat
- */
-public class DebugConnectionListener implements ConnectionListener {
-
- private static final Logger log = LoggerFactory.getLogger(DebugConnectionListener.class);
-
- private ConnectionStatus oldStatus = null;
- private final long guildId;
-
- public DebugConnectionListener(long guildId) {
- this.guildId = guildId;
- }
-
- @Override
- public void onPing(long l) {
-
- }
-
- @Override
- public void onStatusChange(ConnectionStatus connectionStatus) {
- log.debug("Status change for audio connection in guild {} : {} => {}",
- guildId, oldStatus, connectionStatus);
-
- oldStatus = connectionStatus;
- }
-
- @Override
- public void onUserSpeaking(String s, boolean b) {
-
- }
-}
diff --git a/LavalinkServer/src/main/java/lavalink/server/util/SimpleLogToSLF4JAdapter.java b/LavalinkServer/src/main/java/lavalink/server/util/SimpleLogToSLF4JAdapter.java
deleted file mode 100644
index 8ddc27f34..000000000
--- a/LavalinkServer/src/main/java/lavalink/server/util/SimpleLogToSLF4JAdapter.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (c) 2017 Frederik Ar. Mikkelsen & NoobLance
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-package lavalink.server.util;
-
-import net.dv8tion.jda.utils.SimpleLog;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * @author sedmelluq
- */
-public class SimpleLogToSLF4JAdapter implements SimpleLog.LogListener {
- private static final Logger log = LoggerFactory.getLogger("JDAA");
-
- @Override
- public void onLog(SimpleLog simpleLog, SimpleLog.Level logLevel, Object message) {
- if (message == null) {
- message = "null";
- }
-
- switch (logLevel) {
- case TRACE:
- if (log.isTraceEnabled()) {
- log.trace(message.toString());
- }
- break;
- case DEBUG:
- if (log.isDebugEnabled()) {
- log.debug(message.toString());
- }
- break;
- case INFO:
- log.info(message.toString());
- break;
- case WARNING:
- log.warn(message.toString());
- break;
- case FATAL:
- log.error(message.toString());
- break;
- }
- }
-
- @Override
- public void onError(SimpleLog simpleLog, Throwable err) {
- log.error("An exception occurred", err);
- }
-}
diff --git a/LavalinkServer/src/main/java/lavalink/server/util/Ws.java b/LavalinkServer/src/main/java/lavalink/server/util/Ws.java
new file mode 100644
index 000000000..bede83749
--- /dev/null
+++ b/LavalinkServer/src/main/java/lavalink/server/util/Ws.java
@@ -0,0 +1,40 @@
+package lavalink.server.util;
+
+import io.undertow.websockets.core.WebSocketCallback;
+import io.undertow.websockets.core.WebSocketChannel;
+import io.undertow.websockets.core.WebSockets;
+import io.undertow.websockets.jsr.UndertowSession;
+import org.json.JSONObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.socket.WebSocketSession;
+import org.springframework.web.socket.adapter.standard.StandardWebSocketSession;
+
+public class Ws {
+
+ private static final Logger log = LoggerFactory.getLogger(Ws.class);
+
+ /**
+ * Like #send(), but without logging an exception if the socket is closed.
+ */
+ public static void sendIfOpen(WebSocketSession session, JSONObject json) {
+ if (session.isOpen()) send(session, json);
+ }
+
+ public static void send(WebSocketSession session, JSONObject json) {
+ UndertowSession undertowSession = (UndertowSession) ((StandardWebSocketSession) session).getNativeSession();
+ WebSockets.sendText(json.toString(), undertowSession.getWebSocketChannel(),
+ new WebSocketCallback<>() {
+ @Override
+ public void complete(WebSocketChannel channel, Void context) {
+ log.trace("Sent {}", json);
+ }
+
+ @Override
+ public void onError(WebSocketChannel channel, Void context, Throwable throwable) {
+ log.error("Error", throwable);
+ }
+ });
+ }
+
+}
diff --git a/build.gradle b/build.gradle
index bc53b316b..223af44e8 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,7 +1,7 @@
buildscript {
ext {
- springBootVersion = '2.0.2.RELEASE'
- gradleGitVersion = '1.5.1'
+ springBootVersion = '2.0.4.RELEASE'
+ gradleGitVersion = '1.5.2'
sonarqubeVersion = '2.6.2'
}
repositories {
@@ -21,7 +21,7 @@ allprojects {
mavenCentral() // main maven repo
jcenter() // JDA and some other stuff
mavenLocal() // useful for developing
- maven { url "https://jitpack.io" } // build projects directly from github
+ maven { url "https://jitpack.io" } // build projects directly from github
}
group = 'lavalink'
@@ -47,18 +47,19 @@ subprojects {
//@formatter:off
lavaplayerVersion = '1.3.7'
- jdaAudioVersion = '4d7abb48aec49f0a996ba0d87df34fdc67f71275'
- jdaNasVersion = '1.0.6.2-JDA-Audio'
- jappVersion = '1.2'
+ magmaVersion = '0.6.1'
+ jdaNasVersion = '1.0.6'
+ jappVersion = '1.3'
springBootVersion = "${springBootVersion}"
- javaWebSocketVersion = '1.3.8'
+ springWebSocketVersion = '5.0.8.RELEASE'
logbackVersion = '1.2.3'
- sentryLogbackVersion = '1.7.0'
- oshiVersion = '3.5.0'
- jsonOrgVersion = '20180130'
- spotbugsAnnotationsVersion = '3.1.3'
- prometheusVersion = '0.4.0'
+ sentryLogbackVersion = '1.7.7'
+ oshiVersion = '3.8.1'
+ jsonOrgVersion = '20180813'
+ spotbugsAnnotationsVersion = '3.1.6'
+ prometheusVersion = '0.5.0'
+ commonsLangVersion = '3.8'
//@formatter:on
}
@@ -69,7 +70,7 @@ ext {
}
task wrapper(type: Wrapper) {
- gradleVersion = '4.7'
+ gradleVersion = '4.10.2'
//noinspection UnnecessaryQualifiedReference
distributionType = Wrapper.DistributionType.ALL
}
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index 91ca28c8b..29953ea14 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index e6a30918e..d76b502e2 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-4.7-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists