Format code [skip actions]

This commit is contained in:
github-actions 2023-05-16 06:47:42 +00:00
parent 2800cce15a
commit a377fe2107
9 changed files with 90 additions and 104 deletions

View File

@ -4,12 +4,11 @@ import emu.grasscutter.game.Account;
import emu.grasscutter.server.http.objects.*; import emu.grasscutter.server.http.objects.*;
import emu.grasscutter.utils.DispatchUtils; import emu.grasscutter.utils.DispatchUtils;
import io.javalin.http.Context; import io.javalin.http.Context;
import javax.annotation.Nullable;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Builder; import lombok.Builder;
import lombok.Getter; import lombok.Getter;
import javax.annotation.Nullable;
/** Defines an authenticator for the server. Can be changed by plugins. */ /** Defines an authenticator for the server. Can be changed by plugins. */
public interface AuthenticationSystem { public interface AuthenticationSystem {

View File

@ -1,14 +1,14 @@
package emu.grasscutter.auth; package emu.grasscutter.auth;
import static emu.grasscutter.config.Configuration.ACCOUNT;
import static emu.grasscutter.utils.Language.translate;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.auth.DefaultAuthenticators.*; import emu.grasscutter.auth.DefaultAuthenticators.*;
import emu.grasscutter.game.Account; import emu.grasscutter.game.Account;
import emu.grasscutter.server.http.objects.ComboTokenResJson; import emu.grasscutter.server.http.objects.ComboTokenResJson;
import emu.grasscutter.server.http.objects.LoginResultJson; import emu.grasscutter.server.http.objects.LoginResultJson;
import static emu.grasscutter.config.Configuration.ACCOUNT;
import static emu.grasscutter.utils.Language.translate;
/** /**
* The default Grasscutter authentication implementation. Allows all users to access any account. * The default Grasscutter authentication implementation. Allows all users to access any account.
*/ */

View File

@ -1,5 +1,8 @@
package emu.grasscutter.auth; package emu.grasscutter.auth;
import static emu.grasscutter.config.Configuration.ACCOUNT;
import static emu.grasscutter.utils.Language.translate;
import at.favre.lib.crypto.bcrypt.BCrypt; import at.favre.lib.crypto.bcrypt.BCrypt;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.auth.AuthenticationSystem.AuthenticationRequest; import emu.grasscutter.auth.AuthenticationSystem.AuthenticationRequest;
@ -13,17 +16,13 @@ import emu.grasscutter.utils.DispatchUtils;
import emu.grasscutter.utils.FileUtils; import emu.grasscutter.utils.FileUtils;
import emu.grasscutter.utils.Utils; import emu.grasscutter.utils.Utils;
import io.javalin.http.ContentType; import io.javalin.http.ContentType;
import javax.crypto.Cipher;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.security.KeyFactory; import java.security.KeyFactory;
import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPrivateKey;
import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.PKCS8EncodedKeySpec;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.crypto.Cipher;
import static emu.grasscutter.config.Configuration.ACCOUNT;
import static emu.grasscutter.utils.Language.translate;
/** A class containing default authenticators. */ /** A class containing default authenticators. */
public final class DefaultAuthenticators { public final class DefaultAuthenticators {
@ -382,8 +381,7 @@ public final class DefaultAuthenticators {
public HandbookAuthentication() { public HandbookAuthentication() {
try { try {
this.authPage = new String( this.authPage = new String(FileUtils.readResource("/html/handbook_auth.html"));
FileUtils.readResource("/html/handbook_auth.html"));
} catch (Exception ignored) { } catch (Exception ignored) {
throw new RuntimeException("Failed to load handbook auth page."); throw new RuntimeException("Failed to load handbook auth page.");
} }
@ -395,8 +393,7 @@ public final class DefaultAuthenticators {
if (ctx == null) return; if (ctx == null) return;
// Respond with the handbook auth page. // Respond with the handbook auth page.
ctx.contentType(ContentType.TEXT_HTML) ctx.contentType(ContentType.TEXT_HTML).result(this.authPage);
.result(this.authPage);
} }
@Override @Override
@ -407,28 +404,27 @@ public final class DefaultAuthenticators {
// Get the body data. // Get the body data.
var playerId = ctx.formParam("playerid"); var playerId = ctx.formParam("playerid");
if (playerId == null) { if (playerId == null) {
return Response.builder().status(400) return Response.builder().status(400).body("Invalid player ID.").build();
.body("Invalid player ID.").build();
} }
try { try {
// Get the player's session token. // Get the player's session token.
var sessionKey = DispatchUtils.fetchSessionKey( var sessionKey = DispatchUtils.fetchSessionKey(Integer.parseInt(playerId));
Integer.parseInt(playerId));
if (sessionKey == null) { if (sessionKey == null) {
return Response.builder().status(400) return Response.builder().status(400).body("Invalid player ID.").build();
.body("Invalid player ID.").build();
} }
// Check if the account is banned. // Check if the account is banned.
return Response.builder().status(200) return Response.builder()
.body(this.authPage.replace("{{VALUE}}", "true") .status(200)
.body(
this.authPage
.replace("{{VALUE}}", "true")
.replace("{{SESSION_TOKEN}}", sessionKey) .replace("{{SESSION_TOKEN}}", sessionKey)
.replace("{{PLAYER_ID}}", playerId)) .replace("{{PLAYER_ID}}", playerId))
.build(); .build();
} catch (NumberFormatException ignored) { } catch (NumberFormatException ignored) {
return Response.builder().status(500) return Response.builder().status(500).body("Invalid player ID.").build();
.body("Invalid player ID.").build();
} }
} }
} }

View File

@ -6,15 +6,16 @@ import lombok.Getter;
/** Handles player authentication for the web GM handbook. */ /** Handles player authentication for the web GM handbook. */
public interface HandbookAuthenticator { public interface HandbookAuthenticator {
@Getter @Builder @Getter
@Builder
class Response { class Response {
private final int status; private final int status;
private final String body; private final String body;
} }
/** /**
* Invoked when the user requests to authenticate. * Invoked when the user requests to authenticate. This should respond with a page that allows the
* This should respond with a page that allows the user to authenticate. * user to authenticate.
* *
* @route GET /handbook/authenticate * @route GET /handbook/authenticate
* @param request The authentication request. * @param request The authentication request.
@ -22,10 +23,9 @@ public interface HandbookAuthenticator {
void presentPage(AuthenticationRequest request); void presentPage(AuthenticationRequest request);
/** /**
* Invoked when the user requests to authenticate. * Invoked when the user requests to authenticate. This is called when the user submits the
* This is called when the user submits the authentication form. * authentication form. This should respond with HTML that sends a message to the GM Handbook. See
* This should respond with HTML that sends a message to the GM Handbook. * the default handbook authentication page for an example.
* See the default handbook authentication page for an example.
* *
* @param request The authentication request. * @param request The authentication request.
* @return The response to send to the client. * @return The response to send to the client.

View File

@ -1,5 +1,7 @@
package emu.grasscutter.server.dispatch; package emu.grasscutter.server.dispatch;
import static emu.grasscutter.config.Configuration.DISPATCH_INFO;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
@ -10,12 +12,6 @@ import emu.grasscutter.utils.Crypto;
import emu.grasscutter.utils.DispatchUtils; import emu.grasscutter.utils.DispatchUtils;
import emu.grasscutter.utils.JsonUtils; import emu.grasscutter.utils.JsonUtils;
import emu.grasscutter.utils.objects.HandbookBody; import emu.grasscutter.utils.objects.HandbookBody;
import lombok.Getter;
import org.java_websocket.WebSocket;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
import org.slf4j.Logger;
import java.net.ConnectException; import java.net.ConnectException;
import java.net.URI; import java.net.URI;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@ -26,8 +22,11 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.function.Consumer; import java.util.function.Consumer;
import lombok.Getter;
import static emu.grasscutter.config.Configuration.DISPATCH_INFO; import org.java_websocket.WebSocket;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
import org.slf4j.Logger;
public final class DispatchClient extends WebSocketClient implements IDispatcher { public final class DispatchClient extends WebSocketClient implements IDispatcher {
@Getter private final Logger logger = Grasscutter.getLogger(); @Getter private final Logger logger = Grasscutter.getLogger();
@ -125,13 +124,11 @@ public final class DispatchClient extends WebSocketClient implements IDispatcher
// Convert the fields array. // Convert the fields array.
var fieldsList = new ArrayList<String>(); var fieldsList = new ArrayList<String>();
for (var field : fieldsRaw) for (var field : fieldsRaw) fieldsList.add(field.getAsString());
fieldsList.add(field.getAsString());
var fields = fieldsList.toArray(new String[0]); var fields = fieldsList.toArray(new String[0]);
// Return the response object. // Return the response object.
this.sendMessage(PacketIds.GetPlayerFieldsRsp, this.sendMessage(PacketIds.GetPlayerFieldsRsp, DispatchUtils.getPlayerFields(playerId, fields));
DispatchUtils.getPlayerFields(playerId, fields));
} }
/** /**

View File

@ -1,16 +1,12 @@
package emu.grasscutter.server.dispatch; package emu.grasscutter.server.dispatch;
import static emu.grasscutter.config.Configuration.DISPATCH_INFO;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.database.DatabaseHelper; import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.utils.Crypto; import emu.grasscutter.utils.Crypto;
import lombok.Getter;
import org.java_websocket.WebSocket;
import org.java_websocket.handshake.ClientHandshake;
import org.java_websocket.server.WebSocketServer;
import org.slf4j.Logger;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
@ -19,8 +15,11 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.function.Consumer; import java.util.function.Consumer;
import lombok.Getter;
import static emu.grasscutter.config.Configuration.DISPATCH_INFO; import org.java_websocket.WebSocket;
import org.java_websocket.handshake.ClientHandshake;
import org.java_websocket.server.WebSocketServer;
import org.slf4j.Logger;
/* Internal communications server. */ /* Internal communications server. */
public final class DispatchServer extends WebSocketServer implements IDispatcher { public final class DispatchServer extends WebSocketServer implements IDispatcher {
@ -99,8 +98,7 @@ public final class DispatchServer extends WebSocketServer implements IDispatcher
// Get the account from the database. // Get the account from the database.
var account = DatabaseHelper.getAccountById(accountId); var account = DatabaseHelper.getAccountById(accountId);
// Send the account. // Send the account.
this.sendMessage(socket, PacketIds.GetAccountRsp, this.sendMessage(socket, PacketIds.GetAccountRsp, JSON.toJsonTree(account));
JSON.toJsonTree(account));
} }
/** /**

View File

@ -1,14 +1,13 @@
package emu.grasscutter.server.dispatch; package emu.grasscutter.server.dispatch;
import static emu.grasscutter.config.Configuration.DISPATCH_INFO;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import emu.grasscutter.utils.Crypto; import emu.grasscutter.utils.Crypto;
import emu.grasscutter.utils.JsonAdapters.ByteArrayAdapter; import emu.grasscutter.utils.JsonAdapters.ByteArrayAdapter;
import org.java_websocket.WebSocket;
import org.slf4j.Logger;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@ -18,8 +17,8 @@ import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
import org.java_websocket.WebSocket;
import static emu.grasscutter.config.Configuration.DISPATCH_INFO; import org.slf4j.Logger;
public interface IDispatcher { public interface IDispatcher {
Gson JSON = Gson JSON =
@ -28,8 +27,8 @@ public interface IDispatcher {
.registerTypeAdapter(byte[].class, new ByteArrayAdapter()) .registerTypeAdapter(byte[].class, new ByteArrayAdapter())
.create(); .create();
Function<JsonElement, JsonObject> DEFAULT_PARSER = (packet) -> Function<JsonElement, JsonObject> DEFAULT_PARSER =
IDispatcher.decode(packet, JsonObject.class); (packet) -> IDispatcher.decode(packet, JsonObject.class);
/** /**
* Decodes an escaped JSON message. * Decodes an escaped JSON message.
@ -78,8 +77,8 @@ public interface IDispatcher {
* @return The fulfilled data, or null. * @return The fulfilled data, or null.
* @param <T> The type of data to be returned. * @param <T> The type of data to be returned.
*/ */
default <T> T await(JsonObject request, int requestId, int responseId, default <T> T await(
Function<JsonElement, T> parser) { JsonObject request, int requestId, int responseId, Function<JsonElement, T> parser) {
// Perform the setup for the request. // Perform the setup for the request.
var future = this.async(request, requestId, responseId, parser); var future = this.async(request, requestId, responseId, parser);
@ -92,8 +91,7 @@ public interface IDispatcher {
} }
/** /**
* Registers a callback for a packet to be received. * Registers a callback for a packet to be received. Sends a packet with the provided request.
* Sends a packet with the provided request.
* *
* @param request The request object. * @param request The request object.
* @param requestId The packet ID of the request packet. * @param requestId The packet ID of the request packet.
@ -105,8 +103,7 @@ public interface IDispatcher {
} }
/** /**
* Registers a callback for a packet to be received. * Registers a callback for a packet to be received. Sends a packet with the provided request.
* Sends a packet with the provided request.
* *
* @param request The request object. * @param request The request object.
* @param requestId The packet ID of the request packet. * @param requestId The packet ID of the request packet.
@ -115,14 +112,11 @@ public interface IDispatcher {
* @return A promise containing the parsed JSON data. * @return A promise containing the parsed JSON data.
*/ */
default <T> CompletableFuture<T> async( default <T> CompletableFuture<T> async(
JsonObject request, int requestId, int responseId, JsonObject request, int requestId, int responseId, Function<JsonElement, T> parser) {
Function<JsonElement, T> parser
) {
// Create the future. // Create the future.
var future = new CompletableFuture<T>(); var future = new CompletableFuture<T>();
// Listen for the response. // Listen for the response.
this.registerCallback(responseId, packet -> this.registerCallback(responseId, packet -> future.complete(parser.apply(packet)));
future.complete(parser.apply(packet)));
// Broadcast the packet. // Broadcast the packet.
this.sendMessage(requestId, request); this.sendMessage(requestId, request);

View File

@ -1,5 +1,7 @@
package emu.grasscutter.server.http.documentation; package emu.grasscutter.server.http.documentation;
import static emu.grasscutter.config.Configuration.HANDBOOK;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.auth.AuthenticationSystem.AuthenticationRequest; import emu.grasscutter.auth.AuthenticationSystem.AuthenticationRequest;
import emu.grasscutter.server.http.Router; import emu.grasscutter.server.http.Router;
@ -11,8 +13,6 @@ import io.javalin.Javalin;
import io.javalin.http.ContentType; import io.javalin.http.ContentType;
import io.javalin.http.Context; import io.javalin.http.Context;
import static emu.grasscutter.config.Configuration.HANDBOOK;
/** Handles requests for the new GM Handbook. */ /** Handles requests for the new GM Handbook. */
public final class HandbookHandler implements Router { public final class HandbookHandler implements Router {
private final byte[] handbook; private final byte[] handbook;
@ -77,8 +77,8 @@ public final class HandbookHandler implements Router {
} else { } else {
// Pass the request to the authenticator. // Pass the request to the authenticator.
Grasscutter.getAuthenticationSystem() Grasscutter.getAuthenticationSystem()
.getHandbookAuthenticator().presentPage( .getHandbookAuthenticator()
AuthenticationRequest.builder().context(ctx).build()); .presentPage(AuthenticationRequest.builder().context(ctx).build());
} }
} }
@ -93,17 +93,17 @@ public final class HandbookHandler implements Router {
ctx.status(500).result("Handbook not found."); ctx.status(500).result("Handbook not found.");
} else { } else {
// Pass the request to the authenticator. // Pass the request to the authenticator.
var result = Grasscutter.getAuthenticationSystem() var result =
.getHandbookAuthenticator().authenticate( Grasscutter.getAuthenticationSystem()
AuthenticationRequest.builder().context(ctx).build()); .getHandbookAuthenticator()
.authenticate(AuthenticationRequest.builder().context(ctx).build());
if (result == null) { if (result == null) {
ctx.status(500).result("Authentication failed."); ctx.status(500).result("Authentication failed.");
} else { } else {
ctx ctx.status(result.getStatus())
.status(result.getStatus())
.result(result.getBody()) .result(result.getBody())
.contentType(result.getBody().contains("html") ? .contentType(
ContentType.TEXT_HTML : ContentType.TEXT_PLAIN); result.getBody().contains("html") ? ContentType.TEXT_HTML : ContentType.TEXT_PLAIN);
} }
} }
} }

View File

@ -1,5 +1,7 @@
package emu.grasscutter.utils; package emu.grasscutter.utils;
import static emu.grasscutter.config.Configuration.DISPATCH_INFO;
import com.google.gson.JsonNull; import com.google.gson.JsonNull;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
@ -13,16 +15,13 @@ import emu.grasscutter.server.http.handlers.GachaHandler;
import emu.grasscutter.server.http.objects.LoginTokenRequestJson; import emu.grasscutter.server.http.objects.LoginTokenRequestJson;
import emu.grasscutter.utils.objects.HandbookBody; import emu.grasscutter.utils.objects.HandbookBody;
import emu.grasscutter.utils.objects.HandbookBody.*; import emu.grasscutter.utils.objects.HandbookBody.*;
import javax.annotation.Nullable;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.net.http.HttpClient; import java.net.http.HttpClient;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
import static emu.grasscutter.config.Configuration.DISPATCH_INFO;
public interface DispatchUtils { public interface DispatchUtils {
/** HTTP client used for dispatch queries. */ /** HTTP client used for dispatch queries. */
@ -128,12 +127,15 @@ public interface DispatchUtils {
request.addProperty("accountId", accountId); request.addProperty("accountId", accountId);
// Wait for the request to complete. // Wait for the request to complete.
yield Grasscutter.getGameServer().getDispatchClient() yield Grasscutter.getGameServer()
.await(request, PacketIds.GetAccountReq, PacketIds.GetAccountRsp, .getDispatchClient()
.await(
request,
PacketIds.GetAccountReq,
PacketIds.GetAccountRsp,
packet -> IDispatcher.decode(packet, Account.class)); packet -> IDispatcher.decode(packet, Account.class));
} }
case HYBRID, DISPATCH_ONLY -> case HYBRID, DISPATCH_ONLY -> DatabaseHelper.getAccountById(accountId);
DatabaseHelper.getAccountById(accountId);
}; };
} }
@ -154,13 +156,15 @@ public interface DispatchUtils {
// Wait for the request to complete. // Wait for the request to complete.
yield Grasscutter.getDispatchServer() yield Grasscutter.getDispatchServer()
.await(request, PacketIds.GetPlayerFieldsReq, PacketIds.GetPlayerFieldsRsp, .await(
request,
PacketIds.GetPlayerFieldsReq,
PacketIds.GetPlayerFieldsRsp,
IDispatcher.DEFAULT_PARSER); IDispatcher.DEFAULT_PARSER);
} }
case HYBRID, GAME_ONLY -> { case HYBRID, GAME_ONLY -> {
// Get the player by ID. // Get the player by ID.
var player = Grasscutter.getGameServer() var player = Grasscutter.getGameServer().getPlayerByUid(playerId, true);
.getPlayerByUid(playerId, true);
if (player == null) yield null; if (player == null) yield null;
// Prepare field properties. // Prepare field properties.
@ -173,13 +177,11 @@ public interface DispatchUtils {
for (var fieldName : fields) { for (var fieldName : fields) {
try { try {
var field = fieldMap.get(fieldName); var field = fieldMap.get(fieldName);
if (field == null) if (field == null) fieldValues.add(fieldName, JsonNull.INSTANCE);
fieldValues.add(fieldName, JsonNull.INSTANCE);
else { else {
var wasAccessible = field.canAccess(player); var wasAccessible = field.canAccess(player);
field.setAccessible(true); field.setAccessible(true);
fieldValues.add(fieldName, fieldValues.add(fieldName, IDispatcher.JSON.toJsonTree(field.get(player)));
IDispatcher.JSON.toJsonTree(field.get(player)));
field.setAccessible(wasAccessible); field.setAccessible(wasAccessible);
} }
} catch (Exception exception) { } catch (Exception exception) {