diff --git a/src/handbook/index.html b/src/handbook/index.html index a65597435..b2fc29c98 100644 --- a/src/handbook/index.html +++ b/src/handbook/index.html @@ -7,6 +7,11 @@ diff --git a/src/handbook/src/backend/server.ts b/src/handbook/src/backend/server.ts index aefc4efd8..d35c5ab17 100644 --- a/src/handbook/src/backend/server.ts +++ b/src/handbook/src/backend/server.ts @@ -1,12 +1,14 @@ import type { CommandResponse } from "@backend/types"; import emitter from "@backend/events"; +import { getWindowDetails } from "@app/utils"; + let playerToken: string | null = null; // The session token for the player. export let targetPlayer = 0; // The UID of the target player. // The server's address and port. -export let address: string = "127.0.0.1", - port: string = "443"; +export let address: string = getWindowDetails().address, + port: string = getWindowDetails().port.toString(); export let encrypted: boolean = true; export let lockedPlayer = false; // Whether the UID field is locked. @@ -16,6 +18,9 @@ export let connected = false; // Whether the server is connected. * Loads the server details from local storage. */ export function setup(): void { + // Check if the server is disabled. + if (getWindowDetails().disable) return; + // Load the server details from local storage. const storedAddress = localStorage.getItem("address"); const storedPort = localStorage.getItem("port"); diff --git a/src/handbook/src/backend/types.ts b/src/handbook/src/backend/types.ts index b65b9c2c9..df08d4e73 100644 --- a/src/handbook/src/backend/types.ts +++ b/src/handbook/src/backend/types.ts @@ -135,6 +135,12 @@ export type CommandResponse = { message: string; }; +export type WindowDetails = { + address: string, + port: number, + disable: boolean +}; + /** * Checks if a string is a page. * diff --git a/src/handbook/src/ui/widgets/ServerSettings.tsx b/src/handbook/src/ui/widgets/ServerSettings.tsx index dd74f64b9..86aa4f71b 100644 --- a/src/handbook/src/ui/widgets/ServerSettings.tsx +++ b/src/handbook/src/ui/widgets/ServerSettings.tsx @@ -4,6 +4,7 @@ import emitter from "@backend/events"; import { targetPlayer, address, port, setServerDetails, url, setTargetPlayer } from "@backend/server"; import "@css/widgets/ServerSettings.scss"; +import { getWindowDetails } from "@app/utils"; interface IState { webview: boolean; @@ -97,6 +98,8 @@ class ServerSettings extends React.Component<{}, IState> { } render() { + const { disable } = getWindowDetails(); + return (
{this.state.webview ? ( @@ -109,7 +112,14 @@ class ServerSettings extends React.Component<{}, IState> {

Server Settings

-
+

Address:

{ this.setState({ address: value }); }} + disabled={disable} + style={{ + cursor: disable ? "not-allowed" : "text" + }} />
@@ -139,6 +153,10 @@ class ServerSettings extends React.Component<{}, IState> { this.setState({ port: Number(value) }); }} + disabled={disable} + style={{ + cursor: disable ? "not-allowed" : "text" + }} />
diff --git a/src/handbook/src/utils.ts b/src/handbook/src/utils.ts index 3ec901c41..559783a8b 100644 --- a/src/handbook/src/utils.ts +++ b/src/handbook/src/utils.ts @@ -1,4 +1,4 @@ -import type { Entity, Item, EntityInfo, ItemInfo } from "@backend/types"; +import type { Entity, Item, EntityInfo, ItemInfo, WindowDetails } from "@backend/types"; import { ItemType, Quality } from "@backend/types"; /** @@ -165,3 +165,20 @@ export function notNaN(value: number | string): string { const number = parseInt(value.toString()); return isNaN(number) ? "" : number.toString(); } + +/** + * Extracts the server details out of the window. + */ +export function getWindowDetails(): WindowDetails { + const details = (window as any).details; + const { address, port, disable } = details; + + return { + address: address == "{{DETAILS_ADDRESS}}" ? + "127.0.0.1" : address, + port: port == "{{DETAILS_PORT}}" ? + 443 : parseInt(port), + disable: disable == "{{DETAILS_DISABLE}}" ? + false : disable == "true" + }; +} diff --git a/src/main/java/emu/grasscutter/config/ConfigContainer.java b/src/main/java/emu/grasscutter/config/ConfigContainer.java index b4bb262af..bfdb3b0bc 100644 --- a/src/main/java/emu/grasscutter/config/ConfigContainer.java +++ b/src/main/java/emu/grasscutter/config/ConfigContainer.java @@ -32,9 +32,11 @@ public class ConfigContainer { * The field for 'legacyResources' has been removed. * Version 7 - 'regionKey' is being added for authentication * with the new dispatch server. + * Version 8 - 'server' is being added for enforcing handbook server + * addresses. */ private static int version() { - return 7; + return 8; } /** @@ -299,9 +301,19 @@ public class ConfigContainer { public static class HandbookOptions { public boolean enable = false; + public boolean allowCommands = true; public int maxRequests = 10; public int maxEntities = 100; + + public Server server = new Server(); + + public static class Server { + public boolean enforced = false; + public String address = "127.0.0.1"; + public int port = 443; + public boolean canChange = true; + } } } 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 61a27cd47..b6142d62d 100644 --- a/src/main/java/emu/grasscutter/server/http/documentation/HandbookHandler.java +++ b/src/main/java/emu/grasscutter/server/http/documentation/HandbookHandler.java @@ -15,7 +15,7 @@ import static emu.grasscutter.config.Configuration.HANDBOOK; /** Handles requests for the new GM Handbook. */ public final class HandbookHandler implements Router { - private final byte[] handbook; + private String handbook; private final boolean serve; /** @@ -23,8 +23,16 @@ public final class HandbookHandler implements Router { * found. */ public HandbookHandler() { - this.handbook = FileUtils.readResource("/html/handbook.html"); - this.serve = HANDBOOK.enable && this.handbook.length > 0; + this.handbook = new String(FileUtils.readResource("/html/handbook.html")); + this.serve = HANDBOOK.enable && this.handbook.length() > 0; + + var server = HANDBOOK.server; + if (this.serve && server.enforced) { + this.handbook = this.handbook + .replace("{{DETAILS_ADDRESS}}", server.address) + .replace("{{DETAILS_PORT}}", String.valueOf(server.port)) + .replace("{{DETAILS_DISABLE}}", Boolean.toString(server.canChange)); + } } @Override @@ -48,7 +56,7 @@ public final class HandbookHandler implements Router { * @return True if the server can execute handbook commands. */ private boolean controlSupported() { - return HANDBOOK.enable; + return HANDBOOK.enable && HANDBOOK.allowCommands; } /** @@ -61,7 +69,9 @@ public final class HandbookHandler implements Router { if (!this.serve) { ctx.status(500).result("Handbook not found."); } else { - ctx.contentType("text/html").result(this.handbook); + ctx + .contentType(ContentType.TEXT_HTML) + .result(this.handbook); } } @@ -102,8 +112,7 @@ public final class HandbookHandler implements Router { } else { ctx.status(result.getStatus()) .result(result.getBody()) - .contentType( - result.isHtml() ? ContentType.TEXT_HTML : ContentType.TEXT_PLAIN); + .contentType(result.isHtml() ? ContentType.TEXT_HTML : ContentType.TEXT_PLAIN); } } }