2019-01-24 16:43:03 +08:00
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
2018-04-13 17:19:50 +08:00
using System ;
using System.Collections.Generic ;
using System.IO ;
using System.Linq ;
using System.Reflection ;
2021-10-12 15:04:09 +08:00
using System.Threading ;
2018-04-13 17:19:50 +08:00
using osu.Framework.Allocation ;
using osu.Framework.Audio ;
2019-02-21 18:04:31 +08:00
using osu.Framework.Bindables ;
2019-06-20 11:48:45 +08:00
using osu.Framework.Development ;
2020-07-29 11:39:18 +08:00
using osu.Framework.Extensions ;
2018-04-13 17:19:50 +08:00
using osu.Framework.Graphics ;
using osu.Framework.Graphics.Containers ;
using osu.Framework.Graphics.Performance ;
using osu.Framework.Graphics.Textures ;
2018-07-09 14:26:22 +08:00
using osu.Framework.Input ;
2021-09-30 23:27:54 +08:00
using osu.Framework.IO.Stores ;
2018-04-13 17:19:50 +08:00
using osu.Framework.Logging ;
2021-09-30 23:27:54 +08:00
using osu.Framework.Platform ;
2018-05-09 19:52:46 +08:00
using osu.Game.Audio ;
2021-09-30 23:27:54 +08:00
using osu.Game.Beatmaps ;
using osu.Game.Configuration ;
2018-04-13 17:19:50 +08:00
using osu.Game.Database ;
2021-09-30 23:27:54 +08:00
using osu.Game.Graphics ;
using osu.Game.Graphics.Cursor ;
2018-04-13 17:19:50 +08:00
using osu.Game.Input ;
using osu.Game.Input.Bindings ;
using osu.Game.IO ;
2020-12-24 16:58:38 +08:00
using osu.Game.Online ;
2021-09-30 23:27:54 +08:00
using osu.Game.Online.API ;
2021-02-12 13:54:19 +08:00
using osu.Game.Online.Chat ;
2020-12-25 12:38:11 +08:00
using osu.Game.Online.Multiplayer ;
2020-10-22 12:41:54 +08:00
using osu.Game.Online.Spectator ;
2020-08-06 18:01:23 +08:00
using osu.Game.Overlays ;
2019-12-28 21:13:18 +08:00
using osu.Game.Resources ;
2018-04-13 17:19:50 +08:00
using osu.Game.Rulesets ;
2019-05-15 12:00:11 +08:00
using osu.Game.Rulesets.Mods ;
2018-11-28 15:12:57 +08:00
using osu.Game.Scoring ;
2018-04-13 17:19:50 +08:00
using osu.Game.Skinning ;
2021-04-09 07:34:35 +08:00
using osu.Game.Utils ;
2020-08-03 17:48:10 +08:00
using RuntimeInfo = osu . Framework . RuntimeInfo ;
2018-04-13 17:19:50 +08:00
namespace osu.Game
{
/// <summary>
/// The most basic <see cref="Game"/> that can be used to host osu! components and systems.
/// Unlike <see cref="OsuGame"/>, this class will not load any kind of UI, allowing it to be used
/// for provide dependencies to test cases without interfering with them.
/// </summary>
2021-05-28 01:44:44 +08:00
public partial class OsuGameBase : Framework . Game , ICanAcceptFiles
2018-04-13 17:19:50 +08:00
{
2021-05-28 01:27:06 +08:00
public const string CLIENT_STREAM_NAME = @"lazer" ;
2019-06-03 12:16:05 +08:00
2020-03-22 01:16:28 +08:00
public const int SAMPLE_CONCURRENCY = 6 ;
2021-09-05 12:25:10 +08:00
/// <summary>
/// Length of debounce (in milliseconds) for commonly occuring sample playbacks that could stack.
/// </summary>
public const int SAMPLE_DEBOUNCE_TIME = 20 ;
2021-05-28 01:37:14 +08:00
/// <summary>
/// The maximum volume at which audio tracks should playback. This can be set lower than 1 to create some head-room for sound effects.
/// </summary>
2021-11-09 16:27:07 +08:00
private const double global_track_volume_adjust = 0.8 ;
2021-05-28 01:37:14 +08:00
2020-12-24 16:58:38 +08:00
public bool UseDevelopmentServer { get ; }
2021-05-28 01:37:14 +08:00
public virtual Version AssemblyVersion = > Assembly . GetEntryAssembly ( ) ? . GetName ( ) . Version ? ? new Version ( ) ;
/// <summary>
/// MD5 representation of the game executable.
/// </summary>
public string VersionHash { get ; private set ; }
public bool IsDeployedBuild = > AssemblyVersion . Major > 0 ;
public virtual string Version
{
get
{
if ( ! IsDeployedBuild )
return @"local " + ( DebugUtils . IsDebugBuild ? @"debug" : @"release" ) ;
var version = AssemblyVersion ;
2021-07-04 11:39:50 +08:00
return $@"{version.Major}.{version.Minor}.{version.Build}-lazer" ;
2021-05-28 01:37:14 +08:00
}
}
2021-05-28 01:27:06 +08:00
protected OsuConfigManager LocalConfig { get ; private set ; }
2018-04-13 17:19:50 +08:00
2021-04-19 10:30:55 +08:00
protected SessionStatics SessionStatics { get ; private set ; }
2021-05-28 01:27:06 +08:00
protected BeatmapManager BeatmapManager { get ; private set ; }
2018-04-13 17:19:50 +08:00
2021-11-25 16:23:46 +08:00
protected BeatmapModelDownloader BeatmapDownloader { get ; private set ; }
2021-05-28 01:27:06 +08:00
protected ScoreManager ScoreManager { get ; private set ; }
2018-11-28 15:47:10 +08:00
2021-11-25 16:21:05 +08:00
protected ScoreModelDownloader ScoreDownloader { get ; private set ; }
2021-05-28 01:27:06 +08:00
protected SkinManager SkinManager { get ; private set ; }
2018-04-13 17:19:50 +08:00
2021-05-28 01:27:06 +08:00
protected RulesetStore RulesetStore { get ; private set ; }
2018-04-13 17:19:50 +08:00
2021-06-10 12:58:08 +08:00
protected RealmKeyBindingStore KeyBindingStore { get ; private set ; }
2018-04-13 17:19:50 +08:00
2021-05-28 01:27:06 +08:00
protected MenuCursorContainer MenuCursorContainer { get ; private set ; }
2018-12-06 11:17:08 +08:00
2021-05-28 01:27:06 +08:00
protected MusicController MusicController { get ; private set ; }
protected IAPIProvider API { get ; set ; }
2020-10-22 12:41:54 +08:00
2021-05-28 01:27:06 +08:00
protected Storage Storage { get ; set ; }
protected Bindable < WorkingBeatmap > Beatmap { get ; private set ; } // cached via load() method
2018-04-13 17:19:50 +08:00
2019-05-15 12:00:11 +08:00
[Cached]
[Cached(typeof(IBindable<RulesetInfo>))]
protected readonly Bindable < RulesetInfo > Ruleset = new Bindable < RulesetInfo > ( ) ;
2021-02-10 13:38:15 +08:00
/// <summary>
/// The current mod selection for the local user.
/// </summary>
/// <remarks>
/// If a mod select overlay is present, mod instances set to this value are not guaranteed to remain as the provided instance and will be overwritten by a copy.
/// In such a case, changes to settings of a mod will *not* propagate after a mod is added to this collection.
/// As such, all settings should be finalised before adding a mod to this collection.
/// </remarks>
2019-05-15 12:00:11 +08:00
[Cached]
2019-12-13 19:13:53 +08:00
[Cached(typeof(IBindable<IReadOnlyList<Mod>>))]
2019-12-13 20:45:38 +08:00
protected readonly Bindable < IReadOnlyList < Mod > > SelectedMods = new Bindable < IReadOnlyList < Mod > > ( Array . Empty < Mod > ( ) ) ;
2019-02-01 14:42:15 +08:00
2019-12-12 17:53:25 +08:00
/// <summary>
/// Mods available for the current <see cref="Ruleset"/>.
/// </summary>
public readonly Bindable < Dictionary < ModType , IReadOnlyList < Mod > > > AvailableMods = new Bindable < Dictionary < ModType , IReadOnlyList < Mod > > > ( ) ;
2021-05-28 01:30:31 +08:00
private BeatmapDifficultyCache difficultyCache ;
private UserLookupCache userCache ;
2021-11-30 18:27:43 +08:00
private BeatmapLookupCache beatmapCache ;
2021-05-28 01:30:31 +08:00
private RulesetConfigCache rulesetConfigCache ;
private SpectatorClient spectatorClient ;
private MultiplayerClient multiplayerClient ;
2021-01-07 13:07:36 +08:00
private RealmContextFactory realmFactory ;
2021-05-28 01:30:31 +08:00
protected override Container < Drawable > Content = > content ;
private Container content ;
2021-05-28 01:37:14 +08:00
private DependencyContainer dependencies ;
2018-04-13 17:19:50 +08:00
2021-05-28 01:37:14 +08:00
private Bindable < bool > fpsDisplayVisible ;
2018-04-13 17:19:50 +08:00
2021-11-09 16:27:07 +08:00
private readonly BindableNumber < double > globalTrackVolumeAdjust = new BindableNumber < double > ( global_track_volume_adjust ) ;
2018-04-13 17:19:50 +08:00
2022-01-21 13:56:28 +08:00
/// <summary>
/// A legacy EF context factory if migration has not been performed to realm yet.
/// </summary>
protected DatabaseContextFactory EFContextFactory { get ; private set ; }
2018-04-13 17:19:50 +08:00
public OsuGameBase ( )
{
2020-12-24 16:58:38 +08:00
UseDevelopmentServer = DebugUtils . IsDebugBuild ;
2021-07-04 11:39:50 +08:00
Name = @"osu!" ;
2018-04-13 17:19:50 +08:00
}
[BackgroundDependencyLoader]
2021-11-08 17:24:37 +08:00
private void load ( ReadableKeyCombinationProvider keyCombinationProvider )
2018-04-13 17:19:50 +08:00
{
2020-08-03 17:48:10 +08:00
try
{
using ( var str = File . OpenRead ( typeof ( OsuGameBase ) . Assembly . Location ) )
VersionHash = str . ComputeMD5Hash ( ) ;
}
catch
{
// special case for android builds, which can't read DLLs from a packed apk.
// should eventually be handled in a better way.
VersionHash = $"{Version}-{RuntimeInfo.OS}" . ComputeMD5Hash ( ) ;
}
2020-07-29 11:39:18 +08:00
2019-12-28 21:13:18 +08:00
Resources . AddStore ( new DllResourceStore ( OsuResources . ResourceAssembly ) ) ;
2018-06-04 19:01:55 +08:00
2022-01-21 13:56:28 +08:00
if ( Storage . Exists ( DatabaseContextFactory . DATABASE_NAME ) )
dependencies . Cache ( EFContextFactory = new DatabaseContextFactory ( Storage ) ) ;
2021-11-23 10:48:56 +08:00
2022-01-21 13:56:28 +08:00
dependencies . Cache ( realmFactory = new RealmContextFactory ( Storage , "client" , EFContextFactory ) ) ;
2021-11-23 10:48:56 +08:00
2022-01-13 17:23:10 +08:00
dependencies . Cache ( RulesetStore = new RulesetStore ( realmFactory , Storage ) ) ;
2021-12-03 17:14:44 +08:00
dependencies . CacheAs < IRulesetStore > ( RulesetStore ) ;
2018-04-13 17:19:50 +08:00
2022-01-22 10:50:47 +08:00
// Backup is taken here rather than in EFToRealmMigrator to avoid recycling realm contexts
// after initial usages below. It can be moved once a direction is established for handling re-subscription.
// See https://github.com/ppy/osu/pull/16547 for more discussion.
if ( EFContextFactory ! = null )
{
string migration = $"before_final_migration_{DateTimeOffset.UtcNow.ToUnixTimeSeconds()}" ;
EFContextFactory . CreateBackup ( $"client.{migration}.db" ) ;
realmFactory . CreateBackup ( $"client.{migration}.realm" ) ;
using ( var source = Storage . GetStream ( "collection.db" ) )
using ( var destination = Storage . GetStream ( $"collection.{migration}.db" , FileAccess . Write , FileMode . CreateNew ) )
source . CopyTo ( destination ) ;
}
2020-05-04 16:01:05 +08:00
dependencies . CacheAs ( Storage ) ;
2019-01-25 10:41:44 +08:00
var largeStore = new LargeTextureStore ( Host . CreateTextureLoaderStore ( new NamespacedResourceStore < byte [ ] > ( Resources , @"Textures" ) ) ) ;
largeStore . AddStore ( Host . CreateTextureLoaderStore ( new OnlineStore ( ) ) ) ;
2018-09-09 01:41:47 +08:00
dependencies . Cache ( largeStore ) ;
2018-04-13 17:19:50 +08:00
dependencies . CacheAs ( this ) ;
2020-12-24 16:58:38 +08:00
dependencies . CacheAs ( LocalConfig ) ;
2018-04-13 17:19:50 +08:00
2021-09-06 23:45:53 +08:00
InitialiseFonts ( ) ;
2018-04-13 17:19:50 +08:00
2020-03-22 01:16:28 +08:00
Audio . Samples . PlaybackConcurrency = SAMPLE_CONCURRENCY ;
2021-11-29 17:07:32 +08:00
dependencies . Cache ( SkinManager = new SkinManager ( Storage , realmFactory , Host , Resources , Audio , Scheduler ) ) ;
2018-09-01 16:39:54 +08:00
dependencies . CacheAs < ISkinSource > ( SkinManager ) ;
2020-12-24 16:58:38 +08:00
EndpointConfiguration endpoints = UseDevelopmentServer ? ( EndpointConfiguration ) new DevelopmentEndpointConfiguration ( ) : new ProductionEndpointConfiguration ( ) ;
2021-02-12 13:54:19 +08:00
MessageFormatter . WebsiteRootUrl = endpoints . WebsiteRootUrl ;
2021-02-14 22:31:57 +08:00
dependencies . CacheAs ( API ? ? = new APIAccess ( LocalConfig , endpoints , VersionHash ) ) ;
2018-09-01 16:39:54 +08:00
2021-05-20 15:30:56 +08:00
dependencies . CacheAs ( spectatorClient = new OnlineSpectatorClient ( endpoints ) ) ;
2021-05-20 14:39:45 +08:00
dependencies . CacheAs ( multiplayerClient = new OnlineMultiplayerClient ( endpoints ) ) ;
2018-09-01 16:39:54 +08:00
2019-05-31 13:51:12 +08:00
var defaultBeatmap = new DummyWorkingBeatmap ( Audio , Textures ) ;
2018-12-25 17:34:45 +08:00
2019-05-09 14:15:28 +08:00
// ordering is important here to ensure foreign keys rules are not broken in ModelStore.Cleanup()
2021-12-13 16:37:27 +08:00
dependencies . Cache ( ScoreManager = new ScoreManager ( RulesetStore , ( ) = > BeatmapManager , Storage , realmFactory , Scheduler , Host , ( ) = > difficultyCache , LocalConfig ) ) ;
2021-11-24 11:16:08 +08:00
dependencies . Cache ( BeatmapManager = new BeatmapManager ( Storage , realmFactory , RulesetStore , API , Audio , Resources , Host , defaultBeatmap , performOnlineLookups : true ) ) ;
2019-05-09 14:15:28 +08:00
2021-11-25 16:42:41 +08:00
dependencies . Cache ( BeatmapDownloader = new BeatmapModelDownloader ( BeatmapManager , API ) ) ;
dependencies . Cache ( ScoreDownloader = new ScoreModelDownloader ( ScoreManager , API ) ) ;
2021-11-25 16:21:05 +08:00
2021-05-28 01:30:31 +08:00
dependencies . Cache ( difficultyCache = new BeatmapDifficultyCache ( ) ) ;
AddInternal ( difficultyCache ) ;
2020-07-21 22:13:04 +08:00
2021-05-28 01:30:31 +08:00
dependencies . Cache ( userCache = new UserLookupCache ( ) ) ;
AddInternal ( userCache ) ;
2020-11-06 15:38:57 +08:00
2021-11-30 18:27:43 +08:00
dependencies . Cache ( beatmapCache = new BeatmapLookupCache ( ) ) ;
AddInternal ( beatmapCache ) ;
2020-11-06 12:15:33 +08:00
var scorePerformanceManager = new ScorePerformanceCache ( ) ;
2020-11-02 13:49:25 +08:00
dependencies . Cache ( scorePerformanceManager ) ;
AddInternal ( scorePerformanceManager ) ;
2020-09-27 18:44:29 +08:00
2021-12-24 02:02:10 +08:00
dependencies . CacheAs < IRulesetConfigCache > ( rulesetConfigCache = new RulesetConfigCache ( realmFactory , RulesetStore ) ) ;
2021-04-11 14:00:37 +08:00
2021-04-12 22:52:12 +08:00
var powerStatus = CreateBatteryInfo ( ) ;
2021-04-11 14:00:37 +08:00
if ( powerStatus ! = null )
dependencies . CacheAs ( powerStatus ) ;
2021-04-19 10:30:55 +08:00
dependencies . Cache ( SessionStatics = new SessionStatics ( ) ) ;
2018-09-01 16:39:54 +08:00
dependencies . Cache ( new OsuColour ( ) ) ;
2020-10-02 15:08:11 +08:00
RegisterImportHandler ( BeatmapManager ) ;
RegisterImportHandler ( ScoreManager ) ;
RegisterImportHandler ( SkinManager ) ;
2018-09-01 16:39:54 +08:00
2021-02-11 14:02:34 +08:00
// drop track volume game-wide to leave some head-room for UI effects / samples.
// this means that for the time being, gameplay sample playback is louder relative to the audio track, compared to stable.
// we may want to revisit this if users notice or complain about the difference (consider this a bit of a trial).
Audio . Tracks . AddAdjustment ( AdjustableProperty . Volume , globalTrackVolumeAdjust ) ;
2018-04-13 17:19:50 +08:00
2019-11-12 17:45:42 +08:00
Beatmap = new NonNullableBindable < WorkingBeatmap > ( defaultBeatmap ) ;
2020-03-04 18:07:34 +08:00
2019-11-12 17:45:42 +08:00
dependencies . CacheAs < IBindable < WorkingBeatmap > > ( Beatmap ) ;
dependencies . CacheAs ( Beatmap ) ;
2018-04-13 17:19:50 +08:00
2020-10-22 13:54:27 +08:00
// add api components to hierarchy.
2020-05-28 18:05:35 +08:00
if ( API is APIAccess apiAccess )
AddInternal ( apiAccess ) ;
2021-05-20 14:55:07 +08:00
AddInternal ( spectatorClient ) ;
2020-12-20 23:21:41 +08:00
AddInternal ( multiplayerClient ) ;
2020-10-22 13:54:27 +08:00
2021-05-28 01:30:31 +08:00
AddInternal ( rulesetConfigCache ) ;
2018-04-22 03:10:06 +08:00
2021-04-08 14:17:53 +08:00
GlobalActionContainer globalBindings ;
var mainContent = new Drawable [ ]
2018-04-22 03:10:06 +08:00
{
2021-04-08 14:17:53 +08:00
MenuCursorContainer = new MenuCursorContainer { RelativeSizeAxes = Axes . Both } ,
// to avoid positional input being blocked by children, ensure the GlobalActionContainer is above everything.
globalBindings = new GlobalActionContainer ( this )
2018-04-22 03:10:06 +08:00
} ;
2021-08-29 00:51:12 +08:00
MenuCursorContainer . Child = content = new OsuTooltipContainer ( MenuCursorContainer . Cursor ) { RelativeSizeAxes = Axes . Both } ;
2021-04-07 18:00:04 +08:00
2021-04-08 14:17:53 +08:00
base . Content . Add ( CreateScalingContainer ( ) . WithChildren ( mainContent ) ) ;
2018-04-22 03:10:06 +08:00
2021-11-08 17:24:37 +08:00
KeyBindingStore = new RealmKeyBindingStore ( realmFactory , keyCombinationProvider ) ;
2021-09-07 14:19:23 +08:00
KeyBindingStore . Register ( globalBindings , RulesetStore . AvailableRulesets ) ;
2021-01-12 13:59:48 +08:00
2021-04-08 14:17:53 +08:00
dependencies . Cache ( globalBindings ) ;
2018-05-09 19:52:46 +08:00
PreviewTrackManager previewTrackManager ;
2021-11-09 16:27:07 +08:00
dependencies . Cache ( previewTrackManager = new PreviewTrackManager ( BeatmapManager . BeatmapTrackStore ) ) ;
2018-05-09 19:52:46 +08:00
Add ( previewTrackManager ) ;
2019-12-12 17:53:25 +08:00
2020-08-06 18:01:23 +08:00
AddInternal ( MusicController = new MusicController ( ) ) ;
dependencies . CacheAs ( MusicController ) ;
2019-12-12 17:53:25 +08:00
Ruleset . BindValueChanged ( onRulesetChanged ) ;
2022-01-18 11:20:52 +08:00
Beatmap . BindValueChanged ( onBeatmapChanged ) ;
2019-12-12 17:53:25 +08:00
}
2021-09-06 23:45:53 +08:00
protected virtual void InitialiseFonts ( )
{
AddFont ( Resources , @"Fonts/osuFont" ) ;
AddFont ( Resources , @"Fonts/Torus/Torus-Regular" ) ;
AddFont ( Resources , @"Fonts/Torus/Torus-Light" ) ;
AddFont ( Resources , @"Fonts/Torus/Torus-SemiBold" ) ;
AddFont ( Resources , @"Fonts/Torus/Torus-Bold" ) ;
2021-10-04 05:36:39 +08:00
AddFont ( Resources , @"Fonts/Torus-Alternate/Torus-Alternate-Regular" ) ;
AddFont ( Resources , @"Fonts/Torus-Alternate/Torus-Alternate-Light" ) ;
AddFont ( Resources , @"Fonts/Torus-Alternate/Torus-Alternate-SemiBold" ) ;
AddFont ( Resources , @"Fonts/Torus-Alternate/Torus-Alternate-Bold" ) ;
2021-09-06 23:45:53 +08:00
AddFont ( Resources , @"Fonts/Inter/Inter-Regular" ) ;
AddFont ( Resources , @"Fonts/Inter/Inter-RegularItalic" ) ;
AddFont ( Resources , @"Fonts/Inter/Inter-Light" ) ;
AddFont ( Resources , @"Fonts/Inter/Inter-LightItalic" ) ;
AddFont ( Resources , @"Fonts/Inter/Inter-SemiBold" ) ;
AddFont ( Resources , @"Fonts/Inter/Inter-SemiBoldItalic" ) ;
AddFont ( Resources , @"Fonts/Inter/Inter-Bold" ) ;
AddFont ( Resources , @"Fonts/Inter/Inter-BoldItalic" ) ;
AddFont ( Resources , @"Fonts/Noto/Noto-Basic" ) ;
AddFont ( Resources , @"Fonts/Noto/Noto-Hangul" ) ;
AddFont ( Resources , @"Fonts/Noto/Noto-CJK-Basic" ) ;
AddFont ( Resources , @"Fonts/Noto/Noto-CJK-Compatibility" ) ;
AddFont ( Resources , @"Fonts/Noto/Noto-Thai" ) ;
AddFont ( Resources , @"Fonts/Venera/Venera-Light" ) ;
AddFont ( Resources , @"Fonts/Venera/Venera-Bold" ) ;
AddFont ( Resources , @"Fonts/Venera/Venera-Black" ) ;
}
2021-05-28 01:37:14 +08:00
protected override void LoadComplete ( )
{
base . LoadComplete ( ) ;
// TODO: This is temporary until we reimplement the local FPS display.
// It's just to allow end-users to access the framework FPS display without knowing the shortcut key.
fpsDisplayVisible = LocalConfig . GetBindable < bool > ( OsuSetting . ShowFpsDisplay ) ;
fpsDisplayVisible . ValueChanged + = visible = > { FrameStatistics . Value = visible . NewValue ? FrameStatisticsMode . Minimal : FrameStatisticsMode . None ; } ;
fpsDisplayVisible . TriggerChange ( ) ;
FrameStatistics . ValueChanged + = e = > fpsDisplayVisible . Value = e . NewValue ! = FrameStatisticsMode . None ;
}
2021-05-31 12:39:18 +08:00
protected override IReadOnlyDependencyContainer CreateChildDependencies ( IReadOnlyDependencyContainer parent ) = >
dependencies = new DependencyContainer ( base . CreateChildDependencies ( parent ) ) ;
public override void SetHost ( GameHost host )
{
base . SetHost ( host ) ;
// may be non-null for certain tests
Storage ? ? = host . Storage ;
LocalConfig ? ? = UseDevelopmentServer
? new DevelopmentOsuConfigManager ( Storage )
: new OsuConfigManager ( Storage ) ;
}
/// <summary>
/// Use to programatically exit the game as if the user was triggering via alt-f4.
/// Will keep persisting until an exit occurs (exit may be blocked multiple times).
/// </summary>
public void GracefullyExit ( )
{
if ( ! OnExiting ( ) )
Exit ( ) ;
else
Scheduler . AddDelayed ( GracefullyExit , 2000 ) ;
}
public void Migrate ( string path )
{
2021-06-29 19:16:57 +08:00
Logger . Log ( $@"Migrating osu! data from ""{Storage.GetFullPath(string.Empty)}"" to ""{path}""..." ) ;
2021-10-12 15:04:09 +08:00
IDisposable realmBlocker = null ;
try
2021-06-10 12:58:08 +08:00
{
2021-10-12 15:04:09 +08:00
ManualResetEventSlim readyToRun = new ManualResetEventSlim ( ) ;
Scheduler . Add ( ( ) = >
{
realmBlocker = realmFactory . BlockAllOperations ( ) ;
readyToRun . Set ( ) ;
} , false ) ;
readyToRun . Wait ( ) ;
2021-06-10 12:58:08 +08:00
( Storage as OsuStorage ) ? . Migrate ( Host . GetStorage ( path ) ) ;
}
2021-10-12 15:04:09 +08:00
finally
{
realmBlocker ? . Dispose ( ) ;
}
2021-06-29 19:16:57 +08:00
Logger . Log ( @"Migration complete!" ) ;
2021-05-31 12:39:18 +08:00
}
protected override UserInputManager CreateUserInputManager ( ) = > new OsuUserInputManager ( ) ;
protected virtual BatteryInfo CreateBatteryInfo ( ) = > null ;
protected virtual Container CreateScalingContainer ( ) = > new DrawSizePreservingFillContainer ( ) ;
protected override Storage CreateStorage ( GameHost host , Storage defaultStorage ) = > new OsuStorage ( host , defaultStorage ) ;
2022-01-18 11:20:52 +08:00
private void onBeatmapChanged ( ValueChangedEvent < WorkingBeatmap > valueChangedEvent )
{
2022-01-18 15:11:03 +08:00
if ( IsLoaded & & ! ThreadSafety . IsUpdateThread )
2022-01-18 11:20:52 +08:00
throw new InvalidOperationException ( "Global beatmap bindable must be changed from update thread." ) ;
}
2019-12-12 17:53:25 +08:00
private void onRulesetChanged ( ValueChangedEvent < RulesetInfo > r )
{
2022-01-18 15:11:03 +08:00
if ( IsLoaded & & ! ThreadSafety . IsUpdateThread )
2022-01-18 11:20:52 +08:00
throw new InvalidOperationException ( "Global ruleset bindable must be changed from update thread." ) ;
2022-01-10 12:59:33 +08:00
Ruleset instance = null ;
2021-11-24 11:16:08 +08:00
2022-01-10 12:59:33 +08:00
try
{
if ( r . NewValue ? . Available = = true )
{
instance = r . NewValue . CreateInstance ( ) ;
}
}
catch ( Exception e )
{
Logger . Error ( e , "Ruleset load failed and has been rolled back" ) ;
}
if ( instance = = null )
2019-12-15 01:36:49 +08:00
{
2021-07-26 15:34:38 +08:00
// reject the change if the ruleset is not available.
2021-07-30 00:54:30 +08:00
Ruleset . Value = r . OldValue ? . Available = = true ? r . OldValue : RulesetStore . AvailableRulesets . First ( ) ;
2021-07-26 15:34:38 +08:00
return ;
2019-12-15 01:36:49 +08:00
}
2019-12-12 17:53:25 +08:00
2021-07-26 15:34:38 +08:00
var dict = new Dictionary < ModType , IReadOnlyList < Mod > > ( ) ;
foreach ( ModType type in Enum . GetValues ( typeof ( ModType ) ) )
2021-11-24 11:16:08 +08:00
{
dict [ type ] = instance . GetModsFor ( type ) . ToList ( ) ;
}
2021-07-26 15:34:38 +08:00
2019-12-13 23:42:31 +08:00
if ( ! SelectedMods . Disabled )
SelectedMods . Value = Array . Empty < Mod > ( ) ;
2021-01-18 16:50:32 +08:00
2019-12-12 17:53:25 +08:00
AvailableMods . Value = dict ;
2018-04-13 17:19:50 +08:00
}
2019-10-15 15:14:06 +08:00
protected override void Dispose ( bool isDisposing )
{
base . Dispose ( isDisposing ) ;
2020-10-19 18:03:22 +08:00
2019-10-15 15:14:06 +08:00
RulesetStore ? . Dispose ( ) ;
2020-05-22 22:26:37 +08:00
BeatmapManager ? . Dispose ( ) ;
2020-10-19 18:03:22 +08:00
LocalConfig ? . Dispose ( ) ;
2020-05-11 20:37:07 +08:00
2021-10-15 12:58:14 +08:00
realmFactory ? . Dispose ( ) ;
2019-10-15 15:14:06 +08:00
}
2018-04-13 17:19:50 +08:00
}
}