diff --git a/src/main/java/emu/grasscutter/config/Configuration.java b/src/main/java/emu/grasscutter/config/Configuration.java index 7cf8b833a..9e4251f0b 100644 --- a/src/main/java/emu/grasscutter/config/Configuration.java +++ b/src/main/java/emu/grasscutter/config/Configuration.java @@ -1,118 +1,120 @@ -package emu.grasscutter.config; - -import static emu.grasscutter.Grasscutter.config; - -import emu.grasscutter.utils.FileUtils; -import java.nio.file.Path; -import java.util.Locale; - -/** - * A data container for the server's configuration. - * - *

Use `import static emu.grasscutter.Configuration.*;` to import all configuration constants. - */ -public final class Configuration extends ConfigContainer { - - /* - * Constants - */ - - // 'c' is short for 'config' and makes code look 'cleaner'. - public static final ConfigContainer c = config; - - public static final Locale LANGUAGE = config.language.language; - public static final Locale FALLBACK_LANGUAGE = config.language.fallback; - public static final String DOCUMENT_LANGUAGE = config.language.document; - public static final Server SERVER = config.server; - public static final Database DATABASE = config.databaseInfo; - public static final Account ACCOUNT = config.account; - public static final HTTP HTTP_INFO = config.server.http; - public static final Game GAME_INFO = config.server.game; - public static final Dispatch DISPATCH_INFO = config.server.dispatch; - public static final DebugMode DEBUG_MODE_INFO = config.server.debugMode; - public static final Encryption HTTP_ENCRYPTION = config.server.http.encryption; - public static final Policies HTTP_POLICIES = config.server.http.policies; - public static final Files HTTP_STATIC_FILES = config.server.http.files; - public static final GameOptions GAME_OPTIONS = config.server.game.gameOptions; - public static final GameOptions.InventoryLimits INVENTORY_LIMITS = - config.server.game.gameOptions.inventoryLimits; - private static final String DATA_FOLDER = config.folderStructure.data; - private static final String PLUGINS_FOLDER = config.folderStructure.plugins; - private static final String SCRIPTS_FOLDER = config.folderStructure.scripts; - private static final String PACKETS_FOLDER = config.folderStructure.packets; - - /* - * Utilities - */ - @Deprecated(forRemoval = true) - public static String DATA() { - return DATA_FOLDER; - } - - @Deprecated(forRemoval = true) - public static String DATA(String path) { - return Path.of(DATA_FOLDER, path).toString(); - } - - @Deprecated(forRemoval = true) - public static Path getResourcePath(String path) { - return FileUtils.getResourcePath(path); - } - - @Deprecated(forRemoval = true) - public static String RESOURCE(String path) { - return FileUtils.getResourcePath(path).toString(); - } - - @Deprecated(forRemoval = true) - public static String PLUGIN() { - return PLUGINS_FOLDER; - } - - public static String PLUGIN(String path) { - return Path.of(PLUGINS_FOLDER, path).toString(); - } - - @Deprecated(forRemoval = true) - public static String SCRIPT(String path) { - return Path.of(SCRIPTS_FOLDER, path).toString(); - } - - @Deprecated(forRemoval = true) - public static String PACKET(String path) { - return Path.of(PACKETS_FOLDER, path).toString(); - } - - /** - * Fallback method. - * - * @param left Attempt to use. - * @param right Use if left is undefined. - * @return Left or right. - */ - public static T lr(T left, T right) { - return left == null ? right : left; - } - - /** - * {@link Configuration#lr(Object, Object)} for {@link String}s. - * - * @param left Attempt to use. - * @param right Use if left is empty. - * @return Left or right. - */ - public static String lr(String left, String right) { - return left.isEmpty() ? right : left; - } - - /** - * {@link Configuration#lr(Object, Object)} for {@link Integer}s. - * - * @param left Attempt to use. - * @param right Use if left is 0. - * @return Left or right. - */ - public static int lr(int left, int right) { - return left == 0 ? right : left; - } -} +package emu.grasscutter.config; + +import static emu.grasscutter.Grasscutter.config; + +import emu.grasscutter.utils.FileUtils; +import java.nio.file.Path; +import java.util.Locale; + +/** + * A data container for the server's configuration. + * + *

Use `import static emu.grasscutter.Configuration.*;` to import all configuration constants. + */ +public final class Configuration extends ConfigContainer { + + /* + * Constants + */ + + // 'c' is short for 'config' and makes code look 'cleaner'. + public static final ConfigContainer c = config; + + public static final Locale LANGUAGE = config.language.language; + public static final Locale FALLBACK_LANGUAGE = config.language.fallback; + public static final String DOCUMENT_LANGUAGE = config.language.document; + public static final Server SERVER = config.server; + public static final Database DATABASE = config.databaseInfo; + public static final Account ACCOUNT = config.account; + public static final HTTP HTTP_INFO = config.server.http; + public static final Game GAME_INFO = config.server.game; + public static final Dispatch DISPATCH_INFO = config.server.dispatch; + public static final DebugMode DEBUG_MODE_INFO = config.server.debugMode; + public static final Encryption HTTP_ENCRYPTION = config.server.http.encryption; + public static final Policies HTTP_POLICIES = config.server.http.policies; + public static final Files HTTP_STATIC_FILES = config.server.http.files; + public static final GameOptions GAME_OPTIONS = config.server.game.gameOptions; + public static final GameOptions.InventoryLimits INVENTORY_LIMITS = + config.server.game.gameOptions.inventoryLimits; + public static final GameOptions.HandbookOptions HANDBOOK = + config.server.game.gameOptions.handbook; + private static final String DATA_FOLDER = config.folderStructure.data; + private static final String PLUGINS_FOLDER = config.folderStructure.plugins; + private static final String SCRIPTS_FOLDER = config.folderStructure.scripts; + private static final String PACKETS_FOLDER = config.folderStructure.packets; + + /* + * Utilities + */ + @Deprecated(forRemoval = true) + public static String DATA() { + return DATA_FOLDER; + } + + @Deprecated(forRemoval = true) + public static String DATA(String path) { + return Path.of(DATA_FOLDER, path).toString(); + } + + @Deprecated(forRemoval = true) + public static Path getResourcePath(String path) { + return FileUtils.getResourcePath(path); + } + + @Deprecated(forRemoval = true) + public static String RESOURCE(String path) { + return FileUtils.getResourcePath(path).toString(); + } + + @Deprecated(forRemoval = true) + public static String PLUGIN() { + return PLUGINS_FOLDER; + } + + public static String PLUGIN(String path) { + return Path.of(PLUGINS_FOLDER, path).toString(); + } + + @Deprecated(forRemoval = true) + public static String SCRIPT(String path) { + return Path.of(SCRIPTS_FOLDER, path).toString(); + } + + @Deprecated(forRemoval = true) + public static String PACKET(String path) { + return Path.of(PACKETS_FOLDER, path).toString(); + } + + /** + * Fallback method. + * + * @param left Attempt to use. + * @param right Use if left is undefined. + * @return Left or right. + */ + public static T lr(T left, T right) { + return left == null ? right : left; + } + + /** + * {@link Configuration#lr(Object, Object)} for {@link String}s. + * + * @param left Attempt to use. + * @param right Use if left is empty. + * @return Left or right. + */ + public static String lr(String left, String right) { + return left.isEmpty() ? right : left; + } + + /** + * {@link Configuration#lr(Object, Object)} for {@link Integer}s. + * + * @param left Attempt to use. + * @param right Use if left is 0. + * @return Left or right. + */ + public static int lr(int left, int right) { + return left == 0 ? right : left; + } +} diff --git a/src/main/java/emu/grasscutter/server/http/documentation/HandbookHandler.java b/src/main/java/emu/grasscutter/server/http/documentation/HandbookHandler.java index c9cb60364..29a49f562 100644 --- a/src/main/java/emu/grasscutter/server/http/documentation/HandbookHandler.java +++ b/src/main/java/emu/grasscutter/server/http/documentation/HandbookHandler.java @@ -4,12 +4,16 @@ import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter.ServerRunMode; import emu.grasscutter.data.GameData; import emu.grasscutter.game.avatar.Avatar; +import emu.grasscutter.game.inventory.GameItem; +import emu.grasscutter.game.props.ActionReason; import emu.grasscutter.server.http.Router; import emu.grasscutter.utils.FileUtils; import emu.grasscutter.utils.objects.HandbookBody; import io.javalin.Javalin; import io.javalin.http.Context; +import static emu.grasscutter.config.Configuration.HANDBOOK; + /** Handles requests for the new GM Handbook. */ public final class HandbookHandler implements Router { private final byte[] handbook; @@ -21,23 +25,27 @@ public final class HandbookHandler implements Router { */ public HandbookHandler() { this.handbook = FileUtils.readResource("/handbook.html"); - this.serve = this.handbook.length > 0; + this.serve = HANDBOOK.enable && this.handbook.length > 0; } @Override public void applyRoutes(Javalin javalin) { + if (!this.serve) return; + // The handbook content. (built from src/handbook) javalin.get("/handbook", this::serveHandbook); // Handbook control routes. javalin.post("/handbook/avatar", this::grantAvatar); + javalin.post("/handbook/item", this::giveItem); } /** * @return True if the server can execute handbook commands. */ private boolean controlSupported() { - return Grasscutter.getRunMode() == ServerRunMode.HYBRID; + return HANDBOOK.enable && + Grasscutter.getRunMode() == ServerRunMode.HYBRID; } /** @@ -105,6 +113,61 @@ public final class HandbookHandler implements Router { .build()); } catch (NumberFormatException ignored) { ctx.status(500).result("Invalid player UID or avatar ID."); + } catch (Exception exception) { + ctx.status(500).result("An error occurred while granting the avatar."); + Grasscutter.getLogger().debug("A handbook command error occurred.", exception); + } + } + + /** + * Gives an item to the user. + * + * @route POST /handbook/item + * @param ctx The Javalin request context. + */ + private void giveItem(Context ctx) { + if (!this.controlSupported()) { + ctx.status(500).result("Handbook control not supported."); + return; + } + + // Parse the request body into a class. + var request = ctx.bodyAsClass(HandbookBody.GiveItem.class); + // Validate the request. + if (request.getPlayer() == null || request.getItem() == null) { + ctx.status(400).result("Invalid request."); + return; + } + + try { + // Parse the requested player. + var playerId = Integer.parseInt(request.getPlayer()); + var player = Grasscutter.getGameServer().getPlayerByUid(playerId); + + // Parse the requested item. + var itemId = Integer.parseInt(request.getItem()); + var itemData = GameData.getItemDataMap().get(itemId); + + // Validate the request. + if (player == null || itemData == null) { + ctx.status(400).result("Invalid player UID or item ID."); + return; + } + + // Create the new item stack. + var itemStack = new GameItem(itemData, request.getAmount()); + // Add the item to the inventory. + player.getInventory().addItem(itemStack, ActionReason.Gm); + + ctx.json(HandbookBody.Response.builder() + .status(200) + .message("Item granted.") + .build()); + } catch (NumberFormatException ignored) { + ctx.status(500).result("Invalid player UID or item ID."); + } catch (Exception exception) { + ctx.status(500).result("An error occurred while granting the item."); + Grasscutter.getLogger().debug("A handbook command error occurred.", exception); } } } diff --git a/src/main/java/emu/grasscutter/utils/objects/HandbookBody.java b/src/main/java/emu/grasscutter/utils/objects/HandbookBody.java index 1215864ab..f0f76a161 100644 --- a/src/main/java/emu/grasscutter/utils/objects/HandbookBody.java +++ b/src/main/java/emu/grasscutter/utils/objects/HandbookBody.java @@ -4,6 +4,7 @@ import lombok.Builder; import lombok.Getter; /** HTTP request object for handbook controls. */ +@SuppressWarnings("FieldMayBeFinal") public interface HandbookBody { @Builder class Response { @@ -20,4 +21,12 @@ public interface HandbookBody { private int constellations = 6; // Range between 0 - 6. private int talentLevels = 10; // Range between 1 - 15. } + + @Getter + class GiveItem { + private String player; // Parse into online player ID. + private String item; // Parse into item ID. + + private int amount = 1; // Range between 1 - Long.MAX_VALUE. + } }