Fixes hotfix loading.
This commit is contained in:
@@ -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<T> ReadDB2<T>(string fileName, HotfixStatements preparedStatement, HotfixStatements preparedStatementLocale = 0) where T : new()
|
||||
{
|
||||
return DBReader.Read<T>(availableDb2Locales, $"{db2Path}/{defaultLocale}/", fileName, preparedStatement, preparedStatementLocale, ref loadedFileCount);
|
||||
DB6Storage<T> storage = new();
|
||||
storage.LoadData($"{db2Path}/{defaultLocale}/{fileName}");
|
||||
storage.LoadHotfixData(availableDb2Locales, preparedStatement, preparedStatementLocale);
|
||||
|
||||
Global.DB2Mgr.AddDB2(storage.GetTableHash(), storage);
|
||||
loadedFileCount++;
|
||||
return storage;
|
||||
}
|
||||
|
||||
AchievementStorage = ReadDB2<AchievementRecord>("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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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<T> : Dictionary<uint, T>, 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<sbyte>(result, dbIndex, array.Length));
|
||||
break;
|
||||
case TypeCode.Byte:
|
||||
f.SetValue(obj, ReadArray<byte>(result, dbIndex, array.Length));
|
||||
break;
|
||||
case TypeCode.Int16:
|
||||
f.SetValue(obj, ReadArray<short>(result, dbIndex, array.Length));
|
||||
break;
|
||||
case TypeCode.UInt16:
|
||||
f.SetValue(obj, ReadArray<ushort>(result, dbIndex, array.Length));
|
||||
break;
|
||||
case TypeCode.Int32:
|
||||
f.SetValue(obj, ReadArray<int>(result, dbIndex, array.Length));
|
||||
break;
|
||||
case TypeCode.UInt32:
|
||||
f.SetValue(obj, ReadArray<uint>(result, dbIndex, array.Length));
|
||||
break;
|
||||
case TypeCode.Int64:
|
||||
f.SetValue(obj, ReadArray<long>(result, dbIndex, array.Length));
|
||||
break;
|
||||
case TypeCode.UInt64:
|
||||
f.SetValue(obj, ReadArray<ulong>(result, dbIndex, array.Length));
|
||||
break;
|
||||
case TypeCode.Single:
|
||||
f.SetValue(obj, ReadArray<float>(result, dbIndex, array.Length));
|
||||
break;
|
||||
case TypeCode.String:
|
||||
f.SetValue(obj, ReadArray<string>(result, dbIndex, array.Length));
|
||||
break;
|
||||
case TypeCode.Object:
|
||||
if (arrayElementType == typeof(Vector3))
|
||||
{
|
||||
float[] values = ReadArray<float>(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<sbyte>(dbIndex++));
|
||||
break;
|
||||
case TypeCode.Byte:
|
||||
f.SetValue(obj, result.Read<byte>(dbIndex++));
|
||||
break;
|
||||
case TypeCode.Int16:
|
||||
f.SetValue(obj, result.Read<short>(dbIndex++));
|
||||
break;
|
||||
case TypeCode.UInt16:
|
||||
f.SetValue(obj, result.Read<ushort>(dbIndex++));
|
||||
break;
|
||||
case TypeCode.Int32:
|
||||
f.SetValue(obj, result.Read<int>(dbIndex++));
|
||||
break;
|
||||
case TypeCode.UInt32:
|
||||
f.SetValue(obj, result.Read<uint>(dbIndex++));
|
||||
break;
|
||||
case TypeCode.Int64:
|
||||
f.SetValue(obj, result.Read<long>(dbIndex++));
|
||||
break;
|
||||
case TypeCode.UInt64:
|
||||
f.SetValue(obj, result.Read<ulong>(dbIndex++));
|
||||
break;
|
||||
case TypeCode.Single:
|
||||
f.SetValue(obj, result.Read<float>(dbIndex++));
|
||||
break;
|
||||
case TypeCode.String:
|
||||
string str = result.Read<string>(dbIndex++);
|
||||
f.SetValue(obj, str);
|
||||
break;
|
||||
case TypeCode.Object:
|
||||
if (type == typeof(LocalizedString))
|
||||
{
|
||||
LocalizedString locString = new();
|
||||
locString[Global.WorldMgr.GetDefaultDbcLocale()] = result.Read<string>(dbIndex++);
|
||||
|
||||
f.SetValue(obj, locString);
|
||||
}
|
||||
else if (type == typeof(Vector2))
|
||||
{
|
||||
f.SetValue(obj, new Vector2(ReadArray<float>(result, dbIndex, 2)));
|
||||
dbIndex += 2;
|
||||
}
|
||||
else if (type == typeof(Vector3))
|
||||
{
|
||||
f.SetValue(obj, new Vector3(ReadArray<float>(result, dbIndex, 3)));
|
||||
dbIndex += 3;
|
||||
}
|
||||
else if (type == typeof(FlagArray128))
|
||||
{
|
||||
f.SetValue(obj, new FlagArray128(ReadArray<uint>(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<T>());
|
||||
}
|
||||
|
||||
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<uint>(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<sbyte>(result, dbIndex, array.Length));
|
||||
break;
|
||||
case TypeCode.Byte:
|
||||
f.SetValue(obj, ReadArray<byte>(result, dbIndex, array.Length));
|
||||
break;
|
||||
case TypeCode.Int16:
|
||||
f.SetValue(obj, ReadArray<short>(result, dbIndex, array.Length));
|
||||
break;
|
||||
case TypeCode.UInt16:
|
||||
f.SetValue(obj, ReadArray<ushort>(result, dbIndex, array.Length));
|
||||
break;
|
||||
case TypeCode.Int32:
|
||||
f.SetValue(obj, ReadArray<int>(result, dbIndex, array.Length));
|
||||
break;
|
||||
case TypeCode.UInt32:
|
||||
f.SetValue(obj, ReadArray<uint>(result, dbIndex, array.Length));
|
||||
break;
|
||||
case TypeCode.Int64:
|
||||
f.SetValue(obj, ReadArray<long>(result, dbIndex, array.Length));
|
||||
break;
|
||||
case TypeCode.UInt64:
|
||||
f.SetValue(obj, ReadArray<ulong>(result, dbIndex, array.Length));
|
||||
break;
|
||||
case TypeCode.Single:
|
||||
f.SetValue(obj, ReadArray<float>(result, dbIndex, array.Length));
|
||||
break;
|
||||
case TypeCode.String:
|
||||
f.SetValue(obj, ReadArray<string>(result, dbIndex, array.Length));
|
||||
break;
|
||||
case TypeCode.Object:
|
||||
if (arrayElementType == typeof(Vector3))
|
||||
{
|
||||
float[] values = ReadArray<float>(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<sbyte>(dbIndex++));
|
||||
break;
|
||||
case TypeCode.Byte:
|
||||
f.SetValue(obj, result.Read<byte>(dbIndex++));
|
||||
break;
|
||||
case TypeCode.Int16:
|
||||
f.SetValue(obj, result.Read<short>(dbIndex++));
|
||||
break;
|
||||
case TypeCode.UInt16:
|
||||
f.SetValue(obj, result.Read<ushort>(dbIndex++));
|
||||
break;
|
||||
case TypeCode.Int32:
|
||||
f.SetValue(obj, result.Read<int>(dbIndex++));
|
||||
break;
|
||||
case TypeCode.UInt32:
|
||||
f.SetValue(obj, result.Read<uint>(dbIndex++));
|
||||
break;
|
||||
case TypeCode.Int64:
|
||||
f.SetValue(obj, result.Read<long>(dbIndex++));
|
||||
break;
|
||||
case TypeCode.UInt64:
|
||||
f.SetValue(obj, result.Read<ulong>(dbIndex++));
|
||||
break;
|
||||
case TypeCode.Single:
|
||||
f.SetValue(obj, result.Read<float>(dbIndex++));
|
||||
break;
|
||||
case TypeCode.String:
|
||||
string str = result.Read<string>(dbIndex++);
|
||||
f.SetValue(obj, str);
|
||||
break;
|
||||
case TypeCode.Object:
|
||||
if (type == typeof(LocalizedString))
|
||||
{
|
||||
LocalizedString locString = new();
|
||||
locString[Global.WorldMgr.GetDefaultDbcLocale()] = result.Read<string>(dbIndex++);
|
||||
|
||||
f.SetValue(obj, locString);
|
||||
}
|
||||
else if (type == typeof(Vector2))
|
||||
{
|
||||
f.SetValue(obj, new Vector2(ReadArray<float>(result, dbIndex, 2)));
|
||||
dbIndex += 2;
|
||||
}
|
||||
else if (type == typeof(Vector3))
|
||||
{
|
||||
f.SetValue(obj, new Vector3(ReadArray<float>(result, dbIndex, 3)));
|
||||
dbIndex += 3;
|
||||
}
|
||||
else if (type == typeof(FlagArray128))
|
||||
{
|
||||
f.SetValue(obj, new FlagArray128(ReadArray<uint>(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<uint>(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<string>(index++);
|
||||
}
|
||||
} while (localeResult.NextRow());
|
||||
}
|
||||
LocalizedString locString = (LocalizedString)f.GetValue(obj);
|
||||
locString[locale] = result.Read<string>(index++);
|
||||
}
|
||||
} while (result.NextRow());
|
||||
}
|
||||
|
||||
TValue[] ReadArray<TValue>(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;
|
||||
|
||||
@@ -21,37 +21,15 @@ namespace Game.DataStorage
|
||||
{
|
||||
private const uint WDC3FmtSig = 0x33434457; // WDC3
|
||||
|
||||
public static DB6Storage<T> Read<T>(BitSet availableDb2Locales, string db2Path, string fileName, HotfixStatements preparedStatement, HotfixStatements preparedStatementLocale, ref uint loadedFileCount) where T : new()
|
||||
{
|
||||
DB6Storage<T> storage = new();
|
||||
public WDCHeader Header;
|
||||
public FieldMetaData[] FieldMeta;
|
||||
public ColumnMetaData[] ColumnMeta;
|
||||
public Value32[][] PalletData;
|
||||
public Dictionary<int, Value32>[] CommonData;
|
||||
|
||||
if (!File.Exists(db2Path + fileName))
|
||||
{
|
||||
Log.outError(LogFilter.ServerLoading, $"File {fileName} not found.");
|
||||
return storage;
|
||||
}
|
||||
public Dictionary<int, WDC3Row> 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<T>());
|
||||
|
||||
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<int, Value32>[] CommonData;
|
||||
|
||||
Dictionary<int, WDC3Row> _records = new();
|
||||
}
|
||||
|
||||
class WDC3Row
|
||||
|
||||
Reference in New Issue
Block a user