Giter Club home page Giter Club logo

magma's Introduction

Archived

This library has been superseded by koe, please check it out for your Discord audio needs. koe has multiple advantages over Magma:

  • not built around inherited convoluted existing APIs
  • less complicated, therefore easier to maintain and contribute to

My main take-away from this library is to stay far away from reactive libraries and paradigms as possible. The added complexity is not worth the downsides, especially when alternatives are possible to achieve similar performance goals.

Magma

Release Build Status Master Branch License SonarCloud

A voice only API for Discord, focused on delivering music at scale.

Lava? Magma?

Notable features:

  • Event based and non-blocking at its core

Not supported:

  • Audio Receiving

Magma is a heavily modified fork of JDA-Audio (Apache 2.0)

Big shout-out to the to the original authors and maintainers for doing great work!

Besides making use of most of the code for handling audio packets, Magma reuses some of JDAs APIs, namely:

  • IAudioSendSystem
  • IAudioSendFactory
  • IPacketProvider
  • AudioSendHandler

Magma does not ship any implementations for IAudioSendSystem and IAudioSendFactory. It is important that the implementation you choose will never call any method of the packet provider but IPacketProvider#getNextPacket, because none of the others are supported by Magma. Recommended implementations:

Only AudioSendHandlers that provide packets in the opus format are supported. Recommended providers:

Get started

Add Magma to your project

  • JitPack for builds straight from github code

Replace x.y.z in the snippets below with the desired version. Latest: Release

Gradle build.gradle
    repositories {
        maven { url 'https://jitpack.io' }
    }

    dependencies {
        compile group: 'space.npstr.magma', name: 'magma', version: 'x.y.z'
    }
Maven pom.xml
    <repositories>
        <repository>
            <id>jitpack.io</id>
            <url>https://jitpack.io</url>
        </repository>        
    </repositories>

    <dependency>
        <groupId>space.npstr.magma</groupId>
        <artifactId>magma</artifactId>
        <version>x.z.y</version>
    </dependency>

Sample code

Discord supports one single audio connection per user and guild (also called a "member"). This means, an audio connection is exactly identified by those two datapoints, and all of the methods of Magma require those to correctly identify the connection that you want to open/close/change something about.

DSL

Magma uses immutables.org to ensure type and parameter safety, both internally and in the Api you are going to use. Concretely, the Api makes use of immutable Member and ServerUpdate objects.

    Member member = MagmaMember.builder()
        .userId("...")
        .guildId("...")
        .build();
    
    ServerUpdate = MagmaServerUpdate.builder()
        .sessionId("...")
        .endpoint("...")
        .token("...")
        .build();
    

Api

Typical usage of the methods offered by the MagmaApi:

    IAudioSendFactory audioSendFactory = <your implementation here>;
    AudioSendHandler sendHandler = <your implementation here>;

    MagmaApi magmaApi = MagmaFactory.of(__ -> audioSendFactory);
    magmaApi.provideVoiceServerUpdate(member, serverUpdate);
    magmaApi.setSendHandler(member, sendHandler);


    // music plays, then later:

    magmaApi.setSendHandler(member, someOtherHandler);

    // other handler plays music / sounds


    // to clean up:

    magmaApi.removeSendHandler(member);
    magmaApi.closeConnection(member);

    // on shutting down

    magmaApi.shutdown();

    // Calling any other methods of a MagmaApi object after having called shutdown() 
    // will result in undefined behaviour. Do not do this, create a new MagmaApi instead.
    // Please note that you are strongly encouraged to use a single MagmaApi object throughout 
    // the lifecycle of your application.

None of those calls are blocking, as they are translated into events to be processed as soon as possible. Currently, there is no feedback as to when and how these are processed.

You can subscribe to a stream of MagmaEvents through MagmaApi#getEventStream:

    ...
        magmaApi.getEventStream()
                .subscribe(this::handleMagmaEvent);
    ...

    
    private void handleMagmaEvent(MagmaEvent magmaEvent) {
        if (magmaEvent instanceof space.npstr.magma.events.api.WebSocketClosed) {
            WebSocketClosed wsClosedEvent = (WebSocketClosed) magmaEvent;
            log.info("WS in guild {} closed with code {} and reason {}", wsClosedEvent.getMember().getGuildId(),
                    wsClosedEvent.getCloseCode(), wsClosedEvent.getReason());
        }
    }

Who is using this?

Check out these open-source projects for some more real world usage examples:

Alternatives

Magma might for various reasons not fit your needs. Don't worry, there are alternatives for sending voice to Discord:

Numbers

(last updated for 0.2.1)

Magma has been written with Lavalink in mind, numbers shown here compare vanilla Lavalink to a Magma-based branch.

Graphs by courtesy of FredBoat.

CPU Usage

Click me

CPU Usage

Threads

Click me

Threads

Garbage Collection

Click me

GC Time Spent
GC Runs

Memory

Click me

JVM Memory
Process Memory

Audio Frames

Click me

Audio Frames Lost

Debugging

Enabling TRACE/DEBUG logs can help with pinpointing and reporting issues. To get the most out of these log levels, make sure your chosen logger implementation and configuration prints additional MDC information. The MDC keys that Magma uses can be found in the MdcKeys class.

If you are running Magma in a Spring Boot application with logback as the logger implementation (Lavalink does this), adding these following lines to your Spring Application's application.yaml will show the MDC information:

logging:
  pattern:
    level: "[%mdc] %5p"
  level:
    space.npstr.magma: TRACE 

Dependencies:

magma's People

Contributors

almighty-alpaca avatar dv8fromtheworld avatar fabricio20 avatar freyacodes avatar minndevelopment avatar repulser avatar schnapster avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

magma's Issues

Give feedback about the shutdown

Calling MagmaApi.shutdown() returns immediately and provides no feedback about the shutdown status of Magma to the user. This leaves the user in an awkward position when shutting down. How long do they have to wait until all resources are truly released?

To solve this issue

  • Implement a way of letting the user know when Magma has fully shut down.
  • Document it.

Idempotent handling of VoiceServerUpdates

Explore the possibility of handling repeatedly received VoiceServerUpdates that have no actually new content (point to the same server and token).

Instead of tearing down the connection and creating a new one, it might be a good idea to just ignore such duplicate events.

This depends on whether we can be sure that we would not accidentally ignore a valid such event.

If a client wishes to recreate a connection, they would be required to close it first, before resending the VSU.

Reference:
https://discordapp.com/developers/docs/topics/gateway#voice-server-update

TLSv1.3 fails / java.nio.channels.ClosedChannelException: null

Currently, on most JVM distributions, TLSv1.3 negotiation with Cloudflare is broken.
As soon as the audio websocket tries to connect to discord voice servers, the following or similar exception is thrown:

20:26:37.147 [XNIO-1 I/O-1] DEBUG io.undertow.request - UT005013: An IOException occurred
java.nio.channels.ClosedChannelException: null
	at io.undertow.protocols.ssl.SslConduit.doWrap(SslConduit.java:876)
	at io.undertow.protocols.ssl.SslConduit.doHandshake(SslConduit.java:649)
	at io.undertow.protocols.ssl.SslConduit.access$900(SslConduit.java:63)
	at io.undertow.protocols.ssl.SslConduit$SslReadReadyHandler.readReady(SslConduit.java:1137)
	at org.xnio.nio.NioSocketConduit.handleReady(NioSocketConduit.java:88)
	at org.xnio.nio.WorkerThread.run(WorkerThread.java:561)
Exception in thread "main" java.lang.IllegalStateException: Failed to connect to wss://brazil0-test-proxy.discord.gg/
	at space.npstr.magma.impl.connections.hax.ClosingUndertowWebSocketClient$1.handleFailed(ClosingUndertowWebSocketClient.java:79)
	at org.xnio.IoFuture$HandlingNotifier.notify(IoFuture.java:215)
	at org.xnio.AbstractIoFuture$1.run(AbstractIoFuture.java:211)
	at org.xnio.IoUtils$2.execute(IoUtils.java:70)
	at org.xnio.AbstractIoFuture.runNotifier(AbstractIoFuture.java:354)
	at org.xnio.AbstractIoFuture.runAllNotifiers(AbstractIoFuture.java:233)
	at org.xnio.AbstractIoFuture.setException(AbstractIoFuture.java:251)
	at org.xnio.FutureResult.setException(FutureResult.java:89)
	at io.undertow.websockets.client.WebSocketClient$ConnectionBuilder$2.notify(WebSocketClient.java:342)
	at org.xnio.AbstractIoFuture$1.run(AbstractIoFuture.java:211)
	at org.xnio.IoUtils$2.execute(IoUtils.java:70)
	at org.xnio.AbstractIoFuture.runNotifier(AbstractIoFuture.java:354)
	at org.xnio.AbstractIoFuture.runAllNotifiers(AbstractIoFuture.java:233)
	at org.xnio.AbstractIoFuture.setException(AbstractIoFuture.java:251)
	at org.xnio.FutureResult.setException(FutureResult.java:89)
	at org.xnio.http.HttpUpgrade$HttpUpgradeState$StringWriteListener.handleEvent(HttpUpgrade.java:391)
	at org.xnio.http.HttpUpgrade$HttpUpgradeState$StringWriteListener.handleEvent(HttpUpgrade.java:372)
	at org.xnio.ChannelListeners.invokeChannelListener(ChannelListeners.java:92)
	at org.xnio.conduits.WriteReadyHandler$ChannelListenerHandler.writeReady(WriteReadyHandler.java:65)
	at io.undertow.protocols.ssl.SslConduit$SslWriteReadyHandler.writeReady(SslConduit.java:1273)
	at io.undertow.protocols.ssl.SslConduit$3.run(SslConduit.java:275)
	at org.xnio.nio.WorkerThread.safeRun(WorkerThread.java:582)
	at org.xnio.nio.WorkerThread.run(WorkerThread.java:466)
	Suppressed: java.lang.Exception: #block terminated with an error
		at reactor.core.publisher.BlockingSingleSubscriber.blockingGet(BlockingSingleSubscriber.java:93)
		at reactor.core.publisher.Mono.block(Mono.java:1494)
		at space.npstr.magma.Application.main(Application.java:59)
Caused by: java.nio.channels.ClosedChannelException
	at io.undertow.protocols.ssl.SslConduit.write(SslConduit.java:369)
	at org.xnio.conduits.ConduitStreamSinkChannel.write(ConduitStreamSinkChannel.java:150)
	at org.xnio.http.HttpUpgrade$HttpUpgradeState$StringWriteListener.handleEvent(HttpUpgrade.java:385)
	... 7 more

Until the root cause is found and fixed, a possible workaround is to specify TLSv1.2 in the SSL options as the preferred protocol. This is available since Magma 0.12.6

OptionMap sslOptions = OptionMap.create(
    Options.SSL_PROTOCOL, "TLSv1.2"
);
MagmaApi magmaApi = MagmaFactory.of(__ -> audioSendFactory, xnioOptions, sslOptions);

Create our own Sending Apis

Currently Magma is using JDA apis like

  • AudioSendHandler
  • IAudioSendFactory
  • IAudioSendSystem
  • IPacketProvider

However we are only using / supporting a fraction of the methods exposed by these.

Steps to resolve:

  • Create our own, simpler apis.
  • Ensure they are supported by our recommended NAS providers
  • Document the Apis (java docs, or something else)

Exception handling architecture

Currently very little thought has gone into how exceptions are thrown and handled. Reactive doesn't make this an easier task.

Subtasks:

  • Figure out where and how exactly exceptions can bubble up, when using project reactor
  • Replace RuntimeExceptions with meaningful ones
  • Document the resulting exception throwing and handling architecture

Notify the user of close events

The user of this library should be notified (in the form of an event) when an audio websocket is (unexpectedly?) closed, and with which close code. This is particularly useful when a voice server update is cached and later invalidated by Discord.

music error

3|start  | 2020-05-11 07:34:32.450  WARN 25700 --- [     parallel-2] s.n.m.impl.connections.AudioWebSocket    : Connection closed due to [4006 SESSION_NO_LONGER_VALID] Session is no longer valid.. This could indicate an issue with the magma library or your usage of it. Please get in touch. https://github.com/napstr/Magma/issues
3|start  | 2020-05-11 07:34:32.451  INFO 25700 --- [     parallel-2] s.n.m.impl.connections.AudioWebSocket    : Closing
``` please help me 

Session is no longer valid with Lavalink

I just updated Lavalink to the latest version in its releases tab and now I could see this three times within a short period:

Sentry page

Stack trace:

    at space.npstr.magma.connections.AudioWebSocket.handleWebSocketClosed(AudioWebSocket.java:263)
    at space.npstr.magma.connections.AudioWebSocket.hookOnNext(AudioWebSocket.java:169)
    at space.npstr.magma.connections.AudioWebSocket.hookOnNext(AudioWebSocket.java:74)
    at reactor.core.publisher.BaseSubscriber.onNext(BaseSubscriber.java:158)
    at reactor.core.publisher.FluxPublishOn$PublishOnSubscriber.runAsync(FluxPublishOn.java:398)
    at reactor.core.publisher.FluxPublishOn$PublishOnSubscriber.run(FluxPublishOn.java:484)
    at reactor.core.scheduler.WorkerTask.call(WorkerTask.java:84)
    at reactor.core.scheduler.WorkerTask.call(WorkerTask.java:37)

What can I do?

Impossible to play any song with Lavalink

Now, each time I try to play a song with Lavalink, I get this error :
Connection closed due to [4006 SESSION_NO_LONGER_VALID] Session is no longer valid.. This could indicate an issue with the magma library or your usage of it. Please get in touch. https://github.com/napstr/Magma/issues

So, here I am... I already tried updating Lavalink to the latest release, and the strange thing is that it was pefectly working not a long time ago.

How do I fix that ?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.