fix database performance <3

This commit is contained in:
Somebody
2024-05-18 20:55:05 +08:00
parent 710a1dd44e
commit a587a82c42
33 changed files with 479 additions and 144 deletions

View File

@@ -25,7 +25,7 @@ namespace EggLink.DanhengServer.Data.Excel
[JsonConverter(typeof(StringEnumConverter))]
public ItemUseMethodEnum UseMethod { get; set; }
//public List<ItemParam> ReturnItemIDList{ get; set; }
public List<MappingInfoItem> ReturnItemIDList { get; set; } = [];
[JsonIgnore]
public int Exp { get; set; }

View File

@@ -17,6 +17,8 @@ namespace EggLink.DanhengServer.Data.Excel
[JsonIgnore]
public List<MappingInfoItem> DropItemList { get; set; } = [];
[JsonIgnore]
public List<MappingInfoItem> DropRelicItemList { get; set; } = [];
public override int GetId()
{
@@ -182,7 +184,7 @@ namespace EggLink.DanhengServer.Data.Excel
if (amount > 0)
{
drop.ItemNum = (int)amount;
DropItemList.Add(drop);
DropRelicItemList.Add(drop);
}
}
}

View File

@@ -12,6 +12,7 @@ namespace EggLink.DanhengServer.Data.Excel
public int EventID { get; set; }
public int WorldLevel { get; set; }
public int Reward { get; set; }
public List<int> DropList { get; set; } = [];
public int StageID { get; set; }
public override int GetId()

View File

@@ -77,7 +77,7 @@ namespace EggLink.DanhengServer.Data.Excel
CurrentHp = hp == 0 ? 10000 : hp,
CurrentSp = sp,
InternalEntityId = Id,
PlayerData = DatabaseHelper.Instance!.GetInstance<PlayerData>(uid),
PlayerData = DatabaseHelper.GetInstance<PlayerData>(uid),
};
}
}

View File

@@ -33,7 +33,7 @@ namespace EggLink.DanhengServer.Database.Account
public static AccountData? GetAccountByUid(long uid)
{
AccountData? result = DatabaseHelper.Instance?.GetInstance<AccountData>(uid);
AccountData? result = DatabaseHelper.Instance?.GetInstance<AccountData>((int)uid);
return result;
}

View File

@@ -393,7 +393,7 @@ namespace EggLink.DanhengServer.Database.Avatar
Pos = (uint)pos,
};
var inventory = DatabaseHelper.Instance!.GetInstance<InventoryData>(PlayerData!.Uid)!;
var inventory = DatabaseHelper.GetInstance<InventoryData>(PlayerData!.Uid)!;
foreach (var item in Relic)
{
var relic = inventory.RelicItems.Find(x => x.UniqueId == item.Value)!;

View File

@@ -1,20 +1,19 @@
using EggLink.DanhengServer.Configuration;
using EggLink.DanhengServer.Database.Account;
using EggLink.DanhengServer.Database.Inventory;
using EggLink.DanhengServer.Database.Inventory;
using EggLink.DanhengServer.Database.Mission;
using EggLink.DanhengServer.Util;
using Microsoft.Data.Sqlite;
using SqlSugar;
using System.Reflection;
namespace EggLink.DanhengServer.Database
{
public class DatabaseHelper
{
public Logger logger = new("Database");
public static Logger logger = new("Database");
public static SqlSugarScope? sqlSugarScope;
public static DatabaseHelper? Instance;
private static readonly Dictionary<int, object> _lock = [];
public readonly static Dictionary<int, List<BaseDatabaseDataHelper>> UidInstanceMap = [];
public readonly static List<int> ToSaveUidList = [];
public static long LastSaveTick = DateTime.UtcNow.Ticks;
public static Thread? SaveThread;
public DatabaseHelper()
{
@@ -56,6 +55,7 @@ namespace EggLink.DanhengServer.Database
SerializeService = new CustomSerializeService()
}
});
switch (config.Database.DatabaseType)
{
case "sqlite":
@@ -68,6 +68,44 @@ namespace EggLink.DanhengServer.Database
logger.Error("Unsupported database type");
break;
}
var baseType = typeof(BaseDatabaseDataHelper);
var assembly = typeof(BaseDatabaseDataHelper).Assembly;
var types = assembly.GetTypes().Where(t => t.IsSubclassOf(baseType));
foreach (var t in types)
{
typeof(DatabaseHelper).GetMethod("InitializeTable")?.MakeGenericMethod(t).Invoke(null, null); // cache the data
}
LastSaveTick = DateTime.UtcNow.Ticks;
SaveThread = new(() =>
{
while (true)
{
CalcSaveDatabase();
}
});
SaveThread.Start();
}
public static void InitializeTable<T>() where T : class, new()
{
var list = sqlSugarScope?.Queryable<T>()
.Select(x => x)
.ToList();
foreach (var instance in list!)
{
var inst = (instance as BaseDatabaseDataHelper)!;
if (!UidInstanceMap.TryGetValue(inst.Uid, out List<BaseDatabaseDataHelper>? value))
{
value = [];
UidInstanceMap[inst.Uid] = value;
}
value.Add(inst); // add to the map
}
}
public void UpgradeDatabase()
@@ -85,7 +123,7 @@ namespace EggLink.DanhengServer.Database
}
}
public void MoveFromSqlite()
public static void MoveFromSqlite()
{
logger.Info("Moving from sqlite...");
@@ -130,16 +168,6 @@ namespace EggLink.DanhengServer.Database
}
}
public object GetLock(int uid)
{
if (!_lock.TryGetValue(uid, out object? value))
{
value = new();
_lock[uid] = value;
}
return value;
}
public static void InitializeSqlite()
{
var baseType = typeof(BaseDatabaseDataHelper);
@@ -150,7 +178,7 @@ namespace EggLink.DanhengServer.Database
typeof(DatabaseHelper).GetMethod("InitializeSqliteTable")?.MakeGenericMethod(type).Invoke(null, null);
}
}
public static void InitializeMysql()
{
sqlSugarScope?.DbMaintenance.CreateDatabase();
@@ -161,21 +189,35 @@ namespace EggLink.DanhengServer.Database
{
try
{
sqlSugarScope?.Queryable<T>().ToList();
} catch
sqlSugarScope?.Queryable<T>()
.Select(x => x)
.ToList();
}
catch
{
sqlSugarScope?.CodeFirst.InitTables<T>();
}
}
public T? GetInstance<T>(long uid) where T : class, new()
public static T? GetInstance<T>(int uid) where T : class, new()
{
try
{
lock (GetLock((int)uid))
if (!UidInstanceMap.TryGetValue(uid, out List<BaseDatabaseDataHelper>? value))
{
return sqlSugarScope?.Queryable<T>().Where(it => (it as BaseDatabaseDataHelper)!.Uid == uid).First();
value = [];
UidInstanceMap[uid] = value;
}
foreach (var instance in value)
{
if (instance is T)
{
return instance as T; // found
}
}
return null; // not found
}
catch (Exception e)
{
@@ -196,40 +238,91 @@ namespace EggLink.DanhengServer.Database
return instance;
}
public List<T>? GetAllInstance<T>() where T : class, new()
public static List<T>? GetAllInstance<T>() where T : class, new()
{
try
{
return sqlSugarScope?.Queryable<T>().ToList();
} catch(Exception e)
return sqlSugarScope?.Queryable<T>()
.Select(x => x)
.ToList();
}
catch (Exception e)
{
logger.Error("Unsupported type", e);
return null;
}
}
public void SaveInstance<T>(T instance) where T : class, new()
public static void SaveInstance<T>(T instance) where T : class, new()
{
lock (GetLock((instance as BaseDatabaseDataHelper)!.Uid))
{
sqlSugarScope?.Insertable(instance).ExecuteCommand();
}
sqlSugarScope?.Insertable(instance).ExecuteCommand();
UidInstanceMap[(instance as BaseDatabaseDataHelper)!.Uid].Add((instance as BaseDatabaseDataHelper)!); // add to the map
}
public void UpdateInstance<T>(T instance) where T : class, new()
{
lock (GetLock((instance as BaseDatabaseDataHelper)!.Uid))
//lock (GetLock((instance as BaseDatabaseDataHelper)!.Uid))
//{
// sqlSugarScope?.Updateable(instance).ExecuteCommand();
//}
}
public void CalcSaveDatabase() // per 5 min
{
if (LastSaveTick + TimeSpan.TicksPerMinute * 5 > DateTime.UtcNow.Ticks) return;
SaveDatabase();
}
public static void SaveDatabase() // per 5 min
{
try
{
var prev = DateTime.Now;
foreach (var uid in ToSaveUidList)
{
var value = UidInstanceMap[uid];
var baseType = typeof(BaseDatabaseDataHelper);
var assembly = typeof(BaseDatabaseDataHelper).Assembly;
var types = assembly.GetTypes().Where(t => t.IsSubclassOf(baseType));
foreach (var type in types)
{
var instance = value.Find(x => x.GetType() == type);
if (instance != null)
{
typeof(DatabaseHelper).GetMethod("SaveDatabaseType")?.MakeGenericMethod(type).Invoke(null, [instance]);
}
}
}
logger.Info($"Save database. Using {(DateTime.Now - prev).TotalSeconds.ToString()[..4]} seconds.");
ToSaveUidList.Clear();
}
catch (Exception e)
{
logger.Error("An error occurred while saving the database", e);
}
LastSaveTick = DateTime.UtcNow.Ticks;
}
public static void SaveDatabaseType<T>(T instance) where T : class, new()
{
try
{
sqlSugarScope?.Updateable(instance).ExecuteCommand();
}
catch (Exception e)
{
logger.Error("An error occurred while saving the database", e);
}
}
public void DeleteInstance<T>(T instance) where T : class, new()
public static void DeleteInstance<T>(T instance) where T : class, new()
{
lock (GetLock((instance as BaseDatabaseDataHelper)!.Uid))
{
sqlSugarScope?.Deleteable(instance).ExecuteCommand();
}
sqlSugarScope?.Deleteable(instance).ExecuteCommand();
UidInstanceMap[(instance as BaseDatabaseDataHelper)!.Uid].Remove((instance as BaseDatabaseDataHelper)!); // remove from the map
ToSaveUidList.Remove((instance as BaseDatabaseDataHelper)!.Uid); // remove from the save list
}
}
}

View File

@@ -198,6 +198,26 @@ namespace EggLink.DanhengServer.Database.Inventory
return relic;
}
public ItemData Clone()
{
return new()
{
UniqueId = UniqueId,
ItemId = ItemId,
Count = Count,
Level = Level,
Exp = Exp,
TotalExp = TotalExp,
Promotion = Promotion,
Rank = Rank,
Locked = Locked,
Discarded = Discarded,
MainAffix = MainAffix,
SubAffixes = SubAffixes.Select(x => x.Clone()).ToList(),
EquipAvatar = EquipAvatar
};
}
#endregion
}
@@ -236,5 +256,15 @@ namespace EggLink.DanhengServer.Database.Inventory
Cnt = (uint)Count,
Step = (uint)Step
};
public ItemSubAffix Clone()
{
return new()
{
Id = Id,
Count = Count,
Step = Step
};
}
}
}

View File

@@ -48,7 +48,7 @@ namespace EggLink.DanhengServer.Database.Player
public static PlayerData? GetPlayerByUid(long uid)
{
PlayerData? result = DatabaseHelper.Instance?.GetInstance<PlayerData>(uid);
PlayerData? result = DatabaseHelper.Instance?.GetInstance<PlayerData>((int)uid);
return result;
}
@@ -69,7 +69,7 @@ namespace EggLink.DanhengServer.Database.Player
public PlayerSimpleInfo ToSimpleProto(FriendOnlineStatus status)
{
var AvatarInfo = DatabaseHelper.Instance!.GetInstance<AvatarData>(Uid)!;
var AvatarInfo = DatabaseHelper.GetInstance<AvatarData>(Uid)!;
foreach (var avatar in AvatarInfo.Avatars)
{
@@ -120,7 +120,7 @@ namespace EggLink.DanhengServer.Database.Player
RecordInfo = new(),
};
var AvatarInfo = DatabaseHelper.Instance!.GetInstance<AvatarData>(Uid);
var AvatarInfo = DatabaseHelper.GetInstance<AvatarData>(Uid);
if (AvatarInfo != null)
{

View File

@@ -42,7 +42,7 @@ namespace EggLink.DanhengServer.Command
public bool HasPermission(string permission)
{
var account = DatabaseHelper.Instance!.GetInstance<AccountData>(Player.Uid)!;
var account = DatabaseHelper.GetInstance<AccountData>(Player.Uid)!;
return account.Permissions!.Contains(permission);
}
}

View File

@@ -20,6 +20,7 @@ namespace EggLink.DanhengServer.Game.Battle
public int MappingInfoId { get; set; }
public int RoundLimit { get; set; }
public int StageId { get; set; } = stages.Count > 0 ? stages[0].StageID : 0; // Set to 0 when hit monster
public int EventId { get; set; }
public int CustomLevel { get; set; }
public BattleEndStatus BattleEndStatus { get; set; }
@@ -49,6 +50,7 @@ namespace EggLink.DanhengServer.Game.Battle
public ItemList GetDropItemList()
{
if (BattleEndStatus != BattleEndStatus.BattleEndWin) return new();
var list = new ItemList();
foreach (var item in MonsterDropItems)
@@ -56,52 +58,9 @@ namespace EggLink.DanhengServer.Game.Battle
list.ItemList_.Add(item.ToProto());
}
// calculate drops
GameData.MappingInfoData.TryGetValue(MappingInfoId * 10 + WorldLevel, out var mapping);
if (mapping != null)
foreach (var item in Player.InventoryManager!.HandleMappingInfo(MappingInfoId, WorldLevel))
{
List<ItemData> items = [];
foreach (var item in mapping.DropItemList)
{
var random = Random.Shared.Next(0, 101);
if (random <= item.Chance)
{
var amount = item.ItemNum > 0 ? item.ItemNum : Random.Shared.Next(item.MinCount, item.MaxCount + 1);
GameData.ItemConfigData.TryGetValue(item.ItemID, out var itemData);
if (itemData == null) continue;
if (itemData.ItemMainType == ItemMainTypeEnum.Relic || itemData.ItemMainType == ItemMainTypeEnum.Equipment)
{
for (int i = 0; i < amount; i++)
{
items.Add(new ItemData()
{
ItemId = item.ItemID,
Count = 1,
});
}
}
else
{
items.Add(new ItemData()
{
ItemId = item.ItemID,
Count = amount,
});
}
}
}
foreach (var item in items)
{
var i = Player.InventoryManager!.AddItem(item.ItemId, item.Count, false, false)!;
i.Count = item.Count; // return the all thing
list.ItemList_.Add(i.ToProto());
}
DatabaseHelper.Instance!.UpdateInstance(Player.InventoryManager!.Data);
list.ItemList_.Add(item.ToProto());
}
return list;
@@ -137,7 +96,7 @@ namespace EggLink.DanhengServer.Game.Battle
var avatarType = AvatarType.AvatarFormalType;
if (avatar.AssistUid != 0)
{
var player = DatabaseHelper.Instance!.GetInstance<AvatarData>(avatar.AssistUid);
var player = DatabaseHelper.GetInstance<AvatarData>(avatar.AssistUid);
if (player != null)
{
avatarInstance = player.Avatars!.Find(item => item.GetAvatarId() == avatar.BaseAvatarId);

View File

@@ -44,6 +44,7 @@ namespace EggLink.DanhengServer.Game.Battle
Player.SceneInstance!.Entities.TryGetValue((int)entity, out var entityInstance);
if (entityInstance is EntityMonster monster)
{
if (targetList.Contains(monster)) continue; // avoid adding the same monster twice
targetList.Add(monster);
}
}
@@ -86,16 +87,15 @@ namespace EggLink.DanhengServer.Game.Battle
{
Player.LineupManager!.GetCurLineup()!.Heal(2000, false);
Player.SendPacket(new PacketSyncLineupNotify(Player.LineupManager!.GetCurLineup()!));
} else
{
Player.InventoryManager!.HandlePlaneEvent(prop.PropInfo.EventID);
}
Player.RogueManager!.GetRogueInstance()?.OnPropDestruct(prop);
}
if (targetList.Count > 0)
{
if (castAvatar != null && req.SkillIndex > 0)
{
skill.OnCast(castAvatar);
}
// Skill handle
if (!skill.TriggerBattle)
{
@@ -106,6 +106,7 @@ namespace EggLink.DanhengServer.Game.Battle
if (castAvatar != null)
{
skill.OnAttack(Player.SceneInstance!.AvatarInfo[(int)req.AttackedByEntityId], targetList);
skill.OnCast(castAvatar);
}
var triggerBattle = false;
@@ -187,6 +188,7 @@ namespace EggLink.DanhengServer.Game.Battle
BattleInstance battleInstance = new(Player, Player.LineupManager!.GetCurLineup()!, [stageConfig])
{
WorldLevel = Player.Data.WorldLevel,
EventId = eventId,
};
var avatarList = new List<AvatarSceneInfo>();
@@ -305,6 +307,7 @@ namespace EggLink.DanhengServer.Game.Battle
break;
default:
teleportToAnchor = true;
if (battle.CocoonWave > 0) teleportToAnchor = false;
updateStatus = false;
break;
}

View File

@@ -24,14 +24,14 @@ namespace EggLink.DanhengServer.Game.Battle.Skill.Action
public void OnCast(AvatarSceneInfo avatar)
{
avatar.BuffList.Add(new SceneBuff(BuffId, 1, avatar.AvatarInfo.AvatarId, duration));
avatar.AddBuff(new SceneBuff(BuffId, 1, avatar.AvatarInfo.AvatarId, duration));
}
public void OnHitTarget(AvatarSceneInfo avatar, List<EntityMonster> entities)
{
foreach (var entity in entities)
{
entity.BuffList.Add(new SceneBuff(BuffId, 1, avatar.AvatarInfo.AvatarId, duration));
entity.AddBuff(new SceneBuff(BuffId, 1, avatar.AvatarInfo.AvatarId, duration));
}
}
}

View File

@@ -18,7 +18,7 @@ namespace EggLink.DanhengServer.Game.Friend
public void AddFriend(int targetUid)
{
var target = DatabaseHelper.Instance!.GetInstance<FriendData>(targetUid);
var target = DatabaseHelper.GetInstance<FriendData>(targetUid);
if (target == null)
{
return;
@@ -50,6 +50,7 @@ namespace EggLink.DanhengServer.Game.Friend
var targetPlayer = Listener.GetActiveConnection(targetUid);
targetPlayer?.SendPacket(new PacketSyncApplyFriendScNotify(Player.Data));
targetPlayer?.Player!.FriendManager!.FriendData.ReceiveApplyList.Add(Player.Uid);
DatabaseHelper.ToSaveUidList.Add(targetUid);
DatabaseHelper.Instance!.UpdateInstance(FriendData);
DatabaseHelper.Instance!.UpdateInstance(target);
@@ -57,7 +58,7 @@ namespace EggLink.DanhengServer.Game.Friend
public PlayerData? ConfirmAddFriend(int targetUid)
{
var target = DatabaseHelper.Instance!.GetInstance<FriendData>(targetUid);
var target = DatabaseHelper.GetInstance<FriendData>(targetUid);
if (target == null)
{
return null;
@@ -99,7 +100,7 @@ namespace EggLink.DanhengServer.Game.Friend
public void RefuseAddFriend(int targetUid)
{
var target = DatabaseHelper.Instance!.GetInstance<FriendData>(targetUid);
var target = DatabaseHelper.GetInstance<FriendData>(targetUid);
if (target == null)
{
return;
@@ -115,6 +116,7 @@ namespace EggLink.DanhengServer.Game.Friend
var targetPlayer = Listener.GetActiveConnection(targetUid);
targetPlayer?.Player!.FriendManager!.FriendData.SendApplyList.Remove(Player.Uid);
DatabaseHelper.ToSaveUidList.Add(targetUid);
DatabaseHelper.Instance!.UpdateInstance(FriendData);
DatabaseHelper.Instance!.UpdateInstance(target);
@@ -164,8 +166,6 @@ namespace EggLink.DanhengServer.Game.Friend
}
}
DatabaseHelper.Instance!.UpdateInstance(FriendData);
// receive message
var recvPlayer = Listener.GetActiveConnection(recvUid)?.Player!;
if (recvPlayer != null)
@@ -174,7 +174,7 @@ namespace EggLink.DanhengServer.Game.Friend
} else
{
// offline
var friendData = DatabaseHelper.Instance!.GetInstance<FriendData>(recvUid);
var friendData = DatabaseHelper.GetInstance<FriendData>(recvUid);
if (friendData == null) return; // not exist maybe server profile
if (!friendData.ChatHistory.TryGetValue(sendUid, out FriendChatHistory? history))
{
@@ -183,7 +183,7 @@ namespace EggLink.DanhengServer.Game.Friend
}
history.MessageList.Add(data);
DatabaseHelper.Instance!.UpdateInstance(friendData);
DatabaseHelper.ToSaveUidList.Add(recvUid);
}
}
@@ -216,8 +216,6 @@ namespace EggLink.DanhengServer.Game.Friend
}
Player.SendPacket(proto);
DatabaseHelper.Instance!.UpdateInstance(FriendData);
}
public List<ChatMessageData> GetHistoryInfo(int uid)

View File

@@ -8,6 +8,7 @@ using EggLink.DanhengServer.Server.Packet.Send.Avatar;
using EggLink.DanhengServer.Server.Packet.Send.Player;
using EggLink.DanhengServer.Server.Packet.Send.Scene;
using EggLink.DanhengServer.Util;
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace EggLink.DanhengServer.Game.Inventory
@@ -142,20 +143,22 @@ namespace EggLink.DanhengServer.Game.Inventory
break;
}
ItemData? clone = null;
if (itemData != null)
{
clone = itemData.Clone();
Player.SendPacket(new PacketPlayerSyncScNotify(itemData));
clone.Count = count;
if (notify)
{
itemData.Count = count; // only notify the increase count
Player.SendPacket(new PacketScenePlaneEventScNotify(itemData));
Player.SendPacket(new PacketScenePlaneEventScNotify(clone));
}
}
if (save)
DatabaseHelper.Instance?.UpdateInstance(Data);
return itemData;
return clone ?? itemData;
}
public ItemData PutItem(int itemId, int count, int rank = 0, int promotion = 0, int level = 0, int exp = 0, int totalExp = 0, int mainAffix = 0, List<ItemSubAffix>? subAffixes = null, int uniqueId = 0)
@@ -322,8 +325,88 @@ namespace EggLink.DanhengServer.Game.Inventory
GameData.PlaneEventData.TryGetValue(eventId * 10 + Player.Data.WorldLevel, out var planeEvent);
if (planeEvent == null) return;
GameData.RewardDataData.TryGetValue(planeEvent.Reward, out var rewardData);
if (rewardData == null) return;
rewardData.GetItems().ForEach(x => AddItem(x.Item1, x.Item2));
rewardData?.GetItems().ForEach(x => AddItem(x.Item1, x.Item2));
foreach (var id in planeEvent.DropList)
{
GameData.RewardDataData.TryGetValue(id, out var reward);
reward?.GetItems().ForEach(x => AddItem(x.Item1, x.Item2));
}
}
public List<ItemData> HandleMappingInfo(int mappingId, int worldLevel)
{
// calculate drops
List<ItemData> items = [];
GameData.MappingInfoData.TryGetValue(mappingId * 10 + worldLevel, out var mapping);
if (mapping != null)
{
foreach (var item in mapping.DropItemList)
{
var random = Random.Shared.Next(0, 101);
if (random <= item.Chance)
{
var amount = item.ItemNum > 0 ? item.ItemNum : Random.Shared.Next(item.MinCount, item.MaxCount + 1);
GameData.ItemConfigData.TryGetValue(item.ItemID, out var itemData);
if (itemData == null) continue;
items.Add(new ItemData()
{
ItemId = item.ItemID,
Count = amount,
});
}
}
// randomize the order of the relics
var relics = mapping.DropRelicItemList.OrderBy(x => Random.Shared.Next()).ToList();
var relic5Count = Random.Shared.Next(worldLevel - 4, worldLevel - 2);
var relic4Count = worldLevel - 2;
foreach (var relic in relics)
{
var random = Random.Shared.Next(0, 101);
if (random <= relic.Chance)
{
var amount = relic.ItemNum > 0 ? relic.ItemNum : Random.Shared.Next(relic.MinCount, relic.MaxCount + 1);
GameData.ItemConfigData.TryGetValue(relic.ItemID, out var itemData);
if (itemData == null) continue;
if (itemData.Rarity == ItemRarityEnum.SuperRare && relic5Count > 0)
{
relic5Count--;
}
else if (itemData.Rarity == ItemRarityEnum.VeryRare && relic4Count > 0)
{
relic4Count--;
}
else
{
continue;
}
items.Add(new ItemData()
{
ItemId = relic.ItemID,
Count = 1,
});
}
}
foreach (var item in items)
{
var i = Player.InventoryManager!.AddItem(item.ItemId, item.Count, false, false)!;
i.Count = item.Count; // return the all thing
}
DatabaseHelper.Instance!.UpdateInstance(Player.InventoryManager!.Data);
}
return items;
}
public ItemData? ComposeItem(int composeId, int count)
@@ -340,6 +423,63 @@ namespace EggLink.DanhengServer.Game.Inventory
return AddItem(composeConfig.ItemID, count, false);
}
public List<ItemData> SellItem(ItemCostData costData)
{
List<ItemData> items = [];
Dictionary<int, int> ItemMap = [];
foreach (var cost in costData.ItemList)
{
if (cost.EquipmentUniqueId != 0) // equipment
{
var itemData = Data.EquipmentItems.Find(x => x.UniqueId == cost.EquipmentUniqueId);
if (itemData == null) continue;
RemoveItem(itemData.ItemId, 1, (int)cost.EquipmentUniqueId);
GameData.ItemConfigData.TryGetValue(itemData.ItemId, out var itemConfig);
if (itemConfig == null) continue;
foreach (var returnItem in itemConfig.ReturnItemIDList) // return items
{
if (!ItemMap.ContainsKey(returnItem.ItemID))
{
ItemMap[returnItem.ItemID] = 0;
}
ItemMap[returnItem.ItemID] += returnItem.ItemNum;
}
}
else if (cost.RelicUniqueId != 0) // relic
{
var itemData = Data.RelicItems.Find(x => x.UniqueId == cost.RelicUniqueId);
if (itemData == null) continue;
RemoveItem(itemData.ItemId, 1, (int)cost.RelicUniqueId);
GameData.ItemConfigData.TryGetValue(itemData.ItemId, out var itemConfig);
if (itemConfig == null) continue;
foreach (var returnItem in itemConfig.ReturnItemIDList) // return items
{
if (!ItemMap.ContainsKey(returnItem.ItemID))
{
ItemMap[returnItem.ItemID] = 0;
}
ItemMap[returnItem.ItemID] += returnItem.ItemNum;
}
}
else
{
RemoveItem((int)cost.PileItem.ItemId, (int)cost.PileItem.ItemNum);
}
}
foreach (var itemInfo in ItemMap)
{
var item = AddItem(itemInfo.Key, itemInfo.Value, false, false);
if (item != null)
{
items.Add(item);
}
}
return items;
}
#region Equip
public void EquipAvatar(int baseAvatarId, int equipmentUniqueId)
@@ -420,6 +560,19 @@ namespace EggLink.DanhengServer.Game.Inventory
Player.SendPacket(new PacketPlayerSyncScNotify(avatarData, itemData));
}
public void UnequipEquipment(int baseAvatarId)
{
var avatarData = Player.AvatarManager!.GetAvatar(baseAvatarId);
if (avatarData == null) return;
var itemData = Data.EquipmentItems.Find(x => x.UniqueId == avatarData.EquipId);
if (itemData == null) return;
itemData.EquipAvatar = 0;
avatarData.EquipId = 0;
DatabaseHelper.Instance!.UpdateInstance(Data);
DatabaseHelper.Instance!.UpdateInstance(Player.AvatarManager.AvatarData!);
Player.SendPacket(new PacketPlayerSyncScNotify(avatarData, itemData));
}
public List<ItemData> LevelUpAvatar(int baseAvatarId, ItemCostData item)
{
var avatarData = Player.AvatarManager!.GetAvatar(baseAvatarId);

View File

@@ -323,6 +323,7 @@ namespace EggLink.DanhengServer.Game.Lineup
public void GainMp(int count, bool sendPacket = true)
{
count = Math.Min(Math.Max(0, count), 2);
var curLineup = GetCurLineup()!;
curLineup.Mp += count;
curLineup.Mp = Math.Min(Math.Max(0, curLineup.Mp), 5);

View File

@@ -486,25 +486,6 @@ namespace EggLink.DanhengServer.Game.Mission
if (req.StageId.ToString().StartsWith(subMission.ParamInt1.ToString()))
{
FinishSubMission(mission);
} else
{
// may be a boss stage
foreach (var id in subMission.StageList)
{
if (req.StageId.ToString().StartsWith(id.ToString()))
{
var nextStageId = subMission.StageList.Find(x => x > id);
if (nextStageId == 0)
{
FinishSubMission(mission);
} else
{
Player.BattleManager!.StartStage(nextStageId); // need to improve
}
break;
}
}
}
}
}

View File

@@ -299,6 +299,13 @@ namespace EggLink.DanhengServer.Game.Player
}
}
public void OnHeartBeat()
{
OnStaminaRecover();
DatabaseHelper.ToSaveUidList.SafeAdd(Uid);
}
#endregion
#region Scene Actions
@@ -319,8 +326,6 @@ namespace EggLink.DanhengServer.Game.Player
}
}
}
OnStaminaRecover();
}
public EntityProp? InteractProp(int propEntityId, int interactId)

View File

@@ -35,7 +35,7 @@ namespace EggLink.DanhengServer.Game.Rogue.Scene
Scene.IsLoaded = true;
}
public override List<IGameEntity>? LoadGroup(GroupInfo info)
public override List<IGameEntity>? LoadGroup(GroupInfo info, bool forceLoad = false)
{
var entityList = new List<IGameEntity>();
foreach (var npc in info.NPCList)

View File

@@ -53,6 +53,9 @@ namespace EggLink.DanhengServer.Game.Scene.Entity
}
instance.Buffs.Add(new MazeBuff(buff));
}
Scene.Player.SendPacket(new PacketSyncEntityBuffChangeListScNotify(this, BuffList));
BuffList.Clear();
}
public int GetStageId()

View File

@@ -14,13 +14,13 @@ namespace EggLink.DanhengServer.Game.Scene
public int BuffLevel { get; private set; } = buffLevel;
public int OwnerAvatarId { get; private set; } = owner;
public int Duration { get; private set; } = duration * 1000; // in milliseconds
public int Duration { get; set; } = duration * 1000; // in milliseconds
public long CreatedTime { get; private set; } = Extensions.GetUnixMs();
public Dictionary<string, float> DynamicValues = [];
public bool IsExpired()
{
if (Duration == -1)
if (Duration < 0)
return false; // Permanent buff
return Extensions.GetUnixMs() - CreatedTime >= Duration;
}

View File

@@ -58,7 +58,7 @@ namespace EggLink.DanhengServer.Game.Scene
if (oldGroupId.Contains(group.Id)) // check if it should be unloaded
{
if (group.ForceUnloadCondition.IsTrue(Scene.Player.MissionManager!.Data, false))
if (group.ForceUnloadCondition.IsTrue(Scene.Player.MissionManager!.Data, false) || group.UnloadCondition.IsTrue(Scene.Player.MissionManager!.Data, false))
{
foreach (var entity in Scene.Entities.Values)
{
@@ -83,13 +83,19 @@ namespace EggLink.DanhengServer.Game.Scene
}
}
public virtual List<IGameEntity>? LoadGroup(GroupInfo info)
public virtual List<IGameEntity>? LoadGroup(GroupInfo info, bool forceLoad = false)
{
var missionData = Scene.Player.MissionManager!.Data;
if (!info.LoadCondition.IsTrue(missionData) || info.UnloadCondition.IsTrue(missionData, false) || info.ForceUnloadCondition.IsTrue(missionData, false))
if ((!info.LoadCondition.IsTrue(missionData) || info.UnloadCondition.IsTrue(missionData, false) || info.ForceUnloadCondition.IsTrue(missionData, false)) && !forceLoad)
{
return null;
}
if (Scene.Entities.Values.ToList().FindIndex(x => x.GroupID == info.Id) != -1) // check if group is already loaded
{
return null;
}
var entityList = new List<IGameEntity>();
foreach (var npc in info.NPCList)
{
@@ -132,7 +138,7 @@ namespace EggLink.DanhengServer.Game.Scene
{
var group = Scene.FloorInfo?.Groups.TryGetValue(groupId, out GroupInfo? v1) == true ? v1 : null;
if (group == null) { return null; }
var entities = LoadGroup(group);
var entities = LoadGroup(group, true);
if (sendPacket && entities != null && entities.Count > 0)
{

View File

@@ -318,6 +318,7 @@ namespace EggLink.DanhengServer.Game.Scene
public List<SceneBuff> BuffList = [];
public void AddBuff(SceneBuff buff)
{
if (BuffList.FindIndex(x => x.BuffID == buff.BuffID) != -1) return; // already have buff
BuffList.Add(buff);
Player.SendPacket(new PacketSyncEntityBuffChangeListScNotify(this, buff));
}
@@ -332,6 +333,9 @@ namespace EggLink.DanhengServer.Game.Scene
}
instance.Buffs.Add(new MazeBuff(buff));
}
Player.SendPacket(new PacketSyncEntityBuffChangeListScNotify(this, BuffList));
BuffList.Clear();
}
public SceneEntityInfo ToProto()

View File

@@ -135,6 +135,9 @@ namespace EggLink.DanhengServer.Program
private static void PerformCleanup()
{
Listener.Connections.Values.ToList().ForEach(x => x.Stop());
DatabaseHelper.SaveThread?.Interrupt();
DatabaseHelper.SaveDatabase();
}
}
}

View File

@@ -0,0 +1,21 @@
using EggLink.DanhengServer.Proto;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EggLink.DanhengServer.Server.Packet.Recv.Avatar
{
[Opcode(CmdIds.TakeOffEquipmentCsReq)]
public class HandlerTakeOffEquipmentCsReq : Handler
{
public override void OnHandle(Connection connection, byte[] header, byte[] data)
{
var req = TakeOffEquipmentCsReq.Parser.ParseFrom(data);
connection.Player!.InventoryManager!.UnequipEquipment((int)req.DressAvatarId);
connection.SendPacket(CmdIds.TakeOffEquipmentScRsp);
}
}
}

View File

@@ -10,7 +10,7 @@ namespace EggLink.DanhengServer.Server.Packet.Recv.Avatar
var req = TakeOffRelicCsReq.Parser.ParseFrom(data);
foreach (var param in req.RelicTypeList)
{
connection.Player!.InventoryManager!.UnequipRelic((int)req.BaseAvatarId, (int)param);
connection.Player!.InventoryManager!.UnequipRelic((int)req.DressAvatarId, (int)param);
}
connection.SendPacket(CmdIds.TakeOffRelicScRsp);
}

View File

@@ -31,6 +31,10 @@ namespace EggLink.DanhengServer.Server.Packet.Recv.Battle
if (req.HitTargetEntityIdList.Count == 0)
{
// didnt hit any target
if (info != null && req.SkillIndex > 0)
{
mazeSkill.OnCast(info);
}
connection.SendPacket(new PacketSceneCastSkillScRsp(req.CastEntityId));
}
else

View File

@@ -15,7 +15,7 @@ namespace EggLink.DanhengServer.Server.Packet.Recv.Player
{
var req = PlayerGetTokenCsReq.Parser.ParseFrom(data);
var account = DatabaseHelper.Instance?.GetInstance<AccountData>(long.Parse(req.AccountUid));
var account = DatabaseHelper.Instance?.GetInstance<AccountData>(int.Parse(req.AccountUid));
if (account == null)
{
connection.SendPacket(new PacketPlayerGetTokenScRsp());
@@ -31,7 +31,7 @@ namespace EggLink.DanhengServer.Server.Packet.Recv.Player
}
connection.State = SessionState.WAITING_FOR_LOGIN;
var pd = DatabaseHelper.Instance?.GetInstance<PlayerData>(long.Parse(req.AccountUid));
var pd = DatabaseHelper.Instance?.GetInstance<PlayerData>(int.Parse(req.AccountUid));
if (pd == null)
connection.Player = new PlayerInstance(int.Parse(req.AccountUid));
else

View File

@@ -18,6 +18,8 @@ namespace EggLink.DanhengServer.Server.Packet.Recv.Player
{
connection.SendPacket(new PacketPlayerHeartBeatScRsp((long)req.ClientTimeMs));
}
connection.Player?.OnHeartBeat();
}
}
}

View File

@@ -0,0 +1,21 @@
using EggLink.DanhengServer.Proto;
using EggLink.DanhengServer.Server.Packet.Send.Shop;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EggLink.DanhengServer.Server.Packet.Recv.Shop
{
[Opcode(CmdIds.SellItemCsReq)]
public class HandlerSellItemCsReq : Handler
{
public override void OnHandle(Connection connection, byte[] header, byte[] data)
{
var req = SellItemCsReq.Parser.ParseFrom(data);
var items = connection.Player!.InventoryManager!.SellItem(req.CostData);
connection.SendPacket(new PacketSellItemScRsp(items));
}
}
}

View File

@@ -30,6 +30,7 @@ namespace EggLink.DanhengServer.Server.Packet.Send.Battle
Unk1 = new(),
Unk2 = new(),
Unk3 = new(),
EventId = (uint)battle.EventId,
};
SetData(proto);

View File

@@ -23,5 +23,23 @@ namespace EggLink.DanhengServer.Server.Packet.Send.Scene
SetData(proto);
}
public PacketSyncEntityBuffChangeListScNotify(IGameEntity entity, List<SceneBuff> buffs) : base(CmdIds.SyncEntityBuffChangeListScNotify)
{
var proto = new SyncEntityBuffChangeListScNotify();
foreach (var buff in buffs)
{
buff.Duration = 0;
var change = new EntityBuffChange()
{
EntityId = (uint)entity.EntityID,
BuffInfo = buff.ToProto(),
};
proto.EntityBuffChangeList.Add(change);
}
SetData(proto);
}
}
}

View File

@@ -0,0 +1,26 @@
using EggLink.DanhengServer.Database.Inventory;
using EggLink.DanhengServer.Proto;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EggLink.DanhengServer.Server.Packet.Send.Shop
{
public class PacketSellItemScRsp : BasePacket
{
public PacketSellItemScRsp(List<ItemData> items) : base(CmdIds.SellItemScRsp)
{
var proto = new SellItemScRsp()
{
ReturnItemList = new()
{
ItemList_ = { items.Select(x => x.ToProto())}
}
};
SetData(proto);
}
}
}