private void load() { Children = new Drawable[] { new CircularContainer { RelativeSizeAxes = Axes.Both, Scale = new Vector2(0.84f), Anchor = Anchor.Centre, Origin = Anchor.Centre, Masking = true, EdgeEffect = new EdgeEffectParameters { Colour = Color4.Black.Opacity(0.08f), Type = EdgeEffectType.Shadow, Radius = 5, }, Child = new Box { RelativeSizeAxes = Axes.Both, Colour = AccentColour, }, }, new ConstrainedIconContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, // the null coalesce here is only present to make unit tests work (ruleset dlls aren't copied correctly for testing at the moment) Icon = ruleset?.CreateInstance().CreateIcon() ?? new SpriteIcon { Icon = FontAwesome.fa_question_circle_o } } }; }
/// <summary> /// Create all required <see cref="BeatmapInfo"/>s for the provided archive. /// </summary> private List <BeatmapInfo> createBeatmapDifficulties(ArchiveReader reader) { var beatmapInfos = new List <BeatmapInfo>(); foreach (var name in reader.Filenames.Where(f => f.EndsWith(".osu"))) { using (var raw = reader.GetStream(name)) using (var ms = new MemoryStream()) //we need a memory stream so we can seek and shit using (var sr = new StreamReader(ms)) { raw.CopyTo(ms); ms.Position = 0; var decoder = Decoder.GetDecoder(sr); Beatmap beatmap = decoder.DecodeBeatmap(sr); beatmap.BeatmapInfo.Path = name; beatmap.BeatmapInfo.Hash = ms.ComputeSHA2Hash(); beatmap.BeatmapInfo.MD5Hash = ms.ComputeMD5Hash(); RulesetInfo ruleset = rulesets.GetRuleset(beatmap.BeatmapInfo.RulesetID); // TODO: this should be done in a better place once we actually need to dynamically update it. beatmap.BeatmapInfo.Ruleset = ruleset; beatmap.BeatmapInfo.StarDifficulty = ruleset?.CreateInstance()?.CreateDifficultyCalculator(beatmap).Calculate() ?? 0; beatmapInfos.Add(beatmap.BeatmapInfo); } } return(beatmapInfos); }
/// <summary> /// Import a beamap into our local <see cref="FileStore"/> storage. /// If the beatmap is already imported, the existing instance will be returned. /// </summary> /// <param name="files">The store to import beatmap files to.</param> /// <param name="beatmaps">The store to import beatmaps to.</param> /// <param name="reader">The beatmap archive to be read.</param> /// <returns>The imported beatmap, or an existing instance if it is already present.</returns> private BeatmapSetInfo importToStorage(FileStore files, BeatmapStore beatmaps, ArchiveReader reader) { // let's make sure there are actually .osu files to import. string mapName = reader.Filenames.FirstOrDefault(f => f.EndsWith(".osu")); if (string.IsNullOrEmpty(mapName)) { throw new InvalidOperationException("No beatmap files found in the map folder."); } // for now, concatenate all .osu files in the set to create a unique hash. MemoryStream hashable = new MemoryStream(); foreach (string file in reader.Filenames.Where(f => f.EndsWith(".osu"))) { using (Stream s = reader.GetStream(file)) s.CopyTo(hashable); } var hash = hashable.ComputeSHA2Hash(); // check if this beatmap has already been imported and exit early if so. var beatmapSet = beatmaps.BeatmapSets.FirstOrDefault(b => b.Hash == hash); if (beatmapSet != null) { undelete(beatmaps, files, beatmapSet); // ensure all files are present and accessible foreach (var f in beatmapSet.Files) { if (!storage.Exists(f.FileInfo.StoragePath)) { using (Stream s = reader.GetStream(f.Filename)) files.Add(s, false); } } // todo: delete any files which shouldn't exist any more. return(beatmapSet); } List <BeatmapSetFileInfo> fileInfos = new List <BeatmapSetFileInfo>(); // import files to manager foreach (string file in reader.Filenames) { using (Stream s = reader.GetStream(file)) fileInfos.Add(new BeatmapSetFileInfo { Filename = file, FileInfo = files.Add(s) }); } BeatmapMetadata metadata; using (var stream = new StreamReader(reader.GetStream(mapName))) metadata = Decoder.GetDecoder(stream).DecodeBeatmap(stream).Metadata; // check if a set already exists with the same online id. if (metadata.OnlineBeatmapSetID != null) { beatmapSet = beatmaps.BeatmapSets.FirstOrDefault(b => b.OnlineBeatmapSetID == metadata.OnlineBeatmapSetID); } if (beatmapSet == null) { beatmapSet = new BeatmapSetInfo { OnlineBeatmapSetID = metadata.OnlineBeatmapSetID, Beatmaps = new List <BeatmapInfo>(), Hash = hash, Files = fileInfos, Metadata = metadata } } ; var mapNames = reader.Filenames.Where(f => f.EndsWith(".osu")); foreach (var name in mapNames) { using (var raw = reader.GetStream(name)) using (var ms = new MemoryStream()) //we need a memory stream so we can seek and shit using (var sr = new StreamReader(ms)) { raw.CopyTo(ms); ms.Position = 0; var decoder = Decoder.GetDecoder(sr); Beatmap beatmap = decoder.DecodeBeatmap(sr); beatmap.BeatmapInfo.Path = name; beatmap.BeatmapInfo.Hash = ms.ComputeSHA2Hash(); beatmap.BeatmapInfo.MD5Hash = ms.ComputeMD5Hash(); var existing = beatmaps.Beatmaps.FirstOrDefault(b => b.Hash == beatmap.BeatmapInfo.Hash || beatmap.BeatmapInfo.OnlineBeatmapID != null && b.OnlineBeatmapID == beatmap.BeatmapInfo.OnlineBeatmapID); if (existing == null) { // Exclude beatmap-metadata if it's equal to beatmapset-metadata if (metadata.Equals(beatmap.Metadata)) { beatmap.BeatmapInfo.Metadata = null; } RulesetInfo ruleset = rulesets.GetRuleset(beatmap.BeatmapInfo.RulesetID); // TODO: this should be done in a better place once we actually need to dynamically update it. beatmap.BeatmapInfo.Ruleset = ruleset; beatmap.BeatmapInfo.StarDifficulty = ruleset?.CreateInstance()?.CreateDifficultyCalculator(beatmap).Calculate() ?? 0; beatmapSet.Beatmaps.Add(beatmap.BeatmapInfo); } } } return(beatmapSet); }
private void load(AudioManager audio, OsuConfigManager config, OsuGame osu) { dimLevel = config.GetBindable <double>(OsuSetting.DimLevel); mouseWheelDisabled = config.GetBindable <bool>(OsuSetting.MouseDisableWheel); sampleRestart = audio.Sample.Get(@"Gameplay/restart"); Ruleset rulesetInstance; try { if (Beatmap.Value.Beatmap == null) { throw new InvalidOperationException("Beatmap was not loaded"); } ruleset = osu?.Ruleset.Value ?? Beatmap.Value.BeatmapInfo.Ruleset; rulesetInstance = ruleset.CreateInstance(); try { HitRenderer = rulesetInstance.CreateHitRendererWith(Beatmap, ruleset.ID == Beatmap.Value.BeatmapInfo.Ruleset.ID); } catch (BeatmapInvalidForRulesetException) { // we may fail to create a HitRenderer if the beatmap cannot be loaded with the user's preferred ruleset // let's try again forcing the beatmap's ruleset. ruleset = Beatmap.Value.BeatmapInfo.Ruleset; rulesetInstance = ruleset.CreateInstance(); HitRenderer = rulesetInstance.CreateHitRendererWith(Beatmap, true); } if (!HitRenderer.Objects.Any()) { throw new InvalidOperationException("Beatmap contains no hit objects!"); } } catch (Exception e) { Logger.Log($"Could not load this beatmap sucessfully ({e})!", LoggingTarget.Runtime, LogLevel.Error); //couldn't load, hard abort! Exit(); return; } Track track = Beatmap.Value.Track; if (track != null) { adjustableSourceClock = track; } adjustableSourceClock = (IAdjustableClock)track ?? new StopwatchClock(); decoupledClock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; var firstObjectTime = HitRenderer.Objects.First().StartTime; decoupledClock.Seek(Math.Min(0, firstObjectTime - Math.Max(Beatmap.Value.Beatmap.ControlPointInfo.TimingPointAt(firstObjectTime).BeatLength * 4, Beatmap.Value.BeatmapInfo.AudioLeadIn))); decoupledClock.ProcessFrame(); offsetClock = new FramedOffsetClock(decoupledClock); userAudioOffset = config.GetBindable <double>(OsuSetting.AudioOffset); userAudioOffset.ValueChanged += v => offsetClock.Offset = v; userAudioOffset.TriggerChange(); Schedule(() => { adjustableSourceClock.Reset(); foreach (var mod in Beatmap.Value.Mods.Value.OfType <IApplicableToClock>()) { mod.ApplyToClock(adjustableSourceClock); } decoupledClock.ChangeSource(adjustableSourceClock); }); Children = new Drawable[] { pauseContainer = new PauseContainer { AudioClock = decoupledClock, FramedClock = offsetClock, OnRetry = Restart, OnQuit = Exit, CheckCanPause = () => ValidForResume && !HasFailed && !HitRenderer.HasReplayLoaded, Retries = RestartCount, OnPause = () => { hudOverlay.KeyCounter.IsCounting = pauseContainer.IsPaused; }, OnResume = () => { hudOverlay.KeyCounter.IsCounting = true; }, Children = new Drawable[] { new SkipButton(firstObjectTime) { AudioClock = decoupledClock }, new Container { RelativeSizeAxes = Axes.Both, Clock = offsetClock, Children = new Drawable[] { HitRenderer, } }, hudOverlay = new HUDOverlay { Anchor = Anchor.Centre, Origin = Anchor.Centre }, } }, failOverlay = new FailOverlay { OnRetry = Restart, OnQuit = Exit, }, new HotkeyRetryOverlay { Action = () => { //we want to hide the hitrenderer immediately (looks better). //we may be able to remove this once the mouse cursor trail is improved. HitRenderer?.Hide(); Restart(); }, } }; scoreProcessor = HitRenderer.CreateScoreProcessor(); hudOverlay.KeyCounter.AddRange(rulesetInstance.CreateGameplayKeys()); hudOverlay.BindProcessor(scoreProcessor); hudOverlay.BindHitRenderer(HitRenderer); hudOverlay.Progress.Objects = HitRenderer.Objects; hudOverlay.Progress.AudioClock = decoupledClock; hudOverlay.Progress.AllowSeeking = HitRenderer.HasReplayLoaded; hudOverlay.Progress.OnSeek = pos => decoupledClock.Seek(pos); hudOverlay.ModDisplay.Current.BindTo(Beatmap.Value.Mods); //bind HitRenderer to ScoreProcessor and ourselves (for a pass situation) HitRenderer.OnAllJudged += onCompletion; //bind ScoreProcessor to ourselves (for a fail situation) scoreProcessor.Failed += onFail; }
public IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList <Mod> mods = null, TimeSpan?timeout = null) { using (var cancellationSource = createCancellationTokenSource(timeout)) { mods ??= Array.Empty <Mod>(); var rulesetInstance = ruleset.CreateInstance(); IBeatmapConverter converter = CreateBeatmapConverter(Beatmap, rulesetInstance); // Check if the beatmap can be converted if (Beatmap.HitObjects.Count > 0 && !converter.CanConvert()) { throw new BeatmapInvalidForRulesetException($"{nameof(Beatmaps.Beatmap)} can not be converted for the ruleset (ruleset: {ruleset.InstantiationInfo}, converter: {converter})."); } // Apply conversion mods foreach (var mod in mods.OfType <IApplicableToBeatmapConverter>()) { if (cancellationSource.IsCancellationRequested) { throw new BeatmapLoadTimeoutException(BeatmapInfo); } mod.ApplyToBeatmapConverter(converter); } // Convert IBeatmap converted = converter.Convert(cancellationSource.Token); // Apply conversion mods to the result foreach (var mod in mods.OfType <IApplicableAfterBeatmapConversion>()) { if (cancellationSource.IsCancellationRequested) { throw new BeatmapLoadTimeoutException(BeatmapInfo); } mod.ApplyToBeatmap(converted); } // Apply difficulty mods if (mods.Any(m => m is IApplicableToDifficulty)) { converted.BeatmapInfo = converted.BeatmapInfo.Clone(); converted.BeatmapInfo.BaseDifficulty = converted.BeatmapInfo.BaseDifficulty.Clone(); foreach (var mod in mods.OfType <IApplicableToDifficulty>()) { if (cancellationSource.IsCancellationRequested) { throw new BeatmapLoadTimeoutException(BeatmapInfo); } mod.ApplyToDifficulty(converted.BeatmapInfo.BaseDifficulty); } } IBeatmapProcessor processor = rulesetInstance.CreateBeatmapProcessor(converted); processor?.PreProcess(); // Compute default values for hitobjects, including creating nested hitobjects in-case they're needed try { foreach (var obj in converted.HitObjects) { if (cancellationSource.IsCancellationRequested) { throw new BeatmapLoadTimeoutException(BeatmapInfo); } obj.ApplyDefaults(converted.ControlPointInfo, converted.BeatmapInfo.BaseDifficulty, cancellationSource.Token); } } catch (OperationCanceledException) { throw new BeatmapLoadTimeoutException(BeatmapInfo); } foreach (var mod in mods.OfType <IApplicableToHitObject>()) { foreach (var obj in converted.HitObjects) { if (cancellationSource.IsCancellationRequested) { throw new BeatmapLoadTimeoutException(BeatmapInfo); } mod.ApplyToHitObject(obj); } } processor?.PostProcess(); foreach (var mod in mods.OfType <IApplicableToBeatmap>()) { cancellationSource.Token.ThrowIfCancellationRequested(); mod.ApplyToBeatmap(converted); } return(converted); } }
private void load(AudioManager audio, APIAccess api, OsuConfigManager config) { this.api = api; WorkingBeatmap working = Beatmap.Value; if (working is DummyWorkingBeatmap) { return; } sampleRestart = audio.Sample.Get(@"Gameplay/restart"); mouseWheelDisabled = config.GetBindable <bool>(OsuSetting.MouseDisableWheel); userAudioOffset = config.GetBindable <double>(OsuSetting.AudioOffset); IBeatmap beatmap; try { beatmap = working.Beatmap; if (beatmap == null) { throw new InvalidOperationException("Beatmap was not loaded"); } ruleset = Ruleset.Value ?? beatmap.BeatmapInfo.Ruleset; var rulesetInstance = ruleset.CreateInstance(); try { RulesetContainer = rulesetInstance.CreateRulesetContainerWith(working); } catch (BeatmapInvalidForRulesetException) { // we may fail to create a RulesetContainer if the beatmap cannot be loaded with the user's preferred ruleset // let's try again forcing the beatmap's ruleset. ruleset = beatmap.BeatmapInfo.Ruleset; rulesetInstance = ruleset.CreateInstance(); RulesetContainer = rulesetInstance.CreateRulesetContainerWith(Beatmap.Value); } if (!RulesetContainer.Objects.Any()) { Logger.Log("Beatmap contains no hit objects!", level: LogLevel.Error); return; } } catch (Exception e) { Logger.Error(e, "Could not load beatmap sucessfully!"); //couldn't load, hard abort! return; } sourceClock = (IAdjustableClock)working.Track ?? new StopwatchClock(); adjustableClock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; adjustableClock.Seek(AllowLeadIn ? Math.Min(0, RulesetContainer.GameplayStartTime - beatmap.BeatmapInfo.AudioLeadIn) : RulesetContainer.GameplayStartTime); adjustableClock.ProcessFrame(); // Lazer's audio timings in general doesn't match stable. This is the result of user testing, albeit limited. // This only seems to be required on windows. We need to eventually figure out why, with a bit of luck. var platformOffsetClock = new FramedOffsetClock(adjustableClock) { Offset = RuntimeInfo.OS == RuntimeInfo.Platform.Windows ? 22 : 0 }; // the final usable gameplay clock with user-set offsets applied. var offsetClock = new FramedOffsetClock(platformOffsetClock); userAudioOffset.ValueChanged += v => offsetClock.Offset = v; userAudioOffset.TriggerChange(); ScoreProcessor = RulesetContainer.CreateScoreProcessor(); if (!ScoreProcessor.Mode.Disabled) { config.BindWith(OsuSetting.ScoreDisplayMode, ScoreProcessor.Mode); } Children = new Drawable[] { pauseContainer = new PauseContainer(offsetClock, adjustableClock) { Retries = RestartCount, OnRetry = Restart, OnQuit = Exit, CheckCanPause = () => AllowPause && ValidForResume && !HasFailed && !RulesetContainer.HasReplayLoaded, Children = new[] { storyboardContainer = new Container { RelativeSizeAxes = Axes.Both, Alpha = 0, }, new LocalSkinOverrideContainer(working.Skin) { RelativeSizeAxes = Axes.Both, Child = RulesetContainer }, new BreakOverlay(beatmap.BeatmapInfo.LetterboxInBreaks, ScoreProcessor) { Anchor = Anchor.Centre, Origin = Anchor.Centre, ProcessCustomClock = false, Breaks = beatmap.Breaks }, RulesetContainer.Cursor?.CreateProxy() ?? new Container(), hudOverlay = new HUDOverlay(ScoreProcessor, RulesetContainer, working, offsetClock, adjustableClock) { Clock = Clock, // hud overlay doesn't want to use the audio clock directly ProcessCustomClock = false, Anchor = Anchor.Centre, Origin = Anchor.Centre }, new SkipOverlay(RulesetContainer.GameplayStartTime) { Clock = Clock, // skip button doesn't want to use the audio clock directly ProcessCustomClock = false, AdjustableClock = adjustableClock, FramedClock = offsetClock, }, } }, failOverlay = new FailOverlay { OnRetry = Restart, OnQuit = Exit, }, new HotkeyRetryOverlay { Action = () => { if (!IsCurrentScreen) { return; } fadeOut(true); Restart(); }, } }; hudOverlay.HoldToQuit.Action = Exit; hudOverlay.KeyCounter.Visible.BindTo(RulesetContainer.HasReplayLoaded); RulesetContainer.IsPaused.BindTo(pauseContainer.IsPaused); if (ShowStoryboard) { initializeStoryboard(false); } // Bind ScoreProcessor to ourselves ScoreProcessor.AllJudged += onCompletion; ScoreProcessor.Failed += onFail; foreach (var mod in Beatmap.Value.Mods.Value.OfType <IApplicableToScoreProcessor>()) { mod.ApplyToScoreProcessor(ScoreProcessor); } }
private void load(AudioManager audio, APIAccess api, OsuConfigManager config) { this.api = api; sampleRestart = audio.Sample.Get(@"Gameplay/restart"); mouseWheelDisabled = config.GetBindable <bool>(OsuSetting.MouseDisableWheel); userAudioOffset = config.GetBindable <double>(OsuSetting.AudioOffset); WorkingBeatmap working = Beatmap.Value; Beatmap beatmap; try { beatmap = working.Beatmap; if (beatmap == null) { throw new InvalidOperationException("Beatmap was not loaded"); } ruleset = Ruleset.Value ?? beatmap.BeatmapInfo.Ruleset; var rulesetInstance = ruleset.CreateInstance(); try { RulesetContainer = rulesetInstance.CreateRulesetContainerWith(working, ruleset.ID == beatmap.BeatmapInfo.Ruleset.ID); } catch (BeatmapInvalidForRulesetException) { // we may fail to create a RulesetContainer if the beatmap cannot be loaded with the user's preferred ruleset // let's try again forcing the beatmap's ruleset. ruleset = beatmap.BeatmapInfo.Ruleset; rulesetInstance = ruleset.CreateInstance(); RulesetContainer = rulesetInstance.CreateRulesetContainerWith(Beatmap, true); } if (!RulesetContainer.Objects.Any()) { throw new InvalidOperationException("Beatmap contains no hit objects!"); } } catch (Exception e) { Logger.Error(e, "Could not load beatmap sucessfully!"); //couldn't load, hard abort! Exit(); return; } sourceClock = (IAdjustableClock)working.Track ?? new StopwatchClock(); adjustableClock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; var firstObjectTime = RulesetContainer.Objects.First().StartTime; adjustableClock.Seek(AllowLeadIn ? Math.Min(0, firstObjectTime - Math.Max(beatmap.ControlPointInfo.TimingPointAt(firstObjectTime).BeatLength * 4, beatmap.BeatmapInfo.AudioLeadIn)) : firstObjectTime); adjustableClock.ProcessFrame(); // the final usable gameplay clock with user-set offsets applied. var offsetClock = new FramedOffsetClock(adjustableClock); userAudioOffset.ValueChanged += v => offsetClock.Offset = v; userAudioOffset.TriggerChange(); scoreProcessor = RulesetContainer.CreateScoreProcessor(); Children = new Drawable[] { pauseContainer = new PauseContainer(offsetClock, adjustableClock) { OnRetry = Restart, OnQuit = Exit, CheckCanPause = () => AllowPause && ValidForResume && !HasFailed && !RulesetContainer.HasReplayLoaded, OnPause = () => { pauseContainer.Retries = RestartCount; hudOverlay.KeyCounter.IsCounting = pauseContainer.IsPaused; }, OnResume = () => hudOverlay.KeyCounter.IsCounting = true, Children = new Drawable[] { storyboardContainer = new Container { RelativeSizeAxes = Axes.Both, Alpha = 0, }, RulesetContainer, new SkipButton(firstObjectTime) { Clock = Clock, // skip button doesn't want to use the audio clock directly ProcessCustomClock = false, AdjustableClock = adjustableClock, FramedClock = offsetClock, }, hudOverlay = new HUDOverlay(scoreProcessor, RulesetContainer, working, offsetClock, adjustableClock) { Clock = Clock, // hud overlay doesn't want to use the audio clock directly ProcessCustomClock = false, Anchor = Anchor.Centre, Origin = Anchor.Centre }, new BreakOverlay(beatmap.BeatmapInfo.LetterboxInBreaks, scoreProcessor) { Anchor = Anchor.Centre, Origin = Anchor.Centre, ProcessCustomClock = false, Breaks = beatmap.Breaks } } }, failOverlay = new FailOverlay { OnRetry = Restart, OnQuit = Exit, }, new HotkeyRetryOverlay { Action = () => { if (!IsCurrentScreen) { return; } //we want to hide the hitrenderer immediately (looks better). //we may be able to remove this once the mouse cursor trail is improved. RulesetContainer?.Hide(); Restart(); }, } }; if (ShowStoryboard) { initializeStoryboard(false); } // Bind ScoreProcessor to ourselves scoreProcessor.AllJudged += onCompletion; scoreProcessor.Failed += onFail; foreach (var mod in Beatmap.Value.Mods.Value.OfType <IApplicableToScoreProcessor>()) { mod.ApplyToScoreProcessor(scoreProcessor); } }
private Player loadPlayerFor(RulesetInfo ri) => loadPlayerFor(ri.CreateInstance());
private Player loadPlayerFor(RulesetInfo ri) { Ruleset.Value = ri; return(loadPlayerFor(ri.CreateInstance())); }
/// <summary> /// Create all required <see cref="BeatmapInfo"/>s for the provided archive. /// </summary> private List <BeatmapInfo> createBeatmapDifficulties(ArchiveReader reader) { var beatmapInfos = new List <BeatmapInfo>(); bool invalidateOnlineIDs = false; foreach (var name in reader.Filenames.Where(f => f.EndsWith(".osu"))) { using (var raw = reader.GetStream(name)) using (var ms = new MemoryStream()) //we need a memory stream so we can seek and shit using (var sr = new StreamReader(ms)) { raw.CopyTo(ms); ms.Position = 0; var decoder = Decoder.GetDecoder <Beatmap>(sr); IBeatmap beatmap = decoder.Decode(sr); beatmap.BeatmapInfo.Path = name; beatmap.BeatmapInfo.Hash = ms.ComputeSHA2Hash(); beatmap.BeatmapInfo.MD5Hash = ms.ComputeMD5Hash(); if (beatmap.BeatmapInfo.OnlineBeatmapID.HasValue) { var ourId = beatmap.BeatmapInfo.OnlineBeatmapID; // check that no existing beatmap in database exists that is imported with the same online beatmap ID. if so, give it precedence. if (QueryBeatmap(b => b.OnlineBeatmapID.Value == ourId) != null) { beatmap.BeatmapInfo.OnlineBeatmapID = null; } // check that no other beatmap in this imported set has a conflicting online beatmap ID. If so, presume *all* are incorrect. if (beatmapInfos.Any(b => b.OnlineBeatmapID == ourId)) { invalidateOnlineIDs = true; } } RulesetInfo ruleset = rulesets.GetRuleset(beatmap.BeatmapInfo.RulesetID); beatmap.BeatmapInfo.Ruleset = ruleset; if (ruleset != null) { // TODO: this should be done in a better place once we actually need to dynamically update it. beatmap.BeatmapInfo.StarDifficulty = ruleset.CreateInstance().CreateDifficultyCalculator(new DummyConversionBeatmap(beatmap)).Calculate().StarRating; } else { beatmap.BeatmapInfo.StarDifficulty = 0; } beatmapInfos.Add(beatmap.BeatmapInfo); } } if (invalidateOnlineIDs) { beatmapInfos.ForEach(b => b.OnlineBeatmapID = null); } return(beatmapInfos); }
private void load(AudioManager audio, OsuConfigManager config, APIAccess api) { this.api = api; dimLevel = config.GetBindable <double>(OsuSetting.DimLevel); showStoryboard = config.GetBindable <bool>(OsuSetting.ShowStoryboard); mouseWheelDisabled = config.GetBindable <bool>(OsuSetting.MouseDisableWheel); sampleRestart = audio.Sample.Get(@"Gameplay/restart"); WorkingBeatmap working = Beatmap.Value; Beatmap beatmap; try { beatmap = working.Beatmap; if (beatmap == null) { throw new InvalidOperationException("Beatmap was not loaded"); } ruleset = Ruleset.Value ?? beatmap.BeatmapInfo.Ruleset; var rulesetInstance = ruleset.CreateInstance(); try { RulesetContainer = rulesetInstance.CreateRulesetContainerWith(working, ruleset.ID == beatmap.BeatmapInfo.Ruleset.ID); } catch (BeatmapInvalidForRulesetException) { // we may fail to create a RulesetContainer if the beatmap cannot be loaded with the user's preferred ruleset // let's try again forcing the beatmap's ruleset. ruleset = beatmap.BeatmapInfo.Ruleset; rulesetInstance = ruleset.CreateInstance(); RulesetContainer = rulesetInstance.CreateRulesetContainerWith(Beatmap, true); } if (!RulesetContainer.Objects.Any()) { throw new InvalidOperationException("Beatmap contains no hit objects!"); } } catch (Exception e) { Logger.Log($"Could not load this beatmap sucessfully ({e})!", LoggingTarget.Runtime, LogLevel.Error); //couldn't load, hard abort! Exit(); return; } adjustableSourceClock = (IAdjustableClock)working.Track ?? new StopwatchClock(); decoupledClock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; var firstObjectTime = RulesetContainer.Objects.First().StartTime; decoupledClock.Seek(Math.Min(0, firstObjectTime - Math.Max(beatmap.ControlPointInfo.TimingPointAt(firstObjectTime).BeatLength * 4, beatmap.BeatmapInfo.AudioLeadIn))); decoupledClock.ProcessFrame(); offsetClock = new FramedOffsetClock(decoupledClock); userAudioOffset = config.GetBindable <double>(OsuSetting.AudioOffset); userAudioOffset.ValueChanged += v => offsetClock.Offset = v; userAudioOffset.TriggerChange(); Children = new Drawable[] { storyboardContainer = new Container { RelativeSizeAxes = Axes.Both, Clock = offsetClock, Alpha = 0, }, pauseContainer = new PauseContainer { AudioClock = decoupledClock, FramedClock = offsetClock, OnRetry = Restart, OnQuit = Exit, CheckCanPause = () => ValidForResume && !HasFailed && !RulesetContainer.HasReplayLoaded, Retries = RestartCount, OnPause = () => { hudOverlay.KeyCounter.IsCounting = pauseContainer.IsPaused; }, OnResume = () => { hudOverlay.KeyCounter.IsCounting = true; }, Children = new Drawable[] { new SkipButton(firstObjectTime) { AudioClock = decoupledClock }, new Container { RelativeSizeAxes = Axes.Both, Clock = offsetClock, Child = RulesetContainer, }, hudOverlay = new HUDOverlay { Anchor = Anchor.Centre, Origin = Anchor.Centre }, breakOverlay = new BreakOverlay(beatmap.BeatmapInfo.LetterboxInBreaks) { Anchor = Anchor.Centre, Origin = Anchor.Centre, Clock = decoupledClock, Breaks = beatmap.Breaks }, } }, failOverlay = new FailOverlay { OnRetry = Restart, OnQuit = Exit, }, new HotkeyRetryOverlay { Action = () => { //we want to hide the hitrenderer immediately (looks better). //we may be able to remove this once the mouse cursor trail is improved. RulesetContainer?.Hide(); Restart(); }, } }; scoreProcessor = RulesetContainer.CreateScoreProcessor(); if (showStoryboard) { initializeStoryboard(false); } hudOverlay.BindProcessor(scoreProcessor); hudOverlay.BindRulesetContainer(RulesetContainer); hudOverlay.Progress.Objects = RulesetContainer.Objects; hudOverlay.Progress.AudioClock = decoupledClock; hudOverlay.Progress.AllowSeeking = RulesetContainer.HasReplayLoaded; hudOverlay.Progress.OnSeek = pos => decoupledClock.Seek(pos); hudOverlay.ModDisplay.Current.BindTo(working.Mods); breakOverlay.BindProcessor(scoreProcessor); hudOverlay.ReplaySettingsOverlay.PlaybackSettings.AdjustableClock = adjustableSourceClock; // Bind ScoreProcessor to ourselves scoreProcessor.AllJudged += onCompletion; scoreProcessor.Failed += onFail; }
public IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList <Mod> mods = null) { mods ??= Array.Empty <Mod>(); var rulesetInstance = ruleset.CreateInstance(); IBeatmapConverter converter = CreateBeatmapConverter(Beatmap, rulesetInstance); // Check if the beatmap can be converted if (!converter.CanConvert) { throw new BeatmapInvalidForRulesetException($"{nameof(Beatmaps.Beatmap)} can not be converted for the ruleset (ruleset: {ruleset.InstantiationInfo}, converter: {converter})."); } // Apply conversion mods foreach (var mod in mods.OfType <IApplicableToBeatmapConverter>()) { mod.ApplyToBeatmapConverter(converter); } // Convert IBeatmap converted = converter.Convert(); // Apply difficulty mods if (mods.Any(m => m is IApplicableToDifficulty)) { converted.BeatmapInfo = converted.BeatmapInfo.Clone(); converted.BeatmapInfo.BaseDifficulty = converted.BeatmapInfo.BaseDifficulty.Clone(); foreach (var mod in mods.OfType <IApplicableToDifficulty>()) { mod.ApplyToDifficulty(converted.BeatmapInfo.BaseDifficulty); } } IBeatmapProcessor processor = rulesetInstance.CreateBeatmapProcessor(converted); processor?.PreProcess(); // Compute default values for hitobjects, including creating nested hitobjects in-case they're needed foreach (var obj in converted.HitObjects) { obj.ApplyDefaults(converted.ControlPointInfo, converted.BeatmapInfo.BaseDifficulty); } foreach (var mod in mods.OfType <IApplicableToHitObject>()) { foreach (var obj in converted.HitObjects) { mod.ApplyToHitObject(obj); } } processor?.PostProcess(); foreach (var mod in mods.OfType <IApplicableToBeatmap>()) { mod.ApplyToBeatmap(converted); } return(converted); }
private void load(AudioManager audio, BeatmapDatabase beatmaps, OsuConfigManager config, OsuGame osu) { dimLevel = config.GetBindable <int>(OsuConfig.DimLevel); mouseWheelDisabled = config.GetBindable <bool>(OsuConfig.MouseDisableWheel); Ruleset rulesetInstance; try { if (Beatmap == null) { Beatmap = beatmaps.GetWorkingBeatmap(BeatmapInfo, withStoryboard: true); } if ((Beatmap?.Beatmap?.HitObjects.Count ?? 0) == 0) { throw new Exception("No valid objects were found!"); } if (Beatmap == null) { throw new Exception("Beatmap was not loaded"); } ruleset = osu?.Ruleset.Value ?? Beatmap.BeatmapInfo.Ruleset; rulesetInstance = ruleset.CreateInstance(); try { HitRenderer = rulesetInstance.CreateHitRendererWith(Beatmap); } catch (BeatmapInvalidForRulesetException) { // we may fail to create a HitRenderer if the beatmap cannot be loaded with the user's preferred ruleset // let's try again forcing the beatmap's ruleset. ruleset = Beatmap.BeatmapInfo.Ruleset; rulesetInstance = ruleset.CreateInstance(); HitRenderer = rulesetInstance.CreateHitRendererWith(Beatmap); } } catch (Exception e) { Logger.Log($"Could not load this beatmap sucessfully ({e})!", LoggingTarget.Runtime, LogLevel.Error); //couldn't load, hard abort! Exit(); return; } Track track = Beatmap.Track; if (track != null) { audio.Track.SetExclusive(track); sourceClock = track; } sourceClock = (IAdjustableClock)track ?? new StopwatchClock(); offsetClock = new OffsetClock(sourceClock); userAudioOffset = config.GetBindable <double>(OsuConfig.AudioOffset); userAudioOffset.ValueChanged += v => offsetClock.Offset = v; userAudioOffset.TriggerChange(); interpolatedSourceClock = new InterpolatingFramedClock(offsetClock); Schedule(() => { sourceClock.Reset(); foreach (var mod in Beatmap.Mods.Value.OfType <IApplicableToClock>()) { mod.ApplyToClock(sourceClock); } }); scoreProcessor = HitRenderer.CreateScoreProcessor(); hudOverlay = new StandardHudOverlay() { Anchor = Anchor.Centre, Origin = Anchor.Centre }; hudOverlay.KeyCounter.Add(rulesetInstance.CreateGameplayKeys()); hudOverlay.BindProcessor(scoreProcessor); hudOverlay.BindHitRenderer(HitRenderer); hudOverlay.Progress.Objects = HitRenderer.Objects; hudOverlay.Progress.AudioClock = interpolatedSourceClock; //bind HitRenderer to ScoreProcessor and ourselves (for a pass situation) HitRenderer.OnAllJudged += onCompletion; //bind ScoreProcessor to ourselves (for a fail situation) scoreProcessor.Failed += onFail; Children = new Drawable[] { new Container { RelativeSizeAxes = Axes.Both, Clock = interpolatedSourceClock, Children = new Drawable[] { HitRenderer, skipButton = new SkipButton { Alpha = 0 }, } }, hudOverlay, pauseOverlay = new PauseOverlay { OnResume = delegate { Delay(400); Schedule(Resume); }, OnRetry = Restart, OnQuit = Exit, }, failOverlay = new FailOverlay { OnRetry = Restart, OnQuit = Exit, }, new HotkeyRetryOverlay { Action = () => { //we want to hide the hitrenderer immediately (looks better). //we may be able to remove this once the mouse cursor trail is improved. HitRenderer?.Hide(); Restart(); }, } }; }
public RulesetBindingsSection(RulesetInfo ruleset) { Ruleset = ruleset; Defaults = ruleset.CreateInstance().GetDefaultKeyBindings(); }