/Services/WCell.RealmServer/Content/ContentHandler.cs
C# | 568 lines | 401 code | 63 blank | 104 comment | 23 complexity | 889ee9b29fb4fd362a04d20a1a5f09d6 MD5 | raw file
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Reflection;
- using Castle.ActiveRecord;
- using NLog;
- using WCell.Constants;
- using WCell.Core.Database;
- using WCell.Core.Initialization;
- using WCell.RealmServer.Database;
- using WCell.Util;
- using WCell.Util.Data;
- using WCell.Util.DB;
- using WCell.Util.Variables;
- using WCell.Util.Conversion;
- using System.Text;
- using WCell.Util.NLog;
- namespace WCell.RealmServer.Content
- {
- /// <summary>
- /// TODO: Make it simple to add content from outside
- /// </summary>
- public static class ContentHandler
- {
- public static readonly List<Assembly> AdditionalAssemblies = new List<Assembly>();
- private static readonly Logger log = LogManager.GetCurrentClassLogger();
- /// <summary>
- /// Determines how to interact with invalid content data.
- /// </summary>
- [Variable("ContentErrorResponse")]
- public static ErrorResponse ErrorResponse = ErrorResponse.None;
- /// <summary>
- /// Causes an error to be thrown if certain data is not present when requested
- /// </summary>
- [NotVariable]
- public static bool ForceDataPresence = false;
- //ErrorResponse.Warn;
- public static bool EnableCaching = true;
- private static string s_implementationFolder, s_implementationRoot;
- private static LightDBDefinitionSet s_definitions;
- private static Dictionary<Type, LightDBMapper> s_mappersByType;
- /// <summary>
- /// The name of the ContentProvider, which is also the name of the folder within the Content/Impl/ folder,
- /// in which to find the Table-definitions.
- /// </summary>
- public static string ContentProviderName = "UDB";
- private const string CacheFileSuffix = ".cache";
- public static string ImplementationRoot
- {
- get { return s_implementationRoot; }
- }
- public static string ImplementationFolder
- {
- get { return s_implementationFolder; }
- }
- public static LightDBDefinitionSet Definitions
- {
- get { return s_definitions; }
- }
- public static LightDBMapper GetMapper<T>() where T : IDataHolder
- {
- return GetMapper(typeof(T));
- }
- public static LightDBMapper GetMapper(Type t)
- {
- EnsureInitialized();
- LightDBMapper mapper;
- if (!s_mappersByType.TryGetValue(t, out mapper))
- {
- throw new Exception(string.Format(
- "DataHolder Type \"{0}\" was not registered - Make sure that it's XML definition was defined and associated correctly. " +
- "If the Type is not in the Core, call ContentHandler.Initialize(Assembly) on its Assembly first.", t.FullName));
- }
- return mapper;
- }
- public static Dictionary<object, IDataHolder> GetObjectMap<T>() where T : IDataHolder
- {
- return GetMapper<T>().GetObjectMap<T>();
- }
- #region Events
- /// <summary>
- /// Reports incorrect data, that is not Database-provider dependent.
- /// </summary>
- /// <param name="msg"></param>
- /// <param name="args"></param>
- public static void OnInvalidClientData(string msg, params object[] args)
- {
- OnInvalidClientData(string.Format(msg, args));
- }
- /// <summary>
- /// Reports incorrect data, that is not Database-provider dependent.
- /// </summary>
- public static void OnInvalidClientData(string msg)
- {
- switch (ErrorResponse)
- {
- case ErrorResponse.Exception:
- {
- throw new ContentException("Error encountered when loading Content: " + msg);
- }
- case ErrorResponse.Warn:
- {
- log.Warn(msg);
- break;
- }
- default:
- break;
- }
- }
- /// <summary>
- /// Reports incorrect data, caused by the Database-provider.
- /// </summary>
- public static void OnInvalidDBData(string msg, params object[] args)
- {
- OnInvalidDBData(string.Format(msg, args));
- }
- /// <summary>
- /// Reports incorrect data, caused by the Database-provider.
- /// </summary>
- public static void OnInvalidDBData(string msg)
- {
- switch (ErrorResponse)
- {
- case ErrorResponse.Exception:
- {
- throw new ContentException("Error encountered when loading Content: " + msg);
- }
- case ErrorResponse.Warn:
- {
- log.Warn("<" + ContentProviderName + ">" + msg);
- break;
- }
- default:
- break;
- }
- }
- #endregion
- public static void EnsureInitialized()
- {
- if (s_mappersByType == null)
- {
- InitializeDefault();
- }
- }
- #region Init
- private static bool inited;
- [Initialization(InitializationPass.Second, "Initialize Content")]
- public static void Initialize()
- {
- InitializeAndLoad(typeof(ContentHandler).Assembly);
- }
- public static void InitializeDefault()
- {
- RealmDBUtil.Initialize();
- InitializeAndLoad(typeof(ContentHandler).Assembly);
- }
- public static void InitializeAndLoad(Assembly asm)
- {
- if (!inited)
- {
- Converters.Provider = new NHibernateConverterProvider();
- Initialize(asm);
- Load();
- inited = true;
- }
- }
- public static void Initialize(Assembly asm)
- {
- s_implementationRoot = Path.Combine(RealmServerConfiguration.ContentDir, "Impl");
- s_implementationFolder = Path.Combine(s_implementationRoot, ContentProviderName);
- var defs = DataHolderMgr.CreateDataHolderDefinitionArray(asm);
- LightDBMgr.InvalidDataHandler = OnInvalidDBData;
- s_definitions = new LightDBDefinitionSet(defs);
- }
- #endregion
- #region Check
- /// <summary>
- /// Checks the validity of all Table definitions
- /// </summary>
- /// <returns>The amount of invalid columns.</returns>
- public static int Check(Action<string> feedbackCallback)
- {
- EnsureInitialized();
- var count = 0;
- foreach (var mapper in s_mappersByType.Values)
- {
- foreach (var table in mapper.Mapping.TableDefinitions)
- {
- var fields = table.ColumnDefinitions;
- foreach (var field in fields)
- {
- if (!field.IsEmpty)
- {
- try
- {
- using ( //var reader =
- mapper.Wrapper.Query(SqlUtil.BuildSelect(new[] { field.ColumnName }, table.Name, "LIMIT 1")))
- {
- }
- }
- catch (Exception e)
- {
- feedbackCallback(
- string.Format("Invalid column \"{0}\" in table \"{1}\": {2}", field, table.Name,
- e.GetAllMessages().ToString("\n\t")));
- count++;
- }
- }
- }
- }
- }
- return count;
- }
- #endregion
- #region Loading
- public static void Load()
- {
- s_definitions.Clear();
- var tableFile = Path.Combine(s_implementationFolder, "Tables.xml");
- var dataDefDir = Path.Combine(s_implementationFolder, "Data");
- s_definitions.LoadTableDefinitions(tableFile);
- CheckVersion();
- s_definitions.LoadDataHolderDefinitions(dataDefDir);
- if (!ActiveRecordStarter.IsInitialized)
- {
- throw new InvalidOperationException("ActiveRecord must be initialized.");
- }
- s_mappersByType = CreateMappersByType();
- }
- public static void CheckVersion()
- {
- var dbVersion = s_definitions.DBVersionLocation;
- if (dbVersion != null && dbVersion.IsValid)
- {
- string versionStr;
- try
- {
- versionStr = (new NHibernateDbWrapper()).GetDatabaseVersion(dbVersion.Table, dbVersion.Column);
- }
- catch (Exception e)
- {
- throw new ContentException(e, "Unable to validate version of database content - Reqired " + dbVersion);
- }
- //var minVersion = dbVersion.MinVersion;
- //var maxVersion = dbVersion.MaxVersion;
- //float version;
- //if (!float.TryParse(versionStr, out version))
- //{
- // throw new ContentException(string.Format("Unable to read version from Database due to an invalid format: " + versionStr));
- //}
- //if (version < minVersion || version > maxVersion)
- //{
- // throw new ContentException(string.Format("Supplied database's version is {0}, while content provider only supports versions {1} through {2}",
- // versionStr, minVersion, maxVersion));
- //}
- }
- }
- public static Dictionary<Type, LightDBMapper> CreateMappersByType()
- {
- var map = new Dictionary<Type, LightDBMapper>();
- foreach (var mapping in s_definitions.Mappings)
- {
- var mapper = new LightDBMapper(mapping, new NHibernateDbWrapper());
- foreach (var def in mapping.DataHolderDefinitions)
- {
- map.Add(def.Type, mapper);
- }
- }
- return map;
- }
- /// <summary>
- /// Ensures that the DataHolder of the given type and those that are connected with it, are loaded.
- ///
- /// </summary>
- public static void Load<T>() where T : IDataHolder
- {
- Load<T>(false);
- }
- /// <summary>
- /// Ensures that the DataHolder of the given type and those that are connected with it, are loaded.
- ///
- /// </summary>
- /// <param name="force">Whether to re-load if already loaded.</param>
- public static void Load<T>(bool force) where T : IDataHolder
- {
- EnsureInitialized();
- var mapper = GetMapper<T>();
- if (force || !mapper.Fetched)
- {
- Load(mapper);
- }
- }
- public static void Load(LightDBMapper mapper)
- {
- Load(mapper, true);
- }
- public static void Load(LightDBMapper mapper, bool failException)
- {
- try
- {
- if (EnableCaching && mapper.SupportsCaching)
- {
- if (mapper.LoadCache())
- {
- // loaded cache successfully
- return;
- }
- }
- //Utility.Measure("Loading content from DB - " + mapper + "", 1, () => {
- mapper.Fetch();
- //});
- if (EnableCaching && mapper.SupportsCaching)
- {
- log.Info("Saving cache for: " + mapper.Mapping.DataHolderDefinitions.ToString(", "));
- mapper.SaveCache();
- }
- }
- catch (Exception e)
- {
- if (failException)
- {
- throw new ContentException(e, "Unable to load entries using \"{0}\"", mapper);
- }
- // LogUtil.ErrorException(e, "Unable to load entries using \"{0}\"", mapper);
- }
- }
- /// <summary>
- /// Fetches all content of all registered DataHolders.
- /// </summary>
- public static void FetchAll()
- {
- EnsureInitialized();
- foreach (var mapper in s_mappersByType.Values)
- {
- mapper.Fetch();
- }
- }
- #endregion
- #region Editing
- /// <summary>
- /// Updates changes to the Object in the underlying Database.
- /// FlushCommit() needs to be called to persist the operation.
- /// </summary>
- public static void CommitUpdate(this IDataHolder obj)
- {
- var mapper = GetMapper(obj.GetType());
- mapper.Update(obj);
- }
- /// <summary>
- /// Inserts the Object into the underlying Database.
- /// FlushCommit() needs to be called to persist the operation.
- /// </summary>
- public static void CommitInsert(this IDataHolder obj)
- {
- var mapper = GetMapper(obj.GetType());
- mapper.Insert(obj);
- }
- /// <summary>
- /// Deletes the Object from the underlying Database.
- /// FlushCommit() needs to be called to persist the operation.
- /// </summary>
- public static void CommitDelete(this IDataHolder obj)
- {
- var mapper = GetMapper(obj.GetType());
- mapper.Delete(obj);
- }
- /// <summary>
- /// Updates changes to the Object in the underlying Database.
- /// </summary>
- public static void CommitUpdateAndFlush(this IDataHolder obj)
- {
- obj.CommitUpdate();
- FlushCommit(obj.GetType());
- }
- /// <summary>
- /// Inserts the Object into the underlying Database.
- /// FlushCommit() needs to be called to persist the operation.
- /// </summary>
- public static void CommitInsertAndFlush(this IDataHolder obj)
- {
- obj.CommitInsert();
- FlushCommit(obj.GetType());
- }
- /// <summary>
- /// Deletes the Object from the underlying Database.
- /// FlushCommit() needs to be called to persist the operation.
- /// </summary>
- public static void CommitDeleteAndFlush(this IDataHolder obj)
- {
- obj.CommitDelete();
- FlushCommit(obj.GetType());
- }
- /// <summary>
- /// Ignore all changes before last FlushCommit() (will not change the Object's state).
- /// </summary>
- public static void IgnoreUnflushedChanges<T>() where T : IDataHolder
- {
- var mapper = GetMapper(typeof(T));
- mapper.IgnoreUnflushedChanges();
- }
- /// <summary>
- /// Flush all commited changes to the underlying Database.
- /// FlushCommit() needs to be called to persist the operation.
- /// Will be executed in the global IO context.
- /// </summary>
- public static void FlushCommit<T>() where T : IDataHolder
- {
- var mapper = GetMapper(typeof(T));
- RealmServer.Instance.ExecuteInContext(() => mapper.Flush());
- }
- /// <summary>
- /// Flush all commited changes to the underlying Database.
- /// FlushCommit() needs to be called to persist the operation.
- /// Will be executed in the global IO context.
- /// </summary>
- public static void FlushCommit(Type t)
- {
- var mapper = GetMapper(t);
- RealmServer.Instance.ExecuteInContext(() => mapper.Flush());
- }
- #endregion
- #region Caching
- public static void SaveCache(this LightDBMapper mapper)
- {
- // save cache after loading
- var file = GetCacheFilename(mapper);
- try
- {
- mapper.SaveCache(file);
- }
- catch (Exception e)
- {
- File.Delete(file);
- LogUtil.ErrorException(e, "Failed to save cache to file: " + file);
- }
- }
- private static bool LoadCache(this LightDBMapper mapper)
- {
- var file = GetCacheFilename(mapper);
- if (File.Exists(file))
- {
- try
- {
- // Utility.Measure("Loading contnt from Cache - " + mapper, 1, () => {
- if (mapper.LoadCache(file))
- {
- // loaded cache successfully
- return true;
- }
- log.Warn("Cache signature in file \"{0}\" is out of date.", file);
- log.Warn("Reloading content from Database...");
- //});
- //return;
- }
- catch (Exception ex)
- {
- if (ex is EndOfStreamException)
- {
- log.Warn("Cache signature in file \"{0}\" is out of date.", file);
- }
- else
- {
- LogUtil.ErrorException(ex, "Unable to load cache from \"" + file + "\".");
- }
- log.Warn("Reloading content from Database...");
- }
- }
- return false;
- }
- public static string GetCacheFilename(this LightDBMapper mapper)
- {
- var str = new StringBuilder(mapper.Mapping.DataHolderDefinitions.Length * 12);
- foreach (var def in mapper.Mapping.DataHolderDefinitions)
- {
- str.Append(def.Name);
- }
- str.Append(CacheFileSuffix);
- return RealmServerConfiguration.Instance.GetCacheFile(str.ToString());
- }
- /// <summary>
- /// Deletes all cache-files
- /// </summary>
- public static void PurgeCache()
- {
- foreach (var file in Directory.GetFiles(RealmServerConfiguration.Instance.CacheDir))
- {
- if (file.EndsWith(CacheFileSuffix))
- {
- File.Delete(file);
- }
- }
- }
- #endregion
- public static void SaveDefaultStubs()
- {
- SaveStubs(typeof(ContentHandler).Assembly);
- }
- public static void SaveStubs(Assembly asm)
- {
- EnsureInitialized();
- LightDBMgr.SaveAllStubs(Path.Combine(s_implementationRoot, ".stubs"),
- DataHolderMgr.CreateDataHolderDefinitionArray(asm));
- }
- }
- }