Implement proper handbook authentication (pt. 1)

This commit is contained in:
KingRainbow44
2023-05-16 02:38:01 -04:00
Unverified
parent 79d417c3ca
commit f1cf6da178
28 changed files with 1019 additions and 53 deletions
@@ -1,7 +1,5 @@
package emu.grasscutter.server.dispatch;
import static emu.grasscutter.config.Configuration.DISPATCH_INFO;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import emu.grasscutter.Grasscutter;
@@ -12,21 +10,25 @@ import emu.grasscutter.utils.Crypto;
import emu.grasscutter.utils.DispatchUtils;
import emu.grasscutter.utils.JsonUtils;
import emu.grasscutter.utils.objects.HandbookBody;
import java.net.ConnectException;
import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
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.URI;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import static emu.grasscutter.config.Configuration.DISPATCH_INFO;
public final class DispatchClient extends WebSocketClient implements IDispatcher {
@Getter private final Logger logger = Grasscutter.getLogger();
@Getter private final Map<Integer, BiConsumer<WebSocket, JsonElement>> handlers = new HashMap<>();
@@ -41,6 +43,7 @@ public final class DispatchClient extends WebSocketClient implements IDispatcher
this.registerHandler(PacketIds.GachaHistoryReq, this::fetchGachaHistory);
this.registerHandler(PacketIds.GmTalkReq, this::handleHandbookAction);
this.registerHandler(PacketIds.GetPlayerFieldsReq, this::fetchPlayerFields);
}
/**
@@ -105,6 +108,32 @@ public final class DispatchClient extends WebSocketClient implements IDispatcher
this.sendMessage(PacketIds.GmTalkRsp, response);
}
/**
* Fetches the fields of an online player.
*
* @param socket The socket the packet was received from.
* @param object The packet data.
*/
private void fetchPlayerFields(WebSocket socket, JsonElement object) {
var message = IDispatcher.decode(object);
var playerId = message.get("playerId").getAsInt();
var fieldsRaw = message.get("fields").getAsJsonArray();
// Get the player with the specified ID.
var player = Grasscutter.getGameServer().getPlayerByUid(playerId, true);
if (player == null) return;
// Convert the fields array.
var fieldsList = new ArrayList<String>();
for (var field : fieldsRaw)
fieldsList.add(field.getAsString());
var fields = fieldsList.toArray(new String[0]);
// Return the response object.
this.sendMessage(PacketIds.GetPlayerFieldsRsp,
DispatchUtils.getPlayerFields(playerId, fields));
}
/**
* Sends a serialized encrypted message to the server.
*
@@ -1,12 +1,16 @@
package emu.grasscutter.server.dispatch;
import static emu.grasscutter.config.Configuration.DISPATCH_INFO;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.database.DatabaseHelper;
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.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
@@ -15,11 +19,8 @@ import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
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 static emu.grasscutter.config.Configuration.DISPATCH_INFO;
/* Internal communications server. */
public final class DispatchServer extends WebSocketServer implements IDispatcher {
@@ -39,6 +40,7 @@ public final class DispatchServer extends WebSocketServer implements IDispatcher
this.registerHandler(PacketIds.LoginNotify, this::handleLogin);
this.registerHandler(PacketIds.TokenValidateReq, this::validateToken);
this.registerHandler(PacketIds.GetAccountReq, this::fetchAccount);
}
/**
@@ -84,6 +86,23 @@ public final class DispatchServer extends WebSocketServer implements IDispatcher
this.sendMessage(socket, PacketIds.TokenValidateRsp, response);
}
/**
* Fetches an account by its ID.
*
* @param socket The socket the packet was received from.
* @param object The packet data.
*/
private void fetchAccount(WebSocket socket, JsonElement object) {
var message = IDispatcher.decode(object);
var accountId = message.get("accountId").getAsString();
// Get the account from the database.
var account = DatabaseHelper.getAccountById(accountId);
// Send the account.
this.sendMessage(socket, PacketIds.GetAccountRsp,
JSON.toJsonTree(account));
}
/**
* Broadcasts an encrypted message to all connected clients.
*
@@ -1,21 +1,25 @@
package emu.grasscutter.server.dispatch;
import static emu.grasscutter.config.Configuration.DISPATCH_INFO;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import emu.grasscutter.utils.Crypto;
import emu.grasscutter.utils.JsonAdapters.ByteArrayAdapter;
import org.java_websocket.WebSocket;
import org.slf4j.Logger;
import java.nio.charset.StandardCharsets;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import org.java_websocket.WebSocket;
import org.slf4j.Logger;
import java.util.function.Function;
import static emu.grasscutter.config.Configuration.DISPATCH_INFO;
public interface IDispatcher {
Gson JSON =
@@ -24,6 +28,9 @@ public interface IDispatcher {
.registerTypeAdapter(byte[].class, new ByteArrayAdapter())
.create();
Function<JsonElement, JsonObject> DEFAULT_PARSER = (packet) ->
IDispatcher.decode(packet, JsonObject.class);
/**
* Decodes an escaped JSON message.
*
@@ -61,6 +68,75 @@ public interface IDispatcher {
}
}
/**
* Waits for a request from the other server to be fulfilled.
*
* @param request The request data.
* @param requestId The request packet ID.
* @param responseId the response packet ID.
* @param parser The parser for the response data.
* @return The fulfilled data, or null.
* @param <T> The type of data to be returned.
*/
default <T> T await(JsonObject request, int requestId, int responseId,
Function<JsonElement, T> parser) {
// Perform the setup for the request.
var future = this.async(request, requestId, responseId, parser);
try {
// Try to return the value.
return future.get(5L, TimeUnit.SECONDS);
} catch (Exception ignored) {
return null;
}
}
/**
* Registers a callback for a packet to be received.
* Sends a packet with the provided request.
*
* @param request The request object.
* @param requestId The packet ID of the request packet.
* @param responseId The packet ID of the response packet.
* @return A promise containing the parsed JSON data.
*/
default CompletableFuture<JsonObject> async(JsonObject request, int requestId, int responseId) {
return this.async(request, requestId, responseId, DEFAULT_PARSER);
}
/**
* Registers a callback for a packet to be received.
* Sends a packet with the provided request.
*
* @param request The request object.
* @param requestId The packet ID of the request packet.
* @param responseId The packet ID of the response packet.
* @param parser The parser for the received data.
* @return A promise containing the parsed JSON data.
*/
default <T> CompletableFuture<T> async(
JsonObject request, int requestId, int responseId,
Function<JsonElement, T> parser
) {
// Create the future.
var future = new CompletableFuture<T>();
// Listen for the response.
this.registerCallback(responseId, packet ->
future.complete(parser.apply(packet)));
// Broadcast the packet.
this.sendMessage(requestId, request);
return future;
}
/**
* Internally used method to broadcast a packet.
*
* @param packetId The packet ID.
* @param message The packet data.
*/
void sendMessage(int packetId, Object message);
/**
* Decodes a message from the client.
*
@@ -11,4 +11,8 @@ public interface PacketIds {
int GachaHistoryRsp = 5;
int GmTalkReq = PacketOpcodes.GmTalkReq;
int GmTalkRsp = PacketOpcodes.GmTalkRsp;
int GetAccountReq = 6;
int GetAccountRsp = 7;
int GetPlayerFieldsReq = 8;
int GetPlayerFieldsRsp = 9;
}