public async Task LoadCached() { _hasStarted = true; if (!CacheExists) { throw new InvalidOperationException("Cache file doesn't exists!"); } try { Data = await FactorioData.LoadFromCache(CachePath); _localizations = Data.Localizations ?? throw new ApplicationException("Cached data has no localization information!"); OnLoadingCompleted(); } catch (Exception e) { OnLoadingError(e.Message); _hasStarted = false; } }
public async void Start() { _hasStarted = true; // ReSharper disable VariableHidesOuterVariable void DefineSettings(dynamic raw, dynamic settingsStartup, dynamic settingsGlobal, dynamic settingsPlayer, string settingType) // ReSharper restore VariableHidesOuterVariable { var settings = raw[settingType]; if (settings != null) { foreach (var setting in ((Table)raw[settingType]).Values.Select(x => x.Table)) { if (setting != null) { var name = setting["name"]; if ((string)setting["setting_type"] == "startup") { settingsStartup[name] = new Table(_lua) { ["value"] = setting["default_value"] } } ; else if ((string)setting["setting_type"] == "runtime-global") { settingsGlobal[name] = new Table(_lua) { ["value"] = setting["default_value"] } } ; else if ((string)setting["settings_type"] == "runtime-per-user") { settingsPlayer[name] = new Table(_lua) { ["value"] = setting["default_value"] } } ; } } } } Task DoStage(Func <IModule, string?> stageFileCaller) { return(Task.Factory.StartNew(() => { foreach (var mod in ActiveModules) { var fileName = stageFileCaller(mod); if (fileName == null) { continue; } OnModuleFileLoading(new ModuleFileEventArgs(mod, fileName)); ClearModules(); _lua.DoStream(_loader.Load(fileName), _lua.Globals, fileName); } })); } void ClearModules() { ((dynamic)_lua.Globals["package"])["loaded"] = new Table(_lua); } void ResolveDependencies() { foreach (var mod in ActiveModules) { foreach (var dep in mod.Dependencies) { var dependee = ActiveModules.FirstOrDefault(x => x.Name == dep.Name); if (dependee != null) { dep.Resolve(dependee); } } } var sorted = new List <IModule>(ActiveModules.Count); foreach (var x in ActiveModules) { var bestMatch = 0; int idx = -1; foreach (var y in sorted) { idx++; var xDepY = x.Dependencies.Any(mod => mod.ActuallyDependsOn(y)); var yDepX = y.Dependencies.Any(mod => mod.ActuallyDependsOn(x)); if (xDepY && yDepX) { throw new ApplicationException($"Circular dependency found: {x} and {y}"); } if (xDepY) { bestMatch = idx + 1; continue; } if (yDepX) { break; } if (string.Compare(x.Name, y.Name, StringComparison.InvariantCultureIgnoreCase) > 0) { bestMatch = idx + 1; } } sorted.Insert(bestMatch, x); } _activeModules = sorted; ActiveModules = sorted.AsReadOnly(); } void CheckDependencies() { if (ActiveModules[0].Name != "core") { throw new ApplicationException("core should always be the first loaded module!"); } if (ActiveModules[1].Name != "base") { throw new ApplicationException("base should always be the second loaded module!"); } for (int i = 0; i < ActiveModules.Count; i++) { var mod = ActiveModules[i]; foreach (var dep in mod.Dependencies) { var dependee = ActiveModules.FirstOrDefault(x => x.Name == dep.Name); if (dependee != null) { if (ActiveModules.IndexOf(dependee) > i) { throw new ApplicationException("Dependency order is compromised!"); } } else if (dep.Type == Dependency.DependencyTypeEnum.Required) { throw new ApplicationException("Dependency doesn't met!"); } } } } try { ResolveDependencies(); CheckDependencies(); _localizations.Clear(); foreach (var mod in _activeModules) { _ = Task.Run(() => mod.LoadLocalizations(_localizations)); } var table = new Table(_lua); foreach (var mod in _activeModules) { table[mod.Name] = mod.Version.ToString(); } _lua.Globals["mods"] = table; OnSettingsStage(new StageEventArgs(_lua)); await DoStage(mod => mod.Settings); OnSettingsUpdatesStage(new StageEventArgs(_lua)); await DoStage(mod => mod.SettingsUpdates); OnSettingsFinalFixesStage(new StageEventArgs(_lua)); await DoStage(mod => mod.SettingsFinalFixes); var data = (dynamic)_lua.Globals["data"]; if (data["raw"] == null) { data["raw"] = new Table(_lua); } var raw = data["raw"]; var settings = (dynamic)(_lua.Globals["settings"] = new Table(_lua)); var global = settings["global"] = new Table(_lua); var startup = settings["startup"] = new Table(_lua); var player = settings["player"] = new Table(_lua); DefineSettings(raw, startup, global, player, "bool-setting"); DefineSettings(raw, startup, global, player, "int-setting"); DefineSettings(raw, startup, global, player, "double-setting"); DefineSettings(raw, startup, global, player, "string-setting"); OnDataStage(new StageEventArgs(_lua)); await DoStage(mod => mod.Data); var technologies = (Table)((dynamic)_lua.Globals)["data"]["raw"]["technology"]; foreach (var technology in technologies.Values) { if (technology == null) { continue; } technology.Table["prerequisites"] ??= new Table(_lua); } OnDataUpdatesStage(new StageEventArgs(_lua)); await DoStage(mod => mod.DataUpdates); OnDataFinalFixesStage(new StageEventArgs(_lua)); await DoStage(mod => mod.DataFinalFixes); // We don't want to assign Data value before LoadingCompleted event, so use temporary variable var fData = new FactorioData((Table)_lua.Globals["data"]) { Localizations = _localizations }; await fData.Save(CachePath); Data = fData; OnLoadingCompleted(); } catch (Exception e) { OnLoadingError(e.Message); _hasStarted = false; } }