// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; using System.Diagnostics; using osu.Framework.Bindables; using osu.Framework.Configuration; using osu.Framework.Configuration.Tracking; using osu.Framework.Extensions; using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Localisation; using osu.Framework.Platform; using osu.Game.Beatmaps.Drawables.Cards; using osu.Game.Input; using osu.Game.Input.Bindings; using osu.Game.Localisation; using osu.Game.Overlays; using osu.Game.Overlays.Mods.Input; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.OnlinePlay.Lounge.Components; using osu.Game.Screens.Select; using osu.Game.Screens.Select.Filter; using osu.Game.Skinning; using osu.Game.Users; namespace osu.Game.Configuration { public class OsuConfigManager : IniConfigManager, IGameplaySettings { public OsuConfigManager(Storage storage) : base(storage) { Migrate(); } protected override void InitialiseDefaults() { // UI/selection defaults SetDefault(OsuSetting.Ruleset, string.Empty); SetDefault(OsuSetting.Skin, SkinInfo.ARGON_SKIN.ToString()); SetDefault(OsuSetting.BeatmapDetailTab, PlayBeatmapDetailArea.TabType.Local); SetDefault(OsuSetting.BeatmapDetailModsFilter, false); SetDefault(OsuSetting.ShowConvertedBeatmaps, true); SetDefault(OsuSetting.DisplayStarsMinimum, 0.0, 0, 10, 0.1); SetDefault(OsuSetting.DisplayStarsMaximum, 10.1, 0, 10.1, 0.1); SetDefault(OsuSetting.SongSelectGroupingMode, GroupMode.All); SetDefault(OsuSetting.SongSelectSortingMode, SortMode.Title); SetDefault(OsuSetting.RandomSelectAlgorithm, RandomSelectAlgorithm.RandomPermutation); SetDefault(OsuSetting.ModSelectHotkeyStyle, ModSelectHotkeyStyle.Sequential); SetDefault(OsuSetting.ModSelectTextSearchStartsActive, true); SetDefault(OsuSetting.ChatDisplayHeight, ChatOverlay.DEFAULT_HEIGHT, 0.2f, 1f); SetDefault(OsuSetting.BeatmapListingCardSize, BeatmapCardSize.Normal); SetDefault(OsuSetting.ProfileCoverExpanded, true); SetDefault(OsuSetting.ToolbarClockDisplayMode, ToolbarClockDisplayMode.Full); SetDefault(OsuSetting.SongSelectBackgroundBlur, true); // Online settings SetDefault(OsuSetting.Username, string.Empty); SetDefault(OsuSetting.Token, string.Empty); #pragma warning disable CS0618 // Type or member is obsolete // this default set MUST remain despite the setting being deprecated, because `SetDefault()` calls are implicitly used to declare the type returned for the lookup. // if this is removed, the setting will be interpreted as a string, and `Migrate()` will fail due to cast failure. // can be removed 20240618 SetDefault(OsuSetting.AutomaticallyDownloadWhenSpectating, false); #pragma warning restore CS0618 // Type or member is obsolete SetDefault(OsuSetting.AutomaticallyDownloadMissingBeatmaps, false); SetDefault(OsuSetting.SavePassword, true).ValueChanged += enabled => { if (enabled.NewValue) SetValue(OsuSetting.SaveUsername, true); else GetBindable(OsuSetting.Token).SetDefault(); }; SetDefault(OsuSetting.SaveUsername, true).ValueChanged += enabled => { if (!enabled.NewValue) { GetBindable(OsuSetting.Username).SetDefault(); SetValue(OsuSetting.SavePassword, false); } }; SetDefault(OsuSetting.ExternalLinkWarning, true); SetDefault(OsuSetting.PreferNoVideo, false); SetDefault(OsuSetting.ShowOnlineExplicitContent, false); SetDefault(OsuSetting.NotifyOnUsernameMentioned, true); SetDefault(OsuSetting.NotifyOnPrivateMessage, true); // Audio SetDefault(OsuSetting.VolumeInactive, 0.25, 0, 1, 0.01); SetDefault(OsuSetting.MenuVoice, true); SetDefault(OsuSetting.MenuMusic, true); SetDefault(OsuSetting.MenuTips, true); SetDefault(OsuSetting.AudioOffset, 0, -500.0, 500.0, 1); // Input SetDefault(OsuSetting.MenuCursorSize, 1.0f, 0.5f, 2f, 0.01f); SetDefault(OsuSetting.GameplayCursorSize, 1.0f, 0.1f, 2f, 0.01f); SetDefault(OsuSetting.GameplayCursorDuringTouch, false); SetDefault(OsuSetting.AutoCursorSize, false); SetDefault(OsuSetting.MouseDisableButtons, false); SetDefault(OsuSetting.MouseDisableWheel, false); SetDefault(OsuSetting.ConfineMouseMode, OsuConfineMouseMode.DuringGameplay); SetDefault(OsuSetting.TouchDisableGameplayTaps, false); // Graphics SetDefault(OsuSetting.ShowFpsDisplay, false); SetDefault(OsuSetting.ShowStoryboard, true); SetDefault(OsuSetting.BeatmapSkins, true); SetDefault(OsuSetting.BeatmapColours, true); SetDefault(OsuSetting.BeatmapHitsounds, true); SetDefault(OsuSetting.CursorRotation, true); SetDefault(OsuSetting.MenuParallax, true); // See https://stackoverflow.com/a/63307411 for default sourcing. SetDefault(OsuSetting.Prefer24HourTime, !CultureInfoHelper.SystemCulture.DateTimeFormat.ShortTimePattern.Contains(@"tt")); // Gameplay SetDefault(OsuSetting.PositionalHitsoundsLevel, 0.2f, 0, 1); SetDefault(OsuSetting.DimLevel, 0.7, 0, 1, 0.01); SetDefault(OsuSetting.BlurLevel, 0, 0, 1, 0.01); SetDefault(OsuSetting.LightenDuringBreaks, true); SetDefault(OsuSetting.HitLighting, true); SetDefault(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Always); SetDefault(OsuSetting.ShowHealthDisplayWhenCantFail, true); SetDefault(OsuSetting.FadePlayfieldWhenHealthLow, true); SetDefault(OsuSetting.KeyOverlay, false); SetDefault(OsuSetting.ReplaySettingsOverlay, true); SetDefault(OsuSetting.ReplayPlaybackControlsExpanded, true); SetDefault(OsuSetting.GameplayLeaderboard, true); SetDefault(OsuSetting.AlwaysPlayFirstComboBreak, true); SetDefault(OsuSetting.FloatingComments, false); SetDefault(OsuSetting.ScoreDisplayMode, ScoringMode.Standardised); SetDefault(OsuSetting.IncreaseFirstObjectVisibility, true); SetDefault(OsuSetting.GameplayDisableWinKey, true); // Update SetDefault(OsuSetting.ReleaseStream, ReleaseStream.Lazer); SetDefault(OsuSetting.Version, string.Empty); SetDefault(OsuSetting.ShowFirstRunSetup, true); SetDefault(OsuSetting.ScreenshotFormat, ScreenshotFormat.Jpg); SetDefault(OsuSetting.ScreenshotCaptureMenuCursor, false); SetDefault(OsuSetting.SongSelectRightMouseScroll, false); SetDefault(OsuSetting.Scaling, ScalingMode.Off); SetDefault(OsuSetting.SafeAreaConsiderations, true); SetDefault(OsuSetting.ScalingBackgroundDim, 0.9f, 0.5f, 1f); SetDefault(OsuSetting.ScalingSizeX, 0.8f, 0.2f, 1f); SetDefault(OsuSetting.ScalingSizeY, 0.8f, 0.2f, 1f); SetDefault(OsuSetting.ScalingPositionX, 0.5f, 0f, 1f); SetDefault(OsuSetting.ScalingPositionY, 0.5f, 0f, 1f); SetDefault(OsuSetting.UIScale, 1f, 0.8f, 1.6f, 0.01f); SetDefault(OsuSetting.UIHoldActivationDelay, 200.0, 0.0, 500.0, 50.0); SetDefault(OsuSetting.IntroSequence, IntroSequence.Triangles); SetDefault(OsuSetting.MenuBackgroundSource, BackgroundSource.Skin); SetDefault(OsuSetting.SeasonalBackgroundMode, SeasonalBackgroundMode.Sometimes); SetDefault(OsuSetting.DiscordRichPresence, DiscordRichPresenceMode.Full); SetDefault(OsuSetting.EditorDim, 0.25f, 0f, 0.75f, 0.25f); SetDefault(OsuSetting.EditorWaveformOpacity, 0.25f, 0f, 1f, 0.25f); SetDefault(OsuSetting.EditorShowHitMarkers, true); SetDefault(OsuSetting.EditorAutoSeekOnPlacement, true); SetDefault(OsuSetting.EditorLimitedDistanceSnap, false); SetDefault(OsuSetting.EditorShowSpeedChanges, false); SetDefault(OsuSetting.HideCountryFlags, false); SetDefault(OsuSetting.MultiplayerRoomFilter, RoomPermissionsFilter.All); SetDefault(OsuSetting.LastProcessedMetadataId, -1); SetDefault(OsuSetting.ComboColourNormalisationAmount, 0.2f, 0f, 1f, 0.01f); SetDefault(OsuSetting.UserOnlineStatus, null); } protected override bool CheckLookupContainsPrivateInformation(OsuSetting lookup) { switch (lookup) { case OsuSetting.Token: return true; } return false; } public void Migrate() { // arrives as 2020.123.0 string rawVersion = Get(OsuSetting.Version); if (rawVersion.Length < 6) return; string[] pieces = rawVersion.Split('.'); // on a fresh install or when coming from a non-release build, execution will end here. // we don't want to run migrations in such cases. if (!int.TryParse(pieces[0], out int year)) return; if (!int.TryParse(pieces[1], out int monthDay)) return; // ReSharper disable once UnusedVariable int combined = (year * 10000) + monthDay; // migrations can be added here using a condition like: // if (combined < 20220103) { performMigration() } if (combined < 20230918) { #pragma warning disable CS0618 // Type or member is obsolete SetValue(OsuSetting.AutomaticallyDownloadMissingBeatmaps, Get(OsuSetting.AutomaticallyDownloadWhenSpectating)); // can be removed 20240618 #pragma warning restore CS0618 // Type or member is obsolete } } public override TrackedSettings CreateTrackedSettings() { // these need to be assigned in normal game startup scenarios. Debug.Assert(LookupKeyBindings != null); Debug.Assert(LookupSkinName != null); return new TrackedSettings { new TrackedSetting(OsuSetting.ShowFpsDisplay, state => new SettingDescription( rawValue: state, name: GlobalActionKeyBindingStrings.ToggleFPSCounter, value: state ? CommonStrings.Enabled.ToLower() : CommonStrings.Disabled.ToLower(), shortcut: LookupKeyBindings(GlobalAction.ToggleFPSDisplay)) ), new TrackedSetting(OsuSetting.MouseDisableButtons, disabledState => new SettingDescription( rawValue: !disabledState, name: GlobalActionKeyBindingStrings.ToggleGameplayMouseButtons, value: disabledState ? CommonStrings.Disabled.ToLower() : CommonStrings.Enabled.ToLower(), shortcut: LookupKeyBindings(GlobalAction.ToggleGameplayMouseButtons)) ), new TrackedSetting(OsuSetting.GameplayLeaderboard, state => new SettingDescription( rawValue: state, name: GlobalActionKeyBindingStrings.ToggleInGameLeaderboard, value: state ? CommonStrings.Enabled.ToLower() : CommonStrings.Disabled.ToLower(), shortcut: LookupKeyBindings(GlobalAction.ToggleInGameLeaderboard)) ), new TrackedSetting(OsuSetting.HUDVisibilityMode, visibilityMode => new SettingDescription( rawValue: visibilityMode, name: GameplaySettingsStrings.HUDVisibilityMode, value: visibilityMode.GetLocalisableDescription(), shortcut: new TranslatableString(@"_", @"{0}: {1} {2}: {3}", GlobalActionKeyBindingStrings.ToggleInGameInterface, LookupKeyBindings(GlobalAction.ToggleInGameInterface), GlobalActionKeyBindingStrings.HoldForHUD, LookupKeyBindings(GlobalAction.HoldForHUD))) ), new TrackedSetting(OsuSetting.Scaling, scalingMode => new SettingDescription( rawValue: scalingMode, name: GraphicsSettingsStrings.ScreenScaling, value: scalingMode.GetLocalisableDescription() ) ), new TrackedSetting(OsuSetting.Skin, skin => { string skinName = string.Empty; if (Guid.TryParse(skin, out var id)) skinName = LookupSkinName(id); return new SettingDescription( rawValue: skinName, name: SkinSettingsStrings.SkinSectionHeader, value: skinName, shortcut: new TranslatableString(@"_", @"{0}: {1}", GlobalActionKeyBindingStrings.RandomSkin, LookupKeyBindings(GlobalAction.RandomSkin)) ); }), new TrackedSetting(OsuSetting.UIScale, scale => new SettingDescription( rawValue: scale, name: GraphicsSettingsStrings.UIScaling, value: $"{scale:N2}x" // TODO: implement lookup for framework platform key bindings ) ), }; } public Func LookupSkinName { private get; set; } = _ => @"unknown"; public Func LookupKeyBindings { get; set; } = _ => @"unknown"; IBindable IGameplaySettings.ComboColourNormalisationAmount => GetOriginalBindable(OsuSetting.ComboColourNormalisationAmount); IBindable IGameplaySettings.PositionalHitsoundsLevel => GetOriginalBindable(OsuSetting.PositionalHitsoundsLevel); } // IMPORTANT: These are used in user configuration files. // The naming of these keys should not be changed once they are deployed in a release, unless migration logic is also added. public enum OsuSetting { Ruleset, Token, MenuCursorSize, GameplayCursorSize, AutoCursorSize, GameplayCursorDuringTouch, DimLevel, BlurLevel, EditorDim, LightenDuringBreaks, ShowStoryboard, KeyOverlay, GameplayLeaderboard, PositionalHitsoundsLevel, AlwaysPlayFirstComboBreak, FloatingComments, HUDVisibilityMode, ShowHealthDisplayWhenCantFail, FadePlayfieldWhenHealthLow, /// /// Disables mouse buttons clicks during gameplay. /// MouseDisableButtons, MouseDisableWheel, ConfineMouseMode, /// /// Globally applied audio offset. /// This is added to the audio track's current time. Higher values will cause gameplay to occur earlier, relative to the audio track. /// AudioOffset, VolumeInactive, MenuMusic, MenuVoice, MenuTips, CursorRotation, MenuParallax, Prefer24HourTime, BeatmapDetailTab, BeatmapDetailModsFilter, Username, ReleaseStream, SavePassword, SaveUsername, DisplayStarsMinimum, DisplayStarsMaximum, SongSelectGroupingMode, SongSelectSortingMode, RandomSelectAlgorithm, ModSelectHotkeyStyle, ShowFpsDisplay, ChatDisplayHeight, BeatmapListingCardSize, ToolbarClockDisplayMode, SongSelectBackgroundBlur, Version, ShowFirstRunSetup, ShowConvertedBeatmaps, Skin, ScreenshotFormat, ScreenshotCaptureMenuCursor, SongSelectRightMouseScroll, BeatmapSkins, BeatmapColours, BeatmapHitsounds, IncreaseFirstObjectVisibility, ScoreDisplayMode, ExternalLinkWarning, PreferNoVideo, Scaling, ScalingPositionX, ScalingPositionY, ScalingSizeX, ScalingSizeY, ScalingBackgroundDim, UIScale, IntroSequence, NotifyOnUsernameMentioned, NotifyOnPrivateMessage, UIHoldActivationDelay, HitLighting, MenuBackgroundSource, GameplayDisableWinKey, SeasonalBackgroundMode, EditorWaveformOpacity, EditorShowHitMarkers, EditorAutoSeekOnPlacement, DiscordRichPresence, [Obsolete($"Use {nameof(AutomaticallyDownloadMissingBeatmaps)} instead.")] // can be removed 20240318 AutomaticallyDownloadWhenSpectating, ShowOnlineExplicitContent, LastProcessedMetadataId, SafeAreaConsiderations, ComboColourNormalisationAmount, ProfileCoverExpanded, EditorLimitedDistanceSnap, ReplaySettingsOverlay, ReplayPlaybackControlsExpanded, AutomaticallyDownloadMissingBeatmaps, EditorShowSpeedChanges, TouchDisableGameplayTaps, ModSelectTextSearchStartsActive, UserOnlineStatus, MultiplayerRoomFilter, HideCountryFlags, } }