Example #1
0
        /// <summary>
        /// Remove a game asset from the content cache so it's reloaded on the next request. This will reload
        /// core game assets if needed, but references to the former asset will still show the previous content.
        /// </summary>
        /// <param name="assetName">
        /// The name of the game asset whose game cache you want to invalidate. If you want to invalidate
        /// the game caches for all game assets this asset editor can edit, use <see cref="string.Empty"/>.
        /// </param>
        /// <exception cref="ArgumentNullException">The specified <paramref name="assetName"/> is <c>null</c>.</exception>
        /// <exception cref="ArgumentException">The specified <paramref name="assetName"/>is not the name of an asset this asset editor can edit.</exception>
        public void RequestAssetCacheRefresh(string assetName = "")
        {
            if (assetName == null)
            {
                throw new ArgumentNullException(nameof(assetName));
            }

            if (assetName.Equals(""))
            {
                RequestAssetCacheRefresh(this.gameAssetNames.Values.ToList());
            }
            else if (gameAssetNames.ContainsKey(assetName))
            {
                contentHelper.InvalidateCache(assetName);
            }
        }
Example #2
0
 /// <summary>If none of the build in content handlers are sufficient, and making a custom one is overkill, this method lets you handle the injection for a specific type of asset.</summary>
 /// <typeparam name="T">The Type the asset is loaded as.</typeparam>
 /// <param name="helper">The <see cref="IContentHelper" /> this extension method is attached to.</param>
 /// <param name="assetInjector">The delegate assigned to handle loading for this type.</param>
 public static void RegisterInjector <T>(this IContentHelper helper, AssetInjector <T> assetInjector)
 {
     if (DeferredTypeHandler.EditMap.ContainsKey(typeof(T)))
     {
         DeferredTypeHandler.EditMap.Add(typeof(T), new List <Delegate>());
     }
     DeferredTypeHandler.EditMap[typeof(T)].Add(assetInjector);
     helper.InvalidateCache <T>();
 }
Example #3
0
 /// <summary>
 /// Lets you replace a region of pixels in one texture with the contents of another texture
 /// </summary>
 /// <param name="helper">The <see cref="IContentHelper"/> this extension method is attached to</param>
 /// <param name="assetName">The texture asset (Relative to Content and without extension) that you wish to modify</param>
 /// <param name="patchAssetName">The texture used for the modification</param>
 /// <param name="region">The area you wish to replace</param>
 /// <param name="source">The area you wish to use for replacement, if omitted the full patch texture is used</param>
 public static void RegisterTexturePatch(this IContentHelper helper, string assetName, Texture2D patchAsset, Rectangle?destination = null, Rectangle?source = null)
 {
     assetName = helper.GetActualAssetKey(assetName, ContentSource.GameContent);
     if (!TextureInjector._Map.ContainsKey(assetName))
     {
         TextureInjector._Map.Add(assetName, new List <(Texture2D, Rectangle?, Rectangle?)>());
     }
     TextureInjector._Map[assetName].Add((patchAsset, source, destination));
     helper.InvalidateCache(assetName);
 }
Example #4
0
 /// <summary>If none of the build in content handlers are sufficient, and making a custom one is overkill, this method lets you handle the injection for one specific asset.</summary>
 /// <typeparam name="T">The Type the asset is loaded as.</typeparam>
 /// <param name="helper">The <see cref="IContentHelper" /> this extension method is attached to.</param>
 /// <param name="assetName">The asset (Relative to Content and without extension) to handle.</param>
 /// <param name="assetInjector">The delegate assigned to handle injection for this asset.</param>
 public static void RegisterInjector <T>(this IContentHelper helper, string assetName, AssetInjector <T> assetInjector)
 {
     assetName = helper.GetActualAssetKey(assetName, ContentSource.GameContent);
     if (!DeferredAssetHandler.EditMap.ContainsKey(assetName))
     {
         DeferredAssetHandler.EditMap.Add(assetName, new List <DeferredAssetInfo>());
     }
     DeferredAssetHandler.EditMap[assetName].Add(new DeferredAssetInfo(typeof(T), assetInjector));
     helper.InvalidateCache(assetName);
 }
Example #5
0
 /// <summary>If none of the build in content handlers are sufficient, and making a custom one is overkill, this method lets you handle the loading for one specific asset.</summary>
 /// <typeparam name="T">The Type the asset is loaded as.</typeparam>
 /// <param name="helper">The <see cref="IContentHelper" /> this extension method is attached to.</param>
 /// <param name="assetName">The asset (Relative to Content and without extension) to handle.</param>
 /// <param name="assetLoader">The delegate assigned to handle loading for this asset.</param>
 public static void RegisterLoader <T>(this IContentHelper helper, string assetName, AssetLoader <T> assetLoader)
 {
     assetName = helper.GetActualAssetKey(assetName, ContentSource.GameContent);
     if (DeferredAssetHandler.LoadMap.ContainsKey(assetName))
     {
         EntoaroxFrameworkMod.Logger.Log($"[IContentHelper] The `{Globals.GetModName(helper)}` mod\'s attempt to register a replacement asset for the `{assetName}` asset of type `{typeof(T).FullName}` failed, as another mod has already done so.", LogLevel.Error);
     }
     else
     {
         DeferredAssetHandler.LoadMap.Add(assetName, new DeferredAssetInfo(typeof(T), assetLoader));
         helper.InvalidateCache(assetName);
     }
 }
Example #6
0
 /// <summary>
 /// Lets you define a xnb file to completely replace with another
 /// </summary>
 /// <param name="helper">The <see cref="IContentHelper"/> this extension method is attached to</param>
 /// <param name="assetName">The asset (Relative to Content and without extension) to replace</param>
 /// <param name="replacementAssetName">The asset (Relative to your mod directory and without extension) to use instead</param>
 public static void RegisterXnbReplacement(this IContentHelper helper, string assetName, string replacementAssetName)
 {
     assetName            = helper.GetActualAssetKey(assetName, ContentSource.GameContent);
     replacementAssetName = helper.GetActualAssetKey(replacementAssetName, ContentSource.GameContent);
     if (XnbLoader._Map.ContainsKey(assetName))
     {
         EntoaroxFrameworkMod.Logger.Log("[IContentHelper] The `" + Globals.GetModName(helper) + "` mod's attempt to register a replacement asset for the `" + assetName + "` asset failed, as another mod has already done so.", LogLevel.Error);
     }
     else
     {
         XnbLoader._Map.Add(assetName, (helper, replacementAssetName));
         helper.InvalidateCache(assetName);
     }
 }
Example #7
0
        /// <summary>Get all texture asset names in the given content helper.</summary>
        /// <param name="contentHelper">The content helper to search.</param>
        private IEnumerable <string> GetTextureNames(IContentHelper contentHelper)
        {
            // get all texture keys from the content helper (this is such a hack)
            IList <string> textureKeys = new List <string>();

            contentHelper.InvalidateCache(asset =>
            {
                if (asset.DataType == typeof(Texture2D) && !asset.AssetName.Contains("..") && !asset.AssetName.StartsWith(Constants.ExecutionPath))
                {
                    textureKeys.Add(asset.AssetName);
                }
                return(false);
            });
            return(textureKeys);
        }
Example #8
0
 public static void RegisterDictionaryPatch <TKey, TValue>(this IContentHelper helper, string assetName, Dictionary <TKey, TValue> patchAsset)
 {
     try
     {
         var check = Game1.content.Load <Dictionary <TKey, TValue> >(assetName);
         assetName = helper.GetActualAssetKey(assetName, ContentSource.GameContent);
         if (!DictionaryInjector._Map.ContainsKey(assetName))
         {
             DictionaryInjector._Map.Add(assetName, new DictionaryWrapper <TKey, TValue>());
         }
         (DictionaryInjector._Map[assetName] as DictionaryWrapper <TKey, TValue>).Register(helper, patchAsset);
         helper.InvalidateCache(assetName);
     }
     catch
     {
         EntoaroxFrameworkMod.Logger.Log("[IContentHelper] The `" + Globals.GetModName(helper) + "` mod's attempt to inject data into the `" + assetName + "` asset failed, as the TKey and TValue of the injected asset do not match the original.", LogLevel.Error);
     }
 }
Example #9
0
        /// <summary>
        /// Lets you define a xnb file to completely replace with another
        /// </summary>
        /// <param name="helper">The <see cref="IContentHelper"/> this extension method is attached to</param>
        /// <param name="assetMapping">Dictionary with the asset (Relative to Content and without extension) to replace as key, and the asset (Relative to your mod directory and without extension) to use instead as value</param>
        public static void RegisterXnbReplacements(this IContentHelper helper, Dictionary <string, string> assetMapping)
        {
            List <string> matchedAssets = new List <string>();

            foreach (var pair in assetMapping)
            {
                string asset       = helper.GetActualAssetKey(pair.Key, ContentSource.GameContent);
                string replacement = helper.GetActualAssetKey(pair.Value, ContentSource.GameContent);
                if (XnbLoader._Map.ContainsKey(asset))
                {
                    EntoaroxFrameworkMod.Logger.Log("[IContentHelper] The `" + Globals.GetModName(helper) + "` mod's attempt to register a replacement asset for the `" + pair.Key + "` asset failed, as another mod has already done so.", LogLevel.Error);
                }
                else
                {
                    XnbLoader._Map.Add(asset, (helper, replacement));
                    matchedAssets.Add(asset);
                }
            }
            helper.InvalidateCache((assetInfo) => matchedAssets.Contains(assetInfo.AssetName));
        }
Example #10
0
        /// <summary>Update the current context.</summary>
        /// <param name="contentHelper">The content helper which manages game assets.</param>
        /// <param name="language">The current language.</param>
        /// <param name="date">The current in-game date (if applicable).</param>
        /// <param name="weather">The current in-game weather (if applicable).</param>
        /// <param name="spouse">The current player's internal spouse name (if applicable).</param>
        /// <param name="dayEvent">The day event (e.g. wedding or festival) occurring today (if applicable).</param>
        /// <param name="seenEvents">The event IDs which the player has seen.</param>
        /// <param name="mailFlags">The mail flags set for the player.</param>
        /// <param name="friendships">The current player's friendship details.</param>
        public void UpdateContext(IContentHelper contentHelper, LocalizedContentManager.LanguageCode language, SDate date, Weather?weather, string dayEvent, string spouse, int[] seenEvents, string[] mailFlags, IEnumerable <KeyValuePair <string, Friendship> > friendships)
        {
            this.VerboseLog("Propagating context...");

            // update context
            this.ConditionContext.Set(language: language, date: date, weather: weather, dayEvent: dayEvent, spouse: spouse, seenEvents: seenEvents, mailFlags: mailFlags, friendships: friendships);
            IDictionary <ConditionKey, string> tokenisableConditions = this.ConditionContext.GetSingleValueConditions();

            // update patches
            InvariantHashSet reloadAssetNames = new InvariantHashSet();
            string           prevAssetName    = null;

            foreach (IPatch patch in this.Patches.OrderBy(p => p.AssetName).ThenBy(p => p.LogName))
            {
                // log asset name
                if (this.Verbose && prevAssetName != patch.AssetName)
                {
                    this.VerboseLog($"   {patch.AssetName}:");
                    prevAssetName = patch.AssetName;
                }

                // track old values
                string wasAssetName = patch.AssetName;
                bool   wasApplied   = patch.MatchesContext;

                // update patch
                bool changed     = patch.UpdateContext(this.ConditionContext, tokenisableConditions);
                bool shouldApply = patch.MatchesContext;

                // track patches to reload
                bool reload = (wasApplied && changed) || (!wasApplied && shouldApply);
                if (reload)
                {
                    patch.IsApplied = false;
                    if (wasApplied)
                    {
                        reloadAssetNames.Add(wasAssetName);
                    }
                    if (shouldApply)
                    {
                        reloadAssetNames.Add(patch.AssetName);
                    }
                }

                // log change
                if (this.Verbose)
                {
                    IList <string> changes = new List <string>();
                    if (wasApplied != shouldApply)
                    {
                        changes.Add(shouldApply ? "enabled" : "disabled");
                    }
                    if (wasAssetName != patch.AssetName)
                    {
                        changes.Add($"target: {wasAssetName} => {patch.AssetName}");
                    }
                    string changesStr = string.Join(", ", changes);

                    this.VerboseLog($"      [{(shouldApply ? "X" : " ")}] {patch.LogName}: {(changes.Any() ? changesStr : "OK")}");
                }
            }

            // rebuild asset name lookup
            this.PatchesByCurrentTarget = new InvariantDictionary <HashSet <IPatch> >(
                from patchGroup in this.Patches.GroupBy(p => p.AssetName, StringComparer.InvariantCultureIgnoreCase)
                let key                         = patchGroup.Key
                                      let value = new HashSet <IPatch>(patchGroup)
                                                  select new KeyValuePair <string, HashSet <IPatch> >(key, value)
                );

            // reload assets if needed
            if (reloadAssetNames.Any())
            {
                this.VerboseLog($"   reloading {reloadAssetNames.Count} assets: {string.Join(", ", reloadAssetNames.OrderBy(p => p))}");
                contentHelper.InvalidateCache(asset =>
                {
                    this.VerboseLog($"      [{(reloadAssetNames.Contains(asset.AssetName) ? "X" : " ")}] reload {asset.AssetName}");
                    return(reloadAssetNames.Contains(asset.AssetName));
                });
            }
        }
Example #11
0
        /// <summary>Update the current context.</summary>
        /// <param name="contentHelper">The content helper through which to invalidate assets.</param>
        public void UpdateContext(IContentHelper contentHelper)
        {
            this.VerboseLog("Propagating context...");

            // update patches
            InvariantHashSet reloadAssetNames = new InvariantHashSet();
            string           prevAssetName    = null;

            foreach (IPatch patch in this.Patches.OrderByIgnoreCase(p => p.AssetName).ThenByIgnoreCase(p => p.LogName))
            {
                // log asset name
                if (this.Verbose && prevAssetName != patch.AssetName)
                {
                    this.VerboseLog($"   {patch.AssetName}:");
                    prevAssetName = patch.AssetName;
                }

                // track old values
                string wasAssetName = patch.AssetName;
                bool   wasApplied   = patch.MatchesContext;

                // update patch
                IContext tokenContext = this.TokenManager.TrackLocalTokens(patch.ContentPack.Pack);
                bool     changed      = patch.UpdateContext(tokenContext);
                bool     shouldApply  = patch.MatchesContext;

                // track patches to reload
                bool reload = (wasApplied && changed) || (!wasApplied && shouldApply);
                if (reload)
                {
                    patch.IsApplied = false;
                    if (wasApplied)
                    {
                        reloadAssetNames.Add(wasAssetName);
                    }
                    if (shouldApply)
                    {
                        reloadAssetNames.Add(patch.AssetName);
                    }
                }

                // log change
                if (this.Verbose)
                {
                    IList <string> changes = new List <string>();
                    if (wasApplied != shouldApply)
                    {
                        changes.Add(shouldApply ? "enabled" : "disabled");
                    }
                    if (wasAssetName != patch.AssetName)
                    {
                        changes.Add($"target: {wasAssetName} => {patch.AssetName}");
                    }
                    string changesStr = string.Join(", ", changes);

                    this.VerboseLog($"      [{(shouldApply ? "X" : " ")}] {patch.LogName}: {(changes.Any() ? changesStr : "OK")}");
                }

                // warn for invalid load patch
                if (patch is LoadPatch loadPatch && patch.MatchesContext && !patch.ContentPack.FileExists(loadPatch.LocalAsset.Value))
                {
                    this.Monitor.Log($"Patch error: {patch.LogName} has a {nameof(PatchConfig.FromFile)} which matches non-existent file '{loadPatch.LocalAsset.Value}'.", LogLevel.Error);
                }
            }

            // rebuild asset name lookup
            this.PatchesByCurrentTarget = new InvariantDictionary <HashSet <IPatch> >(
                from patchGroup in this.Patches.GroupByIgnoreCase(p => p.AssetName)
                let key                         = patchGroup.Key
                                      let value = new HashSet <IPatch>(patchGroup)
                                                  select new KeyValuePair <string, HashSet <IPatch> >(key, value)
                );

            // reload assets if needed
            if (reloadAssetNames.Any())
            {
                this.VerboseLog($"   reloading {reloadAssetNames.Count} assets: {string.Join(", ", reloadAssetNames.OrderByIgnoreCase(p => p))}");
                contentHelper.InvalidateCache(asset =>
                {
                    this.VerboseLog($"      [{(reloadAssetNames.Contains(asset.AssetName) ? "X" : " ")}] reload {asset.AssetName}");
                    return(reloadAssetNames.Contains(asset.AssetName));
                });
            }
        }