mirror of
https://github.com/Grasscutters/Grasscutter.git
synced 2025-01-08 08:12:57 +08:00
Implement handbook request limiting
This commit is contained in:
parent
a575a2b7f6
commit
8e11f53a2e
@ -4,20 +4,12 @@ import ch.qos.logback.classic.Level;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.Grasscutter.ServerDebugMode;
|
||||
import emu.grasscutter.Grasscutter.ServerRunMode;
|
||||
import emu.grasscutter.utils.Crypto;
|
||||
import emu.grasscutter.utils.JsonUtils;
|
||||
import emu.grasscutter.utils.Utils;
|
||||
import emu.grasscutter.utils.*;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
import static emu.grasscutter.Grasscutter.config;
|
||||
import static emu.grasscutter.Grasscutter.*;
|
||||
|
||||
/**
|
||||
* *when your JVM fails*
|
||||
@ -34,17 +26,18 @@ public class ConfigContainer {
|
||||
* with the new dispatch server.
|
||||
* Version 8 - 'server' is being added for enforcing handbook server
|
||||
* addresses.
|
||||
* Version 9 - 'limits' was added for handbook requests.
|
||||
*/
|
||||
private static int version() {
|
||||
return 8;
|
||||
return 9;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to update the server's existing configuration to the latest
|
||||
* Attempts to update the server's existing configuration.
|
||||
*/
|
||||
public static void updateConfig() {
|
||||
try { // Check if the server is using a legacy config.
|
||||
JsonObject configObject = JsonUtils.loadToClass(Grasscutter.configFile.toPath(), JsonObject.class);
|
||||
var configObject = JsonUtils.loadToClass(Grasscutter.configFile.toPath(), JsonObject.class);
|
||||
if (!configObject.has("version")) {
|
||||
Grasscutter.getLogger().info("Updating legacy ..");
|
||||
Grasscutter.saveConfig(null);
|
||||
@ -58,9 +51,9 @@ public class ConfigContainer {
|
||||
return;
|
||||
|
||||
// Create a new configuration instance.
|
||||
ConfigContainer updated = new ConfigContainer();
|
||||
var updated = new ConfigContainer();
|
||||
// Update all configuration fields.
|
||||
Field[] fields = ConfigContainer.class.getDeclaredFields();
|
||||
var fields = ConfigContainer.class.getDeclaredFields();
|
||||
Arrays.stream(fields).forEach(field -> {
|
||||
try {
|
||||
field.set(updated, field.get(config));
|
||||
@ -73,7 +66,7 @@ public class ConfigContainer {
|
||||
Grasscutter.saveConfig(updated);
|
||||
Grasscutter.loadConfig();
|
||||
} catch (Exception exception) {
|
||||
Grasscutter.getLogger().warn("Failed to inject the updated ", exception);
|
||||
Grasscutter.getLogger().warn("Failed to save the updated configuration.", exception);
|
||||
}
|
||||
}
|
||||
|
||||
@ -301,17 +294,31 @@ public class ConfigContainer {
|
||||
|
||||
public static class HandbookOptions {
|
||||
public boolean enable = false;
|
||||
|
||||
public boolean allowCommands = true;
|
||||
public int maxRequests = 10;
|
||||
public int maxEntities = 100;
|
||||
|
||||
public Limits limits = new Limits();
|
||||
public Server server = new Server();
|
||||
|
||||
public static class Limits {
|
||||
/* Are rate limits checked? */
|
||||
public boolean enabled = false;
|
||||
/* The time for limits to expire. */
|
||||
public int interval = 3;
|
||||
|
||||
/* The maximum amount of normal requests. */
|
||||
public int maxRequests = 10;
|
||||
/* The maximum amount of entities to be spawned in one request. */
|
||||
public int maxEntities = 25;
|
||||
}
|
||||
|
||||
public static class Server {
|
||||
/* Are the server settings sent to the handbook? */
|
||||
public boolean enforced = false;
|
||||
/* The default server address for the handbook's authentication. */
|
||||
public String address = "127.0.0.1";
|
||||
/* The default server port for the handbook's authentication. */
|
||||
public int port = 443;
|
||||
/* Should the defaults be enforced? */
|
||||
public boolean canChange = true;
|
||||
}
|
||||
}
|
||||
|
@ -1,23 +1,27 @@
|
||||
package emu.grasscutter.server.http.documentation;
|
||||
|
||||
import static emu.grasscutter.config.Configuration.HANDBOOK;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.auth.AuthenticationSystem.AuthenticationRequest;
|
||||
import emu.grasscutter.server.http.Router;
|
||||
import emu.grasscutter.utils.DispatchUtils;
|
||||
import emu.grasscutter.utils.FileUtils;
|
||||
import emu.grasscutter.utils.objects.HandbookBody;
|
||||
import emu.grasscutter.utils.*;
|
||||
import emu.grasscutter.utils.objects.*;
|
||||
import emu.grasscutter.utils.objects.HandbookBody.Action;
|
||||
import io.javalin.Javalin;
|
||||
import io.javalin.http.ContentType;
|
||||
import io.javalin.http.Context;
|
||||
import io.javalin.http.*;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
import static emu.grasscutter.config.Configuration.HANDBOOK;
|
||||
|
||||
/** Handles requests for the new GM Handbook. */
|
||||
public final class HandbookHandler implements Router {
|
||||
private String handbook;
|
||||
private final boolean serve;
|
||||
|
||||
private final Map<String, Integer> currentRequests
|
||||
= new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Constructor for the handbook router. Enables serving the handbook if the handbook file is
|
||||
* found.
|
||||
@ -34,6 +38,17 @@ public final class HandbookHandler implements Router {
|
||||
.replace("{{DETAILS_PORT}}", String.valueOf(server.port))
|
||||
.replace("{{DETAILS_DISABLE}}", Boolean.toString(!server.canChange));
|
||||
}
|
||||
|
||||
// Create a new task to reset the request count.
|
||||
if (HANDBOOK.limits.enabled) {
|
||||
new Timer().scheduleAtFixedRate(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
currentRequests.clear();
|
||||
}
|
||||
}, 0, TimeUnit.SECONDS.toMillis(
|
||||
HANDBOOK.limits.interval));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -60,6 +75,32 @@ public final class HandbookHandler implements Router {
|
||||
return HANDBOOK.enable && HANDBOOK.allowCommands;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the request against the normal request limits.
|
||||
*
|
||||
* @param ctx The Javalin request context.
|
||||
* @return True if the request is within the normal limits.
|
||||
*/
|
||||
private boolean normalLimit(Context ctx) {
|
||||
var limits = HANDBOOK.limits;
|
||||
if (!limits.enabled) return true;
|
||||
|
||||
// Check the request count.
|
||||
var address = Utils.address(ctx);
|
||||
var count = this.currentRequests.getOrDefault(address, 0);
|
||||
if (++count >= limits.maxRequests) {
|
||||
// Respond to the request.
|
||||
ctx.status(429).result(JObject.c()
|
||||
.add("timestamp", System.currentTimeMillis())
|
||||
.toString());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update the request count.
|
||||
this.currentRequests.put(address, count);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serves the handbook if it is found.
|
||||
*
|
||||
@ -128,6 +169,9 @@ public final class HandbookHandler implements Router {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for rate limiting.
|
||||
if (!this.normalLimit(ctx)) return;
|
||||
|
||||
// Parse the request body into a class.
|
||||
var request = ctx.bodyAsClass(HandbookBody.GrantAvatar.class);
|
||||
// Get the response.
|
||||
@ -148,6 +192,9 @@ public final class HandbookHandler implements Router {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for rate limiting.
|
||||
if (!this.normalLimit(ctx)) return;
|
||||
|
||||
// Parse the request body into a class.
|
||||
var request = ctx.bodyAsClass(HandbookBody.GiveItem.class);
|
||||
// Get the response.
|
||||
@ -168,6 +215,9 @@ public final class HandbookHandler implements Router {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for rate limiting.
|
||||
if (!this.normalLimit(ctx)) return;
|
||||
|
||||
// Parse the request body into a class.
|
||||
var request = ctx.bodyAsClass(HandbookBody.TeleportTo.class);
|
||||
// Get the response.
|
||||
@ -188,8 +238,23 @@ public final class HandbookHandler implements Router {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for rate limiting.
|
||||
if (!this.normalLimit(ctx)) return;
|
||||
|
||||
// Parse the request body into a class.
|
||||
var request = ctx.bodyAsClass(HandbookBody.SpawnEntity.class);
|
||||
// Check the entity limit.
|
||||
var entityLimit = HANDBOOK.limits.enabled ?
|
||||
Math.max(HANDBOOK.limits.maxEntities, 0) :
|
||||
Long.MAX_VALUE;
|
||||
if (request.getAmount() > entityLimit) {
|
||||
ctx.status(400).result(JObject.c()
|
||||
.add("timestamp", System.currentTimeMillis())
|
||||
.add("error", "Entity limit exceeded.")
|
||||
.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the response.
|
||||
var response = DispatchUtils.performHandbookAction(Action.SPAWN_ENTITY, request);
|
||||
// Send the response.
|
||||
|
@ -128,4 +128,12 @@ public final class JObject {
|
||||
public Object json() {
|
||||
return this.members;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A string representation of this object.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return JsonUtils.encode(this.gson());
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user