public RulesetStore(RealmAccess realm, Storage?storage = null) { realmAccess = realm; // On android in release configuration assemblies are loaded from the apk directly into memory. // We cannot read assemblies from cwd, so should check loaded assemblies instead. loadFromAppDomain(); // This null check prevents Android from attempting to load the rulesets from disk, // as the underlying path "AppContext.BaseDirectory", despite being non-nullable, it returns null on android. // See https://github.com/xamarin/xamarin-android/issues/3489. if (RuntimeInfo.StartupDirectory != null) { loadFromDisk(); } // the event handler contains code for resolving dependency on the game assembly for rulesets located outside the base game directory. // It needs to be attached to the assembly lookup event before the actual call to loadUserRulesets() else rulesets located out of the base game directory will fail // to load as unable to locate the game core assembly. AppDomain.CurrentDomain.AssemblyResolve += resolveRulesetDependencyAssembly; var rulesetStorage = storage?.GetStorageForDirectory(@"rulesets"); if (rulesetStorage != null) { loadUserRulesets(rulesetStorage); } addMissingRulesets(); }
private void load(RealmAccess realm) { string rulesetName = Ruleset?.ShortName; var bindings = realm.Run(r => r.All <RealmKeyBinding>() .Where(b => b.RulesetName == rulesetName && 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()) }); }
public RealmFileStore(RealmAccess realm, Storage storage) { this.realm = realm; Storage = storage.GetStorageForDirectory(@"files"); Store = new StorageBackedResourceStore(Storage); }
public SkinModelManager(Storage storage, RealmAccess realm, IStorageResourceProvider skinResources) : base(storage, realm) { this.skinResources = skinResources; // can be removed 20220420. populateMissingHashes(); }
public void SetUp() { var directory = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString())); storage = new NativeStorage(directory.FullName); realm = new RealmAccess(storage, "test"); keyBindingStore = new RealmKeyBindingStore(realm, new ReadableKeyCombinationProvider()); }
public ScoreManager(RulesetStore rulesets, Func <BeatmapManager> beatmaps, Storage storage, RealmAccess realm, Scheduler scheduler, Func <BeatmapDifficultyCache> difficulties = null, OsuConfigManager configManager = null) { this.realm = realm; this.scheduler = scheduler; this.difficulties = difficulties; this.configManager = configManager; scoreModelManager = new ScoreModelManager(rulesets, beatmaps, storage, realm); }
protected RulesetConfigManager(SettingsStore store, RulesetInfo ruleset, int?variant = null) { realm = store?.Realm; rulesetName = ruleset.ShortName; this.variant = variant ?? 0; Load(); InitialiseDefaults(); }
private static long getFileSize(Storage testStorage, RealmAccess realm) { try { using (var stream = testStorage.GetStream(realm.Filename)) return(stream?.Length ?? 0); } catch { // windows runs may error due to file still being open. return(0); } }
public SkinManager(Storage storage, RealmAccess realm, GameHost host, IResourceStore <byte[]> resources, AudioManager audio, Scheduler scheduler) : base(storage, realm) { this.audio = audio; this.scheduler = scheduler; this.host = host; this.resources = resources; userFiles = new StorageBackedResourceStore(storage.GetStorageForDirectory("files")); skinImporter = new SkinImporter(storage, realm, this) { PostNotification = obj => PostNotification?.Invoke(obj), }; var defaultSkins = new[] { DefaultLegacySkin = new DefaultLegacySkin(this), DefaultSkin = new DefaultSkin(this), }; // Ensure the default entries are present. realm.Write(r => { foreach (var skin in defaultSkins) { if (r.Find <SkinInfo>(skin.SkinInfo.ID) == null) { r.Add(skin.SkinInfo.Value); } } }); 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 override void SetUp() { storage = new TemporaryNativeStorage("realm-benchmark"); storage.DeleteDirectory(string.Empty); realm = new RealmAccess(storage, "client"); realm.Run(r => { realm.Write(c => c.Add(TestResources.CreateTestBeatmapSetInfo(rulesets: new[] { new OsuRuleset().RulesetInfo }))); }); updateThread = new UpdateThread(() => { }, null); updateThread.Start(); }
protected void RunTestWithRealmAsync(Func <RealmAccess, Storage, Task> testAction, [CallerMemberName] string caller = "") { using (HeadlessGameHost host = new CleanRunHeadlessGameHost(callingMethodName: caller)) { host.Run(new RealmTestGame(async() => { var testStorage = storage.GetStorageForDirectory(caller); using (var realm = new RealmAccess(testStorage, OsuGameBase.CLIENT_DATABASE_FILENAME)) { Logger.Log($"Running test using realm file {testStorage.GetFullPath(realm.Filename)}"); await testAction(realm, testStorage); realm.Dispose(); Logger.Log($"Final database size: {getFileSize(testStorage, realm)}"); realm.Compact(); } })); } }
protected void RunTestWithRealm(Action <RealmAccess, OsuStorage> testAction, [CallerMemberName] string caller = "") { using (HeadlessGameHost host = new CleanRunHeadlessGameHost(callingMethodName: caller)) { host.Run(new RealmTestGame(() => { // ReSharper disable once AccessToDisposedClosure var testStorage = new OsuStorage(host, storage.GetStorageForDirectory(caller)); using (var realm = new RealmAccess(testStorage, OsuGameBase.CLIENT_DATABASE_FILENAME)) { Logger.Log($"Running test using realm file {testStorage.GetFullPath(realm.Filename)}"); testAction(realm, testStorage); realm.Dispose(); Logger.Log($"Final database size: {getFileSize(testStorage, realm)}"); realm.Compact(); Logger.Log($"Final database size after compact: {getFileSize(testStorage, realm)}"); } })); } }
public BeatmapManager(Storage storage, RealmAccess realm, RulesetStore rulesets, IAPIProvider?api, AudioManager audioManager, IResourceStore <byte[]> gameResources, GameHost?host = null, WorkingBeatmap?defaultBeatmap = null, bool performOnlineLookups = false) : base(storage, realm) { if (performOnlineLookups) { if (api == null) { throw new ArgumentNullException(nameof(api), "API must be provided if online lookups are required."); } onlineBeatmapLookupQueue = new BeatmapOnlineLookupQueue(api, storage); } var userResources = new RealmFileStore(realm, storage).Store; BeatmapTrackStore = audioManager.GetTrackStore(userResources); beatmapImporter = CreateBeatmapImporter(storage, realm, rulesets, onlineBeatmapLookupQueue); beatmapImporter.PostNotification = obj => PostNotification?.Invoke(obj); workingBeatmapCache = CreateWorkingBeatmapCache(audioManager, gameResources, userResources, defaultBeatmap, host); }
public TestBeatmapManager(Storage storage, RealmAccess realm, RulesetStore rulesets, IAPIProvider api, [NotNull] AudioManager audioManager, IResourceStore <byte[]> resources, GameHost host, WorkingBeatmap defaultBeatmap) : base(storage, realm, rulesets, api, audioManager, resources, host, defaultBeatmap) { }
protected RealmArchiveModelManager(Storage storage, RealmAccess realm) : base(storage, realm) { realmFileStore = new RealmFileStore(realm, storage); }
private void load(OsuConfigManager config, Framework.Game game, RealmAccess realm, IAPIProvider api) { // prevent user from changing beatmap while the intro is still running. beatmap = Beatmap.BeginLease(false); MenuVoice = config.GetBindable <bool>(OsuSetting.MenuVoice); MenuMusic = config.GetBindable <bool>(OsuSetting.MenuMusic); if (api.LocalUser.Value.IsSupporter) { AddInternal(skinnableSeeya = new SkinnableSound(new SampleInfo(SeeyaSampleName))); } else { seeya = audio.Samples.Get(SeeyaSampleName); } // if the user has requested not to play theme music, we should attempt to find a random beatmap from their collection. if (!MenuMusic.Value) { realm.Run(r => { var usableBeatmapSets = r.All <BeatmapSetInfo>().Where(s => !s.DeletePending && !s.Protected).AsRealmCollection(); int setCount = usableBeatmapSets.Count; if (setCount > 0) { var found = usableBeatmapSets[RNG.Next(0, setCount - 1)].Beatmaps.FirstOrDefault(); if (found != null) { initialBeatmap = beatmaps.GetWorkingBeatmap(found); } } }); } // we generally want a song to be playing on startup, so use the intro music even if a user has specified not to if no other track is available. if (initialBeatmap == null) { // Intro beatmaps are generally made using the osu! ruleset. // It might not be present in test projects for other rulesets. bool osuRulesetPresent = rulesets.GetRuleset(0) != null; if (!loadThemedIntro() && osuRulesetPresent) { // if we detect that the theme track or beatmap is unavailable this is either first startup or things are in a bad state. // this could happen if a user has nuked their files store. for now, reimport to repair this. var import = beatmaps.Import(new ZipArchiveReader(game.Resources.GetStream($"Tracks/{BeatmapFile}"), BeatmapFile)).GetResultSafely(); import?.PerformWrite(b => b.Protected = true); loadThemedIntro(); } } bool loadThemedIntro() { var setInfo = beatmaps.QueryBeatmapSet(b => b.Protected && b.Hash == BeatmapHash); if (setInfo == null) { return(false); } setInfo.PerformRead(s => { if (s.Beatmaps.Count == 0) { return; } initialBeatmap = beatmaps.GetWorkingBeatmap(s.Beatmaps.First()); }); return(UsingThemedIntro = initialBeatmap != null); } }
public BeatmapImporter(Storage storage, RealmAccess realm, BeatmapOnlineLookupQueue?onlineLookupQueue = null) : base(storage, realm) { this.onlineLookupQueue = onlineLookupQueue; }
public ScoreImporter(RulesetStore rulesets, Func <BeatmapManager> beatmaps, Storage storage, RealmAccess realm) : base(storage, realm) { this.rulesets = rulesets; this.beatmaps = beatmaps; }
public TestBeatmapModelManager(TestBeatmapManager testBeatmapManager, Storage storage, RealmAccess databaseAccess, BeatmapOnlineLookupQueue beatmapOnlineLookupQueue) : base(databaseAccess, storage, beatmapOnlineLookupQueue) { this.testBeatmapManager = testBeatmapManager; }
public SettingsStore(RealmAccess realm) { Realm = realm; }
protected virtual BeatmapModelManager CreateBeatmapModelManager(Storage storage, RealmAccess realm, RulesetStore rulesets, BeatmapOnlineLookupQueue?onlineLookupQueue) => new BeatmapModelManager(realm, storage, onlineLookupQueue);
protected override BeatmapModelManager CreateBeatmapModelManager(Storage storage, RealmAccess realm, RulesetStore rulesets, BeatmapOnlineLookupQueue onlineLookupQueue) { return(new TestBeatmapModelManager(storage, realm, onlineLookupQueue)); }
public TestBeatmapModelManager(Storage storage, RealmAccess databaseAccess, BeatmapOnlineLookupQueue beatmapOnlineLookupQueue) : base(databaseAccess, storage, beatmapOnlineLookupQueue) { }
private void prepareDetachedRulesets(RealmAccess realmAccess) { realmAccess.Write(realm => { var rulesets = realm.All <RulesetInfo>(); 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 <RulesetInfo>().FirstOrDefault(rr => rr.OnlineID == r.RulesetInfo.OnlineID) == null) { realm.Add(new RulesetInfo(r.RulesetInfo.ShortName, r.RulesetInfo.Name, r.RulesetInfo.InstantiationInfo, r.RulesetInfo.OnlineID)); } } // 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 RulesetInfo(r.RulesetInfo.ShortName, r.RulesetInfo.Name, r.RulesetInfo.InstantiationInfo, r.RulesetInfo.OnlineID)); } } } List <RulesetInfo> detachedRulesets = new List <RulesetInfo>(); // perform a consistency check and detach final rulesets from realm for cross-thread runtime usage. foreach (var r in rulesets.OrderBy(r => r.OnlineID)) { 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"); // If a ruleset isn't up-to-date with the API, it could cause a crash at an arbitrary point of execution. // To eagerly handle cases of missing implementations, enumerate all types here and mark as non-available on throw. resolvedType.Assembly.GetTypes(); 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.OrderBy(r => r)); }); }
public RealmRulesetStore(RealmAccess realm, Storage?storage = null) : base(storage) { prepareDetachedRulesets(realm); }
private void load(GameHost host, RealmAccess realm) { SettingsButton blockAction; SettingsButton unblockAction; Children = new Drawable[] { new SettingsButton { Text = DebugSettingsStrings.ClearAllCaches, Action = host.Collect }, new SettingsButton { Text = DebugSettingsStrings.CompactRealm, Action = () => { // Blocking operations implicitly causes a Compact(). using (realm.BlockAllOperations()) { } } }, blockAction = new SettingsButton { Text = "Block realm", }, unblockAction = new SettingsButton { Text = "Unblock realm", }, }; blockAction.Action = () => { try { var token = realm.BlockAllOperations(); blockAction.Enabled.Value = false; // As a safety measure, unblock after 10 seconds. // This is to handle the case where a dev may block, but then something on the update thread // accesses realm and blocks for eternity. Task.Factory.StartNew(() => { Thread.Sleep(10000); unblock(); }); unblockAction.Action = unblock; void unblock() { if (token == null) { return; } token?.Dispose(); token = null; Scheduler.Add(() => { blockAction.Enabled.Value = true; unblockAction.Action = null; }); } } catch (Exception e) { Logger.Error(e, "Blocking realm failed"); } }; }
protected virtual BeatmapImporter CreateBeatmapImporter(Storage storage, RealmAccess realm, RulesetStore rulesets, BeatmapOnlineLookupQueue?onlineLookupQueue) => new BeatmapImporter(storage, realm, onlineLookupQueue);
public RealmKeyBindingStore(RealmAccess realm, ReadableKeyCombinationProvider keyCombinationProvider) { this.realm = realm; this.keyCombinationProvider = keyCombinationProvider; }
public NonOptimisedBeatmapImporter(RealmAccess realm, Storage storage) : base(realm, storage) { }
private void load(ReadableKeyCombinationProvider keyCombinationProvider) { try { using (var str = File.OpenRead(typeof(OsuGameBase).Assembly.Location)) VersionHash = str.ComputeMD5Hash(); } catch { // special case for android builds, which can't read DLLs from a packed apk. // should eventually be handled in a better way. VersionHash = $"{Version}-{RuntimeInfo.OS}".ComputeMD5Hash(); } Resources.AddStore(new DllResourceStore(OsuResources.ResourceAssembly)); if (Storage.Exists(DatabaseContextFactory.DATABASE_NAME)) { dependencies.Cache(EFContextFactory = new DatabaseContextFactory(Storage)); } dependencies.Cache(realm = new RealmAccess(Storage, "client", EFContextFactory)); dependencies.CacheAs <RulesetStore>(RulesetStore = new RealmRulesetStore(realm, Storage)); dependencies.CacheAs <IRulesetStore>(RulesetStore); Decoder.RegisterDependencies(RulesetStore); // Backup is taken here rather than in EFToRealmMigrator to avoid recycling realm contexts // after initial usages below. It can be moved once a direction is established for handling re-subscription. // See https://github.com/ppy/osu/pull/16547 for more discussion. if (EFContextFactory != null) { const string backup_folder = "backups"; string migration = $"before_final_migration_{DateTimeOffset.UtcNow.ToUnixTimeSeconds()}"; EFContextFactory.CreateBackup(Path.Combine(backup_folder, $"client.{migration}.db")); realm.CreateBackup(Path.Combine(backup_folder, $"client.{migration}.realm")); using (var source = Storage.GetStream("collection.db")) { if (source != null) { using (var destination = Storage.GetStream(Path.Combine(backup_folder, $"collection.{migration}.db"), FileAccess.Write, FileMode.CreateNew)) source.CopyTo(destination); } } } dependencies.CacheAs(Storage); var largeStore = new LargeTextureStore(Host.CreateTextureLoaderStore(new NamespacedResourceStore <byte[]>(Resources, @"Textures"))); largeStore.AddStore(Host.CreateTextureLoaderStore(new OnlineStore())); dependencies.Cache(largeStore); dependencies.CacheAs(this); dependencies.CacheAs(LocalConfig); InitialiseFonts(); Audio.Samples.PlaybackConcurrency = SAMPLE_CONCURRENCY; dependencies.Cache(SkinManager = new SkinManager(Storage, realm, Host, Resources, Audio, Scheduler)); dependencies.CacheAs <ISkinSource>(SkinManager); EndpointConfiguration endpoints = UseDevelopmentServer ? (EndpointConfiguration) new DevelopmentEndpointConfiguration() : new ProductionEndpointConfiguration(); MessageFormatter.WebsiteRootUrl = endpoints.WebsiteRootUrl; dependencies.CacheAs(API ??= new APIAccess(LocalConfig, endpoints, VersionHash)); dependencies.CacheAs(spectatorClient = new OnlineSpectatorClient(endpoints)); dependencies.CacheAs(multiplayerClient = new OnlineMultiplayerClient(endpoints)); var defaultBeatmap = new DummyWorkingBeatmap(Audio, Textures); // ordering is important here to ensure foreign keys rules are not broken in ModelStore.Cleanup() dependencies.Cache(ScoreManager = new ScoreManager(RulesetStore, () => BeatmapManager, Storage, realm, Scheduler, () => difficultyCache, LocalConfig)); dependencies.Cache(BeatmapManager = new BeatmapManager(Storage, realm, RulesetStore, API, Audio, Resources, Host, defaultBeatmap, performOnlineLookups: true)); dependencies.Cache(BeatmapDownloader = new BeatmapModelDownloader(BeatmapManager, API)); dependencies.Cache(ScoreDownloader = new ScoreModelDownloader(ScoreManager, API)); dependencies.Cache(difficultyCache = new BeatmapDifficultyCache()); AddInternal(difficultyCache); dependencies.Cache(userCache = new UserLookupCache()); AddInternal(userCache); dependencies.Cache(beatmapCache = new BeatmapLookupCache()); AddInternal(beatmapCache); var scorePerformanceManager = new ScorePerformanceCache(); dependencies.Cache(scorePerformanceManager); AddInternal(scorePerformanceManager); dependencies.CacheAs <IRulesetConfigCache>(rulesetConfigCache = new RulesetConfigCache(realm, RulesetStore)); var powerStatus = CreateBatteryInfo(); if (powerStatus != null) { dependencies.CacheAs(powerStatus); } dependencies.Cache(SessionStatics = new SessionStatics()); dependencies.Cache(new OsuColour()); RegisterImportHandler(BeatmapManager); RegisterImportHandler(ScoreManager); RegisterImportHandler(SkinManager); // drop track volume game-wide to leave some head-room for UI effects / samples. // this means that for the time being, gameplay sample playback is louder relative to the audio track, compared to stable. // we may want to revisit this if users notice or complain about the difference (consider this a bit of a trial). Audio.Tracks.AddAdjustment(AdjustableProperty.Volume, globalTrackVolumeAdjust); Beatmap = new NonNullableBindable <WorkingBeatmap>(defaultBeatmap); dependencies.CacheAs <IBindable <WorkingBeatmap> >(Beatmap); dependencies.CacheAs(Beatmap); // add api components to hierarchy. if (API is APIAccess apiAccess) { AddInternal(apiAccess); } AddInternal(spectatorClient); AddInternal(multiplayerClient); AddInternal(rulesetConfigCache); GlobalActionContainer globalBindings; base.Content.Add(new SafeAreaContainer { SafeAreaOverrideEdges = SafeAreaOverrideEdges, RelativeSizeAxes = Axes.Both, Child = CreateScalingContainer().WithChildren(new Drawable[] { (MenuCursorContainer = new MenuCursorContainer { RelativeSizeAxes = Axes.Both }).WithChild(content = new OsuTooltipContainer(MenuCursorContainer.Cursor) { RelativeSizeAxes = Axes.Both }), // to avoid positional input being blocked by children, ensure the GlobalActionContainer is above everything. globalBindings = new GlobalActionContainer(this) }) }); KeyBindingStore = new RealmKeyBindingStore(realm, keyCombinationProvider); KeyBindingStore.Register(globalBindings, RulesetStore.AvailableRulesets); dependencies.Cache(globalBindings); PreviewTrackManager previewTrackManager; dependencies.Cache(previewTrackManager = new PreviewTrackManager(BeatmapManager.BeatmapTrackStore)); Add(previewTrackManager); AddInternal(MusicController = new MusicController()); dependencies.CacheAs(MusicController); Ruleset.BindValueChanged(onRulesetChanged); Beatmap.BindValueChanged(onBeatmapChanged); }