2. How to use the Geometry Dash client

JDash provides a simple HTTP client to make requests to Geometry Dash servers. This section will show a basic example of its usage.

The client builder

The role of a client is to send requests to a server and return the response. The client can be either anonymous or authenticated. When it's authenticated, it may have access to protected features in the API. JDash supports both types of clients.

The class GDClientBuilder is what will allow you to configure your client. To create a new instance, use the create() method:

GDClientBuilder builder = GDClientBuilder.create();

Before building the client, you can configure some options. Currently there are four available. First, you can set a custom host for Geometry Dash servers. This will allow you to use JDash for Geometry Dash private servers (GDPS):

builder.withHost("www.my-gdps.com/database");

You can also choose the time to live of requests in cache. When the client makes a request and receives a response, the client will put the response in a cache so that it doesn't have to make a new request in order to give the response for a request that's already been made before. However there should be a limit on how long the responses should stay in cache, because they may change over time and you may want to get the most up-to-date version of them. You can configure this by doing:

builder.withCacheTtl(Duration.ofMinutes(5));

The default value is 15 minutes.

Finally, you can configure a timeout in milliseconds for requests. Handy to cancel requests if they take too long to complete:

builder.withRequestTimeout(Duration.ofSeconds(30));

By default, no timeout are set for requests.

Now you are ready to build the client. To build an anonymous client:

AnonymousGDClient client=builder.buildAnonymous();

To build an authenticated client, it's different because you need to login first. Login credentials are given through a Credentials object:

Credentials credentials = new Credentials("username", "password");

And it is supplied to the buildAuthenticated method which will perform the login request and emit the AuthenticatedGDClient

// blocking, performs the login request right away, so you need to check if login succeeded with a try/catch block
AuthenticatedGDClient client;
        try{
        client=builder.buildAuthenticated(credentials).block();
        }catch(GDLoginFailedException e){
        // Handle login failed
        }

// non-blocking, will perform the login request only when the client will be used for the first time
        Mono<AuthenticatedGDClient> clientMono=builder.buildAuthenticated(credentials)
        .doOnError(GDLoginFailedException.class,e-> /* Handle login failed */)
        .cache();

The builder supports method chaining, meaning you can do everything in one expression if you prefer:

AnonymousGDClient client=GDClientBuilder.create()
        .withHost("www.my-gdps.com/database")
        .withCacheTtl(100_000)
        .withRequestTimeout(Duration.ofSeconds(30));
        .buildAnonymous();

Sending a simple request with an anonymous client

Let's say you want to get data for the level Bloodbath by Riot. To get data for a specific online level, use the getLevelById(long) method. It takes the level ID you want to fetch as argument. If you look at what the method returns, it's a Mono<GDLevel>. A Mono is a wrapper for objects that are not immediately available. Indeed, fetching data from Geometry Dash servers takes time, and might even fail. So you have several options. The most common way is to use block() in order to make your program wait until it has received the response, and use try/catch blocks to handle errors:

// The ID of the desired online level
final long bloodbathLevelID=10565740;
// The anonymous client
        AnonymousGDClient client=GDClientBuilder.create().buildAnonymous();
        try{
        GDLevel level=client.getLevelById(bloodbathLevelID).block();
        System.out.println(level);
        } catch(GDClientException e){
        // Handle errors
        }

If you run this code in a main class, you will get something like this in the console (if the request was successful)

GDLevel [name=Bloodbath, creatorID=503085, creatorName=Riot, description=Whose blood will be spilt in the Bloodbath? Who will the victors be? How many will survive? Good luck..., difficulty=INSANE, demonDifficulty=EXTREME, stars=10, featuredScore=10330, isEpic=false, downloads=13617738, likes=1229901, length=XL, coinCount=0, hasCoinsVerified=false, levelVersion=1, gameVersion=19, objectCount=0, isDemon=true, isAuto=false, originalLevelID=7679228, requestedStars=0]

Authenticated HTTP client

An authenticated client inherits all methods from an anomymous client. With an authenticated client, you can for example view your private messages inbox. This is done through the method getPrivateMessages(int). It takes the page number as argument (first page is 0, second page is 1, etc), and returns the list of messages wrapped in a GDPaginator (see javadoc for more details):

// blocking
try{
        AuthenticatedGDClient client=GDClientBuilder.create().buildAuthenticated(credentials).block();
        try{
        GDPaginator<GDMessage> messages=client.getPrivateMessages(0).block();
        System.out.println(messages);
        } catch(GDClientException e){
        // Handle request failed
        }
        }catch(GDLoginFailedException e){
        // Handle login failed
        }

// non-blocking
        Mono<AuthenticatedGDClient> clientMono=builder.buildAuthenticated(credentials)
        .doOnError(GDLoginFailedException.class,e-> /* Handle login failed */)
        .cache();

        clientMono.flatMap(client->client.getPrivateMessages(0))
        .doOnNext(System.out::println)
        .doOnError(GDClientException.class,e-> /* Handle request failed */)
        .subscribe();

You can read more about non-blocking programming here.

Javadoc

Obviously, there are much more things that you can do other than fetching a level and your message inbox. You can view all client methods available by browsing the official Javadoc here