Esempio n. 1
0
        private void migrateDataToRealm()
        {
            using (var db = contextFactory.GetForWrite())
                using (var realm = realmFactory.CreateContext())
                    using (var transaction = realm.BeginWrite())
                    {
                        // migrate ruleset settings. can be removed 20220315.
                        var existingSettings = db.Context.DatabasedSetting;

                        // only migrate data if the realm database is empty.
                        if (!realm.All <RealmRulesetSetting>().Any())
                        {
                            foreach (var dkb in existingSettings)
                            {
                                if (dkb.RulesetID == null)
                                {
                                    continue;
                                }

                                realm.Add(new RealmRulesetSetting
                                {
                                    Key       = dkb.Key,
                                    Value     = dkb.StringValue,
                                    RulesetID = dkb.RulesetID.Value,
                                    Variant   = dkb.Variant ?? 0,
                                });
                            }
                        }

                        db.Context.RemoveRange(existingSettings);

                        transaction.Commit();
                    }
        }
Esempio n. 2
0
        private void load(RealmContextFactory realmFactory)
        {
            var rulesetId = Ruleset?.ID;

            List <RealmKeyBinding> bindings;

            using (var realm = realmFactory.CreateContext())
                bindings = realm.All <RealmKeyBinding>().Where(b => b.RulesetID == rulesetId && b.Variant == variant).Detach();

            foreach (var defaultGroup in Defaults.GroupBy(d => d.Action))
            {
                int intKey = (int)defaultGroup.Key;

                // one row per valid action.
                Add(new KeyBindingRow(defaultGroup.Key, bindings.Where(b => b.ActionInt.Equals(intKey)).ToList())
                {
                    AllowMainMouseButtons = Ruleset != null,
                    Defaults = defaultGroup.Select(d => d.KeyCombination)
                });
            }

            Add(new ResetButton
            {
                Action = () => Children.OfType <KeyBindingRow>().ForEach(k => k.RestoreDefaults())
            });
        }
Esempio n. 3
0
        protected override bool PerformSave()
        {
            TLookup[] changed;

            lock (pendingWrites)
            {
                changed = pendingWrites.ToArray();
                pendingWrites.Clear();
            }

            if (realmFactory == null)
            {
                return(true);
            }

            using (var context = realmFactory.CreateContext())
            {
                context.Write(realm =>
                {
                    foreach (var c in changed)
                    {
                        var setting = realm.All <RealmRulesetSetting>().First(s => s.RulesetID == rulesetId && s.Variant == variant && s.Key == c.ToString());

                        setting.Value = ConfigStore[c].ToString();
                    }
                });
            }

            return(true);
        }
Esempio n. 4
0
        public void SelectRandomSkin()
        {
            using (var context = contextFactory.CreateContext())
            {
                // choose from only user skins, removing the current selection to ensure a new one is chosen.
                var randomChoices = context.All <SkinInfo>().Where(s => !s.DeletePending && s.ID != CurrentSkinInfo.Value.ID).ToArray();

                if (randomChoices.Length == 0)
                {
                    CurrentSkinInfo.Value = Skinning.DefaultSkin.CreateInfo().ToLiveUnmanaged();
                    return;
                }

                var chosen = randomChoices.ElementAt(RNG.Next(0, randomChoices.Length));

                CurrentSkinInfo.Value = chosen.ToLive(contextFactory);
            }
        }
Esempio n. 5
0
        public IReadOnlyList <string> GetReadableKeyCombinationsFor(GlobalAction globalAction)
        {
            List <string> combinations = new List <string>();

            using (var context = realmFactory.CreateContext())
            {
                foreach (var action in context.All <RealmKeyBinding>().Where(b => b.RulesetID == null && (GlobalAction)b.ActionInt == globalAction))
                {
                    string str = action.KeyCombination.ReadableString();

                    // even if found, the readable string may be empty for an unbound action.
                    if (str.Length > 0)
                    {
                        combinations.Add(str);
                    }
                }
            }

            return(combinations);
        }
Esempio n. 6
0
        public SkinManager(Storage storage, RealmContextFactory contextFactory, GameHost host, IResourceStore <byte[]> resources, AudioManager audio, Scheduler scheduler)
        {
            this.contextFactory = contextFactory;
            this.audio          = audio;
            this.scheduler      = scheduler;
            this.host           = host;
            this.resources      = resources;

            userFiles = new StorageBackedResourceStore(storage.GetStorageForDirectory("files"));

            skinModelManager = new SkinModelManager(storage, contextFactory, host, this);

            var defaultSkins = new[]
            {
                DefaultLegacySkin = new DefaultLegacySkin(this),
                DefaultSkin       = new DefaultSkin(this),
            };

            // Ensure the default entries are present.
            using (var context = contextFactory.CreateContext())
                using (var transaction = context.BeginWrite())
                {
                    foreach (var skin in defaultSkins)
                    {
                        if (context.Find <SkinInfo>(skin.SkinInfo.ID) == null)
                        {
                            context.Add(skin.SkinInfo.Value);
                        }
                    }

                    transaction.Commit();
                }

            CurrentSkinInfo.ValueChanged += skin => CurrentSkin.Value = skin.NewValue.PerformRead(GetSkin);

            CurrentSkin.Value         = DefaultSkin;
            CurrentSkin.ValueChanged += skin =>
            {
                if (!skin.NewValue.SkinInfo.Equals(CurrentSkinInfo.Value))
                {
                    throw new InvalidOperationException($"Setting {nameof(CurrentSkin)}'s value directly is not supported. Use {nameof(CurrentSkinInfo)} instead.");
                }

                SourceChanged?.Invoke();
            };
        }
Esempio n. 7
0
        public void Cleanup()
        {
            Logger.Log(@"Beginning realm file store cleanup");

            int totalFiles   = 0;
            int removedFiles = 0;

            // can potentially be run asynchronously, although we will need to consider operation order for disk deletion vs realm removal.
            using (var realm = realmFactory.CreateContext())
                using (var transaction = realm.BeginWrite())
                {
                    // TODO: consider using a realm native query to avoid iterating all files (https://github.com/realm/realm-dotnet/issues/2659#issuecomment-927823707)
                    var files = realm.All <RealmFile>().ToList();

                    foreach (var file in files)
                    {
                        totalFiles++;

                        if (file.BacklinksCount > 0)
                        {
                            continue;
                        }

                        try
                        {
                            removedFiles++;
                            Storage.Delete(file.GetStoragePath());
                            realm.Remove(file);
                        }
                        catch (Exception e)
                        {
                            Logger.Error(e, $@"Could not delete databased file {file.Hash}");
                        }
                    }

                    transaction.Commit();
                }

            Logger.Log($@"Finished realm file store cleanup ({removedFiles} of {totalFiles} deleted)");
        }
Esempio n. 8
0
        public virtual async Task <ILive <TModel>?> Import(TModel item, ArchiveReader?archive = null, bool lowPriority = false, CancellationToken cancellationToken = default)
        {
            using (var realm = ContextFactory.CreateContext())
            {
                cancellationToken.ThrowIfCancellationRequested();

                bool   checkedExisting = false;
                TModel?existing        = null;

                if (archive != null && !HasCustomHashFunction)
                {
                    // this is a fast bail condition to improve large import performance.
                    item.Hash = computeHashFast(archive);

                    checkedExisting = true;
                    existing        = CheckForExisting(item, realm);

                    if (existing != null)
                    {
                        // bare minimum comparisons
                        //
                        // note that this should really be checking filesizes on disk (of existing files) for some degree of sanity.
                        // or alternatively doing a faster hash check. either of these require database changes and reprocessing of existing files.
                        if (CanSkipImport(existing, item) &&
                            getFilenames(existing.Files).SequenceEqual(getShortenedFilenames(archive).Select(p => p.shortened).OrderBy(f => f)))
                        {
                            LogForModel(item, @$ "Found existing (optimised) {HumanisedModelName} for {item} (ID {existing.ID}) – skipping import.");

                            using (var transaction = realm.BeginWrite())
                            {
                                existing.DeletePending = false;
                                transaction.Commit();
                            }

                            return(existing.ToLive());
                        }

                        LogForModel(item, @"Found existing (optimised) but failed pre-check.");
                    }
                }

                try
                {
                    LogForModel(item, @"Beginning import...");

                    // TODO: do we want to make the transaction this local? not 100% sure, will need further investigation.
                    using (var transaction = realm.BeginWrite())
                    {
                        if (archive != null)
                        {
                            // TODO: look into rollback of file additions (or delayed commit).
                            item.Files.AddRange(createFileInfos(archive, Files, realm));
                        }

                        item.Hash = ComputeHash(item);

                        // TODO: we may want to run this outside of the transaction.
                        await Populate(item, archive, realm, cancellationToken).ConfigureAwait(false);

                        if (!checkedExisting)
                        {
                            existing = CheckForExisting(item, realm);
                        }

                        if (existing != null)
                        {
                            if (CanReuseExisting(existing, item))
                            {
                                LogForModel(item, @$ "Found existing {HumanisedModelName} for {item} (ID {existing.ID}) – skipping import.");

                                existing.DeletePending = false;
                                transaction.Commit();

                                return(existing.ToLive());
                            }

                            LogForModel(item, @"Found existing but failed re-use check.");

                            existing.DeletePending = true;

                            // todo: actually delete? i don't think this is required...
                            // ModelStore.PurgeDeletable(s => s.ID == existing.ID);
                        }

                        PreImport(item, realm);

                        // import to store
                        realm.Add(item);

                        transaction.Commit();
                    }

                    LogForModel(item, @"Import successfully completed!");
                }
                catch (Exception e)
                {
                    if (!(e is TaskCanceledException))
                    {
                        LogForModel(item, @"Database import or population failed and has been rolled back.", e);
                    }

                    throw;
                }

                return(item.ToLive());
            }
        }
Esempio n. 9
0
        private void addMissingRulesets()
        {
            using (var context = realmFactory.CreateContext())
            {
                context.Write(realm =>
                {
                    var rulesets = realm.All <RealmRuleset>();

                    List <Ruleset> instances = loadedAssemblies.Values
                                               .Select(r => Activator.CreateInstance(r) as Ruleset)
                                               .Where(r => r != null)
                                               .Select(r => r.AsNonNull())
                                               .ToList();

                    // add all legacy rulesets first to ensure they have exclusive choice of primary key.
                    foreach (var r in instances.Where(r => r is ILegacyRuleset))
                    {
                        if (realm.All <RealmRuleset>().FirstOrDefault(rr => rr.OnlineID == r.RulesetInfo.ID) == null)
                        {
                            realm.Add(new RealmRuleset(r.RulesetInfo.ShortName, r.RulesetInfo.Name, r.RulesetInfo.InstantiationInfo, r.RulesetInfo.ID));
                        }
                    }

                    // add any other rulesets which have assemblies present but are not yet in the database.
                    foreach (var r in instances.Where(r => !(r is ILegacyRuleset)))
                    {
                        if (rulesets.FirstOrDefault(ri => ri.InstantiationInfo.Equals(r.RulesetInfo.InstantiationInfo, StringComparison.Ordinal)) == null)
                        {
                            var existingSameShortName = rulesets.FirstOrDefault(ri => ri.ShortName == r.RulesetInfo.ShortName);

                            if (existingSameShortName != null)
                            {
                                // even if a matching InstantiationInfo was not found, there may be an existing ruleset with the same ShortName.
                                // this generally means the user or ruleset provider has renamed their dll but the underlying ruleset is *likely* the same one.
                                // in such cases, update the instantiation info of the existing entry to point to the new one.
                                existingSameShortName.InstantiationInfo = r.RulesetInfo.InstantiationInfo;
                            }
                            else
                            {
                                realm.Add(new RealmRuleset(r.RulesetInfo.ShortName, r.RulesetInfo.Name, r.RulesetInfo.InstantiationInfo, r.RulesetInfo.ID));
                            }
                        }
                    }

                    List <RealmRuleset> detachedRulesets = new List <RealmRuleset>();

                    // perform a consistency check and detach final rulesets from realm for cross-thread runtime usage.
                    foreach (var r in rulesets)
                    {
                        try
                        {
                            var resolvedType = Type.GetType(r.InstantiationInfo)
                                               ?? throw new RulesetLoadException(@"Type could not be resolved");

                            var instanceInfo = (Activator.CreateInstance(resolvedType) as Ruleset)?.RulesetInfo
                                               ?? throw new RulesetLoadException(@"Instantiation failure");

                            r.Name              = instanceInfo.Name;
                            r.ShortName         = instanceInfo.ShortName;
                            r.InstantiationInfo = instanceInfo.InstantiationInfo;
                            r.Available         = true;

                            detachedRulesets.Add(r.Clone());
                        }
                        catch (Exception ex)
                        {
                            r.Available = false;
                            Logger.Log($"Could not load ruleset {r}: {ex.Message}");
                        }
                    }

                    availableRulesets.AddRange(detachedRulesets);
                });
            }
        }