internal static bool UpdateCompanionModel(CompanionModel model)
        {
            var cachedModel = companionModels.FirstOrDefault(c => c.GetId() == model.GetId());

            if (cachedModel is null)
            {
                CustomCompanions.monitor.Log($"No companion match found for {model.GetId()}; Did a content patch update the name?", StardewModdingAPI.LogLevel.Trace);
                return(false);
            }

            // Update our cached model
            model.Translations = cachedModel.Translations;
            companionModels[companionModels.IndexOf(cachedModel)] = model;

            // Update any existing companions using this model
            foreach (var activeCompanion in activeCompanions.SelectMany(c => c.Companions).Where(c => c.model.GetId() == model.GetId()))
            {
                // Do model update
                activeCompanion.UpdateModel(model);
            }

            foreach (var sceneryCompanion in sceneryCompanions.SelectMany(c => c.Companions).Where(c => c.model.GetId() == model.GetId()))
            {
                // Do model update
                sceneryCompanion.UpdateModel(model);
            }

            return(true);
        }
        internal void ManualReload(string packUniqueId)
        {
            this.RemoveAllCompanions(owner: packUniqueId);

            // Reset the tracked validation counter
            this.modelValidationIndex = 0;

            // Set up the CompanionManager
            CompanionManager.activeCompanions  = CompanionManager.activeCompanions.Where(c => !c.Companions.Any(m => m.model.Owner.Equals(packUniqueId, StringComparison.OrdinalIgnoreCase))).ToList();
            CompanionManager.sceneryCompanions = CompanionManager.sceneryCompanions.Where(c => !c.Companions.Any(m => m.model.Owner.Equals(packUniqueId, StringComparison.OrdinalIgnoreCase))).ToList();


            // Load the owned content packs
            foreach (IContentPack contentPack in Helper.ContentPacks.GetOwned().Where(c => c.Manifest.UniqueID.Equals(packUniqueId, StringComparison.OrdinalIgnoreCase)))
            {
                Monitor.Log($"Loading companions from pack: {contentPack.Manifest.Name} {contentPack.Manifest.Version} by {contentPack.Manifest.Author}", LogLevel.Debug);

                var companionFolders = new DirectoryInfo(Path.Combine(contentPack.DirectoryPath, "Companions")).GetDirectories();
                if (companionFolders.Count() == 0)
                {
                    Monitor.Log($"No sub-folders found under Companions for the content pack {contentPack.Manifest.Name}!", LogLevel.Warn);
                    continue;
                }

                // Load in the companions
                foreach (var companionFolder in companionFolders)
                {
                    if (!File.Exists(Path.Combine(companionFolder.FullName, "companion.json")))
                    {
                        Monitor.Log($"Content pack {contentPack.Manifest.Name} is missing a companion.json under {companionFolder.Name}!", LogLevel.Warn);
                        continue;
                    }

                    CompanionModel companion = contentPack.ReadJsonFile <CompanionModel>(Path.Combine(companionFolder.Parent.Name, companionFolder.Name, "companion.json"));
                    companion.Name  = companion.Name.Replace(" ", "");
                    companion.Owner = contentPack.Manifest.UniqueID;
                    Monitor.Log(companion.ToString(), LogLevel.Trace);

                    // Save the TileSheet, if one is given
                    if (String.IsNullOrEmpty(companion.TileSheetPath) && !File.Exists(Path.Combine(companionFolder.FullName, "companion.png")))
                    {
                        Monitor.Log($"Unable to add companion {companion.Name} from {contentPack.Manifest.Name}: No associated companion.png or TileSheetPath given", LogLevel.Warn);
                        continue;
                    }
                    else if (String.IsNullOrEmpty(companion.TileSheetPath))
                    {
                        companion.TileSheetPath = contentPack.ModContent.GetInternalAssetName(Path.Combine(companionFolder.Parent.Name, companionFolder.Name, "companion.png")).Name;
                    }

                    // Save the PortraitSheet, if one is given
                    if (companion.Portrait != null)
                    {
                        if (!File.Exists(Path.Combine(companionFolder.FullName, "portrait.png")))
                        {
                            Monitor.Log($"Warning for companion {companion.Name} from {contentPack.Manifest.Name}: Portrait property was given but no portrait.png was found", LogLevel.Warn);
                        }
                        else
                        {
                            companion.PortraitSheetPath = contentPack.ModContent.GetInternalAssetName(Path.Combine(companionFolder.Parent.Name, companionFolder.Name, "portrait.png")).Name;
                        }
                    }

                    if (contentPack.Translation != null)
                    {
                        companion.Translations = contentPack.Translation;
                    }

                    // Cache the full name of the companion, so that it can be reference by a Content Patcher token
                    if (_contentPatcherApi != null)
                    {
                        var assetToken = $"{TOKEN_HEADER}{companion.GetId()}";
                        AssetManager.idToAssetToken[companion.GetId()] = assetToken;

                        var modelObject = Helper.GameContent.Load <TokenModel>(assetToken);
                        //var modelObject = AssetManager.GetCompanionModelObject(Helper.Content.Load<Dictionary<string, object>>(assetToken, ContentSource.GameContent));
                        trackedModels[$"{CustomCompanions.TOKEN_HEADER}{companion.GetId()}"] = modelObject.Companion;
                    }
                }
            }

            this.SpawnSceneryCompanions(Game1.player.currentLocation);

            // Remove companions that no longer have an existing map tile property
            this.RemoveOrphanCompanions(Game1.player.currentLocation);
        }
        private void LoadContentPacks(bool isReload = false)
        {
            this.Reset(false, isReload);

            // Load the owned content packs
            foreach (IContentPack contentPack in Helper.ContentPacks.GetOwned())
            {
                Monitor.Log($"Loading companions from pack: {contentPack.Manifest.Name} {contentPack.Manifest.Version} by {contentPack.Manifest.Author}", LogLevel.Debug);

                var companionFolders = new DirectoryInfo(Path.Combine(contentPack.DirectoryPath, "Companions")).GetDirectories();
                if (companionFolders.Count() == 0)
                {
                    Monitor.Log($"No sub-folders found under Companions for the content pack {contentPack.Manifest.Name}!", LogLevel.Warn);
                    continue;
                }

                // Load in the companions
                foreach (var companionFolder in companionFolders)
                {
                    if (!File.Exists(Path.Combine(companionFolder.FullName, "companion.json")))
                    {
                        Monitor.Log($"Content pack {contentPack.Manifest.Name} is missing a companion.json under {companionFolder.Name}!", LogLevel.Warn);
                        continue;
                    }

                    CompanionModel companion = contentPack.ReadJsonFile <CompanionModel>(Path.Combine(companionFolder.Parent.Name, companionFolder.Name, "companion.json"));
                    companion.Name  = companion.Name.Replace(" ", "");
                    companion.Owner = contentPack.Manifest.UniqueID;

                    // Save the TileSheet, if one is given
                    if (String.IsNullOrEmpty(companion.TileSheetPath) && !File.Exists(Path.Combine(companionFolder.FullName, "companion.png")))
                    {
                        Monitor.Log($"Unable to add companion {companion.Name} from {contentPack.Manifest.Name}: No associated companion.png or TileSheetPath given", LogLevel.Warn);
                        continue;
                    }
                    else if (String.IsNullOrEmpty(companion.TileSheetPath))
                    {
                        companion.TileSheetPath = contentPack.ModContent.GetInternalAssetName(Path.Combine(companionFolder.Parent.Name, companionFolder.Name, "companion.png")).Name;
                    }

                    // Save the PortraitSheet, if one is given
                    if (companion.Portrait != null)
                    {
                        if (!File.Exists(Path.Combine(companionFolder.FullName, "portrait.png")))
                        {
                            Monitor.Log($"Warning for companion {companion.Name} from {contentPack.Manifest.Name}: Portrait property was given but no portrait.png was found", LogLevel.Warn);
                        }
                        else
                        {
                            companion.PortraitSheetPath = contentPack.ModContent.GetInternalAssetName(Path.Combine(companionFolder.Parent.Name, companionFolder.Name, "portrait.png")).Name;
                        }
                    }

                    if (contentPack.Translation != null)
                    {
                        companion.Translations = contentPack.Translation;
                    }
                    Monitor.Log(companion.ToString(), LogLevel.Trace);

                    // Add the companion to our cache
                    CompanionManager.companionModels.Add(companion);

                    // Cache the full name of the companion, so that it can be reference by a Content Patcher token
                    if (_contentPatcherApi != null)
                    {
                        var assetToken = $"{TOKEN_HEADER}{companion.GetId()}";
                        AssetManager.idToAssetToken[companion.GetId()] = assetToken;

                        if (!isReload)
                        {
                            var modelObject = Helper.GameContent.Load <TokenModel>(assetToken);
                            //var modelObject = AssetManager.GetCompanionModelObject(Helper.Content.Load<Dictionary<string, object>>(assetToken, ContentSource.GameContent));
                            trackedModels[$"{CustomCompanions.TOKEN_HEADER}{companion.GetId()}"] = modelObject.Companion;
                        }
                    }
                }

                if (_jsonAssetsApi != null && !isReload)
                {
                    // Load in the rings that will be paired to a companion
                    if (!Directory.Exists(Path.Combine(contentPack.DirectoryPath, "Objects")))
                    {
                        Monitor.Log($"No summoning rings available from {contentPack.Manifest.Name}, this may be intended", LogLevel.Trace);
                        continue;
                    }

                    foreach (var ringFolder in new DirectoryInfo(Path.Combine(contentPack.DirectoryPath, "Objects")).GetDirectories())
                    {
                        if (!File.Exists(Path.Combine(ringFolder.FullName, "object.json")))
                        {
                            Monitor.Log($"Content pack {contentPack.Manifest.Name} is missing a object.json under {ringFolder.Name}!", LogLevel.Warn);
                            continue;
                        }

                        RingModel ring = contentPack.ReadJsonFile <RingModel>(Path.Combine(ringFolder.Parent.Name, ringFolder.Name, "object.json"));
                        ring.Owner = contentPack.Manifest.UniqueID;

                        RingManager.rings.Add(ring);
                    }

                    // Generate content.json for Json Assets
                    contentPack.WriteJsonFile("content-pack.json", new ContentPackModel
                    {
                        Name        = contentPack.Manifest.Name,
                        Author      = contentPack.Manifest.Author,
                        Version     = contentPack.Manifest.Version.ToString(),
                        Description = contentPack.Manifest.Description,
                        UniqueID    = contentPack.Manifest.UniqueID,
                        UpdateKeys  = contentPack.Manifest.UpdateKeys,
                    });

                    // Load in the associated rings objects (via JA)
                    _jsonAssetsApi.LoadAssets(contentPack.DirectoryPath);
                }
            }
        }