diff --git a/Source/Game/DataStorage/CliDB.cs b/Source/Game/DataStorage/CliDB.cs index 5421e58fe..2a972941c 100644 --- a/Source/Game/DataStorage/CliDB.cs +++ b/Source/Game/DataStorage/CliDB.cs @@ -17,7 +17,7 @@ namespace Game.DataStorage { uint oldMSTime = Time.GetMSTime(); - string db2Path = dataPath + "/dbc"; + string db2Path = $"{dataPath}/dbc"; BitSet availableDb2Locales = new((int)Locale.Total); foreach (var dir in Directory.GetDirectories(db2Path)) @@ -33,7 +33,13 @@ namespace Game.DataStorage uint loadedFileCount = 0; DB6Storage ReadDB2(string fileName, HotfixStatements preparedStatement, HotfixStatements preparedStatementLocale = 0) where T : new() { - return DBReader.Read(availableDb2Locales, $"{db2Path}/{defaultLocale}/", fileName, preparedStatement, preparedStatementLocale, ref loadedFileCount); + DB6Storage storage = new(); + storage.LoadData($"{db2Path}/{defaultLocale}/{fileName}"); + storage.LoadHotfixData(availableDb2Locales, preparedStatement, preparedStatementLocale); + + Global.DB2Mgr.AddDB2(storage.GetTableHash(), storage); + loadedFileCount++; + return storage; } AchievementStorage = ReadDB2("Achievement.db2", HotfixStatements.SEL_ACHIEVEMENT, HotfixStatements.SEL_ACHIEVEMENT_LOCALE); @@ -420,7 +426,7 @@ namespace Game.DataStorage !MapStorage.ContainsKey(2582) || // last map added in 10.0.5 (47660) !SpellNameStorage.ContainsKey(401848)) // last spell added in 10.0.5 (47660) { - Log.outError(LogFilter.Misc, "You have _outdated_ DB2 files. Please extract correct versions from current using client."); + Log.outFatal(LogFilter.ServerLoading, "You have _outdated_ DB2 files. Please extract correct versions from current using client."); Environment.Exit(1); } diff --git a/Source/Game/DataStorage/ClientReader/DB6Storage.cs b/Source/Game/DataStorage/ClientReader/DB6Storage.cs index 72239fa72..d33d3187a 100644 --- a/Source/Game/DataStorage/ClientReader/DB6Storage.cs +++ b/Source/Game/DataStorage/ClientReader/DB6Storage.cs @@ -8,6 +8,7 @@ using Framework.IO; using System; using System.Collections; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Numerics; using System.Reflection; @@ -29,192 +30,229 @@ namespace Game.DataStorage public class DB6Storage : Dictionary, IDB2Storage where T : new() { WDCHeader _header; - string _tableName; + string _tableName = typeof(T).Name; - public void LoadData(WDCHeader header, BitSet availableDb2Locales, HotfixStatements preparedStatement, HotfixStatements preparedStatementLocale) + public void LoadData(string fullFileName) { - _header = header; - _tableName = typeof(T).Name; - - SQLResult result = DB.Hotfix.Query(DB.Hotfix.GetPreparedStatement(preparedStatement)); - if (!result.IsEmpty()) + if (!File.Exists(fullFileName)) { - do - { - var obj = new T(); - - int dbIndex = 0; - var fields = typeof(T).GetFields(); - foreach (var f in typeof(T).GetFields()) - { - Type type = f.FieldType; - - if (type.IsArray) - { - Type arrayElementType = type.GetElementType(); - if (arrayElementType.IsEnum) - arrayElementType = arrayElementType.GetEnumUnderlyingType(); - - Array array = (Array)f.GetValue(obj); - switch (Type.GetTypeCode(arrayElementType)) - { - case TypeCode.SByte: - f.SetValue(obj, ReadArray(result, dbIndex, array.Length)); - break; - case TypeCode.Byte: - f.SetValue(obj, ReadArray(result, dbIndex, array.Length)); - break; - case TypeCode.Int16: - f.SetValue(obj, ReadArray(result, dbIndex, array.Length)); - break; - case TypeCode.UInt16: - f.SetValue(obj, ReadArray(result, dbIndex, array.Length)); - break; - case TypeCode.Int32: - f.SetValue(obj, ReadArray(result, dbIndex, array.Length)); - break; - case TypeCode.UInt32: - f.SetValue(obj, ReadArray(result, dbIndex, array.Length)); - break; - case TypeCode.Int64: - f.SetValue(obj, ReadArray(result, dbIndex, array.Length)); - break; - case TypeCode.UInt64: - f.SetValue(obj, ReadArray(result, dbIndex, array.Length)); - break; - case TypeCode.Single: - f.SetValue(obj, ReadArray(result, dbIndex, array.Length)); - break; - case TypeCode.String: - f.SetValue(obj, ReadArray(result, dbIndex, array.Length)); - break; - case TypeCode.Object: - if (arrayElementType == typeof(Vector3)) - { - float[] values = ReadArray(result, dbIndex, array.Length * 3); - - Vector3[] vectors = new Vector3[array.Length]; - for (var i = 0; i < array.Length; ++i) - vectors[i] = new Vector3(values[(i * 3)..(3 + (i * 3))]); - - f.SetValue(obj, vectors); - - dbIndex += array.Length * 3; - } - continue; - default: - Log.outError(LogFilter.ServerLoading, "Wrong Array Type: {0}", arrayElementType.Name); - break; - } - - dbIndex += array.Length; - } - else - { - if (type.IsEnum) - type = type.GetEnumUnderlyingType(); - - switch (Type.GetTypeCode(type)) - { - case TypeCode.SByte: - f.SetValue(obj, result.Read(dbIndex++)); - break; - case TypeCode.Byte: - f.SetValue(obj, result.Read(dbIndex++)); - break; - case TypeCode.Int16: - f.SetValue(obj, result.Read(dbIndex++)); - break; - case TypeCode.UInt16: - f.SetValue(obj, result.Read(dbIndex++)); - break; - case TypeCode.Int32: - f.SetValue(obj, result.Read(dbIndex++)); - break; - case TypeCode.UInt32: - f.SetValue(obj, result.Read(dbIndex++)); - break; - case TypeCode.Int64: - f.SetValue(obj, result.Read(dbIndex++)); - break; - case TypeCode.UInt64: - f.SetValue(obj, result.Read(dbIndex++)); - break; - case TypeCode.Single: - f.SetValue(obj, result.Read(dbIndex++)); - break; - case TypeCode.String: - string str = result.Read(dbIndex++); - f.SetValue(obj, str); - break; - case TypeCode.Object: - if (type == typeof(LocalizedString)) - { - LocalizedString locString = new(); - locString[Global.WorldMgr.GetDefaultDbcLocale()] = result.Read(dbIndex++); - - f.SetValue(obj, locString); - } - else if (type == typeof(Vector2)) - { - f.SetValue(obj, new Vector2(ReadArray(result, dbIndex, 2))); - dbIndex += 2; - } - else if (type == typeof(Vector3)) - { - f.SetValue(obj, new Vector3(ReadArray(result, dbIndex, 3))); - dbIndex += 3; - } - else if (type == typeof(FlagArray128)) - { - f.SetValue(obj, new FlagArray128(ReadArray(result, dbIndex, 4))); - dbIndex += 4; - } - break; - default: - Log.outError(LogFilter.ServerLoading, "Wrong Type: {0}", type.Name); - break; - } - } - } - - var id = (uint)fields[header.IdIndex == -1 ? 0 : header.IdIndex].GetValue(obj); - base[id] = obj; - } - while (result.NextRow()); + Log.outError(LogFilter.ServerLoading, $"File {fullFileName} not found."); + return; } + DBReader reader = new(); + using (var stream = new FileStream(fullFileName, FileMode.Open)) + { + if (!reader.Load(stream)) + { + Log.outError(LogFilter.ServerLoading, $"Error loading {fullFileName}."); + return; + } + } + + _header = reader.Header; + + foreach (var b in reader.Records) + Add((uint)b.Key, b.Value.As()); + } + + public void LoadHotfixData(BitSet availableDb2Locales, HotfixStatements preparedStatement, HotfixStatements preparedStatementLocale) + { + LoadFromDB(false, preparedStatement); + LoadFromDB(true, preparedStatement); + if (preparedStatementLocale == 0) return; for (Locale locale = 0; locale < Locale.Total; ++locale) { - if (Global.WorldMgr.GetDefaultDbcLocale() == locale || !availableDb2Locales[(int)locale]) + if (!availableDb2Locales[(int)locale]) continue; - PreparedStatement stmt = DB.Hotfix.GetPreparedStatement(preparedStatementLocale); - stmt.AddValue(0, locale.ToString()); - SQLResult localeResult = DB.Hotfix.Query(stmt); - if (localeResult.IsEmpty()) - continue; + LoadStringsFromDB(false, locale, preparedStatementLocale); + LoadStringsFromDB(true, locale, preparedStatementLocale); + } + } - do + void LoadFromDB(bool custom, HotfixStatements preparedStatement) + { + // Even though this query is executed only once, prepared statement is used to send data from mysql server in binary format + PreparedStatement stmt = DB.Hotfix.GetPreparedStatement(preparedStatement); + stmt.AddValue(0, !custom); + SQLResult result = DB.Hotfix.Query(stmt); + if (result.IsEmpty()) + return; + + do + { + var obj = new T(); + + int dbIndex = 0; + var fields = typeof(T).GetFields(); + foreach (var f in fields) { - int index = 0; - var obj = this.LookupByKey(localeResult.Read(index++)); - if (obj == null) + Type type = f.FieldType; + + if (type.IsArray) + { + Type arrayElementType = type.GetElementType(); + if (arrayElementType.IsEnum) + arrayElementType = arrayElementType.GetEnumUnderlyingType(); + + Array array = (Array)f.GetValue(obj); + switch (Type.GetTypeCode(arrayElementType)) + { + case TypeCode.SByte: + f.SetValue(obj, ReadArray(result, dbIndex, array.Length)); + break; + case TypeCode.Byte: + f.SetValue(obj, ReadArray(result, dbIndex, array.Length)); + break; + case TypeCode.Int16: + f.SetValue(obj, ReadArray(result, dbIndex, array.Length)); + break; + case TypeCode.UInt16: + f.SetValue(obj, ReadArray(result, dbIndex, array.Length)); + break; + case TypeCode.Int32: + f.SetValue(obj, ReadArray(result, dbIndex, array.Length)); + break; + case TypeCode.UInt32: + f.SetValue(obj, ReadArray(result, dbIndex, array.Length)); + break; + case TypeCode.Int64: + f.SetValue(obj, ReadArray(result, dbIndex, array.Length)); + break; + case TypeCode.UInt64: + f.SetValue(obj, ReadArray(result, dbIndex, array.Length)); + break; + case TypeCode.Single: + f.SetValue(obj, ReadArray(result, dbIndex, array.Length)); + break; + case TypeCode.String: + f.SetValue(obj, ReadArray(result, dbIndex, array.Length)); + break; + case TypeCode.Object: + if (arrayElementType == typeof(Vector3)) + { + float[] values = ReadArray(result, dbIndex, array.Length * 3); + + Vector3[] vectors = new Vector3[array.Length]; + for (var i = 0; i < array.Length; ++i) + vectors[i] = new Vector3(values[(i * 3)..(3 + (i * 3))]); + + f.SetValue(obj, vectors); + + dbIndex += array.Length * 3; + } + continue; + default: + Log.outError(LogFilter.ServerLoading, "Wrong Array Type: {0}", arrayElementType.Name); + break; + } + + dbIndex += array.Length; + } + else + { + if (type.IsEnum) + type = type.GetEnumUnderlyingType(); + + switch (Type.GetTypeCode(type)) + { + case TypeCode.SByte: + f.SetValue(obj, result.Read(dbIndex++)); + break; + case TypeCode.Byte: + f.SetValue(obj, result.Read(dbIndex++)); + break; + case TypeCode.Int16: + f.SetValue(obj, result.Read(dbIndex++)); + break; + case TypeCode.UInt16: + f.SetValue(obj, result.Read(dbIndex++)); + break; + case TypeCode.Int32: + f.SetValue(obj, result.Read(dbIndex++)); + break; + case TypeCode.UInt32: + f.SetValue(obj, result.Read(dbIndex++)); + break; + case TypeCode.Int64: + f.SetValue(obj, result.Read(dbIndex++)); + break; + case TypeCode.UInt64: + f.SetValue(obj, result.Read(dbIndex++)); + break; + case TypeCode.Single: + f.SetValue(obj, result.Read(dbIndex++)); + break; + case TypeCode.String: + string str = result.Read(dbIndex++); + f.SetValue(obj, str); + break; + case TypeCode.Object: + if (type == typeof(LocalizedString)) + { + LocalizedString locString = new(); + locString[Global.WorldMgr.GetDefaultDbcLocale()] = result.Read(dbIndex++); + + f.SetValue(obj, locString); + } + else if (type == typeof(Vector2)) + { + f.SetValue(obj, new Vector2(ReadArray(result, dbIndex, 2))); + dbIndex += 2; + } + else if (type == typeof(Vector3)) + { + f.SetValue(obj, new Vector3(ReadArray(result, dbIndex, 3))); + dbIndex += 3; + } + else if (type == typeof(FlagArray128)) + { + f.SetValue(obj, new FlagArray128(ReadArray(result, dbIndex, 4))); + dbIndex += 4; + } + break; + default: + Log.outError(LogFilter.ServerLoading, "Wrong Type: {0}", type.Name); + break; + } + } + } + + var id = (uint)fields[_header.IdIndex == -1 ? 0 : _header.IdIndex].GetValue(obj); + base[id] = obj; + } + while (result.NextRow()); + } + + void LoadStringsFromDB(bool custom, Locale locale, HotfixStatements preparedStatement) + { + PreparedStatement stmt = DB.Hotfix.GetPreparedStatement(preparedStatement); + stmt.AddValue(0, !custom); + stmt.AddValue(1, locale.ToString()); + SQLResult result = DB.Hotfix.Query(stmt); + if (result.IsEmpty()) + return; + + do + { + int index = 0; + var obj = this.LookupByKey(result.Read(index++)); + if (obj == null) + continue; + + foreach (var f in typeof(T).GetFields()) + { + if (f.FieldType != typeof(LocalizedString)) continue; - foreach (var f in typeof(T).GetFields()) - { - if (f.FieldType != typeof(LocalizedString)) - continue; - - LocalizedString locString = (LocalizedString)f.GetValue(obj); - locString[locale] = localeResult.Read(index++); - } - } while (localeResult.NextRow()); - } + LocalizedString locString = (LocalizedString)f.GetValue(obj); + locString[locale] = result.Read(index++); + } + } while (result.NextRow()); } TValue[] ReadArray(SQLResult result, int dbIndex, int arrayLength) @@ -377,7 +415,7 @@ namespace Game.DataStorage public uint GetTableHash() { return _header.TableHash; } public uint GetNumRows() { return Keys.Max() + 1; } - + public string GetName() { return _tableName; diff --git a/Source/Game/DataStorage/ClientReader/DBReader.cs b/Source/Game/DataStorage/ClientReader/DBReader.cs index be428e98d..6b8b6f1d2 100644 --- a/Source/Game/DataStorage/ClientReader/DBReader.cs +++ b/Source/Game/DataStorage/ClientReader/DBReader.cs @@ -21,37 +21,15 @@ namespace Game.DataStorage { private const uint WDC3FmtSig = 0x33434457; // WDC3 - public static DB6Storage Read(BitSet availableDb2Locales, string db2Path, string fileName, HotfixStatements preparedStatement, HotfixStatements preparedStatementLocale, ref uint loadedFileCount) where T : new() - { - DB6Storage storage = new(); + public WDCHeader Header; + public FieldMetaData[] FieldMeta; + public ColumnMetaData[] ColumnMeta; + public Value32[][] PalletData; + public Dictionary[] CommonData; - if (!File.Exists(db2Path + fileName)) - { - Log.outError(LogFilter.ServerLoading, $"File {fileName} not found."); - return storage; - } + public Dictionary Records = new(); - DBReader reader = new(); - using (var stream = new FileStream(db2Path + fileName, FileMode.Open)) - { - if (!reader.Load(stream)) - { - Log.outError(LogFilter.ServerLoading, $"Error loading {fileName}."); - return storage; - } - } - - foreach (var b in reader._records) - storage.Add((uint)b.Key, b.Value.As()); - - storage.LoadData(reader.Header, availableDb2Locales, preparedStatement, preparedStatementLocale); - - Global.DB2Mgr.AddDB2(reader.Header.TableHash, storage); - loadedFileCount++; - return storage; - } - - bool Load(Stream stream) + public bool Load(Stream stream) { using (var reader = new BinaryReader(stream, Encoding.UTF8)) { @@ -229,16 +207,16 @@ namespace Game.DataStorage long recordOffset = (recordIndex * Header.RecordSize) - (Header.RecordCount * Header.RecordSize); var rec = new WDC3Row(this, bitReader, (int)recordOffset, Header.HasIndexTable() ? (isIndexEmpty ? i : indexData[i]) : -1, hasRef ? refId : -1, stringsTable); - _records.Add(rec.Id, rec); + Records.Add(rec.Id, rec); } foreach (var copyRow in copyData) { if (copyRow.Key != 0) { - var rec = _records[copyRow.Value].Clone(); + var rec = Records[copyRow.Value].Clone(); rec.Id = copyRow.Key; - _records.Add(copyRow.Key, rec); + Records.Add(copyRow.Key, rec); } } @@ -249,14 +227,6 @@ namespace Game.DataStorage return true; } - - internal WDCHeader Header; - internal FieldMetaData[] FieldMeta; - internal ColumnMetaData[] ColumnMeta; - internal Value32[][] PalletData; - internal Dictionary[] CommonData; - - Dictionary _records = new(); } class WDC3Row