private static void ValidateModManifests() { var registeredMods = ModRegistry.GetRegisteredItems(); foreach (var mod in registeredMods.Where(n => n.IsFarmhandMod && n.ModState == ModState.Unloaded).Cast <ModManifest>()) { try { if (mod.UniqueId == null) { mod.UniqueId = new UniqueId <string>(Guid.NewGuid().ToString()); } if (!mod.UniqueId.ThisId.Contains("\\") && (!mod.HasContent || mod.Content.Textures == null || !mod.Content.Textures.Any(n => n.Id.Contains("\\")))) { continue; } Log.Error( $"Error - {mod.Name} by {mod.Author} manifest is invalid. UniqueIDs cannot contain \"\\\""); mod.ModState = ModState.InvalidManifest; } catch (Exception ex) { Log.Error($"Error validating mod {mod.Name} by {mod.Author}\n\t-{ex.Message}"); } } }
private static void LoadFinalMods() { var registeredMods = ModRegistry.GetRegisteredItems(); foreach (var mod in registeredMods.Where(n => n.ModState == ModState.Unloaded)) { Log.Verbose($"Loading mod: {mod.Name} by {mod.Author}"); try { ApiEvents.InvokeModPreLoad(mod); mod.OnBeforeLoaded(); if (mod.HasContent) { mod.LoadContent(); } if (mod.HasDll) { mod.LoadModDll(); } mod.ModState = ModState.Loaded; mod.OnAfterLoaded(); Log.Success($"Loaded Mod: {mod.Name} v{mod.Version} by {mod.Author}"); ApiEvents.InvokeModPostLoad(mod); } catch (Exception ex) { mod.ModState = ModState.Errored; Log.Exception($"Error loading mod {mod.Name} by {mod.Author}", ex); ApiEvents.InvokeModLoadError(mod); } } }
private void SetUpPositions() { _labels.Clear(); _modToggles.Clear(); _currentItemIndex = 0; var mods = ModRegistry.GetRegisteredItems(); for (var index = 0; index < 7; ++index) { _optionSlots.Add(new ClickableComponent(new Rectangle(xPositionOnScreen + Game1.tileSize / 4, yPositionOnScreen + Game1.tileSize * 6 / 4 + Game1.pixelZoom + index * ((height - Game1.tileSize * 2) / 7), width - Game1.tileSize / 2, (height - Game1.tileSize * 2) / 7 + Game1.pixelZoom), string.Concat(index))); } foreach (var mod in mods) { if (mod.UniqueId == ModLoader1.Instance.ModSettings.UniqueId) { continue; } var checkbox = new DisableableOptionCheckbox($"{mod.Name} by {mod.Author}", 11) { IsChecked = mod.ModState == ModState.Loaded }; if (mod.ModState != ModState.Loaded && mod.ModState != ModState.Deactivated) { ResolveLoadingIssue(checkbox, mod); } _modToggles.Add(checkbox); _modOptions[checkbox] = mod; } }
private static void LoadFinalMods() { var registeredMods = ModRegistry.GetRegisteredItems(); foreach (var mod in registeredMods.Where(n => n.ModState == ModState.Unloaded)) { Log.Verbose($"Loading mod: {mod.Name} by {mod.Author}"); try { mod.OnBeforeLoaded(); if (mod.HasContent) { mod.LoadContent(); } if (mod.HasDll) { mod.LoadModDll(); } mod.ModState = ModState.Loaded; mod.OnAfterLoaded(); Log.Success($"Loaded Mod: {mod.Name} v{mod.Version} by {mod.Author}"); } catch (Exception ex) { Log.Exception($"Error loading mod {mod.Name} by {mod.Author}", ex); mod.ModState = ModState.Errored; //TODO, well something broke. Do summut' 'bout it! } } }
public void OnAfterGameInitialise(object sender, EventArgsOnGameInitialised e) { var test = ModRegistry.GetRegisteredItems(); var texture = ModSettings.GetTexture("icon_menuModsButton"); Farmhand.UI.TitleMenu.RegisterNewTitleButton(new Farmhand.UI.TitleMenu.CustomTitleOption { Key = "Mods", Texture = texture, TextureSourceRect = new Rectangle(222, 187, 74, 58), OnClick = OnModMenuItemClicked }); }
public void OnAfterGameInitialise(object sender, EventArgsOnGameInitialised e) { var test = ModRegistry.GetRegisteredItems(); var texture = ModSettings.GetTexture("icon_menuModsButton"); //var texture2 = Texture2D.FromStream(Game1.graphics.GraphicsDevice, new FileStream("FarmhandContent\\customUI.png", FileMode.Open)); Farmhand.UI.TitleMenu.RegisterNewTitleButton(new Farmhand.UI.TitleMenu.CustomTitleOption { Key = "Mods", Texture = texture, TextureSourceRect = new Rectangle(222, 187, 74, 58), OnClick = OnModMenuItemClicked }); }
private static void ApiEvents_OnModError(object sender, Events.Arguments.EventArgsOnModError e) { var mod = ModRegistry.GetRegisteredItems().FirstOrDefault(n => n.ModAssembly == e.Assembly); if (mod != null) { Log.Exception($"Exception thrown by mod: {mod.Name} - {mod.Author}", e.Exception); DeactivateMod(mod, ModState.Errored, e.Exception); } else { Log.Exception($"Exception thrown by unknown mod with assembly: {e.Assembly.FullName}", e.Exception); DetachAssemblyDelegates(e.Assembly); } }
/// <summary> /// Forces the game to reload mod configurations which use save specific settings. /// </summary> public static void ReloadConfigurations() { var mods = ModRegistry.GetRegisteredItems().Where(n => n.IsFarmhandMod).Cast <ModManifest>(); foreach (var mod in mods) { var config = mod.Instance?.ConfigurationSettings; if (config != null) { if (config.UseSaveSpecificConfiguration) { config.Load(); } } } }
internal static void LoadMods() { Log.Info($"Stardew Valley v{Game1.version}"); Log.Info($"Stardew Farmhand v{Constants.Version}"); ApiEvents.OnModError += ApiEvents_OnModError; AppDomain currentDomain = AppDomain.CurrentDomain; currentDomain.AssemblyResolve += CurrentDomainOnAssemblyResolve; Log.Success("Initializing Mappings"); GlobalRouteManager.InitialiseMappings(); Log.Success("Mappings Initialized"); Log.Info("Loading Mods..."); try { Log.Verbose("Loading Mod Manifests"); LoadModManifests(); Log.Verbose("Validating Mod Manifests"); ValidateModManifests(); Log.Verbose("Resolving Mod Dependencies"); ResolveDependencies(); Log.Verbose("Importing Mod DLLs, Settings, and Content"); LoadFinalMods(); if (UsingSmapiMods) { Log.Verbose("Using SMAPI - Attaching SMAPI events"); ModEventManager.AttachSmapiEvents(); } } catch (Exception ex) { Log.Error(ex.Message); Log.Error(ex.StackTrace); } var numModsLoaded = ModRegistry.GetRegisteredItems().Count(n => n.ModState == ModState.Loaded); Log.Info($"{numModsLoaded} Mods Loaded!"); Game1.version += $"Stardew Farmhand v{Constants.Version}: {numModsLoaded} mods loaded"; }
private static void BubbleDependencyLoadError(ModManifest erroredMod) { Log.Error($"Marking {erroredMod.Name} dependency hierarchy as failed"); var problemMods = new Stack <IModManifest>(); // We use this one to avoid circular resolution issues var resolvedMods = new List <IModManifest>(); problemMods.Push(erroredMod); do { var mod = problemMods.Pop(); resolvedMods.Add(mod); var dependants = ModRegistry.GetRegisteredItems() .Where(n => n.IsFarmhandMod) .Cast <ModManifest>() .Where( n => n.Dependencies != null && n.Dependencies.Any(d => d.IsRequired && mod.UniqueId.Equals(d.UniqueId))); foreach (var dependant in dependants) { dependant.ModState = ModState.DependencyLoadError; if (!resolvedMods.Contains(dependant)) { Log.Verbose( $"\tDisabling {dependant.Name} due to {mod.Name} failure." + (mod == erroredMod ? string.Empty : $" (Cascaded failure loading {erroredMod.Name}")); problemMods.Push(dependant); } } }while (problemMods.Count > 0); Log.Verbose($"{erroredMod.Name} all marked failed"); }
private void SetUpPositions() { _labels.Clear(); _modToggles.Clear(); _currentItemIndex = 0; upArrow = new ClickableTextureComponent(new Rectangle(xPositionOnScreen + width + Game1.tileSize / 4, yPositionOnScreen + Game1.tileSize / 4, 11 * Game1.pixelZoom, 12 * Game1.pixelZoom), Game1.mouseCursors, new Rectangle(421, 459, 11, 12), Game1.pixelZoom); downArrow = new ClickableTextureComponent(new Rectangle(xPositionOnScreen + width + Game1.tileSize / 4, yPositionOnScreen + height - Game1.tileSize, 11 * Game1.pixelZoom, 12 * Game1.pixelZoom), Game1.mouseCursors, new Rectangle(421, 472, 11, 12), Game1.pixelZoom); scrollBar = new ClickableTextureComponent(new Rectangle(upArrow.bounds.X + Game1.pixelZoom * 3, upArrow.bounds.Y + upArrow.bounds.Height + Game1.pixelZoom, 6 * Game1.pixelZoom, 10 * Game1.pixelZoom), Game1.mouseCursors, new Rectangle(435, 463, 6, 10), Game1.pixelZoom); scrollBarRunner = new Rectangle(scrollBar.bounds.X, upArrow.bounds.Y + upArrow.bounds.Height + Game1.pixelZoom, scrollBar.bounds.Width, height - Game1.tileSize - upArrow.bounds.Height - Game1.pixelZoom * 7); var mods = ModRegistry.GetRegisteredItems(); for (var index = 0; index < 4; ++index) { _optionSlots.Add(new ClickableComponent(new Rectangle(xPositionOnScreen + Game1.tileSize / 4, yPositionOnScreen + Game1.tileSize / 4 + index * (height / 4) - (index * 7), width - Game1.tileSize / 2, (height / 4 + Game1.pixelZoom) - 11), string.Concat(index))); } foreach (var mod in mods) { if (Equals(mod.UniqueId, ModLoader1.Instance.ModSettings.UniqueId)) { continue; } var checkbox = new DisableableOptionCheckbox("", 11) { IsChecked = mod.ModState == ModState.Loaded }; if (mod.IsFarmhandMod && mod.ModState != ModState.Loaded && mod.ModState != ModState.Deactivated) { ResolveLoadingIssue(checkbox, (ModManifest)mod); } _modToggles.Add(checkbox); _modOptions[checkbox] = mod; } }
private static void LoadFinalMods() { var registeredMods = ModRegistry.GetRegisteredItems(); foreach (var mod in registeredMods.Where(n => n.ModState == ModState.Unloaded)) { Log.Verbose($"Loading mod: {mod.Name} by {mod.Author}"); try { ApiEvents.InvokeModPreLoad(mod); mod.OnBeforeLoaded(); if (mod.HasContent) { mod.LoadContent(); } if (mod.HasDll) { mod.LoadModDll(); } mod.ModState = ModState.Loaded; mod.OnAfterLoaded(); Log.Success($"Loaded Mod: {mod.Name} v{mod.Version} by {mod.Author}"); ApiEvents.InvokeModPostLoad(mod); } catch (Exception ex) { mod.ModState = ModState.Errored; Log.Exception($"Error loading mod {mod.Name} by {mod.Author}", ex); ApiEvents.InvokeModLoadError(mod); } } // See ReferenceFix.Data.BuildXnaTypeCache() // Since mod loading is done we don't need this anymore. // There are a lot of types, so might as well save the memory. ReferenceHelper.XnaTypes.Clear(); }
private static void ResolveDependencies() { var registeredMods = ModRegistry.GetRegisteredItems().Where(n => n.IsFarmhandMod).Cast <ModManifest>(); // Loop to verify every dependent mod is available. bool stateChange; var modInfos = registeredMods as ModManifest[] ?? registeredMods.ToArray(); do { stateChange = false; foreach (var mod in modInfos) { if (mod.ModState == ModState.MissingDependency || mod.Dependencies == null) { continue; } foreach (var dependency in mod.Dependencies.Where(n => n.IsRequired)) { var dependencyMatch = modInfos.FirstOrDefault(n => n.UniqueId.Equals(dependency.UniqueId)); dependency.DependencyState = DependencyState.Ok; if (dependencyMatch == null) { mod.ModState = ModState.MissingDependency; dependency.DependencyState = DependencyState.Missing; stateChange = true; Log.Error($"Failed to load {mod.Name} due to missing dependency: {dependency.UniqueId}"); } else if (dependencyMatch.ModState == ModState.MissingDependency) { mod.ModState = ModState.MissingDependency; dependency.DependencyState = DependencyState.ParentMissing; stateChange = true; Log.Error( $"Failed to load {mod.Name} due to missing dependency missing dependency: {dependency.UniqueId}"); } else { var dependencyVersion = dependencyMatch.Version; if (dependencyVersion == null) { continue; } if (dependency.MinimumVersion != null && dependency.MinimumVersion > dependencyVersion) { mod.ModState = ModState.MissingDependency; dependency.DependencyState = DependencyState.TooLowVersion; stateChange = true; Log.Error( $"Failed to load {mod.Name} due to minimum version incompatibility with {dependency.UniqueId}: " + $"v.{dependencyMatch.Version} < v.{dependency.MinimumVersion}"); } else if (dependency.MaximumVersion != null && dependency.MaximumVersion < dependencyVersion) { mod.ModState = ModState.MissingDependency; dependency.DependencyState = DependencyState.TooHighVersion; stateChange = true; Log.Error( $"Failed to load {mod.Name} due to maximum version incompatibility with {dependency.UniqueId}: " + $"v.{dependencyMatch.Version} > v.{dependency.MaximumVersion}"); } } } } }while (stateChange); }
private static void LoadFinalMods() { Func <ModManifest[], List <ModManifest>, List <ModManifest>, List <ModManifest>, ModManifest[]> getModsForThisPass = (mods, modsLoaded, modsErrored, modsProcessed) => { Func <ModDependency, bool> isDependencyLoaded = dependency => { if (dependency.IsRequired) { return(modsLoaded.Any(ld => ld.UniqueId.Equals(dependency.UniqueId))); } var dependentMod = mods.FirstOrDefault(n => n.UniqueId.Equals(dependency.UniqueId)); return(dependentMod?.ModState != ModState.Unloaded); }; return (mods.Where( n => n.ModState == ModState.Unloaded && (n.Dependencies == null || n.Dependencies.All(d => isDependencyLoaded(d)))) .ToArray()); }; var modsToLoad = ModRegistry.GetRegisteredItems().Where(n => n.IsFarmhandMod).Cast <ModManifest>().ToArray(); var loadedMods = new List <ModManifest>(); var erroredMods = new List <ModManifest>(); var processedMods = new List <ModManifest>(); var modsThisPass = getModsForThisPass(modsToLoad, loadedMods, erroredMods, processedMods); while (modsThisPass.Any()) { foreach (var mod in modsThisPass) { processedMods.Add(mod); if (mod.ModState == ModState.DependencyLoadError) { erroredMods.Add(mod); continue; } Log.Verbose($"Loading mod: {mod.Name} by {mod.Author}"); try { ApiEvents.InvokeModPreLoad(mod); mod.OnBeforeLoaded(); if (mod.HasContent) { mod.LoadContent(); } if (mod.HasDll) { if (!mod.LoadModDll()) { mod.ModState = ModState.Errored; ApiEvents.InvokeModLoadError(mod); erroredMods.Add(mod); BubbleDependencyLoadError(mod); continue; } } mod.ModState = ModState.Loaded; mod.OnAfterLoaded(); Log.Success($"Loaded Mod: {mod.Name} v{mod.Version} by {mod.Author}"); ApiEvents.InvokeModPostLoad(mod); loadedMods.Add(mod); } catch (Exception ex) { mod.ModState = ModState.Errored; Log.Exception($"Error loading mod {mod.Name} by {mod.Author}", ex); ApiEvents.InvokeModLoadError(mod); erroredMods.Add(mod); BubbleDependencyLoadError(mod); break; } } modsThisPass = getModsForThisPass(modsToLoad, loadedMods, erroredMods, processedMods); } // See ReferenceFix.Data.BuildXnaTypeCache() // Since mod loading is done we don't need this anymore. // There are a lot of types, so might as well save the memory. ReferenceHelper.XnaTypes.Clear(); }