Run IntelliJ IDEA code formatter

This commit is contained in:
KingRainbow44
2023-03-31 17:19:26 -04:00
parent 5bf5fb07a2
commit 15e2f3ca34
917 changed files with 30030 additions and 22446 deletions

View File

@@ -4,9 +4,7 @@ import emu.grasscutter.Grasscutter;
import emu.grasscutter.Grasscutter.ServerDebugMode;
import emu.grasscutter.utils.FileUtils;
import io.javalin.Javalin;
import io.javalin.core.util.JavalinLogger;
import io.javalin.http.ContentType;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.util.ssl.SslContextFactory;
@@ -53,13 +51,14 @@ public final class HttpServer {
/**
* Creates an HTTP(S) server.
*
* @return A server instance.
*/
@SuppressWarnings("resource")
private static Server createServer() {
Server server = new Server();
ServerConnector serverConnector
= new ServerConnector(server);
= new ServerConnector(server);
if (HTTP_ENCRYPTION.useEncryption) {
var sslContextFactory = new SslContextFactory.Server();
@@ -97,6 +96,7 @@ public final class HttpServer {
/**
* Returns the handle for the Express application.
*
* @return A Javalin instance.
*/
public Javalin getHandle() {
@@ -105,6 +105,7 @@ public final class HttpServer {
/**
* Initializes the provided class.
*
* @param router The router class.
* @return Method chaining.
*/
@@ -121,18 +122,20 @@ public final class HttpServer {
routerInstance.applyRoutes(this.javalin); // Apply routes.
} catch (Exception exception) {
Grasscutter.getLogger().warn(translate("messages.dispatch.router_error"), exception);
} return this;
}
return this;
}
/**
* Starts listening on the HTTP server.
*
* @throws UnsupportedEncodingException
*/
public void start() throws UnsupportedEncodingException {
// Attempt to start the HTTP server.
if (HTTP_INFO.bindAddress.equals("")) {
this.javalin.start(HTTP_INFO.bindPort);
}else {
} else {
this.javalin.start(HTTP_INFO.bindAddress, HTTP_INFO.bindPort);
}
@@ -144,21 +147,22 @@ public final class HttpServer {
* Handles the '/' (index) endpoint on the Express application.
*/
public static class DefaultRequestRouter implements Router {
@Override public void applyRoutes(Javalin javalin) {
@Override
public void applyRoutes(Javalin javalin) {
javalin.get("/", ctx -> {
// Send file
File file = new File(HTTP_STATIC_FILES.indexFile);
if (!file.exists()) {
ctx.contentType(ContentType.TEXT_HTML);
ctx.result("""
<!DOCTYPE html>
<html>
<head>
<meta charset="utf8">
</head>
<body>%s</body>
</html>
""".formatted(translate("messages.status.welcome")));
<!DOCTYPE html>
<html>
<head>
<meta charset="utf8">
</head>
<body>%s</body>
</html>
""".formatted(translate("messages.status.welcome")));
} else {
var filePath = file.getPath();
ContentType fromExtension = ContentType.getContentTypeByExtension(filePath.substring(filePath.lastIndexOf(".") + 1));
@@ -173,7 +177,8 @@ public final class HttpServer {
* Handles unhandled endpoints on the Express application.
*/
public static class UnhandledRequestRouter implements Router {
@Override public void applyRoutes(Javalin javalin) {
@Override
public void applyRoutes(Javalin javalin) {
javalin.error(404, ctx -> {
// Error log
if (DISPATCH_INFO.logRequests == ServerDebugMode.MISSING)
@@ -183,17 +188,17 @@ public final class HttpServer {
if (!file.exists()) {
ctx.contentType(ContentType.TEXT_HTML);
ctx.result("""
<!DOCTYPE html>
<html>
<head>
<meta charset="utf8">
</head>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf8">
</head>
<body>
<img src="https://http.cat/404" />
</body>
</html>
""");
<body>
<img src="https://http.cat/404" />
</body>
</html>
""");
} else {
var filePath = file.getPath();
ContentType fromExtension = ContentType.getContentTypeByExtension(filePath.substring(filePath.lastIndexOf(".") + 1));

View File

@@ -10,18 +10,20 @@ public interface Router {
/**
* Called when the router is initialized by Express.
*
* @param javalin A Javalin instance.
*/
void applyRoutes(Javalin javalin);
/**
* Applies this handler to all endpoint types
*
* @param javalin A Javalin instance.
* @param path
* @param ctx
* @return The Javalin instance.
*/
public default Javalin allRoutes(Javalin javalin, String path, Handler ctx) {
default Javalin allRoutes(Javalin javalin, String path, Handler ctx) {
javalin.get(path, ctx);
javalin.post(path, ctx);
javalin.put(path, ctx);

View File

@@ -4,8 +4,10 @@ import emu.grasscutter.Grasscutter;
import emu.grasscutter.auth.AuthenticationSystem;
import emu.grasscutter.auth.OAuthAuthenticator.ClientType;
import emu.grasscutter.server.http.Router;
import emu.grasscutter.server.http.objects.*;
import emu.grasscutter.server.http.objects.ComboTokenReqJson;
import emu.grasscutter.server.http.objects.ComboTokenReqJson.LoginTokenData;
import emu.grasscutter.server.http.objects.LoginAccountRequestJson;
import emu.grasscutter.server.http.objects.LoginTokenRequestJson;
import emu.grasscutter.utils.JsonUtils;
import io.javalin.Javalin;
import io.javalin.http.Context;
@@ -16,43 +18,6 @@ import static emu.grasscutter.utils.Language.translate;
* Handles requests related to authentication. (aka dispatch)
*/
public final class DispatchHandler implements Router {
@Override public void applyRoutes(Javalin javalin) {
// OS
// Username & Password login (from client).
javalin.post("/hk4e_global/mdk/shield/api/login", DispatchHandler::clientLogin);
// Cached token login (from registry).
javalin.post("/hk4e_global/mdk/shield/api/verify", DispatchHandler::tokenLogin);
// Combo token login (from session key).
javalin.post("/hk4e_global/combo/granter/login/v2/login", DispatchHandler::sessionKeyLogin);
// CN
// Username & Password login (from client).
javalin.post("/hk4e_cn/mdk/shield/api/login", DispatchHandler::clientLogin);
// Cached token login (from registry).
javalin.post("/hk4e_cn/mdk/shield/api/verify", DispatchHandler::tokenLogin);
// Combo token login (from session key).
javalin.post("/hk4e_cn/combo/granter/login/v2/login", DispatchHandler::sessionKeyLogin);
// External login (from other clients).
javalin.get("/authentication/type", ctx -> ctx.result(Grasscutter.getAuthenticationSystem().getClass().getSimpleName()));
javalin.post("/authentication/login", ctx -> Grasscutter.getAuthenticationSystem().getExternalAuthenticator()
.handleLogin(AuthenticationSystem.fromExternalRequest(ctx)));
javalin.post("/authentication/register", ctx -> Grasscutter.getAuthenticationSystem().getExternalAuthenticator()
.handleAccountCreation(AuthenticationSystem.fromExternalRequest(ctx)));
javalin.post("/authentication/change_password", ctx -> Grasscutter.getAuthenticationSystem().getExternalAuthenticator()
.handlePasswordReset(AuthenticationSystem.fromExternalRequest(ctx)));
// External login (from OAuth2).
javalin.post("/hk4e_global/mdk/shield/api/loginByThirdparty", ctx -> Grasscutter.getAuthenticationSystem().getOAuthAuthenticator()
.handleLogin(AuthenticationSystem.fromExternalRequest(ctx)));
javalin.get("/authentication/openid/redirect", ctx -> Grasscutter.getAuthenticationSystem().getOAuthAuthenticator()
.handleTokenProcess(AuthenticationSystem.fromExternalRequest(ctx)));
javalin.get("/Api/twitter_login", ctx -> Grasscutter.getAuthenticationSystem().getOAuthAuthenticator()
.handleRedirection(AuthenticationSystem.fromExternalRequest(ctx), ClientType.DESKTOP));
javalin.get("/sdkTwitterLogin.html", ctx -> Grasscutter.getAuthenticationSystem().getOAuthAuthenticator()
.handleRedirection(AuthenticationSystem.fromExternalRequest(ctx), ClientType.MOBILE));
}
/**
* @route /hk4e_global/mdk/shield/api/login
*/
@@ -67,8 +32,8 @@ public final class DispatchHandler implements Router {
// Pass data to authentication handler.
var responseData = Grasscutter.getAuthenticationSystem()
.getPasswordAuthenticator()
.authenticate(AuthenticationSystem.fromPasswordRequest(ctx, bodyData));
.getPasswordAuthenticator()
.authenticate(AuthenticationSystem.fromPasswordRequest(ctx, bodyData));
// Send response.
ctx.json(responseData);
@@ -90,8 +55,8 @@ public final class DispatchHandler implements Router {
// Pass data to authentication handler.
var responseData = Grasscutter.getAuthenticationSystem()
.getTokenAuthenticator()
.authenticate(AuthenticationSystem.fromTokenRequest(ctx, bodyData));
.getTokenAuthenticator()
.authenticate(AuthenticationSystem.fromTokenRequest(ctx, bodyData));
// Send response.
ctx.json(responseData);
@@ -116,12 +81,50 @@ public final class DispatchHandler implements Router {
// Pass data to authentication handler.
var responseData = Grasscutter.getAuthenticationSystem()
.getSessionKeyAuthenticator()
.authenticate(AuthenticationSystem.fromComboTokenRequest(ctx, bodyData, tokenData));
.getSessionKeyAuthenticator()
.authenticate(AuthenticationSystem.fromComboTokenRequest(ctx, bodyData, tokenData));
// Send response.
ctx.json(responseData);
// Log to console.
Grasscutter.getLogger().info(translate("messages.dispatch.account.login_attempt", ctx.ip()));
}
@Override
public void applyRoutes(Javalin javalin) {
// OS
// Username & Password login (from client).
javalin.post("/hk4e_global/mdk/shield/api/login", DispatchHandler::clientLogin);
// Cached token login (from registry).
javalin.post("/hk4e_global/mdk/shield/api/verify", DispatchHandler::tokenLogin);
// Combo token login (from session key).
javalin.post("/hk4e_global/combo/granter/login/v2/login", DispatchHandler::sessionKeyLogin);
// CN
// Username & Password login (from client).
javalin.post("/hk4e_cn/mdk/shield/api/login", DispatchHandler::clientLogin);
// Cached token login (from registry).
javalin.post("/hk4e_cn/mdk/shield/api/verify", DispatchHandler::tokenLogin);
// Combo token login (from session key).
javalin.post("/hk4e_cn/combo/granter/login/v2/login", DispatchHandler::sessionKeyLogin);
// External login (from other clients).
javalin.get("/authentication/type", ctx -> ctx.result(Grasscutter.getAuthenticationSystem().getClass().getSimpleName()));
javalin.post("/authentication/login", ctx -> Grasscutter.getAuthenticationSystem().getExternalAuthenticator()
.handleLogin(AuthenticationSystem.fromExternalRequest(ctx)));
javalin.post("/authentication/register", ctx -> Grasscutter.getAuthenticationSystem().getExternalAuthenticator()
.handleAccountCreation(AuthenticationSystem.fromExternalRequest(ctx)));
javalin.post("/authentication/change_password", ctx -> Grasscutter.getAuthenticationSystem().getExternalAuthenticator()
.handlePasswordReset(AuthenticationSystem.fromExternalRequest(ctx)));
// External login (from OAuth2).
javalin.post("/hk4e_global/mdk/shield/api/loginByThirdparty", ctx -> Grasscutter.getAuthenticationSystem().getOAuthAuthenticator()
.handleLogin(AuthenticationSystem.fromExternalRequest(ctx)));
javalin.get("/authentication/openid/redirect", ctx -> Grasscutter.getAuthenticationSystem().getOAuthAuthenticator()
.handleTokenProcess(AuthenticationSystem.fromExternalRequest(ctx)));
javalin.get("/Api/twitter_login", ctx -> Grasscutter.getAuthenticationSystem().getOAuthAuthenticator()
.handleRedirection(AuthenticationSystem.fromExternalRequest(ctx), ClientType.DESKTOP));
javalin.get("/sdkTwitterLogin.html", ctx -> Grasscutter.getAuthenticationSystem().getOAuthAuthenticator()
.handleRedirection(AuthenticationSystem.fromExternalRequest(ctx), ClientType.MOBILE));
}
}

View File

@@ -3,10 +3,10 @@ package emu.grasscutter.server.http.dispatch;
import com.google.protobuf.ByteString;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.Grasscutter.ServerRunMode;
import emu.grasscutter.net.proto.QueryRegionListHttpRspOuterClass.QueryRegionListHttpRsp;
import emu.grasscutter.net.proto.QueryCurrRegionHttpRspOuterClass.QueryCurrRegionHttpRsp;
import emu.grasscutter.net.proto.RegionSimpleInfoOuterClass.RegionSimpleInfo;
import emu.grasscutter.net.proto.QueryRegionListHttpRspOuterClass.QueryRegionListHttpRsp;
import emu.grasscutter.net.proto.RegionInfoOuterClass.RegionInfo;
import emu.grasscutter.net.proto.RegionSimpleInfoOuterClass.RegionSimpleInfo;
import emu.grasscutter.server.event.dispatch.QueryAllRegionsEvent;
import emu.grasscutter.server.event.dispatch.QueryCurrentRegionEvent;
import emu.grasscutter.server.http.Router;
@@ -15,18 +15,19 @@ import emu.grasscutter.utils.Crypto;
import emu.grasscutter.utils.Utils;
import io.javalin.Javalin;
import io.javalin.http.Context;
import org.slf4j.Logger;
import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.security.Signature;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;
import org.slf4j.Logger;
import static emu.grasscutter.config.Configuration.*;
import static emu.grasscutter.utils.Language.translate;
/**
* Handles requests related to region queries.
@@ -44,86 +45,6 @@ public final class RegionHandler implements Router {
}
}
/**
* Configures region data according to configuration.
*/
private void initialize() {
String dispatchDomain = "http" + (HTTP_ENCRYPTION.useInRouting ? "s" : "") + "://"
+ lr(HTTP_INFO.accessAddress, HTTP_INFO.bindAddress) + ":"
+ lr(HTTP_INFO.accessPort, HTTP_INFO.bindPort);
// Create regions.
List<RegionSimpleInfo> servers = new ArrayList<>();
List<String> usedNames = new ArrayList<>(); // List to check for potential naming conflicts.
var configuredRegions = new ArrayList<>(List.of(DISPATCH_INFO.regions));
if (SERVER.runMode != ServerRunMode.HYBRID && configuredRegions.size() == 0) {
Grasscutter.getLogger().error("[Dispatch] There are no game servers available. Exiting due to unplayable state.");
System.exit(1);
} else if (configuredRegions.size() == 0)
configuredRegions.add(new Region("os_usa", DISPATCH_INFO.defaultName,
lr(GAME_INFO.accessAddress, GAME_INFO.bindAddress),
lr(GAME_INFO.accessPort, GAME_INFO.bindPort)));
configuredRegions.forEach(region -> {
if (usedNames.contains(region.Name)) {
Grasscutter.getLogger().error("Region name already in use.");
return;
}
// Create a region identifier.
var identifier = RegionSimpleInfo.newBuilder()
.setName(region.Name).setTitle(region.Title).setType("DEV_PUBLIC")
.setDispatchUrl(dispatchDomain + "/query_cur_region/" + region.Name)
.build();
usedNames.add(region.Name); servers.add(identifier);
// Create a region info object.
var regionInfo = RegionInfo.newBuilder()
.setGateserverIp(region.Ip).setGateserverPort(region.Port)
.setSecretKey(ByteString.copyFrom(Crypto.DISPATCH_SEED))
.build();
// Create an updated region query.
var updatedQuery = QueryCurrRegionHttpRsp.newBuilder().setRegionInfo(regionInfo).build();
regions.put(region.Name, new RegionData(updatedQuery, Utils.base64Encode(updatedQuery.toByteString().toByteArray())));
});
// Create a config object.
byte[] customConfig = "{\"sdkenv\":\"2\",\"checkdevice\":\"false\",\"loadPatch\":\"false\",\"showexception\":\"false\",\"regionConfig\":\"pm|fk|add\",\"downloadMode\":\"0\"}".getBytes();
Crypto.xor(customConfig, Crypto.DISPATCH_KEY); // XOR the config with the key.
// Create an updated region list.
QueryRegionListHttpRsp updatedRegionList = QueryRegionListHttpRsp.newBuilder()
.addAllRegionList(servers)
.setClientSecretKey(ByteString.copyFrom(Crypto.DISPATCH_SEED))
.setClientCustomConfigEncrypted(ByteString.copyFrom(customConfig))
.setEnableLoginPc(true).build();
// Set the region list response.
regionListResponse = Utils.base64Encode(updatedRegionList.toByteString().toByteArray());
// CN
// Create a config object.
byte[] customConfigcn = "{\"sdkenv\":\"0\",\"checkdevice\":\"true\",\"loadPatch\":\"false\",\"showexception\":\"false\",\"regionConfig\":\"pm|fk|add\",\"downloadMode\":\"0\"}".getBytes();
Crypto.xor(customConfigcn, Crypto.DISPATCH_KEY); // XOR the config with the key.
// Create an updated region list.
QueryRegionListHttpRsp updatedRegionListcn = QueryRegionListHttpRsp.newBuilder()
.addAllRegionList(servers)
.setClientSecretKey(ByteString.copyFrom(Crypto.DISPATCH_SEED))
.setClientCustomConfigEncrypted(ByteString.copyFrom(customConfigcn))
.setEnableLoginPc(true).build();
// Set the region list response.
regionListResponsecn = Utils.base64Encode(updatedRegionListcn.toByteString().toByteArray());
}
@Override
public void applyRoutes(Javalin javalin) {
javalin.get("/query_region_list", RegionHandler::queryRegionList);
javalin.get("/query_cur_region/{region}", RegionHandler::queryCurrentRegion);
}
/**
* Handle query region list request.
*
@@ -140,7 +61,7 @@ public final class RegionHandler implements Router {
// Determine the region list to use based on the version and platform.
if ("CNRELiOS".equals(versionCode) || "CNRELWin".equals(versionCode)
|| "CNRELAndroid".equals(versionCode)) {
|| "CNRELAndroid".equals(versionCode)) {
// Use the CN region list.
QueryAllRegionsEvent event = new QueryAllRegionsEvent(regionListResponsecn);
event.call();
@@ -149,7 +70,7 @@ public final class RegionHandler implements Router {
// Respond with the event result.
ctx.result(event.getRegionList());
} else if ("OSRELiOS".equals(versionCode) || "OSRELWin".equals(versionCode)
|| "OSRELAndroid".equals(versionCode)) {
|| "OSRELAndroid".equals(versionCode)) {
// Use the OS region list.
QueryAllRegionsEvent event = new QueryAllRegionsEvent(regionListResponse);
event.call();
@@ -205,11 +126,12 @@ public final class RegionHandler implements Router {
String[] versionCode = versionName.replaceAll(Pattern.compile("[a-zA-Z]").pattern(), "").split("\\.");
int versionMajor = Integer.parseInt(versionCode[0]);
int versionMinor = Integer.parseInt(versionCode[1]);
int versionFix = Integer.parseInt(versionCode[2]);
int versionFix = Integer.parseInt(versionCode[2]);
if (versionMajor >= 3 || (versionMajor == 2 && versionMinor == 7 && versionFix >= 50) || (versionMajor == 2 && versionMinor == 8)) {
try {
QueryCurrentRegionEvent event = new QueryCurrentRegionEvent(regionData); event.call();
QueryCurrentRegionEvent event = new QueryCurrentRegionEvent(regionData);
event.call();
if (ctx.queryParam("dispatchSeed") == null) {
// More love for UA Patch players
@@ -255,14 +177,13 @@ public final class RegionHandler implements Router {
rsp.sign = Utils.base64Encode(privateSignature.sign());
ctx.json(rsp);
}
catch (Exception e) {
} catch (Exception e) {
Grasscutter.getLogger().error("An error occurred while handling query_cur_region.", e);
}
}
else {
} else {
// Invoke event.
QueryCurrentRegionEvent event = new QueryCurrentRegionEvent(regionData); event.call();
QueryCurrentRegionEvent event = new QueryCurrentRegionEvent(regionData);
event.call();
// Respond with event result.
ctx.result(event.getRegionInfo());
}
@@ -270,6 +191,96 @@ public final class RegionHandler implements Router {
Grasscutter.getLogger().info(String.format("Client %s request: query_cur_region/%s", ctx.ip(), regionName));
}
/**
* Gets the current region query.
*
* @return A {@link QueryCurrRegionHttpRsp} object.
*/
public static QueryCurrRegionHttpRsp getCurrentRegion() {
return SERVER.runMode == ServerRunMode.HYBRID ? regions.get("os_usa").getRegionQuery() : null;
}
/**
* Configures region data according to configuration.
*/
private void initialize() {
String dispatchDomain = "http" + (HTTP_ENCRYPTION.useInRouting ? "s" : "") + "://"
+ lr(HTTP_INFO.accessAddress, HTTP_INFO.bindAddress) + ":"
+ lr(HTTP_INFO.accessPort, HTTP_INFO.bindPort);
// Create regions.
List<RegionSimpleInfo> servers = new ArrayList<>();
List<String> usedNames = new ArrayList<>(); // List to check for potential naming conflicts.
var configuredRegions = new ArrayList<>(List.of(DISPATCH_INFO.regions));
if (SERVER.runMode != ServerRunMode.HYBRID && configuredRegions.size() == 0) {
Grasscutter.getLogger().error("[Dispatch] There are no game servers available. Exiting due to unplayable state.");
System.exit(1);
} else if (configuredRegions.size() == 0)
configuredRegions.add(new Region("os_usa", DISPATCH_INFO.defaultName,
lr(GAME_INFO.accessAddress, GAME_INFO.bindAddress),
lr(GAME_INFO.accessPort, GAME_INFO.bindPort)));
configuredRegions.forEach(region -> {
if (usedNames.contains(region.Name)) {
Grasscutter.getLogger().error("Region name already in use.");
return;
}
// Create a region identifier.
var identifier = RegionSimpleInfo.newBuilder()
.setName(region.Name).setTitle(region.Title).setType("DEV_PUBLIC")
.setDispatchUrl(dispatchDomain + "/query_cur_region/" + region.Name)
.build();
usedNames.add(region.Name);
servers.add(identifier);
// Create a region info object.
var regionInfo = RegionInfo.newBuilder()
.setGateserverIp(region.Ip).setGateserverPort(region.Port)
.setSecretKey(ByteString.copyFrom(Crypto.DISPATCH_SEED))
.build();
// Create an updated region query.
var updatedQuery = QueryCurrRegionHttpRsp.newBuilder().setRegionInfo(regionInfo).build();
regions.put(region.Name, new RegionData(updatedQuery, Utils.base64Encode(updatedQuery.toByteString().toByteArray())));
});
// Create a config object.
byte[] customConfig = "{\"sdkenv\":\"2\",\"checkdevice\":\"false\",\"loadPatch\":\"false\",\"showexception\":\"false\",\"regionConfig\":\"pm|fk|add\",\"downloadMode\":\"0\"}".getBytes();
Crypto.xor(customConfig, Crypto.DISPATCH_KEY); // XOR the config with the key.
// Create an updated region list.
QueryRegionListHttpRsp updatedRegionList = QueryRegionListHttpRsp.newBuilder()
.addAllRegionList(servers)
.setClientSecretKey(ByteString.copyFrom(Crypto.DISPATCH_SEED))
.setClientCustomConfigEncrypted(ByteString.copyFrom(customConfig))
.setEnableLoginPc(true).build();
// Set the region list response.
regionListResponse = Utils.base64Encode(updatedRegionList.toByteString().toByteArray());
// CN
// Create a config object.
byte[] customConfigcn = "{\"sdkenv\":\"0\",\"checkdevice\":\"true\",\"loadPatch\":\"false\",\"showexception\":\"false\",\"regionConfig\":\"pm|fk|add\",\"downloadMode\":\"0\"}".getBytes();
Crypto.xor(customConfigcn, Crypto.DISPATCH_KEY); // XOR the config with the key.
// Create an updated region list.
QueryRegionListHttpRsp updatedRegionListcn = QueryRegionListHttpRsp.newBuilder()
.addAllRegionList(servers)
.setClientSecretKey(ByteString.copyFrom(Crypto.DISPATCH_SEED))
.setClientCustomConfigEncrypted(ByteString.copyFrom(customConfigcn))
.setEnableLoginPc(true).build();
// Set the region list response.
regionListResponsecn = Utils.base64Encode(updatedRegionListcn.toByteString().toByteArray());
}
@Override
public void applyRoutes(Javalin javalin) {
javalin.get("/query_region_list", RegionHandler::queryRegionList);
javalin.get("/query_cur_region/{region}", RegionHandler::queryCurrentRegion);
}
/**
* Region data container.
*/
@@ -290,12 +301,4 @@ public final class RegionHandler implements Router {
return this.base64;
}
}
/**
* Gets the current region query.
* @return A {@link QueryCurrRegionHttpRsp} object.
*/
public static QueryCurrRegionHttpRsp getCurrentRegion() {
return SERVER.runMode == ServerRunMode.HYBRID ? regions.get("os_usa").getRegionQuery() : null;
}
}

View File

@@ -2,7 +2,6 @@ package emu.grasscutter.server.http.documentation;
import emu.grasscutter.server.http.Router;
import io.javalin.Javalin;
import io.javalin.http.Context;
public final class DocumentationServerHandler implements Router {

View File

@@ -5,12 +5,12 @@ import emu.grasscutter.utils.Language;
import io.javalin.http.ContentType;
import io.javalin.http.Context;
import static emu.grasscutter.config.Configuration.DOCUMENT_LANGUAGE;
import java.util.List;
import static emu.grasscutter.config.Configuration.DOCUMENT_LANGUAGE;
final class GachaMappingRequestHandler implements DocumentationHandler {
private List<String> gachaJsons;
private final List<String> gachaJsons;
GachaMappingRequestHandler() {
this.gachaJsons = Tools.createGachaMappingJsons();

View File

@@ -1,7 +1,5 @@
package emu.grasscutter.server.http.documentation;
import static emu.grasscutter.config.Configuration.*;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.command.CommandMap;
import emu.grasscutter.data.GameData;
@@ -14,6 +12,7 @@ import emu.grasscutter.utils.Language;
import io.javalin.http.ContentType;
import io.javalin.http.Context;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList;
@@ -42,7 +41,7 @@ final class HandbookRequestHandler implements DocumentationHandler {
Matcher matcher = localePattern.matcher(acceptLanguage);
if (matcher.find()) {
String lang = matcher.group(0);
langIdx = Language.TextStrings.MAP_GC_LANGUAGES.getOrDefault(lang,0);
langIdx = Language.TextStrings.MAP_GC_LANGUAGES.getOrDefault(lang, 0);
}
}
@@ -62,7 +61,7 @@ final class HandbookRequestHandler implements DocumentationHandler {
final List<Language> languages = Language.TextStrings.getLanguages();
final List<StringBuilder> sbs = new ArrayList<>(NUM_LANGUAGES);
for (int langIdx = 0; langIdx < NUM_LANGUAGES; langIdx++)
sbs.add(new StringBuilder(""));
sbs.add(new StringBuilder());
// Commands table
CommandMap.getInstance().getHandlersAsList().forEach(cmd -> {
@@ -71,7 +70,7 @@ final class HandbookRequestHandler implements DocumentationHandler {
for (int langIdx = 0; langIdx < NUM_LANGUAGES; langIdx++)
sbs.get(langIdx).append("<tr><td><code>" + label + "</code></td><td>" + languages.get(langIdx).get(descKey) + "</td></tr>\n");
});
sbs.forEach(sb -> sb.setLength(sb.length()-1)); // Remove trailing \n
sbs.forEach(sb -> sb.setLength(sb.length() - 1)); // Remove trailing \n
final List<String> cmdsTable = sbs.stream().map(StringBuilder::toString).toList();
// Avatars table
@@ -83,7 +82,7 @@ final class HandbookRequestHandler implements DocumentationHandler {
for (int langIdx = 0; langIdx < NUM_LANGUAGES; langIdx++)
sbs.get(langIdx).append("<tr><td><code>" + id + "</code></td><td>" + name.get(langIdx) + "</td></tr>\n");
});
sbs.forEach(sb -> sb.setLength(sb.length()-1)); // Remove trailing \n
sbs.forEach(sb -> sb.setLength(sb.length() - 1)); // Remove trailing \n
final List<String> avatarsTable = sbs.stream().map(StringBuilder::toString).toList();
// Items table
@@ -95,7 +94,7 @@ final class HandbookRequestHandler implements DocumentationHandler {
for (int langIdx = 0; langIdx < NUM_LANGUAGES; langIdx++)
sbs.get(langIdx).append("<tr><td><code>" + id + "</code></td><td>" + name.get(langIdx) + "</td></tr>\n");
});
sbs.forEach(sb -> sb.setLength(sb.length()-1)); // Remove trailing \n
sbs.forEach(sb -> sb.setLength(sb.length() - 1)); // Remove trailing \n
final List<String> itemsTable = sbs.stream().map(StringBuilder::toString).toList();
// Scenes table
@@ -105,7 +104,7 @@ final class HandbookRequestHandler implements DocumentationHandler {
for (int langIdx = 0; langIdx < NUM_LANGUAGES; langIdx++)
sbs.get(langIdx).append("<tr><td><code>" + id + "</code></td><td>" + data.getScriptData() + "</td></tr>\n");
});
sbs.forEach(sb -> sb.setLength(sb.length()-1)); // Remove trailing \n
sbs.forEach(sb -> sb.setLength(sb.length() - 1)); // Remove trailing \n
final List<String> scenesTable = sbs.stream().map(StringBuilder::toString).toList();
// Monsters table
@@ -116,7 +115,7 @@ final class HandbookRequestHandler implements DocumentationHandler {
for (int langIdx = 0; langIdx < NUM_LANGUAGES; langIdx++)
sbs.get(langIdx).append("<tr><td><code>" + id + "</code></td><td>" + name.get(langIdx) + "</td></tr>\n");
});
sbs.forEach(sb -> sb.setLength(sb.length()-1)); // Remove trailing \n
sbs.forEach(sb -> sb.setLength(sb.length() - 1)); // Remove trailing \n
final List<String> monstersTable = sbs.stream().map(StringBuilder::toString).toList();
// Add translated title etc. to the page.

View File

@@ -1,7 +1,5 @@
package emu.grasscutter.server.http.documentation;
import static emu.grasscutter.utils.Language.translate;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.utils.FileUtils;
import io.javalin.http.ContentType;
@@ -10,6 +8,8 @@ import io.javalin.http.Context;
import java.io.IOException;
import java.nio.file.Files;
import static emu.grasscutter.utils.Language.translate;
final class RootRequestHandler implements DocumentationHandler {
private final String template;
@@ -33,8 +33,8 @@ final class RootRequestHandler implements DocumentationHandler {
}
String content = template.replace("{{TITLE}}", translate("documentation.index.title"))
.replace("{{ITEM_HANDBOOK}}", translate("documentation.index.handbook"))
.replace("{{ITEM_GACHA_MAPPING}}", translate("documentation.index.gacha_mapping"));
.replace("{{ITEM_HANDBOOK}}", translate("documentation.index.handbook"))
.replace("{{ITEM_GACHA_MAPPING}}", translate("documentation.index.gacha_mapping"));
ctx.contentType(ContentType.TEXT_HTML);
ctx.result(content);
}

View File

@@ -2,39 +2,24 @@ package emu.grasscutter.server.http.handlers;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.DataLoader;
import emu.grasscutter.server.http.objects.HttpJsonResponse;
import emu.grasscutter.server.http.Router;
import emu.grasscutter.server.http.objects.HttpJsonResponse;
import emu.grasscutter.utils.FileUtils;
import io.javalin.Javalin;
import io.javalin.http.ContentType;
import io.javalin.http.Context;
import static emu.grasscutter.config.Configuration.*;
import java.io.IOException;
import java.io.InputStream;
import java.util.Objects;
import java.util.StringJoiner;
import static emu.grasscutter.config.Configuration.*;
/**
* Handles requests related to the announcements page.
*/
public final class AnnouncementsHandler implements Router {
@Override public void applyRoutes(Javalin javalin) {
// hk4e-api-os.hoyoverse.com
this.allRoutes(javalin, "/common/hk4e_global/announcement/api/getAlertPic", new HttpJsonResponse("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"total\":0,\"list\":[]}}"));
// hk4e-api-os.hoyoverse.com
this.allRoutes(javalin,"/common/hk4e_global/announcement/api/getAlertAnn", new HttpJsonResponse("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"alert\":false,\"alert_id\":0,\"remind\":true}}"));
// hk4e-api-os.hoyoverse.com
this.allRoutes(javalin,"/common/hk4e_global/announcement/api/getAnnList", AnnouncementsHandler::getAnnouncement);
// hk4e-api-os-static.hoyoverse.com
this.allRoutes(javalin,"/common/hk4e_global/announcement/api/getAnnContent", AnnouncementsHandler::getAnnouncement);
// hk4e-sdk-os.hoyoverse.com
this.allRoutes(javalin,"/hk4e_global/mdk/shopwindow/shopwindow/listPriceTier", new HttpJsonResponse("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"suggest_currency\":\"USD\",\"tiers\":[]}}"));
javalin.get("/hk4e/announcement/*", AnnouncementsHandler::getPageResources);
}
private static void getAnnouncement(Context ctx) {
String data = "";
if (Objects.equals(ctx.endpointHandlerPath(), "/common/hk4e_global/announcement/api/getAnnContent")) {
@@ -63,8 +48,8 @@ public final class AnnouncementsHandler implements Router {
}
String dispatchDomain = "http" + (HTTP_ENCRYPTION.useInRouting ? "s" : "") + "://"
+ lr(HTTP_INFO.accessAddress, HTTP_INFO.bindAddress) + ":"
+ lr(HTTP_INFO.accessPort, HTTP_INFO.bindPort);
+ lr(HTTP_INFO.accessAddress, HTTP_INFO.bindAddress) + ":"
+ lr(HTTP_INFO.accessPort, HTTP_INFO.bindPort);
data = data
.replace("{{DISPATCH_PUBLIC}}", dispatchDomain)
@@ -94,4 +79,20 @@ public final class AnnouncementsHandler implements Router {
ctx.status(404);
}
}
@Override
public void applyRoutes(Javalin javalin) {
// hk4e-api-os.hoyoverse.com
this.allRoutes(javalin, "/common/hk4e_global/announcement/api/getAlertPic", new HttpJsonResponse("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"total\":0,\"list\":[]}}"));
// hk4e-api-os.hoyoverse.com
this.allRoutes(javalin, "/common/hk4e_global/announcement/api/getAlertAnn", new HttpJsonResponse("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"alert\":false,\"alert_id\":0,\"remind\":true}}"));
// hk4e-api-os.hoyoverse.com
this.allRoutes(javalin, "/common/hk4e_global/announcement/api/getAnnList", AnnouncementsHandler::getAnnouncement);
// hk4e-api-os-static.hoyoverse.com
this.allRoutes(javalin, "/common/hk4e_global/announcement/api/getAnnContent", AnnouncementsHandler::getAnnouncement);
// hk4e-sdk-os.hoyoverse.com
this.allRoutes(javalin, "/hk4e_global/mdk/shopwindow/shopwindow/listPriceTier", new HttpJsonResponse("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"suggest_currency\":\"USD\",\"tiers\":[]}}"));
javalin.get("/hk4e/announcement/*", AnnouncementsHandler::getPageResources);
}
}

View File

@@ -29,17 +29,11 @@ import static emu.grasscutter.utils.Language.translate;
* Handles all gacha-related HTTP requests.
*/
public final class GachaHandler implements Router {
@Getter private static final Path gachaMappingsPath = FileUtils.getDataUserPath("gacha/mappings.js");
@Getter
private static final Path gachaMappingsPath = FileUtils.getDataUserPath("gacha/mappings.js");
@Deprecated(forRemoval = true)
public static final String gachaMappings = gachaMappingsPath.toString();
@Override public void applyRoutes(Javalin javalin) {
javalin.get("/gacha", GachaHandler::gachaRecords);
javalin.get("/gacha/details", GachaHandler::gachaDetails);
javalin._conf.addSinglePageRoot("/gacha/mappings", gachaMappingsPath.toString(), Location.EXTERNAL); // TODO: This ***must*** be changed to take the Path not a String. This might involve upgrading Javalin.
}
private static void gachaRecords(Context ctx) {
String sessionKey = ctx.queryParam("s");
Account account = DatabaseHelper.getAccountBySessionKey(sessionKey);
@@ -98,10 +92,10 @@ public final class GachaHandler implements Router {
// Add translated title etc. to the page.
template = template.replace("{{TITLE}}", translate(player, "gacha.details.title"))
.replace("{{AVAILABLE_FIVE_STARS}}", translate(player, "gacha.details.available_five_stars"))
.replace("{{AVAILABLE_FOUR_STARS}}", translate(player, "gacha.details.available_four_stars"))
.replace("{{AVAILABLE_THREE_STARS}}", translate(player, "gacha.details.available_three_stars"))
.replace("{{LANGUAGE}}", Utils.getLanguageCode(account.getLocale()));
.replace("{{AVAILABLE_FIVE_STARS}}", translate(player, "gacha.details.available_five_stars"))
.replace("{{AVAILABLE_FOUR_STARS}}", translate(player, "gacha.details.available_four_stars"))
.replace("{{AVAILABLE_THREE_STARS}}", translate(player, "gacha.details.available_three_stars"))
.replace("{{LANGUAGE}}", Utils.getLanguageCode(account.getLocale()));
// Get the banner info for the banner we want.
int scheduleId = Integer.parseInt(ctx.queryParam("scheduleId"));
@@ -135,4 +129,12 @@ public final class GachaHandler implements Router {
ctx.contentType(ContentType.TEXT_HTML);
ctx.result(template);
}
@Override
public void applyRoutes(Javalin javalin) {
javalin.get("/gacha", GachaHandler::gachaRecords);
javalin.get("/gacha/details", GachaHandler::gachaDetails);
javalin._conf.addSinglePageRoot("/gacha/mappings", gachaMappingsPath.toString(), Location.EXTERNAL); // TODO: This ***must*** be changed to take the Path not a String. This might involve upgrading Javalin.
}
}

View File

@@ -1,20 +1,29 @@
package emu.grasscutter.server.http.handlers;
import static emu.grasscutter.config.Configuration.ACCOUNT;
import emu.grasscutter.GameConstants;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.server.http.objects.HttpJsonResponse;
import emu.grasscutter.server.http.Router;
import emu.grasscutter.server.http.objects.HttpJsonResponse;
import emu.grasscutter.server.http.objects.WebStaticVersionResponse;
import io.javalin.Javalin;
import io.javalin.http.Context;
import static emu.grasscutter.config.Configuration.ACCOUNT;
/**
* Handles all generic, hard-coded responses.
*/
public final class GenericHandler implements Router {
@Override public void applyRoutes(Javalin javalin) {
private static void serverStatus(Context ctx) {
int playerCount = Grasscutter.getGameServer().getPlayers().size();
int maxPlayer = ACCOUNT.maxPlayer;
String version = GameConstants.VERSION;
ctx.result("{\"retcode\":0,\"status\":{\"playerCount\":" + playerCount + ",\"maxPlayer\":" + maxPlayer + ",\"version\":\"" + version + "\"}}");
}
@Override
public void applyRoutes(Javalin javalin) {
// hk4e-sdk-os.hoyoverse.com
javalin.get("/hk4e_global/mdk/agreement/api/getAgreementInfos", new HttpJsonResponse("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"marketing_agreements\":[]}}"));
// hk4e-sdk-os.hoyoverse.com (this could be either GET or POST based on the observation of different clients)
@@ -45,12 +54,4 @@ public final class GenericHandler implements Router {
javalin.get("/status/server", GenericHandler::serverStatus);
}
private static void serverStatus(Context ctx) {
int playerCount = Grasscutter.getGameServer().getPlayers().size();
int maxPlayer = ACCOUNT.maxPlayer;
String version = GameConstants.VERSION;
ctx.result("{\"retcode\":0,\"status\":{\"playerCount\":" + playerCount + ",\"maxPlayer\":" + maxPlayer + ",\"version\":\"" + version + "\"}}");
}
}

View File

@@ -8,15 +8,16 @@ import io.javalin.http.Context;
* Handles logging requests made to the server.
*/
public final class LogHandler implements Router {
@Override public void applyRoutes(Javalin javalin) {
private static void log(Context ctx) {
// TODO: Figure out how to dump request body and log to file.
ctx.result("{\"code\":0}");
}
@Override
public void applyRoutes(Javalin javalin) {
// overseauspider.yuanshen.com
javalin.post("/log", LogHandler::log);
// log-upload-os.mihoyo.com
javalin.post("/crash/dataUpload", LogHandler::log);
}
private static void log(Context ctx) {
// TODO: Figure out how to dump request body and log to file.
ctx.result("{\"code\":0}");
}
}

View File

@@ -1,15 +1,15 @@
package emu.grasscutter.server.http.objects;
public class ComboTokenReqJson {
public int app_id;
public int channel_id;
public String data;
public String device;
public String sign;
public static class LoginTokenData {
public String uid;
public String token;
public boolean guest;
}
public int app_id;
public int channel_id;
public String data;
public String device;
public String sign;
public static class LoginTokenData {
public String uid;
public String token;
public boolean guest;
}
}

View File

@@ -1,17 +1,17 @@
package emu.grasscutter.server.http.objects;
public class ComboTokenResJson {
public String message;
public int retcode;
public LoginData data = new LoginData();
public static class LoginData {
public int account_type = 1;
public boolean heartbeat;
public String combo_id;
public String combo_token;
public String open_id;
public String data = "{\"guest\":false}";
public String fatigue_remind = null; // ?
}
public String message;
public int retcode;
public LoginData data = new LoginData();
public static class LoginData {
public int account_type = 1;
public boolean heartbeat;
public String combo_id;
public String combo_token;
public String open_id;
public String data = "{\"guest\":false}";
public String fatigue_remind = null; // ?
}
}

View File

@@ -1,31 +1,30 @@
package emu.grasscutter.server.http.objects;
import java.io.IOException;
import java.util.Arrays;
import java.util.Objects;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.Grasscutter.ServerDebugMode;
import io.javalin.http.Context;
import io.javalin.http.Handler;
import org.jetbrains.annotations.NotNull;
import static emu.grasscutter.config.Configuration.*;
import java.util.Arrays;
import java.util.Objects;
import static emu.grasscutter.config.Configuration.DISPATCH_INFO;
import static emu.grasscutter.utils.Language.translate;
public final class HttpJsonResponse implements Handler {
private final String response;
private final String[] missingRoutes = { // TODO: When http requests for theses routes are found please remove it from this list and update the route request type in the DispatchServer
"/common/hk4e_global/announcement/api/getAlertPic",
"/common/hk4e_global/announcement/api/getAlertAnn",
"/common/hk4e_global/announcement/api/getAnnList",
"/common/hk4e_global/announcement/api/getAnnContent",
"/hk4e_global/mdk/shopwindow/shopwindow/listPriceTier",
"/log/sdk/upload",
"/sdk/upload",
"/perf/config/verify",
"/log",
"/crash/dataUpload"
"/common/hk4e_global/announcement/api/getAlertPic",
"/common/hk4e_global/announcement/api/getAlertAnn",
"/common/hk4e_global/announcement/api/getAnnList",
"/common/hk4e_global/announcement/api/getAnnContent",
"/hk4e_global/mdk/shopwindow/shopwindow/listPriceTier",
"/log/sdk/upload",
"/sdk/upload",
"/perf/config/verify",
"/log",
"/crash/dataUpload"
};
public HttpJsonResponse(String response) {

View File

@@ -1,7 +1,7 @@
package emu.grasscutter.server.http.objects;
public class LoginAccountRequestJson {
public String account;
public String password;
public boolean is_crypto;
public String account;
public String password;
public boolean is_crypto;
}

View File

@@ -1,38 +1,38 @@
package emu.grasscutter.server.http.objects;
public class LoginResultJson {
public String message;
public int retcode;
public VerifyData data = new VerifyData();
public static class VerifyData {
public VerifyAccountData account = new VerifyAccountData();
public boolean device_grant_required = false;
public String realname_operation = "NONE";
public boolean realperson_required = false;
public boolean safe_mobile_required = false;
}
public static class VerifyAccountData {
public String uid;
public String name = "";
public String email = "";
public String mobile = "";
public String is_email_verify = "0";
public String realname = "";
public String identity_card = "";
public String token;
public String safe_mobile = "";
public String facebook_name = "";
public String twitter_name = "";
public String game_center_name = "";
public String google_name = "";
public String apple_name = "";
public String sony_name = "";
public String tap_name = "";
public String country = "US";
public String reactivate_ticket = "";
public String area_code = "**";
public String device_grant_ticket = "";
}
public String message;
public int retcode;
public VerifyData data = new VerifyData();
public static class VerifyData {
public VerifyAccountData account = new VerifyAccountData();
public boolean device_grant_required = false;
public String realname_operation = "NONE";
public boolean realperson_required = false;
public boolean safe_mobile_required = false;
}
public static class VerifyAccountData {
public String uid;
public String name = "";
public String email = "";
public String mobile = "";
public String is_email_verify = "0";
public String realname = "";
public String identity_card = "";
public String token;
public String safe_mobile = "";
public String facebook_name = "";
public String twitter_name = "";
public String game_center_name = "";
public String google_name = "";
public String apple_name = "";
public String sony_name = "";
public String tap_name = "";
public String country = "US";
public String reactivate_ticket = "";
public String area_code = "**";
public String device_grant_ticket = "";
}
}

View File

@@ -1,6 +1,6 @@
package emu.grasscutter.server.http.objects;
public class LoginTokenRequestJson {
public String uid;
public String token;
public String uid;
public String token;
}

View File

@@ -6,21 +6,13 @@ import io.javalin.http.ContentType;
import io.javalin.http.Context;
import io.javalin.http.Handler;
import static emu.grasscutter.config.Configuration.DISPATCH_INFO;
import java.io.IOException;
import java.io.InputStream;
import static emu.grasscutter.config.Configuration.DISPATCH_INFO;
public class WebStaticVersionResponse implements Handler {
@Override
public void handle(Context ctx) throws IOException {
String requestFor = ctx.path().substring(ctx.path().lastIndexOf("-") + 1);
getPageResources("/webstatic/" + requestFor, ctx);
return;
}
private static void getPageResources(String path, Context ctx) {
try (InputStream filestream = FileUtils.readResourceAsStream(path)) {
ContentType fromExtension = ContentType.getContentTypeByExtension(path.substring(path.lastIndexOf(".") + 1));
@@ -33,4 +25,11 @@ public class WebStaticVersionResponse implements Handler {
ctx.status(404);
}
}
@Override
public void handle(Context ctx) throws IOException {
String requestFor = ctx.path().substring(ctx.path().lastIndexOf("-") + 1);
getPageResources("/webstatic/" + requestFor, ctx);
}
}