From d050407421e56431fb814787c82410802f62ed44 Mon Sep 17 00:00:00 2001 From: mikuyourworld <121100193@qq.com> Date: Sun, 1 May 2022 15:07:58 +0900 Subject: [PATCH 01/11] Update readme because the commad /clearartifacts and /clearweapons have been merged in this PR https://github.com/Grasscutters/Grasscutter/pull/245 --- README.md | 3 +-- README_zh-CN.md | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 623baf68b..dc87ff1d8 100644 --- a/README.md +++ b/README.md @@ -110,8 +110,7 @@ There is a dummy user named "Server" in every player's friends list that you can | broadcast | broadcast \ | server.broadcast | Both side | Sends a message to all the players. | b | | coop | coop \ \ | server.coop | Both side | Forces someone to join the world of others. | | | changescene | changescene \ | player.changescene | Client only | Switch scenes by scene ID. | scene | -| clearartifacts | clearartifacts | player.clearartifacts | Client only | Deletes all unequipped and unlocked level 0 artifacts, including 5-star rarity ones from your inventory. | clearart | -| clearweapons | clearweapons | player.clearweapons | Client only | Deletes all unequipped and unlocked weapons, including 5-star rarity ones from your inventory. | clearwpns | +| clear | clear [UID] | player.clearinv | Client only | Deletes all unequipped and unlocked level 0 artifacts(art)/weapons(wp)/material(all) or all, including 5-star rarity ones from your inventory. | clear | | drop | drop [amount] | server.drop | Client only | Drops an item around you. | `d` `dropitem` | | give | give [player] [amount] [level] [finement] | player.give | Both side | Gives item(s) to you or the specified player. (finement option only weapon.) | `g` `item` `giveitem` | | givechar | givechar \ \ | player.givechar | Both side | Gives the player a specified character. | givec | diff --git a/README_zh-CN.md b/README_zh-CN.md index 2558d52fc..9a58b9565 100644 --- a/README_zh-CN.md +++ b/README_zh-CN.md @@ -111,8 +111,7 @@ chmod +x gradlew | broadcast | broadcast <消息内容> | server.broadcast | 均可使用 | 给所有玩家发送公告 | b | | coop | coop \ <目标uid> | server.coop | 均可使用 | 强制某位玩家进入指定玩家的多人世界 | | | changescene | changescene <场景ID> | player.changescene | 仅客户端 | 切换到指定场景 | scene | -| clearartifacts | clearartifacts | player.clearartifacts | 仅客户端 | 删除所有未装备及未解锁的圣遗物,包括五星 | clearart | -| clearweapons | clearweapons | player.clearweapons | 仅客户端 | 删除所有未装备及未解锁的武器,包括五星 | clearwp | +| clear | clear [UID] | player.clearinv | 仅客户端 | 删除所有未装备及未解锁的圣遗物(art)或武器(wp)或材料(mat)或者所有(all),包括五星 | clear | | drop | drop <物品ID\|物品名称> [数量] | server.drop | 仅客户端 | 在指定玩家周围掉落指定物品 | `d` `dropitem` | | give | give [uid] <物品ID\|物品名称> [数量] [等级] [精炼等级] | | | 给予指定玩家一定数量及等级的物品 (精炼等级仅适用于武器) | `g` `item` `giveitem` | | givechar | givechar \ <角色ID> [等级] | player.givechar | 均可使用 | 给予指定玩家对应角色 | givec | From d484ba7ed6f3592d879e36ea2a370f4ddc4d7c4f Mon Sep 17 00:00:00 2001 From: 4Benj_ Date: Sun, 1 May 2022 14:31:39 +0800 Subject: [PATCH 02/11] Cleaned up dispatch iOS fixes (#396) * Attempting to fix crashing on iOS devices plus I forgot a thing in string.format * Removed unnecessary things --- src/main/java/emu/grasscutter/game/Account.java | 6 +++++- .../server/dispatch/DispatchHttpJsonHandler.java | 2 +- .../emu/grasscutter/server/dispatch/DispatchServer.java | 9 --------- .../server/dispatch/json/LoginResultJson.java | 2 +- 4 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/main/java/emu/grasscutter/game/Account.java b/src/main/java/emu/grasscutter/game/Account.java index 7ccaf7e7e..5b8523ec3 100644 --- a/src/main/java/emu/grasscutter/game/Account.java +++ b/src/main/java/emu/grasscutter/game/Account.java @@ -74,7 +74,11 @@ public class Account { } public String getEmail() { - return email; + if(email != null && !email.isEmpty()) { + return email; + } else { + return ""; + } } public void setEmail(String email) { diff --git a/src/main/java/emu/grasscutter/server/dispatch/DispatchHttpJsonHandler.java b/src/main/java/emu/grasscutter/server/dispatch/DispatchHttpJsonHandler.java index 31abc77ae..58d7207c5 100644 --- a/src/main/java/emu/grasscutter/server/dispatch/DispatchHttpJsonHandler.java +++ b/src/main/java/emu/grasscutter/server/dispatch/DispatchHttpJsonHandler.java @@ -36,7 +36,7 @@ public final class DispatchHttpJsonHandler implements HttpContextHandler { public void handle(Request req, Response res) throws IOException { // Checking for ALL here isn't required as when ALL is enabled enableDevLogging() gets enabled if(Grasscutter.getConfig().DebugMode == ServerDebugMode.MISSING && Arrays.stream(missingRoutes).anyMatch(x -> x == req.baseUrl())) { - Grasscutter.getLogger().info(String.format("[Dispatch] Client %s %s request: ", req.ip(), req.method(), req.baseUrl()) + (Grasscutter.getConfig().DebugMode == ServerDebugMode.MISSING ? "(MISSING)" : "")); + Grasscutter.getLogger().info(String.format("[Dispatch] Client %s %s request: %s", req.ip(), req.method(), req.baseUrl()) + (Grasscutter.getConfig().DebugMode == ServerDebugMode.MISSING ? "(MISSING)" : "")); } res.send(response); } diff --git a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java index 1a59cb9ad..b7e1e34ee 100644 --- a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java +++ b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java @@ -319,9 +319,6 @@ public final class DispatchServer { responseData.data.account.uid = account.getId(); responseData.data.account.token = account.generateSessionKey(); responseData.data.account.email = account.getEmail(); - if (responseData.data.account.email == null) { - responseData.data.account.email = ""; - } Grasscutter.getLogger() .info(String.format("[Dispatch] Client %s failed to log in: Account %s created", @@ -346,9 +343,6 @@ public final class DispatchServer { responseData.data.account.uid = account.getId(); responseData.data.account.token = account.generateSessionKey(); responseData.data.account.email = account.getEmail(); - if (responseData.data.account.email == null) { - responseData.data.account.email = ""; - } Grasscutter.getLogger().info(String.format("[Dispatch] Client %s logged in as %s", req.ip(), responseData.data.account.uid)); @@ -389,9 +383,6 @@ public final class DispatchServer { responseData.data.account.uid = requestData.uid; responseData.data.account.token = requestData.token; responseData.data.account.email = account.getEmail(); - if (responseData.data.account.email == null) { // null will trigger crash in some client - responseData.data.account.email = ""; - } Grasscutter.getLogger().info(String.format("[Dispatch] Client %s logged in via token as %s", req.ip(), responseData.data.account.uid)); diff --git a/src/main/java/emu/grasscutter/server/dispatch/json/LoginResultJson.java b/src/main/java/emu/grasscutter/server/dispatch/json/LoginResultJson.java index 88e142d4f..1f4dcd4b4 100644 --- a/src/main/java/emu/grasscutter/server/dispatch/json/LoginResultJson.java +++ b/src/main/java/emu/grasscutter/server/dispatch/json/LoginResultJson.java @@ -16,7 +16,7 @@ public class LoginResultJson { public static class VerifyAccountData { public String uid; public String name = ""; - public String email; + public String email = ""; public String mobile = ""; public String is_email_verify = "0"; public String realname = ""; From ec09bc28f2c55a3babfcedec69a233504af64a3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=AD=B1=E5=82=91?= Date: Sun, 1 May 2022 22:42:02 +0800 Subject: [PATCH 03/11] Fixed can set talent level to 16 bug (#408) Level should be **lower than 16** --- .../java/emu/grasscutter/command/commands/TalentCommand.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/emu/grasscutter/command/commands/TalentCommand.java b/src/main/java/emu/grasscutter/command/commands/TalentCommand.java index f0e779700..32e2f9ee8 100644 --- a/src/main/java/emu/grasscutter/command/commands/TalentCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/TalentCommand.java @@ -53,7 +53,7 @@ public final class TalentCommand implements CommandHandler { CommandHandler.sendMessage(sender, "To get talent ID: /talent getid"); return; } - if (nextLevel > 16){ + if (nextLevel >= 16){ CommandHandler.sendMessage(sender, "Invalid talent level. Level should be lower than 16"); return; } @@ -117,7 +117,7 @@ public final class TalentCommand implements CommandHandler { CommandHandler.sendMessage(sender, "To set talent level: /talent "); return; } - if (nextLevel > 16){ + if (nextLevel >= 16){ CommandHandler.sendMessage(sender, "Invalid talent level. Level should be lower than 16"); return; } From d4e1b265e31f16780897152250ab9df6076983cc Mon Sep 17 00:00:00 2001 From: Kinesis Date: Sun, 1 May 2022 16:24:23 +0800 Subject: [PATCH 04/11] fix McoinExchangeHcoinRsp packet structure --- proto/McoinExchangeHcoinRsp.proto | 3 +-- .../packet/recv/HandlerMcoinExchangeHcoinReq.java | 5 +++-- .../packet/send/PacketMcoinExchangeHcoinRsp.java | 10 +++++----- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/proto/McoinExchangeHcoinRsp.proto b/proto/McoinExchangeHcoinRsp.proto index b1904885b..090341308 100644 --- a/proto/McoinExchangeHcoinRsp.proto +++ b/proto/McoinExchangeHcoinRsp.proto @@ -3,6 +3,5 @@ syntax = "proto3"; option java_package = "emu.grasscutter.net.proto"; message McoinExchangeHcoinRsp { - uint32 mCoinNum = 1; - uint32 hCoinNum = 2; + int32 retcode = 1; } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerMcoinExchangeHcoinReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerMcoinExchangeHcoinReq.java index 57ecaf42b..37dc0fcc7 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerMcoinExchangeHcoinReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerMcoinExchangeHcoinReq.java @@ -4,6 +4,7 @@ import emu.grasscutter.net.packet.Opcodes; import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.proto.McoinExchangeHcoinReqOuterClass; +import emu.grasscutter.net.proto.RetcodeOuterClass; import emu.grasscutter.server.game.GameSession; import emu.grasscutter.server.packet.send.PacketMcoinExchangeHcoinRsp; @@ -15,13 +16,13 @@ public class HandlerMcoinExchangeHcoinReq extends PacketHandler { McoinExchangeHcoinReqOuterClass.McoinExchangeHcoinReq exchangeReq = McoinExchangeHcoinReqOuterClass.McoinExchangeHcoinReq.parseFrom(payload); if (session.getPlayer().getCrystals() < exchangeReq.getMCoinNum() && exchangeReq.getMCoinNum() == exchangeReq.getHCoinNum()) { + session.send(new PacketMcoinExchangeHcoinRsp(RetcodeOuterClass.Retcode.RET_UNKNOWN_ERROR_VALUE)); return; } session.getPlayer().setCrystals(session.getPlayer().getCrystals() - exchangeReq.getMCoinNum()); session.getPlayer().setPrimogems(session.getPlayer().getPrimogems() + exchangeReq.getHCoinNum()); session.getPlayer().save(); - - session.send(new PacketMcoinExchangeHcoinRsp(session.getPlayer().getCrystals(), session.getPlayer().getPrimogems())); + session.send(new PacketMcoinExchangeHcoinRsp(RetcodeOuterClass.Retcode.RET_SUCC_VALUE)); } } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketMcoinExchangeHcoinRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketMcoinExchangeHcoinRsp.java index 65bef7b27..a37c12505 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketMcoinExchangeHcoinRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketMcoinExchangeHcoinRsp.java @@ -6,13 +6,13 @@ import emu.grasscutter.net.proto.McoinExchangeHcoinRspOuterClass; public class PacketMcoinExchangeHcoinRsp extends BasePacket { - public PacketMcoinExchangeHcoinRsp(int mcoin, int hcoin) { + public PacketMcoinExchangeHcoinRsp(int retCode) { super(PacketOpcodes.McoinExchangeHcoinRsp); - McoinExchangeHcoinRspOuterClass.McoinExchangeHcoinRsp mcoinExchangeHcoinRsp = McoinExchangeHcoinRspOuterClass.McoinExchangeHcoinRsp.newBuilder() - .setMCoinNum(mcoin) - .setHCoinNum(hcoin).build(); + McoinExchangeHcoinRspOuterClass.McoinExchangeHcoinRsp proto = McoinExchangeHcoinRspOuterClass.McoinExchangeHcoinRsp.newBuilder() + .setRetcode(retCode) + .build(); - this.setData(mcoinExchangeHcoinRsp); + this.setData(proto); } } From 59d5f4feec2242eb68d768b1cf9f27aa3fd3b1e9 Mon Sep 17 00:00:00 2001 From: coooookies <1164557342@qq.com> Date: Sun, 1 May 2022 18:41:51 +0800 Subject: [PATCH 05/11] GameServerPacketHandler need to be added a registration interface for plugin developers --- .../grasscutter/server/game/GameServerPacketHandler.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/emu/grasscutter/server/game/GameServerPacketHandler.java b/src/main/java/emu/grasscutter/server/game/GameServerPacketHandler.java index 89fa71481..dbead5240 100644 --- a/src/main/java/emu/grasscutter/server/game/GameServerPacketHandler.java +++ b/src/main/java/emu/grasscutter/server/game/GameServerPacketHandler.java @@ -22,7 +22,11 @@ public class GameServerPacketHandler { this.registerHandlers(handlerClass); } - + + public void registerPacketHandler(int opcode, PacketHandler handler) { + this.handlers.put(opcode, handler); + } + public void registerHandlers(Class handlerClass) { Reflections reflections = new Reflections("emu.grasscutter.server.packet"); Set handlerClasses = reflections.getSubTypesOf(handlerClass); From 1e166960d2cfdf6f94a64b5b4ae14626ddd99c15 Mon Sep 17 00:00:00 2001 From: coooookies <1164557342@qq.com> Date: Sun, 1 May 2022 20:47:54 +0800 Subject: [PATCH 06/11] Improve registration methods. --- .../server/game/GameServerPacketHandler.java | 32 +++++++++---------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/src/main/java/emu/grasscutter/server/game/GameServerPacketHandler.java b/src/main/java/emu/grasscutter/server/game/GameServerPacketHandler.java index dbead5240..35303b639 100644 --- a/src/main/java/emu/grasscutter/server/game/GameServerPacketHandler.java +++ b/src/main/java/emu/grasscutter/server/game/GameServerPacketHandler.java @@ -23,8 +23,20 @@ public class GameServerPacketHandler { this.registerHandlers(handlerClass); } - public void registerPacketHandler(int opcode, PacketHandler handler) { - this.handlers.put(opcode, handler); + public void registerPacketHandler(Class handlerClass) { + try { + Opcodes opcode = handlerClass.getAnnotation(Opcodes.class); + + if (opcode == null || opcode.disabled() || opcode.value() <= 0) { + return; + } + + PacketHandler packetHandler = (PacketHandler) handlerClass.newInstance(); + + this.handlers.put(opcode.value(), packetHandler); + } catch (Exception e) { + e.printStackTrace(); + } } public void registerHandlers(Class handlerClass) { @@ -32,21 +44,7 @@ public class GameServerPacketHandler { Set handlerClasses = reflections.getSubTypesOf(handlerClass); for (Object obj : handlerClasses) { - Class c = (Class) obj; - - try { - Opcodes opcode = c.getAnnotation(Opcodes.class); - - if (opcode == null || opcode.disabled() || opcode.value() <= 0) { - continue; - } - - PacketHandler packetHandler = (PacketHandler) c.newInstance(); - - this.handlers.put(opcode.value(), packetHandler); - } catch (Exception e) { - e.printStackTrace(); - } + this.registerPacketHandler((Class) obj); } // Debug From 2c0576f697a249ba028085af946a33576a0df7ec Mon Sep 17 00:00:00 2001 From: JimWails <1142247734@qq.com> Date: Sun, 1 May 2022 23:02:18 +0800 Subject: [PATCH 07/11] Fixed can set avatar level more than 90 and cause game to freeze Limit the avatar level given by "/givechar" command. If avatar level >90, the game will freeze if open the character interface. --- .../emu/grasscutter/command/commands/GiveCharCommand.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/emu/grasscutter/command/commands/GiveCharCommand.java b/src/main/java/emu/grasscutter/command/commands/GiveCharCommand.java index a15a9b95c..70f051ef4 100644 --- a/src/main/java/emu/grasscutter/command/commands/GiveCharCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/GiveCharCommand.java @@ -73,6 +73,12 @@ public final class GiveCharCommand implements CommandHandler { return; } + // Check level. + if (level > 90) { + CommandHandler.sendMessage(sender, "Invalid avatar level."); + return; + } + // Calculate ascension level. if (level <= 40) { ascension = (int) Math.ceil(level / 20f); @@ -88,6 +94,6 @@ public final class GiveCharCommand implements CommandHandler { avatar.recalcStats(); targetPlayer.addAvatar(avatar); - CommandHandler.sendMessage(sender, String.format("Given %s to %s.", avatarId, target)); + CommandHandler.sendMessage(sender, String.format("Given %s with level %s to %s.", avatarId, level, target)); } } From 29c95cb1b67de6f8a5353f3087284b72956dcb8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=AD=B1=E5=82=91?= Date: Mon, 2 May 2022 02:22:29 +0800 Subject: [PATCH 08/11] Add `/setstats mhp` to set Max HP (#407) * Fixed `/setstats hp` without changing the max hp. The Max HP should be modified. * Add `/setstats mhp` to set Max HP --- .../command/commands/SetStatsCommand.java | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/main/java/emu/grasscutter/command/commands/SetStatsCommand.java b/src/main/java/emu/grasscutter/command/commands/SetStatsCommand.java index 6e18573bb..0ca3f07ce 100644 --- a/src/main/java/emu/grasscutter/command/commands/SetStatsCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/SetStatsCommand.java @@ -28,9 +28,21 @@ public final class SetStatsCommand implements CommandHandler { String stat = args.get(0); switch (stat) { default: - CommandHandler.sendMessage(sender, "Usage: /setstats|stats for basic stats"); + CommandHandler.sendMessage(sender, "Usage: /setstats|stats for basic stats"); CommandHandler.sendMessage(sender, "Usage: /stats for elemental bonus"); return; + case "mhp": + try { + int health = Integer.parseInt(args.get(1)); + EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity(); + entity.setFightProperty(FightProperty.FIGHT_PROP_MAX_HP, health); + entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_MAX_HP)); + CommandHandler.sendMessage(sender, "MAX HP set to " + health + "."); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(sender, "Invalid Max HP value."); + return; + } + break; case "hp": try { int health = Integer.parseInt(args.get(1)); From 6d969064843dd934f6ece36bab6681a2376ff002 Mon Sep 17 00:00:00 2001 From: xtaodada Date: Mon, 2 May 2022 04:15:32 +0800 Subject: [PATCH 09/11] Show shopmall --- .../server/packet/send/PacketGetShopmallDataRsp.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketGetShopmallDataRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketGetShopmallDataRsp.java index 17f682406..d380c2953 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketGetShopmallDataRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketGetShopmallDataRsp.java @@ -4,13 +4,19 @@ import emu.grasscutter.net.packet.BasePacket; import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.proto.GetShopmallDataRspOuterClass.GetShopmallDataRsp; +import java.util.List; + public class PacketGetShopmallDataRsp extends BasePacket { public PacketGetShopmallDataRsp() { super(PacketOpcodes.GetShopmallDataRsp); - GetShopmallDataRsp proto = GetShopmallDataRsp.newBuilder().build(); - + List shop_malls = List.of(900, 1052, 902, 1001, 903); + + GetShopmallDataRsp proto = GetShopmallDataRsp.newBuilder() + .addAllShopTypeList(shop_malls) + .build(); + this.setData(proto); } } From 22a651b4aaed18755357e692a26bf9901a6ba519 Mon Sep 17 00:00:00 2001 From: xtaodada Date: Mon, 2 May 2022 04:28:38 +0800 Subject: [PATCH 10/11] Fix goods limit bug --- .../emu/grasscutter/server/packet/recv/HandlerBuyGoodsReq.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerBuyGoodsReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerBuyGoodsReq.java index e0257c4c8..0088b7176 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerBuyGoodsReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerBuyGoodsReq.java @@ -54,7 +54,7 @@ public class HandlerBuyGoodsReq extends PacketHandler { session.getPlayer().save(); } - if (bought + buyGoodsReq.getBoughtNum() > sg.getBuyLimit()) { + if ((bought + buyGoodsReq.getBoughtNum() > sg.getBuyLimit()) && sg.getBuyLimit() != 0) { return; } From 8cf4ef59ec8f1986ed49e582ef30cf95ddccd37c Mon Sep 17 00:00:00 2001 From: mingjun97 Date: Sun, 1 May 2022 12:49:44 -0700 Subject: [PATCH 11/11] Implement gacha history record subsystem * Frontend is not very beautiful yet * Didn't include too much `some anime game` data in the page to avoid being DMCA'd --- data/gacha_records.html | 176 ++++++++++++++++++ .../grasscutter/database/DatabaseHelper.java | 39 ++++ .../grasscutter/database/DatabaseManager.java | 3 +- .../grasscutter/game/gacha/GachaBanner.java | 16 +- .../grasscutter/game/gacha/GachaManager.java | 22 ++- .../grasscutter/game/gacha/GachaRecord.java | 75 ++++++++ .../server/dispatch/DispatchServer.java | 3 +- .../server/http/gacha/GachaRecordHandler.java | 52 ++++++ .../packet/recv/HandlerGetGachaInfoReq.java | 5 +- .../packet/send/PacketGetGachaInfoRsp.java | 8 + 10 files changed, 393 insertions(+), 6 deletions(-) create mode 100644 data/gacha_records.html create mode 100644 src/main/java/emu/grasscutter/game/gacha/GachaRecord.java create mode 100644 src/main/java/emu/grasscutter/server/http/gacha/GachaRecordHandler.java diff --git a/data/gacha_records.html b/data/gacha_records.html new file mode 100644 index 000000000..21270a046 --- /dev/null +++ b/data/gacha_records.html @@ -0,0 +1,176 @@ + + + + + + + +
+

Gacha Records

+

+
+
+ + + + + + + +
TimeItem
+
+ +
+ + + + + \ No newline at end of file diff --git a/src/main/java/emu/grasscutter/database/DatabaseHelper.java b/src/main/java/emu/grasscutter/database/DatabaseHelper.java index b2dae0446..2a247b46e 100644 --- a/src/main/java/emu/grasscutter/database/DatabaseHelper.java +++ b/src/main/java/emu/grasscutter/database/DatabaseHelper.java @@ -3,11 +3,14 @@ package emu.grasscutter.database; import java.util.List; import com.mongodb.client.result.DeleteResult; +import dev.morphia.query.FindOptions; +import dev.morphia.query.Sort; import dev.morphia.query.experimental.filters.Filters; import emu.grasscutter.GameConstants; import emu.grasscutter.game.Account; import emu.grasscutter.game.avatar.Avatar; import emu.grasscutter.game.friends.Friendship; +import emu.grasscutter.game.gacha.GachaRecord; import emu.grasscutter.game.inventory.GameItem; import emu.grasscutter.game.player.Player; @@ -78,6 +81,11 @@ public final class DatabaseHelper { return DatabaseManager.getDatastore().find(Account.class).filter(Filters.eq("token", token)).first(); } + public static Account getAccountBySessionKey(String sessionKey) { + if(sessionKey == null) return null; + return DatabaseManager.getDatastore().find(Account.class).filter(Filters.eq("sessionKey", sessionKey)).first(); + } + public static Account getAccountById(String uid) { return DatabaseManager.getDatastore().find(Account.class).filter(Filters.eq("_id", uid)).first(); } @@ -181,5 +189,36 @@ public final class DatabaseHelper { )).first(); } + public static List getGachaRecords(int ownerId, int page, int gachaType){ + return getGachaRecords(ownerId, page, gachaType, 10); + } + + public static List getGachaRecords(int ownerId, int page, int gachaType, int pageSize){ + return DatabaseManager.getDatastore().find(GachaRecord.class).filter( + Filters.eq("ownerId", ownerId), + Filters.eq("gachaType", gachaType) + ).iterator(new FindOptions() + .sort(Sort.descending("transactionDate")) + .skip(pageSize * page) + .limit(pageSize) + ).toList(); + } + + public static long getGachaRecordsMaxPage(int ownerId, int page, int gachaType){ + return getGachaRecordsMaxPage(ownerId, page, gachaType, 10); + } + + public static long getGachaRecordsMaxPage(int ownerId, int page, int gachaType, int pageSize){ + long count = DatabaseManager.getDatastore().find(GachaRecord.class).filter( + Filters.eq("ownerId", ownerId), + Filters.eq("gachaType", gachaType) + ).count(); + return count / 10 + (count % 10 > 0 ? 1 : 0 ); + } + + public static void saveGachaRecord(GachaRecord gachaRecord){ + DatabaseManager.getDatastore().save(gachaRecord); + } + public static char AWJVN = 'e'; } diff --git a/src/main/java/emu/grasscutter/database/DatabaseManager.java b/src/main/java/emu/grasscutter/database/DatabaseManager.java index c6a5f329a..2376451db 100644 --- a/src/main/java/emu/grasscutter/database/DatabaseManager.java +++ b/src/main/java/emu/grasscutter/database/DatabaseManager.java @@ -16,6 +16,7 @@ import emu.grasscutter.Grasscutter.ServerRunMode; import emu.grasscutter.game.Account; import emu.grasscutter.game.avatar.Avatar; import emu.grasscutter.game.friends.Friendship; +import emu.grasscutter.game.gacha.GachaRecord; import emu.grasscutter.game.inventory.GameItem; import emu.grasscutter.game.player.Player; @@ -28,7 +29,7 @@ public final class DatabaseManager { private static Datastore dispatchDatastore; private static final Class[] mappedClasses = new Class[] { - DatabaseCounter.class, Account.class, Player.class, Avatar.class, GameItem.class, Friendship.class + DatabaseCounter.class, Account.class, Player.class, Avatar.class, GameItem.class, Friendship.class, GachaRecord.class }; public static Datastore getDatastore() { diff --git a/src/main/java/emu/grasscutter/game/gacha/GachaBanner.java b/src/main/java/emu/grasscutter/game/gacha/GachaBanner.java index 9b54c924f..2317af38e 100644 --- a/src/main/java/emu/grasscutter/game/gacha/GachaBanner.java +++ b/src/main/java/emu/grasscutter/game/gacha/GachaBanner.java @@ -91,9 +91,21 @@ public class GachaBanner { return eventChance; } + @Deprecated public GachaInfo toProto() { - String record = "http://" + (Grasscutter.getConfig().getDispatchOptions().PublicIp.isEmpty() ? Grasscutter.getConfig().getDispatchOptions().Ip : Grasscutter.getConfig().getDispatchOptions().PublicIp) + "/gacha"; - + return toProto(""); + } + public GachaInfo toProto(String sessionKey) { + String record = "https://" + + (Grasscutter.getConfig().getDispatchOptions().PublicIp.isEmpty() ? + Grasscutter.getConfig().getDispatchOptions().Ip : + Grasscutter.getConfig().getDispatchOptions().PublicIp) + + ":" + + Integer.toString(Grasscutter.getConfig().getDispatchOptions().PublicPort == 0 ? + Grasscutter.getConfig().getDispatchOptions().Port : + Grasscutter.getConfig().getDispatchOptions().PublicPort) + + "/gacha?s=" + sessionKey + "&gachaType=" + gachaType; + // Grasscutter.getLogger().info("record = " + record); GachaInfo.Builder info = GachaInfo.newBuilder() .setGachaType(this.getGachaType()) .setScheduleId(this.getScheduleId()) diff --git a/src/main/java/emu/grasscutter/game/gacha/GachaManager.java b/src/main/java/emu/grasscutter/game/gacha/GachaManager.java index 5cd484e9a..cd1b5ea94 100644 --- a/src/main/java/emu/grasscutter/game/gacha/GachaManager.java +++ b/src/main/java/emu/grasscutter/game/gacha/GachaManager.java @@ -14,6 +14,7 @@ import com.sun.nio.file.SensitivityWatchEventModifier; import emu.grasscutter.Grasscutter; import emu.grasscutter.data.GameData; import emu.grasscutter.data.def.ItemData; +import emu.grasscutter.database.DatabaseHelper; import emu.grasscutter.game.avatar.Avatar; import emu.grasscutter.game.gacha.GachaBanner.BannerType; import emu.grasscutter.game.inventory.GameItem; @@ -196,6 +197,10 @@ public class GachaManager { if (itemData == null) { continue; } + + // Write gacha record + GachaRecord gachaRecord = new GachaRecord(itemId, player.getUid(), gachaType); + DatabaseHelper.saveGachaRecord(gachaRecord); // Create gacha item GachaItem.Builder gachaItem = GachaItem.newBuilder(); @@ -321,6 +326,7 @@ public class GachaManager { } } + @Deprecated private synchronized GetGachaInfoRsp createProto() { GetGachaInfoRsp.Builder proto = GetGachaInfoRsp.newBuilder().setGachaRandom(12345); @@ -330,12 +336,26 @@ public class GachaManager { return proto.build(); } + + private synchronized GetGachaInfoRsp createProto(String sessionKey) { + GetGachaInfoRsp.Builder proto = GetGachaInfoRsp.newBuilder().setGachaRandom(12345); + + for (GachaBanner banner : getGachaBanners().values()) { + proto.addGachaInfoList(banner.toProto(sessionKey)); + } + + return proto.build(); + } + @Deprecated public GetGachaInfoRsp toProto() { if (this.cachedProto == null) { this.cachedProto = createProto(); } - return this.cachedProto; } + + public GetGachaInfoRsp toProto(String sessionKey) { + return createProto(sessionKey); + } } diff --git a/src/main/java/emu/grasscutter/game/gacha/GachaRecord.java b/src/main/java/emu/grasscutter/game/gacha/GachaRecord.java new file mode 100644 index 000000000..ffaf983b6 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/gacha/GachaRecord.java @@ -0,0 +1,75 @@ +package emu.grasscutter.game.gacha; + +import java.util.Date; + +import org.bson.types.ObjectId; + +import dev.morphia.annotations.*; + +@Entity(value = "gachas", useDiscriminator = false) +public class GachaRecord { + @Id private ObjectId id; + + @Indexed private int ownerId; + + private Date transactionDate; + private int itemID; + @Indexed private int gachaType; + + public GachaRecord() {} + + public GachaRecord(int itemId ,int ownerId, int gachaType){ + this.transactionDate = new Date(); + this.itemID = itemId; + this.ownerId = ownerId; + this.gachaType = gachaType; + } + + public int getOwnerId() { + return ownerId; + } + + public void setOwnerId(int ownerId) { + this.ownerId = ownerId; + } + + public int getGachaType() { + return gachaType; + } + + public void setGachaType(int type) { + this.gachaType = type; + } + + public Date getTransactionDate() { + return transactionDate; + } + + public void setTransactionDate(Date transactionDate) { + this.transactionDate = transactionDate; + } + + public int getItemID() { + return itemID; + } + + public void setItemID(int itemID) { + this.itemID = itemID; + } + + public ObjectId getId(){ + return id; + } + + public void setId(ObjectId id) { + this.id = id; + } + + public String toString() { + return toJsonString(); + } + public String toJsonString() { + return "{\"time\": " + this.transactionDate.getTime() + ",\"item\":" + this.itemID + "}"; + } + +} diff --git a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java index b7e1e34ee..7b5418e15 100644 --- a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java +++ b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java @@ -18,6 +18,7 @@ import emu.grasscutter.server.dispatch.json.*; import emu.grasscutter.server.dispatch.json.ComboTokenReqJson.LoginTokenData; import emu.grasscutter.server.event.dispatch.QueryAllRegionsEvent; import emu.grasscutter.server.event.dispatch.QueryCurrentRegionEvent; +import emu.grasscutter.server.http.gacha.GachaRecordHandler; import emu.grasscutter.utils.FileUtils; import express.Express; import org.eclipse.jetty.server.Connector; @@ -485,7 +486,7 @@ public final class DispatchServer { // webstatic-sea.hoyoverse.com httpServer.get("/admin/mi18n/plat_oversea/m202003048/m202003048-version.json", new DispatchHttpJsonHandler("{\"version\":51}")); - httpServer.get("/gacha", (req, res) -> res.send("Gacha")); + httpServer.get("/gacha", new GachaRecordHandler()); httpServer.listen(Grasscutter.getConfig().getDispatchOptions().Port); Grasscutter.getLogger().info("[Dispatch] Dispatch server started on port " + httpServer.raw().port()); diff --git a/src/main/java/emu/grasscutter/server/http/gacha/GachaRecordHandler.java b/src/main/java/emu/grasscutter/server/http/gacha/GachaRecordHandler.java new file mode 100644 index 000000000..0798a150f --- /dev/null +++ b/src/main/java/emu/grasscutter/server/http/gacha/GachaRecordHandler.java @@ -0,0 +1,52 @@ +package emu.grasscutter.server.http.gacha; + +import java.io.File; +import java.io.IOException; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.database.DatabaseHelper; +import emu.grasscutter.game.Account; +import emu.grasscutter.utils.FileUtils; +import express.http.HttpContextHandler; +import express.http.Request; +import express.http.Response; + +public final class GachaRecordHandler implements HttpContextHandler { + String render_template; + public GachaRecordHandler() { + File template = new File(Grasscutter.getConfig().DATA_FOLDER + "gacha_records.html"); + if (template.exists()) { + // Load from cache + render_template = new String(FileUtils.read(template)); + } else { + render_template = "{{REPLACE_RECORD}}"; + } + } + + @Override + public void handle(Request req, Response res) throws IOException { + // Grasscutter.getLogger().info( req.query().toString() ); + String sessionKey = req.query("s"); + int page = 0; + int gachaType = 0; + if (req.query("p") != null) { + page = Integer.valueOf(req.query("p")); + } + + if (req.query("gachaType") != null) { + gachaType = Integer.valueOf(req.query("gachaType")); + } + + Account account = DatabaseHelper.getAccountBySessionKey(sessionKey); + if (account != null) { + String records = DatabaseHelper.getGachaRecords(account.getPlayerUid(), page, gachaType).toString(); + // Grasscutter.getLogger().info(records); + String response = render_template.replace("{{REPLACE_RECORD}}", records) + .replace("{{REPLACE_MAXPAGE}}", String.valueOf(DatabaseHelper.getGachaRecordsMaxPage(account.getPlayerUid(), page, gachaType))); + + res.send(response); + } else { + res.send("404"); + } + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetGachaInfoReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetGachaInfoReq.java index 6c4c703a8..76d267b99 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetGachaInfoReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetGachaInfoReq.java @@ -11,7 +11,10 @@ public class HandlerGetGachaInfoReq extends PacketHandler { @Override public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { - session.send(new PacketGetGachaInfoRsp(session.getServer().getGachaManager())); + session.send(new PacketGetGachaInfoRsp(session.getServer().getGachaManager(), + // TODO: use other Nonce/key insteadof session key to ensure the overall security for the player + session.getPlayer().getAccount().getSessionKey()) + ); } } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketGetGachaInfoRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketGetGachaInfoRsp.java index 89af334a5..84d857681 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketGetGachaInfoRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketGetGachaInfoRsp.java @@ -6,9 +6,17 @@ import emu.grasscutter.net.packet.PacketOpcodes; public class PacketGetGachaInfoRsp extends BasePacket { + @Deprecated public PacketGetGachaInfoRsp(GachaManager manager) { super(PacketOpcodes.GetGachaInfoRsp); this.setData(manager.toProto()); } + + public PacketGetGachaInfoRsp(GachaManager manager, String sessionKey) { + super(PacketOpcodes.GetGachaInfoRsp); + + this.setData(manager.toProto(sessionKey)); + } + }