mirror of
https://github.com/Grasscutters/Grasscutter.git
synced 2025-01-25 17:22:55 +08:00
Merge remote-tracking branch 'upstream/development' into dev-mail
This commit is contained in:
commit
c25e809f5c
12
.github/workflows/build.yml
vendored
12
.github/workflows/build.yml
vendored
@ -3,9 +3,14 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- "stable"
|
- "stable"
|
||||||
|
pull_request:
|
||||||
|
types:
|
||||||
|
- opened
|
||||||
|
- synchronize
|
||||||
|
- reopened
|
||||||
jobs:
|
jobs:
|
||||||
Build-Server-Jar:
|
Build-Server-Jar:
|
||||||
runs-on: windows-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
@ -13,12 +18,11 @@ jobs:
|
|||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
distribution: temurin
|
distribution: temurin
|
||||||
java-version: '8'
|
java-version: '16'
|
||||||
- name: Run Gradle
|
- name: Run Gradle
|
||||||
run: .\gradlew.bat && .\gradlew jar
|
run: ./gradlew && ./gradlew jar
|
||||||
- name: Upload build
|
- name: Upload build
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: Grasscutter
|
name: Grasscutter
|
||||||
path: grasscutter.jar
|
path: grasscutter.jar
|
||||||
|
|
||||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -50,8 +50,7 @@ resources/*
|
|||||||
logs/*
|
logs/*
|
||||||
data/AbilityEmbryos.json
|
data/AbilityEmbryos.json
|
||||||
data/OpenConfig.json
|
data/OpenConfig.json
|
||||||
proto/auto/
|
proto/*
|
||||||
proto/protoc.exe
|
|
||||||
GM Handbook.txt
|
GM Handbook.txt
|
||||||
config.json
|
config.json
|
||||||
mitmdump.exe
|
mitmdump.exe
|
||||||
|
26
README.md
26
README.md
@ -10,10 +10,10 @@ A WIP server reimplementation for *some anime game* 2.3-2.6
|
|||||||
* Inventory features (recieving items/characters, upgrading items/characters, etc)
|
* Inventory features (recieving items/characters, upgrading items/characters, etc)
|
||||||
* Gacha system
|
* Gacha system
|
||||||
* Friends list
|
* Friends list
|
||||||
* Co-op *partially* work
|
* Co-op *partially* works
|
||||||
# Quick setup guide
|
# Quick setup guide
|
||||||
### Note
|
### 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
|
### Prerequisites
|
||||||
* Java 16
|
* Java 16
|
||||||
@ -21,15 +21,15 @@ A WIP server reimplementation for *some anime game* 2.3-2.6
|
|||||||
* Proxy daemon: mitmproxy (mitmdump, recommended), Fiddler Classic, etc.
|
* Proxy daemon: mitmproxy (mitmdump, recommended), Fiddler Classic, etc.
|
||||||
|
|
||||||
### Starting up Grasscutter server (Assuming you are on Windows)
|
### 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`
|
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.
|
4. Run Grasscutter with `java -jar grasscutter.jar`. Make sure mongodb service is running as well.
|
||||||
|
|
||||||
### Connecting with the client
|
### Connecting with the client
|
||||||
½. Create an account using *server console command* below
|
½. Create an account using *server console command* below
|
||||||
1. Run a proxy daemon: (choose either one)
|
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).
|
- 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)
|
- [Hosts file](https://github.com/Melledy/Grasscutter/wiki/Running#traffic-route-map)
|
||||||
2. Trust CA certificate:
|
2. Trust CA certificate:
|
||||||
@ -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]`
|
`spawn [monster id] [level] [amount]`
|
||||||
|
|
||||||
`give [item id] [amount]`
|
`give [item id] [amount] [level]`
|
||||||
|
|
||||||
`givechar [avatar id] [level]`
|
`givechar [avatar id] [level]`
|
||||||
|
|
||||||
@ -54,26 +54,26 @@ There is a dummy user named "Server" in every player's friends list that you can
|
|||||||
|
|
||||||
`killall`
|
`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
|
`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-star rarity ones** from your inventory
|
||||||
|
|
||||||
`pos` - Gets your current coordinate.
|
`pos` - Gets your current coordinates.
|
||||||
|
|
||||||
`weather [weather id] [climate id]` - Changes the current weather.
|
`weather [weather id] [climate id]` - Changes the current weather.
|
||||||
|
|
||||||
*More commands will be updated in the [wiki](https://github.com/Melledy/Grasscutter/wiki/).*
|
*More commands will be updated in the [wiki](https://github.com/Melledy/Grasscutter/wiki/).*
|
||||||
|
|
||||||
### Bonus
|
### 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
|
# 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 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
|
* Startup sequence: Mongodb > Grasscutter > Proxy daemon (mitmdump, fiddler, etc.) > Client
|
||||||
|
@ -13,16 +13,15 @@ import emu.grasscutter.server.packet.send.PacketItemAddHintNotify;
|
|||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Command(label = "give", usage = "give [player] <itemId|itemName> [amount]",
|
@Command(label = "give", usage = "give [player] <itemId|itemName> [amount] [level]", description = "Gives an item to you or the specified player", aliases = {
|
||||||
description = "Gives an item to you or the specified player", aliases = {"g", "item", "giveitem"}, permission = "player.give")
|
"g", "item", "giveitem" }, permission = "player.give")
|
||||||
public final class GiveCommand implements CommandHandler {
|
public final class GiveCommand implements CommandHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(GenshinPlayer sender, List<String> args) {
|
public void execute(GenshinPlayer sender, List<String> args) {
|
||||||
int target, item, amount = 1;
|
int target, item, lvl, amount = 1;
|
||||||
|
|
||||||
if (sender == null && args.size() < 2) {
|
if (sender == null && args.size() < 2) {
|
||||||
CommandHandler.sendMessage(null, "Usage: give <player> <itemId|itemName> [amount]");
|
CommandHandler.sendMessage(null, "Usage: give <player> <itemId|itemName> [amount] [level]");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,6 +33,7 @@ public final class GiveCommand implements CommandHandler {
|
|||||||
try {
|
try {
|
||||||
item = Integer.parseInt(args.get(0));
|
item = Integer.parseInt(args.get(0));
|
||||||
target = sender.getUid();
|
target = sender.getUid();
|
||||||
|
lvl = 1;
|
||||||
} catch (NumberFormatException ignored) {
|
} catch (NumberFormatException ignored) {
|
||||||
// TODO: Parse from item name using GM Handbook.
|
// TODO: Parse from item name using GM Handbook.
|
||||||
CommandHandler.sendMessage(sender, "Invalid item id.");
|
CommandHandler.sendMessage(sender, "Invalid item id.");
|
||||||
@ -43,6 +43,7 @@ public final class GiveCommand implements CommandHandler {
|
|||||||
case 2: // <itemId|itemName> [amount] | [player] <itemId|itemName>
|
case 2: // <itemId|itemName> [amount] | [player] <itemId|itemName>
|
||||||
try {
|
try {
|
||||||
target = Integer.parseInt(args.get(0));
|
target = Integer.parseInt(args.get(0));
|
||||||
|
lvl = 1;
|
||||||
|
|
||||||
if (Grasscutter.getGameServer().getPlayerByUid(target) == null && sender != null) {
|
if (Grasscutter.getGameServer().getPlayerByUid(target) == null && sender != null) {
|
||||||
target = sender.getUid();
|
target = sender.getUid();
|
||||||
@ -57,17 +58,39 @@ public final class GiveCommand implements CommandHandler {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 3: // [player] <itemId|itemName> [amount]
|
case 3: // [player] <itemId|itemName> [amount] | <itemId|itemName> [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] <itemId|itemName> [amount] [level]
|
||||||
try {
|
try {
|
||||||
target = Integer.parseInt(args.get(0));
|
target = Integer.parseInt(args.get(0));
|
||||||
|
|
||||||
if (Grasscutter.getGameServer().getPlayerByUid(target) == null) {
|
if (Grasscutter.getGameServer().getPlayerByUid(target) == null) {
|
||||||
CommandHandler.sendMessage(sender, "Invalid player ID.");
|
CommandHandler.sendMessage(sender, "Invalid player ID.");
|
||||||
return;
|
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) {
|
} catch (NumberFormatException ignored) {
|
||||||
// TODO: Parse from item name using GM Handbook.
|
// TODO: Parse from item name using GM Handbook.
|
||||||
CommandHandler.sendMessage(sender, "Invalid item or player ID.");
|
CommandHandler.sendMessage(sender, "Invalid item or player ID.");
|
||||||
@ -89,16 +112,37 @@ public final class GiveCommand implements CommandHandler {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.item(targetPlayer, itemData, amount);
|
this.item(targetPlayer, itemData, amount, lvl);
|
||||||
|
|
||||||
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 %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()) {
|
if (itemData.isEquip()) {
|
||||||
List<GenshinItem> items = new LinkedList<>();
|
List<GenshinItem> items = new LinkedList<>();
|
||||||
for (int i = 0; i < amount; i++) {
|
for (int i = 0; i < amount; i++) {
|
||||||
items.add(new GenshinItem(itemData));
|
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.getInventory().addItems(items);
|
||||||
player.sendPacket(new PacketItemAddHintNotify(items, ActionReason.SubfieldDrop));
|
player.sendPacket(new PacketItemAddHintNotify(items, ActionReason.SubfieldDrop));
|
||||||
@ -110,4 +154,3 @@ public final class GiveCommand implements CommandHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package emu.grasscutter.command.commands;
|
package emu.grasscutter.command.commands;
|
||||||
|
|
||||||
|
import emu.grasscutter.Grasscutter;
|
||||||
import emu.grasscutter.command.Command;
|
import emu.grasscutter.command.Command;
|
||||||
import emu.grasscutter.command.CommandHandler;
|
import emu.grasscutter.command.CommandHandler;
|
||||||
import emu.grasscutter.game.GenshinPlayer;
|
import emu.grasscutter.game.GenshinPlayer;
|
||||||
@ -16,7 +17,29 @@ public final class GodModeCommand implements CommandHandler {
|
|||||||
CommandHandler.sendMessage(null, "Run this command in-game.");
|
CommandHandler.sendMessage(null, "Run this command in-game.");
|
||||||
return; // TODO: toggle player's godmode statue from console or other players
|
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() + " .");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ public final class PositionCommand implements CommandHandler {
|
|||||||
return;
|
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()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
package emu.grasscutter.command.commands;
|
||||||
|
|
||||||
|
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.avatar.GenshinAvatar;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketAvatarFetterDataNotify;
|
||||||
|
|
||||||
|
@Command(label = "setfetterlevel", usage = "setfetterlevel <level>",
|
||||||
|
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<String> args) {
|
||||||
|
if (sender == null) {
|
||||||
|
CommandHandler.sendMessage(null, "Run this command in-game.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.size() < 1) {
|
||||||
|
CommandHandler.sendMessage(sender, "Usage: setfetterlevel <level>");
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
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.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,70 @@
|
|||||||
|
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 <x> <y> <z>", aliases = {"tp"},
|
||||||
|
description = "Change the player's position.", permission = "player.teleport")
|
||||||
|
public class TelePortCommand implements CommandHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(GenshinPlayer sender, List<String> args) {
|
||||||
|
if (sender == null) {
|
||||||
|
CommandHandler.sendMessage(null, "Run this command in-game.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.size() < 3){
|
||||||
|
CommandHandler.sendMessage(sender, "Usage: /tp <x> <y> <z> [scene id]");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
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.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -31,6 +31,7 @@ public class GenshinData {
|
|||||||
private static final Int2ObjectMap<AvatarSkillDepotData> avatarSkillDepotDataMap = new Int2ObjectOpenHashMap<>();
|
private static final Int2ObjectMap<AvatarSkillDepotData> avatarSkillDepotDataMap = new Int2ObjectOpenHashMap<>();
|
||||||
private static final Int2ObjectMap<AvatarSkillData> avatarSkillDataMap = new Int2ObjectOpenHashMap<>();
|
private static final Int2ObjectMap<AvatarSkillData> avatarSkillDataMap = new Int2ObjectOpenHashMap<>();
|
||||||
private static final Int2ObjectMap<AvatarCurveData> avatarCurveDataMap = new Int2ObjectLinkedOpenHashMap<>();
|
private static final Int2ObjectMap<AvatarCurveData> avatarCurveDataMap = new Int2ObjectLinkedOpenHashMap<>();
|
||||||
|
private static final Int2ObjectMap<AvatarFetterLevelData> avatarFetterLevelDataMap = new Int2ObjectOpenHashMap<>();
|
||||||
private static final Int2ObjectMap<AvatarPromoteData> avatarPromoteDataMap = new Int2ObjectOpenHashMap<>();
|
private static final Int2ObjectMap<AvatarPromoteData> avatarPromoteDataMap = new Int2ObjectOpenHashMap<>();
|
||||||
private static final Int2ObjectMap<AvatarTalentData> avatarTalentDataMap = new Int2ObjectOpenHashMap<>();
|
private static final Int2ObjectMap<AvatarTalentData> avatarTalentDataMap = new Int2ObjectOpenHashMap<>();
|
||||||
private static final Int2ObjectMap<ProudSkillData> proudSkillDataMap = new Int2ObjectOpenHashMap<>();
|
private static final Int2ObjectMap<ProudSkillData> proudSkillDataMap = new Int2ObjectOpenHashMap<>();
|
||||||
@ -57,6 +58,8 @@ public class GenshinData {
|
|||||||
|
|
||||||
private static final Int2ObjectMap<SceneData> sceneDataMap = new Int2ObjectLinkedOpenHashMap<>();
|
private static final Int2ObjectMap<SceneData> sceneDataMap = new Int2ObjectLinkedOpenHashMap<>();
|
||||||
private static final Int2ObjectMap<FetterData> fetterDataMap = new Int2ObjectOpenHashMap<>();
|
private static final Int2ObjectMap<FetterData> fetterDataMap = new Int2ObjectOpenHashMap<>();
|
||||||
|
private static final Int2ObjectMap<FetterCharacterCardData> fetterCharacterCardDataMap = new Int2ObjectOpenHashMap<>();
|
||||||
|
private static final Int2ObjectMap<RewardData> rewardDataMap = new Int2ObjectOpenHashMap<>();
|
||||||
|
|
||||||
// Cache
|
// Cache
|
||||||
private static Map<Integer, List<Integer>> fetters = new HashMap<>();
|
private static Map<Integer, List<Integer>> fetters = new HashMap<>();
|
||||||
@ -114,6 +117,14 @@ public class GenshinData {
|
|||||||
return playerLevelDataMap;
|
return playerLevelDataMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Int2ObjectMap<AvatarFetterLevelData> getAvatarFetterLevelDataMap() {
|
||||||
|
return avatarFetterLevelDataMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Int2ObjectMap<FetterCharacterCardData> getFetterCharacterCardDataMap() {
|
||||||
|
return fetterCharacterCardDataMap;
|
||||||
|
}
|
||||||
|
|
||||||
public static Int2ObjectMap<AvatarLevelData> getAvatarLevelDataMap() {
|
public static Int2ObjectMap<AvatarLevelData> getAvatarLevelDataMap() {
|
||||||
return avatarLevelDataMap;
|
return avatarLevelDataMap;
|
||||||
}
|
}
|
||||||
@ -176,6 +187,11 @@ public class GenshinData {
|
|||||||
return levelData != null ? levelData.getExp() : 0;
|
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<ProudSkillData> getProudSkillDataMap() {
|
public static Int2ObjectMap<ProudSkillData> getProudSkillDataMap() {
|
||||||
return proudSkillDataMap;
|
return proudSkillDataMap;
|
||||||
}
|
}
|
||||||
@ -228,6 +244,10 @@ public class GenshinData {
|
|||||||
return sceneDataMap;
|
return sceneDataMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Int2ObjectMap<RewardData> getRewardDataMap() {
|
||||||
|
return rewardDataMap;
|
||||||
|
}
|
||||||
|
|
||||||
public static Map<Integer, List<Integer>> getFetterDataEntries() {
|
public static Map<Integer, List<Integer>> getFetterDataEntries() {
|
||||||
if (fetters.isEmpty()) {
|
if (fetters.isEmpty()) {
|
||||||
fetterDataMap.forEach((k, v) -> {
|
fetterDataMap.forEach((k, v) -> {
|
||||||
|
24
src/main/java/emu/grasscutter/data/common/OpenCondData.java
Normal file
24
src/main/java/emu/grasscutter/data/common/OpenCondData.java
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package emu.grasscutter.data.common;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class OpenCondData {
|
||||||
|
private String CondType;
|
||||||
|
private List<Integer> ParamList;
|
||||||
|
|
||||||
|
public String getCondType() {
|
||||||
|
return CondType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCondType(String condType) {
|
||||||
|
CondType = condType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Integer> getParamList() {
|
||||||
|
return ParamList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setParamList(List<Integer> paramList) {
|
||||||
|
ParamList = paramList;
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -57,6 +57,8 @@ public class AvatarData extends GenshinResource {
|
|||||||
private IntList abilities;
|
private IntList abilities;
|
||||||
|
|
||||||
private List<Integer> fetters;
|
private List<Integer> fetters;
|
||||||
|
private int nameCardRewardId;
|
||||||
|
private int nameCardId;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getId(){
|
public int getId(){
|
||||||
@ -199,6 +201,14 @@ public class AvatarData extends GenshinResource {
|
|||||||
return fetters;
|
return fetters;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getNameCardRewardId() {
|
||||||
|
return nameCardRewardId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNameCardId() {
|
||||||
|
return nameCardId;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLoad() {
|
public void onLoad() {
|
||||||
this.skillDepot = GenshinData.getAvatarSkillDepotDataMap().get(this.SkillDepotId);
|
this.skillDepot = GenshinData.getAvatarSkillDepotDataMap().get(this.SkillDepotId);
|
||||||
@ -206,6 +216,14 @@ public class AvatarData extends GenshinResource {
|
|||||||
// Get fetters from GenshinData
|
// Get fetters from GenshinData
|
||||||
this.fetters = GenshinData.getFetterDataEntries().get(this.Id);
|
this.fetters = GenshinData.getFetterDataEntries().get(this.Id);
|
||||||
|
|
||||||
|
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();
|
int size = GenshinData.getAvatarCurveDataMap().size();
|
||||||
this.hpGrowthCurve = new float[size];
|
this.hpGrowthCurve = new float[size];
|
||||||
this.attackGrowthCurve = new float[size];
|
this.attackGrowthCurve = new float[size];
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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() {
|
||||||
|
}
|
||||||
|
}
|
@ -1,13 +1,17 @@
|
|||||||
package emu.grasscutter.data.def;
|
package emu.grasscutter.data.def;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import emu.grasscutter.data.GenshinResource;
|
import emu.grasscutter.data.GenshinResource;
|
||||||
import emu.grasscutter.data.ResourceType;
|
import emu.grasscutter.data.ResourceType;
|
||||||
import emu.grasscutter.data.ResourceType.LoadPriority;
|
import emu.grasscutter.data.ResourceType.LoadPriority;
|
||||||
|
import emu.grasscutter.data.common.OpenCondData;
|
||||||
|
|
||||||
@ResourceType(name = {"FetterInfoExcelConfigData.json", "FettersExcelConfigData.json", "FetterStoryExcelConfigData.json"}, loadPriority = LoadPriority.HIGHEST)
|
@ResourceType(name = {"FetterInfoExcelConfigData.json", "FettersExcelConfigData.json", "FetterStoryExcelConfigData.json"}, loadPriority = LoadPriority.HIGHEST)
|
||||||
public class FetterData extends GenshinResource {
|
public class FetterData extends GenshinResource {
|
||||||
private int AvatarId;
|
private int AvatarId;
|
||||||
private int FetterId;
|
private int FetterId;
|
||||||
|
private List<OpenCondData> OpenCond;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getId() {
|
public int getId() {
|
||||||
@ -18,6 +22,10 @@ public class FetterData extends GenshinResource {
|
|||||||
return AvatarId;
|
return AvatarId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<OpenCondData> getOpenConds() {
|
||||||
|
return OpenCond;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLoad() {
|
public void onLoad() {
|
||||||
}
|
}
|
||||||
|
27
src/main/java/emu/grasscutter/data/def/RewardData.java
Normal file
27
src/main/java/emu/grasscutter/data/def/RewardData.java
Normal file
@ -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<RewardItemData> RewardItemList;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getId() {
|
||||||
|
return RewardId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<RewardItemData> getRewardItemList() {
|
||||||
|
return RewardItemList;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoad() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -101,7 +101,7 @@ public final class DatabaseManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static synchronized int getNextId(Class<?> c) {
|
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) {
|
if (counter == null) {
|
||||||
counter = new DatabaseCounter(c.getSimpleName());
|
counter = new DatabaseCounter(c.getSimpleName());
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,30 @@ import emu.grasscutter.net.proto.SocialDetailOuterClass.SocialDetail;
|
|||||||
import emu.grasscutter.net.proto.WorldPlayerLocationInfoOuterClass.WorldPlayerLocationInfo;
|
import emu.grasscutter.net.proto.WorldPlayerLocationInfoOuterClass.WorldPlayerLocationInfo;
|
||||||
import emu.grasscutter.server.game.GameServer;
|
import emu.grasscutter.server.game.GameServer;
|
||||||
import emu.grasscutter.server.game.GameSession;
|
import emu.grasscutter.server.game.GameSession;
|
||||||
import emu.grasscutter.server.packet.send.*;
|
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.PacketMailChangeNotify;
|
||||||
import emu.grasscutter.utils.Position;
|
import emu.grasscutter.utils.Position;
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||||
@ -75,6 +98,7 @@ public class GenshinPlayer {
|
|||||||
private MpSettingType mpSetting = MpSettingType.MpSettingEnterAfterApply;
|
private MpSettingType mpSetting = MpSettingType.MpSettingEnterAfterApply;
|
||||||
private boolean showAvatar;
|
private boolean showAvatar;
|
||||||
private ArrayList<AvatarProfileData> shownAvatars;
|
private ArrayList<AvatarProfileData> shownAvatars;
|
||||||
|
private Set<Integer> rewardedLevels;
|
||||||
private ArrayList<Mail> mail;
|
private ArrayList<Mail> mail;
|
||||||
|
|
||||||
private int sceneId;
|
private int sceneId;
|
||||||
@ -125,6 +149,7 @@ public class GenshinPlayer {
|
|||||||
this.clientAbilityInitFinishHandler = new InvokeHandler(PacketClientAbilityInitFinishNotify.class);
|
this.clientAbilityInitFinishHandler = new InvokeHandler(PacketClientAbilityInitFinishNotify.class);
|
||||||
|
|
||||||
this.birthday = new PlayerBirthday();
|
this.birthday = new PlayerBirthday();
|
||||||
|
this.rewardedLevels = new HashSet<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
// On player creation
|
// On player creation
|
||||||
@ -648,6 +673,14 @@ public class GenshinPlayer {
|
|||||||
this.updateProfile();
|
this.updateProfile();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Set<Integer> getRewardedLevels() {
|
||||||
|
return rewardedLevels;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRewardedLevels(Set<Integer> rewardedLevels) {
|
||||||
|
this.rewardedLevels = rewardedLevels;
|
||||||
|
}
|
||||||
|
|
||||||
public SocialDetail.Builder getSocialDetail() {
|
public SocialDetail.Builder getSocialDetail() {
|
||||||
SocialDetail.Builder social = SocialDetail.newBuilder()
|
SocialDetail.Builder social = SocialDetail.newBuilder()
|
||||||
.setUid(this.getUid())
|
.setUid(this.getUid())
|
||||||
@ -765,6 +798,7 @@ public class GenshinPlayer {
|
|||||||
session.send(new PacketAvatarDataNotify(this));
|
session.send(new PacketAvatarDataNotify(this));
|
||||||
|
|
||||||
session.send(new PacketPlayerEnterSceneNotify(this)); // Enter game world
|
session.send(new PacketPlayerEnterSceneNotify(this)); // Enter game world
|
||||||
|
session.send(new PacketPlayerLevelRewardUpdateNotify(rewardedLevels));
|
||||||
session.send(new PacketOpenStateUpdateNotify());
|
session.send(new PacketOpenStateUpdateNotify());
|
||||||
|
|
||||||
// First notify packets sent
|
// First notify packets sent
|
||||||
|
@ -90,6 +90,12 @@ public class GenshinAvatar {
|
|||||||
private int costume;
|
private int costume;
|
||||||
private int bornTime;
|
private int bornTime;
|
||||||
|
|
||||||
|
private int fetterLevel = 1;
|
||||||
|
private int fetterExp;
|
||||||
|
|
||||||
|
private int nameCardRewardId;
|
||||||
|
private int nameCardId;
|
||||||
|
|
||||||
public GenshinAvatar() {
|
public GenshinAvatar() {
|
||||||
// Morhpia only!
|
// Morhpia only!
|
||||||
this.equips = new Int2ObjectOpenHashMap<>();
|
this.equips = new Int2ObjectOpenHashMap<>();
|
||||||
@ -107,6 +113,8 @@ public class GenshinAvatar {
|
|||||||
public GenshinAvatar(AvatarData data) {
|
public GenshinAvatar(AvatarData data) {
|
||||||
this();
|
this();
|
||||||
this.avatarId = data.getId();
|
this.avatarId = data.getId();
|
||||||
|
this.nameCardRewardId = data.getNameCardRewardId();
|
||||||
|
this.nameCardId = data.getNameCardId();
|
||||||
this.data = data;
|
this.data = data;
|
||||||
this.bornTime = (int) (System.currentTimeMillis() / 1000);
|
this.bornTime = (int) (System.currentTimeMillis() / 1000);
|
||||||
this.flyCloak = 140001;
|
this.flyCloak = 140001;
|
||||||
@ -169,6 +177,14 @@ public class GenshinAvatar {
|
|||||||
this.satiation = satiation;
|
this.satiation = satiation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getNameCardRewardId() {
|
||||||
|
return nameCardRewardId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNameCardRewardId(int nameCardRewardId) {
|
||||||
|
this.nameCardRewardId = nameCardRewardId;
|
||||||
|
}
|
||||||
|
|
||||||
public int getSatiationPenalty() {
|
public int getSatiationPenalty() {
|
||||||
return satiationPenalty;
|
return satiationPenalty;
|
||||||
}
|
}
|
||||||
@ -281,6 +297,30 @@ public class GenshinAvatar {
|
|||||||
return fetters;
|
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 int getNameCardId() {
|
||||||
|
return nameCardId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNameCardId(int nameCardId) {
|
||||||
|
this.nameCardId = nameCardId;
|
||||||
|
}
|
||||||
|
|
||||||
public float getCurrentHp() {
|
public float getCurrentHp() {
|
||||||
return currentHp;
|
return currentHp;
|
||||||
}
|
}
|
||||||
@ -403,6 +443,8 @@ public class GenshinAvatar {
|
|||||||
|
|
||||||
// Fetters
|
// Fetters
|
||||||
this.setFetterList(data.getFetters());
|
this.setFetterList(data.getFetters());
|
||||||
|
this.setNameCardRewardId(data.getNameCardRewardId());
|
||||||
|
this.setNameCardId(data.getNameCardId());
|
||||||
|
|
||||||
// Get hp percent, set to 100% if none
|
// 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);
|
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);
|
||||||
@ -702,8 +744,8 @@ public class GenshinAvatar {
|
|||||||
|
|
||||||
public AvatarInfo toProto() {
|
public AvatarInfo toProto() {
|
||||||
AvatarFetterInfo.Builder avatarFetter = AvatarFetterInfo.newBuilder()
|
AvatarFetterInfo.Builder avatarFetter = AvatarFetterInfo.newBuilder()
|
||||||
.setExpLevel(10)
|
.setExpLevel(this.getFetterLevel())
|
||||||
.setExpNumber(6325); // Highest Level
|
.setExpNumber(this.getFetterExp());
|
||||||
|
|
||||||
if (this.getFetterList() != null) {
|
if (this.getFetterList() != null) {
|
||||||
for (int i = 0; i < this.getFetterList().size(); i++) {
|
for (int i = 0; i < this.getFetterList().size(); i++) {
|
||||||
@ -715,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()
|
AvatarInfo.Builder avatarInfo = AvatarInfo.newBuilder()
|
||||||
.setAvatarId(this.getAvatarId())
|
.setAvatarId(this.getAvatarId())
|
||||||
.setGuid(this.getGuid())
|
.setGuid(this.getGuid())
|
||||||
|
@ -90,7 +90,7 @@ public class GenshinItem {
|
|||||||
|
|
||||||
// Equip data
|
// Equip data
|
||||||
if (getItemType() == ItemType.ITEM_WEAPON) {
|
if (getItemType() == ItemType.ITEM_WEAPON) {
|
||||||
this.level = 1;
|
this.level = this.count > 1 ? this.count : 1;
|
||||||
this.affixes = new ArrayList<>(2);
|
this.affixes = new ArrayList<>(2);
|
||||||
if (getItemData().getSkillAffix() != null) {
|
if (getItemData().getSkillAffix() != null) {
|
||||||
for (int skillAffix : getItemData().getSkillAffix()) {
|
for (int skillAffix : getItemData().getSkillAffix()) {
|
||||||
|
@ -711,6 +711,31 @@ public class InventoryManager {
|
|||||||
player.sendPacket(new PacketAvatarUpgradeRsp(avatar, oldLevel, oldPropMap));
|
player.sendPacket(new PacketAvatarUpgradeRsp(avatar, oldLevel, oldPropMap));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void upgradeAvatarFetterLevel(GenshinPlayer player, GenshinAvatar avatar, int expGain) {
|
||||||
|
// May work. Not test.
|
||||||
|
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);
|
||||||
|
|
||||||
|
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) {
|
public void upgradeAvatarSkill(GenshinPlayer player, long guid, int skillId) {
|
||||||
// Sanity checks
|
// Sanity checks
|
||||||
GenshinAvatar avatar = player.getAvatars().getAvatarByGuid(guid);
|
GenshinAvatar avatar = player.getAvatars().getAvatarByGuid(guid);
|
||||||
|
@ -0,0 +1,53 @@
|
|||||||
|
package emu.grasscutter.server.packet.recv;
|
||||||
|
|
||||||
|
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;
|
||||||
|
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();
|
||||||
|
|
||||||
|
GenshinAvatar avatar = session
|
||||||
|
.getPlayer()
|
||||||
|
.getAvatars()
|
||||||
|
.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(), rewardId));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GenshinItem item = new GenshinItem(cardId);
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
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;
|
||||||
|
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<RewardItemData> rewardItems = GenshinData.getRewardDataMap().get(rewardId).getRewardItemList();
|
||||||
|
List<GenshinItem> 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));
|
||||||
|
Set<Integer> rewardedLevels = session.getPlayer().getRewardedLevels();
|
||||||
|
rewardedLevels.add(level);
|
||||||
|
session.getPlayer().setRewardedLevels(rewardedLevels);
|
||||||
|
}
|
||||||
|
|
||||||
|
session.send(new PacketTakePlayerLevelRewardRsp(level, rewardId));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
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())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int rewardId = avatar.getNameCardRewardId();
|
||||||
|
int cardId = avatar.getNameCardId();
|
||||||
|
|
||||||
|
if (avatar.getPlayer().getNameCardList().contains(cardId)) {
|
||||||
|
avatarFetter.addRewardedFetterLevelList(rewardId);
|
||||||
|
}
|
||||||
|
|
||||||
|
AvatarFetterInfo avatarFetterInfo = avatarFetter.build();
|
||||||
|
|
||||||
|
AvatarFetterDataNotify proto = AvatarFetterDataNotify.newBuilder()
|
||||||
|
.putFetterInfoMap(avatar.getGuid(), avatarFetterInfo)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
this.setData(proto);
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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<Integer> rewardedLevels) {
|
||||||
|
super(PacketOpcodes.PlayerLevelRewardUpdateNotify);
|
||||||
|
|
||||||
|
PlayerLevelRewardUpdateNotify.Builder proto = PlayerLevelRewardUpdateNotify.newBuilder();
|
||||||
|
|
||||||
|
for (Integer level : rewardedLevels) {
|
||||||
|
proto.addLevelList(level);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setData(proto.build());
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -74,10 +74,11 @@ for /f "tokens=2*" %%a in ('reg query "HKCU\Software\Microsoft\Windows\CurrentVe
|
|||||||
|
|
||||||
@rem TODO: External proxy when ORIG_PROXY_ENABLE == 0x1
|
@rem TODO: External proxy when ORIG_PROXY_ENABLE == 0x1
|
||||||
echo set ws = createobject("wscript.shell") > "%temp%\proxy.vbs"
|
echo set ws = createobject("wscript.shell") > "%temp%\proxy.vbs"
|
||||||
|
|
||||||
if not "%MITMDUMP_PATH%" == "" (
|
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)^&"%CUR_PATH%%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 --allow-hosts "^&chr(34)^&".*\.yuanshen\.com|.*\.mihoyo\.com|.*\.hoyoverse\.com"^&chr(34),0 >> "%temp%\proxy.vbs"
|
||||||
"%temp%\proxy.vbs"
|
"%temp%\proxy.vbs"
|
||||||
del /f /q "%temp%\proxy.vbs" >nul 2>nul
|
del /f /q "%temp%\proxy.vbs" >nul 2>nul
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user