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(); } }
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()) }); }
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); }
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); } }
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); }
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(); }; }
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)"); }
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()); } }
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); }); } }