Skip to content

Commit

Permalink
Allow disable and reload for mods with errors (#292)
Browse files Browse the repository at this point in the history
While organizing my thoughts about #286, I convinced myself this was a
good thing to do regardless of what else (if anything) happens on that
issue.

If there's an error loading a mod (e.g. Kux-BlueprintExtensions) the
user will have the opportunity to disable that mod and load the
remaining mods again. Based on not wanting wanting to add a third file
to bug reports and shpaass
[saying](#286 (comment))
"I'd choose to store the list in a per-mod-folder fashion", I decided to
remember the disabled mods only until the user changes the mod folder or
exits Yafc. If the error isn't obviously related to a mod, the button
will not appear.


![image](https://github.com/user-attachments/assets/818e148a-a6c6-48ed-b841-68a9cb0814ea)
  • Loading branch information
shpaass committed Sep 16, 2024
2 parents 61797a8 + 66d0b24 commit cd421ef
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 9 deletions.
17 changes: 16 additions & 1 deletion Yafc.Parser/FactorioDataSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public static partial class FactorioDataSource {

private static readonly ILogger logger = Logging.GetLogger(typeof(FactorioDataSource));
internal static Dictionary<string, ModInfo> allMods = [];
internal static HashSet<string> disabledMods = [];
public static readonly Version defaultFactorioVersion = new Version(1, 1);
private static byte[] ReadAllBytes(this Stream stream, int length) {
BinaryReader reader = new BinaryReader(stream);
Expand Down Expand Up @@ -168,6 +169,9 @@ public static Project Parse(string factorioPath, string modPath, string projectP
}

allMods["core"] = null!;
foreach (string disabledMod in disabledMods) {
_ = allMods.Remove(disabledMod);
}
logger.Information("Mod list parsed");

List<ModInfo> allFoundMods = [];
Expand Down Expand Up @@ -286,6 +290,7 @@ public static Project Parse(string factorioPath, string modPath, string projectP
DataUtils.netProduction = netProduction;


currentLoadingMod = null;
dataContext = new LuaContext();
object? settings = null;
if (File.Exists(modSettingsPath)) {
Expand All @@ -304,8 +309,8 @@ public static Project Parse(string factorioPath, string modPath, string projectP
dataContext.DoModFiles(modLoadOrder, "data.lua", progress);
dataContext.DoModFiles(modLoadOrder, "data-updates.lua", progress);
dataContext.DoModFiles(modLoadOrder, "data-final-fixes.lua", progress);
_ = dataContext.Exec(postProcess, "*", "post");
currentLoadingMod = null;
_ = dataContext.Exec(postProcess, "*", "post");

FactorioDataDeserializer deserializer = new FactorioDataDeserializer(expensive, factorioVersion ?? defaultFactorioVersion);
var project = deserializer.LoadData(projectPath, dataContext.data, (LuaTable)dataContext.defines["prototypes"]!, netProduction, progress, errorCollector, renderIcons);
Expand Down Expand Up @@ -446,4 +451,14 @@ public static IEnumerable<string> GetAllModFiles(string mod, string prefix) {
}
}
}

/// <summary>
/// Instructs Yafc not to load the specified mod, even if it is listed in mod-list.json. Mods that depend on this mod will also not be loaded.
/// </summary>
/// <param name="modName">The name of the mod to disable.</param>
public static void DisableMod(string modName) => disabledMods.Add(modName);
/// <summary>
/// Instructs Yafc to forget about all mods names previously passed to <see cref="DisableMod"/>, and resume loading all mods in mod-list.json.
/// </summary>
public static void ClearDisabledMods() => disabledMods.Clear();
}
47 changes: 39 additions & 8 deletions Yafc/Windows/WelcomeScreen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,14 @@ public class WelcomeScreen : WindowUtility, IProgress<(string, string)>, IKeyboa
private readonly ILogger logger = Logging.GetLogger<WelcomeScreen>();
private bool loading;
private string? currentLoad1, currentLoad2;
private string path = "", dataPath = "", modsPath = "";
private string path = "", dataPath = "", _modsPath = "";
private string modsPath {
get => _modsPath;
set {
_modsPath = value;
FactorioDataSource.ClearDisabledMods();
}
}
private bool expensive;
private bool netProduction;
private string createText;
Expand Down Expand Up @@ -89,11 +96,11 @@ public WelcomeScreen(ProjectDefinition? cliProject = null) : base(ImGuiUtils.Def

private void BuildError(ImGui gui) {
if (errorMod != null) {
gui.BuildText("Error While loading mod " + errorMod, TextBlockDisplayStyle.Centered with { Color = SchemeColor.Error });
gui.BuildText($"Error while loading mod {errorMod}.", TextBlockDisplayStyle.Centered with { Color = SchemeColor.Error });
}

gui.allocator = RectAllocator.Stretch;
gui.BuildText(errorMessage, TextBlockDisplayStyle.ErrorText);
gui.BuildText(errorMessage, TextBlockDisplayStyle.ErrorText with { Color = SchemeColor.ErrorText });
gui.DrawRectangle(gui.lastRect, SchemeColor.Error);
}

Expand All @@ -119,17 +126,22 @@ protected override void BuildContents(ImGui gui) {
if (gui.BuildButton("Copy to clipboard", SchemeColor.Grey)) {
_ = SDL.SDL_SetClipboardText(errorMessage);
}
if (errorMod != null && gui.BuildButton("Disable & reload").WithTooltip(gui, "Disable this mod until you close YAFC or change the mod folder.")) {
FactorioDataSource.DisableMod(errorMod);
errorMessage = null;
LoadProject();
}
if (gui.RemainingRow().BuildButton("Back")) {
errorMessage = null;
Rebuild();
}
}
}
else {
BuildPathSelect(gui, ref path, "Project file location", "You can leave it empty for a new project", EditType.Workspace);
BuildPathSelect(gui, ref dataPath, "Factorio Data location*\nIt should contain folders 'base' and 'core'",
BuildPathSelect(gui, path, "Project file location", "You can leave it empty for a new project", EditType.Workspace);
BuildPathSelect(gui, dataPath, "Factorio Data location*\nIt should contain folders 'base' and 'core'",
"e.g. C:/Games/Steam/SteamApps/common/Factorio/data", EditType.Factorio);
BuildPathSelect(gui, ref modsPath, "Factorio Mods location (optional)\nIt should contain file 'mod-list.json'",
BuildPathSelect(gui, modsPath, "Factorio Mods location (optional)\nIt should contain file 'mod-list.json'",
"If you don't use separate mod folder, leave it empty", EditType.Mods);

using (gui.EnterRow()) {
Expand Down Expand Up @@ -287,7 +299,7 @@ private void ValidateSelection() {
canCreate = factorioValid && modsValid;
}

private void BuildPathSelect(ImGui gui, ref string path, string description, string placeholder, EditType editType) {
private void BuildPathSelect(ImGui gui, string path, string description, string placeholder, EditType editType) {
gui.BuildText(description, TextBlockDisplayStyle.WrappedText);
gui.spacing = 0.5f;
using (gui.EnterGroup(default, RectAllocator.RightRow)) {
Expand All @@ -296,6 +308,17 @@ private void BuildPathSelect(ImGui gui, ref string path, string description, str
}

if (gui.RemainingRow(0f).BuildTextInput(path, out path, placeholder)) {
switch (editType) {
case EditType.Workspace:
this.path = path;
break;
case EditType.Factorio:
dataPath = path;
break;
case EditType.Mods:
modsPath = path;
break;
}
ValidateSelection();
}
}
Expand Down Expand Up @@ -372,6 +395,7 @@ private async void LoadProject() {
ex = ex.InnerException;
}

errorScroll.RebuildContents();
errorMod = FactorioDataSource.currentLoadingMod;
if (ex is LuaException lua) {
errorMessage = lua.Message;
Expand Down Expand Up @@ -435,10 +459,17 @@ private void BuildRecentProjectList(ImGui gui) {
}

public bool KeyDown(SDL.SDL_Keysym key) {
if (canCreate && key.scancode is SDL.SDL_Scancode.SDL_SCANCODE_RETURN or SDL.SDL_Scancode.SDL_SCANCODE_RETURN2 or SDL.SDL_Scancode.SDL_SCANCODE_KP_ENTER) {
if (canCreate && !loading && errorMessage == null
&& key.scancode is SDL.SDL_Scancode.SDL_SCANCODE_RETURN or SDL.SDL_Scancode.SDL_SCANCODE_RETURN2 or SDL.SDL_Scancode.SDL_SCANCODE_KP_ENTER) {

LoadProject();
return true;
}
if (errorMessage != null && key.scancode == SDL.SDL_Scancode.SDL_SCANCODE_ESCAPE) {
errorMessage = null;
Rebuild();
return true;
}
return false;
}
public bool TextInput(string input) => false;
Expand Down
2 changes: 2 additions & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ Date:
- Add OSX-arm64 build.
- Display link warnings in both the tooltips and the dropdowns.
- Add additional ways of counting buildings and modules when displaying the shopping list.
- Allow disable-and-reload when there's an error loading a mod.
Bugfixes:
- Fixed recipes now become accessible when their crafter does.
- Keyboard and UI fixes/improvements in the Welcome screen.
Internal changes:
- Allow tooltips to be displayed when hovering over radio buttons.
- Require parentheses when mixing && and || in the same expression.
Expand Down

0 comments on commit cd421ef

Please sign in to comment.