mirror of
https://github.com/Grasscutters/Grasscutter.git
synced 2025-01-24 17:53:52 +08:00
Merge remote-tracking branch 'origin/development' into development
This commit is contained in:
commit
72b5ec210c
@ -1,5 +1,7 @@
|
|||||||
package emu.grasscutter;
|
package emu.grasscutter;
|
||||||
|
|
||||||
|
import emu.grasscutter.Grasscutter.ServerDebugMode;
|
||||||
|
import emu.grasscutter.Grasscutter.ServerRunMode;
|
||||||
import emu.grasscutter.game.mail.Mail;
|
import emu.grasscutter.game.mail.Mail;
|
||||||
|
|
||||||
public final class Config {
|
public final class Config {
|
||||||
@ -15,8 +17,8 @@ public final class Config {
|
|||||||
public String SCRIPTS_FOLDER = "./resources/Scripts/";
|
public String SCRIPTS_FOLDER = "./resources/Scripts/";
|
||||||
public String PLUGINS_FOLDER = "./plugins/";
|
public String PLUGINS_FOLDER = "./plugins/";
|
||||||
|
|
||||||
public String DebugMode = "NONE"; // ALL, MISSING, NONE
|
public ServerDebugMode DebugMode = ServerDebugMode.NONE; // ALL, MISSING, NONE
|
||||||
public String RunMode = "HYBRID"; // HYBRID, DISPATCH_ONLY, GAME_ONLY
|
public ServerRunMode RunMode = ServerRunMode.HYBRID; // HYBRID, DISPATCH_ONLY, GAME_ONLY
|
||||||
public GameServerOptions GameServer = new GameServerOptions();
|
public GameServerOptions GameServer = new GameServerOptions();
|
||||||
public DispatchServerOptions DispatchServer = new DispatchServerOptions();
|
public DispatchServerOptions DispatchServer = new DispatchServerOptions();
|
||||||
|
|
||||||
|
@ -35,7 +35,6 @@ public final class Grasscutter {
|
|||||||
|
|
||||||
private static int day; // Current day of week
|
private static int day; // Current day of week
|
||||||
|
|
||||||
public static RunMode MODE = RunMode.BOTH;
|
|
||||||
private static DispatchServer dispatchServer;
|
private static DispatchServer dispatchServer;
|
||||||
private static GameServer gameServer;
|
private static GameServer gameServer;
|
||||||
private static PluginManager pluginManager;
|
private static PluginManager pluginManager;
|
||||||
@ -58,8 +57,6 @@ public final class Grasscutter {
|
|||||||
|
|
||||||
for (String arg : args) {
|
for (String arg : args) {
|
||||||
switch (arg.toLowerCase()) {
|
switch (arg.toLowerCase()) {
|
||||||
case "-auth" -> MODE = RunMode.AUTH;
|
|
||||||
case "-game" -> MODE = RunMode.GAME;
|
|
||||||
case "-handbook" -> {
|
case "-handbook" -> {
|
||||||
Tools.createGmHandbook(); return;
|
Tools.createGmHandbook(); return;
|
||||||
}
|
}
|
||||||
@ -85,12 +82,12 @@ public final class Grasscutter {
|
|||||||
gameServer = new GameServer(new InetSocketAddress(getConfig().getGameServerOptions().Ip, getConfig().getGameServerOptions().Port));
|
gameServer = new GameServer(new InetSocketAddress(getConfig().getGameServerOptions().Ip, getConfig().getGameServerOptions().Port));
|
||||||
|
|
||||||
// Start servers.
|
// Start servers.
|
||||||
if(getConfig().RunMode.equalsIgnoreCase("HYBRID")) {
|
if (getConfig().RunMode == ServerRunMode.HYBRID) {
|
||||||
dispatchServer.start();
|
dispatchServer.start();
|
||||||
gameServer.start();
|
gameServer.start();
|
||||||
} else if (getConfig().RunMode.equalsIgnoreCase("DISPATCH_ONLY")) {
|
} else if (getConfig().RunMode == ServerRunMode.DISPATCH_ONLY) {
|
||||||
dispatchServer.start();
|
dispatchServer.start();
|
||||||
} else if (getConfig().RunMode.equalsIgnoreCase("GAME_ONLY")) {
|
} else if (getConfig().RunMode == ServerRunMode.GAME_ONLY) {
|
||||||
gameServer.start();
|
gameServer.start();
|
||||||
} else {
|
} else {
|
||||||
getLogger().error("Invalid server run mode. " + getConfig().RunMode);
|
getLogger().error("Invalid server run mode. " + getConfig().RunMode);
|
||||||
@ -140,7 +137,7 @@ public final class Grasscutter {
|
|||||||
try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) {
|
try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) {
|
||||||
while ((input = br.readLine()) != null) {
|
while ((input = br.readLine()) != null) {
|
||||||
try {
|
try {
|
||||||
if(getConfig().RunMode.equalsIgnoreCase("DISPATCH_ONLY")) {
|
if (getConfig().RunMode == ServerRunMode.DISPATCH_ONLY) {
|
||||||
getLogger().error("Commands are not supported in dispatch only mode.");
|
getLogger().error("Commands are not supported in dispatch only mode.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -155,12 +152,6 @@ public final class Grasscutter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum RunMode {
|
|
||||||
BOTH,
|
|
||||||
AUTH,
|
|
||||||
GAME
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Config getConfig() {
|
public static Config getConfig() {
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
@ -193,4 +184,12 @@ public final class Grasscutter {
|
|||||||
public static int getCurrentDayOfWeek() {
|
public static int getCurrentDayOfWeek() {
|
||||||
return day;
|
return day;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum ServerRunMode {
|
||||||
|
HYBRID, DISPATCH_ONLY, GAME_ONLY
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ServerDebugMode {
|
||||||
|
ALL, MISSING, NONE
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -307,18 +307,7 @@ public class ResourceLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (Entry<String, OpenConfigData[]> e : config.entrySet()) {
|
for (Entry<String, OpenConfigData[]> e : config.entrySet()) {
|
||||||
List<String> abilityList = new ArrayList<>();
|
OpenConfigEntry entry = new OpenConfigEntry(e.getKey(), e.getValue());
|
||||||
int extraTalentIndex = 0;
|
|
||||||
|
|
||||||
for (OpenConfigData entry : e.getValue()) {
|
|
||||||
if (entry.$type.contains("AddAbility")) {
|
|
||||||
abilityList.add(entry.abilityName);
|
|
||||||
} else if (entry.talentIndex > 0) {
|
|
||||||
extraTalentIndex = entry.talentIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
OpenConfigEntry entry = new OpenConfigEntry(e.getKey(), abilityList, extraTalentIndex);
|
|
||||||
map.put(entry.getName(), entry);
|
map.put(entry.getName(), entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -354,9 +343,11 @@ public class ResourceLoader {
|
|||||||
public OpenConfigData[] data;
|
public OpenConfigData[] data;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class OpenConfigData {
|
public static class OpenConfigData {
|
||||||
public String $type;
|
public String $type;
|
||||||
public String abilityName;
|
public String abilityName;
|
||||||
public int talentIndex;
|
public int talentIndex;
|
||||||
|
public int skillID;
|
||||||
|
public int pointDelta;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,39 @@
|
|||||||
package emu.grasscutter.data.custom;
|
package emu.grasscutter.data.custom;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import emu.grasscutter.data.ResourceLoader.OpenConfigData;
|
||||||
|
|
||||||
public class OpenConfigEntry {
|
public class OpenConfigEntry {
|
||||||
private String name;
|
private String name;
|
||||||
private String[] addAbilities;
|
private String[] addAbilities;
|
||||||
private int extraTalentIndex;
|
private int extraTalentIndex;
|
||||||
|
private SkillPointModifier[] skillPointModifiers;
|
||||||
|
|
||||||
public OpenConfigEntry(String name, List<String> abilityList, int extraTalentIndex) {
|
public OpenConfigEntry(String name, OpenConfigData[] data) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.extraTalentIndex = extraTalentIndex;
|
|
||||||
|
List<String> abilityList = new ArrayList<>();
|
||||||
|
List<SkillPointModifier> modList = new ArrayList<>();
|
||||||
|
|
||||||
|
for (OpenConfigData entry : data) {
|
||||||
|
if (entry.$type.contains("AddAbility")) {
|
||||||
|
abilityList.add(entry.abilityName);
|
||||||
|
} else if (entry.talentIndex > 0) {
|
||||||
|
this.extraTalentIndex = entry.talentIndex;
|
||||||
|
} else if (entry.$type.contains("ModifySkillPoint")) {
|
||||||
|
modList.add(new SkillPointModifier(entry.skillID, entry.pointDelta));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (abilityList.size() > 0) {
|
if (abilityList.size() > 0) {
|
||||||
this.addAbilities = abilityList.toArray(new String[0]);
|
this.addAbilities = abilityList.toArray(new String[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (modList.size() > 0) {
|
||||||
|
this.skillPointModifiers = modList.toArray(new SkillPointModifier[0]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
@ -26,4 +47,26 @@ public class OpenConfigEntry {
|
|||||||
public int getExtraTalentIndex() {
|
public int getExtraTalentIndex() {
|
||||||
return extraTalentIndex;
|
return extraTalentIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SkillPointModifier[] getSkillPointModifiers() {
|
||||||
|
return skillPointModifiers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SkillPointModifier {
|
||||||
|
private int skillId;
|
||||||
|
private int delta;
|
||||||
|
|
||||||
|
public SkillPointModifier(int skillId, int delta) {
|
||||||
|
this.skillId = skillId;
|
||||||
|
this.delta = delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSkillId() {
|
||||||
|
return skillId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDelta() {
|
||||||
|
return delta;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ import dev.morphia.Morphia;
|
|||||||
import dev.morphia.mapping.MapperOptions;
|
import dev.morphia.mapping.MapperOptions;
|
||||||
import dev.morphia.query.experimental.filters.Filters;
|
import dev.morphia.query.experimental.filters.Filters;
|
||||||
import emu.grasscutter.Grasscutter;
|
import emu.grasscutter.Grasscutter;
|
||||||
|
import emu.grasscutter.Grasscutter.ServerRunMode;
|
||||||
import emu.grasscutter.game.Account;
|
import emu.grasscutter.game.Account;
|
||||||
import emu.grasscutter.game.avatar.Avatar;
|
import emu.grasscutter.game.avatar.Avatar;
|
||||||
import emu.grasscutter.game.friends.Friendship;
|
import emu.grasscutter.game.friends.Friendship;
|
||||||
@ -41,7 +42,7 @@ public final class DatabaseManager {
|
|||||||
// Yes. I very dislike this method. However, this will be good for now.
|
// Yes. I very dislike this method. However, this will be good for now.
|
||||||
// TODO: Add dispatch routes for player account management
|
// TODO: Add dispatch routes for player account management
|
||||||
public static Datastore getAccountDatastore() {
|
public static Datastore getAccountDatastore() {
|
||||||
if(Grasscutter.getConfig().RunMode.equalsIgnoreCase("GAME_ONLY")) {
|
if(Grasscutter.getConfig().RunMode == ServerRunMode.GAME_ONLY) {
|
||||||
return dispatchDatastore;
|
return dispatchDatastore;
|
||||||
} else {
|
} else {
|
||||||
return datastore;
|
return datastore;
|
||||||
@ -77,7 +78,7 @@ public final class DatabaseManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(Grasscutter.getConfig().RunMode.equalsIgnoreCase("GAME_ONLY")) {
|
if(Grasscutter.getConfig().RunMode == ServerRunMode.GAME_ONLY) {
|
||||||
dispatchMongoClient = MongoClients.create(Grasscutter.getConfig().getGameServerOptions().DispatchServerDatabaseUrl);
|
dispatchMongoClient = MongoClients.create(Grasscutter.getConfig().getGameServerOptions().DispatchServerDatabaseUrl);
|
||||||
dispatchDatastore = Morphia.createDatastore(dispatchMongoClient, Grasscutter.getConfig().getGameServerOptions().DispatchServerDatabaseCollection);
|
dispatchDatastore = Morphia.createDatastore(dispatchMongoClient, Grasscutter.getConfig().getGameServerOptions().DispatchServerDatabaseCollection);
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import java.util.HashMap;
|
|||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.bson.types.ObjectId;
|
import org.bson.types.ObjectId;
|
||||||
@ -18,6 +19,7 @@ import dev.morphia.annotations.Transient;
|
|||||||
import emu.grasscutter.data.GameData;
|
import emu.grasscutter.data.GameData;
|
||||||
import emu.grasscutter.data.common.FightPropData;
|
import emu.grasscutter.data.common.FightPropData;
|
||||||
import emu.grasscutter.data.custom.OpenConfigEntry;
|
import emu.grasscutter.data.custom.OpenConfigEntry;
|
||||||
|
import emu.grasscutter.data.custom.OpenConfigEntry.SkillPointModifier;
|
||||||
import emu.grasscutter.data.def.AvatarData;
|
import emu.grasscutter.data.def.AvatarData;
|
||||||
import emu.grasscutter.data.def.AvatarPromoteData;
|
import emu.grasscutter.data.def.AvatarPromoteData;
|
||||||
import emu.grasscutter.data.def.AvatarSkillData;
|
import emu.grasscutter.data.def.AvatarSkillData;
|
||||||
@ -46,6 +48,7 @@ import emu.grasscutter.game.props.FightProperty;
|
|||||||
import emu.grasscutter.game.props.PlayerProperty;
|
import emu.grasscutter.game.props.PlayerProperty;
|
||||||
import emu.grasscutter.net.proto.AvatarFetterInfoOuterClass.AvatarFetterInfo;
|
import emu.grasscutter.net.proto.AvatarFetterInfoOuterClass.AvatarFetterInfo;
|
||||||
import emu.grasscutter.net.proto.AvatarInfoOuterClass.AvatarInfo;
|
import emu.grasscutter.net.proto.AvatarInfoOuterClass.AvatarInfo;
|
||||||
|
import emu.grasscutter.net.proto.AvatarSkillInfoOuterClass.AvatarSkillInfo;
|
||||||
import emu.grasscutter.net.proto.FetterDataOuterClass.FetterData;
|
import emu.grasscutter.net.proto.FetterDataOuterClass.FetterData;
|
||||||
import emu.grasscutter.net.proto.ShowAvatarInfoOuterClass;
|
import emu.grasscutter.net.proto.ShowAvatarInfoOuterClass;
|
||||||
import emu.grasscutter.net.proto.ShowAvatarInfoOuterClass.ShowAvatarInfo;
|
import emu.grasscutter.net.proto.ShowAvatarInfoOuterClass.ShowAvatarInfo;
|
||||||
@ -83,6 +86,7 @@ public class Avatar {
|
|||||||
private List<Integer> fetters;
|
private List<Integer> fetters;
|
||||||
|
|
||||||
private Map<Integer, Integer> skillLevelMap; // Talent levels
|
private Map<Integer, Integer> skillLevelMap; // Talent levels
|
||||||
|
private Map<Integer, Integer> skillExtraChargeMap; // Charges
|
||||||
private Map<Integer, Integer> proudSkillBonusMap; // Talent bonus levels (from const)
|
private Map<Integer, Integer> proudSkillBonusMap; // Talent bonus levels (from const)
|
||||||
private int skillDepotId;
|
private int skillDepotId;
|
||||||
private int coreProudSkillLevel; // Constellation level
|
private int coreProudSkillLevel; // Constellation level
|
||||||
@ -123,6 +127,7 @@ public class Avatar {
|
|||||||
this.flyCloak = 140001;
|
this.flyCloak = 140001;
|
||||||
|
|
||||||
this.skillLevelMap = new HashMap<>();
|
this.skillLevelMap = new HashMap<>();
|
||||||
|
this.skillExtraChargeMap = new HashMap<>();
|
||||||
this.talentIdList = new HashSet<>();
|
this.talentIdList = new HashSet<>();
|
||||||
this.proudSkillList = new HashSet<>();
|
this.proudSkillList = new HashSet<>();
|
||||||
|
|
||||||
@ -284,6 +289,13 @@ public class Avatar {
|
|||||||
return skillLevelMap;
|
return skillLevelMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Map<Integer, Integer> getSkillExtraChargeMap() {
|
||||||
|
if (skillExtraChargeMap == null) {
|
||||||
|
skillExtraChargeMap = new HashMap<>();
|
||||||
|
}
|
||||||
|
return skillExtraChargeMap;
|
||||||
|
}
|
||||||
|
|
||||||
public Map<Integer, Integer> getProudSkillBonusMap() {
|
public Map<Integer, Integer> getProudSkillBonusMap() {
|
||||||
return proudSkillBonusMap;
|
return proudSkillBonusMap;
|
||||||
}
|
}
|
||||||
@ -676,9 +688,10 @@ public class Avatar {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void recalcProudSkillBonusMap() {
|
public void recalcConstellations() {
|
||||||
// Clear first
|
// Clear first
|
||||||
this.getProudSkillBonusMap().clear();
|
this.getProudSkillBonusMap().clear();
|
||||||
|
this.getSkillExtraChargeMap().clear();
|
||||||
|
|
||||||
// Sanity checks
|
// Sanity checks
|
||||||
if (getData() == null || getData().getSkillDepot() == null) {
|
if (getData() == null || getData().getSkillDepot() == null) {
|
||||||
@ -699,6 +712,21 @@ public class Avatar {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if we can add charges to a skill
|
||||||
|
if (entry.getSkillPointModifiers() != null) {
|
||||||
|
for (SkillPointModifier mod : entry.getSkillPointModifiers()) {
|
||||||
|
AvatarSkillData skillData = GameData.getAvatarSkillDataMap().get(mod.getSkillId());
|
||||||
|
|
||||||
|
if (skillData == null) continue;
|
||||||
|
|
||||||
|
int charges = skillData.getMaxChargeNum() + mod.getDelta();
|
||||||
|
|
||||||
|
this.getSkillExtraChargeMap().put(mod.getSkillId(), charges);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if a skill can be boosted by +3 levels
|
||||||
int skillId = 0;
|
int skillId = 0;
|
||||||
|
|
||||||
if (entry.getExtraTalentIndex() == 2 && this.getData().getSkillDepot().getSkills().size() >= 2) {
|
if (entry.getExtraTalentIndex() == 2 && this.getData().getSkillDepot().getSkills().size() >= 2) {
|
||||||
@ -788,6 +816,10 @@ public class Avatar {
|
|||||||
.setWearingFlycloakId(this.getFlyCloak())
|
.setWearingFlycloakId(this.getFlyCloak())
|
||||||
.setCostumeId(this.getCostume());
|
.setCostumeId(this.getCostume());
|
||||||
|
|
||||||
|
for (Entry<Integer, Integer> entry : this.getSkillExtraChargeMap().entrySet()) {
|
||||||
|
avatarInfo.putSkillMap(entry.getKey(), AvatarSkillInfo.newBuilder().setMaxChargeCount(entry.getValue()).build());
|
||||||
|
}
|
||||||
|
|
||||||
for (GameItem item : this.getEquips().values()) {
|
for (GameItem item : this.getEquips().values()) {
|
||||||
avatarInfo.addEquipGuidList(item.getGuid());
|
avatarInfo.addEquipGuidList(item.getGuid());
|
||||||
}
|
}
|
||||||
|
@ -148,7 +148,7 @@ public class AvatarStorage implements Iterable<Avatar> {
|
|||||||
avatar.setOwner(getPlayer());
|
avatar.setOwner(getPlayer());
|
||||||
|
|
||||||
// Force recalc of const boosted skills
|
// Force recalc of const boosted skills
|
||||||
avatar.recalcProudSkillBonusMap();
|
avatar.recalcConstellations();
|
||||||
|
|
||||||
// Add to avatar storage
|
// Add to avatar storage
|
||||||
this.avatars.put(avatar.getAvatarId(), avatar);
|
this.avatars.put(avatar.getAvatarId(), avatar);
|
||||||
|
@ -8,6 +8,7 @@ import java.util.stream.Collectors;
|
|||||||
import emu.grasscutter.data.GameData;
|
import emu.grasscutter.data.GameData;
|
||||||
import emu.grasscutter.data.common.ItemParamData;
|
import emu.grasscutter.data.common.ItemParamData;
|
||||||
import emu.grasscutter.data.custom.OpenConfigEntry;
|
import emu.grasscutter.data.custom.OpenConfigEntry;
|
||||||
|
import emu.grasscutter.data.custom.OpenConfigEntry.SkillPointModifier;
|
||||||
import emu.grasscutter.data.def.AvatarPromoteData;
|
import emu.grasscutter.data.def.AvatarPromoteData;
|
||||||
import emu.grasscutter.data.def.AvatarSkillData;
|
import emu.grasscutter.data.def.AvatarSkillData;
|
||||||
import emu.grasscutter.data.def.AvatarSkillDepotData;
|
import emu.grasscutter.data.def.AvatarSkillDepotData;
|
||||||
@ -835,9 +836,22 @@ public class InventoryManager {
|
|||||||
|
|
||||||
// Proud skill bonus map (Extra skills)
|
// Proud skill bonus map (Extra skills)
|
||||||
OpenConfigEntry entry = GameData.getOpenConfigEntries().get(talentData.getOpenConfig());
|
OpenConfigEntry entry = GameData.getOpenConfigEntries().get(talentData.getOpenConfig());
|
||||||
if (entry != null && entry.getExtraTalentIndex() > 0) {
|
if (entry != null) {
|
||||||
avatar.recalcProudSkillBonusMap();
|
if (entry.getExtraTalentIndex() > 0) {
|
||||||
|
// Check if new constellation adds +3 to a skill level
|
||||||
|
avatar.recalcConstellations();
|
||||||
|
// Packet
|
||||||
player.sendPacket(new PacketProudSkillExtraLevelNotify(avatar, entry.getExtraTalentIndex()));
|
player.sendPacket(new PacketProudSkillExtraLevelNotify(avatar, entry.getExtraTalentIndex()));
|
||||||
|
} else if (entry.getSkillPointModifiers() != null) {
|
||||||
|
// Check if new constellation adds skill charges
|
||||||
|
avatar.recalcConstellations();
|
||||||
|
// Packet
|
||||||
|
for (SkillPointModifier mod : entry.getSkillPointModifiers()) {
|
||||||
|
player.sendPacket(
|
||||||
|
new PacketAvatarSkillMaxChargeCountNotify(avatar, mod.getSkillId(), avatar.getSkillExtraChargeMap().getOrDefault(mod.getSkillId(), 0))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recalc + save avatar
|
// Recalc + save avatar
|
||||||
|
@ -4,6 +4,7 @@ import java.io.IOException;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
import emu.grasscutter.Grasscutter;
|
import emu.grasscutter.Grasscutter;
|
||||||
|
import emu.grasscutter.Grasscutter.ServerDebugMode;
|
||||||
import express.http.HttpContextHandler;
|
import express.http.HttpContextHandler;
|
||||||
import express.http.Request;
|
import express.http.Request;
|
||||||
import express.http.Response;
|
import express.http.Response;
|
||||||
@ -30,8 +31,8 @@ public final class DispatchHttpJsonHandler implements HttpContextHandler {
|
|||||||
@Override
|
@Override
|
||||||
public void handle(Request req, Response res) throws IOException {
|
public void handle(Request req, Response res) throws IOException {
|
||||||
// Checking for ALL here isn't required as when ALL is enabled enableDevLogging() gets enabled
|
// Checking for ALL here isn't required as when ALL is enabled enableDevLogging() gets enabled
|
||||||
if(Grasscutter.getConfig().DebugMode.equalsIgnoreCase("MISSING") && Arrays.stream(missingRoutes).anyMatch(x -> x == req.baseUrl())) {
|
if(Grasscutter.getConfig().DebugMode == ServerDebugMode.MISSING && Arrays.stream(missingRoutes).anyMatch(x -> x == req.baseUrl())) {
|
||||||
Grasscutter.getLogger().info(String.format("[Dispatch] Client %s %s request: ", req.ip(), req.method(), req.baseUrl()) + (Grasscutter.getConfig().DebugMode.equalsIgnoreCase("MISSING") ? "(MISSING)" : ""));
|
Grasscutter.getLogger().info(String.format("[Dispatch] Client %s %s request: ", req.ip(), req.method(), req.baseUrl()) + (Grasscutter.getConfig().DebugMode == ServerDebugMode.MISSING ? "(MISSING)" : ""));
|
||||||
}
|
}
|
||||||
res.send(response);
|
res.send(response);
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,8 @@ import com.google.protobuf.ByteString;
|
|||||||
|
|
||||||
import emu.grasscutter.Config;
|
import emu.grasscutter.Config;
|
||||||
import emu.grasscutter.Grasscutter;
|
import emu.grasscutter.Grasscutter;
|
||||||
|
import emu.grasscutter.Grasscutter.ServerDebugMode;
|
||||||
|
import emu.grasscutter.Grasscutter.ServerRunMode;
|
||||||
import emu.grasscutter.database.DatabaseHelper;
|
import emu.grasscutter.database.DatabaseHelper;
|
||||||
import emu.grasscutter.game.Account;
|
import emu.grasscutter.game.Account;
|
||||||
import emu.grasscutter.net.proto.QueryCurrRegionHttpRspOuterClass.QueryCurrRegionHttpRsp;
|
import emu.grasscutter.net.proto.QueryCurrRegionHttpRspOuterClass.QueryCurrRegionHttpRsp;
|
||||||
@ -62,7 +64,7 @@ public final class DispatchServer {
|
|||||||
|
|
||||||
public QueryCurrRegionHttpRsp getCurrRegion() {
|
public QueryCurrRegionHttpRsp getCurrRegion() {
|
||||||
// Needs to be fixed by having the game servers connect to the dispatch server.
|
// Needs to be fixed by having the game servers connect to the dispatch server.
|
||||||
if (Grasscutter.getConfig().RunMode.equalsIgnoreCase("HYBRID")) {
|
if (Grasscutter.getConfig().RunMode == ServerRunMode.HYBRID) {
|
||||||
return regions.get(defaultServerName).parsedRegionQuery;
|
return regions.get(defaultServerName).parsedRegionQuery;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,7 +100,7 @@ public final class DispatchServer {
|
|||||||
|
|
||||||
List<RegionSimpleInfo> servers = new ArrayList<>();
|
List<RegionSimpleInfo> servers = new ArrayList<>();
|
||||||
List<String> usedNames = new ArrayList<>(); // List to check for potential naming conflicts
|
List<String> usedNames = new ArrayList<>(); // List to check for potential naming conflicts
|
||||||
if (Grasscutter.getConfig().RunMode.equalsIgnoreCase("HYBRID")) { // Automatically add the game server if in
|
if (Grasscutter.getConfig().RunMode == ServerRunMode.HYBRID) { // Automatically add the game server if in
|
||||||
// hybrid mode
|
// hybrid mode
|
||||||
RegionSimpleInfo server = RegionSimpleInfo.newBuilder()
|
RegionSimpleInfo server = RegionSimpleInfo.newBuilder()
|
||||||
.setName("os_usa")
|
.setName("os_usa")
|
||||||
@ -233,7 +235,7 @@ public final class DispatchServer {
|
|||||||
});
|
});
|
||||||
|
|
||||||
config.enforceSsl = Grasscutter.getConfig().getDispatchOptions().UseSSL;
|
config.enforceSsl = Grasscutter.getConfig().getDispatchOptions().UseSSL;
|
||||||
if(Grasscutter.getConfig().DebugMode.equalsIgnoreCase("ALL")) {
|
if(Grasscutter.getConfig().DebugMode == ServerDebugMode.ALL) {
|
||||||
config.enableDevLogging();
|
config.enableDevLogging();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -241,7 +243,7 @@ public final class DispatchServer {
|
|||||||
httpServer.get("/", (req, res) -> res.send("Welcome to Grasscutter"));
|
httpServer.get("/", (req, res) -> res.send("Welcome to Grasscutter"));
|
||||||
|
|
||||||
httpServer.raw().error(404, ctx -> {
|
httpServer.raw().error(404, ctx -> {
|
||||||
if(Grasscutter.getConfig().DebugMode.equalsIgnoreCase("MISSING")) {
|
if(Grasscutter.getConfig().DebugMode == ServerDebugMode.MISSING) {
|
||||||
Grasscutter.getLogger().info(String.format("[Dispatch] Potential unhandled %s request: %s", ctx.method(), ctx.url()));
|
Grasscutter.getLogger().info(String.format("[Dispatch] Potential unhandled %s request: %s", ctx.method(), ctx.url()));
|
||||||
}
|
}
|
||||||
ctx.contentType("text/html");
|
ctx.contentType("text/html");
|
||||||
@ -317,6 +319,9 @@ public final class DispatchServer {
|
|||||||
responseData.data.account.uid = account.getId();
|
responseData.data.account.uid = account.getId();
|
||||||
responseData.data.account.token = account.generateSessionKey();
|
responseData.data.account.token = account.generateSessionKey();
|
||||||
responseData.data.account.email = account.getEmail();
|
responseData.data.account.email = account.getEmail();
|
||||||
|
if (responseData.data.account.email == null) {
|
||||||
|
responseData.data.account.email = "";
|
||||||
|
}
|
||||||
|
|
||||||
Grasscutter.getLogger()
|
Grasscutter.getLogger()
|
||||||
.info(String.format("[Dispatch] Client %s failed to log in: Account %s created",
|
.info(String.format("[Dispatch] Client %s failed to log in: Account %s created",
|
||||||
@ -341,6 +346,9 @@ public final class DispatchServer {
|
|||||||
responseData.data.account.uid = account.getId();
|
responseData.data.account.uid = account.getId();
|
||||||
responseData.data.account.token = account.generateSessionKey();
|
responseData.data.account.token = account.generateSessionKey();
|
||||||
responseData.data.account.email = account.getEmail();
|
responseData.data.account.email = account.getEmail();
|
||||||
|
if (responseData.data.account.email == null) {
|
||||||
|
responseData.data.account.email = "";
|
||||||
|
}
|
||||||
|
|
||||||
Grasscutter.getLogger().info(String.format("[Dispatch] Client %s logged in as %s", req.ip(),
|
Grasscutter.getLogger().info(String.format("[Dispatch] Client %s logged in as %s", req.ip(),
|
||||||
responseData.data.account.uid));
|
responseData.data.account.uid));
|
||||||
@ -381,6 +389,9 @@ public final class DispatchServer {
|
|||||||
responseData.data.account.uid = requestData.uid;
|
responseData.data.account.uid = requestData.uid;
|
||||||
responseData.data.account.token = requestData.token;
|
responseData.data.account.token = requestData.token;
|
||||||
responseData.data.account.email = account.getEmail();
|
responseData.data.account.email = account.getEmail();
|
||||||
|
if (responseData.data.account.email == null) { // null will trigger crash in some client
|
||||||
|
responseData.data.account.email = "";
|
||||||
|
}
|
||||||
|
|
||||||
Grasscutter.getLogger().info(String.format("[Dispatch] Client %s logged in via token as %s",
|
Grasscutter.getLogger().info(String.format("[Dispatch] Client %s logged in via token as %s",
|
||||||
req.ip(), responseData.data.account.uid));
|
req.ip(), responseData.data.account.uid));
|
||||||
@ -437,7 +448,8 @@ public final class DispatchServer {
|
|||||||
// hk4e-sdk-os.hoyoverse.com
|
// hk4e-sdk-os.hoyoverse.com
|
||||||
httpServer.get("/hk4e_global/mdk/agreement/api/getAgreementInfos", new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"marketing_agreements\":[]}}"));
|
httpServer.get("/hk4e_global/mdk/agreement/api/getAgreementInfos", new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"marketing_agreements\":[]}}"));
|
||||||
// hk4e-sdk-os.hoyoverse.com
|
// hk4e-sdk-os.hoyoverse.com
|
||||||
httpServer.post("/hk4e_global/combo/granter/api/compareProtocolVersion", new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"modified\":true,\"protocol\":{\"id\":0,\"app_id\":4,\"language\":\"en\",\"user_proto\":\"\",\"priv_proto\":\"\",\"major\":7,\"minimum\":0,\"create_time\":\"0\",\"teenager_proto\":\"\",\"third_proto\":\"\"}}}"));
|
// this could be either GET or POST based on the observation of different clients
|
||||||
|
httpServer.all("/hk4e_global/combo/granter/api/compareProtocolVersion", new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"modified\":true,\"protocol\":{\"id\":0,\"app_id\":4,\"language\":\"en\",\"user_proto\":\"\",\"priv_proto\":\"\",\"major\":7,\"minimum\":0,\"create_time\":\"0\",\"teenager_proto\":\"\",\"third_proto\":\"\"}}}"));
|
||||||
|
|
||||||
// Game data
|
// Game data
|
||||||
// hk4e-api-os.hoyoverse.com
|
// hk4e-api-os.hoyoverse.com
|
||||||
@ -479,6 +491,9 @@ public final class DispatchServer {
|
|||||||
// log-upload-os.mihoyo.com
|
// log-upload-os.mihoyo.com
|
||||||
httpServer.all("/crash/dataUpload", new ClientLogHandler());
|
httpServer.all("/crash/dataUpload", new ClientLogHandler());
|
||||||
|
|
||||||
|
// webstatic-sea.hoyoverse.com
|
||||||
|
httpServer.get("/admin/mi18n/plat_oversea/m202003048/m202003048-version.json", new DispatchHttpJsonHandler("{\"version\":51}"));
|
||||||
|
|
||||||
httpServer.get("/gacha", (req, res) -> res.send("<!doctype html><html lang=\"en\"><head><title>Gacha</title></head><body></body></html>"));
|
httpServer.get("/gacha", (req, res) -> res.send("<!doctype html><html lang=\"en\"><head><title>Gacha</title></head><body></body></html>"));
|
||||||
|
|
||||||
httpServer.listen(Grasscutter.getConfig().getDispatchOptions().Port);
|
httpServer.listen(Grasscutter.getConfig().getDispatchOptions().Port);
|
||||||
|
@ -6,6 +6,7 @@ import emu.grasscutter.server.event.game.ReceivePacketEvent;
|
|||||||
import org.reflections.Reflections;
|
import org.reflections.Reflections;
|
||||||
|
|
||||||
import emu.grasscutter.Grasscutter;
|
import emu.grasscutter.Grasscutter;
|
||||||
|
import emu.grasscutter.Grasscutter.ServerDebugMode;
|
||||||
import emu.grasscutter.net.packet.Opcodes;
|
import emu.grasscutter.net.packet.Opcodes;
|
||||||
import emu.grasscutter.net.packet.PacketHandler;
|
import emu.grasscutter.net.packet.PacketHandler;
|
||||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
@ -88,7 +89,7 @@ public class GameServerPacketHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Log unhandled packets
|
// Log unhandled packets
|
||||||
if (Grasscutter.getConfig().DebugMode.equalsIgnoreCase("MISSING")) {
|
if (Grasscutter.getConfig().DebugMode == ServerDebugMode.MISSING) {
|
||||||
Grasscutter.getLogger().info("Unhandled packet (" + opcode + "): " + emu.grasscutter.net.packet.PacketOpcodesUtil.getOpcodeName(opcode));
|
Grasscutter.getLogger().info("Unhandled packet (" + opcode + "): " + emu.grasscutter.net.packet.PacketOpcodesUtil.getOpcodeName(opcode));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ import java.util.HashSet;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import emu.grasscutter.Grasscutter;
|
import emu.grasscutter.Grasscutter;
|
||||||
|
import emu.grasscutter.Grasscutter.ServerDebugMode;
|
||||||
import emu.grasscutter.game.Account;
|
import emu.grasscutter.game.Account;
|
||||||
import emu.grasscutter.game.player.Player;
|
import emu.grasscutter.game.player.Player;
|
||||||
import emu.grasscutter.net.packet.BasePacket;
|
import emu.grasscutter.net.packet.BasePacket;
|
||||||
@ -163,7 +164,7 @@ public class GameSession extends KcpChannel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Log
|
// Log
|
||||||
if (Grasscutter.getConfig().DebugMode.equalsIgnoreCase("ALL")) {
|
if (Grasscutter.getConfig().DebugMode == ServerDebugMode.ALL) {
|
||||||
logPacket(packet);
|
logPacket(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,7 +231,7 @@ public class GameSession extends KcpChannel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Log packet
|
// Log packet
|
||||||
if (Grasscutter.getConfig().DebugMode.equalsIgnoreCase("ALL")) {
|
if (Grasscutter.getConfig().DebugMode == ServerDebugMode.ALL) {
|
||||||
if (!loopPacket.contains(opcode)) {
|
if (!loopPacket.contains(opcode)) {
|
||||||
Grasscutter.getLogger().info("RECV: " + PacketOpcodesUtil.getOpcodeName(opcode) + " (" + opcode + ")");
|
Grasscutter.getLogger().info("RECV: " + PacketOpcodesUtil.getOpcodeName(opcode) + " (" + opcode + ")");
|
||||||
System.out.println(Utils.bytesToHex(payload));
|
System.out.println(Utils.bytesToHex(payload));
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
package emu.grasscutter.server.packet.recv;
|
||||||
|
|
||||||
|
import emu.grasscutter.net.packet.Opcodes;
|
||||||
|
import emu.grasscutter.net.packet.PacketHandler;
|
||||||
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
|
import emu.grasscutter.net.proto.McoinExchangeHcoinReqOuterClass;
|
||||||
|
import emu.grasscutter.server.game.GameSession;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketMcoinExchangeHcoinRsp;
|
||||||
|
|
||||||
|
@Opcodes(PacketOpcodes.McoinExchangeHcoinReq)
|
||||||
|
public class HandlerMcoinExchangeHcoinReq extends PacketHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
|
||||||
|
McoinExchangeHcoinReqOuterClass.McoinExchangeHcoinReq exchangeReq = McoinExchangeHcoinReqOuterClass.McoinExchangeHcoinReq.parseFrom(payload);
|
||||||
|
|
||||||
|
if (session.getPlayer().getCrystals() < exchangeReq.getMCoinNum() && exchangeReq.getMCoinNum() == exchangeReq.getHCoinNum()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
session.getPlayer().setCrystals(session.getPlayer().getCrystals() - exchangeReq.getMCoinNum());
|
||||||
|
session.getPlayer().setPrimogems(session.getPlayer().getPrimogems() + exchangeReq.getHCoinNum());
|
||||||
|
session.getPlayer().save();
|
||||||
|
|
||||||
|
session.send(new PacketMcoinExchangeHcoinRsp(session.getPlayer().getCrystals(), session.getPlayer().getPrimogems()));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package emu.grasscutter.server.packet.send;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.avatar.Avatar;
|
||||||
|
import emu.grasscutter.net.packet.BasePacket;
|
||||||
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
|
import emu.grasscutter.net.proto.AvatarSkillMaxChargeCountNotifyOuterClass.AvatarSkillMaxChargeCountNotify;
|
||||||
|
|
||||||
|
public class PacketAvatarSkillMaxChargeCountNotify extends BasePacket {
|
||||||
|
|
||||||
|
public PacketAvatarSkillMaxChargeCountNotify(Avatar avatar, int skillId, int maxCharges) {
|
||||||
|
super(PacketOpcodes.AvatarSkillMaxChargeCountNotify);
|
||||||
|
|
||||||
|
AvatarSkillMaxChargeCountNotify proto = AvatarSkillMaxChargeCountNotify.newBuilder()
|
||||||
|
.setAvatarGuid(avatar.getGuid())
|
||||||
|
.setSkillId(skillId)
|
||||||
|
.setMaxChargeCount(maxCharges)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
this.setData(proto);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package emu.grasscutter.server.packet.send;
|
||||||
|
|
||||||
|
import emu.grasscutter.net.packet.BasePacket;
|
||||||
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
|
import emu.grasscutter.net.proto.McoinExchangeHcoinRspOuterClass;
|
||||||
|
|
||||||
|
public class PacketMcoinExchangeHcoinRsp extends BasePacket {
|
||||||
|
|
||||||
|
public PacketMcoinExchangeHcoinRsp(int mcoin, int hcoin) {
|
||||||
|
super(PacketOpcodes.McoinExchangeHcoinRsp);
|
||||||
|
|
||||||
|
McoinExchangeHcoinRspOuterClass.McoinExchangeHcoinRsp mcoinExchangeHcoinRsp = McoinExchangeHcoinRspOuterClass.McoinExchangeHcoinRsp.newBuilder()
|
||||||
|
.setMCoinNum(mcoin)
|
||||||
|
.setHCoinNum(hcoin).build();
|
||||||
|
|
||||||
|
this.setData(mcoinExchangeHcoinRsp);
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,8 @@ package emu.grasscutter.server.packet.send;
|
|||||||
|
|
||||||
import com.google.protobuf.ByteString;
|
import com.google.protobuf.ByteString;
|
||||||
import emu.grasscutter.Grasscutter;
|
import emu.grasscutter.Grasscutter;
|
||||||
|
import emu.grasscutter.Grasscutter.ServerDebugMode;
|
||||||
|
import emu.grasscutter.Grasscutter.ServerRunMode;
|
||||||
import emu.grasscutter.net.packet.BasePacket;
|
import emu.grasscutter.net.packet.BasePacket;
|
||||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
import emu.grasscutter.net.proto.PlayerLoginRspOuterClass.PlayerLoginRsp;
|
import emu.grasscutter.net.proto.PlayerLoginRspOuterClass.PlayerLoginRsp;
|
||||||
@ -25,7 +27,7 @@ public class PacketPlayerLoginRsp extends BasePacket {
|
|||||||
|
|
||||||
RegionInfo info;
|
RegionInfo info;
|
||||||
|
|
||||||
if(Grasscutter.getConfig().RunMode.equalsIgnoreCase("GAME_ONLY")) {
|
if (Grasscutter.getConfig().RunMode == ServerRunMode.GAME_ONLY) {
|
||||||
if (regionCache == null) {
|
if (regionCache == null) {
|
||||||
try {
|
try {
|
||||||
File file = new File(Grasscutter.getConfig().DATA_FOLDER + "query_cur_region.txt");
|
File file = new File(Grasscutter.getConfig().DATA_FOLDER + "query_cur_region.txt");
|
||||||
|
Loading…
Reference in New Issue
Block a user