osu/osu.Game/OsuGameBase.cs
Dean Herbert f149a66a4d Use LargeTextureStore for all online texture retrieval
Until now, many online textures were retrieved via the default texture store, which causes them to never be removed from GPU memory. It also has a performance overhead due to mipmap generation (which will be avoided via ppy/osu-framework#1885.
2018-09-09 02:45:37 +09:00

302 lines
12 KiB
C#

// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.IO.Stores;
using osu.Framework.Platform;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Graphics;
using osu.Game.Graphics.Cursor;
using osu.Game.Online.API;
using osu.Framework.Graphics.Performance;
using osu.Framework.Graphics.Textures;
using osu.Framework.Input;
using osu.Framework.Logging;
using osu.Game.Audio;
using osu.Game.Database;
using osu.Game.Input;
using osu.Game.Input.Bindings;
using osu.Game.IO;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning;
using OpenTK.Input;
using DebugUtils = osu.Game.Utils.DebugUtils;
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>
public class OsuGameBase : Framework.Game, ICanAcceptFiles
{
protected OsuConfigManager LocalConfig;
protected BeatmapManager BeatmapManager;
protected SkinManager SkinManager;
protected RulesetStore RulesetStore;
protected FileStore FileStore;
protected ScoreStore ScoreStore;
protected KeyBindingStore KeyBindingStore;
protected SettingsStore SettingsStore;
protected RulesetConfigCache RulesetConfigCache;
protected MenuCursorContainer MenuCursorContainer;
private Container content;
protected override Container<Drawable> Content => content;
private OsuBindableBeatmap beatmap;
protected BindableBeatmap Beatmap => beatmap;
private Bindable<bool> fpsDisplayVisible;
protected AssemblyName AssemblyName => Assembly.GetEntryAssembly()?.GetName() ?? new AssemblyName { Version = new Version() };
public bool IsDeployedBuild => AssemblyName.Version.Major > 0;
public string Version
{
get
{
if (!IsDeployedBuild)
return @"local " + (DebugUtils.IsDebug ? @"debug" : @"release");
var assembly = AssemblyName;
return $@"{assembly.Version.Major}.{assembly.Version.Minor}.{assembly.Version.Build}";
}
}
public OsuGameBase()
{
Name = @"osu!lazer";
}
private DependencyContainer dependencies;
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) =>
dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
private DatabaseContextFactory contextFactory;
protected override UserInputManager CreateUserInputManager() => new OsuUserInputManager();
[BackgroundDependencyLoader]
private void load()
{
Resources.AddStore(new DllResourceStore(@"osu.Game.Resources.dll"));
dependencies.Cache(contextFactory = new DatabaseContextFactory(Host.Storage));
var largeStore = new LargeTextureStore(new TextureLoaderStore(new NamespacedResourceStore<byte[]>(Resources, @"Textures")));
largeStore.AddStore(new TextureLoaderStore(new OnlineStore()));
dependencies.Cache(largeStore);
dependencies.CacheAs(this);
dependencies.Cache(LocalConfig);
//this completely overrides the framework default. will need to change once we make a proper FontStore.
dependencies.Cache(Fonts = new FontStore(new GlyphStore(Resources, @"Fonts/FontAwesome")));
Fonts.AddStore(new GlyphStore(Resources, @"Fonts/osuFont"));
Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-Medium"));
Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-MediumItalic"));
Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Noto-Basic"));
Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Noto-Hangul"));
Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Noto-CJK-Basic"));
Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Noto-CJK-Compatibility"));
Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-Regular"));
Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-RegularItalic"));
Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-SemiBold"));
Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-SemiBoldItalic"));
Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-Bold"));
Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-BoldItalic"));
Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-Light"));
Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-LightItalic"));
Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-Black"));
Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-BlackItalic"));
Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Venera"));
Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Venera-Light"));
runMigrations();
dependencies.Cache(SkinManager = new SkinManager(Host.Storage, contextFactory, Host, Audio));
dependencies.CacheAs<ISkinSource>(SkinManager);
var api = new APIAccess(LocalConfig);
dependencies.Cache(api);
dependencies.CacheAs<IAPIProvider>(api);
dependencies.Cache(RulesetStore = new RulesetStore(contextFactory));
dependencies.Cache(FileStore = new FileStore(contextFactory, Host.Storage));
dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, contextFactory, RulesetStore, api, Audio, Host));
dependencies.Cache(ScoreStore = new ScoreStore(Host.Storage, contextFactory, Host, BeatmapManager, RulesetStore));
dependencies.Cache(KeyBindingStore = new KeyBindingStore(contextFactory, RulesetStore));
dependencies.Cache(SettingsStore = new SettingsStore(contextFactory));
dependencies.Cache(RulesetConfigCache = new RulesetConfigCache(SettingsStore));
dependencies.Cache(new OsuColour());
fileImporters.Add(BeatmapManager);
fileImporters.Add(ScoreStore);
fileImporters.Add(SkinManager);
var defaultBeatmap = new DummyWorkingBeatmap(this);
beatmap = new OsuBindableBeatmap(defaultBeatmap, Audio);
BeatmapManager.DefaultBeatmap = defaultBeatmap;
// tracks play so loud our samples can't keep up.
// this adds a global reduction of track volume for the time being.
Audio.Track.AddAdjustment(AdjustableProperty.Volume, new BindableDouble(0.8));
dependencies.CacheAs<BindableBeatmap>(beatmap);
dependencies.CacheAs<IBindableBeatmap>(beatmap);
FileStore.Cleanup();
AddInternal(api);
GlobalActionContainer globalBinding;
MenuCursorContainer = new MenuCursorContainer { RelativeSizeAxes = Axes.Both };
MenuCursorContainer.Child = globalBinding = new GlobalActionContainer(this)
{
RelativeSizeAxes = Axes.Both,
Child = content = new OsuTooltipContainer(MenuCursorContainer.Cursor) { RelativeSizeAxes = Axes.Both }
};
base.Content.Add(new DrawSizePreservingFillContainer { Child = MenuCursorContainer });
KeyBindingStore.Register(globalBinding);
dependencies.Cache(globalBinding);
PreviewTrackManager previewTrackManager;
dependencies.Cache(previewTrackManager = new PreviewTrackManager());
Add(previewTrackManager);
}
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 += val => { FrameStatisticsMode = val ? FrameStatisticsMode.Minimal : FrameStatisticsMode.None; };
fpsDisplayVisible.TriggerChange();
}
private void runMigrations()
{
try
{
using (var db = contextFactory.GetForWrite(false))
db.Context.Migrate();
}
catch (Exception e)
{
Logger.Error(e.InnerException ?? e, "Migration failed! We'll be starting with a fresh database.", LoggingTarget.Database);
// if we failed, let's delete the database and start fresh.
// todo: we probably want a better (non-destructive) migrations/recovery process at a later point than this.
contextFactory.ResetDatabase();
Logger.Log("Database purged successfully.", LoggingTarget.Database);
// only run once more, then hard bail.
using (var db = contextFactory.GetForWrite(false))
db.Context.Migrate();
}
}
public override void SetHost(GameHost host)
{
if (LocalConfig == null)
LocalConfig = new OsuConfigManager(host.Storage);
base.SetHost(host);
}
private readonly List<ICanAcceptFiles> fileImporters = new List<ICanAcceptFiles>();
public void Import(params string[] paths)
{
var extension = Path.GetExtension(paths.First());
foreach (var importer in fileImporters)
if (importer.HandledExtensions.Contains(extension)) importer.Import(paths);
}
public string[] HandledExtensions => fileImporters.SelectMany(i => i.HandledExtensions).ToArray();
private class OsuBindableBeatmap : BindableBeatmap
{
public OsuBindableBeatmap(WorkingBeatmap defaultValue, AudioManager audioManager)
: this(defaultValue)
{
RegisterAudioManager(audioManager);
}
private OsuBindableBeatmap(WorkingBeatmap defaultValue)
: base(defaultValue)
{
}
public override BindableBeatmap GetBoundCopy()
{
var copy = new OsuBindableBeatmap(Default);
copy.BindTo(this);
return copy;
}
}
private class OsuUserInputManager : UserInputManager
{
protected override MouseButtonEventManager CreateButtonManagerFor(MouseButton button)
{
switch (button)
{
case MouseButton.Right:
return new RightMouseManager(button);
}
return base.CreateButtonManagerFor(button);
}
private class RightMouseManager : MouseButtonEventManager
{
public RightMouseManager(MouseButton button)
: base(button)
{
}
public override bool EnableDrag => true; // allow right-mouse dragging for absolute scroll in scroll containers.
public override bool EnableClick => false;
public override bool ChangeFocusOnClick => false;
}
}
}
}