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

Merge pull request #1929 from smoogipoo/ruleset-configs

Implement ruleset-specific config managers and attach them to the global OnScreenDisplay
This commit is contained in:
Dean Herbert 2018-01-26 17:54:38 +09:00 committed by GitHub
commit 545a4bd082
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 779 additions and 47 deletions

@ -1 +1 @@
Subproject commit 736a139a748eba7cebea41a09b404d47ca589522 Subproject commit 209021fb491e21625127be5dbf5efb4c409e6f06

View File

@ -0,0 +1,34 @@
// 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.Configuration.Tracking;
using osu.Game.Configuration;
using osu.Game.Rulesets.Configuration;
namespace osu.Game.Rulesets.Mania.Configuration
{
public class ManiaConfigManager : RulesetConfigManager<ManiaSetting>
{
public ManiaConfigManager(SettingsStore settings, RulesetInfo ruleset, int variant)
: base(settings, ruleset, variant)
{
}
protected override void InitialiseDefaults()
{
base.InitialiseDefaults();
Set(ManiaSetting.ScrollTime, 1500.0, 50.0, 10000.0, 50.0);
}
public override TrackedSettings CreateTrackedSettings() => new TrackedSettings
{
new TrackedSetting<double>(ManiaSetting.ScrollTime, v => new SettingDescription(v, "Scroll Time", $"{v}ms"))
};
}
public enum ManiaSetting
{
ScrollTime
}
}

View File

@ -7,10 +7,12 @@ using osu.Framework.Graphics.Containers;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Configuration; using osu.Framework.Configuration;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Mania.Configuration;
using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Rulesets.UI.Scrolling;
namespace osu.Game.Rulesets.Mania.UI namespace osu.Game.Rulesets.Mania.UI
@ -78,6 +80,12 @@ namespace osu.Game.Rulesets.Mania.UI
return null; return null;
} }
[BackgroundDependencyLoader]
private void load(ManiaConfigManager maniaConfig)
{
maniaConfig.BindWith(ManiaSetting.ScrollTime, VisibleTimeRange);
}
internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement) internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
{ {
getStageByColumn(((ManiaHitObject)judgedObject.HitObject).Column).OnJudgement(judgedObject, judgement); getStageByColumn(((ManiaHitObject)judgedObject.HitObject).Column).OnJudgement(judgedObject, judgement);

View File

@ -10,8 +10,11 @@ using osu.Framework.Input;
using osu.Framework.MathUtils; using osu.Framework.MathUtils;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Configuration;
using osu.Game.Rulesets.Configuration;
using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Mods; using osu.Game.Rulesets.Mania.Mods;
using osu.Game.Rulesets.Mania.Configuration;
using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Mania.Replays; using osu.Game.Rulesets.Mania.Replays;
@ -77,11 +80,9 @@ namespace osu.Game.Rulesets.Mania.UI
public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(this); public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(this);
public override PassThroughInputManager CreateInputManager() public override int Variant => (int)(Mods.OfType<IPlayfieldTypeMod>().FirstOrDefault()?.PlayfieldType ?? PlayfieldType.Single) + Beatmap.TotalColumns;
{
var variantType = Mods.OfType<IPlayfieldTypeMod>().FirstOrDefault()?.PlayfieldType ?? PlayfieldType.Single; public override PassThroughInputManager CreateInputManager() => new ManiaInputManager(Ruleset.RulesetInfo, Variant);
return new ManiaInputManager(Ruleset.RulesetInfo, (int)variantType + Beatmap.TotalColumns);
}
protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter() => new ManiaBeatmapConverter(IsForCurrentRuleset, WorkingBeatmap.Beatmap); protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter() => new ManiaBeatmapConverter(IsForCurrentRuleset, WorkingBeatmap.Beatmap);
@ -103,5 +104,7 @@ namespace osu.Game.Rulesets.Mania.UI
protected override Vector2 GetPlayfieldAspectAdjust() => new Vector2(1, 0.8f); protected override Vector2 GetPlayfieldAspectAdjust() => new Vector2(1, 0.8f);
protected override FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay, this); protected override FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay, this);
protected override IRulesetConfigManager CreateConfig(Ruleset ruleset, SettingsStore settings) => new ManiaConfigManager(settings, Ruleset.RulesetInfo, Variant);
} }
} }

View File

@ -58,6 +58,7 @@
<Compile Include="Beatmaps\Patterns\Legacy\PatternType.cs" /> <Compile Include="Beatmaps\Patterns\Legacy\PatternType.cs" />
<Compile Include="Beatmaps\ManiaBeatmapConverter.cs" /> <Compile Include="Beatmaps\ManiaBeatmapConverter.cs" />
<Compile Include="Beatmaps\Patterns\Pattern.cs" /> <Compile Include="Beatmaps\Patterns\Pattern.cs" />
<Compile Include="Configuration\ManiaConfigManager.cs" />
<Compile Include="MathUtils\FastRandom.cs" /> <Compile Include="MathUtils\FastRandom.cs" />
<Compile Include="Judgements\HitWindows.cs" /> <Compile Include="Judgements\HitWindows.cs" />
<Compile Include="Judgements\HoldNoteTailJudgement.cs" /> <Compile Include="Judgements\HoldNoteTailJudgement.cs" />

View File

@ -0,0 +1,71 @@
// 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.Configuration;
using osu.Game.Rulesets;
namespace osu.Game.Configuration
{
public abstract class DatabasedConfigManager<T> : ConfigManager<T>
where T : struct
{
private readonly SettingsStore settings;
private readonly int variant;
private readonly List<DatabasedSetting> databasedSettings;
private readonly RulesetInfo ruleset;
protected DatabasedConfigManager(SettingsStore settings, RulesetInfo ruleset = null, int variant = 0)
{
this.settings = settings;
this.ruleset = ruleset;
this.variant = variant;
databasedSettings = settings.Query(ruleset?.ID, variant);
InitialiseDefaults();
}
protected override void PerformLoad()
{
}
protected override bool PerformSave()
{
return true;
}
protected override void AddBindable<TBindable>(T lookup, Bindable<TBindable> bindable)
{
base.AddBindable(lookup, bindable);
var setting = databasedSettings.FirstOrDefault(s => (int)s.Key == (int)(object)lookup);
if (setting != null)
{
bindable.Parse(setting.Value);
}
else
{
settings.Update(setting = new DatabasedSetting
{
Key = lookup,
Value = bindable.Value,
RulesetID = ruleset?.ID,
Variant = variant,
});
databasedSettings.Add(setting);
}
bindable.ValueChanged += v =>
{
setting.Value = v;
settings.Update(setting);
};
}
}
}

View File

@ -0,0 +1,51 @@
// 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.Schema;
using osu.Game.Database;
namespace osu.Game.Configuration
{
[Table("Settings")]
public class DatabasedSetting : IHasPrimaryKey
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
public int? RulesetID { get; set; }
public int? Variant { get; set; }
[Column("Key")]
public int IntKey
{
get => (int)Key;
private set => Key = value;
}
[Column("Value")]
public string StringValue
{
get => Value.ToString();
set => Value = value;
}
public object Key;
public object Value;
public DatabasedSetting(object key, object value)
{
Key = key;
Value = value;
}
/// <summary>
/// Constructor for derived classes that may require serialisation.
/// </summary>
public DatabasedSetting()
{
}
public override string ToString() => $"{Key}=>{Value}";
}
}

View File

@ -8,7 +8,7 @@ using osu.Game.Screens.Select;
namespace osu.Game.Configuration namespace osu.Game.Configuration
{ {
public class OsuConfigManager : ConfigManager<OsuSetting> public class OsuConfigManager : IniConfigManager<OsuSetting>
{ {
protected override void InitialiseDefaults() protected override void InitialiseDefaults()
{ {

View File

@ -0,0 +1,44 @@
// 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.Linq;
using osu.Game.Database;
namespace osu.Game.Configuration
{
public class SettingsStore : DatabaseBackedStore
{
public event Action SettingChanged;
public SettingsStore(Func<OsuDbContext> createContext)
: base(createContext)
{
}
/// <summary>
/// Retrieve <see cref="DatabasedSetting"/>s for a specified ruleset/variant content.
/// </summary>
/// <param name="rulesetId">The ruleset's internal ID.</param>
/// <param name="variant">An optional variant.</param>
/// <returns></returns>
public List<DatabasedSetting> Query(int? rulesetId = null, int? variant = null) =>
GetContext().DatabasedSetting.Where(b => b.RulesetID == rulesetId && b.Variant == variant).ToList();
public void Update(DatabasedSetting setting)
{
var context = GetContext();
var newValue = setting.Value;
Refresh(ref setting);
setting.Value = newValue;
context.SaveChanges();
SettingChanged?.Invoke();
}
}
}

View File

@ -34,8 +34,14 @@ namespace osu.Game.Database
if (context.Entry(obj).State != EntityState.Detached) return; if (context.Entry(obj).State != EntityState.Detached) return;
var id = obj.ID; var id = obj.ID;
obj = lookupSource?.SingleOrDefault(t => t.ID == id) ?? context.Find<T>(id); var foundObject = lookupSource?.SingleOrDefault(t => t.ID == id) ?? context.Find<T>(id);
context.Entry(obj).Reload(); if (foundObject != null)
{
obj = foundObject;
context.Entry(obj).Reload();
}
else
context.Add(obj);
} }
/// <summary> /// <summary>

View File

@ -8,9 +8,10 @@ using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using osu.Framework.Logging; using osu.Framework.Logging;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Input.Bindings; using osu.Game.Configuration;
using osu.Game.IO; using osu.Game.IO;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using DatabasedKeyBinding = osu.Game.Input.Bindings.DatabasedKeyBinding;
using LogLevel = Microsoft.Extensions.Logging.LogLevel; using LogLevel = Microsoft.Extensions.Logging.LogLevel;
namespace osu.Game.Database namespace osu.Game.Database
@ -22,6 +23,7 @@ namespace osu.Game.Database
public DbSet<BeatmapMetadata> BeatmapMetadata { get; set; } public DbSet<BeatmapMetadata> BeatmapMetadata { get; set; }
public DbSet<BeatmapSetInfo> BeatmapSetInfo { get; set; } public DbSet<BeatmapSetInfo> BeatmapSetInfo { get; set; }
public DbSet<DatabasedKeyBinding> DatabasedKeyBinding { get; set; } public DbSet<DatabasedKeyBinding> DatabasedKeyBinding { get; set; }
public DbSet<DatabasedSetting> DatabasedSetting { get; set; }
public DbSet<FileInfo> FileInfo { get; set; } public DbSet<FileInfo> FileInfo { get; set; }
public DbSet<RulesetInfo> RulesetInfo { get; set; } public DbSet<RulesetInfo> RulesetInfo { get; set; }
@ -86,9 +88,11 @@ namespace osu.Game.Database
modelBuilder.Entity<BeatmapSetInfo>().HasIndex(b => b.DeletePending); modelBuilder.Entity<BeatmapSetInfo>().HasIndex(b => b.DeletePending);
modelBuilder.Entity<BeatmapSetInfo>().HasIndex(b => b.Hash).IsUnique(); modelBuilder.Entity<BeatmapSetInfo>().HasIndex(b => b.Hash).IsUnique();
modelBuilder.Entity<DatabasedKeyBinding>().HasIndex(b => b.Variant); modelBuilder.Entity<DatabasedKeyBinding>().HasIndex(b => new { b.RulesetID, b.Variant });
modelBuilder.Entity<DatabasedKeyBinding>().HasIndex(b => b.IntAction); modelBuilder.Entity<DatabasedKeyBinding>().HasIndex(b => b.IntAction);
modelBuilder.Entity<DatabasedSetting>().HasIndex(b => new { b.RulesetID, b.Variant });
modelBuilder.Entity<FileInfo>().HasIndex(b => b.Hash).IsUnique(); modelBuilder.Entity<FileInfo>().HasIndex(b => b.Hash).IsUnique();
modelBuilder.Entity<FileInfo>().HasIndex(b => b.ReferenceCount); modelBuilder.Entity<FileInfo>().HasIndex(b => b.ReferenceCount);

View File

@ -0,0 +1,329 @@
// <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("20180125143340_Settings")]
partial class Settings
{
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.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");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,57 @@
using Microsoft.EntityFrameworkCore.Migrations;
using System;
using System.Collections.Generic;
namespace osu.Game.Migrations
{
public partial class Settings : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "IX_KeyBinding_Variant",
table: "KeyBinding");
migrationBuilder.CreateTable(
name: "Settings",
columns: table => new
{
ID = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
Key = table.Column<int>(type: "INTEGER", nullable: false),
RulesetID = table.Column<int>(type: "INTEGER", nullable: true),
Value = table.Column<string>(type: "TEXT", nullable: true),
Variant = table.Column<int>(type: "INTEGER", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Settings", x => x.ID);
});
migrationBuilder.CreateIndex(
name: "IX_KeyBinding_RulesetID_Variant",
table: "KeyBinding",
columns: new[] { "RulesetID", "Variant" });
migrationBuilder.CreateIndex(
name: "IX_Settings_RulesetID_Variant",
table: "Settings",
columns: new[] { "RulesetID", "Variant" });
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Settings");
migrationBuilder.DropIndex(
name: "IX_KeyBinding_RulesetID_Variant",
table: "KeyBinding");
migrationBuilder.CreateIndex(
name: "IX_KeyBinding_Variant",
table: "KeyBinding",
column: "Variant");
}
}
}

View File

@ -193,6 +193,28 @@ namespace osu.Game.Migrations
b.ToTable("BeatmapSetInfo"); 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 => modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b =>
{ {
b.Property<int>("ID") b.Property<int>("ID")
@ -212,7 +234,7 @@ namespace osu.Game.Migrations
b.HasIndex("IntAction"); b.HasIndex("IntAction");
b.HasIndex("Variant"); b.HasIndex("RulesetID", "Variant");
b.ToTable("KeyBinding"); b.ToTable("KeyBinding");
}); });

View File

@ -71,6 +71,7 @@ namespace osu.Game
private OsuScreen screenStack; private OsuScreen screenStack;
private VolumeControl volume; private VolumeControl volume;
private OnScreenDisplay onscreenDisplay;
private Bindable<int> configRuleset; private Bindable<int> configRuleset;
public Bindable<RulesetInfo> Ruleset = new Bindable<RulesetInfo>(); public Bindable<RulesetInfo> Ruleset = new Bindable<RulesetInfo>();
@ -195,7 +196,7 @@ namespace osu.Game
}, overlayContent.Add); }, overlayContent.Add);
loadComponentSingleFile(volume = new VolumeControl(), Add); loadComponentSingleFile(volume = new VolumeControl(), Add);
loadComponentSingleFile(new OnScreenDisplay(), Add); loadComponentSingleFile(onscreenDisplay = new OnScreenDisplay(), Add);
//overlay elements //overlay elements
loadComponentSingleFile(direct = new DirectOverlay { Depth = -1 }, mainContent.Add); loadComponentSingleFile(direct = new DirectOverlay { Depth = -1 }, mainContent.Add);
@ -232,6 +233,7 @@ namespace osu.Game
forwardLoggedErrorsToNotifications(); forwardLoggedErrorsToNotifications();
dependencies.Cache(settings); dependencies.Cache(settings);
dependencies.Cache(onscreenDisplay);
dependencies.Cache(social); dependencies.Cache(social);
dependencies.Cache(direct); dependencies.Cache(direct);
dependencies.Cache(chat); dependencies.Cache(chat);

View File

@ -44,6 +44,8 @@ namespace osu.Game
protected KeyBindingStore KeyBindingStore; protected KeyBindingStore KeyBindingStore;
protected SettingsStore SettingsStore;
protected CursorOverrideContainer CursorOverrideContainer; protected CursorOverrideContainer CursorOverrideContainer;
protected override string MainResourceFile => @"osu.Game.Resources.dll"; protected override string MainResourceFile => @"osu.Game.Resources.dll";
@ -109,6 +111,7 @@ namespace osu.Game
dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, contextFactory.GetContext, RulesetStore, API, Host)); dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, contextFactory.GetContext, RulesetStore, API, Host));
dependencies.Cache(ScoreStore = new ScoreStore(Host.Storage, contextFactory.GetContext, Host, BeatmapManager, RulesetStore)); dependencies.Cache(ScoreStore = new ScoreStore(Host.Storage, contextFactory.GetContext, Host, BeatmapManager, RulesetStore));
dependencies.Cache(KeyBindingStore = new KeyBindingStore(contextFactory.GetContext, RulesetStore)); dependencies.Cache(KeyBindingStore = new KeyBindingStore(contextFactory.GetContext, RulesetStore));
dependencies.Cache(SettingsStore = new SettingsStore(contextFactory.GetContext));
dependencies.Cache(new OsuColour()); dependencies.Cache(new OsuColour());
//this completely overrides the framework default. will need to change once we make a proper FontStore. //this completely overrides the framework default. will need to change once we make a proper FontStore.

View File

@ -5,7 +5,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Configuration; using osu.Framework.Configuration;
using osu.Framework.Extensions; using osu.Framework.Configuration.Tracking;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
@ -118,43 +118,62 @@ namespace osu.Game.Overlays
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(FrameworkConfigManager frameworkConfig) private void load(FrameworkConfigManager frameworkConfig)
{ {
trackSetting(frameworkConfig.GetBindable<FrameSync>(FrameworkSetting.FrameSync), v => display(v, "Frame Limiter", v.GetDescription(), "Ctrl+F7")); BeginTracking(this, frameworkConfig);
trackSetting(frameworkConfig.GetBindable<string>(FrameworkSetting.AudioDevice), v => display(v, "Audio Device", string.IsNullOrEmpty(v) ? "Default" : v, v));
trackSetting(frameworkConfig.GetBindable<bool>(FrameworkSetting.ShowLogOverlay), v => display(v, "Debug Logs", v ? "visible" : "hidden", "Ctrl+F10"));
void displayResolution() => display(null, "Screen Resolution", frameworkConfig.Get<int>(FrameworkSetting.Width) + "x" + frameworkConfig.Get<int>(FrameworkSetting.Height));
trackSetting(frameworkConfig.GetBindable<int>(FrameworkSetting.Width), v => displayResolution());
trackSetting(frameworkConfig.GetBindable<int>(FrameworkSetting.Height), v => displayResolution());
trackSetting(frameworkConfig.GetBindable<double>(FrameworkSetting.CursorSensitivity), v => display(v, "Cursor Sensitivity", v.ToString(@"0.##x"), "Ctrl+Alt+R to reset"));
trackSetting(frameworkConfig.GetBindable<string>(FrameworkSetting.ActiveInputHandlers),
delegate (string v)
{
bool raw = v.Contains("Raw");
display(raw, "Raw Input", raw ? "enabled" : "disabled", "Ctrl+Alt+R to reset");
});
trackSetting(frameworkConfig.GetBindable<WindowMode>(FrameworkSetting.WindowMode), v => display(v, "Screen Mode", v.ToString(), "Alt+Enter"));
} }
private readonly List<IBindable> references = new List<IBindable>(); private readonly Dictionary<(object, IConfigManager), TrackedSettings> trackedConfigManagers = new Dictionary<(object, IConfigManager), TrackedSettings>();
private void trackSetting<T>(Bindable<T> bindable, Action<T> action) /// <summary>
/// Registers a <see cref="ConfigManager{T}"/> to have its settings tracked by this <see cref="OnScreenDisplay"/>.
/// </summary>
/// <param name="source">The object that is registering the <see cref="ConfigManager{T}"/> to be tracked.</param>
/// <param name="configManager">The <see cref="ConfigManager{T}"/> to be tracked.</param>
/// <exception cref="ArgumentNullException">If <paramref name="configManager"/> is null.</exception>
/// <exception cref="InvalidOperationException">If <paramref name="configManager"/> is already being tracked from the same <paramref name="source"/>.</exception>
public void BeginTracking(object source, ITrackableConfigManager configManager)
{ {
// we need to keep references as we bind if (configManager == null) throw new ArgumentNullException(nameof(configManager));
references.Add(bindable);
bindable.ValueChanged += action; if (trackedConfigManagers.ContainsKey((source, configManager)))
throw new InvalidOperationException($"{nameof(configManager)} is already registered.");
var trackedSettings = configManager.CreateTrackedSettings();
if (trackedSettings == null)
return;
configManager.LoadInto(trackedSettings);
trackedSettings.SettingChanged += display;
trackedConfigManagers.Add((source, configManager), trackedSettings);
} }
private void display(object rawValue, string settingName, string settingValue, string shortcut = @"") /// <summary>
/// Unregisters a <see cref="ConfigManager{T}"/> from having its settings tracked by this <see cref="OnScreenDisplay"/>.
/// </summary>
/// <param name="source">The object that registered the <see cref="ConfigManager{T}"/> to be tracked.</param>
/// <param name="configManager">The <see cref="ConfigManager{T}"/> that is being tracked.</param>
/// <exception cref="ArgumentNullException">If <paramref name="configManager"/> is null.</exception>
/// <exception cref="InvalidOperationException">If <paramref name="configManager"/> is not being tracked from the same <see cref="source"/>.</exception>
public void StopTracking(object source, ITrackableConfigManager configManager)
{
if (configManager == null) throw new ArgumentNullException(nameof(configManager));
if (!trackedConfigManagers.TryGetValue((source, configManager), out var existing))
throw new InvalidOperationException($"{nameof(configManager)} is not registered.");
existing.Unload();
existing.SettingChanged -= display;
trackedConfigManagers.Remove((source, configManager));
}
private void display(SettingDescription description)
{ {
Schedule(() => Schedule(() =>
{ {
textLine1.Text = settingName.ToUpper(); textLine1.Text = description.Name.ToUpper();
textLine2.Text = settingValue; textLine2.Text = description.Value;
textLine3.Text = shortcut.ToUpper(); textLine3.Text = description.Shortcut.ToUpper();
box.Animate( box.Animate(
b => b.FadeIn(500, Easing.OutQuint), b => b.FadeIn(500, Easing.OutQuint),
@ -167,16 +186,16 @@ namespace osu.Game.Overlays
int optionCount = 0; int optionCount = 0;
int selectedOption = -1; int selectedOption = -1;
if (rawValue is bool) if (description.RawValue is bool)
{ {
optionCount = 1; optionCount = 1;
if ((bool)rawValue) selectedOption = 0; if ((bool)description.RawValue) selectedOption = 0;
} }
else if (rawValue is Enum) else if (description.RawValue is Enum)
{ {
var values = Enum.GetValues(rawValue.GetType()); var values = Enum.GetValues(description.RawValue.GetType());
optionCount = values.Length; optionCount = values.Length;
selectedOption = Convert.ToInt32(rawValue); selectedOption = Convert.ToInt32(description.RawValue);
} }
textLine2.Origin = optionCount > 0 ? Anchor.BottomCentre : Anchor.Centre; textLine2.Origin = optionCount > 0 ? Anchor.BottomCentre : Anchor.Centre;

View File

@ -0,0 +1,11 @@
// 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.Configuration.Tracking;
namespace osu.Game.Rulesets.Configuration
{
public interface IRulesetConfigManager : ITrackableConfigManager
{
}
}

View File

@ -0,0 +1,15 @@
// 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.Game.Configuration;
namespace osu.Game.Rulesets.Configuration
{
public abstract class RulesetConfigManager<T> : DatabasedConfigManager<T>, IRulesetConfigManager
where T : struct
{
protected RulesetConfigManager(SettingsStore settings, RulesetInfo ruleset, int variant) : base(settings, ruleset, variant)
{
}
}
}

View File

@ -16,6 +16,9 @@ using System.Linq;
using osu.Framework.Configuration; using osu.Framework.Configuration;
using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Cursor;
using osu.Framework.Input; using osu.Framework.Input;
using osu.Game.Configuration;
using osu.Game.Overlays;
using osu.Game.Rulesets.Configuration;
using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using OpenTK; using OpenTK;
@ -35,6 +38,11 @@ namespace osu.Game.Rulesets.UI
/// </summary> /// </summary>
public bool AspectAdjust = true; public bool AspectAdjust = true;
/// <summary>
/// The selected variant.
/// </summary>
public virtual int Variant => 0;
/// <summary> /// <summary>
/// The input manager for this RulesetContainer. /// The input manager for this RulesetContainer.
/// </summary> /// </summary>
@ -65,6 +73,14 @@ namespace osu.Game.Rulesets.UI
protected readonly Ruleset Ruleset; protected readonly Ruleset Ruleset;
private IRulesetConfigManager rulesetConfig;
private OnScreenDisplay onScreenDisplay;
private DependencyContainer dependencies;
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
=> dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
/// <summary> /// <summary>
/// A visual representation of a <see cref="Rulesets.Ruleset"/>. /// A visual representation of a <see cref="Rulesets.Ruleset"/>.
/// </summary> /// </summary>
@ -77,6 +93,20 @@ namespace osu.Game.Rulesets.UI
Cursor = CreateCursor(); Cursor = CreateCursor();
} }
[BackgroundDependencyLoader(true)]
private void load(OnScreenDisplay onScreenDisplay, SettingsStore settings)
{
this.onScreenDisplay = onScreenDisplay;
rulesetConfig = CreateConfig(Ruleset, settings);
if (rulesetConfig != null)
{
dependencies.Cache(rulesetConfig);
onScreenDisplay?.BeginTracking(this, rulesetConfig);
}
}
public abstract ScoreProcessor CreateScoreProcessor(); public abstract ScoreProcessor CreateScoreProcessor();
/// <summary> /// <summary>
@ -110,11 +140,24 @@ namespace osu.Game.Rulesets.UI
/// </summary> /// </summary>
protected virtual CursorContainer CreateCursor() => null; protected virtual CursorContainer CreateCursor() => null;
protected virtual IRulesetConfigManager CreateConfig(Ruleset ruleset, SettingsStore settings) => null;
/// <summary> /// <summary>
/// Creates a Playfield. /// Creates a Playfield.
/// </summary> /// </summary>
/// <returns>The Playfield.</returns> /// <returns>The Playfield.</returns>
protected abstract Playfield CreatePlayfield(); protected abstract Playfield CreatePlayfield();
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
if (rulesetConfig != null)
{
onScreenDisplay?.StopTracking(this, rulesetConfig);
rulesetConfig = null;
}
}
} }
/// <summary> /// <summary>

View File

@ -6,7 +6,7 @@ using osu.Framework.Platform;
namespace osu.Game.Screens.Tournament.Components namespace osu.Game.Screens.Tournament.Components
{ {
public class DrawingsConfigManager : ConfigManager<DrawingsConfig> public class DrawingsConfigManager : IniConfigManager<DrawingsConfig>
{ {
protected override string Filename => @"drawings.ini"; protected override string Filename => @"drawings.ini";

View File

@ -265,10 +265,17 @@
<Compile Include="Beatmaps\Formats\JsonBeatmapDecoder.cs" /> <Compile Include="Beatmaps\Formats\JsonBeatmapDecoder.cs" />
<Compile Include="Beatmaps\Formats\LegacyDecoder.cs" /> <Compile Include="Beatmaps\Formats\LegacyDecoder.cs" />
<Compile Include="Beatmaps\Formats\LegacyStoryboardDecoder.cs" /> <Compile Include="Beatmaps\Formats\LegacyStoryboardDecoder.cs" />
<Compile Include="Configuration\DatabasedSetting.cs" />
<Compile Include="Configuration\SettingsStore.cs" />
<Compile Include="Configuration\DatabasedConfigManager.cs" />
<Compile Include="Configuration\SpeedChangeVisualisationMethod.cs" /> <Compile Include="Configuration\SpeedChangeVisualisationMethod.cs" />
<Compile Include="Database\DatabaseContextFactory.cs" /> <Compile Include="Database\DatabaseContextFactory.cs" />
<Compile Include="Database\IHasPrimaryKey.cs" /> <Compile Include="Database\IHasPrimaryKey.cs" />
<Compile Include="Graphics\Textures\LargeTextureStore.cs" /> <Compile Include="Graphics\Textures\LargeTextureStore.cs" />
<Compile Include="Migrations\20180125143340_Settings.cs" />
<Compile Include="Migrations\20180125143340_Settings.designer.cs">
<DependentUpon>20180125143340_Settings.cs</DependentUpon>
</Compile>
<Compile Include="Overlays\Profile\SupporterIcon.cs" /> <Compile Include="Overlays\Profile\SupporterIcon.cs" />
<Compile Include="Online\API\Requests\GetFriendsRequest.cs" /> <Compile Include="Online\API\Requests\GetFriendsRequest.cs" />
<Compile Include="Overlays\Settings\DangerousSettingsButton.cs" /> <Compile Include="Overlays\Settings\DangerousSettingsButton.cs" />
@ -314,6 +321,8 @@
<Compile Include="Overlays\Profile\Sections\Ranks\ScoreModsContainer.cs" /> <Compile Include="Overlays\Profile\Sections\Ranks\ScoreModsContainer.cs" />
<Compile Include="Overlays\Settings\Sections\Gameplay\ScrollingSettings.cs" /> <Compile Include="Overlays\Settings\Sections\Gameplay\ScrollingSettings.cs" />
<Compile Include="Overlays\Settings\Sections\Maintenance\DeleteAllBeatmapsDialog.cs" /> <Compile Include="Overlays\Settings\Sections\Maintenance\DeleteAllBeatmapsDialog.cs" />
<Compile Include="Rulesets\Configuration\IRulesetConfigManager.cs" />
<Compile Include="Rulesets\Configuration\RulesetConfigManager.cs" />
<Compile Include="Rulesets\Mods\IApplicableFailOverride.cs" /> <Compile Include="Rulesets\Mods\IApplicableFailOverride.cs" />
<Compile Include="Rulesets\Mods\IApplicableMod.cs" /> <Compile Include="Rulesets\Mods\IApplicableMod.cs" />
<Compile Include="Rulesets\Mods\IApplicableToBeatmapConverter.cs" /> <Compile Include="Rulesets\Mods\IApplicableToBeatmapConverter.cs" />