/// <summary> /// Registers the specified assembly for automatic PLib localization. If null is /// passed, the calling assembly is registered. /// </summary> /// <param name="assembly">The assembly to register for PLib localization.</param> public static void Register(Assembly assembly = null) { if (assembly == null) { assembly = Assembly.GetCallingAssembly(); } lock (PSharedData.GetLock(PRegistry.KEY_LOCALE_LOCK)) { // Get list holding locale information var list = PSharedData.GetData <IList <Assembly> >(PRegistry.KEY_LOCALE_TABLE); if (list == null) { PSharedData.PutData(PRegistry.KEY_LOCALE_TABLE, list = new List <Assembly>(8)); } list.Add(assembly); } var types = assembly.GetTypes(); if (types == null || types.Length == 0) { PUtil.LogWarning("Registered assembly " + assembly.GetName()?.Name + " that had no types for localization!"); } else { // This call searches all types in the assembly implicitly Localization.RegisterForTranslation(types[0]); } }
/// <summary> /// Initializes and stores the options table for quicker lookups later. /// </summary> internal static void Init() { lock (PSharedData.GetLock(PRegistry.KEY_OPTIONS_LOCK)) { modOptions = PSharedData.GetData <OptionsTable>(PRegistry.KEY_OPTIONS_TABLE); PSharedData.PutData(PRegistry.KEY_OPTIONS_LATEST, typeof(POptions)); } }
/// <summary> /// Adds the techs for every registered building to the database. /// </summary> internal static void AddAllTechs() { if (buildingTable == null) { throw new InvalidOperationException("Building table not loaded"); } lock (PSharedData.GetLock(PRegistry.KEY_BUILDING_LOCK)) { PRegistry.LogPatchDebug("Register techs for {0:D} buildings".F( buildingTable.Count)); foreach (var building in buildingTable) { if (building != null) { try { var trBuilding = Traverse.Create(building); // Building is of type object because it is in another assembly var addTech = Traverse.Create(building).Method(nameof(AddTech)); if (addTech.MethodExists()) { addTech.GetValue(); } else { PRegistry.LogPatchWarning("Invalid building technology!"); } } catch (System.Reflection.TargetInvocationException e) { // Log errors when registering building from another mod PUtil.LogError("Unable to add building tech for " + building.GetType().Assembly?.GetNameSafe() + ":"); PUtil.LogException(e.GetBaseException()); } } } } }
/// <summary> /// Registers a building to properly display its name, description, and tech tree /// entry. PLib must be initialized using InitLibrary before using this method. Each /// building should only be registered once, either in OnLoad or a post-load patch. /// </summary> /// <param name="building">The building to register.</param> public static void Register(PBuilding building) { if (building == null) { throw new ArgumentNullException("building"); } // In case this call is used before the library was initialized if (!PUtil.PLibInit) { PUtil.InitLibrary(false); PUtil.LogWarning("PUtil.InitLibrary was not called before using " + "PBuilding.Register!"); } // Must use object as the building table type lock (PSharedData.GetLock(PRegistry.KEY_BUILDING_LOCK)) { var table = PSharedData.GetData <ICollection <object> >(PRegistry. KEY_BUILDING_TABLE); if (table == null) { PSharedData.PutData(PRegistry.KEY_BUILDING_TABLE, table = new List <object>(64)); } #if DEBUG PUtil.LogDebug("Registered building: {0}".F(building.ID)); #endif table.Add(building); } }
/// <summary> /// Registers a class as a mod options class. /// </summary> /// <param name="optionsType">The class which will represent the options for this mod.</param> public static void RegisterOptions(Type optionsType) { if (optionsType == null) { throw new ArgumentNullException("optionsType"); } var assembly = optionsType.Assembly; var id = Path.GetFileName(GetModDir(assembly)); // Prevent concurrent modification (should be impossible anyways) lock (PSharedData.GetLock(PRegistry.KEY_OPTIONS_LOCK)) { // Get options table var options = PSharedData.GetData <OptionsTable>(PRegistry.KEY_OPTIONS_TABLE); if (options == null) { PSharedData.PutData(PRegistry.KEY_OPTIONS_TABLE, options = new Dictionary <string, Type>(8)); } if (options.ContainsKey(id)) { PUtil.LogWarning("Duplicate mod ID: " + id); } else { // Add as options for this mod options.Add(id, optionsType); PUtil.LogDebug("Registered mod options class {0} for {1}".F( optionsType.Name, assembly.GetName()?.Name)); } } }
/// <summary> /// Adds the techs for every registered building to the database. /// </summary> internal static void AddAllTechs() { if (buildingTable == null) { throw new InvalidOperationException("Building table not loaded"); } lock (PSharedData.GetLock(PRegistry.KEY_BUILDING_LOCK)) { PRegistry.LogPatchDebug("Register techs for {0:D} buildings".F( buildingTable.Count)); foreach (var building in buildingTable) { if (building != null) { var trBuilding = Traverse.Create(building); // Building is of type object because it is in another assembly var addTech = Traverse.Create(building).Method(nameof(AddTech)); if (addTech.MethodExists()) { addTech.GetValue(); } else { PRegistry.LogPatchWarning("Invalid building technology!"); } } } } }
/// <summary> /// Registers a class as a mod options class. The type is registered for its defining /// assembly, not for the calling assembly, for compatibility reasons. /// </summary> /// <param name="optionsType">The class which will represent the options for this mod.</param> public static void RegisterOptions(Type optionsType) { if (optionsType == null) { throw new ArgumentNullException("optionsType"); } #if OPTIONS_ONLY var assembly = optionsType.Assembly; var id = Path.GetFileName(GetModDir(assembly)); // Local options type if (modOptions == null) { modOptions = new Dictionary <string, Type>(4); } if (modOptions.ContainsKey(id)) { PUtil.LogWarning("Duplicate mod ID: " + id); } else { // Add as options for this mod modOptions.Add(id, optionsType); PUtil.LogDebug("Registered mod options class {0} for {1}".F(optionsType.Name, assembly.GetName()?.Name)); } #else // In case this call is used before the library was initialized if (!PUtil.PLibInit) { PUtil.InitLibrary(false); PUtil.LogWarning("PUtil.InitLibrary was not called before using " + "RegisterOptions!"); } var assembly = optionsType.Assembly; var id = Path.GetFileName(GetModDir(assembly)); // Prevent concurrent modification (should be impossible anyways) lock (PSharedData.GetLock(PRegistry.KEY_OPTIONS_LOCK)) { // Get options table var options = PSharedData.GetData <OptionsTable>(PRegistry.KEY_OPTIONS_TABLE); if (options == null) { PSharedData.PutData(PRegistry.KEY_OPTIONS_TABLE, options = new Dictionary <string, Type>(8)); } if (options.ContainsKey(id)) { PUtil.LogWarning("Duplicate mod ID: " + id); } else { // Add as options for this mod options.Add(id, optionsType); PUtil.LogDebug("Registered mod options class {0} for {1}".F(optionsType. Name, assembly.GetName()?.Name)); } } #endif }
/// <summary> /// Registers a light shape handler. /// </summary> /// <param name="identifier">A unique identifier for this shape. If another mod has /// already registered that identifier, the previous mod will take precedence.</param> /// <param name="handler">The handler for that shape.</param> /// <returns>The light shape which can be used.</returns> public static PLightShape Register(string identifier, CastLight handler) { PLightShape lightShape; // In case this call is used before the library was initialized if (!PUtil.PLibInit) { PUtil.InitLibrary(false); PUtil.LogWarning("PUtil.InitLibrary was not called before using " + "PLightShape.Register!"); } lock (PSharedData.GetLock(PRegistry.KEY_LIGHTING_LOCK)) { // Get list holding lighting information var list = PSharedData.GetData <IList <object> >(PRegistry.KEY_LIGHTING_TABLE); if (list == null) { PSharedData.PutData(PRegistry.KEY_LIGHTING_TABLE, list = new List <object>(8)); } // Try to find a match for this identifier object ls = null; int n = list.Count, index = 0; for (int i = 0; i < n; i++) { var light = list[i]; // Might be from another assembly so the types may or may not be compatible if (light != null && light.ToString() == identifier && light.GetType(). Name == typeof(PLightShape).Name) { index = i; break; } } if (ls == null) { // Not currently existing lightShape = new PLightShape(n + 1, identifier, handler); PUtil.LogDebug("Registered new light shape: " + identifier); list.Add(lightShape); } else { // Exists already PUtil.LogDebug("Found existing light shape: " + identifier); lightShape = new PLightShape(n + 1, identifier, null); } } return(lightShape); }
/// <summary> /// Checks for globally registered buildings and puts them into this assembly's /// building cache if present. /// </summary> /// <returns>true if buildings must be patched in, or false otherwise</returns> internal static bool CheckBuildings() { bool any = false; lock (PSharedData.GetLock(PRegistry.KEY_BUILDING_LOCK)) { var table = PSharedData.GetData <ICollection <object> >(PRegistry. KEY_BUILDING_TABLE); if (table != null && table.Count > 0) { buildingTable = table; any = true; } } return(any); }
/// <summary> /// Debug dumps the translation templates for ALL registered PLib localized mods. /// </summary> internal static void DumpAll() { lock (PSharedData.GetLock(PRegistry.KEY_LOCALE_LOCK)) { // Get list holding locale information var list = PSharedData.GetData <IList <Assembly> >(PRegistry.KEY_LOCALE_TABLE); if (list != null) { foreach (var mod in list) { if (mod != null) { ModUtil.RegisterForTranslation(mod.GetTypes()[0]); } } } } }
private static void RegisterEntry(Assembly modAssembly, string lockKey, string tableKey, string entryPath, string debugLine) { // Store the path to the creatures folder on disk for use in loading codex entries string dir = Options.POptions.GetModDir(modAssembly); lock (PSharedData.GetLock(lockKey)) { var table = PSharedData.GetData <IList <string> >(tableKey); if (table == null) { PSharedData.PutData(tableKey, table = new List <string>(8)); } #if DEBUG PUtil.LogDebug(debugLine.F(dir)); #endif table.Add(Path.Combine(dir, entryPath)); } }
/// <summary> /// Loads all codex entries for all mods registered. /// </summary> /// <param name="lockKey">Key for shared data lock.</param> /// <param name="tableKey">Key for shared data table.</param> /// <param name="category">The codex category under which these data entries should be loaded.</param> /// <returns>The list of entries that were loaded.</returns> private static IList <CodexEntry> LoadEntries(string lockKey, string tableKey, string category) { var entries = new List <CodexEntry>(32); lock (PSharedData.GetLock(lockKey)) { var table = PSharedData.GetData <IList <string> >(tableKey); if (table != null) { foreach (string dir in table) { #if DEBUG PUtil.LogDebug("Loaded codex entries from directory: {0}".F(dir)); #endif LoadFromDirectory(entries, dir, category); } } } return(entries); }
/// <summary> /// Loads all codex entries for all mods registered. /// </summary> /// <param name="lockKey">Key for shared data lock.</param> /// <param name="tableKey">Key for shared data table.</param> /// <param name="category">The codex category under which these data entries should be loaded.</param> /// <returns>The list of entries that were loaded.</returns> private static IList <CodexEntry> LoadEntries(string lockKey, string tableKey, string category) { var entries = new List <CodexEntry>(32); lock (PSharedData.GetLock(lockKey)) { var table = PSharedData.GetData <IList <string> >(tableKey); if (table != null) { foreach (string dir in table) { #if DEBUG PUtil.LogDebug("Loaded codex entries from directory: {0}".F(dir)); #endif string[] codexFiles = new string[0]; try { // List codex data files in the codex directory codexFiles = Directory.GetFiles(dir, CODEX_FILES); } catch (UnauthorizedAccessException ex) { PUtil.LogExcWarn(ex); } catch (IOException ex) { PUtil.LogExcWarn(ex); } var widgetTagMappings = Traverse.Create(typeof(CodexCache)). GetField <List <Tuple <string, Type> > >("widgetTagMappings"); foreach (string str in codexFiles) { try { string filename = str; var codexEntry = YamlIO.LoadFile <CodexEntry>(filename, PUtil. YamlParseErrorCB, widgetTagMappings); if (codexEntry != null) { codexEntry.category = category; entries.Add(codexEntry); } } catch (IOException ex) { PUtil.LogException(ex); } }
/// <summary> /// Registers the specified assembly for automatic PLib localization. If null is /// passed, the calling assembly is registered. /// </summary> /// <param name="assembly">The assembly to register for PLib localization.</param> public static void Register(Assembly assembly = null) { if (!PUtil.PLibInit) { PUtil.InitLibrary(false); PUtil.LogWarning("PUtil.InitLibrary was not called before using " + "PLocalization.Register!"); } if (assembly == null) { assembly = Assembly.GetCallingAssembly(); } lock (PSharedData.GetLock(PRegistry.KEY_LOCALE_LOCK)) { // Get list holding locale information var list = PSharedData.GetData <IList <Assembly> >(PRegistry.KEY_LOCALE_TABLE); if (list == null) { PSharedData.PutData(PRegistry.KEY_LOCALE_TABLE, list = new List <Assembly>(8)); } list.Add(assembly); } var types = assembly.GetTypes(); if (types == null || types.Length == 0) { PUtil.LogWarning("Registered assembly " + assembly.GetNameSafe() + " that had no types for localization!"); } else { // This call searches all types in the assembly implicitly Localization.RegisterForTranslation(types[0]); #if DEBUG PUtil.LogDebug("Localizing assembly {0} using base namespace {1}".F(assembly. GetNameSafe(), types[0].Namespace)); #endif } }
/// <summary> /// Creates and initializes the lighting manager instance. /// </summary> /// <returns>true if the lighting manager was initialized and has something to do, /// or false otherwise.</returns> internal static bool InitInstance() { bool patch = false; lock (PSharedData.GetLock(PRegistry.KEY_LIGHTING_LOCK)) { // Only run if any lights were registered var list = PSharedData.GetData <IList <object> >(PRegistry.KEY_LIGHTING_TABLE); if (list != null) { Instance = new PLightManager(); Instance.Init(list); patch = true; } } // Initialize anyways if smooth lighting is forced on if (!patch && ForceSmoothLight) { Instance = new PLightManager(); patch = true; } return(patch); }
/// <summary> /// Shows a mod options dialog now, as if Options was used inside the Mods menu. /// </summary> /// <param name="optionsType">The type of the options to show. The mod to configure, /// configuration directory, and so forth will be retrieved from the provided type. /// This type must be the same type configured in RegisterOptions for the mod.</param> /// <param name="title">The title to show in the dialog. If null, a default title /// will be used.</param> /// <param name="onClose">The method to call when the dialog is closed.</param> public static void ShowNow(Type optionsType, string title = null, Action <object> onClose = null) { #if OPTIONS_ONLY ShowDialog(optionsType, title, onClose); #else Type forwardType; if (optionsType == null) { throw new ArgumentNullException("optionsType"); } // Find latest version if possible lock (PSharedData.GetLock(PRegistry.KEY_OPTIONS_LOCK)) { forwardType = PSharedData.GetData <Type>(PRegistry.KEY_OPTIONS_LATEST); } if (forwardType == null) { forwardType = typeof(POptions); } try { var method = forwardType.GetMethod(nameof(ShowDialog), BindingFlags.Static | BindingFlags.NonPublic, null, new Type[] { typeof(Type), typeof(string), typeof(Action <object>) }, null); // Forward call to that version if (method != null) { method.Invoke(null, new object[] { optionsType, title, onClose }); } else { PUtil.LogWarning("No call to show options dialog found!"); ShowDialog(optionsType, title, onClose); } } catch (AmbiguousMatchException e) { PUtil.LogException(e); } #endif }
/// <summary> /// Localizes all mods which registered for it. /// </summary> /// <param name="locale">The locale to use.</param> internal static void LocalizeAll(Localization.Locale locale) { if (locale == null) { throw new ArgumentNullException("locale"); } lock (PSharedData.GetLock(PRegistry.KEY_LOCALE_LOCK)) { // Get list holding locale information var list = PSharedData.GetData <IList <Assembly> >(PRegistry.KEY_LOCALE_TABLE); if (list != null) { PUtil.LogDebug("Localizing {0:D} mods to locale {1}".F(list.Count, locale.Code)); foreach (var mod in list) { if (mod != null) { Localize(mod, POptions.GetModDir(mod), locale); } } } } }