From 3169e87c60837c005b330d22b35d6dffbed7dcaa Mon Sep 17 00:00:00 2001 From: Melledy <52122272+Melledy@users.noreply.github.com> Date: Sat, 30 Apr 2022 19:34:50 -0700 Subject: [PATCH 1/8] Fix constellations that give an extra charge to skills Fix #228 --- .../emu/grasscutter/data/ResourceLoader.java | 17 ++----- .../data/custom/OpenConfigEntry.java | 49 +++++++++++++++++-- .../emu/grasscutter/game/avatar/Avatar.java | 34 ++++++++++++- .../game/avatar/AvatarStorage.java | 2 +- .../game/managers/InventoryManager.java | 20 ++++++-- ...PacketAvatarSkillMaxChargeCountNotify.java | 21 ++++++++ 6 files changed, 122 insertions(+), 21 deletions(-) create mode 100644 src/main/java/emu/grasscutter/server/packet/send/PacketAvatarSkillMaxChargeCountNotify.java diff --git a/src/main/java/emu/grasscutter/data/ResourceLoader.java b/src/main/java/emu/grasscutter/data/ResourceLoader.java index 180080e51..5478052d8 100644 --- a/src/main/java/emu/grasscutter/data/ResourceLoader.java +++ b/src/main/java/emu/grasscutter/data/ResourceLoader.java @@ -307,18 +307,7 @@ public class ResourceLoader { } for (Entry e : config.entrySet()) { - List abilityList = new ArrayList<>(); - int extraTalentIndex = 0; - - for (OpenConfigData entry : e.getValue()) { - if (entry.$type.contains("AddAbility")) { - abilityList.add(entry.abilityName); - } else if (entry.talentIndex > 0) { - extraTalentIndex = entry.talentIndex; - } - } - - OpenConfigEntry entry = new OpenConfigEntry(e.getKey(), abilityList, extraTalentIndex); + OpenConfigEntry entry = new OpenConfigEntry(e.getKey(), e.getValue()); map.put(entry.getName(), entry); } } @@ -354,9 +343,11 @@ public class ResourceLoader { public OpenConfigData[] data; } - private static class OpenConfigData { + public static class OpenConfigData { public String $type; public String abilityName; public int talentIndex; + public int skillID; + public int pointDelta; } } diff --git a/src/main/java/emu/grasscutter/data/custom/OpenConfigEntry.java b/src/main/java/emu/grasscutter/data/custom/OpenConfigEntry.java index d01467637..8ff646fa9 100644 --- a/src/main/java/emu/grasscutter/data/custom/OpenConfigEntry.java +++ b/src/main/java/emu/grasscutter/data/custom/OpenConfigEntry.java @@ -1,18 +1,39 @@ package emu.grasscutter.data.custom; +import java.util.ArrayList; import java.util.List; +import emu.grasscutter.data.ResourceLoader.OpenConfigData; + public class OpenConfigEntry { private String name; private String[] addAbilities; private int extraTalentIndex; - - public OpenConfigEntry(String name, List abilityList, int extraTalentIndex) { + private SkillPointModifier[] skillPointModifiers; + + public OpenConfigEntry(String name, OpenConfigData[] data) { this.name = name; - this.extraTalentIndex = extraTalentIndex; + + List abilityList = new ArrayList<>(); + List modList = new ArrayList<>(); + + for (OpenConfigData entry : data) { + if (entry.$type.contains("AddAbility")) { + abilityList.add(entry.abilityName); + } else if (entry.talentIndex > 0) { + this.extraTalentIndex = entry.talentIndex; + } else if (entry.$type.contains("ModifySkillPoint")) { + modList.add(new SkillPointModifier(entry.skillID, entry.pointDelta)); + } + } + if (abilityList.size() > 0) { this.addAbilities = abilityList.toArray(new String[0]); } + + if (modList.size() > 0) { + this.skillPointModifiers = modList.toArray(new SkillPointModifier[0]); + } } public String getName() { @@ -26,4 +47,26 @@ public class OpenConfigEntry { public int getExtraTalentIndex() { return extraTalentIndex; } + + public SkillPointModifier[] getSkillPointModifiers() { + return skillPointModifiers; + } + + public static class SkillPointModifier { + private int skillId; + private int delta; + + public SkillPointModifier(int skillId, int delta) { + this.skillId = skillId; + this.delta = delta; + } + + public int getSkillId() { + return skillId; + } + + public int getDelta() { + return delta; + } + } } diff --git a/src/main/java/emu/grasscutter/game/avatar/Avatar.java b/src/main/java/emu/grasscutter/game/avatar/Avatar.java index cf5446ab7..de66e8671 100644 --- a/src/main/java/emu/grasscutter/game/avatar/Avatar.java +++ b/src/main/java/emu/grasscutter/game/avatar/Avatar.java @@ -5,6 +5,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import org.bson.types.ObjectId; @@ -18,6 +19,7 @@ import dev.morphia.annotations.Transient; import emu.grasscutter.data.GameData; import emu.grasscutter.data.common.FightPropData; import emu.grasscutter.data.custom.OpenConfigEntry; +import emu.grasscutter.data.custom.OpenConfigEntry.SkillPointModifier; import emu.grasscutter.data.def.AvatarData; import emu.grasscutter.data.def.AvatarPromoteData; import emu.grasscutter.data.def.AvatarSkillData; @@ -46,6 +48,7 @@ import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.PlayerProperty; import emu.grasscutter.net.proto.AvatarFetterInfoOuterClass.AvatarFetterInfo; import emu.grasscutter.net.proto.AvatarInfoOuterClass.AvatarInfo; +import emu.grasscutter.net.proto.AvatarSkillInfoOuterClass.AvatarSkillInfo; import emu.grasscutter.net.proto.FetterDataOuterClass.FetterData; import emu.grasscutter.net.proto.ShowAvatarInfoOuterClass; import emu.grasscutter.net.proto.ShowAvatarInfoOuterClass.ShowAvatarInfo; @@ -83,6 +86,7 @@ public class Avatar { private List fetters; private Map skillLevelMap; // Talent levels + private Map skillExtraChargeMap; // Charges private Map proudSkillBonusMap; // Talent bonus levels (from const) private int skillDepotId; private int coreProudSkillLevel; // Constellation level @@ -123,6 +127,7 @@ public class Avatar { this.flyCloak = 140001; this.skillLevelMap = new HashMap<>(); + this.skillExtraChargeMap = new HashMap<>(); this.talentIdList = new HashSet<>(); this.proudSkillList = new HashSet<>(); @@ -283,6 +288,13 @@ public class Avatar { public Map getSkillLevelMap() { return skillLevelMap; } + + public Map getSkillExtraChargeMap() { + if (skillExtraChargeMap == null) { + skillExtraChargeMap = new HashMap<>(); + } + return skillExtraChargeMap; + } public Map getProudSkillBonusMap() { return proudSkillBonusMap; @@ -676,9 +688,10 @@ public class Avatar { } } - public void recalcProudSkillBonusMap() { + public void recalcConstellations() { // Clear first this.getProudSkillBonusMap().clear(); + this.getSkillExtraChargeMap().clear(); // Sanity checks if (getData() == null || getData().getSkillDepot() == null) { @@ -699,6 +712,21 @@ public class Avatar { continue; } + // Check if we can add charges to a skill + if (entry.getSkillPointModifiers() != null) { + for (SkillPointModifier mod : entry.getSkillPointModifiers()) { + AvatarSkillData skillData = GameData.getAvatarSkillDataMap().get(mod.getSkillId()); + + if (skillData == null) continue; + + int charges = skillData.getMaxChargeNum() + mod.getDelta(); + + this.getSkillExtraChargeMap().put(mod.getSkillId(), charges); + } + continue; + } + + // Check if a skill can be boosted by +3 levels int skillId = 0; if (entry.getExtraTalentIndex() == 2 && this.getData().getSkillDepot().getSkills().size() >= 2) { @@ -788,6 +816,10 @@ public class Avatar { .setWearingFlycloakId(this.getFlyCloak()) .setCostumeId(this.getCostume()); + for (Entry entry : this.getSkillExtraChargeMap().entrySet()) { + avatarInfo.putSkillMap(entry.getKey(), AvatarSkillInfo.newBuilder().setMaxChargeCount(entry.getValue()).build()); + } + for (GameItem item : this.getEquips().values()) { avatarInfo.addEquipGuidList(item.getGuid()); } diff --git a/src/main/java/emu/grasscutter/game/avatar/AvatarStorage.java b/src/main/java/emu/grasscutter/game/avatar/AvatarStorage.java index 99ccda173..2486e36ab 100644 --- a/src/main/java/emu/grasscutter/game/avatar/AvatarStorage.java +++ b/src/main/java/emu/grasscutter/game/avatar/AvatarStorage.java @@ -148,7 +148,7 @@ public class AvatarStorage implements Iterable { avatar.setOwner(getPlayer()); // Force recalc of const boosted skills - avatar.recalcProudSkillBonusMap(); + avatar.recalcConstellations(); // Add to avatar storage this.avatars.put(avatar.getAvatarId(), avatar); diff --git a/src/main/java/emu/grasscutter/game/managers/InventoryManager.java b/src/main/java/emu/grasscutter/game/managers/InventoryManager.java index 1e028c38c..2a9629b73 100644 --- a/src/main/java/emu/grasscutter/game/managers/InventoryManager.java +++ b/src/main/java/emu/grasscutter/game/managers/InventoryManager.java @@ -8,6 +8,7 @@ import java.util.stream.Collectors; import emu.grasscutter.data.GameData; import emu.grasscutter.data.common.ItemParamData; import emu.grasscutter.data.custom.OpenConfigEntry; +import emu.grasscutter.data.custom.OpenConfigEntry.SkillPointModifier; import emu.grasscutter.data.def.AvatarPromoteData; import emu.grasscutter.data.def.AvatarSkillData; import emu.grasscutter.data.def.AvatarSkillDepotData; @@ -835,9 +836,22 @@ public class InventoryManager { // Proud skill bonus map (Extra skills) OpenConfigEntry entry = GameData.getOpenConfigEntries().get(talentData.getOpenConfig()); - if (entry != null && entry.getExtraTalentIndex() > 0) { - avatar.recalcProudSkillBonusMap(); - player.sendPacket(new PacketProudSkillExtraLevelNotify(avatar, entry.getExtraTalentIndex())); + if (entry != null) { + if (entry.getExtraTalentIndex() > 0) { + // Check if new constellation adds +3 to a skill level + avatar.recalcConstellations(); + // Packet + player.sendPacket(new PacketProudSkillExtraLevelNotify(avatar, entry.getExtraTalentIndex())); + } else if (entry.getSkillPointModifiers() != null) { + // Check if new constellation adds skill charges + avatar.recalcConstellations(); + // Packet + for (SkillPointModifier mod : entry.getSkillPointModifiers()) { + player.sendPacket( + new PacketAvatarSkillMaxChargeCountNotify(avatar, mod.getSkillId(), avatar.getSkillExtraChargeMap().getOrDefault(mod.getSkillId(), 0)) + ); + } + } } // Recalc + save avatar diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketAvatarSkillMaxChargeCountNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketAvatarSkillMaxChargeCountNotify.java new file mode 100644 index 000000000..696cb2027 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketAvatarSkillMaxChargeCountNotify.java @@ -0,0 +1,21 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.game.avatar.Avatar; +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.AvatarSkillMaxChargeCountNotifyOuterClass.AvatarSkillMaxChargeCountNotify; + +public class PacketAvatarSkillMaxChargeCountNotify extends BasePacket { + + public PacketAvatarSkillMaxChargeCountNotify(Avatar avatar, int skillId, int maxCharges) { + super(PacketOpcodes.AvatarSkillMaxChargeCountNotify); + + AvatarSkillMaxChargeCountNotify proto = AvatarSkillMaxChargeCountNotify.newBuilder() + .setAvatarGuid(avatar.getGuid()) + .setSkillId(skillId) + .setMaxChargeCount(maxCharges) + .build(); + + this.setData(proto); + } +} From 8b8777e9329e005211a1d24adf5fcff1a918b3e4 Mon Sep 17 00:00:00 2001 From: mingjun97 Date: Sat, 30 Apr 2022 21:16:50 -0700 Subject: [PATCH 2/8] Fix crash when login * Prevent email to be `null` to avoid crash in certain client setup. --- .../grasscutter/server/dispatch/DispatchServer.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java index bf236bd88..c8902e3f6 100644 --- a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java +++ b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java @@ -381,6 +381,9 @@ 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)); @@ -437,7 +440,7 @@ public final class DispatchServer { // hk4e-sdk-os.hoyoverse.com httpServer.get("/hk4e_global/mdk/agreement/api/getAgreementInfos", new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"marketing_agreements\":[]}}")); // hk4e-sdk-os.hoyoverse.com - httpServer.post("/hk4e_global/combo/granter/api/compareProtocolVersion", new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"modified\":true,\"protocol\":{\"id\":0,\"app_id\":4,\"language\":\"en\",\"user_proto\":\"\",\"priv_proto\":\"\",\"major\":7,\"minimum\":0,\"create_time\":\"0\",\"teenager_proto\":\"\",\"third_proto\":\"\"}}}")); + httpServer.get("/hk4e_global/combo/granter/api/compareProtocolVersion", new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"modified\":true,\"protocol\":{\"id\":0,\"app_id\":4,\"language\":\"en\",\"user_proto\":\"\",\"priv_proto\":\"\",\"major\":7,\"minimum\":0,\"create_time\":\"0\",\"teenager_proto\":\"\",\"third_proto\":\"\"}}}")); // Game data // hk4e-api-os.hoyoverse.com @@ -469,7 +472,7 @@ public final class DispatchServer { // log-upload-os.mihoyo.com httpServer.all("/log/sdk/upload", new DispatchHttpJsonHandler("{\"code\":0}")); httpServer.all("/sdk/upload", new DispatchHttpJsonHandler("{\"code\":0}")); - httpServer.post("/sdk/dataUpload", new DispatchHttpJsonHandler("{\"code\":0}")); + httpServer.post("/sdk/dataUpload", (req, res) -> res.send("Hello")); // /perf/config/verify?device_id=xxx&platform=x&name=xxx httpServer.all("/perf/config/verify", new DispatchHttpJsonHandler("{\"code\":0}")); @@ -479,6 +482,9 @@ public final class DispatchServer { // log-upload-os.mihoyo.com httpServer.all("/crash/dataUpload", new ClientLogHandler()); + // 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.listen(Grasscutter.getConfig().getDispatchOptions().Port); From fb547c17cabe6e166434ffd694f32ead3ff8fe05 Mon Sep 17 00:00:00 2001 From: mingjun97 Date: Sat, 30 Apr 2022 21:25:08 -0700 Subject: [PATCH 3/8] Revert changes for debugging purpose --- .../java/emu/grasscutter/server/dispatch/DispatchServer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java index c8902e3f6..65ded023e 100644 --- a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java +++ b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java @@ -472,7 +472,7 @@ public final class DispatchServer { // log-upload-os.mihoyo.com httpServer.all("/log/sdk/upload", new DispatchHttpJsonHandler("{\"code\":0}")); httpServer.all("/sdk/upload", new DispatchHttpJsonHandler("{\"code\":0}")); - httpServer.post("/sdk/dataUpload", (req, res) -> res.send("Hello")); + httpServer.post("/sdk/dataUpload", new DispatchHttpJsonHandler("{\"code\":0}")); // /perf/config/verify?device_id=xxx&platform=x&name=xxx httpServer.all("/perf/config/verify", new DispatchHttpJsonHandler("{\"code\":0}")); From da3eb3d26cc0871520c7cad1e6b5df756374bad8 Mon Sep 17 00:00:00 2001 From: Kinesis Date: Sun, 1 May 2022 12:43:44 +0800 Subject: [PATCH 4/8] implement McoinExchange packet Handler --- .../recv/HandlerMcoinExchangeHcoinReq.java | 26 +++++++++++++++++++ .../send/PacketMcoinExchangeHcoinRsp.java | 18 +++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 src/main/java/emu/grasscutter/server/packet/recv/HandlerMcoinExchangeHcoinReq.java create mode 100644 src/main/java/emu/grasscutter/server/packet/send/PacketMcoinExchangeHcoinRsp.java diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerMcoinExchangeHcoinReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerMcoinExchangeHcoinReq.java new file mode 100644 index 000000000..0f3d2e7f8 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerMcoinExchangeHcoinReq.java @@ -0,0 +1,26 @@ +package emu.grasscutter.server.packet.recv; + +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.server.game.GameSession; +import emu.grasscutter.server.packet.send.PacketMcoinExchangeHcoinRsp; + +@Opcodes(PacketOpcodes.McoinExchangeHcoinReq) +public class HandlerMcoinExchangeHcoinReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + McoinExchangeHcoinReqOuterClass.McoinExchangeHcoinReq exchangeReq = McoinExchangeHcoinReqOuterClass.McoinExchangeHcoinReq.parseFrom(payload); + if (session == null) { + return; + } else if (session.getPlayer().getCrystals() < exchangeReq.getMCoinNum()) { + 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())); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketMcoinExchangeHcoinRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketMcoinExchangeHcoinRsp.java new file mode 100644 index 000000000..65bef7b27 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketMcoinExchangeHcoinRsp.java @@ -0,0 +1,18 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.McoinExchangeHcoinRspOuterClass; + +public class PacketMcoinExchangeHcoinRsp extends BasePacket { + + public PacketMcoinExchangeHcoinRsp(int mcoin, int hcoin) { + super(PacketOpcodes.McoinExchangeHcoinRsp); + + McoinExchangeHcoinRspOuterClass.McoinExchangeHcoinRsp mcoinExchangeHcoinRsp = McoinExchangeHcoinRspOuterClass.McoinExchangeHcoinRsp.newBuilder() + .setMCoinNum(mcoin) + .setHCoinNum(hcoin).build(); + + this.setData(mcoinExchangeHcoinRsp); + } +} From eac82f3c014aeef04da770a490e92c5fc71dc86a Mon Sep 17 00:00:00 2001 From: Melledy <52122272+Melledy@users.noreply.github.com> Date: Sat, 30 Apr 2022 22:51:21 -0700 Subject: [PATCH 5/8] Fix possible exploit with mcoin exchange --- .../server/packet/recv/HandlerMcoinExchangeHcoinReq.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) 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 0f3d2e7f8..57ecaf42b 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerMcoinExchangeHcoinReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerMcoinExchangeHcoinReq.java @@ -13,14 +13,15 @@ public class HandlerMcoinExchangeHcoinReq extends PacketHandler { @Override public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { McoinExchangeHcoinReqOuterClass.McoinExchangeHcoinReq exchangeReq = McoinExchangeHcoinReqOuterClass.McoinExchangeHcoinReq.parseFrom(payload); - if (session == null) { - return; - } else if (session.getPlayer().getCrystals() < exchangeReq.getMCoinNum()) { + + if (session.getPlayer().getCrystals() < exchangeReq.getMCoinNum() && exchangeReq.getMCoinNum() == exchangeReq.getHCoinNum()) { 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())); } } From 10bfb6976d689f67b33318879873c03db8779fbb Mon Sep 17 00:00:00 2001 From: Melledy <52122272+Melledy@users.noreply.github.com> Date: Sat, 30 Apr 2022 22:52:09 -0700 Subject: [PATCH 6/8] Change RunMode and DebugMode to enums --- src/main/java/emu/grasscutter/Config.java | 6 +++-- .../java/emu/grasscutter/Grasscutter.java | 25 +++++++++---------- .../grasscutter/database/DatabaseManager.java | 5 ++-- .../dispatch/DispatchHttpJsonHandler.java | 5 ++-- .../server/dispatch/DispatchServer.java | 10 +++++--- .../server/game/GameServerPacketHandler.java | 3 ++- .../grasscutter/server/game/GameSession.java | 5 ++-- .../packet/send/PacketPlayerLoginRsp.java | 4 ++- 8 files changed, 36 insertions(+), 27 deletions(-) diff --git a/src/main/java/emu/grasscutter/Config.java b/src/main/java/emu/grasscutter/Config.java index bc691dbd9..5a49c325c 100644 --- a/src/main/java/emu/grasscutter/Config.java +++ b/src/main/java/emu/grasscutter/Config.java @@ -1,5 +1,7 @@ package emu.grasscutter; +import emu.grasscutter.Grasscutter.ServerDebugMode; +import emu.grasscutter.Grasscutter.ServerRunMode; import emu.grasscutter.game.mail.Mail; public final class Config { @@ -15,8 +17,8 @@ public final class Config { public String SCRIPTS_FOLDER = "./resources/Scripts/"; public String PLUGINS_FOLDER = "./plugins/"; - public String DebugMode = "NONE"; // ALL, MISSING, NONE - public String RunMode = "HYBRID"; // HYBRID, DISPATCH_ONLY, GAME_ONLY + public ServerDebugMode DebugMode = ServerDebugMode.NONE; // ALL, MISSING, NONE + public ServerRunMode RunMode = ServerRunMode.HYBRID; // HYBRID, DISPATCH_ONLY, GAME_ONLY public GameServerOptions GameServer = new GameServerOptions(); public DispatchServerOptions DispatchServer = new DispatchServerOptions(); diff --git a/src/main/java/emu/grasscutter/Grasscutter.java b/src/main/java/emu/grasscutter/Grasscutter.java index 60d042b11..c7ab7c637 100644 --- a/src/main/java/emu/grasscutter/Grasscutter.java +++ b/src/main/java/emu/grasscutter/Grasscutter.java @@ -35,7 +35,6 @@ public final class Grasscutter { private static int day; // Current day of week - public static RunMode MODE = RunMode.BOTH; private static DispatchServer dispatchServer; private static GameServer gameServer; private static PluginManager pluginManager; @@ -58,8 +57,6 @@ public final class Grasscutter { for (String arg : args) { switch (arg.toLowerCase()) { - case "-auth" -> MODE = RunMode.AUTH; - case "-game" -> MODE = RunMode.GAME; case "-handbook" -> { Tools.createGmHandbook(); return; } @@ -85,12 +82,12 @@ public final class Grasscutter { gameServer = new GameServer(new InetSocketAddress(getConfig().getGameServerOptions().Ip, getConfig().getGameServerOptions().Port)); // Start servers. - if(getConfig().RunMode.equalsIgnoreCase("HYBRID")) { + if (getConfig().RunMode == ServerRunMode.HYBRID) { dispatchServer.start(); gameServer.start(); - } else if (getConfig().RunMode.equalsIgnoreCase("DISPATCH_ONLY")) { + } else if (getConfig().RunMode == ServerRunMode.DISPATCH_ONLY) { dispatchServer.start(); - } else if (getConfig().RunMode.equalsIgnoreCase("GAME_ONLY")) { + } else if (getConfig().RunMode == ServerRunMode.GAME_ONLY) { gameServer.start(); } else { getLogger().error("Invalid server run mode. " + getConfig().RunMode); @@ -140,7 +137,7 @@ public final class Grasscutter { try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) { while ((input = br.readLine()) != null) { try { - if(getConfig().RunMode.equalsIgnoreCase("DISPATCH_ONLY")) { + if (getConfig().RunMode == ServerRunMode.DISPATCH_ONLY) { getLogger().error("Commands are not supported in dispatch only mode."); return; } @@ -154,12 +151,6 @@ public final class Grasscutter { Grasscutter.getLogger().error("An error occurred.", e); } } - - public enum RunMode { - BOTH, - AUTH, - GAME - } public static Config getConfig() { return config; @@ -193,4 +184,12 @@ public final class Grasscutter { public static int getCurrentDayOfWeek() { return day; } + + public enum ServerRunMode { + HYBRID, DISPATCH_ONLY, GAME_ONLY + } + + public enum ServerDebugMode { + ALL, MISSING, NONE + } } diff --git a/src/main/java/emu/grasscutter/database/DatabaseManager.java b/src/main/java/emu/grasscutter/database/DatabaseManager.java index 68e1f5e87..c6a5f329a 100644 --- a/src/main/java/emu/grasscutter/database/DatabaseManager.java +++ b/src/main/java/emu/grasscutter/database/DatabaseManager.java @@ -12,6 +12,7 @@ import dev.morphia.Morphia; import dev.morphia.mapping.MapperOptions; import dev.morphia.query.experimental.filters.Filters; import emu.grasscutter.Grasscutter; +import emu.grasscutter.Grasscutter.ServerRunMode; import emu.grasscutter.game.Account; import emu.grasscutter.game.avatar.Avatar; import emu.grasscutter.game.friends.Friendship; @@ -41,7 +42,7 @@ public final class DatabaseManager { // Yes. I very dislike this method. However, this will be good for now. // TODO: Add dispatch routes for player account management public static Datastore getAccountDatastore() { - if(Grasscutter.getConfig().RunMode.equalsIgnoreCase("GAME_ONLY")) { + if(Grasscutter.getConfig().RunMode == ServerRunMode.GAME_ONLY) { return dispatchDatastore; } else { return datastore; @@ -77,7 +78,7 @@ public final class DatabaseManager { } } - if(Grasscutter.getConfig().RunMode.equalsIgnoreCase("GAME_ONLY")) { + if(Grasscutter.getConfig().RunMode == ServerRunMode.GAME_ONLY) { dispatchMongoClient = MongoClients.create(Grasscutter.getConfig().getGameServerOptions().DispatchServerDatabaseUrl); dispatchDatastore = Morphia.createDatastore(dispatchMongoClient, Grasscutter.getConfig().getGameServerOptions().DispatchServerDatabaseCollection); diff --git a/src/main/java/emu/grasscutter/server/dispatch/DispatchHttpJsonHandler.java b/src/main/java/emu/grasscutter/server/dispatch/DispatchHttpJsonHandler.java index 80e582b5f..31abc77ae 100644 --- a/src/main/java/emu/grasscutter/server/dispatch/DispatchHttpJsonHandler.java +++ b/src/main/java/emu/grasscutter/server/dispatch/DispatchHttpJsonHandler.java @@ -8,6 +8,7 @@ import java.util.Collections; import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import emu.grasscutter.Grasscutter; +import emu.grasscutter.Grasscutter.ServerDebugMode; import express.http.HttpContextHandler; import express.http.Request; import express.http.Response; @@ -34,8 +35,8 @@ public final class DispatchHttpJsonHandler implements HttpContextHandler { @Override 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.equalsIgnoreCase("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.equalsIgnoreCase("MISSING") ? "(MISSING)" : "")); + 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)" : "")); } 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 65ded023e..9fc85b477 100644 --- a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java +++ b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java @@ -6,6 +6,8 @@ import com.google.protobuf.ByteString; import emu.grasscutter.Config; import emu.grasscutter.Grasscutter; +import emu.grasscutter.Grasscutter.ServerDebugMode; +import emu.grasscutter.Grasscutter.ServerRunMode; import emu.grasscutter.database.DatabaseHelper; import emu.grasscutter.game.Account; import emu.grasscutter.net.proto.QueryCurrRegionHttpRspOuterClass.QueryCurrRegionHttpRsp; @@ -62,7 +64,7 @@ public final class DispatchServer { public QueryCurrRegionHttpRsp getCurrRegion() { // Needs to be fixed by having the game servers connect to the dispatch server. - if (Grasscutter.getConfig().RunMode.equalsIgnoreCase("HYBRID")) { + if (Grasscutter.getConfig().RunMode == ServerRunMode.HYBRID) { return regions.get(defaultServerName).parsedRegionQuery; } @@ -98,7 +100,7 @@ public final class DispatchServer { List servers = new ArrayList<>(); List usedNames = new ArrayList<>(); // List to check for potential naming conflicts - if (Grasscutter.getConfig().RunMode.equalsIgnoreCase("HYBRID")) { // Automatically add the game server if in + if (Grasscutter.getConfig().RunMode == ServerRunMode.HYBRID) { // Automatically add the game server if in // hybrid mode RegionSimpleInfo server = RegionSimpleInfo.newBuilder() .setName("os_usa") @@ -233,7 +235,7 @@ public final class DispatchServer { }); config.enforceSsl = Grasscutter.getConfig().getDispatchOptions().UseSSL; - if(Grasscutter.getConfig().DebugMode.equalsIgnoreCase("ALL")) { + if(Grasscutter.getConfig().DebugMode == ServerDebugMode.ALL) { config.enableDevLogging(); } }); @@ -241,7 +243,7 @@ public final class DispatchServer { httpServer.get("/", (req, res) -> res.send("Welcome to Grasscutter")); httpServer.raw().error(404, ctx -> { - if(Grasscutter.getConfig().DebugMode.equalsIgnoreCase("MISSING")) { + if(Grasscutter.getConfig().DebugMode == ServerDebugMode.MISSING) { Grasscutter.getLogger().info(String.format("[Dispatch] Potential unhandled %s request: %s", ctx.method(), ctx.url())); } ctx.contentType("text/html"); diff --git a/src/main/java/emu/grasscutter/server/game/GameServerPacketHandler.java b/src/main/java/emu/grasscutter/server/game/GameServerPacketHandler.java index 006d4621f..89fa71481 100644 --- a/src/main/java/emu/grasscutter/server/game/GameServerPacketHandler.java +++ b/src/main/java/emu/grasscutter/server/game/GameServerPacketHandler.java @@ -6,6 +6,7 @@ import emu.grasscutter.server.event.game.ReceivePacketEvent; import org.reflections.Reflections; import emu.grasscutter.Grasscutter; +import emu.grasscutter.Grasscutter.ServerDebugMode; import emu.grasscutter.net.packet.Opcodes; import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.net.packet.PacketOpcodes; @@ -88,7 +89,7 @@ public class GameServerPacketHandler { } // Log unhandled packets - if (Grasscutter.getConfig().DebugMode.equalsIgnoreCase("MISSING")) { + if (Grasscutter.getConfig().DebugMode == ServerDebugMode.MISSING) { Grasscutter.getLogger().info("Unhandled packet (" + opcode + "): " + emu.grasscutter.net.packet.PacketOpcodesUtil.getOpcodeName(opcode)); } } diff --git a/src/main/java/emu/grasscutter/server/game/GameSession.java b/src/main/java/emu/grasscutter/server/game/GameSession.java index a980a377c..1ae5067ea 100644 --- a/src/main/java/emu/grasscutter/server/game/GameSession.java +++ b/src/main/java/emu/grasscutter/server/game/GameSession.java @@ -7,6 +7,7 @@ import java.util.HashSet; import java.util.Set; import emu.grasscutter.Grasscutter; +import emu.grasscutter.Grasscutter.ServerDebugMode; import emu.grasscutter.game.Account; import emu.grasscutter.game.player.Player; import emu.grasscutter.net.packet.BasePacket; @@ -163,7 +164,7 @@ public class GameSession extends KcpChannel { } // Log - if (Grasscutter.getConfig().DebugMode.equalsIgnoreCase("ALL")) { + if (Grasscutter.getConfig().DebugMode == ServerDebugMode.ALL) { logPacket(packet); } @@ -230,7 +231,7 @@ public class GameSession extends KcpChannel { } // Log packet - if (Grasscutter.getConfig().DebugMode.equalsIgnoreCase("ALL")) { + if (Grasscutter.getConfig().DebugMode == ServerDebugMode.ALL) { if (!loopPacket.contains(opcode)) { Grasscutter.getLogger().info("RECV: " + PacketOpcodesUtil.getOpcodeName(opcode) + " (" + opcode + ")"); System.out.println(Utils.bytesToHex(payload)); diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerLoginRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerLoginRsp.java index 18ff6c03e..6407e9412 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerLoginRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerLoginRsp.java @@ -2,6 +2,8 @@ package emu.grasscutter.server.packet.send; import com.google.protobuf.ByteString; import emu.grasscutter.Grasscutter; +import emu.grasscutter.Grasscutter.ServerDebugMode; +import emu.grasscutter.Grasscutter.ServerRunMode; import emu.grasscutter.net.packet.BasePacket; import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.proto.PlayerLoginRspOuterClass.PlayerLoginRsp; @@ -25,7 +27,7 @@ public class PacketPlayerLoginRsp extends BasePacket { RegionInfo info; - if(Grasscutter.getConfig().RunMode.equalsIgnoreCase("GAME_ONLY")) { + if (Grasscutter.getConfig().RunMode == ServerRunMode.GAME_ONLY) { if (regionCache == null) { try { File file = new File(Grasscutter.getConfig().DATA_FOLDER + "query_cur_region.txt"); From d3ac090daac95b97247ab68d1b6b10b1eef7c4c4 Mon Sep 17 00:00:00 2001 From: mingjun97 Date: Sat, 30 Apr 2022 22:11:42 -0700 Subject: [PATCH 7/8] Fix crash and revise route * Fix another point which will trigger iOS client to crash * Revise `compareProtocolVersion` route to handle all method --- .../emu/grasscutter/server/dispatch/DispatchServer.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java index 9fc85b477..5ef8f7a89 100644 --- a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java +++ b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java @@ -319,6 +319,9 @@ 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", @@ -442,7 +445,8 @@ public final class DispatchServer { // hk4e-sdk-os.hoyoverse.com httpServer.get("/hk4e_global/mdk/agreement/api/getAgreementInfos", new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"marketing_agreements\":[]}}")); // hk4e-sdk-os.hoyoverse.com - httpServer.get("/hk4e_global/combo/granter/api/compareProtocolVersion", new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"modified\":true,\"protocol\":{\"id\":0,\"app_id\":4,\"language\":\"en\",\"user_proto\":\"\",\"priv_proto\":\"\",\"major\":7,\"minimum\":0,\"create_time\":\"0\",\"teenager_proto\":\"\",\"third_proto\":\"\"}}}")); + // this could be either GET or POST based on the observation of different clients + httpServer.all("/hk4e_global/combo/granter/api/compareProtocolVersion", new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"modified\":true,\"protocol\":{\"id\":0,\"app_id\":4,\"language\":\"en\",\"user_proto\":\"\",\"priv_proto\":\"\",\"major\":7,\"minimum\":0,\"create_time\":\"0\",\"teenager_proto\":\"\",\"third_proto\":\"\"}}}")); // Game data // hk4e-api-os.hoyoverse.com From 049086b21a78ce9b132d308f9ee8d03c29471b13 Mon Sep 17 00:00:00 2001 From: mingjun97 Date: Sat, 30 Apr 2022 22:30:47 -0700 Subject: [PATCH 8/8] Should fix all crashes when login for iOS client --- .../java/emu/grasscutter/server/dispatch/DispatchServer.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java index 5ef8f7a89..1a59cb9ad 100644 --- a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java +++ b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java @@ -346,6 +346,9 @@ 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));