From 0db017dfa40f1b668280fb1a9415173929f6c9ec Mon Sep 17 00:00:00 2001 From: xmplay <81370285+xmplay@users.noreply.github.com> Date: Fri, 22 Apr 2022 09:52:33 +0200 Subject: [PATCH 01/78] fix typos and grammar in README.md --- README.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 853c37ae3..1bff62df9 100644 --- a/README.md +++ b/README.md @@ -10,20 +10,20 @@ A WIP server reimplementation for *some anime game* 2.3-2.6 * Inventory features (recieving items/characters, upgrading items/characters, etc) * Gacha system * Friends list -* Co-op *partially* work +* Co-op *partially* works # Quick setup guide ### Note -* If you update from an older version, delete `config.json` for regeneration +* If you updated from an older version, delete `config.json` to regenerate it. ### Prerequisites -* JDK-8u202 ([mirror link](https://mirrors.huaweicloud.com/java/jdk/8u202-b08/) since Oracle required an account to download old builds) +* JDK-8u202 ([mirror link](https://mirrors.huaweicloud.com/java/jdk/8u202-b08/) since Oracle requires an account to download old builds) * Mongodb (recommended 4.0+) * Proxy daemon: mitmproxy (mitmdump, recommended), Fiddler Classic, etc. ### Starting up Grasscutter server (Assuming you are on Windows) -1. Setup compile environment `gradlew.bat` +1. Setup the compile environment with `gradlew.bat` 2. Compile Grasscutter with `gradlew jar` -3. Create a folder named `resources` in your Grasscutter directory, bring your `BinOutput` and `ExcelBinOutput` folders into it *(Check the wiki for more details how to get those.)* +3. Create a folder named `resources` in your Grasscutter directory, move your `BinOutput` and `ExcelBinOutput` folders there *(Check the wiki for more details on how to get those.)* 4. Run Grasscutter with `java -jar grasscutter.jar`. Make sure mongodb service is running as well. ### Connecting with the client @@ -37,7 +37,7 @@ A WIP server reimplementation for *some anime game* 2.3-2.6 2. Set network proxy to `127.0.0.1:8080` or the proxy port you specified. 4. *yoink* -* or you can use `run.cmd` to start Server & Proxy daemon with one click +* or you can use `run.cmd` to start the Server & Proxy daemon with one click # Grasscutter commands There is a dummy user named "Server" in every player's friends list that you can message to use commands. Commands also work in other chat rooms, such as private/team chats. @@ -54,26 +54,26 @@ There is a dummy user named "Server" in every player's friends list that you can `killall` -`setworldlevel [level]` - Relog to see effects properly +`setworldlevel [level]` - Changes your world level, relog to see effects properly `godmode` - Prevents you from taking damage -`resetconst` - Resets the constellation level on your current active character, will need to relog after using the command to see any changes. +`resetconst` - Resets the constellation level on your currently selected character, will need to relog after using the command to see any changes. -`setstats [stats] [amount]` - Changes the current character's specified stat. +`setstats [stats] [amount]` - Changes the currently selected character's specified stat. -`clearartifacts` - Deletes all unequipped and unlocked level 0 artifacts, **including yellow rarity ones** from your inventory +`clearartifacts` - Deletes all unequipped and unlocked level 0 artifacts, **including 5* rarity ones** from your inventory -`pos` - Gets your current coordinate. +`pos` - Gets your current coordinates. `weather [weather id] [climate id]` - Changes the current weather. *More commands will be updated in the [wiki](https://github.com/Melledy/Grasscutter/wiki/).* ### Bonus -When you want to teleport to somewhere, use the ingame marking function on Map, click Confirm. You will see your character falling from a very high destination, exact location that you marked. +When you want to teleport somewhere, use the in-game marking function on the Map, click Confirm. You will see your character falling from a very high spot at the exact location you marked. # Quick Troubleshooting * If compiling wasn't successful, please check your JDK installation (must be JDK 8 and validated JDK's bin PATH variable) -* My client doesn't connect, doesn't login, 4206, etc... - Mostly your proxy daemon setup is *the issue*, if using Fiddler make sure it running on another port except 8888 +* My client doesn't connect, doesn't login, 4206, etc... - Mostly your proxy daemon setup is *the issue*, if you're using Fiddler make sure it's running on a port other than 8888 * Startup sequence: Mongodb > Grasscutter > Proxy daemon (mitmdump, fiddler, etc.) > Client From 13ebc4f4fcd5e57dba98c87155cfa52b3478303c Mon Sep 17 00:00:00 2001 From: xmplay <81370285+xmplay@users.noreply.github.com> Date: Fri, 22 Apr 2022 09:55:44 +0200 Subject: [PATCH 02/78] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1bff62df9..b5538b012 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ There is a dummy user named "Server" in every player's friends list that you can `setstats [stats] [amount]` - Changes the currently selected character's specified stat. -`clearartifacts` - Deletes all unequipped and unlocked level 0 artifacts, **including 5* rarity ones** from your inventory +`clearartifacts` - Deletes all unequipped and unlocked level 0 artifacts, **including 5-star rarity ones** from your inventory `pos` - Gets your current coordinates. From 159b10af7214695a2927c1945d183806b9f0c8fe Mon Sep 17 00:00:00 2001 From: Yazawazi <47273265+Yazawazi@users.noreply.github.com> Date: Fri, 22 Apr 2022 22:27:40 +0800 Subject: [PATCH 03/78] AvatarFetterLevel --- .../emu/grasscutter/data/GenshinData.java | 10 ++++++++ .../data/def/AvatarFetterLevelData.java | 23 +++++++++++++++++ .../game/avatar/GenshinAvatar.java | 23 +++++++++++++++-- .../game/managers/InventoryManager.java | 25 +++++++++++++++++++ 4 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 src/main/java/emu/grasscutter/data/def/AvatarFetterLevelData.java diff --git a/src/main/java/emu/grasscutter/data/GenshinData.java b/src/main/java/emu/grasscutter/data/GenshinData.java index 7e14b1b46..155ebb60c 100644 --- a/src/main/java/emu/grasscutter/data/GenshinData.java +++ b/src/main/java/emu/grasscutter/data/GenshinData.java @@ -31,6 +31,7 @@ public class GenshinData { private static final Int2ObjectMap avatarSkillDepotDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap avatarSkillDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap avatarCurveDataMap = new Int2ObjectLinkedOpenHashMap<>(); + private static final Int2ObjectMap avatarFetterLevelDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap avatarPromoteDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap avatarTalentDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap proudSkillDataMap = new Int2ObjectOpenHashMap<>(); @@ -114,6 +115,10 @@ public class GenshinData { return playerLevelDataMap; } + public static Int2ObjectMap getAvatarFetterLevelDataMap() { + return avatarFetterLevelDataMap; + } + public static Int2ObjectMap getAvatarLevelDataMap() { return avatarLevelDataMap; } @@ -175,6 +180,11 @@ public class GenshinData { AvatarLevelData levelData = avatarLevelDataMap.get(level); return levelData != null ? levelData.getExp() : 0; } + + public static int getAvatarFetterLevelExpRequired(int level) { + AvatarFetterLevelData levelData = avatarFetterLevelDataMap.get(level); + return levelData != null ? levelData.getExp() : 0; + } public static Int2ObjectMap getProudSkillDataMap() { return proudSkillDataMap; diff --git a/src/main/java/emu/grasscutter/data/def/AvatarFetterLevelData.java b/src/main/java/emu/grasscutter/data/def/AvatarFetterLevelData.java new file mode 100644 index 000000000..8dd93eea5 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/def/AvatarFetterLevelData.java @@ -0,0 +1,23 @@ +package emu.grasscutter.data.def; + +import emu.grasscutter.data.GenshinResource; +import emu.grasscutter.data.ResourceType; + +@ResourceType(name = "AvatarFettersLevelExcelConfigData.json") +public class AvatarFetterLevelData extends GenshinResource { + private int FetterLevel; + private int NeedExp; + + @Override + public int getId() { + return this.FetterLevel; + } + + public int getLevel() { + return FetterLevel; + } + + public int getExp() { + return NeedExp; + } +} diff --git a/src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java b/src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java index 6906d3896..d4e998d46 100644 --- a/src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java +++ b/src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java @@ -89,6 +89,9 @@ public class GenshinAvatar { private int flyCloak; private int costume; private int bornTime; + + private int fetterLevel = 1; + private int fetterExp; public GenshinAvatar() { // Morhpia only! @@ -281,6 +284,22 @@ public class GenshinAvatar { return fetters; } + public int getFetterLevel() { + return fetterLevel; + } + + public void setFetterLevel(int fetterLevel) { + this.fetterLevel = fetterLevel; + } + + public int getFetterExp() { + return fetterExp; + } + + public void setFetterExp(int fetterExp) { + this.fetterExp = fetterExp; + } + public float getCurrentHp() { return currentHp; } @@ -702,8 +721,8 @@ public class GenshinAvatar { public AvatarInfo toProto() { AvatarFetterInfo.Builder avatarFetter = AvatarFetterInfo.newBuilder() - .setExpLevel(10) - .setExpNumber(6325); // Highest Level + .setExpLevel(this.getFetterLevel()) + .setExpNumber(this.getFetterExp()); if (this.getFetterList() != null) { for (int i = 0; i < this.getFetterList().size(); i++) { diff --git a/src/main/java/emu/grasscutter/game/managers/InventoryManager.java b/src/main/java/emu/grasscutter/game/managers/InventoryManager.java index 532155db1..96ab032b8 100644 --- a/src/main/java/emu/grasscutter/game/managers/InventoryManager.java +++ b/src/main/java/emu/grasscutter/game/managers/InventoryManager.java @@ -711,6 +711,31 @@ public class InventoryManager { player.sendPacket(new PacketAvatarUpgradeRsp(avatar, oldLevel, oldPropMap)); } + public void upgradeAvatarFetterLevel(GenshinPlayer player, GenshinAvatar avatar, int expGain) { + // May work. Not test. + int maxLevel = GenshinData.getAvatarFetterLevelDataMap().get(GenshinData.getAvatarFetterLevelDataMap().size() - 1).getLevel(); + int level = avatar.getFetterLevel(); + int exp = avatar.getFetterExp(); + int reqExp = GenshinData.getAvatarFetterLevelExpRequired(level); + + while (expGain > 0 && reqExp > 0 && level < maxLevel) { + int toGain = Math.min(expGain, reqExp - exp); + exp += toGain; + expGain -= toGain; + if (exp >= reqExp) { + exp = 0; + level += 1; + reqExp = GenshinData.getAvatarFetterLevelExpRequired(level); + } + } + + avatar.setFetterLevel(level); + avatar.setFetterExp(exp); + avatar.save(); + + player.sendPacket(new PacketAvatarPropNotify(avatar)); + } + public void upgradeAvatarSkill(GenshinPlayer player, long guid, int skillId) { // Sanity checks GenshinAvatar avatar = player.getAvatars().getAvatarByGuid(guid); From eb709046608e3bec95e289c22280a41a8188746c Mon Sep 17 00:00:00 2001 From: Yazawazi <47273265+Yazawazi@users.noreply.github.com> Date: Fri, 22 Apr 2022 23:46:24 +0800 Subject: [PATCH 04/78] setfetterlevel command --- .../commands/SetFetterLevelCommand.java | 41 +++++++++++++++++++ .../game/managers/InventoryManager.java | 2 +- 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 src/main/java/emu/grasscutter/command/commands/SetFetterLevelCommand.java diff --git a/src/main/java/emu/grasscutter/command/commands/SetFetterLevelCommand.java b/src/main/java/emu/grasscutter/command/commands/SetFetterLevelCommand.java new file mode 100644 index 000000000..60a7e4796 --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/SetFetterLevelCommand.java @@ -0,0 +1,41 @@ +package emu.grasscutter.command.commands; + +import java.util.List; + +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.GenshinPlayer; +import emu.grasscutter.game.entity.EntityAvatar; + +@Command(label = "setfetterlevel", usage = "setfetterlevel ", + description = "Sets your fetter level for your current active character", + aliases = {"setfetterlvl"}, permission = "player.setfetterlevel") +public final class SetFetterLevelCommand implements CommandHandler { + + @Override + public void execute(GenshinPlayer sender, List args) { + if (sender == null) { + CommandHandler.sendMessage(null, "Run this command in-game."); + return; + } + + if (args.size() < 1) { + CommandHandler.sendMessage(sender, "Usage: setfetterlevel "); + return; + } + + try { + int fetterLevel = Integer.parseInt(args.get(0)); + if (fetterLevel < 0 || fetterLevel > 10) { + CommandHandler.sendMessage(sender, "Fetter level must be between 0 and 10."); + return; + } + EntityAvatar avatar = sender.getTeamManager().getCurrentAvatarEntity(); + avatar.getAvatar().setFetterLevel(fetterLevel); + CommandHandler.sendMessage(sender, "Fetter level set to " + fetterLevel); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(null, "Invalid fetter level."); + } + } + +} diff --git a/src/main/java/emu/grasscutter/game/managers/InventoryManager.java b/src/main/java/emu/grasscutter/game/managers/InventoryManager.java index 96ab032b8..4ea9e6bc1 100644 --- a/src/main/java/emu/grasscutter/game/managers/InventoryManager.java +++ b/src/main/java/emu/grasscutter/game/managers/InventoryManager.java @@ -713,7 +713,7 @@ public class InventoryManager { public void upgradeAvatarFetterLevel(GenshinPlayer player, GenshinAvatar avatar, int expGain) { // May work. Not test. - int maxLevel = GenshinData.getAvatarFetterLevelDataMap().get(GenshinData.getAvatarFetterLevelDataMap().size() - 1).getLevel(); + int maxLevel = 10; // Keep it until I think of a more "elegant" way int level = avatar.getFetterLevel(); int exp = avatar.getFetterExp(); int reqExp = GenshinData.getAvatarFetterLevelExpRequired(level); From 48db64469236418a000ea0e30d624f5d3e32aa09 Mon Sep 17 00:00:00 2001 From: Yazawazi <47273265+Yazawazi@users.noreply.github.com> Date: Sat, 23 Apr 2022 01:36:48 +0800 Subject: [PATCH 05/78] command fix --- .../commands/SetFetterLevelCommand.java | 13 +++++-- .../send/PacketAvatarFetterDataNotify.java | 38 +++++++++++++++++++ 2 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 src/main/java/emu/grasscutter/server/packet/send/PacketAvatarFetterDataNotify.java diff --git a/src/main/java/emu/grasscutter/command/commands/SetFetterLevelCommand.java b/src/main/java/emu/grasscutter/command/commands/SetFetterLevelCommand.java index 60a7e4796..74847a49a 100644 --- a/src/main/java/emu/grasscutter/command/commands/SetFetterLevelCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/SetFetterLevelCommand.java @@ -4,8 +4,10 @@ import java.util.List; import emu.grasscutter.command.Command; import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.data.GenshinData; import emu.grasscutter.game.GenshinPlayer; -import emu.grasscutter.game.entity.EntityAvatar; +import emu.grasscutter.game.avatar.GenshinAvatar; +import emu.grasscutter.server.packet.send.PacketAvatarFetterDataNotify; @Command(label = "setfetterlevel", usage = "setfetterlevel ", description = "Sets your fetter level for your current active character", @@ -30,8 +32,13 @@ public final class SetFetterLevelCommand implements CommandHandler { CommandHandler.sendMessage(sender, "Fetter level must be between 0 and 10."); return; } - EntityAvatar avatar = sender.getTeamManager().getCurrentAvatarEntity(); - avatar.getAvatar().setFetterLevel(fetterLevel); + GenshinAvatar avatar = sender.getTeamManager().getCurrentAvatarEntity().getAvatar(); + + avatar.setFetterLevel(fetterLevel); + avatar.setFetterExp(GenshinData.getAvatarFetterLevelDataMap().get(fetterLevel).getExp()); + avatar.save(); + + sender.sendPacket(new PacketAvatarFetterDataNotify(avatar)); CommandHandler.sendMessage(sender, "Fetter level set to " + fetterLevel); } catch (NumberFormatException ignored) { CommandHandler.sendMessage(null, "Invalid fetter level."); diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketAvatarFetterDataNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketAvatarFetterDataNotify.java new file mode 100644 index 000000000..86cea0e02 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketAvatarFetterDataNotify.java @@ -0,0 +1,38 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.game.avatar.GenshinAvatar; +import emu.grasscutter.game.props.FetterState; +import emu.grasscutter.net.packet.GenshinPacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.AvatarFetterDataNotifyOuterClass.AvatarFetterDataNotify; +import emu.grasscutter.net.proto.AvatarFetterInfoOuterClass.AvatarFetterInfo; +import emu.grasscutter.net.proto.FetterDataOuterClass.FetterData; + +public class PacketAvatarFetterDataNotify extends GenshinPacket { + + public PacketAvatarFetterDataNotify(GenshinAvatar avatar) { + super(PacketOpcodes.AvatarFetterDataNotify); + + AvatarFetterInfo.Builder avatarFetter = AvatarFetterInfo.newBuilder() + .setExpLevel(avatar.getFetterLevel()) + .setExpNumber(avatar.getFetterExp()); + + if (avatar.getFetterList() != null) { + for (int i = 0; i < avatar.getFetterList().size(); i++) { + avatarFetter.addFetterList( + FetterData.newBuilder() + .setFetterId(avatar.getFetterList().get(i)) + .setFetterState(FetterState.FINISH.getValue()) + ); + } + } + + AvatarFetterInfo avatarFetterInfo = avatarFetter.build(); + + AvatarFetterDataNotify proto = AvatarFetterDataNotify.newBuilder() + .putFetterInfoMap(avatar.getGuid(), avatarFetterInfo) + .build(); + + this.setData(proto); + } +} From a52eaf9bbcaccdc794085e4f65e694a63c5cd048 Mon Sep 17 00:00:00 2001 From: memetrollsXD Date: Fri, 22 Apr 2022 23:19:58 +0200 Subject: [PATCH 06/78] Use count for level for equips --- .../grasscutter/command/commands/GiveCommand.java | 13 ++++++------- .../emu/grasscutter/game/inventory/GenshinItem.java | 2 +- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/main/java/emu/grasscutter/command/commands/GiveCommand.java b/src/main/java/emu/grasscutter/command/commands/GiveCommand.java index a533130bc..c427d9d89 100644 --- a/src/main/java/emu/grasscutter/command/commands/GiveCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/GiveCommand.java @@ -91,17 +91,16 @@ public final class GiveCommand implements CommandHandler { this.item(targetPlayer, itemData, amount); - CommandHandler.sendMessage(sender, String.format("Given %s of %s to %s.", amount, item, target)); + if (!itemData.isEquip()) CommandHandler.sendMessage(sender, String.format("Given %s of %s to %s.", amount, item, target)); + else CommandHandler.sendMessage(sender, String.format("Given %s with level %s to %s", item, amount, target)); } private void item(GenshinPlayer player, ItemData itemData, int amount) { if (itemData.isEquip()) { - List items = new LinkedList<>(); - for (int i = 0; i < amount; i++) { - items.add(new GenshinItem(itemData)); - } - player.getInventory().addItems(items); - player.sendPacket(new PacketItemAddHintNotify(items, ActionReason.SubfieldDrop)); + GenshinItem item = new GenshinItem(itemData); + item.setLevel(amount); + player.getInventory().addItem(item); + player.sendPacket(new PacketItemAddHintNotify(item, ActionReason.SubfieldDrop)); } else { GenshinItem genshinItem = new GenshinItem(itemData); genshinItem.setCount(amount); diff --git a/src/main/java/emu/grasscutter/game/inventory/GenshinItem.java b/src/main/java/emu/grasscutter/game/inventory/GenshinItem.java index 0b0db49c4..fccc49307 100644 --- a/src/main/java/emu/grasscutter/game/inventory/GenshinItem.java +++ b/src/main/java/emu/grasscutter/game/inventory/GenshinItem.java @@ -90,7 +90,7 @@ public class GenshinItem { // Equip data if (getItemType() == ItemType.ITEM_WEAPON) { - this.level = 1; + this.level = this.count > 1 ? this.count : 1; this.affixes = new ArrayList<>(2); if (getItemData().getSkillAffix() != null) { for (int skillAffix : getItemData().getSkillAffix()) { From 7ccecda07c5d0a1ee5e965a82aae731fd99637ca Mon Sep 17 00:00:00 2001 From: Yazawazi <47273265+Yazawazi@users.noreply.github.com> Date: Sat, 23 Apr 2022 06:08:37 +0800 Subject: [PATCH 07/78] AvatarFetterLevelReward --- .../emu/grasscutter/data/GenshinData.java | 5 +++ .../emu/grasscutter/data/def/AvatarData.java | 9 +++++ .../data/def/FetterCharacterCardData.java | 24 +++++++++++++ .../game/avatar/GenshinAvatar.java | 11 ++++++ .../HandlerAvatarFetterLevelRewardReq.java | 32 +++++++++++++++++ .../PacketAvatarFetterLevelRewardRsp.java | 35 +++++++++++++++++++ 6 files changed, 116 insertions(+) create mode 100644 src/main/java/emu/grasscutter/data/def/FetterCharacterCardData.java create mode 100644 src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarFetterLevelRewardReq.java create mode 100644 src/main/java/emu/grasscutter/server/packet/send/PacketAvatarFetterLevelRewardRsp.java diff --git a/src/main/java/emu/grasscutter/data/GenshinData.java b/src/main/java/emu/grasscutter/data/GenshinData.java index 155ebb60c..98f235c1e 100644 --- a/src/main/java/emu/grasscutter/data/GenshinData.java +++ b/src/main/java/emu/grasscutter/data/GenshinData.java @@ -58,6 +58,7 @@ public class GenshinData { private static final Int2ObjectMap sceneDataMap = new Int2ObjectLinkedOpenHashMap<>(); private static final Int2ObjectMap fetterDataMap = new Int2ObjectOpenHashMap<>(); + private static final Int2ObjectMap fetterCharacterCardDataMap = new Int2ObjectOpenHashMap<>(); // Cache private static Map> fetters = new HashMap<>(); @@ -119,6 +120,10 @@ public class GenshinData { return avatarFetterLevelDataMap; } + public static Int2ObjectMap getFetterCharacterCardDataMap() { + return fetterCharacterCardDataMap; + } + public static Int2ObjectMap getAvatarLevelDataMap() { return avatarLevelDataMap; } diff --git a/src/main/java/emu/grasscutter/data/def/AvatarData.java b/src/main/java/emu/grasscutter/data/def/AvatarData.java index 8097e04a7..a714344ae 100644 --- a/src/main/java/emu/grasscutter/data/def/AvatarData.java +++ b/src/main/java/emu/grasscutter/data/def/AvatarData.java @@ -57,6 +57,7 @@ public class AvatarData extends GenshinResource { private IntList abilities; private List fetters; + private int nameCardRewardId; @Override public int getId(){ @@ -199,12 +200,20 @@ public class AvatarData extends GenshinResource { return fetters; } + public int getNameCardRewardId() { + return nameCardRewardId; + } + @Override public void onLoad() { this.skillDepot = GenshinData.getAvatarSkillDepotDataMap().get(this.SkillDepotId); // Get fetters from GenshinData this.fetters = GenshinData.getFetterDataEntries().get(this.Id); + + if (GenshinData.getFetterCharacterCardDataMap().get(this.Id) != null) { + this.nameCardRewardId = GenshinData.getFetterCharacterCardDataMap().get(this.Id).getRewardId(); + } int size = GenshinData.getAvatarCurveDataMap().size(); this.hpGrowthCurve = new float[size]; diff --git a/src/main/java/emu/grasscutter/data/def/FetterCharacterCardData.java b/src/main/java/emu/grasscutter/data/def/FetterCharacterCardData.java new file mode 100644 index 000000000..2bf96ad8e --- /dev/null +++ b/src/main/java/emu/grasscutter/data/def/FetterCharacterCardData.java @@ -0,0 +1,24 @@ +package emu.grasscutter.data.def; + +import emu.grasscutter.data.GenshinResource; +import emu.grasscutter.data.ResourceType; +import emu.grasscutter.data.ResourceType.LoadPriority; + +@ResourceType(name = "FetterCharacterCardExcelConfigData.json", loadPriority = LoadPriority.HIGHEST) +public class FetterCharacterCardData extends GenshinResource { + private int AvatarId; + private int RewardId; + + @Override + public int getId() { + return AvatarId; + } + + public int getRewardId() { + return RewardId; + } + + @Override + public void onLoad() { + } +} diff --git a/src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java b/src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java index d4e998d46..5ad553f9c 100644 --- a/src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java +++ b/src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java @@ -92,6 +92,8 @@ public class GenshinAvatar { private int fetterLevel = 1; private int fetterExp; + + private int nameCardRewardId; public GenshinAvatar() { // Morhpia only! @@ -110,6 +112,7 @@ public class GenshinAvatar { public GenshinAvatar(AvatarData data) { this(); this.avatarId = data.getId(); + this.nameCardRewardId = data.getNameCardRewardId(); this.data = data; this.bornTime = (int) (System.currentTimeMillis() / 1000); this.flyCloak = 140001; @@ -172,6 +175,14 @@ public class GenshinAvatar { this.satiation = satiation; } + public int getNameCardRewardId() { + return nameCardRewardId; + } + + public void setNameCardRewardId(int nameCardRewardId) { + this.nameCardRewardId = nameCardRewardId; + } + public int getSatiationPenalty() { return satiationPenalty; } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarFetterLevelRewardReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarFetterLevelRewardReq.java new file mode 100644 index 000000000..d3431d535 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarFetterLevelRewardReq.java @@ -0,0 +1,32 @@ +package emu.grasscutter.server.packet.recv; + +import emu.grasscutter.net.packet.Opcodes; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.AvatarFetterLevelRewardReqOuterClass.AvatarFetterLevelRewardReq; +import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.server.packet.send.PacketAvatarFetterLevelRewardRsp; +import emu.grasscutter.net.packet.PacketHandler; + +@Opcodes(PacketOpcodes.AvatarFetterLevelRewardReq) +public class HandlerAvatarFetterLevelRewardReq extends PacketHandler { + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + AvatarFetterLevelRewardReq req = AvatarFetterLevelRewardReq.parseFrom(payload); + if (req.getFetterLevel() < 10) { + // You don't have a full level of fetter level, why do you want to get a divorce certificate? + session.send(new PacketAvatarFetterLevelRewardRsp(req.getAvatarGuid(), req.getFetterLevel())); + } else { + long avatarGuid = req.getAvatarGuid(); + + int rewardId = session + .getPlayer() + .getAvatars() + .getAvatarByGuid(avatarGuid) + .getNameCardRewardId(); + + // Here need to send the packets, I am not at all clear ah! + + session.send(new PacketAvatarFetterLevelRewardRsp(avatarGuid, req.getFetterLevel(), rewardId)); + } + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketAvatarFetterLevelRewardRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketAvatarFetterLevelRewardRsp.java new file mode 100644 index 000000000..e9d4895ff --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketAvatarFetterLevelRewardRsp.java @@ -0,0 +1,35 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.GenshinPacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.AvatarFetterLevelRewardRspOuterClass.AvatarFetterLevelRewardRsp; + +public class PacketAvatarFetterLevelRewardRsp extends GenshinPacket { + + public PacketAvatarFetterLevelRewardRsp(long guid, int fetterLevel, int rewardId) { + super(PacketOpcodes.AvatarFetterLevelRewardRsp); + + AvatarFetterLevelRewardRsp proto = AvatarFetterLevelRewardRsp.newBuilder() + .setAvatarGuid(guid) + .setFetterLevel(fetterLevel) + .setRetcode(0) + .setRewardId(rewardId) + .build(); + + this.setData(proto); + } + + public PacketAvatarFetterLevelRewardRsp(long guid, int fetterLevel) { + super(PacketOpcodes.AvatarFetterLevelRewardRsp); + + AvatarFetterLevelRewardRsp proto = AvatarFetterLevelRewardRsp.newBuilder() + .setAvatarGuid(guid) + .setFetterLevel(fetterLevel) + .setRetcode(1) + .setRewardId(0) + .build(); + + this.setData(proto); + } + +} From 5ea5ea238c7ec7aba834e79fc96ab43c7cc6f669 Mon Sep 17 00:00:00 2001 From: Yazawazi <47273265+Yazawazi@users.noreply.github.com> Date: Sat, 23 Apr 2022 10:51:22 +0800 Subject: [PATCH 08/78] OpenCond for unlocking judgment --- .../grasscutter/data/common/OpenCondData.java | 24 +++++++++++++++++++ .../emu/grasscutter/data/def/FetterData.java | 8 +++++++ 2 files changed, 32 insertions(+) create mode 100644 src/main/java/emu/grasscutter/data/common/OpenCondData.java diff --git a/src/main/java/emu/grasscutter/data/common/OpenCondData.java b/src/main/java/emu/grasscutter/data/common/OpenCondData.java new file mode 100644 index 000000000..9e7b62b11 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/common/OpenCondData.java @@ -0,0 +1,24 @@ +package emu.grasscutter.data.common; + +import java.util.List; + +public class OpenCondData { + private String CondType; + private List ParamList; + + public String getCondType() { + return CondType; + } + + public void setCondType(String condType) { + CondType = condType; + } + + public List getParamList() { + return ParamList; + } + + public void setParamList(List paramList) { + ParamList = paramList; + } +} diff --git a/src/main/java/emu/grasscutter/data/def/FetterData.java b/src/main/java/emu/grasscutter/data/def/FetterData.java index d17c940d1..dc88fd795 100644 --- a/src/main/java/emu/grasscutter/data/def/FetterData.java +++ b/src/main/java/emu/grasscutter/data/def/FetterData.java @@ -1,13 +1,17 @@ package emu.grasscutter.data.def; +import java.util.List; + import emu.grasscutter.data.GenshinResource; import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType.LoadPriority; +import emu.grasscutter.data.common.OpenCondData; @ResourceType(name = {"FetterInfoExcelConfigData.json", "FettersExcelConfigData.json", "FetterStoryExcelConfigData.json"}, loadPriority = LoadPriority.HIGHEST) public class FetterData extends GenshinResource { private int AvatarId; private int FetterId; + private List OpenCond; @Override public int getId() { @@ -18,6 +22,10 @@ public class FetterData extends GenshinResource { return AvatarId; } + public List getOpenConds() { + return OpenCond; + } + @Override public void onLoad() { } From 3707ee54776bef5d33376f6d566ed45a70ffe28c Mon Sep 17 00:00:00 2001 From: fumbling <104180076+fumbling644o@users.noreply.github.com> Date: Fri, 22 Apr 2022 22:02:13 -0400 Subject: [PATCH 09/78] Make gradlew executable --- gradlew | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 gradlew diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 From d1d8378e15cf0d094d3ab6c2979216078cb3715e Mon Sep 17 00:00:00 2001 From: fumbling <104180076+fumbling644o@users.noreply.github.com> Date: Fri, 22 Apr 2022 22:02:41 -0400 Subject: [PATCH 10/78] Workflow: Use Ubuntu and add PR triggers --- .github/workflows/build.yml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8457bf7ac..26a075ccc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -3,9 +3,14 @@ on: push: branches: - "stable" + pull_request: + types: + - opened + - synchronize + - reopened jobs: Build-Server-Jar: - runs-on: windows-latest + runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v2 @@ -15,10 +20,9 @@ jobs: distribution: temurin java-version: '8' - name: Run Gradle - run: .\gradlew.bat && .\gradlew jar + run: ./gradlew && ./gradlew jar - name: Upload build uses: actions/upload-artifact@v3 with: name: Grasscutter path: grasscutter.jar - From 220f252f0fb9a4ba51572690a5a028dd666a9779 Mon Sep 17 00:00:00 2001 From: fumbling <104180076+fumbling644o@users.noreply.github.com> Date: Fri, 22 Apr 2022 22:40:19 -0400 Subject: [PATCH 11/78] Workflow: Switch from Temurin 8 to 16 --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 26a075ccc..cc0193c1a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,7 +18,7 @@ jobs: uses: actions/setup-java@v3 with: distribution: temurin - java-version: '8' + java-version: '16' - name: Run Gradle run: ./gradlew && ./gradlew jar - name: Upload build From c77aa3ff79a274ac4cef12caf31da2309909f21a Mon Sep 17 00:00:00 2001 From: WangYneos Date: Sat, 23 Apr 2022 12:02:10 +0800 Subject: [PATCH 12/78] make mitmproxy ignore non Genshin hosts --- README.md | 2 +- start.cmd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 853c37ae3..a08da974b 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ A WIP server reimplementation for *some anime game* 2.3-2.6 ### Connecting with the client ½. Create an account using *server console command* below 1. Run a proxy daemon: (choose either one) - - mitmdump: `mitmdump -s proxy.py -k` + - mitmdump: `mitmdump -s proxy.py -k`--allow-hosts ".*\.yuanshen\.com|.*\.mihoyo\.com|.*\.hoyoverse\.com" - Fiddler Classic: Run Fiddler Classic, turn on `Decrypt https traffic` in setting and change the default port there (Tools -> Options -> Connections) to anything other than `8888`, and load [this script](https://github.lunatic.moe/fiddlerscript). - [Hosts file](https://github.com/Melledy/Grasscutter/wiki/Running#traffic-route-map) 2. Trust CA certificate: diff --git a/start.cmd b/start.cmd index 8c89f359e..48de450d1 100644 --- a/start.cmd +++ b/start.cmd @@ -75,7 +75,7 @@ for /f "tokens=2*" %%a in ('reg query "HKCU\Software\Microsoft\Windows\CurrentVe @rem TODO: External proxy when ORIG_PROXY_ENABLE == 0x1 echo set ws = createobject("wscript.shell") > "%temp%\proxy.vbs" echo ws.currentdirectory = "%MITMDUMP_PATH%" >> "%temp%\proxy.vbs" -echo ws.run "cmd /c mitmdump.exe -s "^&chr(34)^&"%PROXY_SCRIPT_NAME%"^&chr(34)^&" -k",0 >> "%temp%\proxy.vbs" +echo ws.run "cmd /c mitmdump.exe -s "^&chr(34)^&"%PROXY_SCRIPT_NAME%"^&chr(34)^&" -k --allow-hosts \".*\.yuanshen\.com|.*\.mihoyo\.com|.*\.hoyoverse\.com\"",0 >> "%temp%\proxy.vbs" "%temp%\proxy.vbs" del /f /q "%temp%\proxy.vbs" >nul 2>nul From ae00eb69d67fd5b7561410f77f6426f0599fa8a3 Mon Sep 17 00:00:00 2001 From: WangYneos <42824603+WangYneos@users.noreply.github.com> Date: Sat, 23 Apr 2022 12:07:23 +0800 Subject: [PATCH 13/78] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a08da974b..370dc3c99 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ A WIP server reimplementation for *some anime game* 2.3-2.6 ### Connecting with the client ½. Create an account using *server console command* below 1. Run a proxy daemon: (choose either one) - - mitmdump: `mitmdump -s proxy.py -k`--allow-hosts ".*\.yuanshen\.com|.*\.mihoyo\.com|.*\.hoyoverse\.com" + - mitmdump: `mitmdump -s proxy.py -k --allow-hosts ".*\.yuanshen\.com|.*\.mihoyo\.com|.*\.hoyoverse\.com"` - Fiddler Classic: Run Fiddler Classic, turn on `Decrypt https traffic` in setting and change the default port there (Tools -> Options -> Connections) to anything other than `8888`, and load [this script](https://github.lunatic.moe/fiddlerscript). - [Hosts file](https://github.com/Melledy/Grasscutter/wiki/Running#traffic-route-map) 2. Trust CA certificate: From 22e5ca5545039df239bd0d6bb495c107fc206745 Mon Sep 17 00:00:00 2001 From: WangYneos <42824603+WangYneos@users.noreply.github.com> Date: Sat, 23 Apr 2022 13:57:48 +0800 Subject: [PATCH 14/78] Update start.cmd --- start.cmd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/start.cmd b/start.cmd index 48de450d1..59ff78160 100644 --- a/start.cmd +++ b/start.cmd @@ -75,7 +75,7 @@ for /f "tokens=2*" %%a in ('reg query "HKCU\Software\Microsoft\Windows\CurrentVe @rem TODO: External proxy when ORIG_PROXY_ENABLE == 0x1 echo set ws = createobject("wscript.shell") > "%temp%\proxy.vbs" echo ws.currentdirectory = "%MITMDUMP_PATH%" >> "%temp%\proxy.vbs" -echo ws.run "cmd /c mitmdump.exe -s "^&chr(34)^&"%PROXY_SCRIPT_NAME%"^&chr(34)^&" -k --allow-hosts \".*\.yuanshen\.com|.*\.mihoyo\.com|.*\.hoyoverse\.com\"",0 >> "%temp%\proxy.vbs" +echo ws.run "cmd /c mitmdump.exe -s "^&chr(34)^&"%PROXY_SCRIPT_NAME%"^&chr(34)^&" -k --allow-hosts ^&chr(34)^&.*\.yuanshen\.com|.*\.mihoyo\.com|.*\.hoyoverse\.com^&chr(34)^&",0 >> "%temp%\proxy.vbs" "%temp%\proxy.vbs" del /f /q "%temp%\proxy.vbs" >nul 2>nul @@ -157,4 +157,4 @@ call :LOG [INFO] See you again :) goto :EOF :LOG -echo [%time:~0,8%] %* \ No newline at end of file +echo [%time:~0,8%] %* From 8afcc0b89a7fb45f29af3c03c6ba71c6c8aa46dc Mon Sep 17 00:00:00 2001 From: KingRainbow44 Date: Sat, 23 Apr 2022 02:20:18 -0400 Subject: [PATCH 15/78] Add message for bind failure (HTTP) --- .../grasscutter/netty/MihoyoKcpServer.java | 5 ++-- .../server/dispatch/DispatchServer.java | 24 +++++++++++++++---- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/main/java/emu/grasscutter/netty/MihoyoKcpServer.java b/src/main/java/emu/grasscutter/netty/MihoyoKcpServer.java index c3a9297b2..41e8ad412 100644 --- a/src/main/java/emu/grasscutter/netty/MihoyoKcpServer.java +++ b/src/main/java/emu/grasscutter/netty/MihoyoKcpServer.java @@ -64,9 +64,8 @@ public class MihoyoKcpServer extends Thread { // Wait until the server socket is closed. f.channel().closeFuture().sync(); - } catch (Exception e) { - // TODO Auto-generated catch block - e.printStackTrace(); + } catch (Exception exception) { + Grasscutter.getLogger().error("Unable to start game server.", exception); } finally { // Close finish(); diff --git a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java index 7c7915675..ea7c1166e 100644 --- a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java +++ b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java @@ -24,6 +24,7 @@ import emu.grasscutter.utils.Utils; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import java.io.*; +import java.net.BindException; import java.net.InetSocketAddress; import java.net.URI; import java.net.URLDecoder; @@ -158,12 +159,21 @@ public final class DispatchServer { Grasscutter.getLogger().error("[Dispatch] Error while initializing region info!", e); } } + + private HttpServer safelyCreateServer(InetSocketAddress address) { + try { + return HttpServer.create(address, 0); + } catch (BindException ignored) { + Grasscutter.getLogger().error("Unable to bind to port: " + getAddress().getPort() + " (HTTP)"); + } catch (Exception exception) { + Grasscutter.getLogger().error("Unable to start HTTP server.", exception); + } return null; + } public void start() throws Exception { HttpServer server; if (Grasscutter.getConfig().getDispatchOptions().UseSSL) { - HttpsServer httpsServer; - httpsServer = HttpsServer.create(getAddress(), 0); + HttpsServer httpsServer = HttpsServer.create(getAddress(), 0); SSLContext sslContext = SSLContext.getInstance("TLS"); try (FileInputStream fis = new FileInputStream(Grasscutter.getConfig().getDispatchOptions().KeystorePath)) { char[] keystorePassword = Grasscutter.getConfig().getDispatchOptions().KeystorePassword.toCharArray(); @@ -176,14 +186,20 @@ public final class DispatchServer { httpsServer.setHttpsConfigurator(new HttpsConfigurator(sslContext)); server = httpsServer; + } catch (BindException ignored) { + Grasscutter.getLogger().error("Unable to bind to port: " + getAddress().getPort() + " (HTTPS)"); + server = this.safelyCreateServer(this.getAddress()); } catch (Exception e) { Grasscutter.getLogger().warn("[Dispatch] No SSL cert found! Falling back to HTTP server."); Grasscutter.getConfig().getDispatchOptions().UseSSL = false; - server = HttpServer.create(getAddress(), 0); + server = this.safelyCreateServer(this.getAddress()); } } else { - server = HttpServer.create(getAddress(), 0); + server = this.safelyCreateServer(this.getAddress()); } + + if(server == null) + throw new NullPointerException("An HTTP server was not created."); server.createContext("/", t -> responseHTML(t, "Hello")); From 39e590dfb85988aa7c6e3cdad72f1a90edf238bd Mon Sep 17 00:00:00 2001 From: WangYneos Date: Sat, 23 Apr 2022 14:28:14 +0800 Subject: [PATCH 16/78] Fix Syntax --- start.cmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/start.cmd b/start.cmd index 59ff78160..c9dcc606f 100644 --- a/start.cmd +++ b/start.cmd @@ -75,7 +75,7 @@ for /f "tokens=2*" %%a in ('reg query "HKCU\Software\Microsoft\Windows\CurrentVe @rem TODO: External proxy when ORIG_PROXY_ENABLE == 0x1 echo set ws = createobject("wscript.shell") > "%temp%\proxy.vbs" echo ws.currentdirectory = "%MITMDUMP_PATH%" >> "%temp%\proxy.vbs" -echo ws.run "cmd /c mitmdump.exe -s "^&chr(34)^&"%PROXY_SCRIPT_NAME%"^&chr(34)^&" -k --allow-hosts ^&chr(34)^&.*\.yuanshen\.com|.*\.mihoyo\.com|.*\.hoyoverse\.com^&chr(34)^&",0 >> "%temp%\proxy.vbs" +echo ws.run "cmd /c mitmdump.exe -s "^&chr(34)^&"%PROXY_SCRIPT_NAME%"^&chr(34)^&" -k --allow-hosts "^&chr(34)^&".*\.yuanshen\.com|.*\.mihoyo\.com|.*\.hoyoverse\.com"^&chr(34),0 >> "%temp%\proxy.vbs" "%temp%\proxy.vbs" del /f /q "%temp%\proxy.vbs" >nul 2>nul From 1c3df04f4505a5adc26e719b48a13fe8d89d603a Mon Sep 17 00:00:00 2001 From: Jaida Wu Date: Sat, 23 Apr 2022 15:33:10 +0800 Subject: [PATCH 17/78] Fix proxy daemon can't find script Signed-off-by: Jaida Wu --- start.cmd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/start.cmd b/start.cmd index 8c525dfc8..8d682a213 100644 --- a/start.cmd +++ b/start.cmd @@ -75,9 +75,9 @@ for /f "tokens=2*" %%a in ('reg query "HKCU\Software\Microsoft\Windows\CurrentVe @rem TODO: External proxy when ORIG_PROXY_ENABLE == 0x1 echo set ws = createobject("wscript.shell") > "%temp%\proxy.vbs" if not "%MITMDUMP_PATH%" == "" ( -echo ws.currentdirectory = "%MITMDUMP_PATH%" >> "%temp%\proxy.vbs" + echo ws.currentdirectory = "%MITMDUMP_PATH%" >> "%temp%\proxy.vbs" ) -echo ws.run "cmd /c mitmdump.exe -s "^&chr(34)^&"%PROXY_SCRIPT_NAME%"^&chr(34)^&" -k",0 >> "%temp%\proxy.vbs" +echo ws.run "cmd /c mitmdump.exe -s "^&chr(34)^&"%CUR_PATH%%PROXY_SCRIPT_NAME%"^&chr(34)^&" -k",0 >> "%temp%\proxy.vbs" "%temp%\proxy.vbs" del /f /q "%temp%\proxy.vbs" >nul 2>nul From cd08c52b359ed1a8c3e7019d806d1c6eb3542a2c Mon Sep 17 00:00:00 2001 From: Jaida Wu Date: Sat, 23 Apr 2022 15:39:56 +0800 Subject: [PATCH 18/78] Fix PlayerBirthday exception Signed-off-by: Jaida Wu --- src/main/java/emu/grasscutter/game/player/PlayerBirthday.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/emu/grasscutter/game/player/PlayerBirthday.java b/src/main/java/emu/grasscutter/game/player/PlayerBirthday.java index 733f58ee5..13a8e7c58 100644 --- a/src/main/java/emu/grasscutter/game/player/PlayerBirthday.java +++ b/src/main/java/emu/grasscutter/game/player/PlayerBirthday.java @@ -1,7 +1,9 @@ package emu.grasscutter.game.player; +import dev.morphia.annotations.Entity; import emu.grasscutter.net.proto.BirthdayOuterClass.Birthday; +@Entity public class PlayerBirthday { private int day; private int month; From 3eabd4034520906cd91774787fc0ef3c944b8e17 Mon Sep 17 00:00:00 2001 From: Jaida Wu Date: Sat, 23 Apr 2022 16:47:13 +0800 Subject: [PATCH 19/78] Make some control panel happy Signed-off-by: Jaida Wu --- src/main/java/emu/grasscutter/Grasscutter.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/emu/grasscutter/Grasscutter.java b/src/main/java/emu/grasscutter/Grasscutter.java index ad1107e8e..5335a2254 100644 --- a/src/main/java/emu/grasscutter/Grasscutter.java +++ b/src/main/java/emu/grasscutter/Grasscutter.java @@ -118,6 +118,7 @@ public final class Grasscutter { public static void startConsole() { String input; + getLogger().info("Done! For help, type \"help\""); try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) { while ((input = br.readLine()) != null) { try { From 91894380cbf8710052d19ebf297c632d4320f02d Mon Sep 17 00:00:00 2001 From: TheLostTree <65834918+TheLostTree@users.noreply.github.com> Date: Sat, 23 Apr 2022 03:19:33 -0700 Subject: [PATCH 20/78] drowning added --- .../recv/HandlerSceneEntityDrownReq.java | 43 +++++++++++++++++++ .../send/PacketSceneEntityDrownRsp.java | 4 ++ 2 files changed, 47 insertions(+) create mode 100644 src/main/java/emu/grasscutter/server/packet/recv/HandlerSceneEntityDrownReq.java create mode 100644 src/main/java/emu/grasscutter/server/packet/send/PacketSceneEntityDrownRsp.java diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSceneEntityDrownReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSceneEntityDrownReq.java new file mode 100644 index 000000000..91f27c65b --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSceneEntityDrownReq.java @@ -0,0 +1,43 @@ +package emu.grasscutter.server.packet.recv; + +import emu.grasscutter.game.entity.GenshinEntity; +import emu.grasscutter.game.props.LifeState; +import emu.grasscutter.net.packet.Opcodes; +import emu.grasscutter.net.packet.PacketHandler; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.SceneEntityDrownReqOuterClass.SceneEntityDrownReq; +import emu.grasscutter.net.proto.VisionTypeOuterClass; +import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.server.packet.send.PacketAvatarChangeCostumeRsp; +import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify; +import emu.grasscutter.server.packet.send.PacketSceneEntityDisappearNotify; +import emu.grasscutter.server.packet.send.PacketSceneEntityDrownRsp; + +@Opcodes(PacketOpcodes.SceneEntityDrownReq) +public class HandlerSceneEntityDrownReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + SceneEntityDrownReq req = SceneEntityDrownReq.parseFrom(payload); + + + GenshinEntity entity = session.getPlayer().getScene().getEntityById(req.getEntityId()); + + + PacketLifeStateChangeNotify lifeStateChangeNotify = new PacketLifeStateChangeNotify(entity, entity, LifeState.LIFE_DEAD); + PacketSceneEntityDrownRsp drownRsp = new PacketSceneEntityDrownRsp(req.getEntityId()); + + + + //kill entity + broadcast it + + session.getPlayer().getScene().broadcastPacket(lifeStateChangeNotify); + session.getPlayer().getScene().broadcastPacket(drownRsp); + + //TODO: make a list somewhere of all entities to remove per tick rather than one by one + + session.getPlayer().getScene().removeEntity(entity, VisionTypeOuterClass.VisionType.VisionDie); + + } + +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketSceneEntityDrownRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketSceneEntityDrownRsp.java new file mode 100644 index 000000000..2918c2f66 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketSceneEntityDrownRsp.java @@ -0,0 +1,4 @@ +package emu.grasscutter.server.packet.send; + +public class PacketSceneEntityDrownReq { +} From 3d9b71dc4d4f376a175b36d53b5734b900fc5a2e Mon Sep 17 00:00:00 2001 From: TheLostTree <65834918+TheLostTree@users.noreply.github.com> Date: Sat, 23 Apr 2022 03:32:08 -0700 Subject: [PATCH 21/78] git doesnt like me --- .../recv/HandlerSceneEntityDrownReq.java | 2 -- .../packet/send/PacketSceneEntityDrownRsp.java | 18 +++++++++++++++++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSceneEntityDrownReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSceneEntityDrownReq.java index 91f27c65b..005141958 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSceneEntityDrownReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSceneEntityDrownReq.java @@ -8,9 +8,7 @@ import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.proto.SceneEntityDrownReqOuterClass.SceneEntityDrownReq; import emu.grasscutter.net.proto.VisionTypeOuterClass; import emu.grasscutter.server.game.GameSession; -import emu.grasscutter.server.packet.send.PacketAvatarChangeCostumeRsp; import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify; -import emu.grasscutter.server.packet.send.PacketSceneEntityDisappearNotify; import emu.grasscutter.server.packet.send.PacketSceneEntityDrownRsp; @Opcodes(PacketOpcodes.SceneEntityDrownReq) diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketSceneEntityDrownRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketSceneEntityDrownRsp.java index 2918c2f66..4d3a2b232 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketSceneEntityDrownRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketSceneEntityDrownRsp.java @@ -1,4 +1,20 @@ package emu.grasscutter.server.packet.send; -public class PacketSceneEntityDrownReq { +import emu.grasscutter.game.entity.GenshinEntity; +import emu.grasscutter.net.packet.GenshinPacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.SceneEntityDrownRspOuterClass.SceneEntityDrownRsp; +import emu.grasscutter.net.proto.VisionTypeOuterClass.VisionType; + +public class PacketSceneEntityDrownRsp extends GenshinPacket { + + public PacketSceneEntityDrownRsp(int entityId) { + super(PacketOpcodes.SceneEntityDrownRsp); + + SceneEntityDrownRsp proto = new SceneEntityDrownRsp().toBuilder().setEntityId(entityId).build(); + + this.setData(proto); + } } + + From 31f617e7f0a0c034c6b64f53318257fbba1477f0 Mon Sep 17 00:00:00 2001 From: TheLostTree <65834918+TheLostTree@users.noreply.github.com> Date: Sat, 23 Apr 2022 03:32:18 -0700 Subject: [PATCH 22/78] Update Grasscutter-Protos --- Grasscutter-Protos | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Grasscutter-Protos b/Grasscutter-Protos index 0537e9cc4..dd17415b7 160000 --- a/Grasscutter-Protos +++ b/Grasscutter-Protos @@ -1 +1 @@ -Subproject commit 0537e9cc4c7856a7c6f88bbbaa908a80c4ee677e +Subproject commit dd17415b71dfcff049e72dbe8a76173611f4b0ae From 16a7c9dd67078a80f90206c37b1dbf27b73e94aa Mon Sep 17 00:00:00 2001 From: Melledy <52122272+Melledy@users.noreply.github.com> Date: Sat, 23 Apr 2022 04:36:34 -0700 Subject: [PATCH 23/78] Revert "Drowning packets handled" --- Grasscutter-Protos | 2 +- .../recv/HandlerSceneEntityDrownReq.java | 41 ------------------- .../send/PacketSceneEntityDrownRsp.java | 20 --------- 3 files changed, 1 insertion(+), 62 deletions(-) delete mode 100644 src/main/java/emu/grasscutter/server/packet/recv/HandlerSceneEntityDrownReq.java delete mode 100644 src/main/java/emu/grasscutter/server/packet/send/PacketSceneEntityDrownRsp.java diff --git a/Grasscutter-Protos b/Grasscutter-Protos index dd17415b7..0537e9cc4 160000 --- a/Grasscutter-Protos +++ b/Grasscutter-Protos @@ -1 +1 @@ -Subproject commit dd17415b71dfcff049e72dbe8a76173611f4b0ae +Subproject commit 0537e9cc4c7856a7c6f88bbbaa908a80c4ee677e diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSceneEntityDrownReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSceneEntityDrownReq.java deleted file mode 100644 index 005141958..000000000 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSceneEntityDrownReq.java +++ /dev/null @@ -1,41 +0,0 @@ -package emu.grasscutter.server.packet.recv; - -import emu.grasscutter.game.entity.GenshinEntity; -import emu.grasscutter.game.props.LifeState; -import emu.grasscutter.net.packet.Opcodes; -import emu.grasscutter.net.packet.PacketHandler; -import emu.grasscutter.net.packet.PacketOpcodes; -import emu.grasscutter.net.proto.SceneEntityDrownReqOuterClass.SceneEntityDrownReq; -import emu.grasscutter.net.proto.VisionTypeOuterClass; -import emu.grasscutter.server.game.GameSession; -import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify; -import emu.grasscutter.server.packet.send.PacketSceneEntityDrownRsp; - -@Opcodes(PacketOpcodes.SceneEntityDrownReq) -public class HandlerSceneEntityDrownReq extends PacketHandler { - - @Override - public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { - SceneEntityDrownReq req = SceneEntityDrownReq.parseFrom(payload); - - - GenshinEntity entity = session.getPlayer().getScene().getEntityById(req.getEntityId()); - - - PacketLifeStateChangeNotify lifeStateChangeNotify = new PacketLifeStateChangeNotify(entity, entity, LifeState.LIFE_DEAD); - PacketSceneEntityDrownRsp drownRsp = new PacketSceneEntityDrownRsp(req.getEntityId()); - - - - //kill entity + broadcast it - - session.getPlayer().getScene().broadcastPacket(lifeStateChangeNotify); - session.getPlayer().getScene().broadcastPacket(drownRsp); - - //TODO: make a list somewhere of all entities to remove per tick rather than one by one - - session.getPlayer().getScene().removeEntity(entity, VisionTypeOuterClass.VisionType.VisionDie); - - } - -} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketSceneEntityDrownRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketSceneEntityDrownRsp.java deleted file mode 100644 index 4d3a2b232..000000000 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketSceneEntityDrownRsp.java +++ /dev/null @@ -1,20 +0,0 @@ -package emu.grasscutter.server.packet.send; - -import emu.grasscutter.game.entity.GenshinEntity; -import emu.grasscutter.net.packet.GenshinPacket; -import emu.grasscutter.net.packet.PacketOpcodes; -import emu.grasscutter.net.proto.SceneEntityDrownRspOuterClass.SceneEntityDrownRsp; -import emu.grasscutter.net.proto.VisionTypeOuterClass.VisionType; - -public class PacketSceneEntityDrownRsp extends GenshinPacket { - - public PacketSceneEntityDrownRsp(int entityId) { - super(PacketOpcodes.SceneEntityDrownRsp); - - SceneEntityDrownRsp proto = new SceneEntityDrownRsp().toBuilder().setEntityId(entityId).build(); - - this.setData(proto); - } -} - - From 6a4534d33d9a5d73845c9d585e0b031fc7975e24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=9C=9F=E5=BF=83?= <1307993674@qq.com> Date: Sat, 23 Apr 2022 20:51:36 +0800 Subject: [PATCH 24/78] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e6843b116..311e764cd 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ A WIP server reimplementation for *some anime game* 2.3-2.6 2. Set network proxy to `127.0.0.1:8080` or the proxy port you specified. 4. *yoink* -* or you can use `run.cmd` to start Server & Proxy daemon with one click +* or you can use `start.cmd` to start Server & Proxy daemon with one click # Grasscutter commands There is a dummy user named "Server" in every player's friends list that you can message to use commands. Commands also work in other chat rooms, such as private/team chats. From 3bc249f0426fda483a32f634c001f7d686ec3a8d Mon Sep 17 00:00:00 2001 From: Yazawazi <47273265+Yazawazi@users.noreply.github.com> Date: Sat, 23 Apr 2022 21:03:29 +0800 Subject: [PATCH 25/78] Try to give name card --- .../data/common/RewardItemData.java | 22 +++++++++++++++ .../emu/grasscutter/data/def/RewardData.java | 27 +++++++++++++++++++ .../game/avatar/GenshinAvatar.java | 1 + .../HandlerAvatarFetterLevelRewardReq.java | 21 +++++++++++++-- 4 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 src/main/java/emu/grasscutter/data/common/RewardItemData.java create mode 100644 src/main/java/emu/grasscutter/data/def/RewardData.java diff --git a/src/main/java/emu/grasscutter/data/common/RewardItemData.java b/src/main/java/emu/grasscutter/data/common/RewardItemData.java new file mode 100644 index 000000000..024b89f7f --- /dev/null +++ b/src/main/java/emu/grasscutter/data/common/RewardItemData.java @@ -0,0 +1,22 @@ +package emu.grasscutter.data.common; + +public class RewardItemData { + private int ItemId; + private int ItemCount; + + public int getItemId() { + return ItemId; + } + + public void setItemId(int itemId) { + ItemId = itemId; + } + + public int getItemCount() { + return ItemCount; + } + + public void setItemCount(int itemCount) { + ItemCount = itemCount; + } +} diff --git a/src/main/java/emu/grasscutter/data/def/RewardData.java b/src/main/java/emu/grasscutter/data/def/RewardData.java new file mode 100644 index 000000000..46c39ac10 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/def/RewardData.java @@ -0,0 +1,27 @@ +package emu.grasscutter.data.def; + +import java.util.List; + +import emu.grasscutter.data.GenshinResource; +import emu.grasscutter.data.ResourceType; +import emu.grasscutter.data.common.RewardItemData; + +@ResourceType(name = "RewardExcelConfigData.json") +public class RewardData extends GenshinResource { + public int RewardId; + public List RewardItemList; + + @Override + public int getId() { + return RewardId; + } + + public List getRewardItemList() { + return RewardItemList; + } + + @Override + public void onLoad() { + + } +} diff --git a/src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java b/src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java index 254258300..45b5c09ec 100644 --- a/src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java +++ b/src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java @@ -433,6 +433,7 @@ public class GenshinAvatar { // Fetters this.setFetterList(data.getFetters()); + this.setNameCardRewardId(data.getNameCardRewardId()); // Get hp percent, set to 100% if none float hpPercent = this.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) <= 0 ? 1f : this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) / this.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP); diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarFetterLevelRewardReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarFetterLevelRewardReq.java index d3431d535..2269f8606 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarFetterLevelRewardReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarFetterLevelRewardReq.java @@ -1,10 +1,17 @@ package emu.grasscutter.server.packet.recv; +import emu.grasscutter.Grasscutter; +import emu.grasscutter.data.GenshinData; +import emu.grasscutter.data.def.RewardData; +import emu.grasscutter.game.inventory.GenshinItem; +import emu.grasscutter.game.props.ActionReason; import emu.grasscutter.net.packet.Opcodes; import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.proto.AvatarFetterLevelRewardReqOuterClass.AvatarFetterLevelRewardReq; import emu.grasscutter.server.game.GameSession; import emu.grasscutter.server.packet.send.PacketAvatarFetterLevelRewardRsp; +import emu.grasscutter.server.packet.send.PacketItemAddHintNotify; +import emu.grasscutter.server.packet.send.PacketUnlockNameCardNotify; import emu.grasscutter.net.packet.PacketHandler; @Opcodes(PacketOpcodes.AvatarFetterLevelRewardReq) @@ -23,9 +30,19 @@ public class HandlerAvatarFetterLevelRewardReq extends PacketHandler { .getAvatars() .getAvatarByGuid(avatarGuid) .getNameCardRewardId(); - - // Here need to send the packets, I am not at all clear ah! + RewardData card = GenshinData.getRewardDataMap().get(rewardId); + int cardId = card.getRewardItemList().get(0).getItemId(); + + if (session.getPlayer().getNameCardList().contains(cardId)) { + // Already got divorce certificate. + session.send(new PacketAvatarFetterLevelRewardRsp(req.getAvatarGuid(), req.getFetterLevel())); + return; + } + + GenshinItem item = new GenshinItem(cardId); + session.getPlayer().sendPacket(new PacketItemAddHintNotify(item, ActionReason.FetterLevelReward)); + session.send(new PacketUnlockNameCardNotify(cardId)); session.send(new PacketAvatarFetterLevelRewardRsp(avatarGuid, req.getFetterLevel(), rewardId)); } } From 301d0a174c29d543df65aa0437eb61b90ce52a9f Mon Sep 17 00:00:00 2001 From: xtaodada Date: Sat, 23 Apr 2022 21:13:45 +0800 Subject: [PATCH 26/78] Add another way to set talent level --- .../command/commands/TalentCommand.java | 44 ++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/src/main/java/emu/grasscutter/command/commands/TalentCommand.java b/src/main/java/emu/grasscutter/command/commands/TalentCommand.java index 2ced6f1c3..21cf66249 100644 --- a/src/main/java/emu/grasscutter/command/commands/TalentCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/TalentCommand.java @@ -2,6 +2,7 @@ package emu.grasscutter.command.commands; import emu.grasscutter.command.Command; import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.data.def.AvatarSkillDepotData; import emu.grasscutter.game.GenshinPlayer; import emu.grasscutter.game.avatar.GenshinAvatar; import emu.grasscutter.game.entity.EntityAvatar; @@ -21,8 +22,9 @@ public class TalentCommand implements CommandHandler { return; } - if (args.size() < 0 || args.size() < 1){ + if (args.size() < 1){ CommandHandler.sendMessage(sender, "To set talent level: /talent set "); + CommandHandler.sendMessage(sender, "Another way to set talent level: /talent "); CommandHandler.sendMessage(sender, "To get talent ID: /talent getid"); return; } @@ -31,6 +33,7 @@ public class TalentCommand implements CommandHandler { switch (cmdSwitch) { default: CommandHandler.sendMessage(sender, "To set talent level: /talent set "); + CommandHandler.sendMessage(sender, "Another way to set talent level: /talent "); CommandHandler.sendMessage(sender, "To get talent ID: /talent getid"); return; case "set": @@ -90,6 +93,45 @@ public class TalentCommand implements CommandHandler { return; } + break; + case "n": case "e": case "q": + try { + EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity(); + GenshinAvatar avatar = entity.getAvatar(); + AvatarSkillDepotData SkillDepot = avatar.getData().getSkillDepot(); + int skillId; + switch (cmdSwitch) { + default: + skillId = SkillDepot.getSkills().get(0); + break; + case "e": + skillId = SkillDepot.getSkills().get(1); + break; + case "q": + skillId = SkillDepot.getEnergySkill(); + break; + } + int nextLevel = Integer.parseInt(args.get(1)); + int currentLevel = avatar.getSkillLevelMap().get(skillId); + if (args.size() < 1){ + CommandHandler.sendMessage(sender, "To set talent level: /talent "); + return; + } + if (nextLevel > 16){ + CommandHandler.sendMessage(sender, "Invalid talent level. Level should be lower than 16"); + return; + } + // Upgrade skill + avatar.getSkillLevelMap().put(skillId, nextLevel); + avatar.save(); + // Packet + sender.sendPacket(new PacketAvatarSkillChangeNotify(avatar, skillId, currentLevel, nextLevel)); + sender.sendPacket(new PacketAvatarSkillUpgradeRsp(avatar, skillId, currentLevel, nextLevel)); + CommandHandler.sendMessage(sender, "Set this talent to " + nextLevel + "."); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(sender, "Invalid talent level."); + return; + } break; case "getid": EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity(); From 57e363725134ca02df03db40e24f5c6185e48e96 Mon Sep 17 00:00:00 2001 From: memetrollsXD Date: Sat, 23 Apr 2022 16:11:45 +0200 Subject: [PATCH 27/78] Add promoteLevel and count --- .../command/commands/GiveCommand.java | 80 ++++++++++++++----- 1 file changed, 62 insertions(+), 18 deletions(-) diff --git a/src/main/java/emu/grasscutter/command/commands/GiveCommand.java b/src/main/java/emu/grasscutter/command/commands/GiveCommand.java index c427d9d89..2ecdac1f5 100644 --- a/src/main/java/emu/grasscutter/command/commands/GiveCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/GiveCommand.java @@ -13,16 +13,15 @@ import emu.grasscutter.server.packet.send.PacketItemAddHintNotify; import java.util.LinkedList; import java.util.List; -@Command(label = "give", usage = "give [player] [amount]", - description = "Gives an item to you or the specified player", aliases = {"g", "item", "giveitem"}, permission = "player.give") +@Command(label = "give", usage = "give [player] [amount] [level]", description = "Gives an item to you or the specified player", aliases = { + "g", "item", "giveitem" }, permission = "player.give") public final class GiveCommand implements CommandHandler { @Override public void execute(GenshinPlayer sender, List args) { - int target, item, amount = 1; - + int target, item, lvl, amount = 1; if (sender == null && args.size() < 2) { - CommandHandler.sendMessage(null, "Usage: give [amount]"); + CommandHandler.sendMessage(null, "Usage: give [amount] [level]"); return; } @@ -34,6 +33,7 @@ public final class GiveCommand implements CommandHandler { try { item = Integer.parseInt(args.get(0)); target = sender.getUid(); + lvl = 1; } catch (NumberFormatException ignored) { // TODO: Parse from item name using GM Handbook. CommandHandler.sendMessage(sender, "Invalid item id."); @@ -43,6 +43,7 @@ public final class GiveCommand implements CommandHandler { case 2: // [amount] | [player] try { target = Integer.parseInt(args.get(0)); + lvl = 1; if (Grasscutter.getGameServer().getPlayerByUid(target) == null && sender != null) { target = sender.getUid(); @@ -57,17 +58,39 @@ public final class GiveCommand implements CommandHandler { return; } break; - case 3: // [player] [amount] + case 3: // [player] [amount] | [amount] [level] + try { + target = Integer.parseInt(args.get(0)); + + if (Grasscutter.getGameServer().getPlayerByUid(target) == null && sender != null) { + target = sender.getUid(); + item = Integer.parseInt(args.get(0)); + amount = Integer.parseInt(args.get(1)); + lvl = Integer.parseInt(args.get(2)); + } else { + item = Integer.parseInt(args.get(1)); + amount = Integer.parseInt(args.get(2)); + lvl = 1; + } + + } catch (NumberFormatException ignored) { + // TODO: Parse from item name using GM Handbook. + CommandHandler.sendMessage(sender, "Invalid item or player ID."); + return; + } + break; + case 4: // [player] [amount] [level] try { target = Integer.parseInt(args.get(0)); if (Grasscutter.getGameServer().getPlayerByUid(target) == null) { CommandHandler.sendMessage(sender, "Invalid player ID."); return; + } else { + item = Integer.parseInt(args.get(1)); + amount = Integer.parseInt(args.get(2)); + lvl = Integer.parseInt(args.get(3)); } - - item = Integer.parseInt(args.get(1)); - amount = Integer.parseInt(args.get(2)); } catch (NumberFormatException ignored) { // TODO: Parse from item name using GM Handbook. CommandHandler.sendMessage(sender, "Invalid item or player ID."); @@ -89,18 +112,40 @@ public final class GiveCommand implements CommandHandler { return; } - this.item(targetPlayer, itemData, amount); + this.item(targetPlayer, itemData, amount, lvl); - if (!itemData.isEquip()) CommandHandler.sendMessage(sender, String.format("Given %s of %s to %s.", amount, item, target)); - else CommandHandler.sendMessage(sender, String.format("Given %s with level %s to %s", item, amount, target)); + if (!itemData.isEquip()) + CommandHandler.sendMessage(sender, String.format("Given %s of %s to %s.", amount, item, target)); + else + CommandHandler.sendMessage(sender, + String.format("Given %s with level %s %s times to %s", item, lvl, amount, target)); } - private void item(GenshinPlayer player, ItemData itemData, int amount) { + private void item(GenshinPlayer player, ItemData itemData, int amount, int lvl) { if (itemData.isEquip()) { - GenshinItem item = new GenshinItem(itemData); - item.setLevel(amount); - player.getInventory().addItem(item); - player.sendPacket(new PacketItemAddHintNotify(item, ActionReason.SubfieldDrop)); + List items = new LinkedList<>(); + for (int i = 0; i < amount; i++) { + GenshinItem item = new GenshinItem(itemData); + item.setCount(amount); + item.setLevel(lvl); + item.setPromoteLevel(0); + if (lvl > 20) { // 20/40 + item.setPromoteLevel(1); + } else if (lvl > 40) { // 40/50 + item.setPromoteLevel(2); + } else if (lvl > 50) { // 50/60 + item.setPromoteLevel(3); + } else if (lvl > 60) { // 60/70 + item.setPromoteLevel(4); + } else if (lvl > 70) { // 70/80 + item.setPromoteLevel(5); + } else if (lvl > 80) { // 80/90 + item.setPromoteLevel(6); + } + items.add(item); + } + player.getInventory().addItems(items); + player.sendPacket(new PacketItemAddHintNotify(items, ActionReason.SubfieldDrop)); } else { GenshinItem genshinItem = new GenshinItem(itemData); genshinItem.setCount(amount); @@ -109,4 +154,3 @@ public final class GiveCommand implements CommandHandler { } } } - From ad1c3f9a3b4de30a3978e67e684b2919a5615cf6 Mon Sep 17 00:00:00 2001 From: Yazawazi <47273265+Yazawazi@users.noreply.github.com> Date: Sat, 23 Apr 2022 22:16:59 +0800 Subject: [PATCH 28/78] Will --- .../packet/recv/HandlerAvatarFetterLevelRewardReq.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarFetterLevelRewardReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarFetterLevelRewardReq.java index 2269f8606..d93b157df 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarFetterLevelRewardReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarFetterLevelRewardReq.java @@ -36,13 +36,14 @@ public class HandlerAvatarFetterLevelRewardReq extends PacketHandler { if (session.getPlayer().getNameCardList().contains(cardId)) { // Already got divorce certificate. - session.send(new PacketAvatarFetterLevelRewardRsp(req.getAvatarGuid(), req.getFetterLevel())); + session.getPlayer().sendPacket(new PacketAvatarFetterLevelRewardRsp(req.getAvatarGuid(), req.getFetterLevel())); return; } GenshinItem item = new GenshinItem(cardId); + session.getPlayer().getInventory().addItem(item); session.getPlayer().sendPacket(new PacketItemAddHintNotify(item, ActionReason.FetterLevelReward)); - session.send(new PacketUnlockNameCardNotify(cardId)); + session.getPlayer().sendPacket(new PacketUnlockNameCardNotify(cardId)); session.send(new PacketAvatarFetterLevelRewardRsp(avatarGuid, req.getFetterLevel(), rewardId)); } } From 3baef42aac418ec5d79891836ece4e893389876c Mon Sep 17 00:00:00 2001 From: Yazawazi <47273265+Yazawazi@users.noreply.github.com> Date: Sat, 23 Apr 2022 23:48:06 +0800 Subject: [PATCH 29/78] Name Card Done!!!!!!!!!!! --- .../java/emu/grasscutter/data/GenshinData.java | 5 +++++ .../emu/grasscutter/data/def/AvatarData.java | 9 +++++++++ .../grasscutter/game/avatar/GenshinAvatar.java | 18 ++++++++++++++++++ .../HandlerAvatarFetterLevelRewardReq.java | 13 ++++++++----- .../send/PacketAvatarFetterDataNotify.java | 7 +++++++ 5 files changed, 47 insertions(+), 5 deletions(-) diff --git a/src/main/java/emu/grasscutter/data/GenshinData.java b/src/main/java/emu/grasscutter/data/GenshinData.java index 98f235c1e..3b3d351da 100644 --- a/src/main/java/emu/grasscutter/data/GenshinData.java +++ b/src/main/java/emu/grasscutter/data/GenshinData.java @@ -59,6 +59,7 @@ public class GenshinData { private static final Int2ObjectMap sceneDataMap = new Int2ObjectLinkedOpenHashMap<>(); private static final Int2ObjectMap fetterDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap fetterCharacterCardDataMap = new Int2ObjectOpenHashMap<>(); + private static final Int2ObjectMap rewardDataMap = new Int2ObjectOpenHashMap<>(); // Cache private static Map> fetters = new HashMap<>(); @@ -243,6 +244,10 @@ public class GenshinData { return sceneDataMap; } + public static Int2ObjectMap getRewardDataMap() { + return rewardDataMap; + } + public static Map> getFetterDataEntries() { if (fetters.isEmpty()) { fetterDataMap.forEach((k, v) -> { diff --git a/src/main/java/emu/grasscutter/data/def/AvatarData.java b/src/main/java/emu/grasscutter/data/def/AvatarData.java index a714344ae..3beb21eec 100644 --- a/src/main/java/emu/grasscutter/data/def/AvatarData.java +++ b/src/main/java/emu/grasscutter/data/def/AvatarData.java @@ -58,6 +58,7 @@ public class AvatarData extends GenshinResource { private List fetters; private int nameCardRewardId; + private int nameCardId; @Override public int getId(){ @@ -204,6 +205,10 @@ public class AvatarData extends GenshinResource { return nameCardRewardId; } + public int getNameCardId() { + return nameCardId; + } + @Override public void onLoad() { this.skillDepot = GenshinData.getAvatarSkillDepotDataMap().get(this.SkillDepotId); @@ -214,6 +219,10 @@ public class AvatarData extends GenshinResource { if (GenshinData.getFetterCharacterCardDataMap().get(this.Id) != null) { this.nameCardRewardId = GenshinData.getFetterCharacterCardDataMap().get(this.Id).getRewardId(); } + + if (GenshinData.getRewardDataMap().get(this.nameCardRewardId) != null) { + this.nameCardId = GenshinData.getRewardDataMap().get(this.nameCardRewardId).getRewardItemList().get(0).getItemId(); + } int size = GenshinData.getAvatarCurveDataMap().size(); this.hpGrowthCurve = new float[size]; diff --git a/src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java b/src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java index 45b5c09ec..ab2f67a43 100644 --- a/src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java +++ b/src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java @@ -94,6 +94,7 @@ public class GenshinAvatar { private int fetterExp; private int nameCardRewardId; + private int nameCardId; public GenshinAvatar() { // Morhpia only! @@ -113,6 +114,7 @@ public class GenshinAvatar { this(); this.avatarId = data.getId(); this.nameCardRewardId = data.getNameCardRewardId(); + this.nameCardId = data.getNameCardId(); this.data = data; this.bornTime = (int) (System.currentTimeMillis() / 1000); this.flyCloak = 140001; @@ -311,6 +313,14 @@ public class GenshinAvatar { this.fetterExp = fetterExp; } + public int getNameCardId() { + return nameCardId; + } + + public void setNameCardId(int nameCardId) { + this.nameCardId = nameCardId; + } + public float getCurrentHp() { return currentHp; } @@ -434,6 +444,7 @@ public class GenshinAvatar { // Fetters this.setFetterList(data.getFetters()); this.setNameCardRewardId(data.getNameCardRewardId()); + this.setNameCardId(data.getNameCardId()); // Get hp percent, set to 100% if none float hpPercent = this.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) <= 0 ? 1f : this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) / this.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP); @@ -746,6 +757,13 @@ public class GenshinAvatar { } } + int rewardId = this.getNameCardRewardId(); + int cardId = this.getNameCardId(); + + if (this.getPlayer().getNameCardList().contains(cardId)) { + avatarFetter.addRewardedFetterLevelList(rewardId); + } + AvatarInfo.Builder avatarInfo = AvatarInfo.newBuilder() .setAvatarId(this.getAvatarId()) .setGuid(this.getGuid()) diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarFetterLevelRewardReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarFetterLevelRewardReq.java index d93b157df..51192f620 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarFetterLevelRewardReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarFetterLevelRewardReq.java @@ -1,14 +1,15 @@ package emu.grasscutter.server.packet.recv; -import emu.grasscutter.Grasscutter; import emu.grasscutter.data.GenshinData; import emu.grasscutter.data.def.RewardData; +import emu.grasscutter.game.avatar.GenshinAvatar; import emu.grasscutter.game.inventory.GenshinItem; import emu.grasscutter.game.props.ActionReason; import emu.grasscutter.net.packet.Opcodes; import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.proto.AvatarFetterLevelRewardReqOuterClass.AvatarFetterLevelRewardReq; import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.server.packet.send.PacketAvatarFetterDataNotify; import emu.grasscutter.server.packet.send.PacketAvatarFetterLevelRewardRsp; import emu.grasscutter.server.packet.send.PacketItemAddHintNotify; import emu.grasscutter.server.packet.send.PacketUnlockNameCardNotify; @@ -25,18 +26,19 @@ public class HandlerAvatarFetterLevelRewardReq extends PacketHandler { } else { long avatarGuid = req.getAvatarGuid(); - int rewardId = session + GenshinAvatar avatar = session .getPlayer() .getAvatars() - .getAvatarByGuid(avatarGuid) - .getNameCardRewardId(); + .getAvatarByGuid(avatarGuid); + + int rewardId = avatar.getNameCardRewardId(); RewardData card = GenshinData.getRewardDataMap().get(rewardId); int cardId = card.getRewardItemList().get(0).getItemId(); if (session.getPlayer().getNameCardList().contains(cardId)) { // Already got divorce certificate. - session.getPlayer().sendPacket(new PacketAvatarFetterLevelRewardRsp(req.getAvatarGuid(), req.getFetterLevel())); + session.getPlayer().sendPacket(new PacketAvatarFetterLevelRewardRsp(req.getAvatarGuid(), req.getFetterLevel(), rewardId)); return; } @@ -45,6 +47,7 @@ public class HandlerAvatarFetterLevelRewardReq extends PacketHandler { session.getPlayer().sendPacket(new PacketItemAddHintNotify(item, ActionReason.FetterLevelReward)); session.getPlayer().sendPacket(new PacketUnlockNameCardNotify(cardId)); session.send(new PacketAvatarFetterLevelRewardRsp(avatarGuid, req.getFetterLevel(), rewardId)); + session.send(new PacketAvatarFetterDataNotify(avatar)); } } } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketAvatarFetterDataNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketAvatarFetterDataNotify.java index 86cea0e02..6be12e603 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketAvatarFetterDataNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketAvatarFetterDataNotify.java @@ -26,6 +26,13 @@ public class PacketAvatarFetterDataNotify extends GenshinPacket { ); } } + + int rewardId = avatar.getNameCardRewardId(); + int cardId = avatar.getNameCardId(); + + if (avatar.getPlayer().getNameCardList().contains(cardId)) { + avatarFetter.addRewardedFetterLevelList(rewardId); + } AvatarFetterInfo avatarFetterInfo = avatarFetter.build(); From 2dd8932144c8808af5c6aa288ed0a7e8d0e6e30b Mon Sep 17 00:00:00 2001 From: xtaodada Date: Sun, 24 Apr 2022 03:10:07 +0800 Subject: [PATCH 30/78] Add Teleport command --- .../command/commands/PositionCommand.java | 3 +- .../command/commands/TelePortCommand.java | 43 +++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 src/main/java/emu/grasscutter/command/commands/TelePortCommand.java diff --git a/src/main/java/emu/grasscutter/command/commands/PositionCommand.java b/src/main/java/emu/grasscutter/command/commands/PositionCommand.java index 639301c7f..8bdf3c754 100644 --- a/src/main/java/emu/grasscutter/command/commands/PositionCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/PositionCommand.java @@ -17,6 +17,7 @@ public final class PositionCommand implements CommandHandler { return; } - sender.dropMessage(String.format("Coord: %.3f, %.3f, %.3f", sender.getPos().getX(), sender.getPos().getY(), sender.getPos().getZ())); + sender.dropMessage(String.format("Coord: %.3f, %.3f, %.3f\nScene id: %d", + sender.getPos().getX(), sender.getPos().getY(), sender.getPos().getZ(), sender.getSceneId())); } } diff --git a/src/main/java/emu/grasscutter/command/commands/TelePortCommand.java b/src/main/java/emu/grasscutter/command/commands/TelePortCommand.java new file mode 100644 index 000000000..1a9e4f87a --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/TelePortCommand.java @@ -0,0 +1,43 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.GenshinPlayer; +import emu.grasscutter.utils.Position; + +import java.util.List; + +@Command(label = "teleport", usage = "teleport ", aliases = {"tp"}, + description = "Change the player's position.", permission = "player.teleport") +public class TelePortCommand implements CommandHandler { + + @Override + public void execute(GenshinPlayer sender, List args) { + if (sender == null) { + CommandHandler.sendMessage(null, "Run this command in-game."); + return; + } + + if (args.size() < 3){ + CommandHandler.sendMessage(sender, "Usage: /tp [scene id]"); + return; + } + + try { + float x = Float.parseFloat(args.get(0)); + float y = Float.parseFloat(args.get(1)); + float z = Float.parseFloat(args.get(2)); + int sceneId = sender.getSceneId(); + if (args.size() == 4){ + sceneId = Integer.parseInt(args.get(3)); + } + Position target = new Position(x, y, z); + boolean result = sender.getWorld().transferPlayerToScene(sender, sceneId, target); + if (!result) { + CommandHandler.sendMessage(sender, "Invalid position."); + } + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(sender, "Invalid position."); + } + } +} From 6dae4d43fe4eb207ffefdcd7fc74152096e25688 Mon Sep 17 00:00:00 2001 From: memetrollsXD Date: Sat, 23 Apr 2022 22:33:52 +0200 Subject: [PATCH 31/78] Add level parameter to README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 853c37ae3..e6049370b 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ There is a dummy user named "Server" in every player's friends list that you can `spawn [monster id] [level] [amount]` -`give [item id] [amount]` +`give [item id] [amount] [level]` `givechar [avatar id] [level]` From ebecbe87330b2c0c8c4df78ae720eebe2cb856c9 Mon Sep 17 00:00:00 2001 From: memetrollsXD Date: Sat, 23 Apr 2022 22:44:52 +0200 Subject: [PATCH 32/78] Propose a Contributing COC --- CONTRIBUTING.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..64083e778 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,16 @@ +# Contributing + +Please note we have a code of conduct, please follow it in all your interactions with the project. If you have any further questions please create an issue or ask in the Discord server. + +- Only fix/add the functionality in question OR address wide-spread whitespace/style issues, not both. +- Address a single concern in the least number of changed lines as possible. + +**Do not make a pull request to merge into stable unless it is a hotfix. Use the development branch instead.** + +## Pull Request Process + +1. Ensure any install or build dependencies are removed before the end of the layer when doing a + build. +2. Update the README.md and wiki with details of changes to the interface, this includes new environment + variables, exposed ports, useful file locations and container parameters. +3. Write with detail on your pull request description what you have committed, to make it easier for the collaborators to make a changelog. From 9e523da18eebf9eedddbbbf6ad8f9ec67c3e2bd8 Mon Sep 17 00:00:00 2001 From: Yazawazi <47273265+Yazawazi@users.noreply.github.com> Date: Sun, 24 Apr 2022 05:20:59 +0800 Subject: [PATCH 33/78] tp with relative coordinates --- .../command/commands/TelePortCommand.java | 33 +++++++++++++++++-- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/src/main/java/emu/grasscutter/command/commands/TelePortCommand.java b/src/main/java/emu/grasscutter/command/commands/TelePortCommand.java index 1a9e4f87a..84848afa5 100644 --- a/src/main/java/emu/grasscutter/command/commands/TelePortCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/TelePortCommand.java @@ -24,9 +24,36 @@ public class TelePortCommand implements CommandHandler { } try { - float x = Float.parseFloat(args.get(0)); - float y = Float.parseFloat(args.get(1)); - float z = Float.parseFloat(args.get(2)); + float x = 0f; + float y = 0f; + float z = 0f; + if (args.get(0).contains("~")) { + if (args.get(0).equals("~")) { + x = sender.getPos().getX(); + } else { + x = Float.parseFloat(args.get(0).replace("~", "")) + sender.getPos().getX(); + } + } else { + x = Float.parseFloat(args.get(0)); + } + if (args.get(1).contains("~")) { + if (args.get(1).equals("~")) { + y = sender.getPos().getY(); + } else { + y = Float.parseFloat(args.get(1).replace("~", "")) + sender.getPos().getY(); + } + } else { + y = Float.parseFloat(args.get(1)); + } + if (args.get(2).contains("~")) { + if (args.get(2).equals("~")) { + z = sender.getPos().getZ(); + } else { + z = Float.parseFloat(args.get(2).replace("~", "")) + sender.getPos().getZ(); + } + } else { + z = Float.parseFloat(args.get(2)); + } int sceneId = sender.getSceneId(); if (args.size() == 4){ sceneId = Integer.parseInt(args.get(3)); From 1ebba3af387c3366b6741fa7c5e442d524300b9a Mon Sep 17 00:00:00 2001 From: memetrollsXD Date: Sun, 24 Apr 2022 00:05:40 +0200 Subject: [PATCH 34/78] Fix "weird spacing" --- CONTRIBUTING.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 64083e778..19a564382 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,8 +9,7 @@ Please note we have a code of conduct, please follow it in all your interactions ## Pull Request Process -1. Ensure any install or build dependencies are removed before the end of the layer when doing a - build. +1. Ensure any install or build dependencies are removed before the end of the layer when doing a build. 2. Update the README.md and wiki with details of changes to the interface, this includes new environment variables, exposed ports, useful file locations and container parameters. 3. Write with detail on your pull request description what you have committed, to make it easier for the collaborators to make a changelog. From 11df62969cb547fa480edffde5f2f5a7e57772fd Mon Sep 17 00:00:00 2001 From: memetrollsXD Date: Sun, 24 Apr 2022 00:06:56 +0200 Subject: [PATCH 35/78] Fix another weird line break --- CONTRIBUTING.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 19a564382..55b94eb7d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,6 +10,5 @@ Please note we have a code of conduct, please follow it in all your interactions ## Pull Request Process 1. Ensure any install or build dependencies are removed before the end of the layer when doing a build. -2. Update the README.md and wiki with details of changes to the interface, this includes new environment - variables, exposed ports, useful file locations and container parameters. +2. Update the README.md and wiki with details of changes to the interface, this includes new environment variables, exposed ports, useful file locations and container parameters. 3. Write with detail on your pull request description what you have committed, to make it easier for the collaborators to make a changelog. From 7792c5c73fe9c163ae2dfcb265648d302319d22b Mon Sep 17 00:00:00 2001 From: Miyucchi Date: Sun, 24 Apr 2022 00:10:26 +0200 Subject: [PATCH 36/78] Fix counter --- src/main/java/emu/grasscutter/database/DatabaseManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/emu/grasscutter/database/DatabaseManager.java b/src/main/java/emu/grasscutter/database/DatabaseManager.java index 9d64f9258..6df2ca9f0 100644 --- a/src/main/java/emu/grasscutter/database/DatabaseManager.java +++ b/src/main/java/emu/grasscutter/database/DatabaseManager.java @@ -101,7 +101,7 @@ public final class DatabaseManager { } public static synchronized int getNextId(Class c) { - DatabaseCounter counter = getDatastore().find(DatabaseCounter.class).filter(Filters.eq("_id", c.getName())).first(); + DatabaseCounter counter = getDatastore().find(DatabaseCounter.class).filter(Filters.eq("_id", c.getSimpleName())).first(); if (counter == null) { counter = new DatabaseCounter(c.getSimpleName()); } From 031be2e3d13338d130dcb7118b8c391047598461 Mon Sep 17 00:00:00 2001 From: lunaticwhat Date: Sun, 24 Apr 2022 06:00:27 +0700 Subject: [PATCH 37/78] gitignore'd protos --- .gitignore | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 9df6d0071..4699d31c7 100644 --- a/.gitignore +++ b/.gitignore @@ -50,8 +50,7 @@ resources/* logs/* data/AbilityEmbryos.json data/OpenConfig.json -proto/auto/ -proto/protoc.exe +proto/* GM Handbook.txt config.json mitmdump.exe From 8f81195336ece2bcb302cd1ceb1568217183964d Mon Sep 17 00:00:00 2001 From: memetrollsXD Date: Sun, 24 Apr 2022 04:34:17 +0200 Subject: [PATCH 38/78] Try to fix "No SSL cert found bind exception" --- .../server/dispatch/DispatchServer.java | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java index ea7c1166e..a605fbb81 100644 --- a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java +++ b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java @@ -177,10 +177,20 @@ public final class DispatchServer { SSLContext sslContext = SSLContext.getInstance("TLS"); try (FileInputStream fis = new FileInputStream(Grasscutter.getConfig().getDispatchOptions().KeystorePath)) { char[] keystorePassword = Grasscutter.getConfig().getDispatchOptions().KeystorePassword.toCharArray(); - KeyStore ks = KeyStore.getInstance("PKCS12"); - ks.load(fis, keystorePassword); - KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); - kmf.init(ks, keystorePassword); + + try { + KeyStore ks = KeyStore.getInstance("PKCS12"); + ks.load(fis, keystorePassword); + KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); + kmf.init(ks, keystorePassword); + } catch (Exception e) { + KeyStore ks = KeyStore.getInstance("PKCS12"); + ks.load(fis, "123456"); + KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); + kmf.init(ks, "123456"); + } catch (Exception e) { + Grasscutter.getLogger().warn("[Dispatch] Error while loading keystore!"); + } sslContext.init(kmf.getKeyManagers(), null, null); From 2045dd2832748dc10ca29afe9c69c37d71f81d97 Mon Sep 17 00:00:00 2001 From: memetrollsXD Date: Sun, 24 Apr 2022 04:36:11 +0200 Subject: [PATCH 39/78] Notify user that new pw was used --- .../java/emu/grasscutter/server/dispatch/DispatchServer.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java index a605fbb81..c0033445b 100644 --- a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java +++ b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java @@ -184,6 +184,7 @@ public final class DispatchServer { KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); kmf.init(ks, keystorePassword); } catch (Exception e) { + Grasscutter.getLogger().warn("[Dispatch] Unable to load keystore. Using default keystore password..."); KeyStore ks = KeyStore.getInstance("PKCS12"); ks.load(fis, "123456"); KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); From bed31812522881038b115eff36ae759d41bc2a83 Mon Sep 17 00:00:00 2001 From: memetrollsXD Date: Sun, 24 Apr 2022 04:41:06 +0200 Subject: [PATCH 40/78] Forgot the toCharArray --- .../java/emu/grasscutter/server/dispatch/DispatchServer.java | 4 ++-- 1 file changed, 2 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 c0033445b..deb4faae3 100644 --- a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java +++ b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java @@ -186,9 +186,9 @@ public final class DispatchServer { } catch (Exception e) { Grasscutter.getLogger().warn("[Dispatch] Unable to load keystore. Using default keystore password..."); KeyStore ks = KeyStore.getInstance("PKCS12"); - ks.load(fis, "123456"); + ks.load(fis, "123456".toCharArray()); KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); - kmf.init(ks, "123456"); + kmf.init(ks, "123456".toCharArray()); } catch (Exception e) { Grasscutter.getLogger().warn("[Dispatch] Error while loading keystore!"); } From 79d646d35803bf510e969cb4eef08105de0d90c9 Mon Sep 17 00:00:00 2001 From: memetrollsXD Date: Sun, 24 Apr 2022 04:50:22 +0200 Subject: [PATCH 41/78] Remove last catch and init another variable as placeholder --- .../emu/grasscutter/server/dispatch/DispatchServer.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java index deb4faae3..2d36b224e 100644 --- a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java +++ b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java @@ -177,11 +177,12 @@ public final class DispatchServer { SSLContext sslContext = SSLContext.getInstance("TLS"); try (FileInputStream fis = new FileInputStream(Grasscutter.getConfig().getDispatchOptions().KeystorePath)) { char[] keystorePassword = Grasscutter.getConfig().getDispatchOptions().KeystorePassword.toCharArray(); - + KeyManagerFactory _kmf; try { KeyStore ks = KeyStore.getInstance("PKCS12"); ks.load(fis, keystorePassword); KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); + _kmf = kmf; kmf.init(ks, keystorePassword); } catch (Exception e) { Grasscutter.getLogger().warn("[Dispatch] Unable to load keystore. Using default keystore password..."); @@ -189,11 +190,10 @@ public final class DispatchServer { ks.load(fis, "123456".toCharArray()); KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); kmf.init(ks, "123456".toCharArray()); - } catch (Exception e) { - Grasscutter.getLogger().warn("[Dispatch] Error while loading keystore!"); + _kmf = kmf; } - sslContext.init(kmf.getKeyManagers(), null, null); + sslContext.init(_kmf.getKeyManagers(), null, null); httpsServer.setHttpsConfigurator(new HttpsConfigurator(sslContext)); server = httpsServer; From b2840b17401950bd5c08ab48dfb9da71a35622dd Mon Sep 17 00:00:00 2001 From: Yazawazi <47273265+Yazawazi@users.noreply.github.com> Date: Sun, 24 Apr 2022 11:25:13 +0800 Subject: [PATCH 42/78] Player level reward --- .../recv/HandlerTakePlayerLevelRewardReq.java | 43 +++++++++++++++++++ .../send/PacketTakePlayerLevelRewardRsp.java | 26 +++++++++++ 2 files changed, 69 insertions(+) create mode 100644 src/main/java/emu/grasscutter/server/packet/recv/HandlerTakePlayerLevelRewardReq.java create mode 100644 src/main/java/emu/grasscutter/server/packet/send/PacketTakePlayerLevelRewardRsp.java diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerTakePlayerLevelRewardReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerTakePlayerLevelRewardReq.java new file mode 100644 index 000000000..d3f50acee --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerTakePlayerLevelRewardReq.java @@ -0,0 +1,43 @@ +package emu.grasscutter.server.packet.recv; + +import java.util.LinkedList; +import java.util.List; + +import emu.grasscutter.data.GenshinData; +import emu.grasscutter.data.common.RewardItemData; +import emu.grasscutter.game.inventory.GenshinItem; +import emu.grasscutter.game.props.ActionReason; +import emu.grasscutter.net.packet.Opcodes; +import emu.grasscutter.net.packet.PacketHandler; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.TakePlayerLevelRewardReqOuterClass.TakePlayerLevelRewardReq; +import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.server.packet.send.PacketItemAddHintNotify; +import emu.grasscutter.server.packet.send.PacketTakePlayerLevelRewardRsp; + +@Opcodes(PacketOpcodes.TakePlayerLevelRewardReq) +public class HandlerTakePlayerLevelRewardReq extends PacketHandler { + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + TakePlayerLevelRewardReq req = TakePlayerLevelRewardReq.parseFrom(payload); + + int level = req.getLevel(); + int rewardId = GenshinData.getPlayerLevelDataMap().get(level).getRewardId(); + + if (rewardId != 0) { + List rewardItems = GenshinData.getRewardDataMap().get(rewardId).getRewardItemList(); + List items = new LinkedList<>(); + for (RewardItemData rewardItem : rewardItems) { + if (rewardItem != null) { + if (rewardItem.getItemId() != 0) { + items.add(new GenshinItem(rewardItem.getItemId(), rewardItem.getItemCount())); + } + } + } + session.getPlayer().getInventory().addItems(items); + session.getPlayer().sendPacket(new PacketItemAddHintNotify(items, ActionReason.PlayerUpgradeReward)); + } + + session.send(new PacketTakePlayerLevelRewardRsp(level, rewardId)); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketTakePlayerLevelRewardRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketTakePlayerLevelRewardRsp.java new file mode 100644 index 000000000..5a50d0806 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketTakePlayerLevelRewardRsp.java @@ -0,0 +1,26 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.GenshinPacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.TakePlayerLevelRewardRspOuterClass.TakePlayerLevelRewardRsp; + +public class PacketTakePlayerLevelRewardRsp extends GenshinPacket { + + public PacketTakePlayerLevelRewardRsp(int level, int rewardId) { + super(PacketOpcodes.TakePlayerLevelRewardRsp); + + int retcode = 0; + + if (rewardId == 0) { + retcode = 1; + } + + TakePlayerLevelRewardRsp proto = TakePlayerLevelRewardRsp.newBuilder() + .setLevel(level) + .setRewardId(rewardId) + .setRetcode(retcode) + .build(); + + this.setData(proto); + } +} From 2f5ae6c784a9cf79ff922a15b4eed5e798b8c5f6 Mon Sep 17 00:00:00 2001 From: xtaodada Date: Sun, 24 Apr 2022 11:40:10 +0800 Subject: [PATCH 43/78] GodMode command supports playerId --- .../command/commands/GodModeCommand.java | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/main/java/emu/grasscutter/command/commands/GodModeCommand.java b/src/main/java/emu/grasscutter/command/commands/GodModeCommand.java index e5635dc9f..0b4139ac1 100644 --- a/src/main/java/emu/grasscutter/command/commands/GodModeCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/GodModeCommand.java @@ -1,5 +1,6 @@ package emu.grasscutter.command.commands; +import emu.grasscutter.Grasscutter; import emu.grasscutter.command.Command; import emu.grasscutter.command.CommandHandler; import emu.grasscutter.game.GenshinPlayer; @@ -16,7 +17,29 @@ public final class GodModeCommand implements CommandHandler { CommandHandler.sendMessage(null, "Run this command in-game."); return; // TODO: toggle player's godmode statue from console or other players } - sender.setGodmode(!sender.inGodmode()); - sender.dropMessage("Godmode is now " + (sender.inGodmode() ? "enabled" : "disabled") + "."); + + int target; + if (args.size() == 1) { + try { + target = Integer.parseInt(args.get(0)); + if (Grasscutter.getGameServer().getPlayerByUid(target) == null) { + target = sender.getUid(); + } + } catch (NumberFormatException e) { + CommandHandler.sendMessage(sender, "Invalid player id."); + return; + } + } else { + target = sender.getUid(); + } + GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target); + if (targetPlayer == null) { + CommandHandler.sendMessage(sender, "Player not found."); + return; + } + + targetPlayer.setGodmode(!targetPlayer.inGodmode()); + sender.dropMessage("Godmode is now " + (targetPlayer.inGodmode() ? "enabled" : "disabled") + + "for " + targetPlayer.getNickname() + " ."); } } From 56a1ab64abf8b3ca3693090d11ef46c7cca6ea35 Mon Sep 17 00:00:00 2001 From: Yazawazi <47273265+Yazawazi@users.noreply.github.com> Date: Sun, 24 Apr 2022 13:22:26 +0800 Subject: [PATCH 44/78] Player Level Reward Update Notify Support! --- .../emu/grasscutter/game/GenshinPlayer.java | 12 ++++++++++ .../recv/HandlerTakePlayerLevelRewardReq.java | 5 +++++ .../PacketPlayerLevelRewardUpdateNotify.java | 22 +++++++++++++++++++ 3 files changed, 39 insertions(+) create mode 100644 src/main/java/emu/grasscutter/server/packet/send/PacketPlayerLevelRewardUpdateNotify.java diff --git a/src/main/java/emu/grasscutter/game/GenshinPlayer.java b/src/main/java/emu/grasscutter/game/GenshinPlayer.java index cdef5852e..271a6f014 100644 --- a/src/main/java/emu/grasscutter/game/GenshinPlayer.java +++ b/src/main/java/emu/grasscutter/game/GenshinPlayer.java @@ -52,6 +52,7 @@ import emu.grasscutter.server.packet.send.PacketPlayerPropNotify; import emu.grasscutter.server.packet.send.PacketPlayerStoreNotify; import emu.grasscutter.server.packet.send.PacketPrivateChatNotify; import emu.grasscutter.server.packet.send.PacketScenePlayerLocationNotify; +import emu.grasscutter.server.packet.send.PacketPlayerLevelRewardUpdateNotify; import emu.grasscutter.server.packet.send.PacketSetNameCardRsp; import emu.grasscutter.server.packet.send.PacketStoreWeightLimitNotify; import emu.grasscutter.server.packet.send.PacketUnlockNameCardNotify; @@ -96,6 +97,7 @@ public class GenshinPlayer { private MpSettingType mpSetting = MpSettingType.MpSettingEnterAfterApply; private boolean showAvatar; private ArrayList shownAvatars; + private Set rewardedLevels; private int sceneId; private int regionId; @@ -143,6 +145,7 @@ public class GenshinPlayer { this.clientAbilityInitFinishHandler = new InvokeHandler(PacketClientAbilityInitFinishNotify.class); this.birthday = new PlayerBirthday(); + this.rewardedLevels = new HashSet<>(); } // On player creation @@ -656,6 +659,14 @@ public class GenshinPlayer { this.updateProfile(); } + public Set getRewardedLevels() { + return rewardedLevels; + } + + public void setRewardedLevels(Set rewardedLevels) { + this.rewardedLevels = rewardedLevels; + } + public SocialDetail.Builder getSocialDetail() { SocialDetail.Builder social = SocialDetail.newBuilder() .setUid(this.getUid()) @@ -773,6 +784,7 @@ public class GenshinPlayer { session.send(new PacketAvatarDataNotify(this)); session.send(new PacketPlayerEnterSceneNotify(this)); // Enter game world + session.send(new PacketPlayerLevelRewardUpdateNotify(rewardedLevels)); session.send(new PacketOpenStateUpdateNotify()); // First notify packets sent diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerTakePlayerLevelRewardReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerTakePlayerLevelRewardReq.java index d3f50acee..a81da7758 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerTakePlayerLevelRewardReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerTakePlayerLevelRewardReq.java @@ -2,7 +2,9 @@ package emu.grasscutter.server.packet.recv; import java.util.LinkedList; import java.util.List; +import java.util.Set; +import emu.grasscutter.Grasscutter; import emu.grasscutter.data.GenshinData; import emu.grasscutter.data.common.RewardItemData; import emu.grasscutter.game.inventory.GenshinItem; @@ -36,6 +38,9 @@ public class HandlerTakePlayerLevelRewardReq extends PacketHandler { } session.getPlayer().getInventory().addItems(items); session.getPlayer().sendPacket(new PacketItemAddHintNotify(items, ActionReason.PlayerUpgradeReward)); + Set rewardedLevels = session.getPlayer().getRewardedLevels(); + rewardedLevels.add(level); + session.getPlayer().setRewardedLevels(rewardedLevels); } session.send(new PacketTakePlayerLevelRewardRsp(level, rewardId)); diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerLevelRewardUpdateNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerLevelRewardUpdateNotify.java new file mode 100644 index 000000000..6a20e288b --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerLevelRewardUpdateNotify.java @@ -0,0 +1,22 @@ +package emu.grasscutter.server.packet.send; + +import java.util.Set; + +import emu.grasscutter.net.packet.GenshinPacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.PlayerLevelRewardUpdateNotifyOuterClass.PlayerLevelRewardUpdateNotify; + +public class PacketPlayerLevelRewardUpdateNotify extends GenshinPacket { + + public PacketPlayerLevelRewardUpdateNotify(Set rewardedLevels) { + super(PacketOpcodes.PlayerLevelRewardUpdateNotify); + + PlayerLevelRewardUpdateNotify.Builder proto = PlayerLevelRewardUpdateNotify.newBuilder(); + + for (Integer level : rewardedLevels) { + proto.addLevelList(level); + } + + this.setData(proto.build()); + } +} From 8d8866aa63ea8cba54686118bcb8700b8f333be7 Mon Sep 17 00:00:00 2001 From: Yazawazi <47273265+Yazawazi@users.noreply.github.com> Date: Sun, 24 Apr 2022 15:02:31 +0800 Subject: [PATCH 45/78] Fix fetter system --- .../emu/grasscutter/game/avatar/GenshinAvatar.java | 12 ++++++++---- .../recv/HandlerAvatarFetterLevelRewardReq.java | 4 +++- .../packet/send/PacketAvatarFetterDataNotify.java | 12 ++++++++---- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java b/src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java index ab2f67a43..b6007e280 100644 --- a/src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java +++ b/src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java @@ -743,9 +743,14 @@ public class GenshinAvatar { } public AvatarInfo toProto() { + int fetterLevel = this.getFetterLevel(); AvatarFetterInfo.Builder avatarFetter = AvatarFetterInfo.newBuilder() - .setExpLevel(this.getFetterLevel()) - .setExpNumber(this.getFetterExp()); + .setExpLevel(fetterLevel); + + if (fetterLevel != 10) { + avatarFetter.setExpNumber(this.getFetterExp()); + } + if (this.getFetterList() != null) { for (int i = 0; i < this.getFetterList().size(); i++) { @@ -757,11 +762,10 @@ public class GenshinAvatar { } } - int rewardId = this.getNameCardRewardId(); int cardId = this.getNameCardId(); if (this.getPlayer().getNameCardList().contains(cardId)) { - avatarFetter.addRewardedFetterLevelList(rewardId); + avatarFetter.addRewardedFetterLevelList(10); } AvatarInfo.Builder avatarInfo = AvatarInfo.newBuilder() diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarFetterLevelRewardReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarFetterLevelRewardReq.java index 51192f620..b8b4f5e57 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarFetterLevelRewardReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarFetterLevelRewardReq.java @@ -9,6 +9,7 @@ import emu.grasscutter.net.packet.Opcodes; import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.proto.AvatarFetterLevelRewardReqOuterClass.AvatarFetterLevelRewardReq; import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.server.packet.send.PacketAvatarDataNotify; import emu.grasscutter.server.packet.send.PacketAvatarFetterDataNotify; import emu.grasscutter.server.packet.send.PacketAvatarFetterLevelRewardRsp; import emu.grasscutter.server.packet.send.PacketItemAddHintNotify; @@ -46,8 +47,9 @@ public class HandlerAvatarFetterLevelRewardReq extends PacketHandler { session.getPlayer().getInventory().addItem(item); session.getPlayer().sendPacket(new PacketItemAddHintNotify(item, ActionReason.FetterLevelReward)); session.getPlayer().sendPacket(new PacketUnlockNameCardNotify(cardId)); - session.send(new PacketAvatarFetterLevelRewardRsp(avatarGuid, req.getFetterLevel(), rewardId)); session.send(new PacketAvatarFetterDataNotify(avatar)); + session.send(new PacketAvatarDataNotify(avatar.getPlayer())); + session.send(new PacketAvatarFetterLevelRewardRsp(avatarGuid, req.getFetterLevel(), rewardId)); } } } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketAvatarFetterDataNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketAvatarFetterDataNotify.java index 6be12e603..68d3dbeac 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketAvatarFetterDataNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketAvatarFetterDataNotify.java @@ -13,9 +13,14 @@ public class PacketAvatarFetterDataNotify extends GenshinPacket { public PacketAvatarFetterDataNotify(GenshinAvatar avatar) { super(PacketOpcodes.AvatarFetterDataNotify); + int fetterLevel = avatar.getFetterLevel(); + AvatarFetterInfo.Builder avatarFetter = AvatarFetterInfo.newBuilder() - .setExpLevel(avatar.getFetterLevel()) - .setExpNumber(avatar.getFetterExp()); + .setExpLevel(avatar.getFetterLevel()); + + if (fetterLevel != 10) { + avatarFetter.setExpNumber(avatar.getFetterExp()); + } if (avatar.getFetterList() != null) { for (int i = 0; i < avatar.getFetterList().size(); i++) { @@ -27,11 +32,10 @@ public class PacketAvatarFetterDataNotify extends GenshinPacket { } } - int rewardId = avatar.getNameCardRewardId(); int cardId = avatar.getNameCardId(); if (avatar.getPlayer().getNameCardList().contains(cardId)) { - avatarFetter.addRewardedFetterLevelList(rewardId); + avatarFetter.addRewardedFetterLevelList(10); } AvatarFetterInfo avatarFetterInfo = avatarFetter.build(); From 23f0406b578b6cb97cb3efe008bafdb4f1665090 Mon Sep 17 00:00:00 2001 From: Yazawazi <47273265+Yazawazi@users.noreply.github.com> Date: Sun, 24 Apr 2022 15:10:25 +0800 Subject: [PATCH 46/78] command fix too --- .../grasscutter/command/commands/SetFetterLevelCommand.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/emu/grasscutter/command/commands/SetFetterLevelCommand.java b/src/main/java/emu/grasscutter/command/commands/SetFetterLevelCommand.java index 74847a49a..ebf8d0776 100644 --- a/src/main/java/emu/grasscutter/command/commands/SetFetterLevelCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/SetFetterLevelCommand.java @@ -35,7 +35,9 @@ public final class SetFetterLevelCommand implements CommandHandler { GenshinAvatar avatar = sender.getTeamManager().getCurrentAvatarEntity().getAvatar(); avatar.setFetterLevel(fetterLevel); - avatar.setFetterExp(GenshinData.getAvatarFetterLevelDataMap().get(fetterLevel).getExp()); + if (fetterLevel != 10) { + avatar.setFetterExp(GenshinData.getAvatarFetterLevelDataMap().get(fetterLevel).getExp()); + } avatar.save(); sender.sendPacket(new PacketAvatarFetterDataNotify(avatar)); From 26b68479e7c645bd69975b5b05d0ca284e238ca6 Mon Sep 17 00:00:00 2001 From: lunaticwhat Date: Sun, 24 Apr 2022 14:34:29 +0700 Subject: [PATCH 47/78] corrected some information for dev branch --- README.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 14fe3faf9..fd466c198 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # Grasscutter -A WIP server reimplementation for *some anime game* 2.3-2.6 +A WIP server reimplementation for *certain anime game* 2.3-2.6 + +We always welcome contributors to the project. Before adding your contribution, please carefully read our [Code of Conduct](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md). **Documentation**: [Grasscutter Wiki](https://github.com/Melledy/Grasscutter/wiki/) **Note**: For support please join the [Discord server](https://discord.gg/T5vZU6UyeG). @@ -16,7 +18,7 @@ A WIP server reimplementation for *some anime game* 2.3-2.6 * If you updated from an older version, delete `config.json` to regenerate it. ### Prerequisites -* Java 16 +* Java SE Development Kit 16 * Mongodb (recommended 4.0+) * Proxy daemon: mitmproxy (mitmdump, recommended), Fiddler Classic, etc. @@ -68,11 +70,10 @@ There is a dummy user named "Server" in every player's friends list that you can `weather [weather id] [climate id]` - Changes the current weather. -*More commands will be updated in the [wiki](https://github.com/Melledy/Grasscutter/wiki/).* - -### Bonus When you want to teleport somewhere, use the in-game marking function on the Map, click Confirm. You will see your character falling from a very high spot at the exact location you marked. +*More commands will be updated in the [wiki](https://github.com/Melledy/Grasscutter/wiki/).* + # Quick Troubleshooting * If compiling wasn't successful, please check your JDK installation (must be JDK 8 and validated JDK's bin PATH variable) * My client doesn't connect, doesn't login, 4206, etc... - Mostly your proxy daemon setup is *the issue*, if you're using Fiddler make sure it's running on a port other than 8888 From 96c02107074be949d499ba6222ef1889e6a71085 Mon Sep 17 00:00:00 2001 From: RimuruChan <179793046@qq.com> Date: Sun, 24 Apr 2022 15:35:22 +0800 Subject: [PATCH 48/78] Fix a spelling mistake --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 14fe3faf9..fc8767996 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ A WIP server reimplementation for *some anime game* 2.3-2.6 * Logging in * Combat * Spawning monsters via console -* Inventory features (recieving items/characters, upgrading items/characters, etc) +* Inventory features (receiving items/characters, upgrading items/characters, etc) * Gacha system * Friends list * Co-op *partially* works From 8066b16417f5ba33f15475de1f329b9387aaa7f8 Mon Sep 17 00:00:00 2001 From: memetrollsXD Date: Sun, 24 Apr 2022 12:24:41 +0200 Subject: [PATCH 49/78] Add .vscode to gitignore and use @fumbling644o's implementation --- .gitignore | 5 +- .../server/dispatch/DispatchServer.java | 297 +++++++++++------- 2 files changed, 181 insertions(+), 121 deletions(-) diff --git a/.gitignore b/.gitignore index 9df6d0071..55e9c94af 100644 --- a/.gitignore +++ b/.gitignore @@ -45,6 +45,9 @@ tmp/ .loadpath .recommenders +# VSCode +.vscode + # Grasscutter resources/* logs/* @@ -56,4 +59,4 @@ GM Handbook.txt config.json mitmdump.exe grasscutter.jar -mongod.exe +mongod.exe \ No newline at end of file diff --git a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java index 2d36b224e..6d8a06e7c 100644 --- a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java +++ b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java @@ -38,47 +38,48 @@ public final class DispatchServer { private final InetSocketAddress address; private final Gson gson; private final String defaultServerName = "os_usa"; - + public String regionListBase64; public HashMap regions; public DispatchServer() { this.regions = new HashMap(); - this.address = new InetSocketAddress(Grasscutter.getConfig().getDispatchOptions().Ip, Grasscutter.getConfig().getDispatchOptions().Port); + this.address = new InetSocketAddress(Grasscutter.getConfig().getDispatchOptions().Ip, + Grasscutter.getConfig().getDispatchOptions().Port); this.gson = new GsonBuilder().create(); - + this.loadQueries(); this.initRegion(); } - + public InetSocketAddress getAddress() { return address; } - + public Gson getGsonFactory() { return gson; } 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.equalsIgnoreCase("HYBRID")) { return regions.get(defaultServerName).parsedRegionQuery; } Grasscutter.getLogger().warn("[Dispatch] Unsupported run mode for getCurrRegion()"); return null; } - + public void loadQueries() { File file; - + file = new File(Grasscutter.getConfig().DATA_FOLDER + "query_region_list.txt"); if (file.exists()) { query_region_list = new String(FileUtils.read(file)); } else { Grasscutter.getLogger().warn("[Dispatch] query_region_list not found! Using default region list."); } - + file = new File(Grasscutter.getConfig().DATA_FOLDER + "query_cur_region.txt"); if (file.exists()) { query_cur_region = new String(FileUtils.read(file)); @@ -91,40 +92,58 @@ public final class DispatchServer { try { byte[] decoded = Base64.getDecoder().decode(query_region_list); QueryRegionListHttpRsp rl = QueryRegionListHttpRsp.parseFrom(decoded); - + byte[] decoded2 = Base64.getDecoder().decode(query_cur_region); QueryCurrRegionHttpRsp regionQuery = QueryCurrRegionHttpRsp.parseFrom(decoded2); 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 hybrid mode + if (Grasscutter.getConfig().RunMode.equalsIgnoreCase("HYBRID")) { // Automatically add the game server if in + // hybrid mode RegionSimpleInfo server = RegionSimpleInfo.newBuilder() .setName("os_usa") .setTitle(Grasscutter.getConfig().getGameServerOptions().Name) .setType("DEV_PUBLIC") - .setDispatchUrl("http" + (Grasscutter.getConfig().getDispatchOptions().FrontHTTPS ? "s" : "") + "://" + (Grasscutter.getConfig().getDispatchOptions().PublicIp.isEmpty() ? Grasscutter.getConfig().getDispatchOptions().Ip : Grasscutter.getConfig().getDispatchOptions().PublicIp) + ":" + (Grasscutter.getConfig().getDispatchOptions().PublicPort != 0 ? Grasscutter.getConfig().getDispatchOptions().PublicPort : Grasscutter.getConfig().getDispatchOptions().Port) + "/query_cur_region_" + defaultServerName) + .setDispatchUrl( + "http" + (Grasscutter.getConfig().getDispatchOptions().FrontHTTPS ? "s" : "") + "://" + + (Grasscutter.getConfig().getDispatchOptions().PublicIp.isEmpty() + ? Grasscutter.getConfig().getDispatchOptions().Ip + : Grasscutter.getConfig().getDispatchOptions().PublicIp) + + ":" + + (Grasscutter.getConfig().getDispatchOptions().PublicPort != 0 + ? Grasscutter.getConfig().getDispatchOptions().PublicPort + : Grasscutter.getConfig().getDispatchOptions().Port) + + "/query_cur_region_" + defaultServerName) .build(); usedNames.add(defaultServerName); servers.add(server); RegionInfo serverRegion = regionQuery.getRegionInfo().toBuilder() - .setIp((Grasscutter.getConfig().getGameServerOptions().PublicIp.isEmpty() ? Grasscutter.getConfig().getGameServerOptions().Ip : Grasscutter.getConfig().getGameServerOptions().PublicIp)) - .setPort(Grasscutter.getConfig().getGameServerOptions().PublicPort != 0 ? Grasscutter.getConfig().getGameServerOptions().PublicPort : Grasscutter.getConfig().getGameServerOptions().Port) - .setSecretKey(ByteString.copyFrom(FileUtils.read(Grasscutter.getConfig().KEY_FOLDER + "dispatchSeed.bin"))) + .setIp((Grasscutter.getConfig().getGameServerOptions().PublicIp.isEmpty() + ? Grasscutter.getConfig().getGameServerOptions().Ip + : Grasscutter.getConfig().getGameServerOptions().PublicIp)) + .setPort(Grasscutter.getConfig().getGameServerOptions().PublicPort != 0 + ? Grasscutter.getConfig().getGameServerOptions().PublicPort + : Grasscutter.getConfig().getGameServerOptions().Port) + .setSecretKey(ByteString + .copyFrom(FileUtils.read(Grasscutter.getConfig().KEY_FOLDER + "dispatchSeed.bin"))) .build(); QueryCurrRegionHttpRsp parsedRegionQuery = regionQuery.toBuilder().setRegionInfo(serverRegion).build(); - regions.put(defaultServerName, new RegionData(parsedRegionQuery, Base64.getEncoder().encodeToString(parsedRegionQuery.toByteString().toByteArray()))); + regions.put(defaultServerName, new RegionData(parsedRegionQuery, + Base64.getEncoder().encodeToString(parsedRegionQuery.toByteString().toByteArray()))); } else { - if(Grasscutter.getConfig().getDispatchOptions().getGameServers().length == 0) { - Grasscutter.getLogger().error("[Dispatch] There are no game servers available. Exiting due to unplayable state."); + if (Grasscutter.getConfig().getDispatchOptions().getGameServers().length == 0) { + Grasscutter.getLogger() + .error("[Dispatch] There are no game servers available. Exiting due to unplayable state."); System.exit(1); } } - for (Config.DispatchServerOptions.RegionInfo regionInfo : Grasscutter.getConfig().getDispatchOptions().getGameServers()) { - if(usedNames.contains(regionInfo.Name)) { + for (Config.DispatchServerOptions.RegionInfo regionInfo : Grasscutter.getConfig().getDispatchOptions() + .getGameServers()) { + if (usedNames.contains(regionInfo.Name)) { Grasscutter.getLogger().error("Region name already in use."); continue; } @@ -132,7 +151,12 @@ public final class DispatchServer { .setName(regionInfo.Name) .setTitle(regionInfo.Title) .setType("DEV_PUBLIC") - .setDispatchUrl("http" + (Grasscutter.getConfig().getDispatchOptions().FrontHTTPS ? "s" : "") + "://" + (Grasscutter.getConfig().getDispatchOptions().PublicIp.isEmpty() ? Grasscutter.getConfig().getDispatchOptions().Ip : Grasscutter.getConfig().getDispatchOptions().PublicIp) + ":" + getAddress().getPort() + "/query_cur_region_" + regionInfo.Name) + .setDispatchUrl( + "http" + (Grasscutter.getConfig().getDispatchOptions().FrontHTTPS ? "s" : "") + "://" + + (Grasscutter.getConfig().getDispatchOptions().PublicIp.isEmpty() + ? Grasscutter.getConfig().getDispatchOptions().Ip + : Grasscutter.getConfig().getDispatchOptions().PublicIp) + + ":" + getAddress().getPort() + "/query_cur_region_" + regionInfo.Name) .build(); usedNames.add(regionInfo.Name); servers.add(server); @@ -140,26 +164,28 @@ public final class DispatchServer { RegionInfo serverRegion = regionQuery.getRegionInfo().toBuilder() .setIp(regionInfo.Ip) .setPort(regionInfo.Port) - .setSecretKey(ByteString.copyFrom(FileUtils.read(Grasscutter.getConfig().KEY_FOLDER + "dispatchSeed.bin"))) + .setSecretKey(ByteString + .copyFrom(FileUtils.read(Grasscutter.getConfig().KEY_FOLDER + "dispatchSeed.bin"))) .build(); QueryCurrRegionHttpRsp parsedRegionQuery = regionQuery.toBuilder().setRegionInfo(serverRegion).build(); - regions.put(regionInfo.Name, new RegionData(parsedRegionQuery, Base64.getEncoder().encodeToString(parsedRegionQuery.toByteString().toByteArray()))); + regions.put(regionInfo.Name, new RegionData(parsedRegionQuery, + Base64.getEncoder().encodeToString(parsedRegionQuery.toByteString().toByteArray()))); } QueryRegionListHttpRsp regionList = QueryRegionListHttpRsp.newBuilder() - .addAllServers(servers) - .setClientSecretKey(rl.getClientSecretKey()) - .setClientCustomConfigEncrypted(rl.getClientCustomConfigEncrypted()) - .setEnableLoginPc(true) - .build(); + .addAllServers(servers) + .setClientSecretKey(rl.getClientSecretKey()) + .setClientCustomConfigEncrypted(rl.getClientCustomConfigEncrypted()) + .setEnableLoginPc(true) + .build(); this.regionListBase64 = Base64.getEncoder().encodeToString(regionList.toByteString().toByteArray()); } catch (Exception e) { Grasscutter.getLogger().error("[Dispatch] Error while initializing region info!", e); } } - + private HttpServer safelyCreateServer(InetSocketAddress address) { try { return HttpServer.create(address, 0); @@ -167,7 +193,8 @@ public final class DispatchServer { Grasscutter.getLogger().error("Unable to bind to port: " + getAddress().getPort() + " (HTTP)"); } catch (Exception exception) { Grasscutter.getLogger().error("Unable to start HTTP server.", exception); - } return null; + } + return null; } public void start() throws Exception { @@ -184,17 +211,32 @@ public final class DispatchServer { KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); _kmf = kmf; kmf.init(ks, keystorePassword); - } catch (Exception e) { - Grasscutter.getLogger().warn("[Dispatch] Unable to load keystore. Using default keystore password..."); - KeyStore ks = KeyStore.getInstance("PKCS12"); - ks.load(fis, "123456".toCharArray()); - KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); - kmf.init(ks, "123456".toCharArray()); - _kmf = kmf; + } catch (Exception originalEx) { + try { + // try to initialize kmf with the default password + char[] defaultPassword = "123456".toCharArray(); + + Grasscutter.getLogger() + .warn("[Dispatch] Unable to load keystore. Trying default keystore password..."); + KeyStore ks = KeyStore.getInstance("PKCS12"); + ks.load(fis, defaultPassword); + KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); + kmf.init(ks, defaultPassword); + _kmf = kmf; + + Grasscutter.getLogger().warn( + "[Dispatch] The default keystore password was loaded successfully. Please consider setting the password in config.json."); + } catch (Exception ignored) { + Grasscutter.getLogger().warn("[Dispatch] Error while loading keystore!"); + + // don't care about the exception for the "123456" default password attempt + originalEx.printStackTrace(); + throw originalEx; + } } - + sslContext.init(_kmf.getKeyManagers(), null, null); - + httpsServer.setHttpsConfigurator(new HttpsConfigurator(sslContext)); server = httpsServer; } catch (BindException ignored) { @@ -208,16 +250,17 @@ public final class DispatchServer { } else { server = this.safelyCreateServer(this.getAddress()); } - - if(server == null) + + if (server == null) throw new NullPointerException("An HTTP server was not created."); server.createContext("/", t -> responseHTML(t, "Hello")); - + // Dispatch server.createContext("/query_region_list", t -> { // Log - Grasscutter.getLogger().info(String.format("[Dispatch] Client %s request: query_region_list", t.getRemoteAddress())); + Grasscutter.getLogger() + .info(String.format("[Dispatch] Client %s request: query_region_list", t.getRemoteAddress())); responseHTML(t, regionListBase64); }); @@ -226,7 +269,8 @@ public final class DispatchServer { server.createContext("/query_cur_region_" + regionName, t -> { String regionCurrentBase64 = regions.get(regionName).Base64; // Log - Grasscutter.getLogger().info(String.format("Client %s request: query_cur_region_%s", t.getRemoteAddress(), regionName)); + Grasscutter.getLogger().info( + String.format("Client %s request: query_cur_region_%s", t.getRemoteAddress(), regionName)); // Create a response form the request query parameters URI uri = t.getRequestURI(); String response = "CAESGE5vdCBGb3VuZCB2ZXJzaW9uIGNvbmZpZw=="; @@ -244,7 +288,8 @@ public final class DispatchServer { try { String body = Utils.toString(t.getRequestBody()); requestData = getGsonFactory().fromJson(body, LoginAccountRequestJson.class); - } catch (Exception ignored) { } + } catch (Exception ignored) { + } // Create response json if (requestData == null) { @@ -252,16 +297,19 @@ public final class DispatchServer { } LoginResultJson responseData = new LoginResultJson(); - Grasscutter.getLogger().info(String.format("[Dispatch] Client %s is trying to log in", t.getRemoteAddress())); - + Grasscutter.getLogger() + .info(String.format("[Dispatch] Client %s is trying to log in", t.getRemoteAddress())); + // Login Account account = DatabaseHelper.getAccountByName(requestData.account); - + // Check if account exists, else create a new one. if (account == null) { - // Account doesnt exist, so we can either auto create it if the config value is set + // Account doesnt exist, so we can either auto create it if the config value is + // set if (Grasscutter.getConfig().getDispatchOptions().AutomaticallyCreateAccounts) { - // This account has been created AUTOMATICALLY. There will be no permissions added. + // This account has been created AUTOMATICALLY. There will be no permissions + // added. account = DatabaseHelper.createAccountWithId(requestData.account, 0); if (account != null) { @@ -270,19 +318,23 @@ public final class DispatchServer { responseData.data.account.token = account.generateSessionKey(); responseData.data.account.email = account.getEmail(); - Grasscutter.getLogger().info(String.format("[Dispatch] Client %s failed to log in: Account %s created", t.getRemoteAddress(), responseData.data.account.uid)); + Grasscutter.getLogger() + .info(String.format("[Dispatch] Client %s failed to log in: Account %s created", + t.getRemoteAddress(), responseData.data.account.uid)); } else { responseData.retcode = -201; responseData.message = "Username not found, create failed."; - Grasscutter.getLogger().info(String.format("[Dispatch] Client %s failed to log in: Account create failed", t.getRemoteAddress())); + Grasscutter.getLogger().info(String.format( + "[Dispatch] Client %s failed to log in: Account create failed", t.getRemoteAddress())); } } else { responseData.retcode = -201; responseData.message = "Username not found."; - Grasscutter.getLogger().info(String.format("[Dispatch] Client %s failed to log in: Account no found", t.getRemoteAddress())); - } + Grasscutter.getLogger().info(String + .format("[Dispatch] Client %s failed to log in: Account no found", t.getRemoteAddress())); + } } else { // Account was found, log the player in responseData.message = "OK"; @@ -290,7 +342,8 @@ public final class DispatchServer { responseData.data.account.token = account.generateSessionKey(); responseData.data.account.email = account.getEmail(); - Grasscutter.getLogger().info(String.format("[Dispatch] Client %s logged in as %s", t.getRemoteAddress(), responseData.data.account.uid)); + Grasscutter.getLogger().info(String.format("[Dispatch] Client %s logged in as %s", t.getRemoteAddress(), + responseData.data.account.uid)); } responseJSON(t, responseData); @@ -302,31 +355,35 @@ public final class DispatchServer { try { String body = Utils.toString(t.getRequestBody()); requestData = getGsonFactory().fromJson(body, LoginTokenRequestJson.class); - } catch (Exception ignored) { } + } catch (Exception ignored) { + } // Create response json if (requestData == null) { return; } LoginResultJson responseData = new LoginResultJson(); - Grasscutter.getLogger().info(String.format("[Dispatch] Client %s is trying to log in via token", t.getRemoteAddress())); + Grasscutter.getLogger() + .info(String.format("[Dispatch] Client %s is trying to log in via token", t.getRemoteAddress())); // Login Account account = DatabaseHelper.getAccountById(requestData.uid); - + // Test if (account == null || !account.getSessionKey().equals(requestData.token)) { responseData.retcode = -111; responseData.message = "Game account cache information error"; - Grasscutter.getLogger().info(String.format("[Dispatch] Client %s failed to log in via token", t.getRemoteAddress())); + Grasscutter.getLogger() + .info(String.format("[Dispatch] Client %s failed to log in via token", t.getRemoteAddress())); } else { responseData.message = "OK"; responseData.data.account.uid = requestData.uid; responseData.data.account.token = requestData.token; responseData.data.account.email = account.getEmail(); - Grasscutter.getLogger().info(String.format("[Dispatch] Client %s logged in via token as %s", t.getRemoteAddress(), responseData.data.account.uid)); + Grasscutter.getLogger().info(String.format("[Dispatch] Client %s logged in via token as %s", + t.getRemoteAddress(), responseData.data.account.uid)); } responseJSON(t, responseData); @@ -338,113 +395,112 @@ public final class DispatchServer { try { String body = Utils.toString(t.getRequestBody()); requestData = getGsonFactory().fromJson(body, ComboTokenReqJson.class); - } catch (Exception ignored) { } + } catch (Exception ignored) { + } // Create response json if (requestData == null || requestData.data == null) { return; } - LoginTokenData loginData = getGsonFactory().fromJson(requestData.data, LoginTokenData.class); // Get login data + LoginTokenData loginData = getGsonFactory().fromJson(requestData.data, LoginTokenData.class); // Get login + // data ComboTokenResJson responseData = new ComboTokenResJson(); // Login Account account = DatabaseHelper.getAccountById(loginData.uid); - + // Test if (account == null || !account.getSessionKey().equals(loginData.token)) { responseData.retcode = -201; responseData.message = "Wrong session key."; - Grasscutter.getLogger().info(String.format("[Dispatch] Client %s failed to exchange combo token", t.getRemoteAddress())); + Grasscutter.getLogger().info( + String.format("[Dispatch] Client %s failed to exchange combo token", t.getRemoteAddress())); } else { responseData.message = "OK"; responseData.data.open_id = loginData.uid; responseData.data.combo_id = "157795300"; responseData.data.combo_token = account.generateLoginToken(); - Grasscutter.getLogger().info(String.format("[Dispatch] Client %s succeed to exchange combo token", t.getRemoteAddress())); + Grasscutter.getLogger().info( + String.format("[Dispatch] Client %s succeed to exchange combo token", t.getRemoteAddress())); } responseJSON(t, responseData); }); // Agreement and Protocol server.createContext( // hk4e-sdk-os.hoyoverse.com - "/hk4e_global/mdk/agreement/api/getAgreementInfos", - new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"marketing_agreements\":[]}}") - ); + "/hk4e_global/mdk/agreement/api/getAgreementInfos", + new DispatchHttpJsonHandler( + "{\"retcode\":0,\"message\":\"OK\",\"data\":{\"marketing_agreements\":[]}}")); server.createContext( // hk4e-sdk-os.hoyoverse.com - "/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\":\"\"}}}") - ); + "/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 server.createContext( // hk4e-api-os.hoyoverse.com - "/common/hk4e_global/announcement/api/getAlertPic", - new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"total\":0,\"list\":[]}}") - ); + "/common/hk4e_global/announcement/api/getAlertPic", + new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"total\":0,\"list\":[]}}")); server.createContext( // hk4e-api-os.hoyoverse.com "/common/hk4e_global/announcement/api/getAlertAnn", - new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"alert\":false,\"alert_id\":0,\"remind\":true}}") - ); + new DispatchHttpJsonHandler( + "{\"retcode\":0,\"message\":\"OK\",\"data\":{\"alert\":false,\"alert_id\":0,\"remind\":true}}")); server.createContext( // hk4e-api-os.hoyoverse.com - "/common/hk4e_global/announcement/api/getAnnList", - new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"list\":[],\"total\":0,\"type_list\":[],\"alert\":false,\"alert_id\":0,\"timezone\":0,\"t\":\"" + System.currentTimeMillis() + "\"}}") - ); + "/common/hk4e_global/announcement/api/getAnnList", + new DispatchHttpJsonHandler( + "{\"retcode\":0,\"message\":\"OK\",\"data\":{\"list\":[],\"total\":0,\"type_list\":[],\"alert\":false,\"alert_id\":0,\"timezone\":0,\"t\":\"" + + System.currentTimeMillis() + "\"}}")); server.createContext( // hk4e-api-os-static.hoyoverse.com - "/common/hk4e_global/announcement/api/getAnnContent", - new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"list\":[],\"total\":0}}") - ); + "/common/hk4e_global/announcement/api/getAnnContent", + new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"list\":[],\"total\":0}}")); server.createContext( // hk4e-sdk-os.hoyoverse.com - "/hk4e_global/mdk/shopwindow/shopwindow/listPriceTier", - new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"suggest_currency\":\"USD\",\"tiers\":[]}}") - ); + "/hk4e_global/mdk/shopwindow/shopwindow/listPriceTier", + new DispatchHttpJsonHandler( + "{\"retcode\":0,\"message\":\"OK\",\"data\":{\"suggest_currency\":\"USD\",\"tiers\":[]}}")); // Captcha server.createContext( // api-account-os.hoyoverse.com - "/account/risky/api/check", - new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"id\":\"c8820f246a5241ab9973f71df3ddd791\",\"action\":\"\",\"geetest\":{\"challenge\":\"\",\"gt\":\"\",\"new_captcha\":0,\"success\":1}}}") - ); - // Config + "/account/risky/api/check", + new DispatchHttpJsonHandler( + "{\"retcode\":0,\"message\":\"OK\",\"data\":{\"id\":\"c8820f246a5241ab9973f71df3ddd791\",\"action\":\"\",\"geetest\":{\"challenge\":\"\",\"gt\":\"\",\"new_captcha\":0,\"success\":1}}}")); + // Config server.createContext( // sdk-os-static.hoyoverse.com - "/combo/box/api/config/sdk/combo", - new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"vals\":{\"disable_email_bind_skip\":\"false\",\"email_bind_remind_interval\":\"7\",\"email_bind_remind\":\"true\"}}}") - ); + "/combo/box/api/config/sdk/combo", + new DispatchHttpJsonHandler( + "{\"retcode\":0,\"message\":\"OK\",\"data\":{\"vals\":{\"disable_email_bind_skip\":\"false\",\"email_bind_remind_interval\":\"7\",\"email_bind_remind\":\"true\"}}}")); server.createContext( // hk4e-sdk-os-static.hoyoverse.com - "/hk4e_global/combo/granter/api/getConfig", - new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"protocol\":true,\"qr_enabled\":false,\"log_level\":\"INFO\",\"announce_url\":\"https://webstatic-sea.hoyoverse.com/hk4e/announcement/index.html?sdk_presentation_style=fullscreen\\u0026sdk_screen_transparent=true\\u0026game_biz=hk4e_global\\u0026auth_appid=announcement\\u0026game=hk4e#/\",\"push_alias_type\":2,\"disable_ysdk_guard\":false,\"enable_announce_pic_popup\":true}}") - ); + "/hk4e_global/combo/granter/api/getConfig", + new DispatchHttpJsonHandler( + "{\"retcode\":0,\"message\":\"OK\",\"data\":{\"protocol\":true,\"qr_enabled\":false,\"log_level\":\"INFO\",\"announce_url\":\"https://webstatic-sea.hoyoverse.com/hk4e/announcement/index.html?sdk_presentation_style=fullscreen\\u0026sdk_screen_transparent=true\\u0026game_biz=hk4e_global\\u0026auth_appid=announcement\\u0026game=hk4e#/\",\"push_alias_type\":2,\"disable_ysdk_guard\":false,\"enable_announce_pic_popup\":true}}")); server.createContext( // hk4e-sdk-os-static.hoyoverse.com - "/hk4e_global/mdk/shield/api/loadConfig", - new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"id\":6,\"game_key\":\"hk4e_global\",\"client\":\"PC\",\"identity\":\"I_IDENTITY\",\"guest\":false,\"ignore_versions\":\"\",\"scene\":\"S_NORMAL\",\"name\":\"原神海外\",\"disable_regist\":false,\"enable_email_captcha\":false,\"thirdparty\":[\"fb\",\"tw\"],\"disable_mmt\":false,\"server_guest\":false,\"thirdparty_ignore\":{\"tw\":\"\",\"fb\":\"\"},\"enable_ps_bind_account\":false,\"thirdparty_login_configs\":{\"tw\":{\"token_type\":\"TK_GAME_TOKEN\",\"game_token_expires_in\":604800},\"fb\":{\"token_type\":\"TK_GAME_TOKEN\",\"game_token_expires_in\":604800}}}}") - ); + "/hk4e_global/mdk/shield/api/loadConfig", + new DispatchHttpJsonHandler( + "{\"retcode\":0,\"message\":\"OK\",\"data\":{\"id\":6,\"game_key\":\"hk4e_global\",\"client\":\"PC\",\"identity\":\"I_IDENTITY\",\"guest\":false,\"ignore_versions\":\"\",\"scene\":\"S_NORMAL\",\"name\":\"原神海外\",\"disable_regist\":false,\"enable_email_captcha\":false,\"thirdparty\":[\"fb\",\"tw\"],\"disable_mmt\":false,\"server_guest\":false,\"thirdparty_ignore\":{\"tw\":\"\",\"fb\":\"\"},\"enable_ps_bind_account\":false,\"thirdparty_login_configs\":{\"tw\":{\"token_type\":\"TK_GAME_TOKEN\",\"game_token_expires_in\":604800},\"fb\":{\"token_type\":\"TK_GAME_TOKEN\",\"game_token_expires_in\":604800}}}}")); // Test api? server.createContext( // abtest-api-data-sg.hoyoverse.com - "/data_abtest_api/config/experiment/list", - new DispatchHttpJsonHandler("{\"retcode\":0,\"success\":true,\"message\":\"\",\"data\":[{\"code\":1000,\"type\":2,\"config_id\":\"14\",\"period_id\":\"6036_99\",\"version\":\"1\",\"configs\":{\"cardType\":\"old\"}}]}") - ); - // Log Server + "/data_abtest_api/config/experiment/list", + new DispatchHttpJsonHandler( + "{\"retcode\":0,\"success\":true,\"message\":\"\",\"data\":[{\"code\":1000,\"type\":2,\"config_id\":\"14\",\"period_id\":\"6036_99\",\"version\":\"1\",\"configs\":{\"cardType\":\"old\"}}]}")); + // Log Server server.createContext( // log-upload-os.mihoyo.com - "/log/sdk/upload", - new DispatchHttpJsonHandler("{\"code\":0}") - ); + "/log/sdk/upload", + new DispatchHttpJsonHandler("{\"code\":0}")); server.createContext( // log-upload-os.mihoyo.com - "/sdk/upload", - new DispatchHttpJsonHandler("{\"code\":0}") - ); + "/sdk/upload", + new DispatchHttpJsonHandler("{\"code\":0}")); server.createContext( // /perf/config/verify?device_id=xxx&platform=x&name=xxx - "/perf/config/verify", - new DispatchHttpJsonHandler("{\"code\":0}") - ); - + "/perf/config/verify", + new DispatchHttpJsonHandler("{\"code\":0}")); + // Logging servers server.createContext( // overseauspider.yuanshen.com "/log", - new DispatchHttpJsonHandler("{\"code\":0}") - ); + new DispatchHttpJsonHandler("{\"code\":0}")); server.createContext( // log-upload-os.mihoyo.com "/crash/dataUpload", - new DispatchHttpJsonHandler("{\"code\":0}") - ); - server.createContext("/gacha", t -> responseHTML(t, "Gacha")); + new DispatchHttpJsonHandler("{\"code\":0}")); + server.createContext("/gacha", t -> responseHTML(t, + "Gacha")); // Start server server.start(); @@ -467,12 +523,12 @@ public final class DispatchServer { // Set the response header status and length t.getResponseHeaders().put("Content-Type", Collections.singletonList("text/html; charset=UTF-8")); t.sendResponseHeaders(200, response.getBytes().length); - //Write the response string + // Write the response string OutputStream os = t.getResponseBody(); os.write(response.getBytes()); os.close(); } - + private Map parseQueryString(String qs) { Map result = new HashMap<>(); if (qs == null) { @@ -492,7 +548,8 @@ public final class DispatchServer { if (eqPos < 0 || eqPos > next) { result.put(URLDecoder.decode(qs.substring(last, next), "utf-8"), ""); } else { - result.put(URLDecoder.decode(qs.substring(last, eqPos), "utf-8"), URLDecoder.decode(qs.substring(eqPos + 1, next), "utf-8")); + result.put(URLDecoder.decode(qs.substring(last, eqPos), "utf-8"), + URLDecoder.decode(qs.substring(eqPos + 1, next), "utf-8")); } } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); // will never happen, utf-8 support is mandatory for java From fc23d3e0bdbe9704e7f4446e3de90f25d47cd17e Mon Sep 17 00:00:00 2001 From: FahimFBA Date: Sun, 24 Apr 2022 16:35:53 +0600 Subject: [PATCH 50/78] Create CODE_OF_CONDUCT.md --- CODE_OF_CONDUCT.md | 128 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 CODE_OF_CONDUCT.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..66afbd0ca --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,128 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +[mail](mailto:). +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. \ No newline at end of file From 5a508bce55848f4c558a88edbc79d1abf469dade Mon Sep 17 00:00:00 2001 From: FahimFBA Date: Sun, 24 Apr 2022 16:38:30 +0600 Subject: [PATCH 51/78] Update CODE_OF_CONDUCT.md Added the email address. Please change the mail address as you see fit. --- CODE_OF_CONDUCT.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 66afbd0ca..1a3357144 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -60,9 +60,8 @@ representative at an online or offline event. Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at -[mail](mailto:). +[kobedo11@gmail.com](mailto:kobedo11@gmail.com). All complaints will be reviewed and investigated promptly and fairly. - All community leaders are obligated to respect the privacy and security of the reporter of any incident. From a65cc94e24f0da762d326a7bffa2c4e04280cb88 Mon Sep 17 00:00:00 2001 From: FahimFBA Date: Sun, 24 Apr 2022 19:23:23 +0600 Subject: [PATCH 52/78] Update CODE_OF_CONDUCT.md Changed the mail address to the Discord server --- CODE_OF_CONDUCT.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 1a3357144..9f1e0d3f1 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -60,7 +60,7 @@ representative at an online or offline event. Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at -[kobedo11@gmail.com](mailto:kobedo11@gmail.com). +[our Discord server](https://discord.gg/T5vZU6UyeG). All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the reporter of any incident. From 4c7051bb6adcbc894b4045424ae7da6953e9ec04 Mon Sep 17 00:00:00 2001 From: FahimFBA Date: Sun, 24 Apr 2022 19:24:23 +0600 Subject: [PATCH 53/78] Update CODE_OF_CONDUCT.md --- CODE_OF_CONDUCT.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 9f1e0d3f1..5196d0514 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -60,7 +60,7 @@ representative at an online or offline event. Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at -[our Discord server](https://discord.gg/T5vZU6UyeG). +[the Discord server](https://discord.gg/T5vZU6UyeG). All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the reporter of any incident. From 46d89f5f5c11b7baa6adec90eec5c9f7612195a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=AA=E7=A2=A7?= <42002296+RimuruChan@users.noreply.github.com> Date: Sun, 24 Apr 2022 21:32:48 +0800 Subject: [PATCH 54/78] Update build.gradle fix three cve. --- build.gradle | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index dcc109d0e..c32d56e1f 100644 --- a/build.gradle +++ b/build.gradle @@ -25,12 +25,12 @@ dependencies { implementation fileTree(dir: 'lib', include: ['*.jar']) implementation group: 'org.slf4j', name: 'slf4j-api', version: '1.7.32' - implementation group: 'ch.qos.logback', name: 'logback-core', version: '1.2.6' - implementation group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.6' - implementation group: 'io.netty', name: 'netty-all', version: '4.1.69.Final' + implementation group: 'ch.qos.logback', name: 'logback-core', version: '1.2.9' + implementation group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.9' + implementation group: 'io.netty', name: 'netty-all', version: '4.1.71.Final' implementation group: 'com.google.code.gson', name: 'gson', version: '2.8.8' - implementation group: 'com.google.protobuf', name: 'protobuf-java', version: '3.18.1' + implementation group: 'com.google.protobuf', name: 'protobuf-java', version: '3.18.2' implementation group: 'org.reflections', name: 'reflections', version: '0.10.2' From 4817ed9c5266de2d589ce998bec04d1b03e88a34 Mon Sep 17 00:00:00 2001 From: Alangy Date: Sun, 24 Apr 2022 23:05:08 +0800 Subject: [PATCH 55/78] bugfix: portbind error when falling back to http server --- .../emu/grasscutter/server/dispatch/DispatchServer.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java index 6d8a06e7c..f681baa60 100644 --- a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java +++ b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java @@ -200,8 +200,6 @@ public final class DispatchServer { public void start() throws Exception { HttpServer server; if (Grasscutter.getConfig().getDispatchOptions().UseSSL) { - HttpsServer httpsServer = HttpsServer.create(getAddress(), 0); - SSLContext sslContext = SSLContext.getInstance("TLS"); try (FileInputStream fis = new FileInputStream(Grasscutter.getConfig().getDispatchOptions().KeystorePath)) { char[] keystorePassword = Grasscutter.getConfig().getDispatchOptions().KeystorePassword.toCharArray(); KeyManagerFactory _kmf; @@ -234,9 +232,9 @@ public final class DispatchServer { throw originalEx; } } - + SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(_kmf.getKeyManagers(), null, null); - + HttpsServer httpsServer = HttpsServer.create(getAddress(), 0); httpsServer.setHttpsConfigurator(new HttpsConfigurator(sslContext)); server = httpsServer; } catch (BindException ignored) { From 5010b8ac75e05d78282d400bca547ccf84011717 Mon Sep 17 00:00:00 2001 From: Yazawazi <47273265+Yazawazi@users.noreply.github.com> Date: Mon, 25 Apr 2022 01:22:51 +0800 Subject: [PATCH 56/78] add setfriendship aliases --- .../emu/grasscutter/command/commands/SetFetterLevelCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/emu/grasscutter/command/commands/SetFetterLevelCommand.java b/src/main/java/emu/grasscutter/command/commands/SetFetterLevelCommand.java index ebf8d0776..676a2b279 100644 --- a/src/main/java/emu/grasscutter/command/commands/SetFetterLevelCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/SetFetterLevelCommand.java @@ -11,7 +11,7 @@ import emu.grasscutter.server.packet.send.PacketAvatarFetterDataNotify; @Command(label = "setfetterlevel", usage = "setfetterlevel ", description = "Sets your fetter level for your current active character", - aliases = {"setfetterlvl"}, permission = "player.setfetterlevel") + aliases = {"setfetterlvl", "setfriendship"}, permission = "player.setfetterlevel") public final class SetFetterLevelCommand implements CommandHandler { @Override From 4837ccb98303ccce5cd521ce91a0e9a3bcc701f0 Mon Sep 17 00:00:00 2001 From: alt3ri <95161279+alt3ri@users.noreply.github.com> Date: Mon, 25 Apr 2022 02:45:43 +0700 Subject: [PATCH 57/78] Added UID parameter Now you can delete other player's weapons with UID Usage: /clearwp [uid] --- .../command/commands/ClearWeaponsCommand.java | 28 +++++++++++++++++-- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/src/main/java/emu/grasscutter/command/commands/ClearWeaponsCommand.java b/src/main/java/emu/grasscutter/command/commands/ClearWeaponsCommand.java index 8fab1768b..fbe72db87 100644 --- a/src/main/java/emu/grasscutter/command/commands/ClearWeaponsCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/ClearWeaponsCommand.java @@ -1,5 +1,6 @@ package emu.grasscutter.command.commands; +import emu.grasscutter.Grasscutter; import emu.grasscutter.command.Command; import emu.grasscutter.command.CommandHandler; import emu.grasscutter.game.GenshinPlayer; @@ -10,20 +11,41 @@ import java.util.List; @Command(label = "clearweapons", usage = "clearweapons", description = "Deletes all unequipped and unlocked weapons, including yellow rarity ones from your inventory", - aliases = {"clearwpns"}, permission = "player.clearweapons") + aliases = {"clearwp"}, permission = "player.clearweapons") public final class ClearWeaponsCommand implements CommandHandler { @Override public void execute(GenshinPlayer sender, List args) { if (sender == null) { CommandHandler.sendMessage(null, "Run this command in-game."); - return; // TODO: clear player's weapons from console or other players + return; } - Inventory playerInventory = sender.getInventory(); + int target; + if (args.size() == 1) { + try { + target = Integer.parseInt(args.get(0)); + if (Grasscutter.getGameServer().getPlayerByUid(target) == null) { + target = sender.getUid(); + } + } catch (NumberFormatException e) { + CommandHandler.sendMessage(sender, "Invalid player id."); + return; + } + } else { + target = sender.getUid(); + } + GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target); + if (targetPlayer == null) { + CommandHandler.sendMessage(sender, "Player not found."); + return; + } + + Inventory playerInventory = targetPlayer.getInventory(); playerInventory.getItems().values().stream() .filter(item -> item.getItemType() == ItemType.ITEM_WEAPON) .filter(item -> !item.isLocked() && !item.isEquipped()) .forEach(item -> playerInventory.removeItem(item, item.getCount())); + sender.dropMessage("Cleared weapons for " + targetPlayer.getNickname() + " ."); } } From 21b3ac103afbe32e602cd3a45bdc0c4dc4fdfe05 Mon Sep 17 00:00:00 2001 From: alangy98 <56515358+alangy98@users.noreply.github.com> Date: Mon, 25 Apr 2022 02:30:45 +0800 Subject: [PATCH 58/78] Revert "bugfix: portbind error when falling back to http server" This reverts commit 4817ed9c5266de2d589ce998bec04d1b03e88a34. the bugfix is inappropriate --- .../emu/grasscutter/server/dispatch/DispatchServer.java | 6 ++++-- 1 file changed, 4 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 f681baa60..6d8a06e7c 100644 --- a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java +++ b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java @@ -200,6 +200,8 @@ public final class DispatchServer { public void start() throws Exception { HttpServer server; if (Grasscutter.getConfig().getDispatchOptions().UseSSL) { + HttpsServer httpsServer = HttpsServer.create(getAddress(), 0); + SSLContext sslContext = SSLContext.getInstance("TLS"); try (FileInputStream fis = new FileInputStream(Grasscutter.getConfig().getDispatchOptions().KeystorePath)) { char[] keystorePassword = Grasscutter.getConfig().getDispatchOptions().KeystorePassword.toCharArray(); KeyManagerFactory _kmf; @@ -232,9 +234,9 @@ public final class DispatchServer { throw originalEx; } } - SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(_kmf.getKeyManagers(), null, null); - HttpsServer httpsServer = HttpsServer.create(getAddress(), 0); + httpsServer.setHttpsConfigurator(new HttpsConfigurator(sslContext)); server = httpsServer; } catch (BindException ignored) { From 2238a7c3aa568dbbdc69db5b15b665010aaea7f6 Mon Sep 17 00:00:00 2001 From: Yan <61280820+ubdjshdb@users.noreply.github.com> Date: Sun, 24 Apr 2022 21:38:16 +0300 Subject: [PATCH 59/78] fix setting electro dmg stat --- .../emu/grasscutter/command/commands/SetStatsCommand.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/emu/grasscutter/command/commands/SetStatsCommand.java b/src/main/java/emu/grasscutter/command/commands/SetStatsCommand.java index e3efdb0d5..d7d27a24a 100644 --- a/src/main/java/emu/grasscutter/command/commands/SetStatsCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/SetStatsCommand.java @@ -197,8 +197,8 @@ public final class SetStatsCommand implements CommandHandler { float eelec = Integer.parseInt(args.get(1)); EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity(); float elec = eelec / 10000; - entity.setFightProperty(FightProperty.FIGHT_PROP_CRITICAL_HURT, elec); - entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CRITICAL_HURT)); + entity.setFightProperty(FightProperty.FIGHT_PROP_ELEC_ADD_HURT, elec); + entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_ELEC_ADD_HURT)); float igelec = elec * 100; CommandHandler.sendMessage(sender, "Electro DMG Bonus set to " + igelec + "%"); } catch (NumberFormatException ignored) { From 6666fef81a895f3951ae0cadcc05e899965d856d Mon Sep 17 00:00:00 2001 From: Wansn <2632324507@qq.com> Date: Mon, 25 Apr 2022 10:53:36 +0800 Subject: [PATCH 60/78] Update README.md --- README.md | 166 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 115 insertions(+), 51 deletions(-) diff --git a/README.md b/README.md index a95bf8b1d..009d76800 100644 --- a/README.md +++ b/README.md @@ -1,80 +1,144 @@ -# Grasscutter -A WIP server reimplementation for *certain anime game* 2.3-2.6 +![Grasscutter](https://socialify.git.ci/Grasscutters/Grasscutter/image?description=1&font=KoHo&forks=1&issues=1&language=1&name=1&owner=1&pattern=Circuit%20Board&pulls=1&stargazers=1&theme=Light) +
Documention GitHub release (latest by date) GitHub GitHub last commit GitHub Workflow Status
-We always welcome contributors to the project. Before adding your contribution, please carefully read our [Code of Conduct](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md). +
Discord - Grasscutter
+ +**Attention:** We always welcome contributors to the project. Before adding your contribution, please carefully read our [Code of Conduct](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md). + +## Current features -**Documentation**: [Grasscutter Wiki](https://github.com/Melledy/Grasscutter/wiki/) -**Note**: For support please join the [Discord server](https://discord.gg/T5vZU6UyeG). -# Current features * Logging in * Combat -* Spawning monsters via console -* Inventory features (receiving items/characters, upgrading items/characters, etc) -* Gacha system * Friends list -* Co-op *partially* works -# Quick setup guide -### Note -* If you updated from an older version, delete `config.json` to regenerate it. +* Teleportation +* Gacha system +* Co-op *partially* work +* Spawning monsters via console +* Inventory features (recieving items/characters, upgrading items/characters, etc) + +## Quick setup guide + +**Note:** for support please join our [Discord]() + +### Requirements + +* Java - SE ([mirror link](https://github.com/adoptium/temurin16-binaries/releases/tag/jdk-16.0.2+7) since Oracle required an account to download old builds) + + **Note:** If you just want to **run it**, then **jre** is fine + +* MongoDB (recommended 4.0+) -### Prerequisites -* Java SE Development Kit 16 -* Mongodb (recommended 4.0+) * Proxy daemon: mitmproxy (mitmdump, recommended), Fiddler Classic, etc. -### Starting up Grasscutter server (Assuming you are on Windows) -1. Setup the compile environment with `gradlew.bat` -2. Compile Grasscutter with `gradlew jar` -3. Create a folder named `resources` in your Grasscutter directory, move your `BinOutput` and `ExcelBinOutput` folders there *(Check the wiki for more details on how to get those.)* -4. Run Grasscutter with `java -jar grasscutter.jar`. Make sure mongodb service is running as well. +### Running + +**Note:** If you update from an older version, delete `config.json` for regeneration + +1. Get `grasscutter.jar` + - Download from [actions](https://nightly.link/Grasscutters/Grasscutter/workflows/build/stable/Grasscutter.zip) + - [Build by yourself](#Building) +2. Create a `resources` folder in the directory where grasscutter.jar is located and bring your `BinOutput` and `ExcelBinOutput` folders into it *(Check the [wiki](https://github.com/Grasscutters/Grasscutter/wiki) for more details how to get those.)* +3. Run Grasscutter with `java -jar grasscutter.jar`. **Make sure mongodb service is running as well.** ### Connecting with the client -½. Create an account using *server console command* below -1. Run a proxy daemon: (choose either one) - - mitmdump: `mitmdump -s proxy.py -k --allow-hosts ".*\.yuanshen\.com|.*\.mihoyo\.com|.*\.hoyoverse\.com"` - - Fiddler Classic: Run Fiddler Classic, turn on `Decrypt https traffic` in setting and change the default port there (Tools -> Options -> Connections) to anything other than `8888`, and load [this script](https://github.lunatic.moe/fiddlerscript). - - [Hosts file](https://github.com/Melledy/Grasscutter/wiki/Running#traffic-route-map) -2. Trust CA certificate: - - mitmdump: `certutil -addstore root %USERPROFILE%\.mitmproxy\mitmproxy-ca-cert.cer` + +½. Create an account using [server console command](#Commands). + +1. Redirect traffic: (choose one) + - mitmdump: `mitmdump -s proxy.py -k` + + Trust CA certificate: + + ​ **Note:**The CA certificate is usually stored in `% USERPROFILE%\ .mitmproxy`, or you can download it from `http://mitm.it` + + ​ Double click for [install](https://docs.microsoft.com/en-us/skype-sdk/sdn/articles/installing-the-trusted-root-certificate#installing-a-trusted-root-certificate) or ... + + - Via command line + + ```shell + certutil -addstore root %USERPROFILE%\.mitmproxy\mitmproxy-ca-cert.cer + ``` + + - Fiddler Classic: Run Fiddler Classic, turn on `Decrypt https traffic` in setting and change the default port there (Tools -> Options -> Connections) to anything other than `8888`, and load [this script](https://github.lunatic.moe/fiddlerscript). + + - [Hosts file](https://github.com/Melledy/Grasscutter/wiki/Running#traffic-route-map) + 2. Set network proxy to `127.0.0.1:8080` or the proxy port you specified. -4. *yoink* -* or you can use `start.cmd` to start Server & Proxy daemon with one click +**you can also use `run.cmd` to start servers and proxy daemons automatically** -# Grasscutter commands -There is a dummy user named "Server" in every player's friends list that you can message to use commands. Commands also work in other chat rooms, such as private/team chats. +### Building -`account create [username] {playerid}` - Creates an account with the specified username and the in-game uid for that account. The playerid parameter is optional and will be auto generated if not set. +Grasscutter uses Gradle to handle dependencies & building. -`spawn [monster id] [level] [amount]` +**Requirements:** -`give [item id] [amount] [level]` +- Java Development Kits - 8u202 +- Git -`givechar [avatar id] [level]` +##### Windows -`drop [item id] [amount]` +```shell +git clone https://github.com/Grasscutters/Grasscutter.git +cd Grasscutter +.\gradlew.bat # Setting up environments +.\gradlew jar # Compile +``` -`killall` +##### Linux -`setworldlevel [level]` - Changes your world level, relog to see effects properly +```bash +git clone https://github.com/Grasscutters/Grasscutter.git +cd Grasscutter +chmod +x gradlew +./gradlew jar # Compile +``` -`godmode` - Prevents you from taking damage +You can find the output jar in the root of the project folder -`resetconst` - Resets the constellation level on your currently selected character, will need to relog after using the command to see any changes. -`setstats [stats] [amount]` - Changes the currently selected character's specified stat. +## Commands -`clearartifacts` - Deletes all unequipped and unlocked level 0 artifacts, **including 5-star rarity ones** from your inventory +| Commands | Usage | Permission node | Availability | description | Alias | +| -------------- | ------------------------------------------------- | ------------------------- | ------------ | ------------------------------------------------------------ | ----------------------------------------------- | +| account | account [uid] | | Server only | Creates an account with the specified username and the in-game uid for that account. The uid will be auto generated if not set. | | +| broadcast | broadcast | server.broadcast | Both side | Sends a message to all the players. | b | +| 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 yellow rarity ones from your inventory. | clearart | +| clearweapons | clearweapons | player.clearweapons | Client only | Deletes all unequipped and unlocked weapons, including yellow rarity ones from your inventory. | clearwpns | +| drop | drop [amount] | server.drop | Client only | Drops an item around you. | `d` `dropitem` | +| give | give [player] [amount] [level] | player.give | Both side | Gives item(s) to you or the specified player. | `g` `item` `giveitem` | +| givechar | givechar [level] | player.givechar | Both side | Gives the player a specified character. | givec | +| godmode | godmode [uid] | player.godmode | Client only | Prevents you from taking damage. | | +| heal | heal | player.heal | Client only | Heal all characters in your current team. | h | +| help | help [command] | | Both side | Sends the help message or shows information about a specified command. | | +| kick | kick | server.kick | Both side | Kicks the specified player from the server. (WIP) | k | +| killall | killall [playerUid] [sceneId] | server.killall | Both side | Kill all entities in the current scene or specified scene of the corresponding player. | | +| list | list | | Both side | List online players. | | +| permission | permission | * | Both side | Grants or removes a permission for a user. | | +| position | position | | Client only | Get coordinates. | pos | +| reload | reload | server.reload | Both side | Reload server config | | +| resetconst | resetconst [all] | player.resetconstellation | Client only | Resets the constellation level on your current active character, will need to relog after using the command to see any changes. | resetconstellation | +| restart | | | Both side | Restarts the current session | | +| say | say | server.sendmessage | Both side | Sends a message to a player as the server | `sendservmsg` `sendservermessage` `sendmessage` | +| setfetterlevel | setfetterlevel | player.setfetterlevel | Client only | Sets your fetter level for your current active character | setfetterlvl | +| setstats | setstats | player.setstats | Client only | Set fight property for your current active character | stats | +| setworldlevel | setworldlevel | player.setworldlevel | Client only | Sets your world level (Relog to see proper effects) | setworldlvl | +| spawn | spanw [level] [amount] | server.spawn | Client only | Spawns an entity near you | | +| stop | stop | server.stop | Both side | Stops the server | | +| talent | talent | player.settalent | Client only | Set talent level for your current active character | | +| teleport | teleport | player.teleport | Client only | Change the player's position. | tp | +| weather | weather | player.weather | Client only | Changes the weather | w | -`pos` - Gets your current coordinates. +### Bonus -`weather [weather id] [climate id]` - Changes the current weather. - -When you want to teleport somewhere, use the in-game marking function on the Map, click Confirm. You will see your character falling from a very high spot at the exact location you marked. - -*More commands will be updated in the [wiki](https://github.com/Melledy/Grasscutter/wiki/).* +When you want to teleport to somewhere, use the ingame marking function on Map, click Confirm. You will see your +character falling from a very high destination, exact location that you marked. # Quick Troubleshooting -* If compiling wasn't successful, please check your JDK installation (must be JDK 8 and validated JDK's bin PATH variable) -* My client doesn't connect, doesn't login, 4206, etc... - Mostly your proxy daemon setup is *the issue*, if you're using Fiddler make sure it's running on a port other than 8888 + +* If compiling wasn't successful, please check your JDK installation (must be JDK 8 and validated JDK's bin PATH + variable) +* My client doesn't connect, doesn't login, 4206, etc... - Mostly your proxy daemon setup is *the issue*, if using + Fiddler make sure it running on another port except 8888 * Startup sequence: Mongodb > Grasscutter > Proxy daemon (mitmdump, fiddler, etc.) > Client From 848efc229882be376c96c274c6f1091f98c7adb9 Mon Sep 17 00:00:00 2001 From: Wansn <2632324507@qq.com> Date: Mon, 25 Apr 2022 10:58:27 +0800 Subject: [PATCH 61/78] Update README.md --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 009d76800..7ac84456b 100644 --- a/README.md +++ b/README.md @@ -96,9 +96,12 @@ chmod +x gradlew You can find the output jar in the root of the project folder - ## Commands +You might want to use this command (`java -jar grasscutter.jar -handbook`) in a cmd that is in the grasscutter folder. It will create a handbook file (GM Handbook.txt) where you can find the item IDs for stuff you want + +There is a dummy user named "Server" in every player's friends list that you can message to use commands. Commands also work in other chat rooms, such as private/team chats. to run commands ingame, you need to add prefix `/` or `!` such as `/pos` + | Commands | Usage | Permission node | Availability | description | Alias | | -------------- | ------------------------------------------------- | ------------------------- | ------------ | ------------------------------------------------------------ | ----------------------------------------------- | | account | account [uid] | | Server only | Creates an account with the specified username and the in-game uid for that account. The uid will be auto generated if not set. | | From 570f86978663aa2f5eacebd63472b4473332acd6 Mon Sep 17 00:00:00 2001 From: Wansn <2632324507@qq.com> Date: Mon, 25 Apr 2022 10:59:28 +0800 Subject: [PATCH 62/78] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7ac84456b..1983bab80 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ 2. Set network proxy to `127.0.0.1:8080` or the proxy port you specified. -**you can also use `run.cmd` to start servers and proxy daemons automatically** +**you can also use `start.cmd` to start servers and proxy daemons automatically** ### Building From 67990b4684cfab02fed4152fe3754509b4cf5710 Mon Sep 17 00:00:00 2001 From: Wansn <49867372+Wansn-w@users.noreply.github.com> Date: Mon, 25 Apr 2022 11:13:16 +0800 Subject: [PATCH 63/78] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1983bab80..a47f702b8 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![Grasscutter](https://socialify.git.ci/Grasscutters/Grasscutter/image?description=1&font=KoHo&forks=1&issues=1&language=1&name=1&owner=1&pattern=Circuit%20Board&pulls=1&stargazers=1&theme=Light) +![Grasscutter](https://socialify.git.ci/Grasscutters/Grasscutter/image?description=1&forks=1&issues=1&language=1&logo=https%3A%2F%2Fs2.loli.net%2F2022%2F04%2F25%2FxOiJn7lCdcT5Mw1.png&name=1&owner=1&pulls=1&stargazers=1&theme=Light)
Documention GitHub release (latest by date) GitHub GitHub last commit GitHub Workflow Status
Discord - Grasscutter
@@ -18,7 +18,7 @@ ## Quick setup guide -**Note:** for support please join our [Discord]() +**Note:** for support please join our [Discord](https://discord.gg/T5vZU6UyeG) ### Requirements From bbda2e96db053502eedd4a01c86c61e3c8e19f57 Mon Sep 17 00:00:00 2001 From: Wansn <2632324507@qq.com> Date: Mon, 25 Apr 2022 12:31:43 +0800 Subject: [PATCH 64/78] Fix outdated jdk versions in some places --- README.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index a47f702b8..8f7420f06 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,9 @@ ![Grasscutter](https://socialify.git.ci/Grasscutters/Grasscutter/image?description=1&forks=1&issues=1&language=1&logo=https%3A%2F%2Fs2.loli.net%2F2022%2F04%2F25%2FxOiJn7lCdcT5Mw1.png&name=1&owner=1&pulls=1&stargazers=1&theme=Light) -
Documention GitHub release (latest by date) GitHub GitHub last commit GitHub Workflow Status
+
Documention GitHub release (latest by date) GitHub GitHub Workflow Status
-
Discord - Grasscutter
+
Discord - Grasscutter
+ +EN | [中文](README_zh-CN.md) **Attention:** We always welcome contributors to the project. Before adding your contribution, please carefully read our [Code of Conduct](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md). @@ -22,7 +24,7 @@ ### Requirements -* Java - SE ([mirror link](https://github.com/adoptium/temurin16-binaries/releases/tag/jdk-16.0.2+7) since Oracle required an account to download old builds) +* Java SE - 16 ([mirror link](https://github.com/adoptium/temurin16-binaries/releases/tag/jdk-16.0.2+7) since Oracle required an account to download old builds) **Note:** If you just want to **run it**, then **jre** is fine @@ -73,7 +75,7 @@ Grasscutter uses Gradle to handle dependencies & building. **Requirements:** -- Java Development Kits - 8u202 +- Java SE Development Kits - 16 - Git ##### Windows @@ -108,7 +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 | | 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 yellow rarity ones from your inventory. | clearart | -| clearweapons | clearweapons | player.clearweapons | Client only | Deletes all unequipped and unlocked weapons, including yellow rarity ones from your inventory. | clearwpns | +| clearweapons | clearweapons | player.clearweapons | Client only | Deletes all unequipped and unlocked weapons, including yellow rarity ones from your inventory. | clearwp | | drop | drop [amount] | server.drop | Client only | Drops an item around you. | `d` `dropitem` | | give | give [player] [amount] [level] | player.give | Both side | Gives item(s) to you or the specified player. | `g` `item` `giveitem` | | givechar | givechar [level] | player.givechar | Both side | Gives the player a specified character. | givec | @@ -124,7 +126,7 @@ There is a dummy user named "Server" in every player's friends list that you can | resetconst | resetconst [all] | player.resetconstellation | Client only | Resets the constellation level on your current active character, will need to relog after using the command to see any changes. | resetconstellation | | restart | | | Both side | Restarts the current session | | | say | say | server.sendmessage | Both side | Sends a message to a player as the server | `sendservmsg` `sendservermessage` `sendmessage` | -| setfetterlevel | setfetterlevel | player.setfetterlevel | Client only | Sets your fetter level for your current active character | setfetterlvl | +| setfetterlevel | setfetterlevel | player.setfetterlevel | Client only | Sets your fetter level for your current active character | `setfetterlvl` `setfriendship` | | setstats | setstats | player.setstats | Client only | Set fight property for your current active character | stats | | setworldlevel | setworldlevel | player.setworldlevel | Client only | Sets your world level (Relog to see proper effects) | setworldlvl | | spawn | spanw [level] [amount] | server.spawn | Client only | Spawns an entity near you | | @@ -140,8 +142,7 @@ character falling from a very high destination, exact location that you marked. # Quick Troubleshooting -* If compiling wasn't successful, please check your JDK installation (must be JDK 8 and validated JDK's bin PATH - variable) +* If compiling wasn't successful, please check your JDK installation (must be JDK 16 and validated JDK's bin PATH variable) * My client doesn't connect, doesn't login, 4206, etc... - Mostly your proxy daemon setup is *the issue*, if using Fiddler make sure it running on another port except 8888 * Startup sequence: Mongodb > Grasscutter > Proxy daemon (mitmdump, fiddler, etc.) > Client From f7d9be5c0557f72ec4f79fc072fb79cd6c0b3ea4 Mon Sep 17 00:00:00 2001 From: Wansn <2632324507@qq.com> Date: Mon, 25 Apr 2022 12:36:29 +0800 Subject: [PATCH 65/78] Fix outdated jdk versions in some places --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 8f7420f06..5bcec2715 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ ![Grasscutter](https://socialify.git.ci/Grasscutters/Grasscutter/image?description=1&forks=1&issues=1&language=1&logo=https%3A%2F%2Fs2.loli.net%2F2022%2F04%2F25%2FxOiJn7lCdcT5Mw1.png&name=1&owner=1&pulls=1&stargazers=1&theme=Light) -
Documention GitHub release (latest by date) GitHub GitHub Workflow Status
+
Documention GitHub release (latest by date) GitHub GitHub last commit GitHub Workflow Status
Discord - Grasscutter
@@ -110,7 +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 | | 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 yellow rarity ones from your inventory. | clearart | -| clearweapons | clearweapons | player.clearweapons | Client only | Deletes all unequipped and unlocked weapons, including yellow rarity ones from your inventory. | clearwp | +| clearweapons | clearweapons | player.clearweapons | Client only | Deletes all unequipped and unlocked weapons, including yellow rarity ones from your inventory. | clearwpns | | drop | drop [amount] | server.drop | Client only | Drops an item around you. | `d` `dropitem` | | give | give [player] [amount] [level] | player.give | Both side | Gives item(s) to you or the specified player. | `g` `item` `giveitem` | | givechar | givechar [level] | player.givechar | Both side | Gives the player a specified character. | givec | @@ -126,7 +126,7 @@ There is a dummy user named "Server" in every player's friends list that you can | resetconst | resetconst [all] | player.resetconstellation | Client only | Resets the constellation level on your current active character, will need to relog after using the command to see any changes. | resetconstellation | | restart | | | Both side | Restarts the current session | | | say | say | server.sendmessage | Both side | Sends a message to a player as the server | `sendservmsg` `sendservermessage` `sendmessage` | -| setfetterlevel | setfetterlevel | player.setfetterlevel | Client only | Sets your fetter level for your current active character | `setfetterlvl` `setfriendship` | +| setfetterlevel | setfetterlevel | player.setfetterlevel | Client only | Sets your fetter level for your current active character | setfetterlvl | | setstats | setstats | player.setstats | Client only | Set fight property for your current active character | stats | | setworldlevel | setworldlevel | player.setworldlevel | Client only | Sets your world level (Relog to see proper effects) | setworldlvl | | spawn | spanw [level] [amount] | server.spawn | Client only | Spawns an entity near you | | @@ -142,7 +142,7 @@ character falling from a very high destination, exact location that you marked. # Quick Troubleshooting -* If compiling wasn't successful, please check your JDK installation (must be JDK 16 and validated JDK's bin PATH variable) +* If compiling wasn't successful, please check your JDK installation (JDK 16 and validated JDK's bin PATH variable) * My client doesn't connect, doesn't login, 4206, etc... - Mostly your proxy daemon setup is *the issue*, if using Fiddler make sure it running on another port except 8888 * Startup sequence: Mongodb > Grasscutter > Proxy daemon (mitmdump, fiddler, etc.) > Client From 0a497b362608ca83bceaf1a59508f78dae6651cf Mon Sep 17 00:00:00 2001 From: Wansn <2632324507@qq.com> Date: Mon, 25 Apr 2022 12:38:02 +0800 Subject: [PATCH 66/78] Create README_zh-CN.md --- README_zh-CN.md | 147 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 README_zh-CN.md diff --git a/README_zh-CN.md b/README_zh-CN.md new file mode 100644 index 000000000..9d3878ee4 --- /dev/null +++ b/README_zh-CN.md @@ -0,0 +1,147 @@ +![Grasscutter](https://socialify.git.ci/Grasscutters/Grasscutter/image?description=1&forks=1&issues=1&language=1&logo=https%3A%2F%2Fs2.loli.net%2F2022%2F04%2F25%2FxOiJn7lCdcT5Mw1.png&name=1&owner=1&pulls=1&stargazers=1&theme=Light) +
Documention GitHub release (latest by date) GitHub GitHub last commit GitHub Workflow Status
+ +
Discord - Grasscutter
+ +[EN](README.md) | 中文 + +**注意:** 我们一直欢迎您成为该项目的贡献者。在添加您的代码之前,请仔细阅读我们的 [代码规范](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md). + +## 当前特性 + +* 登录 +* 战斗 +* 好友列表 +* 传送系统 +* 祈愿系统 +* 从控制台生成魔物 +* 多人游戏 *部分* 可用 +* 物品栏相关 (接收物品/角色, 升级角色/武器等) + +## 快速设置指南 + +**附:** 加入我们的 [Discord](https://discord.gg/T5vZU6UyeG) 获取更多帮助! + +### 环境需求 + +* Java SE - 16 ([镜像](https://github.com/adoptium/temurin16-binaries/releases/tag/jdk-16.0.2+7) 若您没有Oracle账户) + + **注:** 如果您仅仅想要简单地**运行服务端**, 那么使用 **jre** 便足够了 + +* MongoDB (推荐 4.0+) + +* Proxy daemon: mitmproxy (推荐使用mitmdump), Fiddler Classic, 等 + +### 运行 + +**注:** 如果您从旧版本升级到新版本,最好删除 `config.json` 并启动服务端jar来重新生成它 + +1. 获取 `grasscutter.jar` + - 从 [actions](https://nightly.link/Grasscutters/Grasscutter/workflows/build/stable/Grasscutter.zip) 中下载 + - [自行构建](#构建) +2. 在**grasscutter.jar** 所在目录中创建 `resources` 文件夹并将 `BinOutput` 和 `ExcelBinOutput` 放入其中 *(查看 [wiki](https://github.com/Grasscutters/Grasscutter/wiki) 了解更多)* +3. 通过命令 `java -jar grasscutter.jar` 来运行Grasscutter. **在此之前请确认MongoDB服务运行正常** + +### 连接 + +½. 在服务器控制台中 [创建账户](#命令列表). + +1. 重定向流量: (选其一) + - mitmdump: `mitmdump -s proxy.py -k` + + 信任 CA 证书: + + ​ **注:** mitmproxy的CA证书通常存放在 `% USERPROFILE%\ .mitmproxy`, 或者你也可以从`http://mitm.it` 中下载它 + + ​ 双击来[安装根证书](https://docs.microsoft.com/en-us/skype-sdk/sdn/articles/installing-the-trusted-root-certificate#installing-a-trusted-root-certificate) 或者.. + + - 使用命令行 + + ```shell + certutil -addstore root %USERPROFILE%\.mitmproxy\mitmproxy-ca-cert.cer + ``` + + - Fiddler Classic: 运行Fiddler Classic, 在设置中开启 `解密https通信` 并将端口切换到除`8888` 以外的任意端口 (工具 -> 选项 -> 连接) 并加载 [此脚本](https://github.lunatic.moe/fiddlerscript). + + - [Hosts文件](https://github.com/Grasscutters/Grasscutter/wiki/Running#traffic-route-map) + +2. 设置代理为 `127.0.0.1:8080` 或其它你所设定的端口 + +**你也可以简单地运行 `start.cmd` 来全自动启动服务端并设置代理** + +### 构建 + +Grasscutter 使用 Gradle 来处理依赖及构建. + +**依赖:** + +- Java SE Development Kits - 16 +- Git + +##### Windows + +```shell +git clone https://github.com/Grasscutters/Grasscutter.git +cd Grasscutter +.\gradlew.bat # Setting up environments +.\gradlew jar # Compile +``` + +##### Linux + +```bash +git clone https://github.com/Grasscutters/Grasscutter.git +cd Grasscutter +chmod +x gradlew +./gradlew jar # Compile +``` + +你可以在项目根目录中找到`grasscutter.jar` + +## 命令列表 + +你可能需要在终端中运行 `java -jar grasscutter.jar -handbook` 它将会创建一个 `GM Handbook.txt` 以方便您查阅物品ID等 + +在每个玩家的朋友列表中都有一个名为“服务器”的虚拟用户,你可以通过发送消息来使用命令。命令也适用于其他聊天室,例如私人/团队聊天。 +要在游戏中使用命令,需要添加 `/` 或 `!` 前缀,如 `/pos` + +| 命令 | 用法 | 权限节点 | 可用性 | 注释 | 别名 | +| -------------- | -------------------------------------------- | ------------------------- | -------- | ------------------------------------------ | ----------------------------------------------- | +| account | account <用户名> [uid] | | 仅服务端 | 通过指定用户名和uid增删账户 | | +| broadcast | broadcast <消息内容> | server.broadcast | 均可使用 | 给所有玩家发送公告 | b | +| changescene | changescene <场景ID> | player.changescene | 仅客户端 | 切换到指定场景 | scene | +| clearartifacts | clearartifacts | player.clearartifacts | 仅客户端 | 删除所有未装备及未解锁的圣遗物,包括五星 | clearart | +| clearweapons | clearweapons | player.clearweapons | 仅客户端 | 删除所有未装备及未解锁的武器,包括五星 | clearwp | +| drop | drop <物品ID\|物品名称> [数量] | server.drop | 仅客户端 | 在指定玩家周围掉落指定物品 | `d` `dropitem` | +| give | give [uid] <物品ID\|物品名称> [数量] [等级] | | | 给予指定玩家一定数量及等级的物品 | `g` `item` `giveitem` | +| givechar | givechar <角色ID> [等级] | player.givechar | 均可使用 | 给予指定玩家对应角色 | givec | +| godmode | godmode [uid] | player.godmode | 仅客户端 | 保护你不受到任何伤害(依然会被击退) | | +| heal | heal | player.heal | 仅客户端 | 治疗队伍中所有角色 | h | +| help | help [命令] | | 均可使用 | 显示帮助或展示指定命令的帮助 | | +| kick | kick | server.kick | 均可使用 | 从服务器中踢出指定玩家 (WIP) | k | +| killall | killall [uid] [场景ID] | server.killall | 均可使用 | 杀死指定玩家世界中所在或指定场景的全部生物 | | +| list | list | | 均可使用 | 列出在线玩家 | | +| permission | permission <用户名> <权限节点> | * | 均可使用 | 添加或移除玩家的权限 | | +| position | position | | 仅客户端 | 获取当前坐标 | pos | +| reload | reload | server.reload | 均可使用 | 重载服务器配置 | | +| resetconst | resetconst [all] | player.resetconstellation | 仅客户端 | 重置当前角色的命座,重新登录即可生效 | resetconstellation | +| restart | restart | | 均可使用 | 重启服务端 | | +| say | say <消息> | server.sendmessage | 均可使用 | 作为服务器发送消息给玩家 | `sendservmsg` `sendservermessage` `sendmessage` | +| setfetterlevel | setfetterlevel <好感等级> | player.setfetterlevel | 仅客户端 | 设置当前角色的好感等级 | `setfetterlvl` `setfriendship` | +| setstats | setstats <属性> <树脂> | player.setstats | 仅客户端 | 直接修改当前角色的面板 | stats | +| setworldlevel | setworldlevel <世界等级> | player.setworldlevel | 仅客户端 | 设置世界等级(重新登陆即可生效) | setworldlvl | +| spawn | spanw <实体ID\|实体名称> [等级] [数量] | server.spawn | 仅客户端 | 在你周围生成实体 | | +| stop | stop | server.stop | 均可使用 | 停止服务器 | | +| talent | talent <天赋ID> <等级> | player.settalent | 仅客户端 | 设置当前角色的天赋等级 | | +| teleport | teleport | player.teleport | 仅客户端 | 传送玩家到指定坐标 | tp | +| weather | weather <天气ID> <气候ID> | player.weather | 仅客户端 | 改变天气 | w | + +### 额外功能 + +当你想传送到某个地点, 只需要在地图中创建标记, 关闭地图后即可到达目标地点上空 + +# 快速排除问题 + +* 如果编译未能成功,请检查您的jdk安装 (JDK 16并确认jdk处于环境变量`PATH`中 +* 我的客户端无法登录/连接, 4206, 其它... - 大部分情况下这是因为您的代理存在问题.如果使用Fiddler请确认Fiddler监听端口不是`8888` +* 启动顺序: MongoDB > Grasscutter > 代理程序 (mitmdump, fiddler等.) > 客户端 From b6801cf4279743ec78912d4ccb8cb6c447610815 Mon Sep 17 00:00:00 2001 From: Wansn <2632324507@qq.com> Date: Mon, 25 Apr 2022 12:39:15 +0800 Subject: [PATCH 67/78] Update README_zh-CN.md --- README_zh-CN.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README_zh-CN.md b/README_zh-CN.md index 9d3878ee4..2bf82aa97 100644 --- a/README_zh-CN.md +++ b/README_zh-CN.md @@ -24,7 +24,7 @@ ### 环境需求 -* Java SE - 16 ([镜像](https://github.com/adoptium/temurin16-binaries/releases/tag/jdk-16.0.2+7) 若您没有Oracle账户) +* Java SE - 16 (当您没有Oracle账户,可以使用[镜像](https://github.com/adoptium/temurin16-binaries/releases/tag/jdk-16.0.2+7)) **注:** 如果您仅仅想要简单地**运行服务端**, 那么使用 **jre** 便足够了 From 122568ad2767a08fc81bf3550c425462343b6528 Mon Sep 17 00:00:00 2001 From: Asnxthaony Date: Mon, 25 Apr 2022 13:46:16 +0800 Subject: [PATCH 68/78] fix SetPlayerBirthdayRsp --- .../recv/HandlerSetPlayerBirthdayReq.java | 45 +++++++++---------- .../send/PacketSetPlayerBirthdayRsp.java | 4 +- 2 files changed, 21 insertions(+), 28 deletions(-) diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetPlayerBirthdayReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetPlayerBirthdayReq.java index 0edb08f73..5bfa9f8d5 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetPlayerBirthdayReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetPlayerBirthdayReq.java @@ -1,38 +1,33 @@ 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.SetPlayerBirthdayReqOuterClass.SetPlayerBirthdayReq; +import emu.grasscutter.net.proto.SocialDetailOuterClass.SocialDetail; import emu.grasscutter.server.game.GameSession; import emu.grasscutter.server.packet.send.PacketGetPlayerSocialDetailRsp; import emu.grasscutter.server.packet.send.PacketSetPlayerBirthdayRsp; -import emu.grasscutter.net.packet.Opcodes; -import emu.grasscutter.net.packet.PacketOpcodes; -import emu.grasscutter.net.packet.PacketHandler; - -import emu.grasscutter.net.proto.SocialDetailOuterClass.SocialDetail; -import emu.grasscutter.net.proto.SetPlayerBirthdayReqOuterClass.SetPlayerBirthdayReq; - -import com.google.gson.Gson; - @Opcodes(PacketOpcodes.SetPlayerBirthdayReq) public class HandlerSetPlayerBirthdayReq extends PacketHandler { - @Override - public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { - SetPlayerBirthdayReq req = SetPlayerBirthdayReq.parseFrom(payload); + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + SetPlayerBirthdayReq req = SetPlayerBirthdayReq.parseFrom(payload); - if(req.getBirth() != null && req.getBirth().getDay() > 0 && req.getBirth().getMonth() > 0) - { - int day = req.getBirth().getDay(); - int month = req.getBirth().getMonth(); + if (req.getBirthday().getDay() > 0 && req.getBirthday().getMonth() > 0) { + int day = req.getBirthday().getDay(); + int month = req.getBirthday().getMonth(); - // Update birthday value - session.getPlayer().setBirthday(day, month); + // Update birthday value + session.getPlayer().setBirthday(day, month); - // Save birthday month and day - session.getPlayer().save(); - SocialDetail.Builder detail = session.getPlayer().getSocialDetail(); + // Save birthday month and day + session.getPlayer().save(); + SocialDetail.Builder detail = session.getPlayer().getSocialDetail(); - session.send(new PacketSetPlayerBirthdayRsp(session.getPlayer())); - session.send(new PacketGetPlayerSocialDetailRsp(detail)); - } - } + session.send(new PacketSetPlayerBirthdayRsp(session.getPlayer())); + session.send(new PacketGetPlayerSocialDetailRsp(detail)); + } + } } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketSetPlayerBirthdayRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketSetPlayerBirthdayRsp.java index 9b73b6b13..135d22f2f 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketSetPlayerBirthdayRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketSetPlayerBirthdayRsp.java @@ -1,18 +1,16 @@ package emu.grasscutter.server.packet.send; -import emu.grasscutter.Grasscutter; import emu.grasscutter.game.GenshinPlayer; import emu.grasscutter.net.packet.GenshinPacket; import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.proto.SetPlayerBirthdayRspOuterClass.SetPlayerBirthdayRsp; -import emu.grasscutter.net.proto.BirthdayOuterClass.Birthday; public class PacketSetPlayerBirthdayRsp extends GenshinPacket { public PacketSetPlayerBirthdayRsp(GenshinPlayer player) { super(PacketOpcodes.SetPlayerBirthdayRsp); SetPlayerBirthdayRsp proto = SetPlayerBirthdayRsp.newBuilder() - .setBirth(player.getBirthday().toProto()) + .setBirthday(player.getBirthday().toProto()) .build(); this.setData(proto); From 4e5d72a99a30c0bd5e09607cedfc5566a216c999 Mon Sep 17 00:00:00 2001 From: Asnxthaony Date: Mon, 25 Apr 2022 14:51:34 +0800 Subject: [PATCH 69/78] sanity check --- .../emu/grasscutter/game/GenshinPlayer.java | 279 ++++++++---------- .../recv/HandlerSetPlayerBirthdayReq.java | 64 +++- .../send/PacketSetPlayerBirthdayRsp.java | 25 +- 3 files changed, 189 insertions(+), 179 deletions(-) diff --git a/src/main/java/emu/grasscutter/game/GenshinPlayer.java b/src/main/java/emu/grasscutter/game/GenshinPlayer.java index 271a6f014..3c05e6ef5 100644 --- a/src/main/java/emu/grasscutter/game/GenshinPlayer.java +++ b/src/main/java/emu/grasscutter/game/GenshinPlayer.java @@ -1,7 +1,5 @@ package emu.grasscutter.game; -import java.util.*; - import dev.morphia.annotations.*; import emu.grasscutter.GenshinConstants; import emu.grasscutter.Grasscutter; @@ -23,7 +21,6 @@ import emu.grasscutter.game.props.ActionReason; import emu.grasscutter.game.props.PlayerProperty; import emu.grasscutter.net.packet.GenshinPacket; import emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry; -import emu.grasscutter.net.proto.BirthdayOuterClass.Birthday; import emu.grasscutter.net.proto.CombatInvokeEntryOuterClass.CombatInvokeEntry; import emu.grasscutter.net.proto.HeadImageOuterClass.HeadImage; import emu.grasscutter.net.proto.InteractTypeOuterClass.InteractType; @@ -35,39 +32,18 @@ import emu.grasscutter.net.proto.SocialDetailOuterClass.SocialDetail; import emu.grasscutter.net.proto.WorldPlayerLocationInfoOuterClass.WorldPlayerLocationInfo; import emu.grasscutter.server.game.GameServer; import emu.grasscutter.server.game.GameSession; -import emu.grasscutter.server.packet.send.PacketAbilityInvocationsNotify; -import emu.grasscutter.server.packet.send.PacketAvatarAddNotify; -import emu.grasscutter.server.packet.send.PacketAvatarDataNotify; -import emu.grasscutter.server.packet.send.PacketAvatarGainCostumeNotify; -import emu.grasscutter.server.packet.send.PacketAvatarGainFlycloakNotify; -import emu.grasscutter.server.packet.send.PacketClientAbilityInitFinishNotify; -import emu.grasscutter.server.packet.send.PacketCombatInvocationsNotify; -import emu.grasscutter.server.packet.send.PacketGadgetInteractRsp; -import emu.grasscutter.server.packet.send.PacketItemAddHintNotify; -import emu.grasscutter.server.packet.send.PacketOpenStateUpdateNotify; -import emu.grasscutter.server.packet.send.PacketPlayerApplyEnterMpResultNotify; -import emu.grasscutter.server.packet.send.PacketPlayerDataNotify; -import emu.grasscutter.server.packet.send.PacketPlayerEnterSceneNotify; -import emu.grasscutter.server.packet.send.PacketPlayerPropNotify; -import emu.grasscutter.server.packet.send.PacketPlayerStoreNotify; -import emu.grasscutter.server.packet.send.PacketPrivateChatNotify; -import emu.grasscutter.server.packet.send.PacketScenePlayerLocationNotify; -import emu.grasscutter.server.packet.send.PacketPlayerLevelRewardUpdateNotify; -import emu.grasscutter.server.packet.send.PacketSetNameCardRsp; -import emu.grasscutter.server.packet.send.PacketStoreWeightLimitNotify; -import emu.grasscutter.server.packet.send.PacketUnlockNameCardNotify; -import emu.grasscutter.server.packet.send.PacketWorldPlayerLocationNotify; -import emu.grasscutter.server.packet.send.PacketWorldPlayerRTTNotify; +import emu.grasscutter.server.packet.send.*; import emu.grasscutter.utils.Position; - import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import java.util.*; + @Entity(value = "players", useDiscriminator = false) public class GenshinPlayer { @Id private int id; @Indexed(options = @IndexOptions(unique = true)) private String accountId; - + @Transient private Account account; private String nickname; private String signature; @@ -76,12 +52,12 @@ public class GenshinPlayer { private Position pos; private Position rotation; private PlayerBirthday birthday; - + private Map properties; private Set nameCardList; private Set flyCloakList; private Set costumeList; - + @Transient private long nextGuid = 0; @Transient private int peerId; @Transient private World world; @@ -90,7 +66,7 @@ public class GenshinPlayer { @Transient private AvatarStorage avatars; @Transient private Inventory inventory; @Transient private FriendsList friendsList; - + private TeamManager teamManager; private PlayerGachaInfo gachaInfo; private PlayerProfile playerProfile; @@ -98,25 +74,26 @@ public class GenshinPlayer { private boolean showAvatar; private ArrayList shownAvatars; private Set rewardedLevels; - + private int sceneId; private int regionId; private int mainCharacterId; private boolean godmode; - + @Transient private boolean paused; @Transient private int enterSceneToken; @Transient private SceneLoadState sceneState; @Transient private boolean hasSentAvatarDataNotify; @Transient private long nextSendPlayerLocTime = 0; - + @Transient private final Int2ObjectMap coopRequests; @Transient private final InvokeHandler combatInvokeHandler; @Transient private final InvokeHandler abilityInvokeHandler; @Transient private final InvokeHandler clientAbilityInitFinishHandler; - - @Deprecated @SuppressWarnings({ "rawtypes", "unchecked" }) // Morphia only! - public GenshinPlayer() { + + @Deprecated + @SuppressWarnings({"rawtypes", "unchecked"}) // Morphia only! + public GenshinPlayer() { this.inventory = new Inventory(this); this.avatars = new AvatarStorage(this); this.friendsList = new FriendsList(this); @@ -129,16 +106,16 @@ public class GenshinPlayer { } this.properties.put(prop.getId(), 0); } - + this.gachaInfo = new PlayerGachaInfo(); this.nameCardList = new HashSet<>(); this.flyCloakList = new HashSet<>(); this.costumeList = new HashSet<>(); - + this.setSceneId(3); this.setRegionId(1); this.sceneState = SceneLoadState.NONE; - + this.coopRequests = new Int2ObjectOpenHashMap<>(); this.combatInvokeHandler = new InvokeHandler(PacketCombatInvocationsNotify.class); this.abilityInvokeHandler = new InvokeHandler(PacketAbilityInvocationsNotify.class); @@ -147,7 +124,7 @@ public class GenshinPlayer { this.birthday = new PlayerBirthday(); this.rewardedLevels = new HashSet<>(); } - + // On player creation public GenshinPlayer(GameSession session) { this(); @@ -179,7 +156,7 @@ public class GenshinPlayer { public void setUid(int id) { this.id = id; } - + public long getNextGenshinGuid() { long nextId = ++this.nextGuid; return ((long) this.getUid() << 32) + nextId; @@ -201,23 +178,23 @@ public class GenshinPlayer { public void setSession(GameSession session) { this.session = session; } - + public boolean isOnline() { return this.getSession() != null && this.getSession().isActive(); } - + public GameServer getServer() { return this.getSession().getServer(); } - + public synchronized World getWorld() { return this.world; } - + public synchronized void setWorld(World world) { this.world = world; } - + public GenshinScene getScene() { return scene; } @@ -238,7 +215,7 @@ public class GenshinPlayer { this.nickname = nickName; this.updateProfile(); } - + public int getHeadImage() { return headImage; } @@ -260,71 +237,71 @@ public class GenshinPlayer { public Position getPos() { return pos; } - + public Position getRotation() { return rotation; } - + public int getLevel() { return this.getProperty(PlayerProperty.PROP_PLAYER_LEVEL); } - + public int getExp() { return this.getProperty(PlayerProperty.PROP_PLAYER_EXP); } - + public int getWorldLevel() { return this.getProperty(PlayerProperty.PROP_PLAYER_WORLD_LEVEL); } - + public int getPrimogems() { return this.getProperty(PlayerProperty.PROP_PLAYER_HCOIN); } - + public void setPrimogems(int primogem) { this.setProperty(PlayerProperty.PROP_PLAYER_HCOIN, primogem); this.sendPacket(new PacketPlayerPropNotify(this, PlayerProperty.PROP_PLAYER_HCOIN)); } - + public int getMora() { return this.getProperty(PlayerProperty.PROP_PLAYER_SCOIN); } - + public void setMora(int mora) { this.setProperty(PlayerProperty.PROP_PLAYER_SCOIN, mora); this.sendPacket(new PacketPlayerPropNotify(this, PlayerProperty.PROP_PLAYER_SCOIN)); } - + private int getExpRequired(int level) { PlayerLevelData levelData = GenshinData.getPlayerLevelDataMap().get(level); - return levelData != null ? levelData.getExp() : 0; + return levelData != null ? levelData.getExp() : 0; } - + private float getExpModifier() { return Grasscutter.getConfig().getGameServerOptions().getGameRates().ADVENTURE_EXP_RATE; } - + // Affected by exp rate public void earnExp(int exp) { addExpDirectly((int) (exp * getExpModifier())); } - + // Directly give player exp public void addExpDirectly(int gain) { boolean hasLeveledUp = false; int level = getLevel(); int exp = getExp(); int reqExp = getExpRequired(level); - + exp += gain; - + while (exp >= reqExp && reqExp > 0) { exp -= reqExp; level += 1; reqExp = getExpRequired(level); hasLeveledUp = true; } - + if (hasLeveledUp) { // Set level property this.setProperty(PlayerProperty.PROP_PLAYER_LEVEL, level); @@ -333,18 +310,18 @@ public class GenshinPlayer { // Update player with packet this.sendPacket(new PacketPlayerPropNotify(this, PlayerProperty.PROP_PLAYER_LEVEL)); } - + // Set exp this.setProperty(PlayerProperty.PROP_PLAYER_EXP, exp); - + // Update player with packet this.sendPacket(new PacketPlayerPropNotify(this, PlayerProperty.PROP_PLAYER_EXP)); } - + private void updateProfile() { getProfile().syncWithCharacter(this); } - + public boolean isFirstLoginEnterScene() { return !this.hasSentAvatarDataNotify; } @@ -367,11 +344,11 @@ public class GenshinPlayer { public Map getProperties() { return properties; } - + public void setProperty(PlayerProperty prop, int value) { getProperties().put(prop.getId(), value); } - + public int getProperty(PlayerProperty prop) { return getProperties().get(prop.getId()); } @@ -379,11 +356,11 @@ public class GenshinPlayer { public Set getFlyCloakList() { return flyCloakList; } - + public Set getCostumeList() { return costumeList; } - + public Set getNameCardList() { return this.nameCardList; } @@ -391,7 +368,7 @@ public class GenshinPlayer { public MpSettingType getMpSetting() { return mpSetting; } - + public synchronized Int2ObjectMap getCoopRequests() { return coopRequests; } @@ -399,7 +376,7 @@ public class GenshinPlayer { public InvokeHandler getCombatInvokeHandler() { return this.combatInvokeHandler; } - + public InvokeHandler getAbilityInvokeHandler() { return this.abilityInvokeHandler; } @@ -415,11 +392,11 @@ public class GenshinPlayer { public AvatarStorage getAvatars() { return avatars; } - + public Inventory getInventory() { return inventory; } - + public FriendsList getFriendsList() { return this.friendsList; } @@ -472,7 +449,7 @@ public class GenshinPlayer { public void setPaused(boolean newPauseState) { boolean oldPauseState = this.paused; this.paused = newPauseState; - + if (newPauseState && !oldPauseState) { this.onPause(); } else if (oldPauseState && !newPauseState) { @@ -487,7 +464,7 @@ public class GenshinPlayer { public void setSceneLoadState(SceneLoadState sceneState) { this.sceneState = sceneState; } - + public boolean isInMultiplayer() { return this.getWorld() != null && this.getWorld().isMultiplayer(); } @@ -507,7 +484,7 @@ public class GenshinPlayer { public void setRegionId(int regionId) { this.regionId = regionId; } - + public boolean inGodmode() { return godmode; } @@ -526,11 +503,11 @@ public class GenshinPlayer { public void addAvatar(GenshinAvatar avatar) { boolean result = getAvatars().addAvatar(avatar); - + if (result) { // Add starting weapon getAvatars().addStartingWeapon(avatar); - + // Try adding to team if possible //List currentTeam = this.getTeamManager().getCurrentTeam(); boolean addedToTeam = false; @@ -540,7 +517,7 @@ public class GenshinPlayer { addedToTeam = currentTeam } */ - + // Done if (hasSentAvatarDataNotify()) { // Recalc stats @@ -552,55 +529,56 @@ public class GenshinPlayer { // Failed adding avatar } } - + public void addFlycloak(int flycloakId) { this.getFlyCloakList().add(flycloakId); this.sendPacket(new PacketAvatarGainFlycloakNotify(flycloakId)); } - + public void addCostume(int costumeId) { this.getCostumeList().add(costumeId); this.sendPacket(new PacketAvatarGainCostumeNotify(costumeId)); } - + public void addNameCard(int nameCardId) { this.getNameCardList().add(nameCardId); this.sendPacket(new PacketUnlockNameCardNotify(nameCardId)); } - + public void setNameCard(int nameCardId) { if (!this.getNameCardList().contains(nameCardId)) { return; } - + this.setNameCardId(nameCardId); - + this.sendPacket(new PacketSetNameCardRsp(nameCardId)); } - + public void dropMessage(Object message) { this.sendPacket(new PacketPrivateChatNotify(GenshinConstants.SERVER_CONSOLE_UID, getUid(), message.toString())); } /** * Sends a message to another player. - * @param sender The sender of the message. + * + * @param sender The sender of the message. * @param message The message to send. */ public void sendMessage(GenshinPlayer sender, Object message) { this.sendPacket(new PacketPrivateChatNotify(sender.getUid(), this.getUid(), message.toString())); } - + public void interactWith(int gadgetEntityId) { GenshinEntity entity = getScene().getEntityById(gadgetEntityId); - + if (entity == null) { return; } - + // Delete entity.getScene().removeEntity(entity); - + // Handle if (entity instanceof EntityItem) { // Pick item @@ -613,44 +591,44 @@ public class GenshinPlayer { this.sendPacket(new PacketItemAddHintNotify(item, ActionReason.SubfieldDrop)); } } - + return; } - + public void onPause() { - + } - + public void onUnpause() { - + } - + public void sendPacket(GenshinPacket packet) { if (this.hasSentAvatarDataNotify) { this.getSession().send(packet); } } - + public OnlinePlayerInfo getOnlinePlayerInfo() { OnlinePlayerInfo.Builder onlineInfo = OnlinePlayerInfo.newBuilder() - .setUid(this.getUid()) - .setNickname(this.getNickname()) - .setPlayerLevel(this.getLevel()) - .setMpSettingType(this.getMpSetting()) - .setNameCardId(this.getNameCardId()) - .setSignature(this.getSignature()) - .setAvatar(HeadImage.newBuilder().setAvatarId(this.getHeadImage())); - + .setUid(this.getUid()) + .setNickname(this.getNickname()) + .setPlayerLevel(this.getLevel()) + .setMpSettingType(this.getMpSetting()) + .setNameCardId(this.getNameCardId()) + .setSignature(this.getSignature()) + .setAvatar(HeadImage.newBuilder().setAvatarId(this.getHeadImage())); + if (this.getWorld() != null) { onlineInfo.setCurPlayerNumInWorld(this.getWorld().getPlayers().indexOf(this) + 1); } else { onlineInfo.setCurPlayerNumInWorld(1); } - + return onlineInfo.build(); } - public PlayerBirthday getBirthday(){ + public PlayerBirthday getBirthday() { return this.birthday; } @@ -659,6 +637,10 @@ public class GenshinPlayer { this.updateProfile(); } + public boolean hasBirthday() { + return this.birthday.getDay() > 0; + } + public Set getRewardedLevels() { return rewardedLevels; } @@ -668,36 +650,18 @@ public class GenshinPlayer { } public SocialDetail.Builder getSocialDetail() { - SocialDetail.Builder social = SocialDetail.newBuilder() - .setUid(this.getUid()) - .setAvatar(HeadImage.newBuilder().setAvatarId(this.getHeadImage())) - .setNickname(this.getNickname()) - .setSignature(this.getSignature()) - .setLevel(this.getLevel()) - .setBirthday(this.getBirthday().getFilledProtoWhenNotEmpty()) - .setWorldLevel(this.getWorldLevel()) - .setUnk1(1) - .setUnk3(1) - .setNameCardId(this.getNameCardId()) - .setFinishAchievementNum(0); + SocialDetail.Builder social = SocialDetail.newBuilder().setUid(this.getUid()).setAvatar(HeadImage.newBuilder().setAvatarId(this.getHeadImage())).setNickname(this.getNickname()).setSignature(this.getSignature()).setLevel(this.getLevel()).setBirthday(this.getBirthday().getFilledProtoWhenNotEmpty()).setWorldLevel(this.getWorldLevel()).setUnk1(1).setUnk3(1).setNameCardId(this.getNameCardId()).setFinishAchievementNum(0); return social; } - + public WorldPlayerLocationInfo getWorldPlayerLocationInfo() { - return WorldPlayerLocationInfo.newBuilder() - .setSceneId(this.getSceneId()) - .setPlayerLoc(this.getPlayerLocationInfo()) - .build(); + return WorldPlayerLocationInfo.newBuilder().setSceneId(this.getSceneId()).setPlayerLoc(this.getPlayerLocationInfo()).build(); } - + public PlayerLocationInfo getPlayerLocationInfo() { - return PlayerLocationInfo.newBuilder() - .setUid(this.getUid()) - .setPos(this.getPos().toProto()) - .setRot(this.getRotation().toProto()) - .build(); + return PlayerLocationInfo.newBuilder().setUid(this.getUid()).setPos(this.getPos().toProto()).setRot(this.getRotation().toProto()).build(); } - + public synchronized void onTick() { // Check ping if (this.getLastPingTime() > System.currentTimeMillis() + 60000) { @@ -717,7 +681,7 @@ public class GenshinPlayer { if (this.getWorld() != null) { // RTT notify - very important to send this often this.sendPacket(new PacketWorldPlayerRTTNotify(this.getWorld())); - + // Update player locations if in multiplayer every 5 seconds long time = System.currentTimeMillis(); if (this.getWorld().isMultiplayer() && this.getScene() != null && time > nextSendPlayerLocTime) { @@ -727,7 +691,7 @@ public class GenshinPlayer { } } } - + public void resetSendPlayerLocTime() { this.nextSendPlayerLocTime = System.currentTimeMillis() + 5000; } @@ -736,7 +700,7 @@ public class GenshinPlayer { private void onLoad() { this.getTeamManager().setPlayer(this); } - + public void save() { DatabaseHelper.savePlayer(this); } @@ -745,44 +709,45 @@ public class GenshinPlayer { // Make sure these exist if (this.getTeamManager() == null) { this.teamManager = new TeamManager(this); - } if (this.getProfile().getUid() == 0) { + } + if (this.getProfile().getUid() == 0) { this.getProfile().syncWithCharacter(this); } - + // Check if player object exists in server // TODO - optimize GenshinPlayer exists = this.getServer().getPlayerByUid(getUid()); if (exists != null) { exists.getSession().close(); } - + // Load from db this.getAvatars().loadFromDatabase(); this.getInventory().loadFromDatabase(); this.getAvatars().postLoad(); - + this.getFriendsList().loadFromDatabase(); - + // Create world World world = new World(this); world.addPlayer(this); - + // Add to gameserver if (getSession().isActive()) { getServer().registerPlayer(this); getProfile().setPlayer(this); // Set online } - - // Multiplayer setting + + // Multiplayer setting this.setProperty(PlayerProperty.PROP_PLAYER_MP_SETTING_TYPE, this.getMpSetting().getNumber()); this.setProperty(PlayerProperty.PROP_IS_MP_MODE_AVAILABLE, 1); - + // Packets session.send(new PacketPlayerDataNotify(this)); // Player data session.send(new PacketStoreWeightLimitNotify()); session.send(new PacketPlayerStoreNotify(this)); session.send(new PacketAvatarDataNotify(this)); - + session.send(new PacketPlayerEnterSceneNotify(this)); // Enter game world session.send(new PacketPlayerLevelRewardUpdateNotify(rewardedLevels)); session.send(new PacketOpenStateUpdateNotify()); @@ -790,34 +755,34 @@ public class GenshinPlayer { // First notify packets sent this.setHasSentAvatarDataNotify(true); } - + public void onLogout() { // Leave world if (this.getWorld() != null) { this.getWorld().removePlayer(this); } - + // Status stuff this.getProfile().syncWithCharacter(this); this.getProfile().setPlayer(null); // Set offline - + this.getCoopRequests().clear(); - + // Save to db this.save(); this.getTeamManager().saveAvatars(); this.getFriendsList().save(); } - + public enum SceneLoadState { - NONE (0), LOADING (1), INIT (2), LOADED (3); - + NONE(0), LOADING(1), INIT(2), LOADED(3); + private final int value; - + private SceneLoadState(int value) { this.value = value; } - + public int getValue() { return this.value; } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetPlayerBirthdayReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetPlayerBirthdayReq.java index 5bfa9f8d5..31f86128a 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetPlayerBirthdayReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetPlayerBirthdayReq.java @@ -11,23 +11,57 @@ import emu.grasscutter.server.packet.send.PacketSetPlayerBirthdayRsp; @Opcodes(PacketOpcodes.SetPlayerBirthdayReq) public class HandlerSetPlayerBirthdayReq extends PacketHandler { - @Override - public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { - SetPlayerBirthdayReq req = SetPlayerBirthdayReq.parseFrom(payload); + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + SetPlayerBirthdayReq req = SetPlayerBirthdayReq.parseFrom(payload); - if (req.getBirthday().getDay() > 0 && req.getBirthday().getMonth() > 0) { - int day = req.getBirthday().getDay(); - int month = req.getBirthday().getMonth(); + // RET_BIRTHDAY_CANNOT_BE_SET_TWICE = 7009 + if (session.getPlayer().hasBirthday()) { + session.send(new PacketSetPlayerBirthdayRsp(7009)); + return; + } - // Update birthday value - session.getPlayer().setBirthday(day, month); + int month = req.getBirthday().getMonth(); + int day = req.getBirthday().getDay(); - // Save birthday month and day - session.getPlayer().save(); - SocialDetail.Builder detail = session.getPlayer().getSocialDetail(); + // RET_BIRTHDAY_FORMAT_ERROR = 7022 + if (!isValidBirthday(month, day)) { + session.send(new PacketSetPlayerBirthdayRsp(7022)); + return; + } + + // Update birthday value + session.getPlayer().setBirthday(day, month); + + // Save birthday month and day + session.getPlayer().save(); + SocialDetail.Builder detail = session.getPlayer().getSocialDetail(); + + session.send(new PacketSetPlayerBirthdayRsp(session.getPlayer())); + session.send(new PacketGetPlayerSocialDetailRsp(detail)); + } + + private boolean isValidBirthday(int month, int day) { + + switch (month) { + case 1: + case 3: + case 5: + case 7: + case 8: + case 10: + case 12: + return day > 0 & day <= 31; + case 4: + case 6: + case 9: + case 11: + return day > 0 && day <= 30; + case 2: + return day > 0 & day <= 29; + } + + return false; + } - session.send(new PacketSetPlayerBirthdayRsp(session.getPlayer())); - session.send(new PacketGetPlayerSocialDetailRsp(detail)); - } - } } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketSetPlayerBirthdayRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketSetPlayerBirthdayRsp.java index 135d22f2f..8a343bf1d 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketSetPlayerBirthdayRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketSetPlayerBirthdayRsp.java @@ -6,13 +6,24 @@ import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.proto.SetPlayerBirthdayRspOuterClass.SetPlayerBirthdayRsp; public class PacketSetPlayerBirthdayRsp extends GenshinPacket { - public PacketSetPlayerBirthdayRsp(GenshinPlayer player) { - super(PacketOpcodes.SetPlayerBirthdayRsp); - SetPlayerBirthdayRsp proto = SetPlayerBirthdayRsp.newBuilder() - .setBirthday(player.getBirthday().toProto()) - .build(); + public PacketSetPlayerBirthdayRsp(int retCode) { + super(PacketOpcodes.SetPlayerBirthdayRsp); - this.setData(proto); - } + SetPlayerBirthdayRsp proto = SetPlayerBirthdayRsp.newBuilder() + .setRetcode(retCode) + .build(); + + this.setData(proto); + } + + public PacketSetPlayerBirthdayRsp(GenshinPlayer player) { + super(PacketOpcodes.SetPlayerBirthdayRsp); + + SetPlayerBirthdayRsp proto = SetPlayerBirthdayRsp.newBuilder() + .setBirthday(player.getBirthday().toProto()) + .build(); + + this.setData(proto); + } } From 480374fb399fb7125fb9405ff16fa0047dda604e Mon Sep 17 00:00:00 2001 From: Asnxthaony Date: Mon, 25 Apr 2022 15:16:18 +0800 Subject: [PATCH 70/78] fix indent by using smart tabs --- .../emu/grasscutter/game/GenshinPlayer.java | 38 ++++++++++++++----- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/src/main/java/emu/grasscutter/game/GenshinPlayer.java b/src/main/java/emu/grasscutter/game/GenshinPlayer.java index 3c05e6ef5..3c68f04c2 100644 --- a/src/main/java/emu/grasscutter/game/GenshinPlayer.java +++ b/src/main/java/emu/grasscutter/game/GenshinPlayer.java @@ -611,13 +611,13 @@ public class GenshinPlayer { public OnlinePlayerInfo getOnlinePlayerInfo() { OnlinePlayerInfo.Builder onlineInfo = OnlinePlayerInfo.newBuilder() - .setUid(this.getUid()) - .setNickname(this.getNickname()) - .setPlayerLevel(this.getLevel()) - .setMpSettingType(this.getMpSetting()) - .setNameCardId(this.getNameCardId()) - .setSignature(this.getSignature()) - .setAvatar(HeadImage.newBuilder().setAvatarId(this.getHeadImage())); + .setUid(this.getUid()) + .setNickname(this.getNickname()) + .setPlayerLevel(this.getLevel()) + .setMpSettingType(this.getMpSetting()) + .setNameCardId(this.getNameCardId()) + .setSignature(this.getSignature()) + .setAvatar(HeadImage.newBuilder().setAvatarId(this.getHeadImage())); if (this.getWorld() != null) { onlineInfo.setCurPlayerNumInWorld(this.getWorld().getPlayers().indexOf(this) + 1); @@ -650,16 +650,34 @@ public class GenshinPlayer { } public SocialDetail.Builder getSocialDetail() { - SocialDetail.Builder social = SocialDetail.newBuilder().setUid(this.getUid()).setAvatar(HeadImage.newBuilder().setAvatarId(this.getHeadImage())).setNickname(this.getNickname()).setSignature(this.getSignature()).setLevel(this.getLevel()).setBirthday(this.getBirthday().getFilledProtoWhenNotEmpty()).setWorldLevel(this.getWorldLevel()).setUnk1(1).setUnk3(1).setNameCardId(this.getNameCardId()).setFinishAchievementNum(0); + SocialDetail.Builder social = SocialDetail.newBuilder() + .setUid(this.getUid()) + .setAvatar(HeadImage.newBuilder().setAvatarId(this.getHeadImage())) + .setNickname(this.getNickname()) + .setSignature(this.getSignature()) + .setLevel(this.getLevel()) + .setBirthday(this.getBirthday().getFilledProtoWhenNotEmpty()) + .setWorldLevel(this.getWorldLevel()) + .setUnk1(1) + .setUnk3(1) + .setNameCardId(this.getNameCardId()) + .setFinishAchievementNum(0); return social; } public WorldPlayerLocationInfo getWorldPlayerLocationInfo() { - return WorldPlayerLocationInfo.newBuilder().setSceneId(this.getSceneId()).setPlayerLoc(this.getPlayerLocationInfo()).build(); + return WorldPlayerLocationInfo.newBuilder() + .setSceneId(this.getSceneId()) + .setPlayerLoc(this.getPlayerLocationInfo()) + .build(); } public PlayerLocationInfo getPlayerLocationInfo() { - return PlayerLocationInfo.newBuilder().setUid(this.getUid()).setPos(this.getPos().toProto()).setRot(this.getRotation().toProto()).build(); + return PlayerLocationInfo.newBuilder() + .setUid(this.getUid()) + .setPos(this.getPos().toProto()) + .setRot(this.getRotation().toProto()) + .build(); } public synchronized void onTick() { From 16e84390582f9bf613a2a4691e6333120975bb81 Mon Sep 17 00:00:00 2001 From: zhufengning Date: Mon, 25 Apr 2022 13:50:28 +0800 Subject: [PATCH 71/78] fix spelling in README_zh-CN.md --- README_zh-CN.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README_zh-CN.md b/README_zh-CN.md index 2bf82aa97..075bc11a1 100644 --- a/README_zh-CN.md +++ b/README_zh-CN.md @@ -128,7 +128,7 @@ chmod +x gradlew | restart | restart | | 均可使用 | 重启服务端 | | | say | say <消息> | server.sendmessage | 均可使用 | 作为服务器发送消息给玩家 | `sendservmsg` `sendservermessage` `sendmessage` | | setfetterlevel | setfetterlevel <好感等级> | player.setfetterlevel | 仅客户端 | 设置当前角色的好感等级 | `setfetterlvl` `setfriendship` | -| setstats | setstats <属性> <树脂> | player.setstats | 仅客户端 | 直接修改当前角色的面板 | stats | +| setstats | setstats <属性> <数值> | player.setstats | 仅客户端 | 直接修改当前角色的面板 | stats | | setworldlevel | setworldlevel <世界等级> | player.setworldlevel | 仅客户端 | 设置世界等级(重新登陆即可生效) | setworldlvl | | spawn | spanw <实体ID\|实体名称> [等级] [数量] | server.spawn | 仅客户端 | 在你周围生成实体 | | | stop | stop | server.stop | 均可使用 | 停止服务器 | | From cfeec14d010a799a900c13f74859ca1b07be9dab Mon Sep 17 00:00:00 2001 From: Magix Date: Mon, 25 Apr 2022 02:54:48 -0400 Subject: [PATCH 72/78] Update issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 38 +++++++++++++++++++++++ .github/ISSUE_TEMPLATE/custom.md | 10 ++++++ .github/ISSUE_TEMPLATE/feature_request.md | 20 ++++++++++++ 3 files changed, 68 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/custom.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..dd84ea782 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,38 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/custom.md b/.github/ISSUE_TEMPLATE/custom.md new file mode 100644 index 000000000..48d5f81fa --- /dev/null +++ b/.github/ISSUE_TEMPLATE/custom.md @@ -0,0 +1,10 @@ +--- +name: Custom issue template +about: Describe this issue template's purpose here. +title: '' +labels: '' +assignees: '' + +--- + + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..bbcbbe7d6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. From c1ef679e3025b845a8005f0cbb594c192aa164f8 Mon Sep 17 00:00:00 2001 From: alt3ri <95161279+alt3ri@users.noreply.github.com> Date: Tue, 26 Apr 2022 00:13:16 +0700 Subject: [PATCH 73/78] Delete ClearWeaponsCommand.java --- .../command/commands/ClearWeaponsCommand.java | 51 ------------------- 1 file changed, 51 deletions(-) delete mode 100644 src/main/java/emu/grasscutter/command/commands/ClearWeaponsCommand.java diff --git a/src/main/java/emu/grasscutter/command/commands/ClearWeaponsCommand.java b/src/main/java/emu/grasscutter/command/commands/ClearWeaponsCommand.java deleted file mode 100644 index fbe72db87..000000000 --- a/src/main/java/emu/grasscutter/command/commands/ClearWeaponsCommand.java +++ /dev/null @@ -1,51 +0,0 @@ -package emu.grasscutter.command.commands; - -import emu.grasscutter.Grasscutter; -import emu.grasscutter.command.Command; -import emu.grasscutter.command.CommandHandler; -import emu.grasscutter.game.GenshinPlayer; -import emu.grasscutter.game.inventory.Inventory; -import emu.grasscutter.game.inventory.ItemType; - -import java.util.List; - -@Command(label = "clearweapons", usage = "clearweapons", - description = "Deletes all unequipped and unlocked weapons, including yellow rarity ones from your inventory", - aliases = {"clearwp"}, permission = "player.clearweapons") -public final class ClearWeaponsCommand implements CommandHandler { - - @Override - public void execute(GenshinPlayer sender, List args) { - if (sender == null) { - CommandHandler.sendMessage(null, "Run this command in-game."); - return; - } - - int target; - if (args.size() == 1) { - try { - target = Integer.parseInt(args.get(0)); - if (Grasscutter.getGameServer().getPlayerByUid(target) == null) { - target = sender.getUid(); - } - } catch (NumberFormatException e) { - CommandHandler.sendMessage(sender, "Invalid player id."); - return; - } - } else { - target = sender.getUid(); - } - GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target); - if (targetPlayer == null) { - CommandHandler.sendMessage(sender, "Player not found."); - return; - } - - Inventory playerInventory = targetPlayer.getInventory(); - playerInventory.getItems().values().stream() - .filter(item -> item.getItemType() == ItemType.ITEM_WEAPON) - .filter(item -> !item.isLocked() && !item.isEquipped()) - .forEach(item -> playerInventory.removeItem(item, item.getCount())); - sender.dropMessage("Cleared weapons for " + targetPlayer.getNickname() + " ."); - } -} From 56eb7110557f4e37102762daa87341c05130ebcd Mon Sep 17 00:00:00 2001 From: alt3ri <95161279+alt3ri@users.noreply.github.com> Date: Tue, 26 Apr 2022 00:13:26 +0700 Subject: [PATCH 74/78] Delete ClearArtifactsCommand.java --- .../commands/ClearArtifactsCommand.java | 30 ------------------- 1 file changed, 30 deletions(-) delete mode 100644 src/main/java/emu/grasscutter/command/commands/ClearArtifactsCommand.java diff --git a/src/main/java/emu/grasscutter/command/commands/ClearArtifactsCommand.java b/src/main/java/emu/grasscutter/command/commands/ClearArtifactsCommand.java deleted file mode 100644 index 258e1e1d5..000000000 --- a/src/main/java/emu/grasscutter/command/commands/ClearArtifactsCommand.java +++ /dev/null @@ -1,30 +0,0 @@ -package emu.grasscutter.command.commands; - -import emu.grasscutter.command.Command; -import emu.grasscutter.command.CommandHandler; -import emu.grasscutter.game.GenshinPlayer; -import emu.grasscutter.game.inventory.Inventory; -import emu.grasscutter.game.inventory.ItemType; - -import java.util.List; - -@Command(label = "clearartifacts", usage = "clearartifacts", - description = "Deletes all unequipped and unlocked level 0 artifacts, including yellow rarity ones from your inventory", - aliases = {"clearart"}, permission = "player.clearartifacts") -public final class ClearArtifactsCommand implements CommandHandler { - - @Override - public void execute(GenshinPlayer sender, List args) { - if (sender == null) { - CommandHandler.sendMessage(null, "Run this command in-game."); - return; // TODO: clear player's artifacts from console or other players - } - - Inventory playerInventory = sender.getInventory(); - playerInventory.getItems().values().stream() - .filter(item -> item.getItemType() == ItemType.ITEM_RELIQUARY) - .filter(item -> item.getLevel() == 1 && item.getExp() == 0) - .filter(item -> !item.isLocked() && !item.isEquipped()) - .forEach(item -> playerInventory.removeItem(item, item.getCount())); - } -} From 5dd5f70903df72eff15938dd162aa34e4a591a93 Mon Sep 17 00:00:00 2001 From: alt3ri <95161279+alt3ri@users.noreply.github.com> Date: Tue, 26 Apr 2022 00:18:48 +0700 Subject: [PATCH 75/78] Merged /clearartifacts and /clearweapons I merged ClearArtifactsCommand and ClearWeaponsCommand to ClearCommand with UID parameter. Still no send from console cuz I forgor :skull: Usage: /clear [uid] --- .../command/commands/ClearCommand.java | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 src/main/java/emu/grasscutter/command/commands/ClearCommand.java diff --git a/src/main/java/emu/grasscutter/command/commands/ClearCommand.java b/src/main/java/emu/grasscutter/command/commands/ClearCommand.java new file mode 100644 index 000000000..8d16e58c1 --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/ClearCommand.java @@ -0,0 +1,106 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.data.GenshinData; +import emu.grasscutter.data.def.ItemData; +import emu.grasscutter.game.GenshinPlayer; +import emu.grasscutter.game.inventory.GenshinItem; +import emu.grasscutter.game.inventory.Inventory; +import emu.grasscutter.game.inventory.ItemType; + +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; + +@Command(label = "clear", usage = "clear ", //Merged /clearartifacts and /clearweapons to /clear [uid] + description = "Deletes unequipped unlocked items, including yellow rarity ones from your inventory", + aliases = {"clear"}, permission = "player.clearinv") + +public final class ClearCommand implements CommandHandler { + + @Override + public void execute(GenshinPlayer sender, List args) { + int target; + if (sender == null) { + CommandHandler.sendMessage(null, "Run this command in-game."); + return; + } + String cmdSwitch = args.get(1); + + Inventory playerInventory = sender.getInventory(); + try { + target = Integer.parseInt(args.get(0)); + GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target); + if (targetPlayer == null && sender != null) { + target = sender.getUid(); + } else { + switch (cmdSwitch){ + case "wp": + playerInventory.getItems().values().stream() + .filter(item -> item.getItemType() == ItemType.ITEM_WEAPON) + .filter(item -> !item.isLocked() && !item.isEquipped()) + .forEach(item -> playerInventory.removeItem(item, item.getCount())); + sender.dropMessage("Cleared weapons for " + targetPlayer.getNickname() + " ."); + break; + case "art": + playerInventory.getItems().values().stream() + .filter(item -> item.getItemType() == ItemType.ITEM_RELIQUARY) + .filter(item -> item.getLevel() == 1 && item.getExp() == 0) + .filter(item -> !item.isLocked() && !item.isEquipped()) + .forEach(item -> playerInventory.removeItem(item, item.getCount())); + sender.dropMessage("Cleared artifacts for " + targetPlayer.getNickname() + " ."); + break; + case "mat": + playerInventory.getItems().values().stream() + .filter(item -> item.getItemType() == ItemType.ITEM_MATERIAL) + .filter(item -> item.getLevel() == 1 && item.getExp() == 0) + .filter(item -> !item.isLocked() && !item.isEquipped()) + .forEach(item -> playerInventory.removeItem(item, item.getCount())); + sender.dropMessage("Cleared artifacts for " + targetPlayer.getNickname() + " ."); + break; + case "all": + playerInventory.getItems().values().stream() + .filter(item1 -> item1.getItemType() == ItemType.ITEM_RELIQUARY) + .filter(item1 -> item1.getLevel() == 1 && item1.getExp() == 0) + .filter(item1 -> !item1.isLocked() && !item1.isEquipped()) + .forEach(item1 -> playerInventory.removeItem(item1, item1.getCount())); + playerInventory.getItems().values().stream() + .filter(item2 -> item2.getItemType() == ItemType.ITEM_MATERIAL) + .filter(item2 -> !item2.isLocked() && !item2.isEquipped()) + .forEach(item2 -> playerInventory.removeItem(item2, item2.getCount())); + playerInventory.getItems().values().stream() + .filter(item3 -> item3.getItemType() == ItemType.ITEM_WEAPON) + .filter(item3 -> item3.getLevel() == 1 && item3.getExp() == 0) + .filter(item3 -> !item3.isLocked() && !item3.isEquipped()) + .forEach(item3 -> playerInventory.removeItem(item3, item3.getCount())); + playerInventory.getItems().values().stream() + .filter(item4 -> item4.getItemType() == ItemType.ITEM_FURNITURE) + .filter(item4 -> !item4.isLocked() && !item4.isEquipped()) + .forEach(item4 -> playerInventory.removeItem(item4, item4.getCount())); + playerInventory.getItems().values().stream() + .filter(item5 -> item5.getItemType() == ItemType.ITEM_DISPLAY) + .filter(item5 -> !item5.isLocked() && !item5.isEquipped()) + .forEach(item5 -> playerInventory.removeItem(item5, item5.getCount())); + playerInventory.getItems().values().stream() + .filter(item6 -> item6.getItemType() == ItemType.ITEM_VIRTUAL) + .filter(item6 -> !item6.isLocked() && !item6.isEquipped()) + .forEach(item6 -> playerInventory.removeItem(item6, item6.getCount())); + sender.dropMessage("Cleared everything for " + targetPlayer.getNickname() + " ."); + break; + } + } + } catch (NumberFormatException ignored) { + // TODO: Parse from item name using GM Handbook. + CommandHandler.sendMessage(sender, "Invalid playerId."); + return; + } + + GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target); + if (targetPlayer == null) { + CommandHandler.sendMessage(sender, "Player not found."); + return; + } + } +} From 00a40a84bbaaf504ede327868e03a3459f4b1063 Mon Sep 17 00:00:00 2001 From: xtaodada Date: Mon, 25 Apr 2022 21:07:34 +0800 Subject: [PATCH 76/78] Add killcharacter command --- .../commands/KillCharacterCommand.java | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 src/main/java/emu/grasscutter/command/commands/KillCharacterCommand.java diff --git a/src/main/java/emu/grasscutter/command/commands/KillCharacterCommand.java b/src/main/java/emu/grasscutter/command/commands/KillCharacterCommand.java new file mode 100644 index 000000000..b396fdaa1 --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/KillCharacterCommand.java @@ -0,0 +1,57 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.GenshinPlayer; +import emu.grasscutter.game.entity.EntityAvatar; +import emu.grasscutter.game.props.FightProperty; +import emu.grasscutter.game.props.LifeState; +import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify; +import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify; + +import java.util.List; + +@Command(label = "killcharacter", usage = "killcharacter [playerId]", aliases = {"suicide"}, + description = "Directly kill a player's current active character", permission = "player.killcharacter") +public final class KillCharacterCommand implements CommandHandler { + + @Override + public void execute(GenshinPlayer sender, List args) { + if (sender == null) { + CommandHandler.sendMessage(null, "Run this command in-game."); + return; // TODO: kill a player's current active character from console + } + + int target; + if (args.size() == 1) { + try { + target = Integer.parseInt(args.get(0)); + if (Grasscutter.getGameServer().getPlayerByUid(target) == null) { + target = sender.getUid(); + } + } catch (NumberFormatException e) { + CommandHandler.sendMessage(sender, "Invalid player id."); + return; + } + } else { + target = sender.getUid(); + } + GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target); + if (targetPlayer == null) { + CommandHandler.sendMessage(sender, "Player not found or offline."); + return; + } + + EntityAvatar entity = targetPlayer.getTeamManager().getCurrentAvatarEntity(); + entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 0f); + // Packets + entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP)); + entity.getWorld().broadcastPacket(new PacketLifeStateChangeNotify(0, entity, LifeState.LIFE_DEAD)); + // remove + targetPlayer.getScene().removeEntity(entity); + entity.onDeath(0); + + sender.dropMessage("Killed " + targetPlayer.getNickname() + "'s current active character."); + } +} From dc6702eb5c877645a717f6b9bff30f3410e46568 Mon Sep 17 00:00:00 2001 From: xtaodada Date: Mon, 25 Apr 2022 21:22:33 +0800 Subject: [PATCH 77/78] Add a new alias to the command `killcharacter` --- .../emu/grasscutter/command/commands/KillCharacterCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/emu/grasscutter/command/commands/KillCharacterCommand.java b/src/main/java/emu/grasscutter/command/commands/KillCharacterCommand.java index b396fdaa1..f877c64df 100644 --- a/src/main/java/emu/grasscutter/command/commands/KillCharacterCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/KillCharacterCommand.java @@ -12,7 +12,7 @@ import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify; import java.util.List; -@Command(label = "killcharacter", usage = "killcharacter [playerId]", aliases = {"suicide"}, +@Command(label = "killcharacter", usage = "killcharacter [playerId]", aliases = {"suicide", "kill"}, description = "Directly kill a player's current active character", permission = "player.killcharacter") public final class KillCharacterCommand implements CommandHandler { From b6fedcf262ade807dda91baa75adb327fa65cf03 Mon Sep 17 00:00:00 2001 From: xtaodada Date: Mon, 25 Apr 2022 21:56:12 +0800 Subject: [PATCH 78/78] Support kill from console --- .../commands/KillCharacterCommand.java | 41 ++++++++++++------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/src/main/java/emu/grasscutter/command/commands/KillCharacterCommand.java b/src/main/java/emu/grasscutter/command/commands/KillCharacterCommand.java index f877c64df..beaa4f959 100644 --- a/src/main/java/emu/grasscutter/command/commands/KillCharacterCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/KillCharacterCommand.java @@ -13,30 +13,41 @@ import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify; import java.util.List; @Command(label = "killcharacter", usage = "killcharacter [playerId]", aliases = {"suicide", "kill"}, - description = "Directly kill a player's current active character", permission = "player.killcharacter") + description = "Kills the players current character", permission = "player.killcharacter") public final class KillCharacterCommand implements CommandHandler { @Override public void execute(GenshinPlayer sender, List args) { - if (sender == null) { - CommandHandler.sendMessage(null, "Run this command in-game."); - return; // TODO: kill a player's current active character from console - } - int target; - if (args.size() == 1) { - try { - target = Integer.parseInt(args.get(0)); - if (Grasscutter.getGameServer().getPlayerByUid(target) == null) { - target = sender.getUid(); + if (sender == null) { + // from console + if (args.size() == 1) { + try { + target = Integer.parseInt(args.get(0)); + } catch (NumberFormatException e) { + CommandHandler.sendMessage(null, "Invalid player id."); + return; } - } catch (NumberFormatException e) { - CommandHandler.sendMessage(sender, "Invalid player id."); + } else { + CommandHandler.sendMessage(null, "Usage: /killcharacter [playerId]"); return; } } else { - target = sender.getUid(); + if (args.size() == 1) { + try { + target = Integer.parseInt(args.get(0)); + if (Grasscutter.getGameServer().getPlayerByUid(target) == null) { + target = sender.getUid(); + } + } catch (NumberFormatException e) { + CommandHandler.sendMessage(sender, "Invalid player id."); + return; + } + } else { + target = sender.getUid(); + } } + GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target); if (targetPlayer == null) { CommandHandler.sendMessage(sender, "Player not found or offline."); @@ -52,6 +63,6 @@ public final class KillCharacterCommand implements CommandHandler { targetPlayer.getScene().removeEntity(entity); entity.onDeath(0); - sender.dropMessage("Killed " + targetPlayer.getNickname() + "'s current active character."); + CommandHandler.sendMessage(sender, "Killed " + targetPlayer.getNickname() + " current character."); } }