public void AddUpdateEvent(UpdateHandle handle, UpdateRate _rate)
 {
     if (_rate == UpdateRate.NormalFrame)
     {
         if (m_AllUpdateEvent_Normal.Contains(handle))
         {
             //  Debug.Log("Regist Again " + handle.GetHashCode());
             return;
         }
         //  Debug.Log("Regist " + handle.GetHashCode());
         m_AllUpdateEvent_Normal.Add(handle);
     }//if
     else if (_rate == UpdateRate.DelayOneFrame)
     {
         if (m_AllUpdateEvent_OneFrameDelay.Contains(handle))
         {
             return;
         }
         m_AllUpdateEvent_OneFrameDelay.Add(handle);
     }//if
     else if (_rate == UpdateRate.DelayTwooFrame)
     {
         if (m_AllUpdateEvent_TwoFrameDelay.Contains(handle))
         {
             return;
         }
         m_AllUpdateEvent_TwoFrameDelay.Add(handle);
     }//if
 }
Example #2
0
    private void CreateBucketGroup(int sub_tick_count, float seconds_per_sub_tick, UpdateRate update_rate, List <BucketGroup> sub_group)
    {
        BucketGroup item = new BucketGroup(sub_tick_count, seconds_per_sub_tick, update_rate);

        bucketGroups.Add(item);
        sub_group.Add(item);
    }
    public static Type GetUpdateInterface(UpdateRate update_rate)
    {
        switch (update_rate)
        {
        case UpdateRate.RENDER_EVERY_TICK:
            return(typeof(IRenderEveryTick));

        case UpdateRate.RENDER_200ms:
            return(typeof(IRender200ms));

        case UpdateRate.RENDER_1000ms:
            return(typeof(IRender1000ms));

        case UpdateRate.SIM_33ms:
            return(typeof(ISim33ms));

        case UpdateRate.SIM_200ms:
            return(typeof(ISim200ms));

        case UpdateRate.SIM_1000ms:
            return(typeof(ISim1000ms));

        case UpdateRate.SIM_4000ms:
            return(typeof(ISim4000ms));

        default:
            return(null);
        }
    }
        public void AddFixedUpdateEvent(UpdateHandle handle, UpdateRate _rate)
        {
            if (_rate == UpdateRate.NormalFrame)
            {
                return;
            }//if

            if (_rate == UpdateRate.DelayOneFrame)
            {
                if (m_AllFixedUpdateEvent_OneFrameDelay.Contains(handle))
                {
                    return;
                }
                m_AllFixedUpdateEvent_OneFrameDelay.Add(handle);
                return;
            }//if

            if (_rate == UpdateRate.DelayTwooFrame)
            {
                if (m_AllFixedUpdateEvent_TwoFrameDelay.Contains(handle))
                {
                    return;
                }
                m_AllFixedUpdateEvent_TwoFrameDelay.Add(handle);
                return;
            }//if
        }
 protected virtual void OnUpdateRate(EventArgs e)
 {
     if (UpdateRate != null)
     {
         UpdateRate?.Invoke(this, e);
     }
 }
Example #6
0
 public BucketGroup(int frame_count, float seconds_per_sub_tick, UpdateRate update_rate)
 {
     for (int i = 0; i < frame_count; i++)
     {
         bucketFrames.Add(new List <BaseUpdateBucket>());
     }
     secondsPerSubTick = seconds_per_sub_tick;
     updateRate        = update_rate;
     name = "BucketGroup-" + update_rate.ToString();
 }
 public void DefaultRate(string rate)
 {
     if (this.rate == UpdateRate.Default)
     {
         if (rate == "FixedUpdate")
         {
             this.rate = UpdateRate.FixedUpdate;
         }
         if (rate == "LateUpdate")
         {
             this.rate = UpdateRate.LateUpdate;
         }
         if (rate == "Update")
         {
             this.rate = UpdateRate.Update;
         }
     }
 }
    private Entry ManifestEntry <UpdateInterface>(string name, bool load_balance)
    {
        if (bucketTable.TryGetValue(name, out Entry value))
        {
            DebugUtil.DevAssertArgs(value.buckets.Length == ((!load_balance) ? 1 : Singleton <StateMachineUpdater> .Instance.GetFrameCount(GetUpdateRate <UpdateInterface>())), "load_balance doesn't match previous registration...maybe load_balance erroneously on for a BatchUpdate type ", name, "?");
            return(value);
        }
        value = default(Entry);
        UpdateRate updateRate = GetUpdateRate <UpdateInterface>();
        int        num        = (!load_balance) ? 1 : Singleton <StateMachineUpdater> .Instance.GetFrameCount(updateRate);

        value.buckets = new StateMachineUpdater.BaseUpdateBucket[num];
        for (int i = 0; i < num; i++)
        {
            value.buckets[i] = new UpdateBucketWithUpdater <UpdateInterface>(name);
            Singleton <StateMachineUpdater> .Instance.AddBucket(updateRate, value.buckets[i]);
        }
        return(value);
    }
        public void RemoveFixedUpdateEvent(UpdateHandle handle, UpdateRate _rate)
        {
            if (m_AllUpdateEvent_Normal.Contains(handle))
            {
                return;
            }

            if (m_AllFixedUpdateEvent_OneFrameDelay.Contains(handle))
            {
                m_AllFixedUpdateEvent_OneFrameDelay.Remove(handle);
                return;
            }

            if (m_AllFixedUpdateEvent_TwoFrameDelay.Contains(handle))
            {
                m_AllFixedUpdateEvent_TwoFrameDelay.Remove(handle);
                return;
            }
        }
Example #10
0
        public void RemoveUpdateEvent(UpdateHandle handle, UpdateRate _rate)
        {
            if (m_AllUpdateEvent_Normal.Contains(handle))
            {
                //  Debug.Log("Cancle Regist " + handle.GetHashCode());
                m_AllUpdateEvent_Normal.Remove(handle);
                return;
            }
            //Debug.Log("Cancle  22 Regist    :: " + handle.GetHashCode());
            if (m_AllUpdateEvent_OneFrameDelay.Contains(handle))
            {
                m_AllUpdateEvent_OneFrameDelay.Remove(handle);
                return;
            }

            if (m_AllUpdateEvent_TwoFrameDelay.Contains(handle))
            {
                m_AllUpdateEvent_TwoFrameDelay.Remove(handle);
                return;
            }
        }
 public UpdaterManager(UpdateRate update_rate)
     : base(update_rate)
 {
 }
 protected BaseUpdaterManager(UpdateRate update_rate)
 {
     updateRate = update_rate;
 }
    public Handle Schedule <SimUpdateType>(string name, UpdateBucketWithUpdater <SimUpdateType> .IUpdater bucket_updater, UpdateRate update_rate, SimUpdateType updater, bool load_balance = false)
    {
        Entry value = ManifestEntry <SimUpdateType>(name, load_balance);
        UpdateBucketWithUpdater <SimUpdateType> updateBucketWithUpdater = (UpdateBucketWithUpdater <SimUpdateType>)value.buckets[value.nextBucketIdx];
        Handle result = default(Handle);

        result.handle       = updateBucketWithUpdater.Add(updater, Singleton <StateMachineUpdater> .Instance.GetFrameTime(update_rate, updateBucketWithUpdater.frame), bucket_updater);
        result.bucket       = updateBucketWithUpdater;
        value.nextBucketIdx = (value.nextBucketIdx + 1) % value.buckets.Length;
        bucketTable[name]   = value;
        return(result);
    }
Example #14
0
 public int GetFrameCount(UpdateRate update_rate)
 {
     return(bucketGroups[(int)update_rate].subTickCount);
 }
 private static string MakeBucketId(Type updater_type, UpdateRate update_rate)
 {
     return($"{updater_type.Name} {update_rate.ToString()}");
 }
Example #16
0
        /*********
        ** Public methods
        *********/
        /// <summary>Construct an instance.</summary>
        /// <param name="indexPath">The path of indexes from the root <c>content.json</c> to this patch; see <see cref="IPatch.IndexPath"/>.</param>
        /// <param name="path">The path to the patch from the root content file.</param>
        /// <param name="assetName">The normalized asset name to intercept.</param>
        /// <param name="conditions">The conditions which determine whether this patch should be applied.</param>
        /// <param name="fromAsset">The asset key to load from the content pack instead.</param>
        /// <param name="fromArea">The map area from which to read tiles.</param>
        /// <param name="patchMode">Indicates how the map should be patched.</param>
        /// <param name="toArea">The map area to overwrite.</param>
        /// <param name="mapProperties">The map properties to change when editing a map, if any.</param>
        /// <param name="mapTiles">The map tiles to change when editing a map.</param>
        /// <param name="addWarps">The warps to add to the location.</param>
        /// <param name="textOperations">The text operations to apply to existing values.</param>
        /// <param name="updateRate">When the patch should be updated.</param>
        /// <param name="contentPack">The content pack which requested the patch.</param>
        /// <param name="parentPatch">The parent patch for which this patch was loaded, if any.</param>
        /// <param name="monitor">Encapsulates monitoring and logging.</param>
        /// <param name="parseAssetName">Parse an asset name.</param>
        public EditMapPatch(int[] indexPath, LogPathBuilder path, IManagedTokenString assetName, IEnumerable <Condition> conditions, IManagedTokenString?fromAsset, TokenRectangle?fromArea, TokenRectangle?toArea, PatchMapMode patchMode, IEnumerable <EditMapPatchProperty>?mapProperties, IEnumerable <EditMapPatchTile>?mapTiles, IEnumerable <IManagedTokenString>?addWarps, IEnumerable <ITextOperation>?textOperations, UpdateRate updateRate, IContentPack contentPack, IPatch?parentPatch, IMonitor monitor, Func <string, IAssetName> parseAssetName)
            : base(
                indexPath: indexPath,
                path: path,
                type: PatchType.EditMap,
                assetName: assetName,
                fromAsset: fromAsset,
                conditions: conditions,
                updateRate: updateRate,
                contentPack: contentPack,
                parentPatch: parentPatch,
                parseAssetName: parseAssetName
                )
        {
            this.FromArea       = fromArea;
            this.ToArea         = toArea;
            this.PatchMode      = patchMode;
            this.MapProperties  = mapProperties?.ToArray() ?? Array.Empty <EditMapPatchProperty>();
            this.MapTiles       = mapTiles?.ToArray() ?? Array.Empty <EditMapPatchTile>();
            this.AddWarps       = addWarps?.Reverse().ToArray() ?? Array.Empty <IManagedTokenString>(); // reversing the warps allows later ones to 'overwrite' earlier ones, since the game checks them in the listed order
            this.TextOperations = textOperations?.ToArray() ?? Array.Empty <ITextOperation>();
            this.Monitor        = monitor;

            this.Contextuals
            .Add(this.FromArea)
            .Add(this.ToArea)
            .Add(this.MapProperties)
            .Add(this.MapTiles)
            .Add(this.AddWarps)
            .Add(this.TextOperations);
        }
Example #17
0
 public void AddBucket(UpdateRate update_rate, BaseUpdateBucket bucket)
 {
     bucketGroups[(int)update_rate].AddBucket(bucket);
 }
Example #18
0
        /*********
        ** Public methods
        *********/
        /// <summary>Construct an instance.</summary>
        /// <param name="indexPath">The path of indexes from the root <c>content.json</c> to this patch; see <see cref="IPatch.IndexPath"/>.</param>
        /// <param name="path">The path to the patch from the root content file.</param>
        /// <param name="assetName">The normalized asset name to intercept.</param>
        /// <param name="conditions">The conditions which determine whether this patch should be applied.</param>
        /// <param name="fromAsset">The asset key to load from the content pack instead.</param>
        /// <param name="fromArea">The sprite area from which to read an image.</param>
        /// <param name="toArea">The sprite area to overwrite.</param>
        /// <param name="patchMode">Indicates how the image should be patched.</param>
        /// <param name="updateRate">When the patch should be updated.</param>
        /// <param name="contentPack">The content pack which requested the patch.</param>
        /// <param name="parentPatch">The parent patch for which this patch was loaded, if any.</param>
        /// <param name="monitor">Encapsulates monitoring and logging.</param>
        /// <param name="parseAssetName">Parse an asset name.</param>
        public EditImagePatch(int[] indexPath, LogPathBuilder path, IManagedTokenString assetName, IEnumerable <Condition> conditions, IManagedTokenString fromAsset, TokenRectangle?fromArea, TokenRectangle?toArea, PatchImageMode patchMode, UpdateRate updateRate, IContentPack contentPack, IPatch?parentPatch, IMonitor monitor, Func <string, IAssetName> parseAssetName)
            : base(
                indexPath: indexPath,
                path: path,
                type: PatchType.EditImage,
                assetName: assetName,
                conditions: conditions,
                parseAssetName: parseAssetName,
                fromAsset: fromAsset,
                updateRate: updateRate,
                contentPack: contentPack,
                parentPatch: parentPatch
                )
        {
            this.FromArea  = fromArea;
            this.ToArea    = toArea;
            this.PatchMode = patchMode;
            this.Monitor   = monitor;

            this.Contextuals
            .Add(fromArea)
            .Add(toArea);
        }
Example #19
0
 /*********
 ** Public methods
 *********/
 /// <summary>Construct an instance.</summary>
 /// <param name="indexPath">The path of indexes from the root <c>content.json</c> to this patch; see <see cref="IPatch.IndexPath"/>.</param>
 /// <param name="path">The path to the patch from the root content file.</param>
 /// <param name="assetName">The normalized asset name to intercept.</param>
 /// <param name="conditions">The conditions which determine whether this patch should be applied.</param>
 /// <param name="fromFile">The normalized asset key from which to load entries (if applicable), including tokens.</param>
 /// <param name="updateRate">When the patch should be updated.</param>
 /// <param name="contentPack">The content pack which requested the patch.</param>
 /// <param name="parentPatch">The parent patch for which this patch was loaded, if any.</param>
 /// <param name="normalizeAssetName">Normalize an asset name.</param>
 /// <param name="monitor">Encapsulates monitoring and logging.</param>
 /// <param name="patchLoader">Handles loading and unloading patches for content packs.</param>
 public IncludePatch(int[] indexPath, LogPathBuilder path, IManagedTokenString assetName, IEnumerable <Condition> conditions, IManagedTokenString fromFile, UpdateRate updateRate, RawContentPack contentPack, IPatch parentPatch, Func <string, string> normalizeAssetName, IMonitor monitor, PatchLoader patchLoader)
     : base(
         indexPath: indexPath,
         path: path,
         type: PatchType.Include,
         assetName: assetName,
         conditions: conditions,
         fromAsset: fromFile,
         updateRate: updateRate,
         parentPatch: parentPatch,
         contentPack: contentPack.ContentPack,
         normalizeAssetName: normalizeAssetName
         )
 {
     this.RawContentPack = contentPack;
     this.Monitor        = monitor;
     this.PatchLoader    = patchLoader;
 }
Example #20
0
        /*********
        ** Public methods
        *********/
        /// <summary>Construct an instance.</summary>
        /// <param name="indexPath">The path of indexes from the root <c>content.json</c> to this patch; see <see cref="IPatch.IndexPath"/>.</param>
        /// <param name="path">The path to the patch from the root content file.</param>
        /// <param name="assetName">The normalized asset name to intercept.</param>
        /// <param name="conditions">The conditions which determine whether this patch should be applied.</param>
        /// <param name="fromAsset">The asset key to load from the content pack instead.</param>
        /// <param name="fromArea">The map area from which to read tiles.</param>
        /// <param name="patchMode">Indicates how the map should be patched.</param>
        /// <param name="toArea">The map area to overwrite.</param>
        /// <param name="mapProperties">The map properties to change when editing a map, if any.</param>
        /// <param name="textOperations">The text operations to apply to existing values.</param>
        /// <param name="mapTiles">The map tiles to change when editing a map.</param>
        /// <param name="updateRate">When the patch should be updated.</param>
        /// <param name="contentPack">The content pack which requested the patch.</param>
        /// <param name="parentPatch">The parent patch for which this patch was loaded, if any.</param>
        /// <param name="monitor">Encapsulates monitoring and logging.</param>
        /// <param name="reflection">Simplifies access to private code.</param>
        /// <param name="normalizeAssetName">Normalize an asset name.</param>
        public EditMapPatch(int[] indexPath, LogPathBuilder path, IManagedTokenString assetName, IEnumerable <Condition> conditions, IManagedTokenString fromAsset, TokenRectangle fromArea, TokenRectangle toArea, PatchMapMode patchMode, IEnumerable <EditMapPatchProperty> mapProperties, IEnumerable <EditMapPatchTile> mapTiles, IEnumerable <TextOperation> textOperations, UpdateRate updateRate, IContentPack contentPack, IPatch parentPatch, IMonitor monitor, IReflectionHelper reflection, Func <string, string> normalizeAssetName)
            : base(
                indexPath: indexPath,
                path: path,
                type: PatchType.EditMap,
                assetName: assetName,
                fromAsset: fromAsset,
                conditions: conditions,
                updateRate: updateRate,
                contentPack: contentPack,
                parentPatch: parentPatch,
                normalizeAssetName: normalizeAssetName
                )
        {
            this.FromArea       = fromArea;
            this.ToArea         = toArea;
            this.PatchMode      = patchMode;
            this.MapProperties  = mapProperties?.ToArray() ?? new EditMapPatchProperty[0];
            this.MapTiles       = mapTiles?.ToArray() ?? new EditMapPatchTile[0];
            this.TextOperations = textOperations?.ToArray() ?? new TextOperation[0];
            this.Monitor        = monitor;
            this.Reflection     = reflection;

            this.Contextuals
            .Add(this.FromArea)
            .Add(this.ToArea)
            .Add(this.MapProperties)
            .Add(this.MapTiles)
            .Add(this.TextOperations);
        }
Example #21
0
 /*********
 ** Public methods
 *********/
 /// <summary>Construct an instance.</summary>
 /// <param name="indexPath">The path of indexes from the root <c>content.json</c> to this patch; see <see cref="IPatch.IndexPath"/>.</param>
 /// <param name="path">The path to the patch from the root content file.</param>
 /// <param name="assetName">The normalized asset name to intercept.</param>
 /// <param name="localAsset">The asset key to load from the content pack instead.</param>
 /// <param name="conditions">The conditions which determine whether this patch should be applied.</param>
 /// <param name="updateRate">When the patch should be updated.</param>
 /// <param name="contentPack">The content pack which requested the patch.</param>
 /// <param name="parentPatch">The parent patch for which this patch was loaded, if any.</param>
 /// <param name="parseAssetName">Parse an asset name.</param>
 public LoadPatch(int[] indexPath, LogPathBuilder path, IManagedTokenString assetName, IManagedTokenString localAsset, IEnumerable <Condition> conditions, UpdateRate updateRate, IContentPack contentPack, IPatch?parentPatch, Func <string, IAssetName> parseAssetName)
     : base(
         indexPath: indexPath,
         path: path,
         type: PatchType.Load,
         assetName: assetName,
         conditions: conditions,
         updateRate: updateRate,
         contentPack: contentPack,
         parentPatch: parentPatch,
         parseAssetName: parseAssetName,
         fromAsset: localAsset
         )
 {
 }
Example #22
0
 public float GetFrameTime(UpdateRate update_rate, int frame)
 {
     return(bucketGroups[(int)update_rate].GetFrameTime(frame));
 }
Example #23
0
        /*********
        ** Protected methods
        *********/
        /// <summary>Construct an instance.</summary>
        /// <param name="path">The path to the patch from the root content file.</param>
        /// <param name="type">The patch type.</param>
        /// <param name="assetName">The normalized asset name to intercept.</param>
        /// <param name="conditions">The conditions which determine whether this patch should be applied.</param>
        /// <param name="updateRate">When the patch should be updated.</param>
        /// <param name="normalizeAssetName">Normalize an asset name.</param>
        /// <param name="contentPack">The content pack which requested the patch.</param>
        /// <param name="parentPatch">The parent <see cref="PatchType.Include"/> patch for which this patch was loaded, if any.</param>
        /// <param name="fromAsset">The normalized asset key from which to load the local asset (if applicable), including tokens.</param>
        protected Patch(LogPathBuilder path, PatchType type, IManagedTokenString assetName, IEnumerable <Condition> conditions, UpdateRate updateRate, ManagedContentPack contentPack, IPatch parentPatch, Func <string, string> normalizeAssetName, IManagedTokenString fromAsset = null)
        {
            this.Path = path;
            this.Type = type;
            this.ManagedRawTargetAsset  = assetName;
            this.Conditions             = conditions.ToArray();
            this.UpdateRate             = updateRate;
            this.NormalizeAssetNameImpl = normalizeAssetName;
            this.PrivateContext         = new LocalContext(scope: contentPack.Manifest.UniqueID);
            this.ManagedRawFromAsset    = fromAsset;
            this.ContentPack            = contentPack;
            this.ParentPatch            = parentPatch;

            this.Contextuals
            .Add(this.Conditions)
            .Add(assetName)
            .Add(fromAsset);
            this.ManuallyUpdatedTokens.Add(assetName);
            this.ManuallyUpdatedTokens.Add(fromAsset);
        }
        /*********
        ** Public methods
        *********/
        /// <summary>Construct an instance.</summary>
        /// <param name="indexPath">The path of indexes from the root <c>content.json</c> to this patch; see <see cref="IPatch.IndexPath"/>.</param>
        /// <param name="path">The path to the patch from the root content file.</param>
        /// <param name="assetName">The normalized asset name to intercept.</param>
        /// <param name="conditions">The conditions which determine whether this patch should be applied.</param>
        /// <param name="fromAsset">The asset key to load from the content pack instead.</param>
        /// <param name="fromArea">The map area from which to read tiles.</param>
        /// <param name="patchMode">Indicates how the map should be patched.</param>
        /// <param name="toArea">The map area to overwrite.</param>
        /// <param name="mapProperties">The map properties to change when editing a map, if any.</param>
        /// <param name="mapTiles">The map tiles to change when editing a map.</param>
        /// <param name="addWarps">The warps to add to the location.</param>
        /// <param name="textOperations">The text operations to apply to existing values.</param>
        /// <param name="updateRate">When the patch should be updated.</param>
        /// <param name="contentPack">The content pack which requested the patch.</param>
        /// <param name="parentPatch">The parent patch for which this patch was loaded, if any.</param>
        /// <param name="monitor">Encapsulates monitoring and logging.</param>
        /// <param name="reflection">Simplifies access to private code.</param>
        /// <param name="normalizeAssetName">Normalize an asset name.</param>
        public EditMapPatch(int[] indexPath, LogPathBuilder path, IManagedTokenString assetName, IEnumerable <Condition> conditions, IManagedTokenString fromAsset, TokenRectangle fromArea, TokenRectangle toArea, PatchMapMode patchMode, IEnumerable <EditMapPatchProperty> mapProperties, IEnumerable <EditMapPatchTile> mapTiles, IEnumerable <IManagedTokenString> addWarps, IEnumerable <TextOperation> textOperations, UpdateRate updateRate, IContentPack contentPack, IPatch parentPatch, IMonitor monitor, IReflectionHelper reflection, Func <string, string> normalizeAssetName)
            : base(
                indexPath: indexPath,
                path: path,
                type: PatchType.EditMap,
                assetName: assetName,
                fromAsset: fromAsset,
                conditions: conditions,
                updateRate: updateRate,
                contentPack: contentPack,
                parentPatch: parentPatch,
                normalizeAssetName: normalizeAssetName
                )
        {
            this.FromArea       = fromArea;
            this.ToArea         = toArea;
            this.PatchMode      = patchMode;
            this.MapProperties  = mapProperties?.ToArray() ?? new EditMapPatchProperty[0];
            this.MapTiles       = mapTiles?.ToArray() ?? new EditMapPatchTile[0];
            this.AddWarps       = addWarps?.Reverse().ToArray() ?? new IManagedTokenString[0]; // reversing the warps allows later ones to 'overwrite' earlier ones, since the game checks them in the listed order
            this.TextOperations = textOperations?.ToArray() ?? new TextOperation[0];
            this.Monitor        = monitor;
            this.Reflection     = reflection;

            this.Contextuals
            .Add(this.FromArea)
            .Add(this.ToArea)
            .Add(this.MapProperties)
            .Add(this.MapTiles)
            .Add(this.AddWarps)
            .Add(this.TextOperations);
        }
Example #25
0
        /// <summary>Handle the 'patch summary' command.</summary>
        /// <param name="args">The subcommand arguments.</param>
        /// <returns>Returns whether the command was handled.</returns>
        private bool HandleSummary(string[] args)
        {
            StringBuilder  output = new StringBuilder();
            LogPathBuilder path   = new LogPathBuilder("console command");

            // parse arguments
            bool showFull  = false;
            var  forModIds = new HashSet <string>(StringComparer.OrdinalIgnoreCase);

            foreach (string arg in args)
            {
                // flags
                if (arg.Equals("full", StringComparison.OrdinalIgnoreCase))
                {
                    showFull = true;
                    continue;
                }

                // for mod ID
                forModIds.Add(arg);
            }

            // truncate token values if needed
            string GetTruncatedTokenValues(IEnumerable <string> values)
            {
                const int    maxLength       = 200;
                const string truncatedSuffix = "... (use `patch summary full` to see other values)";

                string valueStr = string.Join(", ", values);

                return(showFull || valueStr.Length <= maxLength
                    ? valueStr
                    : $"{valueStr.Substring(0, maxLength - truncatedSuffix.Length)}{truncatedSuffix}");
            }

            // add condition summary
            output.AppendLine();
            output.AppendLine("=====================");
            output.AppendLine("==  Global tokens  ==");
            output.AppendLine("=====================");
            {
                // get data
                var tokensByProvider =
                    (
                        from token in this.TokenManager.GetTokens(enforceContext: false)
                        let inputArgs = token.GetAllowedInputArguments().ToArray()
                                        let rootValues = !token.RequiresInput ? token.GetValues(InputArguments.Empty).ToArray() : new string[0]
                                                         let isMultiValue =
                            inputArgs.Length > 1 ||
                            rootValues.Length > 1 ||
                            (inputArgs.Length == 1 && token.GetValues(new InputArguments(new LiteralString(inputArgs[0], path.With(token.Name, "input")))).Count() > 1)
                            let mod = (token as ModProvidedToken)?.Mod
                                      orderby isMultiValue, token.Name // single-value tokens first, then alphabetically
                        select new { Mod = mod, Token = token }
                    )
                    .GroupBy(p => p.Mod?.Name?.Trim())
                    .OrderBy(p => p.Key) // default tokens (key is null), then tokens added by other mods
                    .ToArray();
                int labelWidth = Math.Max(tokensByProvider.Max(group => group.Max(p => p.Token.Name.Length)), "token name".Length);

                // group by provider mod (if any)
                foreach (var tokenGroup in tokensByProvider)
                {
                    if (tokenGroup.Key != null && forModIds.Any() && !forModIds.Contains(tokenGroup.First().Mod.UniqueID))
                    {
                        continue;
                    }

                    // print mod name
                    output.AppendLine($"   {tokenGroup.Key ?? "Content Patcher"}:");
                    output.AppendLine();

                    // print table header
                    output.AppendLine($"      {"token name".PadRight(labelWidth)} | value");
                    output.AppendLine($"      {"".PadRight(labelWidth, '-')} | -----");

                    // print tokens
                    foreach (IToken token in tokenGroup.Select(p => p.Token))
                    {
                        output.Append($"      {token.Name.PadRight(labelWidth)} | ");

                        if (!token.IsReady)
                        {
                            output.AppendLine("[ ] n/a");
                        }
                        else if (token.RequiresInput)
                        {
                            InvariantHashSet allowedInputs = token.GetAllowedInputArguments();
                            if (allowedInputs.Any())
                            {
                                bool isFirst = true;
                                foreach (string input in allowedInputs.OrderByIgnoreCase(input => input))
                                {
                                    if (isFirst)
                                    {
                                        output.Append("[X] ");
                                        isFirst = false;
                                    }
                                    else
                                    {
                                        output.Append($"      {"".PadRight(labelWidth, ' ')} |     ");
                                    }

                                    output.AppendLine($":{input}: {GetTruncatedTokenValues(token.GetValues(new InputArguments(new LiteralString(input, path.With(token.Name, "input")))))}");
                                }
                            }
                            else
                            {
                                output.AppendLine("[X] (token returns a dynamic value)");
                            }
                        }
                        else
                        {
                            output.AppendLine("[X] " + GetTruncatedTokenValues(token.GetValues(InputArguments.Empty).OrderByIgnoreCase(p => p)));
                        }
                    }

                    output.AppendLine();
                }
            }

            // add patch summary
            var patches = this.GetAllPatches()
                          .Where(p => !forModIds.Any() || forModIds.Contains(p.ContentPack.Manifest.UniqueID))
                          .GroupByIgnoreCase(p => p.ContentPack.Manifest.Name)
                          .OrderByIgnoreCase(p => p.Key);

            output.AppendLine(
                "=====================\n"
                + "== Content patches ==\n"
                + "=====================\n"
                + "The following patches were loaded. For each patch:\n"
                + "  - 'loaded' shows whether the patch is loaded and enabled (see details for the reason if not).\n"
                + "  - 'conditions' shows whether the patch matches with the current conditions (see details for the reason if not). If this is unexpectedly false, check (a) the conditions above and (b) your Where field.\n"
                + "  - 'applied' shows whether the target asset was loaded and patched. If you expected it to be loaded by this point but it's false, double-check (a) that the game has actually loaded the asset yet, and (b) your Targets field is correct.\n"
                + (forModIds.Any() ? $"\n(Filtered to content pack ID{(forModIds.Count > 1 ? "s" : "")}: {string.Join(", ", forModIds.OrderByIgnoreCase(p => p))}.)\n" : "")
                + "\n"
                );
            foreach (IGrouping <string, PatchInfo> patchGroup in patches)
            {
                ModTokenContext tokenContext = this.TokenManager.TrackLocalTokens(patchGroup.First().ContentPack);
                output.AppendLine($"{patchGroup.Key}:");
                output.AppendLine("".PadRight(patchGroup.Key.Length + 1, '-'));

                // print tokens
                {
                    var tokens =
                        (
                            // get non-global tokens
                            from IToken token in tokenContext.GetTokens(enforceContext: false)
                            where token.Scope != null

                            // get input arguments
                            let validInputs = token.IsReady && token.RequiresInput
                                ? token.GetAllowedInputArguments().Select(p => new LiteralString(p, path.With(patchGroup.Key, token.Name, $"input '{p}'"))).AsEnumerable <ITokenString>()
                                : new ITokenString[] { null }
                            from ITokenString input in validInputs

                            where !token.RequiresInput || validInputs.Any() // don't show tokens which can't be represented

                            // select display data
                            let result = new
                    {
                        Name = token.RequiresInput ? $"{token.Name}:{input}" : token.Name,
                        Values = token.IsReady ? token.GetValues(new InputArguments(input)).ToArray() : new string[0],
                        IsReady = token.IsReady
                    }
                            orderby result.Name
                            select result
                        )
                        .ToArray();
                    if (tokens.Any())
                    {
                        int labelWidth = Math.Max(tokens.Max(p => p.Name.Length), "token name".Length);

                        output.AppendLine();
                        output.AppendLine("   Local tokens:");

                        output.AppendLine($"      {"token name".PadRight(labelWidth)} | value");
                        output.AppendLine($"      {"".PadRight(labelWidth, '-')} | -----");

                        foreach (var token in tokens)
                        {
                            output.AppendLine($"      {token.Name.PadRight(labelWidth)} | [{(token.IsReady ? "X" : " ")}] {GetTruncatedTokenValues(token.Values)}");
                        }
                    }
                }

                // print patches
                output.AppendLine();
                output.AppendLine("   Patches:");
                output.AppendLine("      loaded  | conditions | applied | name + details");
                output.AppendLine("      ------- | ---------- | ------- | --------------");
                foreach (PatchInfo patch in patchGroup.OrderBy(p => p, new PatchDisplaySortComparer()))
                {
                    // log checkbox and patch name
                    output.Append($"      [{(patch.IsLoaded ? "X" : " ")}]     | [{(patch.MatchesContext ? "X" : " ")}]        | [{(patch.IsApplied ? "X" : " ")}]     | {patch.PathWithoutContentPackPrefix}");

                    // log target value if different from name
                    {
                        // get patch values
                        string rawIdentifyingPath = PathUtilities.NormalizePath(patch.ParsedType == PatchType.Include
                            ? patch.RawFromAsset
                            : patch.RawTargetAsset
                                                                                );
                        var parsedIdentifyingPath = patch.ParsedType == PatchType.Include
                            ? patch.ParsedFromAsset
                            : patch.ParsedTargetAsset;

                        // get raw name if different
                        // (ignore differences in whitespace, capitalization, and path separators)
                        string rawValue = !PathUtilities.NormalizePath(patch.PathWithoutContentPackPrefix.ToString().Replace(" ", "")).ContainsIgnoreCase(rawIdentifyingPath?.Replace(" ", ""))
                            ? $"{patch.ParsedType?.ToString() ?? patch.RawType} {rawIdentifyingPath}"
                            : null;

                        // get parsed value
                        string parsedValue = patch.MatchesContext && parsedIdentifyingPath?.HasAnyTokens == true
                            ? PathUtilities.NormalizePath(parsedIdentifyingPath.Value)
                            : null;

                        // format
                        if (rawValue != null || parsedValue != null)
                        {
                            output.Append(" (");
                            if (rawValue != null)
                            {
                                output.Append(rawValue);
                                if (parsedValue != null)
                                {
                                    output.Append(" ");
                                }
                            }
                            if (parsedValue != null)
                            {
                                output.Append($"=> {parsedValue}");
                            }
                            output.Append(")");
                        }
                    }

                    // log reason not applied
                    string errorReason = this.GetReasonNotLoaded(patch);
                    if (errorReason != null)
                    {
                        output.Append($"  // {errorReason}");
                    }

                    // log common issues if not applied
                    if (errorReason == null && patch.IsLoaded && !patch.IsApplied && patch.ParsedTargetAsset.IsMeaningful())
                    {
                        string assetName = patch.ParsedTargetAsset.Value;

                        List <string> issues = new List <string>();
                        if (this.AssetNameWithContentPattern.IsMatch(assetName))
                        {
                            issues.Add("shouldn't include 'Content/' prefix");
                        }
                        if (this.AssetNameWithExtensionPattern.IsMatch(assetName))
                        {
                            var match = this.AssetNameWithExtensionPattern.Match(assetName);
                            issues.Add($"shouldn't include '{match.Captures[0]}' extension");
                        }
                        if (this.AssetNameWithLocalePattern.IsMatch(assetName))
                        {
                            issues.Add("shouldn't include language code (use conditions instead)");
                        }

                        if (issues.Any())
                        {
                            output.Append($" // hint: asset name may be incorrect ({string.Join("; ", issues)}).");
                        }
                    }

                    // log update rate issues
                    if (patch.Patch != null)
                    {
                        foreach (var pair in this.TokenManager.TokensWithSpecialUpdateRates)
                        {
                            UpdateRate rate       = pair.Item1;
                            string     label      = pair.Item2;
                            var        tokenNames = pair.Item3;

                            if (!patch.Patch.UpdateRate.HasFlag(rate))
                            {
                                var tokensUsed = new InvariantHashSet(patch.Patch.GetTokensUsed());

                                string[] locationTokensUsed = tokenNames.Where(p => tokensUsed.Contains(p)).ToArray();
                                if (locationTokensUsed.Any())
                                {
                                    output.Append($" // hint: patch uses {label}, but doesn't set \"{nameof(PatchConfig.Update)}\": \"{rate}\".");
                                }
                            }
                        }
                    }

                    // end line
                    output.AppendLine();
                }

                // print patch effects
                {
                    IDictionary <string, InvariantHashSet> effectsByPatch = new Dictionary <string, InvariantHashSet>(StringComparer.OrdinalIgnoreCase);
                    foreach (PatchInfo patch in patchGroup)
                    {
                        if (!patch.IsApplied || patch.Patch == null)
                        {
                            continue;
                        }

                        string[] changeLabels = patch.GetChangeLabels().ToArray();
                        if (!changeLabels.Any())
                        {
                            continue;
                        }

                        if (!effectsByPatch.TryGetValue(patch.ParsedTargetAsset.Value, out InvariantHashSet effects))
                        {
                            effectsByPatch[patch.ParsedTargetAsset.Value] = effects = new InvariantHashSet();
                        }

                        effects.AddMany(patch.GetChangeLabels());
                    }

                    output.AppendLine();
                    if (effectsByPatch.Any())
                    {
                        int maxAssetNameWidth = Math.Max("asset name".Length, effectsByPatch.Max(p => p.Key.Length));

                        output.AppendLine("   Current changes:");
                        output.AppendLine($"      asset name{"".PadRight(maxAssetNameWidth - "asset name".Length)} | changes");
                        output.AppendLine($"      ----------{"".PadRight(maxAssetNameWidth - "----------".Length, '-')} | -------");

                        foreach (var pair in effectsByPatch.OrderBy(p => p.Key, StringComparer.OrdinalIgnoreCase))
                        {
                            output.AppendLine($"      {pair.Key}{"".PadRight(maxAssetNameWidth - pair.Key.Length)} | {string.Join("; ", pair.Value.OrderBy(p => p, StringComparer.OrdinalIgnoreCase))}");
                        }
                    }
                    else
                    {
                        output.AppendLine("   No current changes.");
                    }
                }

                // add blank line between groups
                output.AppendLine();
            }

            this.Monitor.Log(output.ToString(), LogLevel.Debug);
            return(true);
        }
Example #26
0
        /*********
        ** Public methods
        *********/
        /// <summary>Construct an instance.</summary>
        /// <param name="path">The path to the patch from the root content file.</param>
        /// <param name="assetName">The normalized asset name to intercept.</param>
        /// <param name="conditions">The conditions which determine whether this patch should be applied.</param>
        /// <param name="fromFile">The normalized asset key from which to load entries (if applicable), including tokens.</param>
        /// <param name="records">The data records to edit.</param>
        /// <param name="fields">The data fields to edit.</param>
        /// <param name="moveRecords">The records to reorder, if the target is a list asset.</param>
        /// <param name="updateRate">When the patch should be updated.</param>
        /// <param name="contentPack">The content pack which requested the patch.</param>
        /// <param name="parentPatch">The parent patch for which this patch was loaded, if any.</param>
        /// <param name="monitor">Encapsulates monitoring and logging.</param>
        /// <param name="normalizeAssetName">Normalize an asset name.</param>
        /// <param name="tryParseFields">Parse the data change fields for an <see cref="PatchType.EditData"/> patch.</param>
        public EditDataPatch(LogPathBuilder path, IManagedTokenString assetName, IEnumerable <Condition> conditions, IManagedTokenString fromFile, IEnumerable <EditDataPatchRecord> records, IEnumerable <EditDataPatchField> fields, IEnumerable <EditDataPatchMoveRecord> moveRecords, UpdateRate updateRate, ManagedContentPack contentPack, IPatch parentPatch, IMonitor monitor, Func <string, string> normalizeAssetName, TryParseFieldsDelegate tryParseFields)
            : base(
                path: path,
                type: PatchType.EditData,
                assetName: assetName,
                conditions: conditions,
                updateRate: updateRate,
                contentPack: contentPack,
                parentPatch: parentPatch,
                normalizeAssetName: normalizeAssetName,
                fromAsset: fromFile
                )
        {
            // set fields
            this.Records        = records?.ToArray();
            this.Fields         = fields?.ToArray();
            this.MoveRecords    = moveRecords?.ToArray();
            this.Monitor        = monitor;
            this.TryParseFields = tryParseFields;

            // track contextuals
            this.Contextuals
            .Add(this.Records)
            .Add(this.Fields)
            .Add(this.MoveRecords)
            .Add(this.Conditions);
        }
Example #27
0
 /*********
 ** Public methods
 *********/
 /// <summary>Construct an instance.</summary>
 /// <param name="path">The path to the patch from the root content file.</param>
 /// <param name="assetName">The normalized asset name to intercept.</param>
 /// <param name="localAsset">The asset key to load from the content pack instead.</param>
 /// <param name="conditions">The conditions which determine whether this patch should be applied.</param>
 /// <param name="updateRate">When the patch should be updated.</param>
 /// <param name="contentPack">The content pack which requested the patch.</param>
 /// <param name="parentPatch">The parent patch for which this patch was loaded, if any.</param>
 /// <param name="normalizeAssetName">Normalize an asset name.</param>
 public LoadPatch(LogPathBuilder path, IManagedTokenString assetName, IManagedTokenString localAsset, IEnumerable <Condition> conditions, UpdateRate updateRate, IContentPack contentPack, IPatch parentPatch, Func <string, string> normalizeAssetName)
     : base(
         path: path,
         type: PatchType.Load,
         assetName: assetName,
         conditions: conditions,
         updateRate: updateRate,
         contentPack: contentPack,
         parentPatch: parentPatch,
         normalizeAssetName: normalizeAssetName,
         fromAsset: localAsset
         )
 {
 }
Example #28
0
        /*********
        ** Public methods
        *********/
        /// <summary>Construct an instance.</summary>
        /// <param name="path">The path to the patch from the root content file.</param>
        /// <param name="assetName">The normalized asset name to intercept.</param>
        /// <param name="conditions">The conditions which determine whether this patch should be applied.</param>
        /// <param name="fromAsset">The asset key to load from the content pack instead.</param>
        /// <param name="fromArea">The sprite area from which to read an image.</param>
        /// <param name="toArea">The sprite area to overwrite.</param>
        /// <param name="patchMode">Indicates how the image should be patched.</param>
        /// <param name="updateRate">When the patch should be updated.</param>
        /// <param name="contentPack">The content pack which requested the patch.</param>
        /// <param name="parentPatch">The parent patch for which this patch was loaded, if any.</param>
        /// <param name="monitor">Encapsulates monitoring and logging.</param>
        /// <param name="normalizeAssetName">Normalize an asset name.</param>
        public EditImagePatch(LogPathBuilder path, IManagedTokenString assetName, IEnumerable <Condition> conditions, IManagedTokenString fromAsset, TokenRectangle fromArea, TokenRectangle toArea, PatchMode patchMode, UpdateRate updateRate, ManagedContentPack contentPack, IPatch parentPatch, IMonitor monitor, Func <string, string> normalizeAssetName)
            : base(
                path: path,
                type: PatchType.EditImage,
                assetName: assetName,
                conditions: conditions,
                normalizeAssetName: normalizeAssetName,
                fromAsset: fromAsset,
                updateRate: updateRate,
                contentPack: contentPack,
                parentPatch: parentPatch
                )
        {
            this.FromArea  = fromArea;
            this.ToArea    = toArea;
            this.PatchMode = patchMode;
            this.Monitor   = monitor;

            this.Contextuals
            .Add(fromArea)
            .Add(toArea);
        }