Пример #1
0
 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
             }
         }
     };
 }
Пример #2
0
        /// <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);
        }
Пример #3
0
        /// <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);
        }
Пример #4
0
        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;
        }
Пример #5
0
        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);
            }
        }
Пример #6
0
        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);
            }
        }
Пример #7
0
        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);
            }
        }
Пример #8
0
 private Player loadPlayerFor(RulesetInfo ri) => loadPlayerFor(ri.CreateInstance());
Пример #9
0
 private Player loadPlayerFor(RulesetInfo ri)
 {
     Ruleset.Value = ri;
     return(loadPlayerFor(ri.CreateInstance()));
 }
Пример #10
0
        /// <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);
        }
Пример #11
0
        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;
        }
Пример #12
0
        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);
        }
Пример #13
0
        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();
                    },
                }
            };
        }
Пример #14
0
        public RulesetBindingsSection(RulesetInfo ruleset)
        {
            Ruleset = ruleset;

            Defaults = ruleset.CreateInstance().GetDefaultKeyBindings();
        }