diff --git a/Common/Common.csproj b/Common/Common.csproj
index c0a66cbb..04252ba2 100644
--- a/Common/Common.csproj
+++ b/Common/Common.csproj
@@ -18,6 +18,7 @@
+
diff --git a/Common/Data/Config/Scene/FloorInfo.cs b/Common/Data/Config/Scene/FloorInfo.cs
index 09c30324..c2165291 100644
--- a/Common/Data/Config/Scene/FloorInfo.cs
+++ b/Common/Data/Config/Scene/FloorInfo.cs
@@ -1,17 +1,18 @@
-using EggLink.DanhengServer.Enums.Scene;
+using System.Collections.Concurrent;
+using EggLink.DanhengServer.Enums.Scene;
using Newtonsoft.Json;
namespace EggLink.DanhengServer.Data.Config.Scene;
public class FloorInfo
{
- [JsonIgnore] public Dictionary CachedTeleports = [];
+ [JsonIgnore] public ConcurrentDictionary CachedTeleports = [];
- [JsonIgnore] public Dictionary Groups = [];
+ [JsonIgnore] public ConcurrentDictionary Groups = [];
[JsonIgnore] public bool Loaded;
- [JsonIgnore] public List UnlockedCheckpoints = [];
+ [JsonIgnore] public ConcurrentBag UnlockedCheckpoints = [];
public int FloorID { get; set; }
public int StartGroupIndex { get; set; }
diff --git a/Common/Data/Config/SummonUnit/UnitCustomTriggerConfigInfo.cs b/Common/Data/Config/SummonUnit/UnitCustomTriggerConfigInfo.cs
index f0367a21..e0778494 100644
--- a/Common/Data/Config/SummonUnit/UnitCustomTriggerConfigInfo.cs
+++ b/Common/Data/Config/SummonUnit/UnitCustomTriggerConfigInfo.cs
@@ -17,8 +17,9 @@ public class UnitCustomTriggerConfigInfo
public bool DependOnServerTarget { get; set; }
public bool IsSingle { get; set; }
// EntityType TargetEntityType { get; set; }
- public DynamicFloat TargetGroupID { get; set; }
- public DynamicFloat TargetID { get; set; }
+ public DynamicFloat TargetGroupID { get; set; } = new();
+
+ public DynamicFloat TargetID { get; set; } = new();
// EntityType[] TargetTypes { get; set; }
// PredicateConfigInfo TargetFilter { get; set; }
public string ColliderRelativePath { get; set; } = "";
diff --git a/Common/Data/Excel/RogueBuffGroupExcel.cs b/Common/Data/Excel/RogueBuffGroupExcel.cs
index 92e66afc..32d114c6 100644
--- a/Common/Data/Excel/RogueBuffGroupExcel.cs
+++ b/Common/Data/Excel/RogueBuffGroupExcel.cs
@@ -35,7 +35,9 @@ public class RogueBuffGroupExcel : ExcelResource
if (IsLoaded) return;
var count = 0;
foreach (var buffId in BuffTagList)
- if (GameData.RogueBuffData.FirstOrDefault(x => x.Value.RogueBuffTag == buffId).Value is RogueBuffExcel buff)
+ {
+ List buffs = [.. GameData.RogueBuffData.Values];
+ if (buffs.FirstOrDefault(x => x.RogueBuffTag == buffId) is { } buff)
{
BuffList.SafeAdd(buff);
count++;
@@ -48,6 +50,7 @@ public class RogueBuffGroupExcel : ExcelResource
BuffList.SafeAddRange(group.BuffList);
count++;
}
+ }
if (count == BuffTagList.Count) IsLoaded = true;
}
diff --git a/Common/Data/Excel/RogueTournBuffGroupExcel.cs b/Common/Data/Excel/RogueTournBuffGroupExcel.cs
index df015ce1..312f6f14 100644
--- a/Common/Data/Excel/RogueTournBuffGroupExcel.cs
+++ b/Common/Data/Excel/RogueTournBuffGroupExcel.cs
@@ -34,7 +34,9 @@ public class RogueTournBuffGroupExcel : ExcelResource
if (IsLoaded) return;
var count = 0;
foreach (var buffId in RogueBuffDrop)
- if (GameData.RogueTournBuffData.FirstOrDefault(x => x.Value.RogueBuffTag == buffId).Value is { } buff)
+ {
+ List buffs = [.. GameData.RogueTournBuffData.Values];
+ if (buffs.FirstOrDefault(x => x.RogueBuffTag == buffId) is { } buff)
{
BuffList.SafeAdd(buff);
count++;
@@ -47,6 +49,7 @@ public class RogueTournBuffGroupExcel : ExcelResource
BuffList.SafeAddRange(group.BuffList);
count++;
}
+ }
if (count == RogueBuffDrop.Count) IsLoaded = true;
}
diff --git a/Common/Data/GameData.cs b/Common/Data/GameData.cs
index 5606d1d7..2f046121 100644
--- a/Common/Data/GameData.cs
+++ b/Common/Data/GameData.cs
@@ -2,6 +2,7 @@
using EggLink.DanhengServer.Data.Custom;
using EggLink.DanhengServer.Data.Excel;
using EggLink.DanhengServer.Enums.Rogue;
+using System.Collections.Concurrent;
namespace EggLink.DanhengServer.Data;
@@ -98,7 +99,7 @@ public static class GameData
#region Maze
public static Dictionary NpcDataData { get; private set; } = [];
- public static Dictionary FloorInfoData { get; } = [];
+ public static ConcurrentDictionary FloorInfoData { get; } = [];
public static Dictionary MapEntranceData { get; private set; } = [];
public static Dictionary MazePlaneData { get; private set; } = [];
public static Dictionary MazePropData { get; private set; } = [];
diff --git a/Common/Data/ResourceManager.cs b/Common/Data/ResourceManager.cs
index be53a787..78f10e24 100644
--- a/Common/Data/ResourceManager.cs
+++ b/Common/Data/ResourceManager.cs
@@ -21,14 +21,18 @@ public class ResourceManager
public static void LoadGameData()
{
LoadExcel();
- LoadFloorInfo();
- LoadMissionInfo();
- LoadMazeSkill();
- LoadSummonUnit();
- LoadDialogueInfo();
- LoadPerformanceInfo();
- LoadSubMissionInfo();
- LoadRogueChestMapInfo();
+
+ var t1 = Task.Run(LoadFloorInfo);
+ var t2 = Task.Run(LoadMazeSkill);
+ var t3 = Task.Run(LoadSummonUnit);
+ var t4 = Task.Run(() =>
+ {
+ LoadMissionInfo();
+ LoadSubMissionInfo();
+ });
+ var t5 = Task.Run(LoadPerformanceInfo);
+ var t6 = Task.Run(LoadDialogueInfo);
+ var t7 = Task.Run(LoadRogueChestMapInfo);
GameData.ActivityConfig = LoadCustomFile("Activity", "ActivityConfig") ?? new ActivityConfig();
GameData.BannersConfig = LoadCustomFile("Banner", "Banners") ?? new BannersConfig();
GameData.RogueMapGenData = LoadCustomFile>>("Rogue Map", "RogueMapGen") ?? [];
@@ -38,6 +42,8 @@ public class ResourceManager
LoadCustomFile("Rogue Miracle Effect", "RogueMiracleEffectGen") ??
new RogueMiracleEffectConfig();
LoadChessRogueRoomData();
+
+ Task.WaitAll(t1, t2, t3, t4, t5, t6, t7);
}
public static void LoadExcel()
@@ -161,8 +167,11 @@ public class ResourceManager
return;
}
- // Load floor infos
- foreach (var file in directory.GetFiles())
+ var files = directory.GetFiles();
+
+ // Load floor infos in parallel
+ var res = Parallel.ForEach(files, file =>
+ {
try
{
using var reader = file.OpenRead();
@@ -170,7 +179,54 @@ public class ResourceManager
var text = reader2.ReadToEnd();
var info = JsonConvert.DeserializeObject(text);
var name = file.Name[..file.Name.IndexOf('.')];
- GameData.FloorInfoData.Add(name, info!);
+ if (info == null) return;
+ GameData.FloorInfoData[name] = info;
+
+ // Load group infos sequentially to maintain order
+ foreach (var groupInfo in info.GroupInstanceList)
+ {
+ if (groupInfo.IsDelete) continue;
+ FileInfo groupFile = new(ConfigManager.Config.Path.ResourcePath + "/" + groupInfo.GroupPath);
+ if (!groupFile.Exists) continue;
+
+ try
+ {
+ using var groupReader = groupFile.OpenRead();
+ using StreamReader groupReader2 = new(groupReader);
+ var groupText = groupReader2.ReadToEnd();
+ var group = JsonConvert.DeserializeObject(groupText);
+ if (group != null)
+ {
+ group.Id = groupInfo.ID;
+ // Use a sorted collection or maintain order manually
+ info.Groups[groupInfo.ID] = group;
+ group.Load();
+
+ // Load graph
+ var graphPath = ConfigManager.Config.Path.ResourcePath + "/" + group.LevelGraph;
+ var graphFile = new FileInfo(graphPath);
+ if (graphFile.Exists)
+ {
+ using var graphReader = graphFile.OpenRead();
+ using StreamReader graphReader2 = new(graphReader);
+ var graphText = graphReader2.ReadToEnd().Replace("$type", "Type");
+ var graphObj = JObject.Parse(graphText);
+ var graphInfo = LevelGraphConfigInfo.LoadFromJsonObject(graphObj);
+ group.LevelGraphConfig = graphInfo;
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Logger.Error(
+ I18NManager.Translate("Server.ServerInfo.FailedToReadItem", groupFile.Name,
+ I18NManager.Translate("Word.Error")), ex);
+ }
+ }
+
+ if (info.Groups.Count == 0) missingGroupInfos = true;
+
+ info.OnLoad();
}
catch (Exception ex)
{
@@ -178,63 +234,27 @@ public class ResourceManager
I18NManager.Translate("Server.ServerInfo.FailedToReadItem", file.Name,
I18NManager.Translate("Word.Error")), ex);
}
+ });
- foreach (var info in GameData.FloorInfoData.Values)
+ // wait it done
+ while (!res.IsCompleted)
{
- foreach (var groupInfo in info.GroupInstanceList)
- {
- if (groupInfo.IsDelete) continue;
- FileInfo file = new(ConfigManager.Config.Path.ResourcePath + "/" + groupInfo.GroupPath);
- if (!file.Exists) continue;
- try
- {
- using var reader = file.OpenRead();
- using StreamReader reader2 = new(reader);
- var text = reader2.ReadToEnd();
- var group = JsonConvert.DeserializeObject(text);
- if (group != null)
- {
- group.Id = groupInfo.ID;
- info.Groups.TryAdd(groupInfo.ID, group);
- group.Load();
-
- // load graph
- var graphPath = ConfigManager.Config.Path.ResourcePath + "/" + group.LevelGraph;
- var graphFile = new FileInfo(graphPath);
- if (graphFile.Exists)
- {
- using var graphReader = graphFile.OpenRead();
- using StreamReader graphReader2 = new(graphReader);
- var graphText = graphReader2.ReadToEnd().Replace("$type", "Type");
- var graphObj = JObject.Parse(graphText);
- var graphInfo = LevelGraphConfigInfo.LoadFromJsonObject(graphObj);
- group.LevelGraphConfig = graphInfo;
- }
- }
- }
- catch (Exception ex)
- {
- Logger.Error(
- I18NManager.Translate("Server.ServerInfo.FailedToReadItem", file.Name,
- I18NManager.Translate("Word.Error")), ex);
- }
-
- if (info.Groups.Count == 0) missingGroupInfos = true;
- }
-
- info.OnLoad();
+ Thread.Sleep(10);
}
if (missingGroupInfos)
+ {
Logger.Warn(I18NManager.Translate("Server.ServerInfo.ConfigMissing",
I18NManager.Translate("Word.FloorGroupInfo"),
$"{ConfigManager.Config.Path.ResourcePath}/Config/LevelOutput/SharedRuntimeGroup",
I18NManager.Translate("Word.FloorGroupMissingResult")));
+ }
Logger.Info(I18NManager.Translate("Server.ServerInfo.LoadedItems", GameData.FloorInfoData.Count.ToString(),
I18NManager.Translate("Word.FloorInfo")));
}
+
public static void LoadMissionInfo()
{
Logger.Info(I18NManager.Translate("Server.ServerInfo.LoadingItem", I18NManager.Translate("Word.MissionInfo")));
@@ -250,14 +270,14 @@ public class ResourceManager
var missingMissionInfos = false;
var count = 0;
- foreach (var missionExcel in GameData.MainMissionData)
+ var res = Parallel.ForEach(GameData.MainMissionData, missionExcel =>
{
var path =
$"{ConfigManager.Config.Path.ResourcePath}/Config/Level/Mission/{missionExcel.Key}/MissionInfo_{missionExcel.Key}.json";
if (!File.Exists(path))
{
missingMissionInfos = true;
- continue;
+ return;
}
var json = File.ReadAllText(path);
@@ -271,6 +291,12 @@ public class ResourceManager
{
missingMissionInfos = true;
}
+ });
+
+ // wait it done
+ while (!res.IsCompleted)
+ {
+ Thread.Sleep(10);
}
if (missingMissionInfos)
@@ -340,14 +366,14 @@ public class ResourceManager
Logger.Info(I18NManager.Translate("Server.ServerInfo.LoadingItem",
I18NManager.Translate("Word.MazeSkillInfo")));
var count = 0;
- foreach (var adventure in GameData.AdventurePlayerData.Values)
+ var res = Parallel.ForEach(GameData.AdventurePlayerData.Values, adventure =>
{
var avatar = GameData.AvatarConfigData[adventure.AvatarID];
var adventurePath = adventure.PlayerJsonPath.Replace("_Config.json", "_Ability.json")
.Replace("ConfigCharacter", "ConfigAdventureAbility");
var path = ConfigManager.Config.Path.ResourcePath + "/" + adventurePath;
var file = new FileInfo(path);
- if (!file.Exists) continue;
+ if (!file.Exists) return;
try
{
using var reader = file.OpenRead();
@@ -363,6 +389,12 @@ public class ResourceManager
I18NManager.Translate("Server.ServerInfo.FailedToReadItem", adventurePath,
I18NManager.Translate("Word.Error")), ex);
}
+ });
+
+ // wait it done
+ while (!res.IsCompleted)
+ {
+ Thread.Sleep(10);
}
if (count < GameData.AdventurePlayerData.Count)
@@ -380,11 +412,11 @@ public class ResourceManager
Logger.Info(I18NManager.Translate("Server.ServerInfo.LoadingItem",
I18NManager.Translate("Word.SummonUnitInfo")));
var count = 0;
- foreach (var summonUnit in GameData.SummonUnitDataData.Values)
+ var res = Parallel.ForEach(GameData.SummonUnitDataData.Values, summonUnit =>
{
var path = ConfigManager.Config.Path.ResourcePath + "/" + summonUnit.JsonPath;
var file = new FileInfo(path);
- if (!file.Exists) continue;
+ if (!file.Exists) return;
try
{
using var reader = file.OpenRead();
@@ -403,6 +435,12 @@ public class ResourceManager
I18NManager.Translate("Server.ServerInfo.FailedToReadItem", summonUnit.JsonPath,
I18NManager.Translate("Word.Error")), ex);
}
+ });
+
+ // wait it done
+ while (!res.IsCompleted)
+ {
+ Thread.Sleep(10);
}
if (count < GameData.SummonUnitDataData.Count)
@@ -419,23 +457,21 @@ public class ResourceManager
{
Logger.Info(I18NManager.Translate("Server.ServerInfo.LoadingItem", I18NManager.Translate("Word.DialogueInfo")));
var count = 0;
- foreach (var dialogue in GameData.RogueNPCData)
+ var res = Parallel.ForEach(GameData.RogueNPCData.Values, dialogue =>
{
- var path = ConfigManager.Config.Path.ResourcePath + "/" + dialogue.Value.NPCJsonPath;
+ var path = ConfigManager.Config.Path.ResourcePath + "/" + dialogue.NPCJsonPath;
var file = new FileInfo(path);
- if (!file.Exists) continue;
+ if (!file.Exists) return;
try
{
using var reader = file.OpenRead();
using StreamReader reader2 = new(reader);
var text = reader2.ReadToEnd().Replace("$type", "Type");
var dialogueInfo = JsonConvert.DeserializeObject(text);
- if (dialogueInfo != null)
- {
- dialogue.Value.RogueNpcConfig = dialogueInfo;
- dialogueInfo.Loaded();
- count++;
- }
+ if (dialogueInfo == null) return;
+ dialogue.RogueNpcConfig = dialogueInfo;
+ count++;
+ dialogueInfo.Loaded();
}
catch (Exception ex)
{
@@ -443,6 +479,12 @@ public class ResourceManager
I18NManager.Translate("Server.ServerInfo.FailedToReadItem", file.Name,
I18NManager.Translate("Word.Error")), ex);
}
+ });
+
+ // wait it done
+ while (!res.IsCompleted)
+ {
+ Thread.Sleep(10);
}
if (count < GameData.RogueNPCData.Count)
@@ -460,17 +502,18 @@ public class ResourceManager
Logger.Info(I18NManager.Translate("Server.ServerInfo.LoadingItem",
I18NManager.Translate("Word.PerformanceInfo")));
var count = 0;
- foreach (var performance in GameData.PerformanceEData.Values)
+
+ var res = Parallel.ForEach(GameData.PerformanceEData.Values, performance =>
{
if (performance.PerformancePath == "")
{
count++;
- continue;
+ return;
}
var path = ConfigManager.Config.Path.ResourcePath + "/" + performance.PerformancePath;
var file = new FileInfo(path);
- if (!file.Exists) continue;
+ if (!file.Exists) return;
try
{
using var reader = file.OpenRead();
@@ -487,19 +530,19 @@ public class ResourceManager
I18NManager.Translate("Server.ServerInfo.FailedToReadItem", file.Name,
I18NManager.Translate("Word.Error")), ex);
}
- }
+ });
- foreach (var performance in GameData.PerformanceDData.Values)
+ var res2 = Parallel.ForEach(GameData.PerformanceDData.Values, performance =>
{
if (performance.PerformancePath == "")
{
count++;
- continue;
+ return;
}
var path = ConfigManager.Config.Path.ResourcePath + "/" + performance.PerformancePath;
var file = new FileInfo(path);
- if (!file.Exists) continue;
+ if (!file.Exists) return;
try
{
using var reader = file.OpenRead();
@@ -516,6 +559,12 @@ public class ResourceManager
I18NManager.Translate("Server.ServerInfo.FailedToReadItem", file.Name,
I18NManager.Translate("Word.Error")), ex);
}
+ });
+
+ // wait it done
+ while (!(res.IsCompleted && res2.IsCompleted))
+ {
+ Thread.Sleep(10);
}
if (count < GameData.PerformanceEData.Count + GameData.PerformanceDData.Count)
@@ -533,13 +582,13 @@ public class ResourceManager
Logger.Info(
I18NManager.Translate("Server.ServerInfo.LoadingItem", I18NManager.Translate("Word.SubMissionInfo")));
var count = 0;
- foreach (var subMission in GameData.SubMissionData.Values)
+ var res = Parallel.ForEach(GameData.SubMissionData.Values, subMission =>
{
- if (subMission.SubMissionInfo == null || subMission.SubMissionInfo.MissionJsonPath == "") continue;
+ if (subMission.SubMissionInfo == null || subMission.SubMissionInfo.MissionJsonPath == "") return;
var path = ConfigManager.Config.Path.ResourcePath + "/" + subMission.SubMissionInfo.MissionJsonPath;
var file = new FileInfo(path);
- if (!file.Exists) continue;
+ if (!file.Exists) return;
try
{
using var reader = file.OpenRead();
@@ -556,6 +605,12 @@ public class ResourceManager
I18NManager.Translate("Server.ServerInfo.FailedToReadItem", file.Name,
I18NManager.Translate("Word.Error")), ex);
}
+ });
+
+ // wait it done
+ while (!res.IsCompleted)
+ {
+ Thread.Sleep(10);
}
if (count < GameData.SubMissionData.Count)
diff --git a/Common/Database/DatabaseHelper.cs b/Common/Database/DatabaseHelper.cs
index 37c4a5c7..cd2645af 100644
--- a/Common/Database/DatabaseHelper.cs
+++ b/Common/Database/DatabaseHelper.cs
@@ -1,4 +1,7 @@
-using System.Globalization;
+using System.Collections.Concurrent;
+using System.Globalization;
+using EggLink.DanhengServer.Data.Excel;
+using EggLink.DanhengServer.Database.Account;
using EggLink.DanhengServer.Database.Inventory;
using EggLink.DanhengServer.Database.Quests;
using EggLink.DanhengServer.Internationalization;
@@ -12,10 +15,12 @@ public class DatabaseHelper
public static Logger logger = new("Database");
public static SqlSugarScope? sqlSugarScope;
public static DatabaseHelper? Instance;
- public static readonly Dictionary> UidInstanceMap = [];
+ public static readonly ConcurrentDictionary> UidInstanceMap = [];
public static readonly List ToSaveUidList = [];
public static long LastSaveTick = DateTime.UtcNow.Ticks;
public static Thread? SaveThread;
+ public static bool LoadAccount = false;
+ public static bool LoadAllData = false;
public DatabaseHelper()
{
@@ -71,10 +76,42 @@ public class DatabaseHelper
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
+
+ var list = sqlSugarScope.Queryable()
+ .Select(x => x)
+ .ToList();
+
+ foreach (var inst in list!.Select(instance => (instance as BaseDatabaseDataHelper)!))
+ {
+ if (!UidInstanceMap.TryGetValue(inst.Uid, out var value))
+ {
+ value = [];
+ UidInstanceMap[inst.Uid] = value;
+ }
+
+ value.Add(inst); // add to the map
+ }
+
+ // start dispatch server
+ LoadAccount = true;
+
+ var res = Parallel.ForEach(list, account =>
+ {
+ Parallel.ForEach(types, t =>
+ {
+ if (t == typeof(AccountData)) return; // skip the account data
+
+ typeof(DatabaseHelper).GetMethod(nameof(InitializeTable))?.MakeGenericMethod(t)
+ .Invoke(null, [account.Uid]);
+ }); // cache the data
+ });
+
+ while (!res.IsCompleted)
+ {
+
+ }
LastSaveTick = DateTime.UtcNow.Ticks;
@@ -83,12 +120,16 @@ public class DatabaseHelper
while (true) CalcSaveDatabase();
});
SaveThread.Start();
+
+ LoadAllData = true;
}
- public static void InitializeTable() where T : class, new()
+ public static void InitializeTable(int uid) where T : BaseDatabaseDataHelper, new()
{
var list = sqlSugarScope?.Queryable()
.Select(x => x)
+ .Select()
+ .Where(x => x.Uid == uid)
.ToList();
foreach (var inst in list!.Select(instance => (instance as BaseDatabaseDataHelper)!))
diff --git a/Common/Internationalization/Message/LanguageCHS.cs b/Common/Internationalization/Message/LanguageCHS.cs
index dc75c665..460df56b 100644
--- a/Common/Internationalization/Message/LanguageCHS.cs
+++ b/Common/Internationalization/Message/LanguageCHS.cs
@@ -80,6 +80,8 @@ public class WordTextCHS
public string RogueChestMapInfo => "模拟宇宙地图文件";
public string ChessRogueRoom => "模拟宇宙DLC";
public string ChessRogueRoomInfo => "模拟宇宙DLC文件";
+
+ public string DatabaseAccount => "数据库账号";
}
#endregion
@@ -150,6 +152,7 @@ public class ServerInfoTextCHS
public string ConfigMissing => "{0} 缺失,请检查你的资源文件夹:{1},{2} 可能不能使用。";
public string UnloadedItems => "卸载了所有 {0}。";
public string SaveDatabase => "已保存数据库,用时 {0}s";
+ public string WaitForAllDone => "现在还不可以进入游戏,请等待所有项目加载完成后再试";
}
#endregion
diff --git a/Common/Internationalization/Message/LanguageCHT.cs b/Common/Internationalization/Message/LanguageCHT.cs
index 41a973a4..92e9be51 100644
--- a/Common/Internationalization/Message/LanguageCHT.cs
+++ b/Common/Internationalization/Message/LanguageCHT.cs
@@ -80,6 +80,8 @@ public class WordTextCHT
public string RogueChestMapInfo => "模擬宇宙地圖文件";
public string ChessRogueRoom => "模擬宇宙DLC";
public string ChessRogueRoomInfo => "模擬宇宙DLC文件";
+
+ public string DatabaseAccount => "數據庫賬號";
}
#endregion
@@ -150,6 +152,7 @@ public class ServerInfoTextCHT
public string ConfigMissing => "{0} 缺失,請檢查你的資源文件夾:{1},{2} 可能不能使用。";
public string UnloadedItems => "卸載了所有 {0}。";
public string SaveDatabase => "已保存數據庫,用時 {0}s";
+ public string WaitForAllDone => "現在還不可以進入遊戲,請等待所有項目加載完成後再試";
}
#endregion
diff --git a/Common/Internationalization/Message/LanguageEN.cs b/Common/Internationalization/Message/LanguageEN.cs
index feaf09cb..eb87f74f 100644
--- a/Common/Internationalization/Message/LanguageEN.cs
+++ b/Common/Internationalization/Message/LanguageEN.cs
@@ -80,6 +80,8 @@ public class WordTextEN
public string RogueChestMapInfo => "Simulated Universe Map Info";
public string ChessRogueRoom => "Simulated Universe DLC";
public string ChessRogueRoomInfo => "Simulated Universe DLC Info";
+
+ public string DatabaseAccount => "Database Account";
}
#endregion
@@ -154,6 +156,8 @@ public class ServerInfoTextEN
public string ConfigMissing => "{0} is missing. Please check your resource folder: {1}, {2} may not be available.";
public string UnloadedItems => "Unloaded all {0}.";
public string SaveDatabase => "Database saved in {0}s";
+
+ public string WaitForAllDone => "You cannot enter the game yet. Please wait for all items to load before trying again";
}
#endregion
diff --git a/DanhengKcpSharp/DanhengConnection.cs b/DanhengKcpSharp/DanhengConnection.cs
index 3e44ebe0..ec31cc75 100644
--- a/DanhengKcpSharp/DanhengConnection.cs
+++ b/DanhengKcpSharp/DanhengConnection.cs
@@ -1,4 +1,6 @@
-using System.Net;
+using System.Collections.Concurrent;
+using System.IO.Pipelines;
+using System.Net;
using System.Reflection;
using EggLink.DanhengServer.Kcp.KcpSharp;
using EggLink.DanhengServer.Util;
@@ -11,11 +13,11 @@ public class DanhengConnection
{
public const int MAX_MSG_SIZE = 16384;
public const int HANDSHAKE_SIZE = 20;
- public static readonly List BannedPackets = [];
+ public static readonly ConcurrentBag BannedPackets = [];
private static readonly Logger Logger = new("GameServer");
- public static readonly Dictionary LogMap = [];
+ public static readonly ConcurrentDictionary LogMap = [];
- public static readonly List IgnoreLog =
+ public static readonly ConcurrentBag IgnoreLog =
[
CmdIds.PlayerHeartBeatCsReq, CmdIds.PlayerHeartBeatScRsp, CmdIds.SceneEntityMoveCsReq,
CmdIds.SceneEntityMoveScRsp, CmdIds.GetShopListCsReq, CmdIds.GetShopListScRsp
diff --git a/DanhengKcpSharp/DanhengKcpSharp.csproj b/DanhengKcpSharp/DanhengKcpSharp.csproj
index 367c1764..0bc5bbe5 100644
--- a/DanhengKcpSharp/DanhengKcpSharp.csproj
+++ b/DanhengKcpSharp/DanhengKcpSharp.csproj
@@ -10,6 +10,7 @@
+
diff --git a/GameServer/GameServer.csproj b/GameServer/GameServer.csproj
index 9a712eb0..94b8148b 100644
--- a/GameServer/GameServer.csproj
+++ b/GameServer/GameServer.csproj
@@ -22,6 +22,7 @@
+
diff --git a/Program/Program/EntryPoint.cs b/Program/Program/EntryPoint.cs
index 41623a25..67249c91 100644
--- a/Program/Program/EntryPoint.cs
+++ b/Program/Program/EntryPoint.cs
@@ -25,7 +25,7 @@ public class EntryPoint
public static readonly Listener Listener = new();
public static readonly CommandManager CommandManager = new();
- public static void Main(string[] args)
+ public static async Task Main(string[] args)
{
AppDomain.CurrentDomain.ProcessExit += (_, _) =>
{
@@ -85,11 +85,15 @@ public class EntryPoint
// Initialize the database
try
{
- DatabaseHelper.Initialize();
+ _ = Task.Run(DatabaseHelper.Initialize); // do not wait
- if (args.Contains("--upgrade-database")) DatabaseHelper.UpgradeDatabase();
+ while (!DatabaseHelper.LoadAccount)
+ {
+ Thread.Sleep(100);
+ }
- if (args.Contains("--move")) DatabaseHelper.MoveFromSqlite();
+ Logger.Info(I18NManager.Translate("Server.ServerInfo.LoadedItem", I18NManager.Translate("Word.DatabaseAccount")));
+ Logger.Warn(I18NManager.Translate("Server.ServerInfo.WaitForAllDone"));
}
catch (Exception e)
{
@@ -211,6 +215,26 @@ public class EntryPoint
// generate the handbook
HandbookGenerator.Generate();
+ if (!DatabaseHelper.LoadAllData)
+ {
+ Logger.Warn(I18NManager.Translate("Server.ServerInfo.WaitForAllDone"));
+ var t = Task.Run(() =>
+ {
+ while (!DatabaseHelper.LoadAllData) // wait for all data to be loaded
+ {
+ Thread.Sleep(100);
+ }
+ });
+
+ await t.WaitAsync(new CancellationToken());
+
+ Logger.Info(I18NManager.Translate("Server.ServerInfo.LoadedItem", I18NManager.Translate("Word.Database")));
+ }
+
+ if (args.Contains("--upgrade-database")) DatabaseHelper.UpgradeDatabase();
+
+ if (args.Contains("--move")) DatabaseHelper.MoveFromSqlite();
+
var elapsed = DateTime.Now - time;
Logger.Info(I18NManager.Translate("Server.ServerInfo.ServerStarted",
Math.Round(elapsed.TotalSeconds, 2).ToString(CultureInfo.InvariantCulture)));
@@ -245,7 +269,7 @@ public class EntryPoint
{
var name = opcode.Name;
var value = (int)opcode.GetValue(null)!;
- DanhengConnection.LogMap.Add(value, name);
+ DanhengConnection.LogMap.TryAdd(value, name);
}
}
}
\ No newline at end of file
diff --git a/WebServer/Controllers/GateServerRoutes.cs b/WebServer/Controllers/GateServerRoutes.cs
index 60713ae8..a00b3129 100644
--- a/WebServer/Controllers/GateServerRoutes.cs
+++ b/WebServer/Controllers/GateServerRoutes.cs
@@ -8,8 +8,9 @@ namespace EggLink.DanhengServer.WebServer.Controllers;
public class GateServerRoutes
{
[HttpGet("/query_gateway")]
- public string QueryGateway()
+ public async ValueTask QueryGateway()
{
+ await ValueTask.CompletedTask;
return new QueryGatewayHandler().Data;
}
}
\ No newline at end of file