Ejemplo n.º 1
0
        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;
            }
        }
Ejemplo n.º 2
0
        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;
            }
        }