Commit a8293102 authored by Melledy's avatar Melledy Committed by GitHub
Browse files

Merge branch 'development' into stable

parents 304b9cb8 ecf7a81a
package emu.grasscutter.data.def; package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType;
@ResourceType(name = "ReliquarySetExcelConfigData.json") @ResourceType(name = "ReliquarySetExcelConfigData.json")
public class ReliquarySetData extends GameResource { public class ReliquarySetData extends GameResource {
private int SetId; private int setId;
private int[] SetNeedNum; private int[] setNeedNum;
private int EquipAffixId; private int EquipAffixId;
private int DisableFilter; private int disableFilter;
private int[] ContainsList; private int[] containsList;
@Override @Override
public int getId() { public int getId() {
return SetId; return setId;
} }
public int[] getSetNeedNum() { public int[] getSetNeedNum() {
return SetNeedNum; return setNeedNum;
} }
public int getEquipAffixId() { public int getEquipAffixId() {
...@@ -25,11 +25,11 @@ public class ReliquarySetData extends GameResource { ...@@ -25,11 +25,11 @@ public class ReliquarySetData extends GameResource {
} }
public int getDisableFilter() { public int getDisableFilter() {
return DisableFilter; return disableFilter;
} }
public int[] getContainsList() { public int[] getContainsList() {
return ContainsList; return containsList;
} }
@Override @Override
......
package emu.grasscutter.data.def; package emu.grasscutter.data.excels;
import java.util.List; import java.util.List;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.common.RewardItemData; import emu.grasscutter.data.common.ItemParamData;
@ResourceType(name = "RewardExcelConfigData.json") @ResourceType(name = "RewardExcelConfigData.json")
public class RewardData extends GameResource { public class RewardData extends GameResource {
public int RewardId; public int rewardId;
public List<RewardItemData> RewardItemList; public List<ItemParamData> rewardItemList;
@Override @Override
public int getId() { public int getId() {
return RewardId; return rewardId;
} }
public List<RewardItemData> getRewardItemList() { public List<ItemParamData> getRewardItemList() {
return RewardItemList; return rewardItemList;
} }
@Override @Override
public void onLoad() { public void onLoad() {
rewardItemList = rewardItemList.stream().filter(i -> i.getId() > 0).toList();
} }
} }
package emu.grasscutter.data.def; package emu.grasscutter.data.excels;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
...@@ -16,28 +16,28 @@ import emu.grasscutter.data.common.ItemParamStringData; ...@@ -16,28 +16,28 @@ import emu.grasscutter.data.common.ItemParamStringData;
@ResourceType(name = "RewardPreviewExcelConfigData.json", loadPriority = LoadPriority.HIGH) @ResourceType(name = "RewardPreviewExcelConfigData.json", loadPriority = LoadPriority.HIGH)
public class RewardPreviewData extends GameResource { public class RewardPreviewData extends GameResource {
private int Id; private int id;
private ItemParamStringData[] PreviewItems; private ItemParamStringData[] previewItems;
private ItemParamData[] PreviewItemsArray; private ItemParamData[] previewItemsArray;
@Override @Override
public int getId() { public int getId() {
return this.Id; return this.id;
} }
public ItemParamData[] getPreviewItems() { public ItemParamData[] getPreviewItems() {
return PreviewItemsArray; return previewItemsArray;
} }
@Override @Override
public void onLoad() { public void onLoad() {
if (this.PreviewItems != null && this.PreviewItems.length > 0) { if (this.previewItems != null && this.previewItems.length > 0) {
this.PreviewItemsArray = Arrays.stream(this.PreviewItems) this.previewItemsArray = Arrays.stream(this.previewItems)
.filter(d -> d.getId() > 0 && d.getCount() != null && !d.getCount().isEmpty()) .filter(d -> d.getId() > 0 && d.getCount() != null && !d.getCount().isEmpty())
.map(ItemParamStringData::toItemParamData) .map(ItemParamStringData::toItemParamData)
.toArray(size -> new ItemParamData[size]); .toArray(size -> new ItemParamData[size]);
} else { } else {
this.PreviewItemsArray = new ItemParamData[0]; this.previewItemsArray = new ItemParamData[0];
} }
} }
} }
package emu.grasscutter.data.def; package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
...@@ -8,22 +8,21 @@ import emu.grasscutter.game.props.SceneType; ...@@ -8,22 +8,21 @@ import emu.grasscutter.game.props.SceneType;
@ResourceType(name = "SceneExcelConfigData.json") @ResourceType(name = "SceneExcelConfigData.json")
public class SceneData extends GameResource { public class SceneData extends GameResource {
private int Id; private int id;
private SceneType Type; private SceneType type;
private String ScriptData; private String scriptData;
@Override @Override
public int getId() { public int getId() {
return this.Id; return this.id;
} }
public SceneType getSceneType() { public SceneType getSceneType() {
return Type; return type;
} }
public String getScriptData() { public String getScriptData() {
return ScriptData; return scriptData;
} }
@Override @Override
......
package emu.grasscutter.data.def; package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType;
...@@ -9,34 +9,34 @@ import java.util.List; ...@@ -9,34 +9,34 @@ import java.util.List;
@ResourceType(name = "ShopGoodsExcelConfigData.json") @ResourceType(name = "ShopGoodsExcelConfigData.json")
public class ShopGoodsData extends GameResource { public class ShopGoodsData extends GameResource {
private int GoodsId; private int goodsId;
private int ShopType; private int shopType;
private int ItemId; private int itemId;
private int ItemCount; private int itemCount;
private int CostScoin; private int costScoin;
private int CostHcoin; private int costHcoin;
private int CostMcoin; private int costMcoin;
private List<ItemParamData> CostItems; private List<ItemParamData> costItems;
private int MinPlayerLevel; private int minPlayerLevel;
private int MaxPlayerLevel; private int maxPlayerLevel;
private int BuyLimit; private int buyLimit;
private int SubTabId; private int subTabId;
private String RefreshType; private String refreshType;
private transient ShopInfo.ShopRefreshType RefreshTypeEnum; private transient ShopInfo.ShopRefreshType refreshTypeEnum;
private int RefreshParam; private int refreshParam;
@Override @Override
public void onLoad() { public void onLoad() {
if (this.RefreshType == null) if (this.refreshType == null)
this.RefreshTypeEnum = ShopInfo.ShopRefreshType.NONE; this.refreshTypeEnum = ShopInfo.ShopRefreshType.NONE;
else { else {
this.RefreshTypeEnum = switch (this.RefreshType) { this.refreshTypeEnum = switch (this.refreshType) {
case "SHOP_REFRESH_DAILY" -> ShopInfo.ShopRefreshType.SHOP_REFRESH_DAILY; case "SHOP_REFRESH_DAILY" -> ShopInfo.ShopRefreshType.SHOP_REFRESH_DAILY;
case "SHOP_REFRESH_WEEKLY" -> ShopInfo.ShopRefreshType.SHOP_REFRESH_WEEKLY; case "SHOP_REFRESH_WEEKLY" -> ShopInfo.ShopRefreshType.SHOP_REFRESH_WEEKLY;
case "SHOP_REFRESH_MONTHLY" -> ShopInfo.ShopRefreshType.SHOP_REFRESH_MONTHLY; case "SHOP_REFRESH_MONTHLY" -> ShopInfo.ShopRefreshType.SHOP_REFRESH_MONTHLY;
...@@ -51,58 +51,58 @@ public class ShopGoodsData extends GameResource { ...@@ -51,58 +51,58 @@ public class ShopGoodsData extends GameResource {
} }
public int getGoodsId() { public int getGoodsId() {
return GoodsId; return goodsId;
} }
public int getShopType() { public int getShopType() {
return ShopType; return shopType;
} }
public int getItemId() { public int getItemId() {
return ItemId; return itemId;
} }
public int getItemCount() { public int getItemCount() {
return ItemCount; return itemCount;
} }
public int getCostScoin() { public int getCostScoin() {
return CostScoin; return costScoin;
} }
public int getCostHcoin() { public int getCostHcoin() {
return CostHcoin; return costHcoin;
} }
public int getCostMcoin() { public int getCostMcoin() {
return CostMcoin; return costMcoin;
} }
public List<ItemParamData> getCostItems() { public List<ItemParamData> getCostItems() {
return CostItems; return costItems;
} }
public int getMinPlayerLevel() { public int getMinPlayerLevel() {
return MinPlayerLevel; return minPlayerLevel;
} }
public int getMaxPlayerLevel() { public int getMaxPlayerLevel() {
return MaxPlayerLevel; return maxPlayerLevel;
} }
public int getBuyLimit() { public int getBuyLimit() {
return BuyLimit; return buyLimit;
} }
public int getSubTabId() { public int getSubTabId() {
return SubTabId; return subTabId;
} }
public ShopInfo.ShopRefreshType getRefreshType() { public ShopInfo.ShopRefreshType getRefreshType() {
return RefreshTypeEnum; return refreshTypeEnum;
} }
public int getRefreshParam() { public int getRefreshParam() {
return RefreshParam; return refreshParam;
} }
} }
package emu.grasscutter.data.def; package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType;
...@@ -6,16 +6,16 @@ import emu.grasscutter.data.ResourceType; ...@@ -6,16 +6,16 @@ import emu.grasscutter.data.ResourceType;
@ResourceType(name = "TowerFloorExcelConfigData.json") @ResourceType(name = "TowerFloorExcelConfigData.json")
public class TowerFloorData extends GameResource { public class TowerFloorData extends GameResource {
private int FloorId; private int floorId;
private int FloorIndex; private int floorIndex;
private int LevelId; private int levelGroupId;
private int OverrideMonsterLevel; private int overrideMonsterLevel;
private int TeamNum; private int teamNum;
private int FloorLevelConfigId; private int floorLevelConfigId;
@Override @Override
public int getId() { public int getId() {
return this.FloorId; return this.floorId;
} }
@Override @Override
...@@ -24,50 +24,26 @@ public class TowerFloorData extends GameResource { ...@@ -24,50 +24,26 @@ public class TowerFloorData extends GameResource {
} }
public int getFloorId() { public int getFloorId() {
return FloorId; return floorId;
}
public void setFloorId(int floorId) {
FloorId = floorId;
} }
public int getFloorIndex() { public int getFloorIndex() {
return FloorIndex; return floorIndex;
}
public void setFloorIndex(int floorIndex) {
FloorIndex = floorIndex;
}
public int getLevelId() {
return LevelId;
} }
public void setLevelId(int levelId) { public int getLevelGroupId() {
LevelId = levelId; return levelGroupId;
} }
public int getOverrideMonsterLevel() { public int getOverrideMonsterLevel() {
return OverrideMonsterLevel; return overrideMonsterLevel;
}
public void setOverrideMonsterLevel(int overrideMonsterLevel) {
OverrideMonsterLevel = overrideMonsterLevel;
} }
public int getTeamNum() { public int getTeamNum() {
return TeamNum; return teamNum;
}
public void setTeamNum(int teamNum) {
TeamNum = teamNum;
} }
public int getFloorLevelConfigId() { public int getFloorLevelConfigId() {
return FloorLevelConfigId; return floorLevelConfigId;
}
public void setFloorLevelConfigId(int floorLevelConfigId) {
FloorLevelConfigId = floorLevelConfigId;
} }
} }
package emu.grasscutter.data.def; package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType;
@ResourceType(name = "TowerLevelExcelConfigData.json") @ResourceType(name = "TowerLevelExcelConfigData.json")
public class TowerLevelData extends GameResource { public class TowerLevelData extends GameResource {
private int ID; private int levelId;
private int LevelId; private int levelIndex;
private int LevelIndex; private int levelGroupId;
private int DungeonId; private int dungeonId;
@Override @Override
public int getId() { public int getId() {
return this.ID; return this.getLevelId();
}
@Override
public void onLoad() {
super.onLoad();
}
public int getID() {
return ID;
}
public void setID(int ID) {
this.ID = ID;
} }
public int getLevelId() { public int getLevelId() {
return LevelId; return levelId;
} }
public void setLevelId(int levelId) { public int getLevelGroupId() {
LevelId = levelId; return levelGroupId;
} }
public int getLevelIndex() { public int getLevelIndex() {
return LevelIndex; return levelIndex;
}
public void setLevelIndex(int levelIndex) {
LevelIndex = levelIndex;
} }
public int getDungeonId() { public int getDungeonId() {
return DungeonId; return dungeonId;
}
public void setDungeonId(int dungeonId) {
DungeonId = dungeonId;
} }
} }
package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import java.util.List;
@ResourceType(name = "TowerScheduleExcelConfigData.json")
public class TowerScheduleData extends GameResource {
private int scheduleId;
private List<Integer> entranceFloorId;
private List<ScheduleDetail> schedules;
private int monthlyLevelConfigId;
@Override
public int getId() {
return scheduleId;
}
@Override
public void onLoad() {
super.onLoad();
this.schedules = this.schedules.stream()
.filter(item -> item.getFloorList().size() > 0)
.toList();
}
public int getScheduleId() {
return scheduleId;
}
public List<Integer> getEntranceFloorId() {
return entranceFloorId;
}
public List<ScheduleDetail> getSchedules() {
return schedules;
}
public int getMonthlyLevelConfigId() {
return monthlyLevelConfigId;
}
public static class ScheduleDetail{
private List<Integer> floorList;
public List<Integer> getFloorList() {
return floorList;
}
}
}
package emu.grasscutter.data.def; package emu.grasscutter.data.excels;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
...@@ -10,23 +10,23 @@ import emu.grasscutter.data.common.CurveInfo; ...@@ -10,23 +10,23 @@ import emu.grasscutter.data.common.CurveInfo;
@ResourceType(name = "WeaponCurveExcelConfigData.json") @ResourceType(name = "WeaponCurveExcelConfigData.json")
public class WeaponCurveData extends GameResource { public class WeaponCurveData extends GameResource {
private int Level; private int level;
private CurveInfo[] CurveInfos; private CurveInfo[] curveInfos;
private Map<String, Float> curveInfos; private Map<String, Float> curveInfosMap;
@Override @Override
public int getId() { public int getId() {
return Level; return level;
} }
public float getMultByProp(String fightProp) { public float getMultByProp(String fightProp) {
return curveInfos.getOrDefault(fightProp, 1f); return curveInfosMap.getOrDefault(fightProp, 1f);
} }
@Override @Override
public void onLoad() { public void onLoad() {
this.curveInfos = new HashMap<>(); this.curveInfosMap = new HashMap<>();
Stream.of(this.CurveInfos).forEach(info -> this.curveInfos.put(info.getType(), info.getValue())); Stream.of(this.curveInfos).forEach(info -> this.curveInfosMap.put(info.getType(), info.getValue()));
} }
} }
package emu.grasscutter.data.def; package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType;
@ResourceType(name = "WeaponLevelExcelConfigData.json") @ResourceType(name = "WeaponLevelExcelConfigData.json")
public class WeaponLevelData extends GameResource { public class WeaponLevelData extends GameResource {
private int Level; private int level;
private int[] RequiredExps; private int[] requiredExps;
@Override @Override
public int getId() { public int getId() {
return this.Level; return this.level;
} }
public int getLevel() { public int getLevel() {
return Level; return level;
} }
public int[] getRequiredExps() { public int[] getRequiredExps() {
return RequiredExps; return requiredExps;
} }
} }
package emu.grasscutter.data.def; package emu.grasscutter.data.excels;
import java.util.ArrayList; import java.util.ArrayList;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
...@@ -9,45 +9,45 @@ import emu.grasscutter.data.common.ItemParamData; ...@@ -9,45 +9,45 @@ import emu.grasscutter.data.common.ItemParamData;
@ResourceType(name = "WeaponPromoteExcelConfigData.json") @ResourceType(name = "WeaponPromoteExcelConfigData.json")
public class WeaponPromoteData extends GameResource { public class WeaponPromoteData extends GameResource {
private int WeaponPromoteId; private int weaponPromoteId;
private int PromoteLevel; private int promoteLevel;
private ItemParamData[] CostItems; private ItemParamData[] costItems;
private int CoinCost; private int coinCost;
private FightPropData[] AddProps; private FightPropData[] addProps;
private int UnlockMaxLevel; private int unlockMaxLevel;
private int RequiredPlayerLevel; private int requiredPlayerLevel;
@Override @Override
public int getId() { public int getId() {
return (WeaponPromoteId << 8) + PromoteLevel; return (weaponPromoteId << 8) + promoteLevel;
} }
public int getWeaponPromoteId() { public int getWeaponPromoteId() {
return WeaponPromoteId; return weaponPromoteId;
} }
public int getPromoteLevel() { public int getPromoteLevel() {
return PromoteLevel; return promoteLevel;
} }
public ItemParamData[] getCostItems() { public ItemParamData[] getCostItems() {
return CostItems; return costItems;
} }
public int getCoinCost() { public int getCoinCost() {
return CoinCost; return coinCost;
} }
public FightPropData[] getAddProps() { public FightPropData[] getAddProps() {
return AddProps; return addProps;
} }
public int getUnlockMaxLevel() { public int getUnlockMaxLevel() {
return UnlockMaxLevel; return unlockMaxLevel;
} }
public int getRequiredPlayerLevel() { public int getRequiredPlayerLevel() {
return RequiredPlayerLevel; return requiredPlayerLevel;
} }
@Override @Override
...@@ -60,7 +60,7 @@ public class WeaponPromoteData extends GameResource { ...@@ -60,7 +60,7 @@ public class WeaponPromoteData extends GameResource {
} }
trim.add(itemParam); trim.add(itemParam);
} }
this.CostItems = trim.toArray(new ItemParamData[trim.size()]); this.costItems = trim.toArray(new ItemParamData[trim.size()]);
// Trim fight prop data // Trim fight prop data
ArrayList<FightPropData> parsed = new ArrayList<>(getAddProps().length); ArrayList<FightPropData> parsed = new ArrayList<>(getAddProps().length);
for (FightPropData prop : getAddProps()) { for (FightPropData prop : getAddProps()) {
...@@ -69,6 +69,6 @@ public class WeaponPromoteData extends GameResource { ...@@ -69,6 +69,6 @@ public class WeaponPromoteData extends GameResource {
parsed.add(prop); parsed.add(prop);
} }
} }
this.AddProps = parsed.toArray(new FightPropData[parsed.size()]); this.addProps = parsed.toArray(new FightPropData[parsed.size()]);
} }
} }
package emu.grasscutter.data.def; package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType;
@ResourceType(name = "WorldLevelExcelConfigData.json") @ResourceType(name = "WorldLevelExcelConfigData.json")
public class WorldLevelData extends GameResource { public class WorldLevelData extends GameResource {
private int Level; private int level;
private int MonsterLevel; private int monsterLevel;
@Override @Override
public int getId() { public int getId() {
return this.Level; return this.level;
} }
public int getMonsterLevel() { public int getMonsterLevel() {
return MonsterLevel; return monsterLevel;
} }
@Override @Override
......
...@@ -3,10 +3,12 @@ package emu.grasscutter.database; ...@@ -3,10 +3,12 @@ package emu.grasscutter.database;
import java.util.List; import java.util.List;
import com.mongodb.client.result.DeleteResult; import com.mongodb.client.result.DeleteResult;
import dev.morphia.query.FindOptions; import dev.morphia.query.FindOptions;
import dev.morphia.query.Sort; import dev.morphia.query.Sort;
import dev.morphia.query.experimental.filters.Filters; import dev.morphia.query.experimental.filters.Filters;
import emu.grasscutter.GameConstants; import emu.grasscutter.GameConstants;
import emu.grasscutter.Grasscutter;
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;
...@@ -14,27 +16,34 @@ import emu.grasscutter.game.gacha.GachaRecord; ...@@ -14,27 +16,34 @@ import emu.grasscutter.game.gacha.GachaRecord;
import emu.grasscutter.game.inventory.GameItem; import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.mail.Mail; import emu.grasscutter.game.mail.Mail;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.quest.GameMainQuest;
import static com.mongodb.client.model.Filters.eq;
public final class DatabaseHelper { public final class DatabaseHelper {
public static Account createAccount(String username) { public static Account createAccount(String username) {
return createAccountWithId(username, 0); return createAccountWithUid(username, 0);
} }
public static Account createAccountWithId(String username, int reservedId) { public static Account createAccountWithUid(String username, int reservedUid) {
// Unique names only // Unique names only
Account exists = DatabaseHelper.getAccountByName(username); if (DatabaseHelper.checkIfAccountExists(username)) {
if (exists != null) {
return null; return null;
} }
// Make sure there are no id collisions // Make sure there are no id collisions
if (reservedId > 0) { if (reservedUid > 0) {
// Cannot make account with the same uid as the server console // Cannot make account with the same uid as the server console
if (reservedId == GameConstants.SERVER_CONSOLE_UID) { if (reservedUid == GameConstants.SERVER_CONSOLE_UID) {
return null;
}
if (DatabaseHelper.checkIfAccountExists(reservedUid)) {
return null; return null;
} }
exists = DatabaseHelper.getAccountByPlayerId(reservedId);
if (exists != null) { // Make sure no existing player already has this id.
if (DatabaseHelper.checkIfPlayerExists(reservedUid)) {
return null; return null;
} }
} }
...@@ -44,8 +53,8 @@ public final class DatabaseHelper { ...@@ -44,8 +53,8 @@ public final class DatabaseHelper {
account.setUsername(username); account.setUsername(username);
account.setId(Integer.toString(DatabaseManager.getNextId(account))); account.setId(Integer.toString(DatabaseManager.getNextId(account)));
if (reservedId > 0) { if (reservedUid > 0) {
account.setPlayerId(reservedId); account.setReservedPlayerUid(reservedUid);
} }
DatabaseHelper.saveAccount(account); DatabaseHelper.saveAccount(account);
...@@ -74,118 +83,158 @@ public final class DatabaseHelper { ...@@ -74,118 +83,158 @@ public final class DatabaseHelper {
} }
public static Account getAccountByName(String username) { public static Account getAccountByName(String username) {
return DatabaseManager.getDatastore().find(Account.class).filter(Filters.eq("username", username)).first(); return DatabaseManager.getGameDatastore().find(Account.class).filter(Filters.eq("username", username)).first();
} }
public static Account getAccountByToken(String token) { public static Account getAccountByToken(String token) {
if(token == null) return null; if(token == null) return null;
return DatabaseManager.getDatastore().find(Account.class).filter(Filters.eq("token", token)).first(); return DatabaseManager.getGameDatastore().find(Account.class).filter(Filters.eq("token", token)).first();
} }
public static Account getAccountBySessionKey(String sessionKey) { public static Account getAccountBySessionKey(String sessionKey) {
if(sessionKey == null) return null; if(sessionKey == null) return null;
return DatabaseManager.getDatastore().find(Account.class).filter(Filters.eq("sessionKey", sessionKey)).first(); return DatabaseManager.getGameDatastore().find(Account.class).filter(Filters.eq("sessionKey", sessionKey)).first();
} }
public static Account getAccountById(String uid) { public static Account getAccountById(String uid) {
return DatabaseManager.getDatastore().find(Account.class).filter(Filters.eq("_id", uid)).first(); return DatabaseManager.getGameDatastore().find(Account.class).filter(Filters.eq("_id", uid)).first();
} }
public static Account getAccountByPlayerId(int playerId) { public static Account getAccountByPlayerId(int playerId) {
return DatabaseManager.getDatastore().find(Account.class).filter(Filters.eq("playerId", playerId)).first(); return DatabaseManager.getGameDatastore().find(Account.class).filter(Filters.eq("reservedPlayerId", playerId)).first();
} }
public static boolean deleteAccount(String username) { public static boolean checkIfAccountExists(String name) {
return DatabaseManager.getDatastore().find(Account.class).filter(Filters.eq("username", username)).delete().getDeletedCount() > 0; return DatabaseManager.getGameDatastore().find(Account.class).filter(Filters.eq("username", name)).count() > 0;
}
public static boolean checkIfAccountExists(int reservedUid) {
return DatabaseManager.getGameDatastore().find(Account.class).filter(Filters.eq("reservedPlayerId", reservedUid)).count() > 0;
}
public static void deleteAccount(Account target) {
// To delete an account, we need to also delete all the other documents in the database that reference the account.
// This should optimally be wrapped inside a transaction, to make sure an error thrown mid-way does not leave the
// database in an inconsistent state, but unfortunately Mongo only supports that when we have a replica set ...
Player player = Grasscutter.getGameServer().getPlayerByAccountId(target.getId());
if (player != null) {
// Close session first
player.getSession().close();
// Delete data from collections
DatabaseManager.getGameDatabase().getCollection("mail").deleteMany(eq("ownerUid", player.getUid()));
DatabaseManager.getGameDatabase().getCollection("avatars").deleteMany(eq("ownerId", player.getUid()));
DatabaseManager.getGameDatabase().getCollection("gachas").deleteMany(eq("ownerId", player.getUid()));
DatabaseManager.getGameDatabase().getCollection("items").deleteMany(eq("ownerId", player.getUid()));
DatabaseManager.getGameDatabase().getCollection("quests").deleteMany(eq("ownerUid", player.getUid()));
// Delete friendships.
// Here, we need to make sure to not only delete the deleted account's friendships,
// but also all friendship entries for that account's friends.
DatabaseManager.getGameDatabase().getCollection("friendships").deleteMany(eq("ownerId", player.getUid()));
DatabaseManager.getGameDatabase().getCollection("friendships").deleteMany(eq("friendId", player.getUid()));
// Delete the player last.
DatabaseManager.getGameDatastore().find(Player.class).filter(Filters.eq("id", player.getUid())).delete();
}
// Finally, delete the account itself.
DatabaseManager.getGameDatastore().find(Account.class).filter(Filters.eq("id", target.getId())).delete();
} }
public static List<Player> getAllPlayers() { public static List<Player> getAllPlayers() {
return DatabaseManager.getDatastore().find(Player.class).stream().toList(); return DatabaseManager.getGameDatastore().find(Player.class).stream().toList();
} }
public static Player getPlayerById(int id) { public static Player getPlayerByUid(int id) {
return DatabaseManager.getDatastore().find(Player.class).filter(Filters.eq("_id", id)).first(); return DatabaseManager.getGameDatastore().find(Player.class).filter(Filters.eq("_id", id)).first();
} }
public static boolean checkPlayerExists(int id) { public static Player getPlayerByAccount(Account account) {
return DatabaseManager.getDatastore().find(Player.class).filter(Filters.eq("_id", id)).first() != null; return DatabaseManager.getGameDatastore().find(Player.class).filter(Filters.eq("accountId", account.getId())).first();
}
public static boolean checkIfPlayerExists(int uid) {
return DatabaseManager.getGameDatastore().find(Player.class).filter(Filters.eq("_id", uid)).count() > 0;
} }
public static synchronized Player createPlayer(Player character, int reservedId) { public static synchronized Player generatePlayerUid(Player character, int reservedId) {
// Check if reserved id // Check if reserved id
int id; int id;
if (reservedId > 0 && !checkPlayerExists(reservedId)) { if (reservedId > 0 && !checkIfPlayerExists(reservedId)) {
id = reservedId; id = reservedId;
character.setUid(id); character.setUid(id);
} else { } else {
do { do {
id = DatabaseManager.getNextId(character); id = DatabaseManager.getNextId(character);
} }
while (checkPlayerExists(id)); while (checkIfPlayerExists(id));
character.setUid(id); character.setUid(id);
} }
// Save to database // Save to database
DatabaseManager.getDatastore().save(character); DatabaseManager.getGameDatastore().save(character);
return character; return character;
} }
public static synchronized int getNextPlayerId(int reservedId) { public static synchronized int getNextPlayerId(int reservedId) {
// Check if reserved id // Check if reserved id
int id; int id;
if (reservedId > 0 && !checkPlayerExists(reservedId)) { if (reservedId > 0 && !checkIfPlayerExists(reservedId)) {
id = reservedId; id = reservedId;
} else { } else {
do { do {
id = DatabaseManager.getNextId(Player.class); id = DatabaseManager.getNextId(Player.class);
} }
while (checkPlayerExists(id)); while (checkIfPlayerExists(id));
} }
return id; return id;
} }
public static void savePlayer(Player character) { public static void savePlayer(Player character) {
DatabaseManager.getDatastore().save(character); DatabaseManager.getGameDatastore().save(character);
} }
public static void saveAvatar(Avatar avatar) { public static void saveAvatar(Avatar avatar) {
DatabaseManager.getDatastore().save(avatar); DatabaseManager.getGameDatastore().save(avatar);
} }
public static List<Avatar> getAvatars(Player player) { public static List<Avatar> getAvatars(Player player) {
return DatabaseManager.getDatastore().find(Avatar.class).filter(Filters.eq("ownerId", player.getUid())).stream().toList(); return DatabaseManager.getGameDatastore().find(Avatar.class).filter(Filters.eq("ownerId", player.getUid())).stream().toList();
} }
public static void saveItem(GameItem item) { public static void saveItem(GameItem item) {
DatabaseManager.getDatastore().save(item); DatabaseManager.getGameDatastore().save(item);
} }
public static boolean deleteItem(GameItem item) { public static boolean deleteItem(GameItem item) {
DeleteResult result = DatabaseManager.getDatastore().delete(item); DeleteResult result = DatabaseManager.getGameDatastore().delete(item);
return result.wasAcknowledged(); return result.wasAcknowledged();
} }
public static List<GameItem> getInventoryItems(Player player) { public static List<GameItem> getInventoryItems(Player player) {
return DatabaseManager.getDatastore().find(GameItem.class).filter(Filters.eq("ownerId", player.getUid())).stream().toList(); return DatabaseManager.getGameDatastore().find(GameItem.class).filter(Filters.eq("ownerId", player.getUid())).stream().toList();
} }
public static List<Friendship> getFriends(Player player) { public static List<Friendship> getFriends(Player player) {
return DatabaseManager.getDatastore().find(Friendship.class).filter(Filters.eq("ownerId", player.getUid())).stream().toList(); return DatabaseManager.getGameDatastore().find(Friendship.class).filter(Filters.eq("ownerId", player.getUid())).stream().toList();
} }
public static List<Friendship> getReverseFriends(Player player) { public static List<Friendship> getReverseFriends(Player player) {
return DatabaseManager.getDatastore().find(Friendship.class).filter(Filters.eq("friendId", player.getUid())).stream().toList(); return DatabaseManager.getGameDatastore().find(Friendship.class).filter(Filters.eq("friendId", player.getUid())).stream().toList();
} }
public static void saveFriendship(Friendship friendship) { public static void saveFriendship(Friendship friendship) {
DatabaseManager.getDatastore().save(friendship); DatabaseManager.getGameDatastore().save(friendship);
} }
public static void deleteFriendship(Friendship friendship) { public static void deleteFriendship(Friendship friendship) {
DatabaseManager.getDatastore().delete(friendship); DatabaseManager.getGameDatastore().delete(friendship);
} }
public static Friendship getReverseFriendship(Friendship friendship) { public static Friendship getReverseFriendship(Friendship friendship) {
return DatabaseManager.getDatastore().find(Friendship.class).filter(Filters.and( return DatabaseManager.getGameDatastore().find(Friendship.class).filter(Filters.and(
Filters.eq("ownerId", friendship.getFriendId()), Filters.eq("ownerId", friendship.getFriendId()),
Filters.eq("friendId", friendship.getOwnerId()) Filters.eq("friendId", friendship.getOwnerId())
)).first(); )).first();
...@@ -196,7 +245,7 @@ public final class DatabaseHelper { ...@@ -196,7 +245,7 @@ public final class DatabaseHelper {
} }
public static List<GachaRecord> getGachaRecords(int ownerId, int page, int gachaType, int pageSize){ public static List<GachaRecord> getGachaRecords(int ownerId, int page, int gachaType, int pageSize){
return DatabaseManager.getDatastore().find(GachaRecord.class).filter( return DatabaseManager.getGameDatastore().find(GachaRecord.class).filter(
Filters.eq("ownerId", ownerId), Filters.eq("ownerId", ownerId),
Filters.eq("gachaType", gachaType) Filters.eq("gachaType", gachaType)
).iterator(new FindOptions() ).iterator(new FindOptions()
...@@ -211,7 +260,7 @@ public final class DatabaseHelper { ...@@ -211,7 +260,7 @@ public final class DatabaseHelper {
} }
public static long getGachaRecordsMaxPage(int ownerId, int page, int gachaType, int pageSize){ public static long getGachaRecordsMaxPage(int ownerId, int page, int gachaType, int pageSize){
long count = DatabaseManager.getDatastore().find(GachaRecord.class).filter( long count = DatabaseManager.getGameDatastore().find(GachaRecord.class).filter(
Filters.eq("ownerId", ownerId), Filters.eq("ownerId", ownerId),
Filters.eq("gachaType", gachaType) Filters.eq("gachaType", gachaType)
).count(); ).count();
...@@ -219,19 +268,31 @@ public final class DatabaseHelper { ...@@ -219,19 +268,31 @@ public final class DatabaseHelper {
} }
public static void saveGachaRecord(GachaRecord gachaRecord){ public static void saveGachaRecord(GachaRecord gachaRecord){
DatabaseManager.getDatastore().save(gachaRecord); DatabaseManager.getGameDatastore().save(gachaRecord);
} }
public static List<Mail> getAllMail(Player player) { public static List<Mail> getAllMail(Player player) {
return DatabaseManager.getDatastore().find(Mail.class).filter(Filters.eq("ownerUid", player.getUid())).stream().toList(); return DatabaseManager.getGameDatastore().find(Mail.class).filter(Filters.eq("ownerUid", player.getUid())).stream().toList();
} }
public static void saveMail(Mail mail) { public static void saveMail(Mail mail) {
DatabaseManager.getDatastore().save(mail); DatabaseManager.getGameDatastore().save(mail);
} }
public static boolean deleteMail(Mail mail) { public static boolean deleteMail(Mail mail) {
DeleteResult result = DatabaseManager.getDatastore().delete(mail); DeleteResult result = DatabaseManager.getGameDatastore().delete(mail);
return result.wasAcknowledged(); return result.wasAcknowledged();
} }
public static List<GameMainQuest> getAllQuests(Player player) {
return DatabaseManager.getGameDatastore().find(GameMainQuest.class).filter(Filters.eq("ownerUid", player.getUid())).stream().toList();
}
public static void saveQuest(GameMainQuest quest) {
DatabaseManager.getGameDatastore().save(quest);
}
public static boolean deleteQuest(GameMainQuest quest) {
return DatabaseManager.getGameDatastore().delete(quest).wasAcknowledged();
}
} }
package emu.grasscutter.database; package emu.grasscutter.database;
import com.mongodb.MongoClientURI;
import com.mongodb.MongoCommandException; import com.mongodb.MongoCommandException;
import com.mongodb.client.MongoClient; import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients; import com.mongodb.client.MongoClients;
...@@ -20,69 +19,70 @@ import emu.grasscutter.game.gacha.GachaRecord; ...@@ -20,69 +19,70 @@ import emu.grasscutter.game.gacha.GachaRecord;
import emu.grasscutter.game.inventory.GameItem; import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.mail.Mail; import emu.grasscutter.game.mail.Mail;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.quest.GameMainQuest;
import emu.grasscutter.game.quest.GameQuest;
public final class DatabaseManager { import static emu.grasscutter.Configuration.*;
private static MongoClient mongoClient;
private static MongoClient dispatchMongoClient;
private static Datastore datastore; public final class DatabaseManager {
private static Datastore gameDatastore;
private static Datastore dispatchDatastore; private static Datastore dispatchDatastore;
private static final Class<?>[] mappedClasses = new Class<?>[] { private static final Class<?>[] mappedClasses = new Class<?>[] {
DatabaseCounter.class, Account.class, Player.class, Avatar.class, GameItem.class, Friendship.class, GachaRecord.class, Mail.class DatabaseCounter.class, Account.class, Player.class, Avatar.class, GameItem.class, Friendship.class,
GachaRecord.class, Mail.class, GameMainQuest.class
}; };
public static Datastore getDatastore() { public static Datastore getGameDatastore() {
return datastore; return gameDatastore;
} }
public static MongoDatabase getDatabase() { public static MongoDatabase getGameDatabase() {
return getDatastore().getDatabase(); return getGameDatastore().getDatabase();
} }
// 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 == ServerRunMode.GAME_ONLY) { if(SERVER.runMode == ServerRunMode.GAME_ONLY) {
return dispatchDatastore; return dispatchDatastore;
} else { } else {
return datastore; return gameDatastore;
} }
} }
public static void initialize() { public static void initialize() {
// Initialize // Initialize
MongoClient mongoClient = MongoClients.create(Grasscutter.getConfig().DatabaseUrl); MongoClient gameMongoClient = MongoClients.create(DATABASE.game.connectionUri);
// Set mapper options. // Set mapper options.
MapperOptions mapperOptions = MapperOptions.builder() MapperOptions mapperOptions = MapperOptions.builder()
.storeEmpties(true).storeNulls(false).build(); .storeEmpties(true).storeNulls(false).build();
// Create data store. // Create data store.
datastore = Morphia.createDatastore(mongoClient, Grasscutter.getConfig().DatabaseCollection, mapperOptions); gameDatastore = Morphia.createDatastore(gameMongoClient, DATABASE.game.collection, mapperOptions);
// Map classes. // Map classes.
datastore.getMapper().map(mappedClasses); gameDatastore.getMapper().map(mappedClasses);
// Ensure indexes // Ensure indexes
try { try {
datastore.ensureIndexes(); gameDatastore.ensureIndexes();
} catch (MongoCommandException exception) { } catch (MongoCommandException exception) {
Grasscutter.getLogger().info("Mongo index error: ", exception); Grasscutter.getLogger().info("Mongo index error: ", exception);
// Duplicate index error // Duplicate index error
if (exception.getCode() == 85) { if (exception.getCode() == 85) {
// Drop all indexes and re add them // Drop all indexes and re add them
MongoIterable<String> collections = datastore.getDatabase().listCollectionNames(); MongoIterable<String> collections = gameDatastore.getDatabase().listCollectionNames();
for (String name : collections) { for (String name : collections) {
datastore.getDatabase().getCollection(name).dropIndexes(); gameDatastore.getDatabase().getCollection(name).dropIndexes();
} }
// Add back indexes // Add back indexes
datastore.ensureIndexes(); gameDatastore.ensureIndexes();
} }
} }
if(Grasscutter.getConfig().RunMode == ServerRunMode.GAME_ONLY) { if(SERVER.runMode == ServerRunMode.GAME_ONLY) {
dispatchMongoClient = MongoClients.create(Grasscutter.getConfig().getGameServerOptions().DispatchServerDatabaseUrl); MongoClient dispatchMongoClient = MongoClients.create(DATABASE.server.connectionUri);
dispatchDatastore = Morphia.createDatastore(dispatchMongoClient, Grasscutter.getConfig().getGameServerOptions().DispatchServerDatabaseCollection); dispatchDatastore = Morphia.createDatastore(dispatchMongoClient, DATABASE.server.collection);
// Ensure indexes for dispatch server // Ensure indexes for dispatch server
try { try {
...@@ -104,14 +104,14 @@ public final class DatabaseManager { ...@@ -104,14 +104,14 @@ 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.getSimpleName())).first(); DatabaseCounter counter = getGameDatastore().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());
} }
try { try {
return counter.getNextId(); return counter.getNextId();
} finally { } finally {
getDatastore().save(counter); getGameDatastore().save(counter);
} }
} }
......
...@@ -5,12 +5,12 @@ import emu.grasscutter.database.DatabaseHelper; ...@@ -5,12 +5,12 @@ import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.utils.Crypto; import emu.grasscutter.utils.Crypto;
import emu.grasscutter.utils.Utils; import emu.grasscutter.utils.Utils;
import java.util.ArrayList; import java.util.*;
import java.util.List; import java.util.stream.Stream;
import org.bson.Document; import org.bson.Document;
import com.mongodb.DBObject; import static emu.grasscutter.Configuration.*;
@Entity(value = "accounts", useDiscriminator = false) @Entity(value = "accounts", useDiscriminator = false)
public class Account { public class Account {
...@@ -21,16 +21,18 @@ public class Account { ...@@ -21,16 +21,18 @@ public class Account {
private String username; private String username;
private String password; // Unused for now private String password; // Unused for now
@AlsoLoad("playerUid") private int playerId; private int reservedPlayerId;
private String email; private String email;
private String token; private String token;
private String sessionKey; // Session token for dispatch server private String sessionKey; // Session token for dispatch server
private List<String> permissions; private List<String> permissions;
private Locale locale;
@Deprecated @Deprecated
public Account() { public Account() {
this.permissions = new ArrayList<>(); this.permissions = new ArrayList<>();
this.locale = LANGUAGE;
} }
public String getId() { public String getId() {
...@@ -65,12 +67,12 @@ public class Account { ...@@ -65,12 +67,12 @@ public class Account {
this.token = token; this.token = token;
} }
public int getPlayerUid() { public int getReservedPlayerUid() {
return this.playerId; return this.reservedPlayerId;
} }
public void setPlayerId(int playerId) { public void setReservedPlayerUid(int playerId) {
this.playerId = playerId; this.reservedPlayerId = playerId;
} }
public String getEmail() { public String getEmail() {
...@@ -95,6 +97,14 @@ public class Account { ...@@ -95,6 +97,14 @@ public class Account {
return this.sessionKey; return this.sessionKey;
} }
public Locale getLocale() {
return locale;
}
public void setLocale(Locale locale) {
this.locale = locale;
}
/** /**
* The collection of a player's permissions. * The collection of a player's permissions.
*/ */
...@@ -107,13 +117,49 @@ public class Account { ...@@ -107,13 +117,49 @@ public class Account {
this.permissions.add(permission); return true; this.permissions.add(permission); return true;
} }
public static boolean permissionMatchesWildcard(String wildcard, String[] permissionParts) {
String[] wildcardParts = wildcard.split("\\.");
if (permissionParts.length < wildcardParts.length) { // A longer wildcard can never match a shorter permission
return false;
}
for (int i=0; i<wildcardParts.length; i++) {
switch (wildcardParts[i]) {
case "**": // Recursing match
return true;
case "*": // Match only one layer
if (i >= (permissionParts.length-1)) {
return true;
}
break;
default: // This layer isn't a wildcard, it needs to match exactly
if (!wildcardParts[i].equals(permissionParts[i])) {
return false;
}
}
}
// At this point the wildcard will have matched every layer, but if it is shorter then the permission then this is not a match at this point (no **).
return (wildcardParts.length == permissionParts.length);
}
public boolean hasPermission(String permission) { public boolean hasPermission(String permission) {
return this.permissions.contains(permission) || if(this.permissions.contains("*") && this.permissions.size() == 1) return true;
this.permissions.contains("*") ||
(this.permissions.contains("player") || this.permissions.contains("player.*")) && permission.startsWith("player.") || // Add default permissions if it doesn't exist
(this.permissions.contains("server") || this.permissions.contains("server.*")) && permission.startsWith("server."); List<String> permissions = Stream.of(this.permissions, Arrays.asList(ACCOUNT.defaultPermissions))
.flatMap(Collection::stream)
.distinct().toList();
if (permissions.contains(permission)) return true;
String[] permissionParts = permission.split("\\.");
for (String p : permissions) {
if (p.startsWith("-") && permissionMatchesWildcard(p.substring(1), permissionParts)) return false;
if (permissionMatchesWildcard(p, permissionParts)) return true;
}
return permissions.contains("*");
} }
public boolean removePermission(String permission) { public boolean removePermission(String permission) {
return this.permissions.remove(permission); return this.permissions.remove(permission);
} }
...@@ -135,5 +181,10 @@ public class Account { ...@@ -135,5 +181,10 @@ public class Account {
if (!document.containsKey("permissions")) { if (!document.containsKey("permissions")) {
this.addPermission("*"); this.addPermission("*");
} }
// Set account default language as server default language
if (!document.containsKey("locale")) {
this.locale = LANGUAGE;
}
} }
} }
package emu.grasscutter.game.ability;
import java.util.Optional;
import com.google.protobuf.InvalidProtocolBufferException;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.binout.AbilityModifierEntry;
import emu.grasscutter.data.binout.AbilityModifier.AbilityModifierAction;
import emu.grasscutter.data.excels.AvatarSkillDepotData;
import emu.grasscutter.data.excels.ItemData;
import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.entity.EntityAvatar;
import emu.grasscutter.game.entity.EntityClientGadget;
import emu.grasscutter.game.entity.EntityItem;
import emu.grasscutter.game.entity.GameEntity;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ElementType;
import emu.grasscutter.net.proto.AbilityActionGenerateElemBallOuterClass.AbilityActionGenerateElemBall;
import emu.grasscutter.net.proto.AbilityInvokeEntryHeadOuterClass.AbilityInvokeEntryHead;
import emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry;
import emu.grasscutter.net.proto.AbilityMetaModifierChangeOuterClass.AbilityMetaModifierChange;
import emu.grasscutter.net.proto.AbilityMetaReInitOverrideMapOuterClass.AbilityMetaReInitOverrideMap;
import emu.grasscutter.net.proto.AbilityMixinCostStaminaOuterClass.AbilityMixinCostStamina;
import emu.grasscutter.net.proto.AbilityScalarValueEntryOuterClass.AbilityScalarValueEntry;
import emu.grasscutter.net.proto.ModifierActionOuterClass.ModifierAction;
import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.Utils;
public class AbilityManager {
private Player player;
public AbilityManager(Player player) {
this.player = player;
}
public Player getPlayer() {
return this.player;
}
public void onAbilityInvoke(AbilityInvokeEntry invoke) throws Exception {
// Grasscutter.getLogger().info(invoke.getArgumentType() + " (" + invoke.getArgumentTypeValue() + "): " + Utils.bytesToHex(invoke.toByteArray()));
switch (invoke.getArgumentType()) {
case ABILITY_INVOKE_ARGUMENT_META_OVERRIDE_PARAM:
handleOverrideParam(invoke);
break;
case ABILITY_INVOKE_ARGUMENT_META_REINIT_OVERRIDEMAP:
handleReinitOverrideMap(invoke);
break;
case ABILITY_INVOKE_ARGUMENT_META_MODIFIER_CHANGE:
handleModifierChange(invoke);
break;
case ABILITY_INVOKE_ARGUMENT_MIXIN_COST_STAMINA:
handleMixinCostStamina(invoke);
break;
case ABILITY_INVOKE_ARGUMENT_ACTION_GENERATE_ELEM_BALL:
handleGenerateElemBall(invoke);
break;
default:
break;
}
}
private void handleOverrideParam(AbilityInvokeEntry invoke) throws Exception {
GameEntity entity = player.getScene().getEntityById(invoke.getEntityId());
if (entity == null) {
return;
}
AbilityScalarValueEntry entry = AbilityScalarValueEntry.parseFrom(invoke.getAbilityData());
entity.getMetaOverrideMap().put(entry.getKey().getStr(), entry.getFloatValue());
}
private void handleReinitOverrideMap(AbilityInvokeEntry invoke) throws Exception {
GameEntity entity = player.getScene().getEntityById(invoke.getEntityId());
if (entity == null) {
return;
}
AbilityMetaReInitOverrideMap map = AbilityMetaReInitOverrideMap.parseFrom(invoke.getAbilityData());
for (AbilityScalarValueEntry entry : map.getOverrideMapList()) {
entity.getMetaOverrideMap().put(entry.getKey().getStr(), entry.getFloatValue());
}
}
private void handleModifierChange(AbilityInvokeEntry invoke) throws Exception {
GameEntity target = player.getScene().getEntityById(invoke.getEntityId());
if (target == null) {
return;
}
AbilityInvokeEntryHead head = invoke.getHead();
if (head == null) {
return;
}
AbilityMetaModifierChange data = AbilityMetaModifierChange.parseFrom(invoke.getAbilityData());
if (data == null) {
return;
}
GameEntity sourceEntity = player.getScene().getEntityById(data.getApplyEntityId());
if (sourceEntity == null) {
return;
}
// This is not how it works but we will keep it for now since healing abilities dont work properly anyways
if (data.getAction() == ModifierAction.ADDED && data.getParentAbilityName() != null) {
// Handle add modifier here
String modifierString = data.getParentAbilityName().getStr();
AbilityModifierEntry modifier = GameData.getAbilityModifiers().get(modifierString);
if (modifier != null && modifier.getOnAdded().size() > 0) {
for (AbilityModifierAction action : modifier.getOnAdded()) {
invokeAction(action, target, sourceEntity);
}
}
// Add to meta modifier list
target.getMetaModifiers().put(head.getInstancedModifierId(), modifierString);
} else if (data.getAction() == ModifierAction.REMOVED) {
String modifierString = target.getMetaModifiers().get(head.getInstancedModifierId());
if (modifierString != null) {
// Get modifier and call on remove event
AbilityModifierEntry modifier = GameData.getAbilityModifiers().get(modifierString);
if (modifier != null && modifier.getOnRemoved().size() > 0) {
for (AbilityModifierAction action : modifier.getOnRemoved()) {
invokeAction(action, target, sourceEntity);
}
}
// Remove from meta modifiers
target.getMetaModifiers().remove(head.getInstancedModifierId());
}
}
}
private void handleMixinCostStamina(AbilityInvokeEntry invoke) throws InvalidProtocolBufferException {
AbilityMixinCostStamina costStamina = AbilityMixinCostStamina.parseFrom((invoke.getAbilityData()));
getPlayer().getStaminaManager().handleMixinCostStamina(costStamina.getIsSwim());
}
private void handleGenerateElemBall(AbilityInvokeEntry invoke) throws InvalidProtocolBufferException {
this.player.getEnergyManager().handleGenerateElemBall(invoke);
}
private void invokeAction(AbilityModifierAction action, GameEntity target, GameEntity sourceEntity) {
switch (action.type) {
case HealHP -> {
if (action.amount == null) {
return;
}
float healAmount = 0;
if (action.amount.isDynamic && action.amount.dynamicKey != null) {
healAmount = sourceEntity.getMetaOverrideMap().getOrDefault(action.amount.dynamicKey, 0f);
}
if (healAmount > 0) {
target.heal(healAmount);
}
}
case LoseHP -> {
if (action.amountByTargetCurrentHPRatio == null) {
return;
}
float damageAmount = 0;
if (action.amount.isDynamic && action.amount.dynamicKey != null) {
damageAmount = sourceEntity.getMetaOverrideMap().getOrDefault(action.amount.dynamicKey, 0f);
}
if (damageAmount > 0) {
target.damage(damageAmount);
}
}
}
}
}
...@@ -17,24 +17,24 @@ import dev.morphia.annotations.PostLoad; ...@@ -17,24 +17,24 @@ import dev.morphia.annotations.PostLoad;
import dev.morphia.annotations.PrePersist; import dev.morphia.annotations.PrePersist;
import dev.morphia.annotations.Transient; import dev.morphia.annotations.Transient;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.data.binout.OpenConfigEntry;
import emu.grasscutter.data.binout.OpenConfigEntry.SkillPointModifier;
import emu.grasscutter.data.common.FightPropData; import emu.grasscutter.data.common.FightPropData;
import emu.grasscutter.data.custom.OpenConfigEntry; import emu.grasscutter.data.excels.AvatarData;
import emu.grasscutter.data.custom.OpenConfigEntry.SkillPointModifier; import emu.grasscutter.data.excels.AvatarPromoteData;
import emu.grasscutter.data.def.AvatarData; import emu.grasscutter.data.excels.AvatarSkillData;
import emu.grasscutter.data.def.AvatarPromoteData; import emu.grasscutter.data.excels.AvatarSkillDepotData;
import emu.grasscutter.data.def.AvatarSkillData; import emu.grasscutter.data.excels.AvatarTalentData;
import emu.grasscutter.data.def.AvatarSkillDepotData; import emu.grasscutter.data.excels.EquipAffixData;
import emu.grasscutter.data.def.AvatarSkillDepotData.InherentProudSkillOpens; import emu.grasscutter.data.excels.ProudSkillData;
import emu.grasscutter.data.def.AvatarTalentData; import emu.grasscutter.data.excels.ReliquaryAffixData;
import emu.grasscutter.data.def.EquipAffixData; import emu.grasscutter.data.excels.ReliquaryLevelData;
import emu.grasscutter.data.def.ItemData.WeaponProperty; import emu.grasscutter.data.excels.ReliquaryMainPropData;
import emu.grasscutter.data.def.ProudSkillData; import emu.grasscutter.data.excels.ReliquarySetData;
import emu.grasscutter.data.def.ReliquaryAffixData; import emu.grasscutter.data.excels.WeaponCurveData;
import emu.grasscutter.data.def.ReliquaryLevelData; import emu.grasscutter.data.excels.WeaponPromoteData;
import emu.grasscutter.data.def.ReliquaryMainPropData; import emu.grasscutter.data.excels.AvatarSkillDepotData.InherentProudSkillOpens;
import emu.grasscutter.data.def.ReliquarySetData; import emu.grasscutter.data.excels.ItemData.WeaponProperty;
import emu.grasscutter.data.def.WeaponCurveData;
import emu.grasscutter.data.def.WeaponPromoteData;
import emu.grasscutter.database.DatabaseHelper; import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.game.entity.EntityAvatar; import emu.grasscutter.game.entity.EntityAvatar;
import emu.grasscutter.game.inventory.EquipType; import emu.grasscutter.game.inventory.EquipType;
...@@ -62,6 +62,8 @@ import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; ...@@ -62,6 +62,8 @@ import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import static emu.grasscutter.Configuration.GAME_OPTIONS;
@Entity(value = "avatars", useDiscriminator = false) @Entity(value = "avatars", useDiscriminator = false)
public class Avatar { public class Avatar {
@Id private ObjectId id; @Id private ObjectId id;
...@@ -69,6 +71,7 @@ public class Avatar { ...@@ -69,6 +71,7 @@ public class Avatar {
@Transient private Player owner; @Transient private Player owner;
@Transient private AvatarData data; @Transient private AvatarData data;
@Transient private AvatarSkillDepotData skillDepot;
@Transient private long guid; // Player unique id @Transient private long guid; // Player unique id
private int avatarId; // Id of avatar private int avatarId; // Id of avatar
...@@ -78,6 +81,7 @@ public class Avatar { ...@@ -78,6 +81,7 @@ public class Avatar {
private int satiation; // ? private int satiation; // ?
private int satiationPenalty; // ? private int satiationPenalty; // ?
private float currentHp; private float currentHp;
private float currentEnergy;
@Transient private final Int2ObjectMap<GameItem> equips; @Transient private final Int2ObjectMap<GameItem> equips;
@Transient private final Int2FloatOpenHashMap fightProp; @Transient private final Int2FloatOpenHashMap fightProp;
...@@ -103,8 +107,8 @@ public class Avatar { ...@@ -103,8 +107,8 @@ public class Avatar {
private int nameCardRewardId; private int nameCardRewardId;
private int nameCardId; private int nameCardId;
@Deprecated // Do not use. Morhpia only!
public Avatar() { public Avatar() {
// Morhpia only!
this.equips = new Int2ObjectOpenHashMap<>(); this.equips = new Int2ObjectOpenHashMap<>();
this.fightProp = new Int2FloatOpenHashMap(); this.fightProp = new Int2FloatOpenHashMap();
this.extraAbilityEmbryos = new HashSet<>(); this.extraAbilityEmbryos = new HashSet<>();
...@@ -140,13 +144,13 @@ public class Avatar { ...@@ -140,13 +144,13 @@ public class Avatar {
} }
// Skill depot // Skill depot
this.setSkillDepot(getAvatarData().getSkillDepot()); this.setSkillDepotData(getAvatarData().getSkillDepot());
// Set stats // Set stats
this.recalcStats(); this.recalcStats();
this.currentHp = getFightProperty(FightProperty.FIGHT_PROP_MAX_HP); this.currentHp = getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, this.currentHp); setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, this.currentHp);
this.currentEnergy = 0f;
// Load handler // Load handler
this.onLoad(); this.onLoad();
} }
...@@ -164,7 +168,8 @@ public class Avatar { ...@@ -164,7 +168,8 @@ public class Avatar {
} }
protected void setAvatarData(AvatarData data) { protected void setAvatarData(AvatarData data) {
this.data = data; if (this.data != null) return;
this.data = data; // Used while loading this from the database
} }
public int getOwnerId() { public int getOwnerId() {
...@@ -257,9 +262,19 @@ public class Avatar { ...@@ -257,9 +262,19 @@ public class Avatar {
return skillDepotId; return skillDepotId;
} }
public void setSkillDepot(AvatarSkillDepotData skillDepot) { public AvatarSkillDepotData getSkillDepot() {
// Set id return skillDepot;
}
protected void setSkillDepot(AvatarSkillDepotData skillDepot) {
if (this.skillDepot != null) return;
this.skillDepot = skillDepot; // Used while loading this from the database
}
public void setSkillDepotData(AvatarSkillDepotData skillDepot) {
// Set id and depot
this.skillDepotId = skillDepot.getId(); this.skillDepotId = skillDepot.getId();
this.skillDepot = skillDepot;
// Clear, then add skills // Clear, then add skills
getSkillLevelMap().clear(); getSkillLevelMap().clear();
if (skillDepot.getEnergySkill() > 0) { if (skillDepot.getEnergySkill() > 0) {
...@@ -344,6 +359,34 @@ public class Avatar { ...@@ -344,6 +359,34 @@ public class Avatar {
this.currentHp = currentHp; this.currentHp = currentHp;
} }
public void setCurrentEnergy() {
if (GAME_OPTIONS.energyUsage) {
this.setCurrentEnergy(this.currentEnergy);
}
}
public void setCurrentEnergy(float currentEnergy) {
if (this.getSkillDepot() != null && this.getSkillDepot().getEnergySkillData() != null) {
ElementType element = this.getSkillDepot().getElementType();
this.setFightProperty(element.getMaxEnergyProp(), this.getSkillDepot().getEnergySkillData().getCostElemVal());
if (GAME_OPTIONS.energyUsage) {
this.setFightProperty(element.getCurEnergyProp(), currentEnergy);
}
else {
this.setFightProperty(element.getCurEnergyProp(), this.getSkillDepot().getEnergySkillData().getCostElemVal());
}
}
}
public void setCurrentEnergy(FightProperty curEnergyProp, float currentEnergy) {
if (GAME_OPTIONS.energyUsage) {
this.setFightProperty(curEnergyProp, currentEnergy);
this.currentEnergy = currentEnergy;
this.save();
}
}
public Int2FloatOpenHashMap getFightProperties() { public Int2FloatOpenHashMap getFightProperties() {
return fightProp; return fightProp;
} }
...@@ -481,6 +524,9 @@ public class Avatar { ...@@ -481,6 +524,9 @@ public class Avatar {
// 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);
// Store current energy value for later
float currentEnergy = (this.getSkillDepot() != null) ? this.getFightProperty(this.getSkillDepot().getElementType().getCurEnergyProp()) : 0f;
// Clear properties // Clear properties
this.getFightProperties().clear(); this.getFightProperties().clear();
...@@ -499,11 +545,7 @@ public class Avatar { ...@@ -499,11 +545,7 @@ public class Avatar {
} }
// Set energy usage // Set energy usage
if (data.getSkillDepot() != null && data.getSkillDepot().getEnergySkillData() != null) { setCurrentEnergy(currentEnergy);
ElementType element = data.getSkillDepot().getElementType();
this.setFightProperty(element.getEnergyProperty(), data.getSkillDepot().getEnergySkillData().getCostElemVal());
this.setFightProperty((element.getEnergyProperty().getId() % 70) + 1000, data.getSkillDepot().getEnergySkillData().getCostElemVal());
}
// Artifacts // Artifacts
for (int slotId = 1; slotId <= 5; slotId++) { for (int slotId = 1; slotId <= 5; slotId++) {
......
...@@ -4,7 +4,8 @@ import java.util.Iterator; ...@@ -4,7 +4,8 @@ import java.util.Iterator;
import java.util.List; import java.util.List;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.data.def.AvatarData; import emu.grasscutter.data.excels.AvatarData;
import emu.grasscutter.data.excels.AvatarSkillDepotData;
import emu.grasscutter.database.DatabaseHelper; import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.game.entity.EntityAvatar; import emu.grasscutter.game.entity.EntityAvatar;
import emu.grasscutter.game.inventory.GameItem; import emu.grasscutter.game.inventory.GameItem;
...@@ -139,12 +140,14 @@ public class AvatarStorage implements Iterable<Avatar> { ...@@ -139,12 +140,14 @@ public class AvatarStorage implements Iterable<Avatar> {
} }
AvatarData avatarData = GameData.getAvatarDataMap().get(avatar.getAvatarId()); AvatarData avatarData = GameData.getAvatarDataMap().get(avatar.getAvatarId());
if (avatarData == null) { AvatarSkillDepotData skillDepot = GameData.getAvatarSkillDepotDataMap().get(avatar.getSkillDepotId());
if (avatarData == null || skillDepot == null) {
continue; continue;
} }
// Set ownerships // Set ownerships
avatar.setAvatarData(avatarData); avatar.setAvatarData(avatarData);
avatar.setSkillDepot(skillDepot);
avatar.setOwner(getPlayer()); avatar.setOwner(getPlayer());
// Force recalc of const boosted skills // Force recalc of const boosted skills
......
package emu.grasscutter.game.combine; package emu.grasscutter.game.combine;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.data.def.CombineData; import emu.grasscutter.data.common.ItemParamData;
import emu.grasscutter.data.excels.CombineData;
import emu.grasscutter.game.inventory.ItemType; import emu.grasscutter.game.inventory.ItemType;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.net.proto.RetcodeOuterClass; import emu.grasscutter.net.proto.RetcodeOuterClass;
...@@ -71,7 +72,7 @@ public class CombineManger { ...@@ -71,7 +72,7 @@ public class CombineManger {
CombineResult result = new CombineResult(); CombineResult result = new CombineResult();
result.setMaterial(List.of()); result.setMaterial(List.of());
result.setResult(List.of(new CombineData.CombineItemPair(combineData.getResultItemId(), result.setResult(List.of(new ItemParamData(combineData.getResultItemId(),
combineData.getResultItemCount() * count))); combineData.getResultItemCount() * count)));
// TODO lucky characters // TODO lucky characters
result.setExtra(List.of()); result.setExtra(List.of());
......
package emu.grasscutter.game.combine; package emu.grasscutter.game.combine;
import emu.grasscutter.data.def.CombineData;
import java.util.List; import java.util.List;
import emu.grasscutter.data.common.ItemParamData;
import emu.grasscutter.data.excels.CombineData;
public class CombineResult { public class CombineResult {
private List<CombineData.CombineItemPair> material; private List<ItemParamData> material;
private List<CombineData.CombineItemPair> result; private List<ItemParamData> result;
private List<CombineData.CombineItemPair> extra; private List<ItemParamData> extra;
private List<CombineData.CombineItemPair> back; private List<ItemParamData> back;
public List<CombineData.CombineItemPair> getMaterial() { public List<ItemParamData> getMaterial() {
return material; return material;
} }
public void setMaterial(List<CombineData.CombineItemPair> material) { public void setMaterial(List<ItemParamData> material) {
this.material = material; this.material = material;
} }
public List<CombineData.CombineItemPair> getResult() { public List<ItemParamData> getResult() {
return result; return result;
} }
public void setResult(List<CombineData.CombineItemPair> result) { public void setResult(List<ItemParamData> result) {
this.result = result; this.result = result;
} }
public List<CombineData.CombineItemPair> getExtra() { public List<ItemParamData> getExtra() {
return extra; return extra;
} }
public void setExtra(List<CombineData.CombineItemPair> extra) { public void setExtra(List<ItemParamData> extra) {
this.extra = extra; this.extra = extra;
} }
public List<CombineData.CombineItemPair> getBack() { public List<ItemParamData> getBack() {
return back; return back;
} }
public void setBack(List<CombineData.CombineItemPair> back) { public void setBack(List<ItemParamData> back) {
this.back = back; this.back = back;
} }
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment