diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs index 0cdc1694f4..47acc1c926 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs @@ -16,11 +16,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps { public override void PostProcess(Beatmap beatmap) { - if (beatmap.ComboColours.Count == 0) - return; - int index = 0; - int colourIndex = 0; CatchHitObject lastObj = null; @@ -31,11 +27,9 @@ namespace osu.Game.Rulesets.Catch.Beatmaps if (obj.NewCombo) { if (lastObj != null) lastObj.LastInCombo = true; - colourIndex = (colourIndex + 1) % beatmap.ComboColours.Count; } obj.IndexInBeatmap = index++; - obj.ComboColour = beatmap.ComboColours[colourIndex]; lastObj = obj; } diff --git a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs index bfcdec9321..5b72d1ac6e 100644 --- a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs @@ -14,22 +14,16 @@ namespace osu.Game.Rulesets.Osu.Beatmaps { applyStacking(beatmap); - if (beatmap.ComboColours.Count == 0) - return; - int comboIndex = 0; - int colourIndex = 0; foreach (var obj in beatmap.HitObjects) { if (obj.NewCombo) { comboIndex = 0; - colourIndex = (colourIndex + 1) % beatmap.ComboColours.Count; } obj.IndexInCurrentCombo = comboIndex++; - obj.ComboColour = beatmap.ComboColours[colourIndex]; } } diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index 2c46a124d8..bc878b599b 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -11,6 +11,7 @@ using osu.Game.Audio; using osu.Game.Rulesets.Objects.Types; using osu.Game.Beatmaps.Formats; using osu.Game.Beatmaps.Timing; +using osu.Game.Skinning; namespace osu.Game.Tests.Beatmaps.Formats { @@ -163,7 +164,7 @@ namespace osu.Game.Tests.Beatmaps.Formats [Test] public void TestDecodeBeatmapColors() { - var decoder = new LegacyBeatmapDecoder(); + var decoder = new LegacySkinDecoder(); using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) using (var stream = new StreamReader(resStream)) { diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs index c36e825252..f37672b5cc 100644 --- a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs @@ -12,7 +12,6 @@ using osu.Game.IO.Serialization; using osu.Game.Rulesets.Objects.Types; using osu.Game.Tests.Resources; using OpenTK; -using OpenTK.Graphics; namespace osu.Game.Tests.Beatmaps.Formats { @@ -89,24 +88,6 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.AreEqual(2, difficulty.SliderTickRate); } - [Test] - public void TestDecodeColors() - { - var beatmap = decodeAsJson(normal); - Color4[] expected = - { - new Color4(142, 199, 255, 255), - new Color4(255, 128, 128, 255), - new Color4(128, 255, 255, 255), - new Color4(128, 255, 128, 255), - new Color4(255, 187, 255, 255), - new Color4(255, 177, 140, 255), - }; - Assert.AreEqual(expected.Length, beatmap.ComboColours.Count); - for (int i = 0; i < expected.Length; i++) - Assert.AreEqual(expected[i], beatmap.ComboColours[i]); - } - [Test] public void TestDecodeHitObjects() { diff --git a/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs b/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs index cede0160bc..5fd8fcc9c3 100644 --- a/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs @@ -68,7 +68,7 @@ namespace osu.Game.Tests.Visual IDatabaseContextFactory factory = new SingletonContextFactory(new OsuDbContext()); dependencies.Cache(rulesets = new RulesetStore(factory)); - dependencies.Cache(manager = new BeatmapManager(storage, factory, rulesets, null) + dependencies.Cache(manager = new BeatmapManager(storage, factory, rulesets, null, null) { DefaultBeatmap = defaultBeatmap = game.Beatmap.Default }); diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs index 93817b9b8f..60cf93fd91 100644 --- a/osu.Game/Beatmaps/Beatmap.cs +++ b/osu.Game/Beatmaps/Beatmap.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using OpenTK.Graphics; using osu.Game.Beatmaps.Timing; using osu.Game.Rulesets.Objects; using System.Collections.Generic; @@ -9,7 +8,6 @@ using System.Linq; using osu.Game.Beatmaps.ControlPoints; using osu.Game.IO.Serialization; using Newtonsoft.Json; -using osu.Game.Beatmaps.Formats; using osu.Game.IO.Serialization.Converters; namespace osu.Game.Beatmaps @@ -17,21 +15,13 @@ namespace osu.Game.Beatmaps /// /// A Beatmap containing converted HitObjects. /// - public class Beatmap : IJsonSerializable, IHasComboColours + public class Beatmap : IJsonSerializable where T : HitObject { public BeatmapInfo BeatmapInfo = new BeatmapInfo(); public ControlPointInfo ControlPointInfo = new ControlPointInfo(); public List Breaks = new List(); - public List ComboColours { get; set; } = new List - { - new Color4(17, 136, 170, 255), - new Color4(102, 136, 0, 255), - new Color4(204, 102, 0, 255), - new Color4(121, 9, 13, 255) - }; - [JsonIgnore] public BeatmapMetadata Metadata => BeatmapInfo?.Metadata ?? BeatmapInfo?.BeatmapSet?.Metadata; @@ -56,7 +46,6 @@ namespace osu.Game.Beatmaps BeatmapInfo = original?.BeatmapInfo.DeepClone() ?? BeatmapInfo; ControlPointInfo = original?.ControlPointInfo ?? ControlPointInfo; Breaks = original?.Breaks ?? Breaks; - ComboColours = original?.ComboColours ?? ComboColours; HitObjects = original?.HitObjects ?? HitObjects; if (original == null && Metadata == null) diff --git a/osu.Game/Beatmaps/BeatmapConverter.cs b/osu.Game/Beatmaps/BeatmapConverter.cs index c35c5df89b..2003b845d9 100644 --- a/osu.Game/Beatmaps/BeatmapConverter.cs +++ b/osu.Game/Beatmaps/BeatmapConverter.cs @@ -57,7 +57,6 @@ namespace osu.Game.Beatmaps beatmap.ControlPointInfo = original.ControlPointInfo; beatmap.HitObjects = original.HitObjects.SelectMany(h => convert(h, original)).ToList(); beatmap.Breaks = original.Breaks; - beatmap.ComboColours = original.ComboColours; return beatmap; } diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 817a3388e2..1113e38d7a 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -8,6 +8,7 @@ using System.Linq; using System.Linq.Expressions; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; +using osu.Framework.Audio; using osu.Framework.Extensions; using osu.Framework.Logging; using osu.Framework.Platform; @@ -55,6 +56,8 @@ namespace osu.Game.Beatmaps private readonly APIAccess api; + private readonly AudioManager audioManager; + private readonly List currentDownloads = new List(); /// @@ -62,7 +65,7 @@ namespace osu.Game.Beatmaps /// public Func GetStableStorage { private get; set; } - public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, APIAccess api, IIpcHost importHost = null) + public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, APIAccess api, AudioManager audioManager, IIpcHost importHost = null) : base(storage, contextFactory, new BeatmapStore(contextFactory), importHost) { beatmaps = (BeatmapStore)ModelStore; @@ -71,6 +74,7 @@ namespace osu.Game.Beatmaps this.rulesets = rulesets; this.api = api; + this.audioManager = audioManager; } protected override void Populate(BeatmapSetInfo model, ArchiveReader archive) @@ -217,7 +221,7 @@ namespace osu.Game.Beatmaps if (beatmapInfo.Metadata == null) beatmapInfo.Metadata = beatmapInfo.BeatmapSet.Metadata; - WorkingBeatmap working = new BeatmapManagerWorkingBeatmap(Files.Store, beatmapInfo); + WorkingBeatmap working = new BeatmapManagerWorkingBeatmap(Files.Store, beatmapInfo, audioManager); previous?.TransferTo(working); diff --git a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs index fb11684309..58b51085a4 100644 --- a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs @@ -4,12 +4,14 @@ using System; using System.IO; using System.Linq; +using osu.Framework.Audio; using osu.Framework.Audio.Track; using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; using osu.Framework.Logging; using osu.Game.Beatmaps.Formats; using osu.Game.Graphics.Textures; +using osu.Game.Skinning; using osu.Game.Storyboards; namespace osu.Game.Beatmaps @@ -19,11 +21,13 @@ namespace osu.Game.Beatmaps protected class BeatmapManagerWorkingBeatmap : WorkingBeatmap { private readonly IResourceStore store; + private readonly AudioManager audioManager; - public BeatmapManagerWorkingBeatmap(IResourceStore store, BeatmapInfo beatmapInfo) + public BeatmapManagerWorkingBeatmap(IResourceStore store, BeatmapInfo beatmapInfo, AudioManager audioManager) : base(beatmapInfo) { this.store = store; + this.audioManager = audioManager; } protected override Beatmap GetBeatmap() @@ -100,6 +104,22 @@ namespace osu.Game.Beatmaps return storyboard; } + + protected override Skin GetSkin() + { + Skin skin; + try + { + skin = new BeatmapSkin(BeatmapInfo, store, audioManager); + } + catch (Exception e) + { + Logger.Error(e, "Skin failed to load"); + skin = new DefaultSkin(); + } + + return skin; + } } } } diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 8a2a7b01a1..5c0ad7685b 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -14,6 +14,7 @@ using osu.Framework.IO.File; using System.IO; using osu.Game.IO.Serialization; using System.Diagnostics; +using osu.Game.Skinning; namespace osu.Game.Beatmaps { @@ -40,6 +41,7 @@ namespace osu.Game.Beatmaps track = new AsyncLazy(populateTrack); waveform = new AsyncLazy(populateWaveform); storyboard = new AsyncLazy(populateStoryboard); + skin = new AsyncLazy(populateSkin); } /// @@ -56,6 +58,7 @@ namespace osu.Game.Beatmaps protected abstract Beatmap GetBeatmap(); protected abstract Texture GetBackground(); protected abstract Track GetTrack(); + protected virtual Skin GetSkin() => new DefaultSkin(); protected virtual Waveform GetWaveform() => new Waveform(); protected virtual Storyboard GetStoryboard() => new Storyboard { BeatmapInfo = BeatmapInfo }; @@ -109,6 +112,13 @@ namespace osu.Game.Beatmaps private Storyboard populateStoryboard() => GetStoryboard(); + public bool SkinLoaded => skin.IsResultAvailable; + public Skin Skin => skin.Value.Result; + public async Task GetSkinAsync() => await skin.Value; + private readonly AsyncLazy skin; + + private Skin populateSkin() => GetSkin(); + public void TransferTo(WorkingBeatmap other) { if (track.IsResultAvailable && Track != null && BeatmapInfo.AudioEquals(other.BeatmapInfo)) @@ -123,6 +133,7 @@ namespace osu.Game.Beatmaps if (BackgroundLoaded) Background?.Dispose(); if (WaveformLoaded) Waveform?.Dispose(); if (StoryboardLoaded) Storyboard?.Dispose(); + if (SkinLoaded) Skin?.Dispose(); } /// diff --git a/osu.Game/Database/IHasFiles.cs b/osu.Game/Database/IHasFiles.cs index deaf75360c..faf3f16dfe 100644 --- a/osu.Game/Database/IHasFiles.cs +++ b/osu.Game/Database/IHasFiles.cs @@ -10,6 +10,8 @@ namespace osu.Game.Database /// /// The model representing a file. public interface IHasFiles + where TFile : INamedFileInfo + { List Files { get; set; } } diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 45fd45b4b5..f5d7d15a47 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -113,7 +113,7 @@ namespace osu.Game dependencies.Cache(RulesetStore = new RulesetStore(contextFactory)); dependencies.Cache(FileStore = new FileStore(contextFactory, Host.Storage)); - dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, contextFactory, RulesetStore, api, Host)); + 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)); diff --git a/osu.Game/Skinning/BeatmapSkin.cs b/osu.Game/Skinning/BeatmapSkin.cs new file mode 100644 index 0000000000..815aac2f64 --- /dev/null +++ b/osu.Game/Skinning/BeatmapSkin.cs @@ -0,0 +1,31 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.IO; +using osu.Framework.Audio; +using osu.Framework.Graphics.Textures; +using osu.Framework.IO.Stores; +using osu.Game.Beatmaps; + +namespace osu.Game.Skinning +{ + public class BeatmapSkin : LegacySkin + { + public BeatmapSkin(BeatmapInfo beatmap, IResourceStore storage, AudioManager audioManager) + : base(new SkinInfo { Name = beatmap.ToString(), Creator = beatmap.Metadata.Author.ToString() }) + { + storage = new LegacySkinResourceStore(beatmap.BeatmapSet, storage); + + Samples = audioManager.GetSampleManager(storage); + + Textures = new TextureStore(new RawTextureLoaderStore(storage)); + + var decoder = new LegacySkinDecoder(); + + using (StreamReader reader = new StreamReader(storage.GetStream(beatmap.Path))) + { + Configuration = decoder.Decode(reader); + } + } + } +} diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index b531d791b0..cfee2cfab2 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -10,24 +10,24 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; +using osu.Game.Database; namespace osu.Game.Skinning { public class LegacySkin : Skin { - private readonly TextureStore textures; + protected TextureStore Textures; - private readonly SampleManager samples; + protected SampleManager Samples; public LegacySkin(SkinInfo skin, IResourceStore storage, AudioManager audioManager) - : base(skin) + : this(skin) { - storage = new LegacySkinResourceStore(skin, storage); - samples = audioManager.GetSampleManager(storage); - textures = new TextureStore(new RawTextureLoaderStore(storage)); + storage = new LegacySkinResourceStore(skin, storage); + Samples = audioManager.GetSampleManager(storage); + Textures = new TextureStore(new RawTextureLoaderStore(storage)); Stream stream = storage.GetStream("skin.ini"); - if (stream != null) using (StreamReader reader = new StreamReader(stream)) Configuration = new LegacySkinDecoder().Decode(reader); @@ -35,6 +35,10 @@ namespace osu.Game.Skinning Configuration = new SkinConfiguration(); } + protected LegacySkin(SkinInfo skin) : base(skin) + { + } + public override Drawable GetDrawableComponent(string componentName) { switch (componentName) @@ -53,17 +57,18 @@ namespace osu.Game.Skinning break; } - var texture = textures.Get(componentName); + var texture = Textures.Get(componentName); if (texture == null) return null; return new Sprite { Texture = texture }; } - public override SampleChannel GetSample(string sampleName) => samples.Get(sampleName); + public override SampleChannel GetSample(string sampleName) => Samples.Get(sampleName); - private class LegacySkinResourceStore : IResourceStore + protected class LegacySkinResourceStore : IResourceStore + where T : INamedFileInfo { - private readonly SkinInfo skin; + private readonly IHasFiles source; private readonly IResourceStore underlyingStore; private string getPathForFile(string filename) @@ -72,14 +77,14 @@ namespace osu.Game.Skinning string lastPiece = filename.Split('/').Last(); - var file = skin.Files.FirstOrDefault(f => + var file = source.Files.FirstOrDefault(f => string.Equals(hasExtension ? f.Filename : Path.GetFileNameWithoutExtension(f.Filename), lastPiece, StringComparison.InvariantCultureIgnoreCase)); return file?.FileInfo.StoragePath; } - public LegacySkinResourceStore(SkinInfo skin, IResourceStore underlyingStore) + public LegacySkinResourceStore(IHasFiles source, IResourceStore underlyingStore) { - this.skin = skin; + this.source = source; this.underlyingStore = underlyingStore; } diff --git a/osu.Game/Skinning/Skin.cs b/osu.Game/Skinning/Skin.cs index 7b4e894dfd..53bcf30b0e 100644 --- a/osu.Game/Skinning/Skin.cs +++ b/osu.Game/Skinning/Skin.cs @@ -1,12 +1,13 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using osu.Framework.Audio.Sample; using osu.Framework.Graphics; namespace osu.Game.Skinning { - public abstract class Skin + public abstract class Skin : IDisposable { public readonly SkinInfo SkinInfo; @@ -20,5 +21,29 @@ namespace osu.Game.Skinning { SkinInfo = skin; } + + #region Disposal + + ~Skin() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private bool isDisposed; + + protected virtual void Dispose(bool isDisposing) + { + if (isDisposed) + return; + isDisposed = true; + } + + #endregion } } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index b325e52ed1..50c272e827 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -871,6 +871,7 @@ +