1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-14 22:22:54 +08:00

Merge branch 'beatmap-as-a-skin'

This commit is contained in:
Dean Herbert 2018-03-20 16:24:41 +09:00
commit 52472e005e
16 changed files with 122 additions and 65 deletions

View File

@ -16,11 +16,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
{
public override void PostProcess(Beatmap<CatchHitObject> 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;
}

View File

@ -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];
}
}

View File

@ -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))
{

View File

@ -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()
{

View File

@ -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
});

View File

@ -1,7 +1,6 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// 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
/// <summary>
/// A Beatmap containing converted HitObjects.
/// </summary>
public class Beatmap<T> : IJsonSerializable, IHasComboColours
public class Beatmap<T> : IJsonSerializable
where T : HitObject
{
public BeatmapInfo BeatmapInfo = new BeatmapInfo();
public ControlPointInfo ControlPointInfo = new ControlPointInfo();
public List<BreakPeriod> Breaks = new List<BreakPeriod>();
public List<Color4> ComboColours { get; set; } = new List<Color4>
{
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)

View File

@ -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;
}

View File

@ -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<DownloadBeatmapSetRequest> currentDownloads = new List<DownloadBeatmapSetRequest>();
/// <summary>
@ -62,7 +65,7 @@ namespace osu.Game.Beatmaps
/// </summary>
public Func<Storage> 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);

View File

@ -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<byte[]> store;
private readonly AudioManager audioManager;
public BeatmapManagerWorkingBeatmap(IResourceStore<byte[]> store, BeatmapInfo beatmapInfo)
public BeatmapManagerWorkingBeatmap(IResourceStore<byte[]> 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;
}
}
}
}

View File

@ -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<Track>(populateTrack);
waveform = new AsyncLazy<Waveform>(populateWaveform);
storyboard = new AsyncLazy<Storyboard>(populateStoryboard);
skin = new AsyncLazy<Skin>(populateSkin);
}
/// <summary>
@ -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<Skin> GetSkinAsync() => await skin.Value;
private readonly AsyncLazy<Skin> 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();
}
/// <summary>

View File

@ -10,6 +10,8 @@ namespace osu.Game.Database
/// </summary>
/// <typeparam name="TFile">The model representing a file.</typeparam>
public interface IHasFiles<TFile>
where TFile : INamedFileInfo
{
List<TFile> Files { get; set; }
}

View File

@ -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));

View File

@ -0,0 +1,31 @@
// 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.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<byte[]> storage, AudioManager audioManager)
: base(new SkinInfo { Name = beatmap.ToString(), Creator = beatmap.Metadata.Author.ToString() })
{
storage = new LegacySkinResourceStore<BeatmapSetFileInfo>(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);
}
}
}
}

View File

@ -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<byte[]> 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<SkinFileInfo>(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<byte[]>
protected class LegacySkinResourceStore<T> : IResourceStore<byte[]>
where T : INamedFileInfo
{
private readonly SkinInfo skin;
private readonly IHasFiles<T> source;
private readonly IResourceStore<byte[]> 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<byte[]> underlyingStore)
public LegacySkinResourceStore(IHasFiles<T> source, IResourceStore<byte[]> underlyingStore)
{
this.skin = skin;
this.source = source;
this.underlyingStore = underlyingStore;
}

View File

@ -1,12 +1,13 @@
// 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 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
}
}

View File

@ -871,6 +871,7 @@
<Compile Include="Screens\Tournament\Teams\DrawingsTeam.cs" />
<Compile Include="Screens\Tournament\Teams\ITeamList.cs" />
<Compile Include="Screens\Tournament\Teams\StorageBackedTeamList.cs" />
<Compile Include="Skinning\BeatmapSkin.cs" />
<Compile Include="Skinning\DefaultSkin.cs" />
<Compile Include="Skinning\LegacySkin.cs" />
<Compile Include="Skinning\LegacySkinDecoder.cs" />