Merge remote-tracking branch 'origin/unstable' into unstable

This commit is contained in:
KingRainbow44 2023-05-15 03:46:29 -04:00
commit 964cc8143b
No known key found for this signature in database
GPG Key ID: FC2CB64B00D257BE
8 changed files with 91 additions and 114 deletions

View File

@ -1,5 +1,7 @@
package emu.grasscutter.database; package emu.grasscutter.database;
import static com.mongodb.client.model.Filters.eq;
import com.mongodb.client.result.DeleteResult; import com.mongodb.client.result.DeleteResult;
import dev.morphia.query.FindOptions; import dev.morphia.query.FindOptions;
import dev.morphia.query.Sort; import dev.morphia.query.Sort;
@ -20,12 +22,9 @@ import emu.grasscutter.game.mail.Mail;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.quest.GameMainQuest; import emu.grasscutter.game.quest.GameMainQuest;
import emu.grasscutter.game.world.SceneGroupInstance; import emu.grasscutter.game.world.SceneGroupInstance;
import java.util.List; import java.util.List;
import java.util.stream.Stream; import java.util.stream.Stream;
import static com.mongodb.client.model.Filters.eq;
public final class DatabaseHelper { public final class DatabaseHelper {
public static Account createAccount(String username) { public static Account createAccount(String username) {
return createAccountWithUid(username, 0); return createAccountWithUid(username, 0);
@ -219,8 +218,8 @@ public final class DatabaseHelper {
} }
/** /**
* Use {@link DatabaseHelper#getPlayerByAccount(Account, Class)} for creating a real player. * Use {@link DatabaseHelper#getPlayerByAccount(Account, Class)} for creating a real player. This
* This method is used for fetching the player's data. * method is used for fetching the player's data.
* *
* @param accountId The account's ID. * @param accountId The account's ID.
* @return The player. * @return The player.

View File

@ -8,7 +8,6 @@ import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.props.ActionReason; import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.server.packet.send.PacketAddNoGachaAvatarCardNotify; import emu.grasscutter.server.packet.send.PacketAddNoGachaAvatarCardNotify;
import emu.grasscutter.utils.objects.HandbookBody.*; import emu.grasscutter.utils.objects.HandbookBody.*;
import java.util.Objects; import java.util.Objects;
/** Commands executed by the handbook. */ /** Commands executed by the handbook. */
@ -22,8 +21,7 @@ public interface HandbookActions {
static Response grantAvatar(GrantAvatar request) { static Response grantAvatar(GrantAvatar request) {
// Validate the request. // Validate the request.
if (request.getPlayer() == null || request.getAvatar() == null) { if (request.getPlayer() == null || request.getAvatar() == null) {
return Response.builder().status(400) return Response.builder().status(400).message("Invalid request.").build();
.message("Invalid request.").build();
} }
try { try {
@ -37,12 +35,10 @@ public interface HandbookActions {
// Validate the request. // Validate the request.
if (player == null) { if (player == null) {
return Response.builder().status(1) return Response.builder().status(1).message("Player not found.").build();
.message("Player not found.").build();
} }
if (avatarData == null) { if (avatarData == null) {
return Response.builder().status(400) return Response.builder().status(400).message("Invalid avatar ID.").build();
.message("Invalid avatar ID.").build();
} }
// Create the new avatar. // Create the new avatar.
@ -58,17 +54,16 @@ public interface HandbookActions {
// Add the avatar. // Add the avatar.
player.addAvatar(avatar); player.addAvatar(avatar);
player.sendPacket(new PacketAddNoGachaAvatarCardNotify( player.sendPacket(new PacketAddNoGachaAvatarCardNotify(avatar, ActionReason.Gm));
avatar, ActionReason.Gm)); return Response.builder().status(200).message("Avatar granted.").build();
return Response.builder().status(200)
.message("Avatar granted.").build();
} catch (NumberFormatException ignored) { } catch (NumberFormatException ignored) {
return Response.builder().status(500) return Response.builder().status(500).message("Invalid player UID or avatar ID.").build();
.message("Invalid player UID or avatar ID.").build();
} catch (Exception exception) { } catch (Exception exception) {
Grasscutter.getLogger().debug("A handbook command error occurred.", exception); Grasscutter.getLogger().debug("A handbook command error occurred.", exception);
return Response.builder().status(500) return Response.builder()
.message("An error occurred while granting the avatar.").build(); .status(500)
.message("An error occurred while granting the avatar.")
.build();
} }
} }
@ -81,8 +76,7 @@ public interface HandbookActions {
static Response giveItem(GiveItem request) { static Response giveItem(GiveItem request) {
// Validate the request. // Validate the request.
if (request.getPlayer() == null || request.getItem() == null) { if (request.getPlayer() == null || request.getItem() == null) {
return Response.builder().status(400) return Response.builder().status(400).message("Invalid request.").build();
.message("Invalid request.").build();
} }
try { try {
@ -96,12 +90,10 @@ public interface HandbookActions {
// Validate the request. // Validate the request.
if (player == null) { if (player == null) {
return Response.builder().status(1) return Response.builder().status(1).message("Player not found.").build();
.message("Player not found.").build();
} }
if (itemData == null) { if (itemData == null) {
return Response.builder().status(400) return Response.builder().status(400).message("Invalid player UID or item ID.").build();
.message("Invalid player UID or item ID.").build();
} }
// Add the item to the player's inventory. // Add the item to the player's inventory.
@ -122,15 +114,15 @@ public interface HandbookActions {
var itemStack = new GameItem(itemData, (int) amount); var itemStack = new GameItem(itemData, (int) amount);
player.getInventory().addItem(itemStack, ActionReason.Gm); player.getInventory().addItem(itemStack, ActionReason.Gm);
return Response.builder().status(200) return Response.builder().status(200).message("Item granted.").build();
.message("Item granted.").build();
} catch (NumberFormatException ignored) { } catch (NumberFormatException ignored) {
return Response.builder().status(500) return Response.builder().status(500).message("Invalid player UID or item ID.").build();
.message("Invalid player UID or item ID.").build();
} catch (Exception exception) { } catch (Exception exception) {
Grasscutter.getLogger().debug("A handbook command error occurred.", exception); Grasscutter.getLogger().debug("A handbook command error occurred.", exception);
return Response.builder().status(500) return Response.builder()
.message("An error occurred while granting the item.").build(); .status(500)
.message("An error occurred while granting the item.")
.build();
} }
} }
@ -143,8 +135,7 @@ public interface HandbookActions {
static Response teleportTo(TeleportTo request) { static Response teleportTo(TeleportTo request) {
// Validate the request. // Validate the request.
if (request.getPlayer() == null || request.getScene() == null) { if (request.getPlayer() == null || request.getScene() == null) {
return Response.builder().status(400) return Response.builder().status(400).message("Invalid request.").build();
.message("Invalid request.").build();
} }
try { try {
@ -157,15 +148,13 @@ public interface HandbookActions {
// Validate the request. // Validate the request.
if (player == null) { if (player == null) {
return Response.builder().status(1) return Response.builder().status(1).message("Player not found.").build();
.message("Player not found.").build();
} }
// Find the scene in the player's world. // Find the scene in the player's world.
var scene = player.getWorld().getSceneById(sceneId); var scene = player.getWorld().getSceneById(sceneId);
if (scene == null) { if (scene == null) {
return Response.builder().status(400) return Response.builder().status(400).message("Invalid scene ID.").build();
.message("Invalid scene ID.").build();
} }
// Resolve the correct teleport position. // Resolve the correct teleport position.
@ -175,15 +164,15 @@ public interface HandbookActions {
scene.getWorld().transferPlayerToScene(player, scene.getId(), position); scene.getWorld().transferPlayerToScene(player, scene.getId(), position);
player.getRotation().set(rotation); player.getRotation().set(rotation);
return Response.builder().status(200) return Response.builder().status(200).message("Player teleported.").build();
.message("Player teleported.").build();
} catch (NumberFormatException ignored) { } catch (NumberFormatException ignored) {
return Response.builder().status(400) return Response.builder().status(400).message("Invalid player UID or scene ID.").build();
.message("Invalid player UID or scene ID.").build();
} catch (Exception exception) { } catch (Exception exception) {
Grasscutter.getLogger().debug("A handbook command error occurred.", exception); Grasscutter.getLogger().debug("A handbook command error occurred.", exception);
return Response.builder().status(500) return Response.builder()
.message("An error occurred while teleporting to the scene.").build(); .status(500)
.message("An error occurred while teleporting to the scene.")
.build();
} }
} }
@ -196,8 +185,7 @@ public interface HandbookActions {
static Response spawnEntity(SpawnEntity request) { static Response spawnEntity(SpawnEntity request) {
// Validate the request. // Validate the request.
if (request.getPlayer() == null || request.getEntity() == null) { if (request.getPlayer() == null || request.getEntity() == null) {
return Response.builder().status(400) return Response.builder().status(400).message("Invalid request.").build();
.message("Invalid request.").build();
} }
try { try {
@ -211,20 +199,17 @@ public interface HandbookActions {
// Validate the request. // Validate the request.
if (player == null) { if (player == null) {
return Response.builder().status(1) return Response.builder().status(1).message("Player not found.").build();
.message("Player not found.").build();
} }
if (entityData == null) { if (entityData == null) {
return Response.builder().status(400) return Response.builder().status(400).message("Invalid entity ID.").build();
.message("Invalid entity ID.").build();
} }
// Validate request properties. // Validate request properties.
var scene = player.getScene(); var scene = player.getScene();
var level = request.getLevel(); var level = request.getLevel();
if (scene == null || level > 200 || level < 1) { if (scene == null || level > 200 || level < 1) {
return Response.builder().status(400) return Response.builder().status(400).message("Invalid scene or level.").build();
.message("Invalid scene or level.").build();
} }
// Create the entity. // Create the entity.
@ -233,15 +218,15 @@ public interface HandbookActions {
scene.addEntity(entity); scene.addEntity(entity);
} }
return Response.builder().status(200) return Response.builder().status(200).message("Entity(s) spawned.").build();
.message("Entity(s) spawned.").build();
} catch (NumberFormatException ignored) { } catch (NumberFormatException ignored) {
return Response.builder().status(400) return Response.builder().status(400).message("Invalid player UID or entity ID.").build();
.message("Invalid player UID or entity ID.").build();
} catch (Exception exception) { } catch (Exception exception) {
Grasscutter.getLogger().debug("A handbook command error occurred.", exception); Grasscutter.getLogger().debug("A handbook command error occurred.", exception);
return Response.builder().status(500) return Response.builder()
.message("An error occurred while teleporting to the scene.").build(); .status(500)
.message("An error occurred while teleporting to the scene.")
.build();
} }
} }
} }

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;
@ -25,8 +21,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();
@ -89,7 +88,10 @@ public final class DispatchClient extends WebSocketClient implements IDispatcher
var action = HandbookBody.Action.valueOf(actionStr); var action = HandbookBody.Action.valueOf(actionStr);
// Produce a handbook response. // Produce a handbook response.
var response = DispatchUtils.performHandbookAction(action, switch (action) { var response =
DispatchUtils.performHandbookAction(
action,
switch (action) {
case GRANT_AVATAR -> JsonUtils.decode(data, HandbookBody.GrantAvatar.class); case GRANT_AVATAR -> JsonUtils.decode(data, HandbookBody.GrantAvatar.class);
case GIVE_ITEM -> JsonUtils.decode(data, HandbookBody.GiveItem.class); case GIVE_ITEM -> JsonUtils.decode(data, HandbookBody.GiveItem.class);
case TELEPORT_TO -> JsonUtils.decode(data, HandbookBody.TeleportTo.class); case TELEPORT_TO -> JsonUtils.decode(data, HandbookBody.TeleportTo.class);

View File

@ -1,22 +1,21 @@
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;
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 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 =

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.server.http.Router; import emu.grasscutter.server.http.Router;
import emu.grasscutter.utils.DispatchUtils; import emu.grasscutter.utils.DispatchUtils;
import emu.grasscutter.utils.FileUtils; import emu.grasscutter.utils.FileUtils;
@ -8,8 +10,6 @@ import emu.grasscutter.utils.objects.HandbookBody.Action;
import io.javalin.Javalin; import io.javalin.Javalin;
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;
@ -74,11 +74,9 @@ public final class HandbookHandler implements Router {
// Parse the request body into a class. // Parse the request body into a class.
var request = ctx.bodyAsClass(HandbookBody.GrantAvatar.class); var request = ctx.bodyAsClass(HandbookBody.GrantAvatar.class);
// Get the response. // Get the response.
var response = DispatchUtils.performHandbookAction( var response = DispatchUtils.performHandbookAction(Action.GRANT_AVATAR, request);
Action.GRANT_AVATAR, request);
// Send the response. // Send the response.
ctx.status(response.getStatus() > 100 ? ctx.status(response.getStatus() > 100 ? response.getStatus() : 500).json(response);
response.getStatus() : 500).json(response);
} }
/** /**
@ -96,11 +94,9 @@ public final class HandbookHandler implements Router {
// Parse the request body into a class. // Parse the request body into a class.
var request = ctx.bodyAsClass(HandbookBody.GiveItem.class); var request = ctx.bodyAsClass(HandbookBody.GiveItem.class);
// Get the response. // Get the response.
var response = DispatchUtils.performHandbookAction( var response = DispatchUtils.performHandbookAction(Action.GIVE_ITEM, request);
Action.GIVE_ITEM, request);
// Send the response. // Send the response.
ctx.status(response.getStatus() > 100 ? ctx.status(response.getStatus() > 100 ? response.getStatus() : 500).json(response);
response.getStatus() : 500).json(response);
} }
/** /**
@ -118,11 +114,9 @@ public final class HandbookHandler implements Router {
// Parse the request body into a class. // Parse the request body into a class.
var request = ctx.bodyAsClass(HandbookBody.TeleportTo.class); var request = ctx.bodyAsClass(HandbookBody.TeleportTo.class);
// Get the response. // Get the response.
var response = DispatchUtils.performHandbookAction( var response = DispatchUtils.performHandbookAction(Action.TELEPORT_TO, request);
Action.TELEPORT_TO, request);
// Send the response. // Send the response.
ctx.status(response.getStatus() > 100 ? ctx.status(response.getStatus() > 100 ? response.getStatus() : 500).json(response);
response.getStatus() : 500).json(response);
} }
/** /**
@ -138,13 +132,10 @@ public final class HandbookHandler implements Router {
} }
// Parse the request body into a class. // Parse the request body into a class.
var request = ctx.bodyAsClass( var request = ctx.bodyAsClass(HandbookBody.SpawnEntity.class);
HandbookBody.SpawnEntity.class);
// Get the response. // Get the response.
var response = DispatchUtils.performHandbookAction( var response = DispatchUtils.performHandbookAction(Action.SPAWN_ENTITY, request);
Action.SPAWN_ENTITY, request);
// Send the response. // Send the response.
ctx.status(response.getStatus() > 100 ? ctx.status(response.getStatus() > 100 ? response.getStatus() : 500).json(response);
response.getStatus() : 500).json(response);
} }
} }

View File

@ -1,5 +1,7 @@
package emu.grasscutter.server.http.handlers; package emu.grasscutter.server.http.handlers;
import static emu.grasscutter.utils.Language.translate;
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;
@ -11,16 +13,13 @@ import emu.grasscutter.utils.Utils;
import io.javalin.Javalin; 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 lombok.Getter;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Arrays; import java.util.Arrays;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import lombok.Getter;
import static emu.grasscutter.utils.Language.translate;
/** Handles all gacha-related HTTP requests. */ /** Handles all gacha-related HTTP requests. */
public final class GachaHandler implements Router { public final class GachaHandler implements Router {
@ -164,7 +163,6 @@ public final class GachaHandler implements Router {
public void applyRoutes(Javalin javalin) { public void applyRoutes(Javalin javalin) {
javalin.get("/gacha", GachaHandler::gachaRecords); javalin.get("/gacha", GachaHandler::gachaRecords);
javalin.get("/gacha/details", GachaHandler::gachaDetails); javalin.get("/gacha/details", GachaHandler::gachaDetails);
javalin.get("/gacha/mappings", ctx -> javalin.get("/gacha/mappings", ctx -> ctx.result(FileUtils.read(gachaMappingsPath.toString())));
ctx.result(FileUtils.read(gachaMappingsPath.toString())));
} }
} }

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.JsonObject; import com.google.gson.JsonObject;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.auth.AuthenticationSystem.AuthenticationRequest; import emu.grasscutter.auth.AuthenticationSystem.AuthenticationRequest;
@ -12,13 +14,10 @@ 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.net.http.HttpClient; import java.net.http.HttpClient;
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. */
@ -136,8 +135,9 @@ public interface DispatchUtils {
var future = new CompletableFuture<Response>(); var future = new CompletableFuture<Response>();
// Listen for the response. // Listen for the response.
var server = Grasscutter.getDispatchServer(); var server = Grasscutter.getDispatchServer();
server.registerCallback(PacketIds.GmTalkRsp, packet -> server.registerCallback(
future.complete(IDispatcher.decode(packet, Response.class))); PacketIds.GmTalkRsp,
packet -> future.complete(IDispatcher.decode(packet, Response.class)));
// Broadcast the request. // Broadcast the request.
server.sendMessage(PacketIds.GmTalkReq, request); server.sendMessage(PacketIds.GmTalkReq, request);
@ -146,8 +146,10 @@ public interface DispatchUtils {
// Wait for the response. // Wait for the response.
yield future.get(5L, TimeUnit.SECONDS); yield future.get(5L, TimeUnit.SECONDS);
} catch (Exception ignored) { } catch (Exception ignored) {
yield Response.builder().status(400) yield Response.builder()
.message("No response received from any server.").build(); .status(400)
.message("No response received from any server.")
.build();
} }
} }
case HYBRID, GAME_ONLY -> switch (action) { case HYBRID, GAME_ONLY -> switch (action) {

View File

@ -6,7 +6,8 @@ import lombok.Getter;
/** HTTP request object for handbook controls. */ /** HTTP request object for handbook controls. */
@SuppressWarnings("FieldMayBeFinal") @SuppressWarnings("FieldMayBeFinal")
public interface HandbookBody { public interface HandbookBody {
@Builder @Getter @Builder
@Getter
class Response { class Response {
private int status; private int status;
private String message; private String message;