Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion Java/src/main/java/net/hypixel/api/HypixelAPI.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import net.hypixel.api.reply.*;
import net.hypixel.api.reply.skyblock.*;
import net.hypixel.api.util.GameType;
import net.hypixel.api.util.RateLimiter;
import net.hypixel.api.util.ResourceType;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
Expand All @@ -25,6 +26,7 @@
import java.util.concurrent.Executors;

public class HypixelAPI {
private static final int DEFAULT_MAX_REQUESTS_PER_MINUTE = 120;

private static final Gson GSON = new GsonBuilder()
.registerTypeAdapter(UUID.class, new UUIDTypeAdapter())
Expand All @@ -40,19 +42,33 @@ public class HypixelAPI {

private final ExecutorService executorService;
private final HttpClient httpClient;
private final RateLimiter rateLimiter;

public HypixelAPI(UUID apiKey) {
this.apiKey = apiKey;

this.executorService = Executors.newCachedThreadPool();
this.httpClient = HttpClientBuilder.create().build();
this.rateLimiter = new RateLimiter(DEFAULT_MAX_REQUESTS_PER_MINUTE);
}

/**
* Shuts down the internal executor service
* Set how many requests this API instance is allowed to make in one minute.
* <p>If more requests attempt to pass past this limit in one minute,
* they will need to wait for the next minute. Default is 120.
*
* @param limitPerMinute The new limit.
*/
public void setRateLimit(int limitPerMinute) {
rateLimiter.setRate(limitPerMinute);
}

/**
* Shuts down the internal executor service and rate limiter service.
*/
public void shutdown() {
executorService.shutdown();
rateLimiter.shutdown();
}

/**
Expand Down Expand Up @@ -246,6 +262,8 @@ private <R extends AbstractReply> CompletableFuture<R> get(Class<R> clazz, Strin

executorService.submit(() -> {
try {
rateLimiter.beforeAction();

R response = httpClient.execute(new HttpGet(url.toString()), obj -> {
String content = EntityUtils.toString(obj.getEntity(), "UTF-8");
if (clazz == ResourceReply.class) {
Expand Down
62 changes: 62 additions & 0 deletions Java/src/main/java/net/hypixel/api/util/RateLimiter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package net.hypixel.api.util;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;

import static java.util.concurrent.TimeUnit.MINUTES;

public class RateLimiter {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should make sure to format this class similar to other classes in the project, using 4 spaces for indentation instead of tabs are the main thing

private final Object lock = new Object();

private final ScheduledExecutorService resetter = Executors.newSingleThreadScheduledExecutor();
private volatile int limitPerMinute;
private int actionsThisMinute;

/**
* @param limitPerMinute Maximum amount of times {@link RateLimiter#beforeAction()} can be called in the same minute
* before it begins blocking threads until the next minute.
*/
public RateLimiter(int limitPerMinute) {
this.limitPerMinute = limitPerMinute;
resetter.scheduleAtFixedRate(this::reset, 1, 1, MINUTES);
}

private void reset() {
synchronized ( lock ) {
actionsThisMinute = 0;
lock.notifyAll();
}
}

/**
* Set the action queue limit to be used by {@link RateLimiter#beforeAction()}.
*
* @param limitPerMinute The new limit.
*/
public void setRate(int limitPerMinute) {
this.limitPerMinute = limitPerMinute;
}

/**
* Blocks the current thread in the event that the action queue has been filled for this minute.
* Unblocks when the queue refreshes.
*
* @throws InterruptedException If interrupted while waiting.
*/
public void beforeAction() throws InterruptedException {
synchronized ( lock ) {
while (actionsThisMinute >= limitPerMinute) {
lock.wait();
}

actionsThisMinute++;
}
}

/**
* Shut down the internal executor service.
*/
public void shutdown() {
resetter.shutdown();
}
}