1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-18 22:30:53 +08:00

Compare commits

...

8 Commits

19 changed files with 810 additions and 19 deletions
@@ -6,30 +6,24 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Game.Skinning;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
public class ApproachCircle : Container
{
private readonly Sprite approachCircle;
public ApproachCircle()
{
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
AutoSizeAxes = Axes.Both;
Children = new Drawable[]
{
approachCircle = new Sprite()
};
RelativeSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
approachCircle.Texture = textures.Get(@"Play/osu/approachcircle");
Child = new SkinnableDrawable("Play/osu/approachcircle", name => new Sprite { Texture = textures.Get(name) });
}
}
}
@@ -29,8 +29,8 @@ namespace osu.Game.Rulesets.Taiko.Audio
{
mappings[s.Time] = new DrumSample
{
Centre = s.GetSampleInfo().GetChannel(audio.Sample, "Taiko"),
Rim = s.GetSampleInfo(SampleInfo.HIT_CLAP).GetChannel(audio.Sample, "Taiko")
Centre = s.GetSampleInfo().GetChannel(audio.Sample.Get, "Taiko"),
Rim = s.GetSampleInfo(SampleInfo.HIT_CLAP).GetChannel(audio.Sample.Get, "Taiko")
};
}
}
+3 -3
View File
@@ -14,16 +14,16 @@ namespace osu.Game.Audio
public const string HIT_NORMAL = @"hitnormal";
public const string HIT_CLAP = @"hitclap";
public SampleChannel GetChannel(SampleManager manager, string resourceNamespace = null)
public SampleChannel GetChannel(Func<string, SampleChannel> getChannel, string resourceNamespace = null)
{
SampleChannel channel = null;
if (resourceNamespace != null)
channel = manager.Get($"Gameplay/{resourceNamespace}/{Bank}-{Name}");
channel = getChannel($"Gameplay/{resourceNamespace}/{Bank}-{Name}");
// try without namespace as a fallback.
if (channel == null)
channel = manager.Get($"Gameplay/{Bank}-{Name}");
channel = getChannel($"Gameplay/{Bank}-{Name}");
if (channel != null)
channel.Volume.Value = Volume / 100.0;
+2
View File
@@ -13,6 +13,7 @@ using osu.Game.IO;
using osu.Game.Rulesets;
using DatabasedKeyBinding = osu.Game.Input.Bindings.DatabasedKeyBinding;
using LogLevel = Microsoft.Extensions.Logging.LogLevel;
using osu.Game.Skinning;
namespace osu.Game.Database
{
@@ -26,6 +27,7 @@ namespace osu.Game.Database
public DbSet<DatabasedSetting> DatabasedSetting { get; set; }
public DbSet<FileInfo> FileInfo { get; set; }
public DbSet<RulesetInfo> RulesetInfo { get; set; }
public DbSet<SkinInfo> SkinInfo { get; set; }
private readonly string connectionString;
+379
View File
@@ -0,0 +1,379 @@
// <auto-generated />
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage;
using osu.Game.Database;
using System;
namespace osu.Game.Migrations
{
[DbContext(typeof(OsuDbContext))]
[Migration("20180219060912_AddSkins")]
partial class AddSkins
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.0.0-rtm-26452");
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<float>("ApproachRate");
b.Property<float>("CircleSize");
b.Property<float>("DrainRate");
b.Property<float>("OverallDifficulty");
b.Property<float>("SliderMultiplier");
b.Property<float>("SliderTickRate");
b.HasKey("ID");
b.ToTable("BeatmapDifficulty");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("AudioLeadIn");
b.Property<int>("BaseDifficultyID");
b.Property<int>("BeatDivisor");
b.Property<int>("BeatmapSetInfoID");
b.Property<bool>("Countdown");
b.Property<double>("DistanceSpacing");
b.Property<int>("GridSize");
b.Property<string>("Hash");
b.Property<bool>("Hidden");
b.Property<bool>("LetterboxInBreaks");
b.Property<string>("MD5Hash");
b.Property<int?>("MetadataID");
b.Property<int?>("OnlineBeatmapID");
b.Property<string>("Path");
b.Property<int>("RulesetID");
b.Property<bool>("SpecialStyle");
b.Property<float>("StackLeniency");
b.Property<double>("StarDifficulty");
b.Property<string>("StoredBookmarks");
b.Property<double>("TimelineZoom");
b.Property<string>("Version");
b.Property<bool>("WidescreenStoryboard");
b.HasKey("ID");
b.HasIndex("BaseDifficultyID");
b.HasIndex("BeatmapSetInfoID");
b.HasIndex("Hash")
.IsUnique();
b.HasIndex("MD5Hash")
.IsUnique();
b.HasIndex("MetadataID");
b.HasIndex("OnlineBeatmapID")
.IsUnique();
b.HasIndex("RulesetID");
b.ToTable("BeatmapInfo");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Artist");
b.Property<string>("ArtistUnicode");
b.Property<string>("AudioFile");
b.Property<string>("AuthorString")
.HasColumnName("Author");
b.Property<string>("BackgroundFile");
b.Property<int>("PreviewTime");
b.Property<string>("Source");
b.Property<string>("Tags");
b.Property<string>("Title");
b.Property<string>("TitleUnicode");
b.HasKey("ID");
b.ToTable("BeatmapMetadata");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("BeatmapSetInfoID");
b.Property<int>("FileInfoID");
b.Property<string>("Filename")
.IsRequired();
b.HasKey("ID");
b.HasIndex("BeatmapSetInfoID");
b.HasIndex("FileInfoID");
b.ToTable("BeatmapSetFileInfo");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<bool>("DeletePending");
b.Property<string>("Hash");
b.Property<int?>("MetadataID");
b.Property<int?>("OnlineBeatmapSetID");
b.Property<bool>("Protected");
b.HasKey("ID");
b.HasIndex("DeletePending");
b.HasIndex("Hash")
.IsUnique();
b.HasIndex("MetadataID");
b.HasIndex("OnlineBeatmapSetID")
.IsUnique();
b.ToTable("BeatmapSetInfo");
});
modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("IntKey")
.HasColumnName("Key");
b.Property<int?>("RulesetID");
b.Property<string>("StringValue")
.HasColumnName("Value");
b.Property<int?>("Variant");
b.HasKey("ID");
b.HasIndex("RulesetID", "Variant");
b.ToTable("Settings");
});
modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("IntAction")
.HasColumnName("Action");
b.Property<string>("KeysString")
.HasColumnName("Keys");
b.Property<int?>("RulesetID");
b.Property<int?>("Variant");
b.HasKey("ID");
b.HasIndex("IntAction");
b.HasIndex("RulesetID", "Variant");
b.ToTable("KeyBinding");
});
modelBuilder.Entity("osu.Game.IO.FileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Hash");
b.Property<int>("ReferenceCount");
b.HasKey("ID");
b.HasIndex("Hash")
.IsUnique();
b.HasIndex("ReferenceCount");
b.ToTable("FileInfo");
});
modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b =>
{
b.Property<int?>("ID")
.ValueGeneratedOnAdd();
b.Property<bool>("Available");
b.Property<string>("InstantiationInfo");
b.Property<string>("Name");
b.Property<string>("ShortName");
b.HasKey("ID");
b.HasIndex("Available");
b.HasIndex("ShortName")
.IsUnique();
b.ToTable("RulesetInfo");
});
modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("FileInfoID");
b.Property<string>("Filename")
.IsRequired();
b.Property<int>("SkinInfoID");
b.HasKey("ID");
b.HasIndex("FileInfoID");
b.HasIndex("SkinInfoID");
b.ToTable("SkinFileInfo");
});
modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Creator");
b.Property<bool>("DeletePending");
b.Property<string>("Name");
b.HasKey("ID");
b.ToTable("SkinInfo");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty")
.WithMany()
.HasForeignKey("BaseDifficultyID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet")
.WithMany("Beatmaps")
.HasForeignKey("BeatmapSetInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata")
.WithMany("Beatmaps")
.HasForeignKey("MetadataID");
b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset")
.WithMany()
.HasForeignKey("RulesetID")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo")
.WithMany("Files")
.HasForeignKey("BeatmapSetInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
.WithMany()
.HasForeignKey("FileInfoID")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata")
.WithMany("BeatmapSets")
.HasForeignKey("MetadataID");
});
modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b =>
{
b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
.WithMany()
.HasForeignKey("FileInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Skinning.SkinInfo")
.WithMany("Files")
.HasForeignKey("SkinInfoID")
.OnDelete(DeleteBehavior.Cascade);
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,73 @@
using Microsoft.EntityFrameworkCore.Migrations;
using System;
using System.Collections.Generic;
namespace osu.Game.Migrations
{
public partial class AddSkins : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "SkinInfo",
columns: table => new
{
ID = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
Creator = table.Column<string>(type: "TEXT", nullable: true),
DeletePending = table.Column<bool>(type: "INTEGER", nullable: false),
Name = table.Column<string>(type: "TEXT", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_SkinInfo", x => x.ID);
});
migrationBuilder.CreateTable(
name: "SkinFileInfo",
columns: table => new
{
ID = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
FileInfoID = table.Column<int>(type: "INTEGER", nullable: false),
Filename = table.Column<string>(type: "TEXT", nullable: false),
SkinInfoID = table.Column<int>(type: "INTEGER", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_SkinFileInfo", x => x.ID);
table.ForeignKey(
name: "FK_SkinFileInfo_FileInfo_FileInfoID",
column: x => x.FileInfoID,
principalTable: "FileInfo",
principalColumn: "ID",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_SkinFileInfo_SkinInfo_SkinInfoID",
column: x => x.SkinInfoID,
principalTable: "SkinInfo",
principalColumn: "ID",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_SkinFileInfo_FileInfoID",
table: "SkinFileInfo",
column: "FileInfoID");
migrationBuilder.CreateIndex(
name: "IX_SkinFileInfo_SkinInfoID",
table: "SkinFileInfo",
column: "SkinInfoID");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "SkinFileInfo");
migrationBuilder.DropTable(
name: "SkinInfo");
}
}
}
@@ -281,6 +281,43 @@ namespace osu.Game.Migrations
b.ToTable("RulesetInfo");
});
modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("FileInfoID");
b.Property<string>("Filename")
.IsRequired();
b.Property<int>("SkinInfoID");
b.HasKey("ID");
b.HasIndex("FileInfoID");
b.HasIndex("SkinInfoID");
b.ToTable("SkinFileInfo");
});
modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Creator");
b.Property<bool>("DeletePending");
b.Property<string>("Name");
b.HasKey("ID");
b.ToTable("SkinInfo");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty")
@@ -322,6 +359,19 @@ namespace osu.Game.Migrations
.WithMany("BeatmapSets")
.HasForeignKey("MetadataID");
});
modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b =>
{
b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
.WithMany()
.HasForeignKey("FileInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Skinning.SkinInfo")
.WithMany("Files")
.HasForeignKey("SkinInfoID")
.OnDelete(DeleteBehavior.Cascade);
});
#pragma warning restore 612, 618
}
}
+2
View File
@@ -187,7 +187,9 @@ namespace osu.Game
CursorOverrideContainer.CanShowCursor = currentScreen?.CursorVisible ?? false;
// hook up notifications to components.
SkinManager.PostNotification = n => notifications?.Post(n);
BeatmapManager.PostNotification = n => notifications?.Post(n);
BeatmapManager.GetStableStorage = GetStorageForStableInstall;
AddRange(new Drawable[]
+6
View File
@@ -30,6 +30,7 @@ using osu.Game.Input.Bindings;
using osu.Game.IO;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning;
namespace osu.Game
{
@@ -39,6 +40,8 @@ namespace osu.Game
protected BeatmapManager BeatmapManager;
protected SkinManager SkinManager;
protected RulesetStore RulesetStore;
protected FileStore FileStore;
@@ -103,6 +106,8 @@ namespace osu.Game
runMigrations();
dependencies.Cache(SkinManager = new SkinManager(Host.Storage, contextFactory, Host, Audio));
dependencies.Cache(API = new APIAccess
{
Username = LocalConfig.Get<string>(OsuSetting.Username),
@@ -120,6 +125,7 @@ namespace osu.Game
fileImporters.Add(BeatmapManager);
fileImporters.Add(ScoreStore);
fileImporters.Add(SkinManager);
//this completely overrides the framework default. will need to change once we make a proper FontStore.
dependencies.Cache(Fonts = new FontStore { ScaleAdjust = 100 });
@@ -17,6 +17,7 @@ using osu.Framework.Configuration;
using OpenTK;
using osu.Framework.Graphics.Primitives;
using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning;
namespace osu.Game.Rulesets.Objects.Drawables
{
@@ -82,8 +83,10 @@ namespace osu.Game.Rulesets.Objects.Drawables
HitObject = hitObject;
}
private readonly Bindable<Skin> skin = new Bindable<Skin>();
[BackgroundDependencyLoader]
private void load(AudioManager audio)
private void load(AudioManager audio, SkinManager skins)
{
var samples = GetSamples();
if (samples.Any())
@@ -101,12 +104,17 @@ namespace osu.Game.Rulesets.Objects.Drawables
Volume = s.Volume > 0 ? s.Volume : HitObject.SampleControlPoint.SampleVolume
};
SampleChannel channel = localSampleInfo.GetChannel(audio.Sample, SampleNamespace);
void loadSamples(Skin skin)
{
SampleChannel channel = localSampleInfo.GetChannel(skin.GetSample, SampleNamespace) ?? localSampleInfo.GetChannel(audio.Sample.Get, SampleNamespace);
if (channel == null)
continue;
if (channel == null) return;
Samples.Add(channel);
Samples.Add(channel);
}
skin.ValueChanged += loadSamples;
skin.BindTo(skins.CurrentSkin);
}
}
}
+17
View File
@@ -0,0 +1,17 @@
using osu.Framework.Audio.Sample;
using osu.Framework.Graphics;
namespace osu.Game.Skinning
{
public class DefaultSkin : Skin
{
public DefaultSkin()
: base("Default")
{
}
public override Drawable GetDrawableComponent(string componentName) => null;
public override SampleChannel GetSample(string sampleName) => null;
}
}
+52
View File
@@ -0,0 +1,52 @@
// 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.IO;
using System.Linq;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.IO.Stores;
namespace osu.Game.Skinning
{
public class LegacySkin : Skin
{
private readonly TextureStore textures;
public SkinInfo SkinInfo;
private readonly SampleManager samples;
public LegacySkin(SkinInfo skin, IResourceStore<byte[]> storage, AudioManager audioManager)
: base(skin.Name)
{
SkinInfo = skin;
var audioStore = new ResourceStore<byte[]>(storage);
samples = audioManager.GetSampleManager(audioStore);
textures = new TextureStore(new RawTextureLoaderStore(storage));
}
private string getPathForFile(string filename) => SkinInfo.Files.FirstOrDefault(f => string.Equals(Path.GetFileNameWithoutExtension(f.Filename), filename, StringComparison.InvariantCultureIgnoreCase))?.FileInfo.StoragePath;
public override Drawable GetDrawableComponent(string componentName)
{
var texture = textures.Get(getPathForFile(componentName.Split('/').Last()));
if (texture == null) return null;
return new Sprite
{
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fit,
Texture = texture,
};
}
public override SampleChannel GetSample(string sampleName) => samples.Get(getPathForFile(sampleName.Split('/').Last()));
}
}
+22
View File
@@ -0,0 +1,22 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Audio.Sample;
using osu.Framework.Graphics;
namespace osu.Game.Skinning
{
public abstract class Skin
{
public string Name { get; }
public abstract Drawable GetDrawableComponent(string componentName);
public abstract SampleChannel GetSample(string sampleName);
protected Skin(string name)
{
Name = name;
}
}
}
+25
View File
@@ -0,0 +1,25 @@
// 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.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using osu.Game.Database;
using osu.Game.IO;
namespace osu.Game.Skinning
{
public class SkinFileInfo : INamedFileInfo
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
public int SkinInfoID { get; set; }
public int FileInfoID { get; set; }
public FileInfo FileInfo { get; set; }
[Required]
public string Filename { get; set; }
}
}
+23
View File
@@ -0,0 +1,23 @@
// 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.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using osu.Game.Database;
namespace osu.Game.Skinning
{
public class SkinInfo : IHasFiles<SkinFileInfo>, IHasPrimaryKey, ISoftDelete
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
public string Name { get; set; }
public string Creator { get; set; }
public List<SkinFileInfo> Files { get; set; }
public bool DeletePending { get; set; }
}
}
+56
View File
@@ -0,0 +1,56 @@
// 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.Collections.Generic;
using System.Linq;
using osu.Framework.Audio;
using osu.Framework.Configuration;
using osu.Framework.Platform;
using osu.Game.Database;
using osu.Game.IO.Archives;
namespace osu.Game.Skinning
{
public class SkinManager : ArchiveModelManager<SkinInfo, SkinFileInfo>
{
private readonly AudioManager audio;
public Bindable<Skin> CurrentSkin = new Bindable<Skin>(new DefaultSkin());
public override string[] HandledExtensions => new[] { ".osk" };
/// <summary>
/// Returns a list of all usable <see cref="SkinInfo"/>s.
/// </summary>
/// <returns>A list of available <see cref="SkinInfo"/>.</returns>
public List<SkinInfo> GetAllUsableSkins() => ModelStore.ConsumableItems.Where(s => !s.DeletePending).ToList();
protected override SkinInfo CreateModel(ArchiveReader archive) => new SkinInfo
{
Name = archive.Name
};
/// <summary>
/// Retrieve a <see cref="Skin"/> instance for the provided <see cref="SkinInfo"/>
/// </summary>
/// <param name="skinInfo">The skin to lookup.</param>
/// <returns>A <see cref="Skin"/> instance correlating to the provided <see cref="SkinInfo"/>.</returns>
public Skin GetSkin(SkinInfo skinInfo)
{
return new LegacySkin(skinInfo, Files.Store, audio);
}
private SkinStore store;
public SkinManager(Storage storage, DatabaseContextFactory contextFactory, IIpcHost importHost, AudioManager audio)
: base(storage, contextFactory, new SkinStore(contextFactory, storage), importHost)
{
this.audio = audio;
// use the first available skin.
var userSkin = GetAllUsableSkins().FirstOrDefault();
if (userSkin != null)
CurrentSkin.Value = GetSkin(userSkin);
}
}
}
+22
View File
@@ -0,0 +1,22 @@
// 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.Linq;
using Microsoft.EntityFrameworkCore;
using osu.Framework.Platform;
using osu.Game.Database;
namespace osu.Game.Skinning
{
public class SkinStore : MutableDatabaseBackedStore<SkinInfo>
{
public SkinStore(DatabaseContextFactory contextFactory, Storage storage = null)
: base(contextFactory, storage)
{
}
protected override IQueryable<SkinInfo> AddIncludesForConsumption(IQueryable<SkinInfo> query) =>
base.AddIncludesForConsumption(query)
.Include(s => s.Files).ThenInclude(f => f.FileInfo);
}
}
+48
View File
@@ -0,0 +1,48 @@
// 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.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
namespace osu.Game.Skinning
{
public class SkinnableDrawable : SkinnableDrawable<Drawable>
{
public SkinnableDrawable(string name, Func<string,Drawable> defaultImplementation)
: base(name, defaultImplementation)
{
RelativeSizeAxes = Axes.Both;
}
}
public class SkinnableDrawable<T> : CompositeDrawable
where T : Drawable
{
private Bindable<Skin> skin;
protected Func<string,T> CreateDefault;
public string ComponentName { get; set; }
public SkinnableDrawable(string name, Func<string,T> defaultImplementation)
{
ComponentName = name;
CreateDefault = defaultImplementation;
}
[BackgroundDependencyLoader]
private void load(SkinManager skinManager)
{
skin = skinManager.CurrentSkin.GetBoundCopy();
skin.ValueChanged += updateComponent;
skin.TriggerChange();
}
private void updateComponent(Skin skin)
{
InternalChild = skin.GetDrawableComponent(ComponentName) ?? CreateDefault(Name);
}
}
}
+12
View File
@@ -322,6 +322,10 @@
<Compile Include="Migrations\20171209034410_AddRulesetInfoShortName.Designer.cs">
<DependentUpon>20171209034410_AddRulesetInfoShortName.cs</DependentUpon>
</Compile>
<Compile Include="Migrations\20180219060912_AddSkins.cs" />
<Compile Include="Migrations\20180219060912_AddSkins.designer.cs">
<DependentUpon>20180219060912_AddSkins.cs</DependentUpon>
</Compile>
<Compile Include="Migrations\OsuDbContextModelSnapshot.cs" />
<Compile Include="Online\API\Requests\GetBeatmapSetRequest.cs" />
<Compile Include="Online\Chat\DrawableLinkCompiler.cs" />
@@ -849,6 +853,14 @@
<Compile Include="Screens\Tournament\Teams\DrawingsTeam.cs" />
<Compile Include="Screens\Tournament\Teams\ITeamList.cs" />
<Compile Include="Screens\Tournament\Teams\StorageBackedTeamList.cs" />
<Compile Include="Skinning\DefaultSkin.cs" />
<Compile Include="Skinning\LegacySkin.cs" />
<Compile Include="Skinning\Skin.cs" />
<Compile Include="Skinning\SkinFileInfo.cs" />
<Compile Include="Skinning\SkinInfo.cs" />
<Compile Include="Skinning\SkinManager.cs" />
<Compile Include="Skinning\SkinnableDrawable.cs" />
<Compile Include="Skinning\SkinStore.cs" />
<Compile Include="Storyboards\CommandLoop.cs" />
<Compile Include="Storyboards\CommandTimeline.cs" />
<Compile Include="Storyboards\CommandTimelineGroup.cs" />