mirror of
https://github.com/ppy/osu.git
synced 2024-12-14 21:02:55 +08:00
Merge branch 'master' into notification-thread-safety
This commit is contained in:
commit
35ce6fd2bd
@ -1,6 +1,7 @@
|
||||
# 2017-09-14
|
||||
clone_depth: 1
|
||||
version: '{branch}-{build}'
|
||||
image: Visual Studio 2017
|
||||
configuration: Debug
|
||||
cache:
|
||||
- C:\ProgramData\chocolatey\bin -> appveyor.yml
|
||||
@ -11,7 +12,7 @@ install:
|
||||
- cmd: git submodule update --init --recursive
|
||||
- cmd: choco install resharper-clt -y
|
||||
- cmd: choco install nvika -y
|
||||
- cmd: appveyor DownloadFile https://github.com/peppy/CodeFileSanity/releases/download/v0.2.2/CodeFileSanity.exe
|
||||
- cmd: appveyor DownloadFile https://github.com/peppy/CodeFileSanity/releases/download/v0.2.3/CodeFileSanity.exe
|
||||
before_build:
|
||||
- cmd: CodeFileSanity.exe
|
||||
- cmd: nuget restore
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit b1f36efca59840da65df788a52107b1674a904c6
|
||||
Subproject commit dbcfa5c244555e7901dac7d94eab53b3b04d17e6
|
@ -11,6 +11,10 @@
|
||||
<assemblyIdentity name="SharpCompress" publicKeyToken="afb0a02973931d96" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-0.18.1.0" newVersion="0.18.1.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral"/>
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0"/>
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
||||
|
@ -57,7 +57,6 @@
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
|
||||
<RunCodeAnalysis>false</RunCodeAnalysis>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
@ -76,7 +75,6 @@
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
@ -102,7 +100,6 @@
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<LangVersion>6</LangVersion>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
|
||||
<StartArguments>--tests</StartArguments>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
@ -158,6 +155,10 @@
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.ValueTuple, Version=4.0.2.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51">
|
||||
<HintPath>../packages/System.ValueTuple.4.4.0/lib/net461/System.ValueTuple.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@ -1,13 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
-->
|
||||
<packages>
|
||||
<package id="DeltaCompressionDotNet" version="1.1.0" targetFramework="net45" />
|
||||
<package id="Mono.Cecil" version="0.9.6.4" targetFramework="net45" />
|
||||
<package id="OpenTK" version="3.0.0-git00009" targetFramework="net461" />
|
||||
<package id="SharpCompress" version="0.18.1" targetFramework="net461" />
|
||||
<package id="Splat" version="2.0.0" targetFramework="net45" />
|
||||
<package id="squirrel.windows" version="1.7.8" targetFramework="net461" />
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
-->
|
||||
<packages>
|
||||
<package id="DeltaCompressionDotNet" version="1.1.0" targetFramework="net45" />
|
||||
<package id="Mono.Cecil" version="0.9.6.4" targetFramework="net45" />
|
||||
<package id="OpenTK" version="3.0.0-git00009" targetFramework="net461" />
|
||||
<package id="SharpCompress" version="0.18.1" targetFramework="net461" />
|
||||
<package id="Splat" version="2.0.0" targetFramework="net45" />
|
||||
<package id="squirrel.windows" version="1.7.8" targetFramework="net461" />
|
||||
<package id="System.ValueTuple" version="4.4.0" targetFramework="net461" />
|
||||
</packages>
|
@ -10,6 +10,10 @@
|
||||
<assemblyIdentity name="SharpCompress" publicKeyToken="afb0a02973931d96" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-0.18.1.0" newVersion="0.18.1.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral"/>
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0"/>
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
||||
{
|
||||
beatmap = original;
|
||||
|
||||
BeatmapDifficulty difficulty = original.BeatmapInfo.Difficulty;
|
||||
BeatmapDifficulty difficulty = original.BeatmapInfo.BaseDifficulty;
|
||||
|
||||
int seed = (int)Math.Round(difficulty.DrainRate + difficulty.CircleSize) * 20 + (int)(difficulty.OverallDifficulty * 41.2) + (int)Math.Round(difficulty.ApproachRate);
|
||||
random = new FastRandom(seed);
|
||||
|
@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
// The true distance, accounting for any repeats
|
||||
double distance = (distanceData?.Distance ?? 0) * repeatCount;
|
||||
// The velocity of the osu! hit object - calculated as the velocity of a slider
|
||||
double osuVelocity = osu_base_scoring_distance * beatmap.BeatmapInfo.Difficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier / timingPoint.BeatLength;
|
||||
double osuVelocity = osu_base_scoring_distance * beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier / timingPoint.BeatLength;
|
||||
// The duration of the osu! hit object
|
||||
double osuDuration = distance / osuVelocity;
|
||||
|
||||
|
@ -104,7 +104,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
if (drainTime == 0)
|
||||
drainTime = 10000;
|
||||
|
||||
BeatmapDifficulty difficulty = Beatmap.BeatmapInfo.Difficulty;
|
||||
BeatmapDifficulty difficulty = Beatmap.BeatmapInfo.BaseDifficulty;
|
||||
conversionDifficulty = ((difficulty.DrainRate + MathHelper.Clamp(difficulty.ApproachRate, 4, 7)) / 1.5 + Beatmap.HitObjects.Count / drainTime * 9f) / 38f * 5f / 1.15;
|
||||
conversionDifficulty = Math.Min(conversionDifficulty.Value, 12);
|
||||
|
||||
|
@ -21,6 +21,6 @@ namespace osu.Game.Rulesets.Mania
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter() => new ManiaBeatmapConverter(true, (int)Math.Max(1, Math.Round(Beatmap.BeatmapInfo.Difficulty.CircleSize)));
|
||||
protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter() => new ManiaBeatmapConverter(true, (int)Math.Max(1, Math.Round(Beatmap.BeatmapInfo.BaseDifficulty.CircleSize)));
|
||||
}
|
||||
}
|
||||
|
@ -100,7 +100,7 @@ namespace osu.Game.Rulesets.Mania.Scoring
|
||||
|
||||
protected override void SimulateAutoplay(Beatmap<ManiaHitObject> beatmap)
|
||||
{
|
||||
BeatmapDifficulty difficulty = beatmap.BeatmapInfo.Difficulty;
|
||||
BeatmapDifficulty difficulty = beatmap.BeatmapInfo.BaseDifficulty;
|
||||
hpMultiplier = BeatmapDifficulty.DifficultyRange(difficulty.DrainRate, hp_multiplier_min, hp_multiplier_mid, hp_multiplier_max);
|
||||
hpMissMultiplier = BeatmapDifficulty.DifficultyRange(difficulty.DrainRate, hp_multiplier_miss_min, hp_multiplier_miss_mid, hp_multiplier_miss_max);
|
||||
|
||||
|
@ -88,18 +88,18 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter()
|
||||
{
|
||||
if (IsForCurrentRuleset)
|
||||
AvailableColumns = (int)Math.Max(1, Math.Round(WorkingBeatmap.BeatmapInfo.Difficulty.CircleSize));
|
||||
AvailableColumns = (int)Math.Max(1, Math.Round(WorkingBeatmap.BeatmapInfo.BaseDifficulty.CircleSize));
|
||||
else
|
||||
{
|
||||
float percentSliderOrSpinner = (float)WorkingBeatmap.Beatmap.HitObjects.Count(h => h is IHasEndTime) / WorkingBeatmap.Beatmap.HitObjects.Count;
|
||||
if (percentSliderOrSpinner < 0.2)
|
||||
AvailableColumns = 7;
|
||||
else if (percentSliderOrSpinner < 0.3 || Math.Round(WorkingBeatmap.BeatmapInfo.Difficulty.CircleSize) >= 5)
|
||||
AvailableColumns = Math.Round(WorkingBeatmap.BeatmapInfo.Difficulty.OverallDifficulty) > 5 ? 7 : 6;
|
||||
else if (percentSliderOrSpinner < 0.3 || Math.Round(WorkingBeatmap.BeatmapInfo.BaseDifficulty.CircleSize) >= 5)
|
||||
AvailableColumns = Math.Round(WorkingBeatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty) > 5 ? 7 : 6;
|
||||
else if (percentSliderOrSpinner > 0.6)
|
||||
AvailableColumns = Math.Round(WorkingBeatmap.BeatmapInfo.Difficulty.OverallDifficulty) > 4 ? 5 : 4;
|
||||
AvailableColumns = Math.Round(WorkingBeatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty) > 4 ? 5 : 4;
|
||||
else
|
||||
AvailableColumns = Math.Max(4, Math.Min((int)Math.Round(WorkingBeatmap.BeatmapInfo.Difficulty.OverallDifficulty) + 1, 7));
|
||||
AvailableColumns = Math.Max(4, Math.Min((int)Math.Round(WorkingBeatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty) + 1, 7));
|
||||
}
|
||||
|
||||
return new ManiaBeatmapConverter(IsForCurrentRuleset, AvailableColumns);
|
||||
|
@ -10,6 +10,10 @@
|
||||
<assemblyIdentity name="SharpCompress" publicKeyToken="afb0a02973931d96" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-0.18.1.0" newVersion="0.18.1.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral"/>
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0"/>
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Osu.Scoring
|
||||
|
||||
protected override void SimulateAutoplay(Beatmap<OsuHitObject> beatmap)
|
||||
{
|
||||
hpDrainRate = beatmap.BeatmapInfo.Difficulty.DrainRate;
|
||||
hpDrainRate = beatmap.BeatmapInfo.BaseDifficulty.DrainRate;
|
||||
|
||||
foreach (var obj in beatmap.HitObjects)
|
||||
{
|
||||
|
@ -125,7 +125,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
||||
if (autoCursorScale && beatmap.Value != null)
|
||||
{
|
||||
// if we have a beatmap available, let's get its circle size to figure out an automatic cursor scale modifier.
|
||||
scale *= (float)(1 - 0.7 * (1 + beatmap.Value.BeatmapInfo.Difficulty.CircleSize - BeatmapDifficulty.DEFAULT_DIFFICULTY) / BeatmapDifficulty.DEFAULT_DIFFICULTY);
|
||||
scale *= (float)(1 - 0.7 * (1 + beatmap.Value.BeatmapInfo.BaseDifficulty.CircleSize - BeatmapDifficulty.DEFAULT_DIFFICULTY) / BeatmapDifficulty.DEFAULT_DIFFICULTY);
|
||||
}
|
||||
|
||||
cursorContainer.Scale = new Vector2(scale);
|
||||
|
@ -10,6 +10,10 @@
|
||||
<assemblyIdentity name="SharpCompress" publicKeyToken="afb0a02973931d96" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-0.18.1.0" newVersion="0.18.1.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral"/>
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0"/>
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
@ -51,7 +51,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
|
||||
{
|
||||
// Rewrite the beatmap info to add the slider velocity multiplier
|
||||
BeatmapInfo info = original.BeatmapInfo.DeepClone();
|
||||
info.Difficulty.SliderMultiplier *= legacy_velocity_multiplier;
|
||||
info.BaseDifficulty.SliderMultiplier *= legacy_velocity_multiplier;
|
||||
|
||||
Beatmap<TaikoHitObject> converted = base.ConvertBeatmap(original);
|
||||
|
||||
@ -96,7 +96,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
|
||||
double distance = distanceData.Distance * repeats * legacy_velocity_multiplier;
|
||||
|
||||
// The velocity of the taiko hit object - calculated as the velocity of a drum roll
|
||||
double taikoVelocity = taiko_base_distance * beatmap.BeatmapInfo.Difficulty.SliderMultiplier * legacy_velocity_multiplier / speedAdjustedBeatLength;
|
||||
double taikoVelocity = taiko_base_distance * beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier * legacy_velocity_multiplier / speedAdjustedBeatLength;
|
||||
// The duration of the taiko hit object
|
||||
double taikoDuration = distance / taikoVelocity;
|
||||
|
||||
@ -106,12 +106,12 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
|
||||
speedAdjustedBeatLength *= speedAdjustment;
|
||||
|
||||
// The velocity of the osu! hit object - calculated as the velocity of a slider
|
||||
double osuVelocity = osu_base_scoring_distance * beatmap.BeatmapInfo.Difficulty.SliderMultiplier * legacy_velocity_multiplier / speedAdjustedBeatLength;
|
||||
double osuVelocity = osu_base_scoring_distance * beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier * legacy_velocity_multiplier / speedAdjustedBeatLength;
|
||||
// The duration of the osu! hit object
|
||||
double osuDuration = distance / osuVelocity;
|
||||
|
||||
// If the drum roll is to be split into hit circles, assume the ticks are 1/8 spaced within the duration of one beat
|
||||
double tickSpacing = Math.Min(speedAdjustedBeatLength / beatmap.BeatmapInfo.Difficulty.SliderTickRate, taikoDuration / repeats);
|
||||
double tickSpacing = Math.Min(speedAdjustedBeatLength / beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate, taikoDuration / repeats);
|
||||
|
||||
if (!isForCurrentRuleset && tickSpacing > 0 && osuDuration < 2 * speedAdjustedBeatLength)
|
||||
{
|
||||
@ -154,13 +154,13 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
|
||||
Samples = obj.Samples,
|
||||
IsStrong = strong,
|
||||
Duration = taikoDuration,
|
||||
TickRate = beatmap.BeatmapInfo.Difficulty.SliderTickRate == 3 ? 3 : 4,
|
||||
TickRate = beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate == 3 ? 3 : 4,
|
||||
};
|
||||
}
|
||||
}
|
||||
else if (endTimeData != null)
|
||||
{
|
||||
double hitMultiplier = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.Difficulty.OverallDifficulty, 3, 5, 7.5) * swell_hit_multiplier;
|
||||
double hitMultiplier = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty, 3, 5, 7.5) * swell_hit_multiplier;
|
||||
|
||||
yield return new Swell
|
||||
{
|
||||
|
@ -71,12 +71,12 @@ namespace osu.Game.Rulesets.Taiko.Scoring
|
||||
|
||||
protected override void SimulateAutoplay(Beatmap<TaikoHitObject> beatmap)
|
||||
{
|
||||
double hpMultiplierNormal = 1 / (hp_hit_great * beatmap.HitObjects.FindAll(o => o is Hit).Count * BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.Difficulty.DrainRate, 0.5, 0.75, 0.98));
|
||||
double hpMultiplierNormal = 1 / (hp_hit_great * beatmap.HitObjects.FindAll(o => o is Hit).Count * BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.5, 0.75, 0.98));
|
||||
|
||||
hpIncreaseTick = hp_hit_tick;
|
||||
hpIncreaseGreat = hpMultiplierNormal * hp_hit_great;
|
||||
hpIncreaseGood = hpMultiplierNormal * hp_hit_good;
|
||||
hpIncreaseMiss = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.Difficulty.DrainRate, hp_miss_min, hp_miss_mid, hp_miss_max);
|
||||
hpIncreaseMiss = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, hp_miss_min, hp_miss_mid, hp_miss_max);
|
||||
|
||||
foreach (var obj in beatmap.HitObjects)
|
||||
{
|
||||
|
@ -67,7 +67,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
||||
HitObjects = new List<HitObject> { new CentreHit() },
|
||||
BeatmapInfo = new BeatmapInfo
|
||||
{
|
||||
Difficulty = new BeatmapDifficulty(),
|
||||
BaseDifficulty = new BeatmapDifficulty(),
|
||||
Metadata = new BeatmapMetadata
|
||||
{
|
||||
Artist = @"Unknown",
|
||||
|
@ -63,7 +63,7 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
StartTime = time,
|
||||
};
|
||||
|
||||
barLine.ApplyDefaults(Beatmap.ControlPointInfo, Beatmap.BeatmapInfo.Difficulty);
|
||||
barLine.ApplyDefaults(Beatmap.ControlPointInfo, Beatmap.BeatmapInfo.BaseDifficulty);
|
||||
|
||||
bool isMajor = currentBeat % (int)currentPoint.TimeSignature == 0;
|
||||
Playfield.Add(isMajor ? new DrawableBarLineMajor(barLine) : new DrawableBarLine(barLine));
|
||||
|
@ -10,6 +10,10 @@
|
||||
<assemblyIdentity name="SharpCompress" publicKeyToken="afb0a02973931d96" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-0.18.1.0" newVersion="0.18.1.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral"/>
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0"/>
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
@ -85,7 +85,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
||||
{
|
||||
var beatmap = decoder.Decode(new StreamReader(stream));
|
||||
var difficulty = beatmap.BeatmapInfo.Difficulty;
|
||||
var difficulty = beatmap.BeatmapInfo.BaseDifficulty;
|
||||
Assert.AreEqual(6.5f, difficulty.DrainRate);
|
||||
Assert.AreEqual(4, difficulty.CircleSize);
|
||||
Assert.AreEqual(8, difficulty.OverallDifficulty);
|
||||
|
@ -95,8 +95,6 @@ namespace osu.Game.Tests.Beatmaps.IO
|
||||
|
||||
private OsuGameBase loadOsu(GameHost host)
|
||||
{
|
||||
host.Storage.DeleteDatabase(@"client");
|
||||
|
||||
var osu = new OsuGameBase();
|
||||
Task.Run(() => host.Run(osu));
|
||||
|
||||
@ -117,7 +115,7 @@ namespace osu.Game.Tests.Beatmaps.IO
|
||||
//ensure we were stored to beatmap database backing...
|
||||
Assert.IsTrue(resultSets.Count() == 1, $@"Incorrect result count found ({resultSets.Count()} but should be 1).");
|
||||
|
||||
Func<IEnumerable<BeatmapInfo>> queryBeatmaps = () => store.QueryBeatmaps(s => s.OnlineBeatmapSetID == 241526 && s.BaseDifficultyID > 0);
|
||||
Func<IEnumerable<BeatmapInfo>> queryBeatmaps = () => store.QueryBeatmaps(s => s.BeatmapSet.OnlineBeatmapSetID == 241526 && s.BaseDifficultyID > 0);
|
||||
Func<IEnumerable<BeatmapSetInfo>> queryBeatmapSets = () => store.QueryBeatmapSets(s => s.OnlineBeatmapSetID == 241526);
|
||||
|
||||
//if we don't re-check here, the set will be inserted but the beatmaps won't be present yet.
|
||||
@ -157,7 +155,7 @@ namespace osu.Game.Tests.Beatmaps.IO
|
||||
|
||||
private void waitForOrAssert(Func<bool> result, string failureMessage, int timeout = 60000)
|
||||
{
|
||||
Action waitAction = () => { while (!result()) Thread.Sleep(20); };
|
||||
Action waitAction = () => { while (!result()) Thread.Sleep(200); };
|
||||
Assert.IsTrue(waitAction.BeginInvoke(null, null).AsyncWaitHandle.WaitOne(timeout), failureMessage);
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,10 @@
|
||||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral"/>
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0"/>
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
@ -39,14 +39,9 @@
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="SQLite.Net">
|
||||
<HintPath>$(SolutionDir)\packages\SQLite.Net.Core-PCL.3.1.1\lib\portable-win8+net45+wp8+wpa81+MonoAndroid1+MonoTouch1\SQLite.Net.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SQLite.Net.Platform.Win32">
|
||||
<HintPath>$(SolutionDir)\packages\SQLite.Net-PCL.3.1.1\lib\net4\SQLite.Net.Platform.Win32.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SQLite.Net.Platform.Generic">
|
||||
<HintPath>$(SolutionDir)\packages\SQLite.Net-PCL.3.1.1\lib\net40\SQLite.Net.Platform.Generic.dll</HintPath>
|
||||
<Reference Include="System.ValueTuple, Version=4.0.2.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51">
|
||||
<HintPath>$(SolutionDir)\packages\System.ValueTuple.4.4.0\lib\net461\System.ValueTuple.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@ -1,11 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
-->
|
||||
<packages>
|
||||
<package id="NUnit" version="3.8.1" targetFramework="net461" />
|
||||
<package id="OpenTK" version="3.0.0-git00009" targetFramework="net461" />
|
||||
<package id="SQLite.Net.Core-PCL" version="3.1.1" targetFramework="net45" />
|
||||
<package id="SQLite.Net-PCL" version="3.1.1" targetFramework="net45" />
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
-->
|
||||
<packages>
|
||||
<package id="NUnit" version="3.8.1" targetFramework="net461" />
|
||||
<package id="OpenTK" version="3.0.0-git00009" targetFramework="net461" />
|
||||
<package id="System.ValueTuple" version="4.4.0" targetFramework="net461" />
|
||||
</packages>
|
@ -72,7 +72,7 @@ namespace osu.Game.Beatmaps
|
||||
AuthorString = @"Unknown Creator",
|
||||
},
|
||||
Version = @"Normal",
|
||||
Difficulty = new BeatmapDifficulty()
|
||||
BaseDifficulty = new BeatmapDifficulty()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using SQLite.Net.Attributes;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
{
|
||||
@ -12,8 +12,9 @@ namespace osu.Game.Beatmaps
|
||||
/// </summary>
|
||||
public const float DEFAULT_DIFFICULTY = 5;
|
||||
|
||||
[PrimaryKey, AutoIncrement]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public int ID { get; set; }
|
||||
|
||||
public float DrainRate { get; set; } = DEFAULT_DIFFICULTY;
|
||||
public float CircleSize { get; set; } = DEFAULT_DIFFICULTY;
|
||||
public float OverallDifficulty { get; set; } = DEFAULT_DIFFICULTY;
|
||||
|
@ -2,18 +2,18 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using osu.Game.IO.Serialization;
|
||||
using osu.Game.Rulesets;
|
||||
using SQLite.Net.Attributes;
|
||||
using SQLiteNetExtensions.Attributes;
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
{
|
||||
public class BeatmapInfo : IEquatable<BeatmapInfo>, IJsonSerializable
|
||||
{
|
||||
[PrimaryKey, AutoIncrement]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public int ID { get; set; }
|
||||
|
||||
//TODO: should be in database
|
||||
@ -23,30 +23,24 @@ namespace osu.Game.Beatmaps
|
||||
public int? OnlineBeatmapID { get; set; }
|
||||
|
||||
[JsonProperty("beatmapset_id")]
|
||||
[NotMapped]
|
||||
public int? OnlineBeatmapSetID { get; set; }
|
||||
|
||||
[ForeignKey(typeof(BeatmapSetInfo))]
|
||||
public int BeatmapSetInfoID { get; set; }
|
||||
|
||||
[ManyToOne]
|
||||
[Required]
|
||||
public BeatmapSetInfo BeatmapSet { get; set; }
|
||||
|
||||
[ForeignKey(typeof(BeatmapMetadata))]
|
||||
public int BeatmapMetadataID { get; set; }
|
||||
|
||||
[OneToOne(CascadeOperations = CascadeOperation.All)]
|
||||
public BeatmapMetadata Metadata { get; set; }
|
||||
|
||||
[ForeignKey(typeof(BeatmapDifficulty)), NotNull]
|
||||
public int BaseDifficultyID { get; set; }
|
||||
|
||||
[OneToOne(CascadeOperations = CascadeOperation.All)]
|
||||
public BeatmapDifficulty Difficulty { get; set; }
|
||||
public BeatmapDifficulty BaseDifficulty { get; set; }
|
||||
|
||||
[Ignore]
|
||||
[NotMapped]
|
||||
public BeatmapMetrics Metrics { get; set; }
|
||||
|
||||
[Ignore]
|
||||
[NotMapped]
|
||||
public BeatmapOnlineInfo OnlineInfo { get; set; }
|
||||
|
||||
public string Path { get; set; }
|
||||
@ -59,7 +53,6 @@ namespace osu.Game.Beatmaps
|
||||
/// <summary>
|
||||
/// MD5 is kept for legacy support (matching against replays, osu-web-10 etc.).
|
||||
/// </summary>
|
||||
[Indexed]
|
||||
[JsonProperty("file_md5")]
|
||||
public string MD5Hash { get; set; }
|
||||
|
||||
@ -69,10 +62,8 @@ namespace osu.Game.Beatmaps
|
||||
public float StackLeniency { get; set; }
|
||||
public bool SpecialStyle { get; set; }
|
||||
|
||||
[ForeignKey(typeof(RulesetInfo))]
|
||||
public int RulesetID { get; set; }
|
||||
|
||||
[OneToOne(CascadeOperations = CascadeOperation.CascadeRead)]
|
||||
public RulesetInfo Ruleset { get; set; }
|
||||
|
||||
public bool LetterboxInBreaks { get; set; }
|
||||
@ -101,7 +92,7 @@ namespace osu.Game.Beatmaps
|
||||
}
|
||||
}
|
||||
|
||||
[Ignore]
|
||||
[NotMapped]
|
||||
public int[] Bookmarks { get; set; } = new int[0];
|
||||
|
||||
public double DistanceSpacing { get; set; }
|
||||
|
@ -5,8 +5,9 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Threading.Tasks;
|
||||
using Ionic.Zip;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
@ -15,14 +16,13 @@ using osu.Framework.Logging;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Game.Beatmaps.Formats;
|
||||
using osu.Game.Beatmaps.IO;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.IO;
|
||||
using osu.Game.IPC;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Overlays.Notifications;
|
||||
using osu.Game.Rulesets;
|
||||
using SQLite.Net;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Game.Online.API;
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
{
|
||||
@ -58,9 +58,19 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
private readonly Storage storage;
|
||||
|
||||
private readonly FileStore files;
|
||||
private BeatmapStore createBeatmapStore(Func<OsuDbContext> context)
|
||||
{
|
||||
var store = new BeatmapStore(context);
|
||||
store.BeatmapSetAdded += s => BeatmapSetAdded?.Invoke(s);
|
||||
store.BeatmapSetRemoved += s => BeatmapSetRemoved?.Invoke(s);
|
||||
store.BeatmapHidden += b => BeatmapHidden?.Invoke(b);
|
||||
store.BeatmapRestored += b => BeatmapRestored?.Invoke(b);
|
||||
return store;
|
||||
}
|
||||
|
||||
private readonly SQLiteConnection connection;
|
||||
private readonly Func<OsuDbContext> createContext;
|
||||
|
||||
private readonly FileStore files;
|
||||
|
||||
private readonly RulesetStore rulesets;
|
||||
|
||||
@ -83,22 +93,27 @@ namespace osu.Game.Beatmaps
|
||||
/// </summary>
|
||||
public Func<Storage> GetStableStorage { private get; set; }
|
||||
|
||||
public BeatmapManager(Storage storage, FileStore files, SQLiteConnection connection, RulesetStore rulesets, APIAccess api, IIpcHost importHost = null)
|
||||
public BeatmapManager(Storage storage, Func<OsuDbContext> context, RulesetStore rulesets, APIAccess api, IIpcHost importHost = null)
|
||||
{
|
||||
beatmaps = new BeatmapStore(connection);
|
||||
beatmaps.BeatmapSetAdded += s => BeatmapSetAdded?.Invoke(s);
|
||||
beatmaps.BeatmapSetRemoved += s => BeatmapSetRemoved?.Invoke(s);
|
||||
beatmaps.BeatmapHidden += b => BeatmapHidden?.Invoke(b);
|
||||
beatmaps.BeatmapRestored += b => BeatmapRestored?.Invoke(b);
|
||||
createContext = context;
|
||||
importContext = new Lazy<OsuDbContext>(() =>
|
||||
{
|
||||
var c = createContext();
|
||||
c.Database.AutoTransactionsEnabled = false;
|
||||
return c;
|
||||
});
|
||||
|
||||
this.storage = storage;
|
||||
this.files = files;
|
||||
this.connection = connection;
|
||||
beatmaps = createBeatmapStore(context);
|
||||
files = new FileStore(context, storage);
|
||||
|
||||
this.storage = files.Storage;
|
||||
this.rulesets = rulesets;
|
||||
this.api = api;
|
||||
|
||||
if (importHost != null)
|
||||
ipc = new BeatmapIPCChannel(importHost, this);
|
||||
|
||||
beatmaps.Cleanup();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -156,7 +171,7 @@ namespace osu.Game.Beatmaps
|
||||
notification.State = ProgressNotificationState.Completed;
|
||||
}
|
||||
|
||||
private readonly object importLock = new object();
|
||||
private readonly Lazy<OsuDbContext> importContext;
|
||||
|
||||
/// <summary>
|
||||
/// Import a beatmap from an <see cref="ArchiveReader"/>.
|
||||
@ -164,13 +179,29 @@ namespace osu.Game.Beatmaps
|
||||
/// <param name="archiveReader">The beatmap to be imported.</param>
|
||||
public BeatmapSetInfo Import(ArchiveReader archiveReader)
|
||||
{
|
||||
BeatmapSetInfo set = null;
|
||||
|
||||
// let's only allow one concurrent import at a time for now.
|
||||
lock (importLock)
|
||||
connection.RunInTransaction(() => Import(set = importToStorage(archiveReader)));
|
||||
lock (importContext)
|
||||
{
|
||||
var context = importContext.Value;
|
||||
|
||||
return set;
|
||||
using (var transaction = context.Database.BeginTransaction())
|
||||
{
|
||||
// create local stores so we can isolate and thread safely, and share a context/transaction.
|
||||
var iFiles = new FileStore(() => context, storage);
|
||||
var iBeatmaps = createBeatmapStore(() => context);
|
||||
|
||||
BeatmapSetInfo set = importToStorage(iFiles, iBeatmaps, archiveReader);
|
||||
|
||||
if (set.ID == 0)
|
||||
{
|
||||
iBeatmaps.Add(set);
|
||||
context.SaveChanges();
|
||||
}
|
||||
|
||||
transaction.Commit();
|
||||
return set;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -182,7 +213,7 @@ namespace osu.Game.Beatmaps
|
||||
// If we have an ID then we already exist in the database.
|
||||
if (beatmapSetInfo.ID != 0) return;
|
||||
|
||||
beatmaps.Add(beatmapSetInfo);
|
||||
createBeatmapStore(createContext).Add(beatmapSetInfo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -260,10 +291,33 @@ namespace osu.Game.Beatmaps
|
||||
/// <param name="beatmapSet">The beatmap set to delete.</param>
|
||||
public void Delete(BeatmapSetInfo beatmapSet)
|
||||
{
|
||||
if (!beatmaps.Delete(beatmapSet)) return;
|
||||
lock (importContext)
|
||||
{
|
||||
var context = importContext.Value;
|
||||
|
||||
if (!beatmapSet.Protected)
|
||||
files.Dereference(beatmapSet.Files.Select(f => f.FileInfo).ToArray());
|
||||
using (var transaction = context.Database.BeginTransaction())
|
||||
{
|
||||
context.ChangeTracker.AutoDetectChangesEnabled = false;
|
||||
|
||||
// re-fetch the beatmap set on the import context.
|
||||
beatmapSet = context.BeatmapSetInfo.Include(s => s.Files).ThenInclude(f => f.FileInfo).First(s => s.ID == beatmapSet.ID);
|
||||
|
||||
// create local stores so we can isolate and thread safely, and share a context/transaction.
|
||||
var iFiles = new FileStore(() => context, storage);
|
||||
var iBeatmaps = createBeatmapStore(() => context);
|
||||
|
||||
if (iBeatmaps.Delete(beatmapSet))
|
||||
{
|
||||
if (!beatmapSet.Protected)
|
||||
iFiles.Dereference(beatmapSet.Files.Select(f => f.FileInfo).ToArray());
|
||||
}
|
||||
|
||||
context.ChangeTracker.AutoDetectChangesEnabled = true;
|
||||
context.SaveChanges();
|
||||
|
||||
transaction.Commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -283,7 +337,7 @@ namespace osu.Game.Beatmaps
|
||||
/// Is a no-op for already usable beatmaps.
|
||||
/// </summary>
|
||||
/// <param name="beatmapSet">The beatmap to restore.</param>
|
||||
public void Undelete(BeatmapSetInfo beatmapSet)
|
||||
private void undelete(BeatmapStore beatmaps, FileStore files, BeatmapSetInfo beatmapSet)
|
||||
{
|
||||
if (!beatmaps.Undelete(beatmapSet)) return;
|
||||
|
||||
@ -302,9 +356,6 @@ namespace osu.Game.Beatmaps
|
||||
if (beatmapInfo == null || beatmapInfo == DefaultBeatmap?.BeatmapInfo)
|
||||
return DefaultBeatmap;
|
||||
|
||||
lock (beatmaps)
|
||||
beatmaps.Populate(beatmapInfo);
|
||||
|
||||
if (beatmapInfo.BeatmapSet == null)
|
||||
throw new InvalidOperationException($@"Beatmap set {beatmapInfo.BeatmapSetInfoID} is not in the local database.");
|
||||
|
||||
@ -318,32 +369,12 @@ namespace osu.Game.Beatmaps
|
||||
return working;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset the manager to an empty state.
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
lock (beatmaps)
|
||||
beatmaps.Reset();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform a lookup query on available <see cref="BeatmapSetInfo"/>s.
|
||||
/// </summary>
|
||||
/// <param name="query">The query.</param>
|
||||
/// <returns>The first result for the provided query, or null if no results were found.</returns>
|
||||
public BeatmapSetInfo QueryBeatmapSet(Func<BeatmapSetInfo, bool> query)
|
||||
{
|
||||
lock (beatmaps)
|
||||
{
|
||||
BeatmapSetInfo set = beatmaps.Query<BeatmapSetInfo>().FirstOrDefault(query);
|
||||
|
||||
if (set != null)
|
||||
beatmaps.Populate(set);
|
||||
|
||||
return set;
|
||||
}
|
||||
}
|
||||
public BeatmapSetInfo QueryBeatmapSet(Func<BeatmapSetInfo, bool> query) => beatmaps.BeatmapSets.FirstOrDefault(query);
|
||||
|
||||
/// <summary>
|
||||
/// Refresh an existing instance of a <see cref="BeatmapSetInfo"/> from the store.
|
||||
@ -357,35 +388,21 @@ namespace osu.Game.Beatmaps
|
||||
/// </summary>
|
||||
/// <param name="query">The query.</param>
|
||||
/// <returns>Results from the provided query.</returns>
|
||||
public List<BeatmapSetInfo> QueryBeatmapSets(Expression<Func<BeatmapSetInfo, bool>> query)
|
||||
{
|
||||
return beatmaps.QueryAndPopulate(query);
|
||||
}
|
||||
public List<BeatmapSetInfo> QueryBeatmapSets(Func<BeatmapSetInfo, bool> query) => beatmaps.BeatmapSets.Where(query).ToList();
|
||||
|
||||
/// <summary>
|
||||
/// Perform a lookup query on available <see cref="BeatmapInfo"/>s.
|
||||
/// </summary>
|
||||
/// <param name="query">The query.</param>
|
||||
/// <returns>The first result for the provided query, or null if no results were found.</returns>
|
||||
public BeatmapInfo QueryBeatmap(Func<BeatmapInfo, bool> query)
|
||||
{
|
||||
BeatmapInfo set = beatmaps.Query<BeatmapInfo>().FirstOrDefault(query);
|
||||
|
||||
if (set != null)
|
||||
beatmaps.Populate(set);
|
||||
|
||||
return set;
|
||||
}
|
||||
public BeatmapInfo QueryBeatmap(Func<BeatmapInfo, bool> query) => beatmaps.Beatmaps.FirstOrDefault(query);
|
||||
|
||||
/// <summary>
|
||||
/// Perform a lookup query on available <see cref="BeatmapInfo"/>s.
|
||||
/// </summary>
|
||||
/// <param name="query">The query.</param>
|
||||
/// <returns>Results from the provided query.</returns>
|
||||
public List<BeatmapInfo> QueryBeatmaps(Expression<Func<BeatmapInfo, bool>> query)
|
||||
{
|
||||
lock (beatmaps) return beatmaps.QueryAndPopulate(query);
|
||||
}
|
||||
public List<BeatmapInfo> QueryBeatmaps(Func<BeatmapInfo, bool> query) => beatmaps.Beatmaps.Where(query).ToList();
|
||||
|
||||
/// <summary>
|
||||
/// Creates an <see cref="ArchiveReader"/> from a valid storage path.
|
||||
@ -395,9 +412,9 @@ namespace osu.Game.Beatmaps
|
||||
private ArchiveReader getReaderFrom(string path)
|
||||
{
|
||||
if (ZipFile.IsZipFile(path))
|
||||
// ReSharper disable once InconsistentlySynchronizedField
|
||||
return new OszArchiveReader(storage.GetStream(path));
|
||||
else
|
||||
return new LegacyFilesystemReader(path);
|
||||
return new LegacyFilesystemReader(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -406,7 +423,7 @@ namespace osu.Game.Beatmaps
|
||||
/// </summary>
|
||||
/// <param name="reader">The beatmap archive to be read.</param>
|
||||
/// <returns>The imported beatmap, or an existing instance if it is already present.</returns>
|
||||
private BeatmapSetInfo importToStorage(ArchiveReader reader)
|
||||
private BeatmapSetInfo importToStorage(FileStore files, BeatmapStore beatmaps, ArchiveReader reader)
|
||||
{
|
||||
// let's make sure there are actually .osu files to import.
|
||||
string mapName = reader.Filenames.FirstOrDefault(f => f.EndsWith(".osu"));
|
||||
@ -422,13 +439,11 @@ namespace osu.Game.Beatmaps
|
||||
var hash = hashable.ComputeSHA2Hash();
|
||||
|
||||
// check if this beatmap has already been imported and exit early if so.
|
||||
BeatmapSetInfo beatmapSet;
|
||||
lock (beatmaps)
|
||||
beatmapSet = beatmaps.QueryAndPopulate<BeatmapSetInfo>(b => b.Hash == hash).FirstOrDefault();
|
||||
var beatmapSet = beatmaps.BeatmapSets.FirstOrDefault(b => b.Hash == hash);
|
||||
|
||||
if (beatmapSet != null)
|
||||
{
|
||||
Undelete(beatmapSet);
|
||||
undelete(beatmaps, files, beatmapSet);
|
||||
|
||||
// ensure all files are present and accessible
|
||||
foreach (var f in beatmapSet.Files)
|
||||
@ -438,6 +453,8 @@ namespace osu.Game.Beatmaps
|
||||
files.Add(s, false);
|
||||
}
|
||||
|
||||
// todo: delete any files which shouldn't exist any more.
|
||||
|
||||
return beatmapSet;
|
||||
}
|
||||
|
||||
@ -487,10 +504,11 @@ namespace osu.Game.Beatmaps
|
||||
// TODO: Diff beatmap metadata with set metadata and leave it here if necessary
|
||||
beatmap.BeatmapInfo.Metadata = null;
|
||||
|
||||
RulesetInfo ruleset = rulesets.GetRuleset(beatmap.BeatmapInfo.RulesetID);
|
||||
|
||||
// TODO: this should be done in a better place once we actually need to dynamically update it.
|
||||
beatmap.BeatmapInfo.Ruleset = rulesets.Query<RulesetInfo>().FirstOrDefault(r => r.ID == beatmap.BeatmapInfo.RulesetID);
|
||||
beatmap.BeatmapInfo.StarDifficulty = rulesets.Query<RulesetInfo>().FirstOrDefault(r => r.ID == beatmap.BeatmapInfo.RulesetID)?.CreateInstance()?.CreateDifficultyCalculator(beatmap)
|
||||
.Calculate() ?? 0;
|
||||
beatmap.BeatmapInfo.Ruleset = ruleset;
|
||||
beatmap.BeatmapInfo.StarDifficulty = ruleset?.CreateInstance()?.CreateDifficultyCalculator(beatmap).Calculate() ?? 0;
|
||||
|
||||
beatmapSet.Beatmaps.Add(beatmap.BeatmapInfo);
|
||||
}
|
||||
@ -502,17 +520,10 @@ namespace osu.Game.Beatmaps
|
||||
/// <summary>
|
||||
/// Returns a list of all usable <see cref="BeatmapSetInfo"/>s.
|
||||
/// </summary>
|
||||
/// <param name="populate">Whether returned objects should be pre-populated with all data.</param>
|
||||
/// <returns>A list of available <see cref="BeatmapSetInfo"/>.</returns>
|
||||
public List<BeatmapSetInfo> GetAllUsableBeatmapSets(bool populate = true)
|
||||
public List<BeatmapSetInfo> GetAllUsableBeatmapSets()
|
||||
{
|
||||
lock (beatmaps)
|
||||
{
|
||||
if (populate)
|
||||
return beatmaps.QueryAndPopulate<BeatmapSetInfo>(b => !b.DeletePending).ToList();
|
||||
else
|
||||
return beatmaps.Query<BeatmapSetInfo>(b => !b.DeletePending).ToList();
|
||||
}
|
||||
return beatmaps.BeatmapSets.Where(s => !s.DeletePending).ToList();
|
||||
}
|
||||
|
||||
protected class BeatmapManagerWorkingBeatmap : WorkingBeatmap
|
||||
@ -547,7 +558,10 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
return beatmap;
|
||||
}
|
||||
catch { return null; }
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private string getPathForFile(string filename) => BeatmapSetInfo.Files.First(f => string.Equals(f.Filename, filename, StringComparison.InvariantCultureIgnoreCase)).FileInfo.StoragePath;
|
||||
@ -561,7 +575,10 @@ namespace osu.Game.Beatmaps
|
||||
{
|
||||
return new TextureStore(new RawTextureLoaderStore(store), false).Get(getPathForFile(Metadata.BackgroundFile));
|
||||
}
|
||||
catch { return null; }
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected override Track GetTrack()
|
||||
@ -571,7 +588,10 @@ namespace osu.Game.Beatmaps
|
||||
var trackData = store.GetStream(getPathForFile(Metadata.AudioFile));
|
||||
return trackData == null ? null : new TrackBass(trackData);
|
||||
}
|
||||
catch { return new TrackVirtual(); }
|
||||
catch
|
||||
{
|
||||
return new TrackVirtual();
|
||||
}
|
||||
}
|
||||
|
||||
protected override Waveform GetWaveform() => new Waveform(store.GetStream(getPathForFile(Metadata.AudioFile)));
|
||||
@ -595,9 +615,9 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
public void DeleteAll()
|
||||
{
|
||||
var maps = GetAllUsableBeatmapSets().ToArray();
|
||||
var maps = GetAllUsableBeatmapSets();
|
||||
|
||||
if (maps.Length == 0) return;
|
||||
if (maps.Count == 0) return;
|
||||
|
||||
var notification = new ProgressNotification
|
||||
{
|
||||
@ -615,8 +635,8 @@ namespace osu.Game.Beatmaps
|
||||
// user requested abort
|
||||
return;
|
||||
|
||||
notification.Text = $"Deleting ({i} of {maps.Length})";
|
||||
notification.Progress = (float)++i / maps.Length;
|
||||
notification.Text = $"Deleting ({i} of {maps.Count})";
|
||||
notification.Progress = (float)++i / maps.Count;
|
||||
Delete(b);
|
||||
}
|
||||
|
||||
|
@ -1,18 +1,20 @@
|
||||
// Copyright (c) 2007-2017 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 System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using osu.Game.Users;
|
||||
using SQLite.Net.Attributes;
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
{
|
||||
public class BeatmapMetadata
|
||||
{
|
||||
[PrimaryKey, AutoIncrement]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public int ID { get; set; }
|
||||
|
||||
[NotMapped]
|
||||
public int? OnlineBeatmapSetID { get; set; }
|
||||
|
||||
public string Title { get; set; }
|
||||
@ -20,6 +22,9 @@ namespace osu.Game.Beatmaps
|
||||
public string Artist { get; set; }
|
||||
public string ArtistUnicode { get; set; }
|
||||
|
||||
public List<BeatmapInfo> Beatmaps { get; set; }
|
||||
public List<BeatmapSetInfo> BeatmapSets { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Helper property to deserialize a username to <see cref="User"/>.
|
||||
/// </summary>
|
||||
|
@ -1,27 +1,24 @@
|
||||
// Copyright (c) 2007-2017 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.IO;
|
||||
using SQLite.Net.Attributes;
|
||||
using SQLiteNetExtensions.Attributes;
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
{
|
||||
public class BeatmapSetFileInfo
|
||||
{
|
||||
[PrimaryKey, AutoIncrement]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public int ID { get; set; }
|
||||
|
||||
[ForeignKey(typeof(BeatmapSetInfo)), NotNull]
|
||||
public int BeatmapSetInfoID { get; set; }
|
||||
|
||||
[ForeignKey(typeof(FileInfo)), NotNull]
|
||||
public int FileInfoID { get; set; }
|
||||
|
||||
[OneToOne(CascadeOperations = CascadeOperation.CascadeRead)]
|
||||
public FileInfo FileInfo { get; set; }
|
||||
|
||||
[NotNull]
|
||||
[Required]
|
||||
public string Filename { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,41 +2,34 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
using SQLite.Net.Attributes;
|
||||
using SQLiteNetExtensions.Attributes;
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
{
|
||||
public class BeatmapSetInfo
|
||||
{
|
||||
[PrimaryKey, AutoIncrement]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public int ID { get; set; }
|
||||
|
||||
public int? OnlineBeatmapSetID { get; set; }
|
||||
|
||||
[OneToOne(CascadeOperations = CascadeOperation.All)]
|
||||
public BeatmapMetadata Metadata { get; set; }
|
||||
|
||||
[NotNull, ForeignKey(typeof(BeatmapMetadata))]
|
||||
public int BeatmapMetadataID { get; set; }
|
||||
|
||||
[OneToMany(CascadeOperations = CascadeOperation.All)]
|
||||
public List<BeatmapInfo> Beatmaps { get; set; }
|
||||
|
||||
[Ignore]
|
||||
[NotMapped]
|
||||
public BeatmapSetOnlineInfo OnlineInfo { get; set; }
|
||||
|
||||
public double MaxStarDifficulty => Beatmaps.Max(b => b.StarDifficulty);
|
||||
|
||||
[Indexed]
|
||||
[NotMapped]
|
||||
public bool DeletePending { get; set; }
|
||||
|
||||
public string Hash { get; set; }
|
||||
|
||||
public string StoryboardFile => Files.FirstOrDefault(f => f.Filename.EndsWith(".osb"))?.Filename;
|
||||
|
||||
[OneToMany(CascadeOperations = CascadeOperation.All)]
|
||||
public List<BeatmapSetFileInfo> Files { get; set; }
|
||||
|
||||
public bool Protected { get; set; }
|
||||
|
@ -2,9 +2,10 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using osu.Game.Database;
|
||||
using SQLite.Net;
|
||||
using SQLiteNetExtensions.Extensions;
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
{
|
||||
@ -19,76 +20,23 @@ namespace osu.Game.Beatmaps
|
||||
public event Action<BeatmapInfo> BeatmapHidden;
|
||||
public event Action<BeatmapInfo> BeatmapRestored;
|
||||
|
||||
/// <summary>
|
||||
/// The current version of this store. Used for migrations (see <see cref="PerformMigration(int, int)"/>).
|
||||
/// The initial version is 1.
|
||||
/// </summary>
|
||||
protected override int StoreVersion => 4;
|
||||
|
||||
public BeatmapStore(SQLiteConnection connection)
|
||||
: base(connection)
|
||||
public BeatmapStore(Func<OsuDbContext> factory)
|
||||
: base(factory)
|
||||
{
|
||||
}
|
||||
|
||||
protected override Type[] ValidTypes => new[]
|
||||
{
|
||||
typeof(BeatmapSetInfo),
|
||||
typeof(BeatmapInfo),
|
||||
typeof(BeatmapMetadata),
|
||||
typeof(BeatmapDifficulty),
|
||||
};
|
||||
|
||||
protected override void Prepare(bool reset = false)
|
||||
{
|
||||
if (reset)
|
||||
{
|
||||
Connection.DropTable<BeatmapMetadata>();
|
||||
Connection.DropTable<BeatmapDifficulty>();
|
||||
Connection.DropTable<BeatmapSetInfo>();
|
||||
Connection.DropTable<BeatmapSetFileInfo>();
|
||||
Connection.DropTable<BeatmapInfo>();
|
||||
}
|
||||
var context = GetContext();
|
||||
|
||||
Connection.CreateTable<BeatmapMetadata>();
|
||||
Connection.CreateTable<BeatmapDifficulty>();
|
||||
Connection.CreateTable<BeatmapSetInfo>();
|
||||
Connection.CreateTable<BeatmapSetFileInfo>();
|
||||
Connection.CreateTable<BeatmapInfo>();
|
||||
}
|
||||
|
||||
protected override void StartupTasks()
|
||||
{
|
||||
base.StartupTasks();
|
||||
cleanupPendingDeletions();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform migrations between two store versions.
|
||||
/// </summary>
|
||||
/// <param name="currentVersion">The current store version. This will be zero on a fresh database initialisation.</param>
|
||||
/// <param name="targetVersion">The target version which we are migrating to (equal to the current <see cref="StoreVersion"/>).</param>
|
||||
protected override void PerformMigration(int currentVersion, int targetVersion)
|
||||
{
|
||||
base.PerformMigration(currentVersion, targetVersion);
|
||||
|
||||
while (currentVersion++ < targetVersion)
|
||||
{
|
||||
switch (currentVersion)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
// cannot migrate; breaking underlying changes.
|
||||
Reset();
|
||||
break;
|
||||
case 3:
|
||||
// Added MD5Hash column to BeatmapInfo
|
||||
Connection.MigrateTable<BeatmapInfo>();
|
||||
break;
|
||||
case 4:
|
||||
// Added Hidden column to BeatmapInfo
|
||||
Connection.MigrateTable<BeatmapInfo>();
|
||||
break;
|
||||
}
|
||||
// https://stackoverflow.com/a/10450893
|
||||
context.Database.ExecuteSqlCommand("DELETE FROM BeatmapMetadata");
|
||||
context.Database.ExecuteSqlCommand("DELETE FROM BeatmapDifficulty");
|
||||
context.Database.ExecuteSqlCommand("DELETE FROM BeatmapSetInfo");
|
||||
context.Database.ExecuteSqlCommand("DELETE FROM BeatmapSetFileInfo");
|
||||
context.Database.ExecuteSqlCommand("DELETE FROM BeatmapInfo");
|
||||
}
|
||||
}
|
||||
|
||||
@ -98,10 +46,10 @@ namespace osu.Game.Beatmaps
|
||||
/// <param name="beatmapSet">The beatmap to add.</param>
|
||||
public void Add(BeatmapSetInfo beatmapSet)
|
||||
{
|
||||
Connection.RunInTransaction(() =>
|
||||
{
|
||||
Connection.InsertOrReplaceWithChildren(beatmapSet, true);
|
||||
});
|
||||
var context = GetContext();
|
||||
|
||||
context.BeatmapSetInfo.Attach(beatmapSet);
|
||||
context.SaveChanges();
|
||||
|
||||
BeatmapSetAdded?.Invoke(beatmapSet);
|
||||
}
|
||||
@ -113,10 +61,12 @@ namespace osu.Game.Beatmaps
|
||||
/// <returns>Whether the beatmap's <see cref="BeatmapSetInfo.DeletePending"/> was changed.</returns>
|
||||
public bool Delete(BeatmapSetInfo beatmapSet)
|
||||
{
|
||||
var context = GetContext();
|
||||
|
||||
if (beatmapSet.DeletePending) return false;
|
||||
|
||||
beatmapSet.DeletePending = true;
|
||||
Connection.Update(beatmapSet);
|
||||
context.SaveChanges();
|
||||
|
||||
BeatmapSetRemoved?.Invoke(beatmapSet);
|
||||
return true;
|
||||
@ -129,10 +79,12 @@ namespace osu.Game.Beatmaps
|
||||
/// <returns>Whether the beatmap's <see cref="BeatmapSetInfo.DeletePending"/> was changed.</returns>
|
||||
public bool Undelete(BeatmapSetInfo beatmapSet)
|
||||
{
|
||||
var context = GetContext();
|
||||
|
||||
if (!beatmapSet.DeletePending) return false;
|
||||
|
||||
beatmapSet.DeletePending = false;
|
||||
Connection.Update(beatmapSet);
|
||||
context.SaveChanges();
|
||||
|
||||
BeatmapSetAdded?.Invoke(beatmapSet);
|
||||
return true;
|
||||
@ -145,10 +97,12 @@ namespace osu.Game.Beatmaps
|
||||
/// <returns>Whether the beatmap's <see cref="BeatmapInfo.Hidden"/> was changed.</returns>
|
||||
public bool Hide(BeatmapInfo beatmap)
|
||||
{
|
||||
var context = GetContext();
|
||||
|
||||
if (beatmap.Hidden) return false;
|
||||
|
||||
beatmap.Hidden = true;
|
||||
Connection.Update(beatmap);
|
||||
context.SaveChanges();
|
||||
|
||||
BeatmapHidden?.Invoke(beatmap);
|
||||
return true;
|
||||
@ -161,22 +115,49 @@ namespace osu.Game.Beatmaps
|
||||
/// <returns>Whether the beatmap's <see cref="BeatmapInfo.Hidden"/> was changed.</returns>
|
||||
public bool Restore(BeatmapInfo beatmap)
|
||||
{
|
||||
var context = GetContext();
|
||||
|
||||
if (!beatmap.Hidden) return false;
|
||||
|
||||
beatmap.Hidden = false;
|
||||
Connection.Update(beatmap);
|
||||
context.SaveChanges();
|
||||
|
||||
BeatmapRestored?.Invoke(beatmap);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void cleanupPendingDeletions()
|
||||
public override void Cleanup()
|
||||
{
|
||||
Connection.RunInTransaction(() =>
|
||||
{
|
||||
foreach (var b in QueryAndPopulate<BeatmapSetInfo>(b => b.DeletePending && !b.Protected))
|
||||
Connection.Delete(b, true);
|
||||
});
|
||||
var context = GetContext();
|
||||
|
||||
var purgeable = context.BeatmapSetInfo.Where(s => s.DeletePending && !s.Protected)
|
||||
.Include(s => s.Beatmaps).ThenInclude(b => b.Metadata)
|
||||
.Include(s => s.Beatmaps).ThenInclude(b => b.BaseDifficulty)
|
||||
.Include(s => s.Metadata);
|
||||
|
||||
// metadata is M-N so we can't rely on cascades
|
||||
context.BeatmapMetadata.RemoveRange(purgeable.Select(s => s.Metadata));
|
||||
context.BeatmapMetadata.RemoveRange(purgeable.SelectMany(s => s.Beatmaps.Select(b => b.Metadata)));
|
||||
|
||||
// todo: we can probably make cascades work here with a FK in BeatmapDifficulty. just make to make it work correctly.
|
||||
context.BeatmapDifficulty.RemoveRange(purgeable.SelectMany(s => s.Beatmaps.Select(b => b.BaseDifficulty)));
|
||||
|
||||
// cascades down to beatmaps.
|
||||
context.BeatmapSetInfo.RemoveRange(purgeable);
|
||||
context.SaveChanges();
|
||||
}
|
||||
|
||||
public IEnumerable<BeatmapSetInfo> BeatmapSets => GetContext().BeatmapSetInfo
|
||||
.Include(s => s.Metadata)
|
||||
.Include(s => s.Beatmaps).ThenInclude(s => s.Ruleset)
|
||||
.Include(s => s.Beatmaps).ThenInclude(b => b.BaseDifficulty)
|
||||
.Include(s => s.Beatmaps).ThenInclude(b => b.Metadata)
|
||||
.Include(s => s.Files).ThenInclude(f => f.FileInfo);
|
||||
|
||||
public IEnumerable<BeatmapInfo> Beatmaps => GetContext().BeatmapInfo
|
||||
.Include(b => b.BeatmapSet).ThenInclude(s => s.Metadata)
|
||||
.Include(b => b.Metadata)
|
||||
.Include(b => b.Ruleset)
|
||||
.Include(b => b.BaseDifficulty);
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ namespace osu.Game.Beatmaps
|
||||
Objects = CreateBeatmapConverter().Convert(beatmap).HitObjects;
|
||||
|
||||
foreach (var h in Objects)
|
||||
h.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.Difficulty);
|
||||
h.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.BaseDifficulty);
|
||||
|
||||
PreprocessHitObjects();
|
||||
}
|
||||
|
@ -83,8 +83,7 @@ namespace osu.Game.Beatmaps.Drawables
|
||||
RelativeSizeAxes = Axes.X,
|
||||
};
|
||||
|
||||
BeatmapSet.Beatmaps = BeatmapSet.Beatmaps.Where(b => !b.Hidden).OrderBy(b => b.StarDifficulty).ToList();
|
||||
BeatmapPanels = BeatmapSet.Beatmaps.Select(b => new BeatmapPanel(b)
|
||||
BeatmapPanels = BeatmapSet.Beatmaps.Where(b => !b.Hidden).OrderBy(b => b.StarDifficulty).Select(b => new BeatmapPanel(b)
|
||||
{
|
||||
Alpha = 0,
|
||||
GainedSelection = panelGainedSelection,
|
||||
|
@ -135,7 +135,7 @@ namespace osu.Game.Beatmaps.Drawables
|
||||
new OsuSpriteText
|
||||
{
|
||||
Font = @"Exo2.0-MediumItalic",
|
||||
Text = $"{(beatmap.Metadata ?? beatmap.BeatmapSet.Metadata).Author}",
|
||||
Text = $"{(beatmap.Metadata ?? beatmap.BeatmapSet.Metadata).Author.Username}",
|
||||
TextSize = 16,
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft
|
||||
|
@ -25,7 +25,7 @@ namespace osu.Game.Beatmaps
|
||||
AuthorString = "no one",
|
||||
},
|
||||
BeatmapSet = new BeatmapSetInfo(),
|
||||
Difficulty = new BeatmapDifficulty
|
||||
BaseDifficulty = new BeatmapDifficulty
|
||||
{
|
||||
DrainRate = 0,
|
||||
CircleSize = 0,
|
||||
|
@ -49,7 +49,7 @@ namespace osu.Game.Beatmaps.Formats
|
||||
BeatmapInfo = new BeatmapInfo
|
||||
{
|
||||
Metadata = new BeatmapMetadata(),
|
||||
Difficulty = new BeatmapDifficulty(),
|
||||
BaseDifficulty = new BeatmapDifficulty(),
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -196,7 +196,7 @@ namespace osu.Game.Beatmaps.Formats
|
||||
{
|
||||
var pair = splitKeyVal(line, ':');
|
||||
|
||||
var difficulty = beatmap.BeatmapInfo.Difficulty;
|
||||
var difficulty = beatmap.BeatmapInfo.BaseDifficulty;
|
||||
switch (pair.Key)
|
||||
{
|
||||
case @"HPDrainRate":
|
||||
@ -674,7 +674,7 @@ namespace osu.Game.Beatmaps.Formats
|
||||
}
|
||||
|
||||
foreach (var hitObject in beatmap.HitObjects)
|
||||
hitObject.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.Difficulty);
|
||||
hitObject.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.BaseDifficulty);
|
||||
}
|
||||
|
||||
private KeyValuePair<string, string> splitKeyVal(string line, char separator)
|
||||
|
@ -2,27 +2,21 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Platform;
|
||||
using SQLite.Net;
|
||||
using SQLiteNetExtensions.Extensions;
|
||||
|
||||
namespace osu.Game.Database
|
||||
{
|
||||
public abstract class DatabaseBackedStore
|
||||
{
|
||||
protected readonly Storage Storage;
|
||||
protected readonly SQLiteConnection Connection;
|
||||
|
||||
protected virtual int StoreVersion => 1;
|
||||
protected readonly Func<OsuDbContext> GetContext;
|
||||
|
||||
protected DatabaseBackedStore(SQLiteConnection connection, Storage storage = null)
|
||||
protected DatabaseBackedStore(Func<OsuDbContext> getContext, Storage storage = null)
|
||||
{
|
||||
Storage = storage;
|
||||
Connection = connection;
|
||||
GetContext = getContext;
|
||||
|
||||
try
|
||||
{
|
||||
@ -33,46 +27,15 @@ namespace osu.Game.Database
|
||||
Logger.Error(e, $@"Failed to initialise the {GetType()}! Trying again with a clean database...");
|
||||
Prepare(true);
|
||||
}
|
||||
|
||||
checkMigrations();
|
||||
}
|
||||
|
||||
private void checkMigrations()
|
||||
{
|
||||
var storeName = GetType().Name;
|
||||
|
||||
var reportedVersion = Connection.Table<StoreVersion>().Where(s => s.StoreName == storeName).FirstOrDefault() ?? new StoreVersion
|
||||
{
|
||||
StoreName = storeName,
|
||||
Version = 0
|
||||
};
|
||||
|
||||
if (reportedVersion.Version != StoreVersion)
|
||||
PerformMigration(reportedVersion.Version, reportedVersion.Version = StoreVersion);
|
||||
|
||||
Connection.InsertOrReplace(reportedVersion);
|
||||
|
||||
StartupTasks();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the database version of this store doesn't match the local version.
|
||||
/// Any manual migration operations should be performed in this.
|
||||
/// Perform any common clean-up tasks. Should be run when idle, or whenever necessary.
|
||||
/// </summary>
|
||||
/// <param name="currentVersion">The current store version. This will be zero on a fresh database initialisation.</param>
|
||||
/// <param name="targetVersion">The target version which we are migrating to (equal to the current <see cref="StoreVersion"/>).</param>
|
||||
protected virtual void PerformMigration(int currentVersion, int targetVersion)
|
||||
public virtual void Cleanup()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform any common startup tasks. Runs after <see cref="Prepare(bool)"/> and <see cref="PerformMigration(int, int)"/>.
|
||||
/// </summary>
|
||||
protected virtual void StartupTasks()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prepare this database for use. Tables should be created here.
|
||||
/// </summary>
|
||||
@ -82,50 +45,5 @@ namespace osu.Game.Database
|
||||
/// Reset this database to a default state. Undo all changes to database and storage backings.
|
||||
/// </summary>
|
||||
public void Reset() => Prepare(true);
|
||||
|
||||
|
||||
public TableQuery<T> Query<T>(Expression<Func<T, bool>> filter = null) where T : class
|
||||
{
|
||||
checkType(typeof(T));
|
||||
|
||||
var query = Connection.Table<T>();
|
||||
|
||||
if (filter != null)
|
||||
query = query.Where(filter);
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Query and populate results.
|
||||
/// </summary>
|
||||
/// <param name="filter">An filter to refine results.</param>
|
||||
/// <returns></returns>
|
||||
public List<T> QueryAndPopulate<T>(Expression<Func<T, bool>> filter)
|
||||
where T : class
|
||||
{
|
||||
checkType(typeof(T));
|
||||
|
||||
return Connection.GetAllWithChildren(filter, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populate a database-backed item.
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <param name="recursive">Whether population should recurse beyond a single level.</param>
|
||||
public void Populate<T>(T item, bool recursive = true)
|
||||
{
|
||||
checkType(item.GetType());
|
||||
Connection.GetChildren(item, recursive);
|
||||
}
|
||||
|
||||
private void checkType(Type type)
|
||||
{
|
||||
if (!ValidTypes.Contains(type))
|
||||
throw new InvalidOperationException($"The requested operation specified a type of {type}, which is invalid for this {nameof(DatabaseBackedStore)}.");
|
||||
}
|
||||
|
||||
protected abstract Type[] ValidTypes { get; }
|
||||
}
|
||||
}
|
||||
|
19
osu.Game/Database/DatabaseContextFactory.cs
Normal file
19
osu.Game/Database/DatabaseContextFactory.cs
Normal file
@ -0,0 +1,19 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Platform;
|
||||
|
||||
namespace osu.Game.Database
|
||||
{
|
||||
public class DatabaseContextFactory
|
||||
{
|
||||
private readonly GameHost host;
|
||||
|
||||
public DatabaseContextFactory(GameHost host)
|
||||
{
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
public OsuDbContext GetContext() => new OsuDbContext(host.Storage.GetDatabaseConnectionString(@"client"));
|
||||
}
|
||||
}
|
245
osu.Game/Database/OsuDbContext.cs
Normal file
245
osu.Game/Database/OsuDbContext.cs
Normal file
@ -0,0 +1,245 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osu.Game.IO;
|
||||
using osu.Game.Rulesets;
|
||||
using LogLevel = Microsoft.Extensions.Logging.LogLevel;
|
||||
|
||||
namespace osu.Game.Database
|
||||
{
|
||||
public class OsuDbContext : DbContext
|
||||
{
|
||||
public DbSet<BeatmapInfo> BeatmapInfo { get; set; }
|
||||
public DbSet<BeatmapDifficulty> BeatmapDifficulty { get; set; }
|
||||
public DbSet<BeatmapMetadata> BeatmapMetadata { get; set; }
|
||||
public DbSet<BeatmapSetInfo> BeatmapSetInfo { get; set; }
|
||||
public DbSet<DatabasedKeyBinding> DatabasedKeyBinding { get; set; }
|
||||
public DbSet<FileInfo> FileInfo { get; set; }
|
||||
public DbSet<RulesetInfo> RulesetInfo { get; set; }
|
||||
private readonly string connectionString;
|
||||
|
||||
private static readonly Lazy<OsuDbLoggerFactory> logger = new Lazy<OsuDbLoggerFactory>(() => new OsuDbLoggerFactory());
|
||||
|
||||
static OsuDbContext()
|
||||
{
|
||||
// required to initialise native SQLite libraries on some platforms.
|
||||
SQLitePCL.Batteries_V2.Init();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new in-memory OsuDbContext instance.
|
||||
/// </summary>
|
||||
public OsuDbContext()
|
||||
: this("DataSource=:memory:")
|
||||
{
|
||||
// required for tooling (see https://wildermuth.com/2017/07/06/Program-cs-in-ASP-NET-Core-2-0).
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new OsuDbContext instance.
|
||||
/// </summary>
|
||||
/// <param name="connectionString">A valid SQLite connection string.</param>
|
||||
public OsuDbContext(string connectionString)
|
||||
{
|
||||
this.connectionString = connectionString;
|
||||
|
||||
Database.SetCommandTimeout(new TimeSpan(TimeSpan.TicksPerSecond * 10));
|
||||
|
||||
var connection = Database.GetDbConnection();
|
||||
connection.Open();
|
||||
using (var cmd = connection.CreateCommand())
|
||||
{
|
||||
cmd.CommandText = "PRAGMA journal_mode=WAL;";
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
base.OnConfiguring(optionsBuilder);
|
||||
optionsBuilder.UseSqlite(connectionString);
|
||||
optionsBuilder.UseLoggerFactory(logger.Value);
|
||||
}
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
base.OnModelCreating(modelBuilder);
|
||||
|
||||
modelBuilder.Entity<BeatmapInfo>().HasIndex(b => b.MD5Hash);
|
||||
modelBuilder.Entity<BeatmapInfo>().HasIndex(b => b.Hash);
|
||||
|
||||
modelBuilder.Entity<BeatmapSetInfo>().HasIndex(b => b.DeletePending);
|
||||
modelBuilder.Entity<BeatmapSetInfo>().HasIndex(b => b.Hash);
|
||||
|
||||
modelBuilder.Entity<DatabasedKeyBinding>().HasIndex(b => b.Variant);
|
||||
modelBuilder.Entity<DatabasedKeyBinding>().HasIndex(b => b.IntAction);
|
||||
|
||||
modelBuilder.Entity<FileInfo>().HasIndex(b => b.Hash).IsUnique();
|
||||
modelBuilder.Entity<FileInfo>().HasIndex(b => b.ReferenceCount);
|
||||
|
||||
modelBuilder.Entity<RulesetInfo>().HasIndex(b => b.Available);
|
||||
|
||||
modelBuilder.Entity<BeatmapInfo>().HasOne(b => b.BaseDifficulty);
|
||||
}
|
||||
|
||||
private class OsuDbLoggerFactory : ILoggerFactory
|
||||
{
|
||||
#region Disposal
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public ILogger CreateLogger(string categoryName) => new OsuDbLogger();
|
||||
|
||||
public void AddProvider(ILoggerProvider provider)
|
||||
{
|
||||
// no-op. called by tooling.
|
||||
}
|
||||
|
||||
private class OsuDbLoggerProvider : ILoggerProvider
|
||||
{
|
||||
#region Disposal
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public ILogger CreateLogger(string categoryName) => new OsuDbLogger();
|
||||
}
|
||||
|
||||
private class OsuDbLogger : ILogger
|
||||
{
|
||||
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
|
||||
{
|
||||
if (logLevel < LogLevel.Information)
|
||||
return;
|
||||
|
||||
Framework.Logging.LogLevel frameworkLogLevel;
|
||||
|
||||
switch (logLevel)
|
||||
{
|
||||
default:
|
||||
frameworkLogLevel = Framework.Logging.LogLevel.Debug;
|
||||
break;
|
||||
case LogLevel.Warning:
|
||||
frameworkLogLevel = Framework.Logging.LogLevel.Important;
|
||||
break;
|
||||
case LogLevel.Error:
|
||||
case LogLevel.Critical:
|
||||
frameworkLogLevel = Framework.Logging.LogLevel.Error;
|
||||
break;
|
||||
}
|
||||
|
||||
Logger.Log(formatter(state, exception), LoggingTarget.Database, frameworkLogLevel);
|
||||
}
|
||||
|
||||
public bool IsEnabled(LogLevel logLevel)
|
||||
{
|
||||
#if DEBUG
|
||||
return logLevel > LogLevel.Debug;
|
||||
#else
|
||||
return logLevel > LogLevel.Information;
|
||||
#endif
|
||||
}
|
||||
|
||||
public IDisposable BeginScope<TState>(TState state) => null;
|
||||
}
|
||||
}
|
||||
|
||||
public void Migrate()
|
||||
{
|
||||
migrateFromSqliteNet();
|
||||
Database.Migrate();
|
||||
}
|
||||
|
||||
private void migrateFromSqliteNet()
|
||||
{
|
||||
try
|
||||
{
|
||||
// will fail if EF hasn't touched the database yet.
|
||||
Database.ExecuteSqlCommand("SELECT * FROM __EFMigrationsHistory LIMIT 1");
|
||||
}
|
||||
catch
|
||||
{
|
||||
try
|
||||
{
|
||||
// will fail (intentionally) if we don't have sqlite-net data present.
|
||||
Database.ExecuteSqlCommand("SELECT OnlineBeatmapSetId FROM BeatmapMetadata LIMIT 1");
|
||||
|
||||
try
|
||||
{
|
||||
// we are good to perform messy migration of data!.
|
||||
Database.ExecuteSqlCommand("ALTER TABLE BeatmapDifficulty RENAME TO BeatmapDifficulty_Old");
|
||||
Database.ExecuteSqlCommand("ALTER TABLE BeatmapMetadata RENAME TO BeatmapMetadata_Old");
|
||||
Database.ExecuteSqlCommand("ALTER TABLE FileInfo RENAME TO FileInfo_Old");
|
||||
Database.ExecuteSqlCommand("ALTER TABLE KeyBinding RENAME TO KeyBinding_Old");
|
||||
Database.ExecuteSqlCommand("ALTER TABLE BeatmapSetInfo RENAME TO BeatmapSetInfo_Old");
|
||||
Database.ExecuteSqlCommand("ALTER TABLE BeatmapInfo RENAME TO BeatmapInfo_Old");
|
||||
Database.ExecuteSqlCommand("ALTER TABLE BeatmapSetFileInfo RENAME TO BeatmapSetFileInfo_Old");
|
||||
Database.ExecuteSqlCommand("ALTER TABLE RulesetInfo RENAME TO RulesetInfo_Old");
|
||||
|
||||
Database.ExecuteSqlCommand("DROP TABLE StoreVersion");
|
||||
|
||||
// perform EF migrations to create sane table structure.
|
||||
Database.Migrate();
|
||||
|
||||
// copy data table by table to new structure, dropping old tables as we go.
|
||||
Database.ExecuteSqlCommand("INSERT INTO FileInfo SELECT * FROM FileInfo_Old");
|
||||
Database.ExecuteSqlCommand("DROP TABLE FileInfo_Old");
|
||||
|
||||
Database.ExecuteSqlCommand("INSERT INTO KeyBinding SELECT ID, [Action], Keys, RulesetID, Variant FROM KeyBinding_Old");
|
||||
Database.ExecuteSqlCommand("DROP TABLE KeyBinding_Old");
|
||||
|
||||
Database.ExecuteSqlCommand(
|
||||
"INSERT INTO BeatmapMetadata SELECT ID, Artist, ArtistUnicode, AudioFile, Author, BackgroundFile, PreviewTime, Source, Tags, Title, TitleUnicode FROM BeatmapMetadata_Old");
|
||||
Database.ExecuteSqlCommand("DROP TABLE BeatmapMetadata_Old");
|
||||
|
||||
Database.ExecuteSqlCommand(
|
||||
"INSERT INTO BeatmapDifficulty SELECT `ID`, `ApproachRate`, `CircleSize`, `DrainRate`, `OverallDifficulty`, `SliderMultiplier`, `SliderTickRate` FROM BeatmapDifficulty_Old");
|
||||
Database.ExecuteSqlCommand("DROP TABLE BeatmapDifficulty_Old");
|
||||
|
||||
Database.ExecuteSqlCommand("INSERT INTO BeatmapSetInfo SELECT ID, DeletePending, Hash, BeatmapMetadataID, OnlineBeatmapSetID, Protected FROM BeatmapSetInfo_Old");
|
||||
Database.ExecuteSqlCommand("DROP TABLE BeatmapSetInfo_Old");
|
||||
|
||||
Database.ExecuteSqlCommand("INSERT INTO BeatmapSetFileInfo SELECT ID, BeatmapSetInfoID, FileInfoID, Filename FROM BeatmapSetFileInfo_Old");
|
||||
Database.ExecuteSqlCommand("DROP TABLE BeatmapSetFileInfo_Old");
|
||||
|
||||
Database.ExecuteSqlCommand("INSERT INTO RulesetInfo SELECT ID, Available, InstantiationInfo, Name FROM RulesetInfo_Old");
|
||||
Database.ExecuteSqlCommand("DROP TABLE RulesetInfo_Old");
|
||||
|
||||
Database.ExecuteSqlCommand(
|
||||
"INSERT INTO BeatmapInfo SELECT ID, AudioLeadIn, BaseDifficultyID, BeatDivisor, BeatmapSetInfoID, Countdown, DistanceSpacing, GridSize, Hash, Hidden, LetterboxInBreaks, MD5Hash, NULLIF(BeatmapMetadataID, 0), OnlineBeatmapID, Path, RulesetID, SpecialStyle, StackLeniency, StarDifficulty, StoredBookmarks, TimelineZoom, Version, WidescreenStoryboard FROM BeatmapInfo_Old");
|
||||
Database.ExecuteSqlCommand("DROP TABLE BeatmapInfo_Old");
|
||||
}
|
||||
catch
|
||||
{
|
||||
// if anything went wrong during migration just nuke the database.
|
||||
throw new MigrationFailedException();
|
||||
}
|
||||
}
|
||||
catch (MigrationFailedException e)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class MigrationFailedException : Exception
|
||||
{
|
||||
}
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using SQLite.Net.Attributes;
|
||||
|
||||
namespace osu.Game.Database
|
||||
{
|
||||
public class StoreVersion
|
||||
{
|
||||
[PrimaryKey]
|
||||
public string StoreName { get; set; }
|
||||
|
||||
public int Version { get; set; }
|
||||
}
|
||||
}
|
@ -1,22 +1,20 @@
|
||||
// Copyright (c) 2007-2017 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 System.IO;
|
||||
using SQLite.Net.Attributes;
|
||||
|
||||
namespace osu.Game.IO
|
||||
{
|
||||
public class FileInfo
|
||||
{
|
||||
[PrimaryKey, AutoIncrement]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public int ID { get; set; }
|
||||
|
||||
[Indexed(Unique = true)]
|
||||
public string Hash { get; set; }
|
||||
|
||||
public string StoragePath => Path.Combine(Hash.Remove(1), Hash.Remove(2), Hash);
|
||||
|
||||
[Indexed]
|
||||
public int ReferenceCount { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -4,12 +4,12 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.IO.Stores;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Game.Database;
|
||||
using SQLite.Net;
|
||||
|
||||
namespace osu.Game.IO
|
||||
{
|
||||
@ -18,75 +18,37 @@ namespace osu.Game.IO
|
||||
/// </summary>
|
||||
public class FileStore : DatabaseBackedStore
|
||||
{
|
||||
private const string prefix = "files";
|
||||
public readonly IResourceStore<byte[]> Store;
|
||||
|
||||
public readonly ResourceStore<byte[]> Store;
|
||||
public Storage Storage => base.Storage;
|
||||
|
||||
protected override int StoreVersion => 2;
|
||||
|
||||
public FileStore(SQLiteConnection connection, Storage storage) : base(connection, storage)
|
||||
public FileStore(Func<OsuDbContext> getContext, Storage storage) : base(getContext, storage.GetStorageForDirectory(@"files"))
|
||||
{
|
||||
Store = new NamespacedResourceStore<byte[]>(new StorageBackedResourceStore(storage), prefix);
|
||||
Store = new StorageBackedResourceStore(Storage);
|
||||
}
|
||||
|
||||
protected override Type[] ValidTypes => new[] {
|
||||
typeof(FileInfo),
|
||||
};
|
||||
|
||||
protected override void Prepare(bool reset = false)
|
||||
{
|
||||
if (reset)
|
||||
{
|
||||
// in earlier versions we stored beatmaps as solid archives, but not any more.
|
||||
if (Storage.ExistsDirectory("beatmaps"))
|
||||
Storage.DeleteDirectory("beatmaps");
|
||||
if (Storage.ExistsDirectory(string.Empty))
|
||||
Storage.DeleteDirectory(string.Empty);
|
||||
|
||||
if (Storage.ExistsDirectory(prefix))
|
||||
Storage.DeleteDirectory(prefix);
|
||||
|
||||
Connection.DropTable<FileInfo>();
|
||||
}
|
||||
|
||||
Connection.CreateTable<FileInfo>();
|
||||
}
|
||||
|
||||
protected override void StartupTasks()
|
||||
{
|
||||
base.StartupTasks();
|
||||
deletePending();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform migrations between two store versions.
|
||||
/// </summary>
|
||||
/// <param name="currentVersion">The current store version. This will be zero on a fresh database initialisation.</param>
|
||||
/// <param name="targetVersion">The target version which we are migrating to (equal to the current <see cref="StoreVersion"/>).</param>
|
||||
protected override void PerformMigration(int currentVersion, int targetVersion)
|
||||
{
|
||||
base.PerformMigration(currentVersion, targetVersion);
|
||||
|
||||
while (currentVersion++ < targetVersion)
|
||||
{
|
||||
switch (currentVersion)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
// cannot migrate; breaking underlying changes.
|
||||
Reset();
|
||||
break;
|
||||
}
|
||||
GetContext().Database.ExecuteSqlCommand("DELETE FROM FileInfo");
|
||||
}
|
||||
}
|
||||
|
||||
public FileInfo Add(Stream data, bool reference = true)
|
||||
{
|
||||
var context = GetContext();
|
||||
|
||||
string hash = data.ComputeSHA2Hash();
|
||||
|
||||
var existing = Connection.Table<FileInfo>().Where(f => f.Hash == hash).FirstOrDefault();
|
||||
var existing = context.FileInfo.FirstOrDefault(f => f.Hash == hash);
|
||||
|
||||
var info = existing ?? new FileInfo { Hash = hash };
|
||||
|
||||
string path = Path.Combine(prefix, info.StoragePath);
|
||||
string path = info.StoragePath;
|
||||
|
||||
// we may be re-adding a file to fix missing store entries.
|
||||
if (!Storage.Exists(path))
|
||||
@ -99,62 +61,58 @@ namespace osu.Game.IO
|
||||
data.Seek(0, SeekOrigin.Begin);
|
||||
}
|
||||
|
||||
if (existing == null)
|
||||
Connection.Insert(info);
|
||||
|
||||
if (reference || existing == null)
|
||||
Reference(info);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
public void Reference(params FileInfo[] files)
|
||||
{
|
||||
Connection.RunInTransaction(() =>
|
||||
{
|
||||
var incrementedFiles = files.GroupBy(f => f.ID).Select(f =>
|
||||
{
|
||||
var accurateRefCount = Connection.Get<FileInfo>(f.First().ID);
|
||||
accurateRefCount.ReferenceCount += f.Count();
|
||||
return accurateRefCount;
|
||||
});
|
||||
public void Reference(params FileInfo[] files) => reference(GetContext(), files);
|
||||
|
||||
Connection.UpdateAll(incrementedFiles);
|
||||
});
|
||||
private void reference(OsuDbContext context, FileInfo[] files)
|
||||
{
|
||||
foreach (var f in files.GroupBy(f => f.ID))
|
||||
{
|
||||
var refetch = context.Find<FileInfo>(f.First().ID) ?? f.First();
|
||||
refetch.ReferenceCount += f.Count();
|
||||
context.FileInfo.Update(refetch);
|
||||
}
|
||||
|
||||
context.SaveChanges();
|
||||
}
|
||||
|
||||
public void Dereference(params FileInfo[] files)
|
||||
{
|
||||
Connection.RunInTransaction(() =>
|
||||
{
|
||||
var incrementedFiles = files.GroupBy(f => f.ID).Select(f =>
|
||||
{
|
||||
var accurateRefCount = Connection.Get<FileInfo>(f.First().ID);
|
||||
accurateRefCount.ReferenceCount -= f.Count();
|
||||
return accurateRefCount;
|
||||
});
|
||||
public void Dereference(params FileInfo[] files) => dereference(GetContext(), files);
|
||||
|
||||
Connection.UpdateAll(incrementedFiles);
|
||||
});
|
||||
private void dereference(OsuDbContext context, FileInfo[] files)
|
||||
{
|
||||
foreach (var f in files.GroupBy(f => f.ID))
|
||||
{
|
||||
var refetch = context.FileInfo.Find(f.Key);
|
||||
refetch.ReferenceCount -= f.Count();
|
||||
context.FileInfo.Update(refetch);
|
||||
}
|
||||
|
||||
context.SaveChanges();
|
||||
}
|
||||
|
||||
private void deletePending()
|
||||
public override void Cleanup()
|
||||
{
|
||||
Connection.RunInTransaction(() =>
|
||||
var context = GetContext();
|
||||
|
||||
foreach (var f in context.FileInfo.Where(f => f.ReferenceCount < 1))
|
||||
{
|
||||
foreach (var f in Query<FileInfo>(f => f.ReferenceCount < 1))
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
Storage.Delete(Path.Combine(prefix, f.StoragePath));
|
||||
Connection.Delete(f);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error(e, $@"Could not delete beatmap {f}");
|
||||
}
|
||||
Storage.Delete(f.StoragePath);
|
||||
context.FileInfo.Remove(f);
|
||||
}
|
||||
});
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error(e, $@"Could not delete beatmap {f}");
|
||||
}
|
||||
}
|
||||
|
||||
context.SaveChanges();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,23 +1,19 @@
|
||||
// Copyright (c) 2007-2017 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.Framework.Input.Bindings;
|
||||
using osu.Game.Rulesets;
|
||||
using SQLite.Net.Attributes;
|
||||
using SQLiteNetExtensions.Attributes;
|
||||
|
||||
namespace osu.Game.Input.Bindings
|
||||
{
|
||||
[Table("KeyBinding")]
|
||||
public class DatabasedKeyBinding : KeyBinding
|
||||
{
|
||||
[PrimaryKey, AutoIncrement]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public int ID { get; set; }
|
||||
|
||||
[ForeignKey(typeof(RulesetInfo))]
|
||||
public int? RulesetID { get; set; }
|
||||
|
||||
[Indexed]
|
||||
public int? Variant { get; set; }
|
||||
|
||||
[Column("Keys")]
|
||||
@ -27,7 +23,6 @@ namespace osu.Game.Input.Bindings
|
||||
private set { KeyCombination = value; }
|
||||
}
|
||||
|
||||
[Indexed]
|
||||
[Column("Action")]
|
||||
public int IntAction
|
||||
{
|
||||
@ -35,4 +30,4 @@ namespace osu.Game.Input.Bindings
|
||||
set { Action = value; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -51,4 +51,4 @@ namespace osu.Game.Input.Bindings
|
||||
KeyBindings = store.Query(ruleset?.ID, variant);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,21 +4,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osu.Game.Rulesets;
|
||||
using SQLite.Net;
|
||||
|
||||
namespace osu.Game.Input
|
||||
{
|
||||
public class KeyBindingStore : DatabaseBackedStore
|
||||
{
|
||||
public KeyBindingStore(SQLiteConnection connection, RulesetStore rulesets, Storage storage = null)
|
||||
: base(connection, storage)
|
||||
public KeyBindingStore(Func<OsuDbContext> getContext, RulesetStore rulesets, Storage storage = null)
|
||||
: base(getContext, storage)
|
||||
{
|
||||
foreach (var info in rulesets.AllRulesets)
|
||||
foreach (var info in rulesets.AvailableRulesets)
|
||||
{
|
||||
var ruleset = info.CreateInstance();
|
||||
foreach (var variant in ruleset.AvailableVariants)
|
||||
@ -28,66 +28,55 @@ namespace osu.Game.Input
|
||||
|
||||
public void Register(KeyBindingInputManager manager) => insertDefaults(manager.DefaultKeyBindings);
|
||||
|
||||
protected override int StoreVersion => 3;
|
||||
|
||||
protected override void PerformMigration(int currentVersion, int targetVersion)
|
||||
{
|
||||
base.PerformMigration(currentVersion, targetVersion);
|
||||
|
||||
while (currentVersion++ < targetVersion)
|
||||
{
|
||||
switch (currentVersion)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
// cannot migrate; breaking underlying changes.
|
||||
Reset();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Prepare(bool reset = false)
|
||||
{
|
||||
if (reset)
|
||||
Connection.DropTable<DatabasedKeyBinding>();
|
||||
|
||||
Connection.CreateTable<DatabasedKeyBinding>();
|
||||
GetContext().Database.ExecuteSqlCommand("DELETE FROM KeyBinding");
|
||||
}
|
||||
|
||||
private void insertDefaults(IEnumerable<KeyBinding> defaults, int? rulesetId = null, int? variant = null)
|
||||
{
|
||||
var query = Query(rulesetId, variant);
|
||||
var context = GetContext();
|
||||
|
||||
// compare counts in database vs defaults
|
||||
foreach (var group in defaults.GroupBy(k => k.Action))
|
||||
{
|
||||
int count;
|
||||
while (group.Count() > (count = query.Count(k => (int)k.Action == (int)group.Key)))
|
||||
{
|
||||
var insertable = group.Skip(count).First();
|
||||
int count = query(context, rulesetId, variant).Count(k => (int)k.Action == (int)group.Key);
|
||||
int aimCount = group.Count();
|
||||
|
||||
if (aimCount <= count)
|
||||
continue;
|
||||
|
||||
foreach (var insertable in group.Skip(count).Take(aimCount - count))
|
||||
// insert any defaults which are missing.
|
||||
Connection.Insert(new DatabasedKeyBinding
|
||||
context.DatabasedKeyBinding.Add(new DatabasedKeyBinding
|
||||
{
|
||||
KeyCombination = insertable.KeyCombination,
|
||||
Action = insertable.Action,
|
||||
RulesetID = rulesetId,
|
||||
Variant = variant
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
context.SaveChanges();
|
||||
}
|
||||
|
||||
protected override Type[] ValidTypes => new[]
|
||||
/// <summary>
|
||||
/// Retrieve <see cref="KeyBinding"/>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 IEnumerable<KeyBinding> Query(int? rulesetId = null, int? variant = null) => query(GetContext(), rulesetId, variant);
|
||||
|
||||
private IEnumerable<KeyBinding> query(OsuDbContext context, int? rulesetId = null, int? variant = null) =>
|
||||
context.DatabasedKeyBinding.Where(b => b.RulesetID == rulesetId && b.Variant == variant);
|
||||
|
||||
public void Update(KeyBinding keyBinding)
|
||||
{
|
||||
typeof(DatabasedKeyBinding)
|
||||
};
|
||||
|
||||
public IEnumerable<KeyBinding> Query(int? rulesetId = null, int? variant = null) =>
|
||||
Query<DatabasedKeyBinding>(b => b.RulesetID == rulesetId && b.Variant == variant);
|
||||
|
||||
public void Update(KeyBinding keyBinding) => Connection.Update(keyBinding);
|
||||
var context = GetContext();
|
||||
context.Update(keyBinding);
|
||||
context.SaveChanges();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
293
osu.Game/Migrations/20171019041408_InitialCreate.Designer.cs
generated
Normal file
293
osu.Game/Migrations/20171019041408_InitialCreate.Designer.cs
generated
Normal file
@ -0,0 +1,293 @@
|
||||
// <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("20171019041408_InitialCreate")]
|
||||
partial class InitialCreate
|
||||
{
|
||||
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");
|
||||
|
||||
b.HasIndex("MD5Hash");
|
||||
|
||||
b.HasIndex("MetadataID");
|
||||
|
||||
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");
|
||||
|
||||
b.HasIndex("MetadataID");
|
||||
|
||||
b.ToTable("BeatmapSetInfo");
|
||||
});
|
||||
|
||||
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("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.HasKey("ID");
|
||||
|
||||
b.HasIndex("Available");
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
313
osu.Game/Migrations/20171019041408_InitialCreate.cs
Normal file
313
osu.Game/Migrations/20171019041408_InitialCreate.cs
Normal file
@ -0,0 +1,313 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace osu.Game.Migrations
|
||||
{
|
||||
public partial class InitialCreate : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "BeatmapDifficulty",
|
||||
columns: table => new
|
||||
{
|
||||
ID = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
ApproachRate = table.Column<float>(type: "REAL", nullable: false),
|
||||
CircleSize = table.Column<float>(type: "REAL", nullable: false),
|
||||
DrainRate = table.Column<float>(type: "REAL", nullable: false),
|
||||
OverallDifficulty = table.Column<float>(type: "REAL", nullable: false),
|
||||
SliderMultiplier = table.Column<float>(type: "REAL", nullable: false),
|
||||
SliderTickRate = table.Column<float>(type: "REAL", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_BeatmapDifficulty", x => x.ID);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "BeatmapMetadata",
|
||||
columns: table => new
|
||||
{
|
||||
ID = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
Artist = table.Column<string>(type: "TEXT", nullable: true),
|
||||
ArtistUnicode = table.Column<string>(type: "TEXT", nullable: true),
|
||||
AudioFile = table.Column<string>(type: "TEXT", nullable: true),
|
||||
Author = table.Column<string>(type: "TEXT", nullable: true),
|
||||
BackgroundFile = table.Column<string>(type: "TEXT", nullable: true),
|
||||
PreviewTime = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
Source = table.Column<string>(type: "TEXT", nullable: true),
|
||||
Tags = table.Column<string>(type: "TEXT", nullable: true),
|
||||
Title = table.Column<string>(type: "TEXT", nullable: true),
|
||||
TitleUnicode = table.Column<string>(type: "TEXT", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_BeatmapMetadata", x => x.ID);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "FileInfo",
|
||||
columns: table => new
|
||||
{
|
||||
ID = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
Hash = table.Column<string>(type: "TEXT", nullable: true),
|
||||
ReferenceCount = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_FileInfo", x => x.ID);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "KeyBinding",
|
||||
columns: table => new
|
||||
{
|
||||
ID = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
Action = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
Keys = table.Column<string>(type: "TEXT", nullable: true),
|
||||
RulesetID = table.Column<int>(type: "INTEGER", nullable: true),
|
||||
Variant = table.Column<int>(type: "INTEGER", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_KeyBinding", x => x.ID);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "RulesetInfo",
|
||||
columns: table => new
|
||||
{
|
||||
ID = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
Available = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||
InstantiationInfo = table.Column<string>(type: "TEXT", nullable: true),
|
||||
Name = table.Column<string>(type: "TEXT", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_RulesetInfo", x => x.ID);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "BeatmapSetInfo",
|
||||
columns: table => new
|
||||
{
|
||||
ID = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
DeletePending = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||
Hash = table.Column<string>(type: "TEXT", nullable: true),
|
||||
MetadataID = table.Column<int>(type: "INTEGER", nullable: true),
|
||||
OnlineBeatmapSetID = table.Column<int>(type: "INTEGER", nullable: true),
|
||||
Protected = table.Column<bool>(type: "INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_BeatmapSetInfo", x => x.ID);
|
||||
table.ForeignKey(
|
||||
name: "FK_BeatmapSetInfo_BeatmapMetadata_MetadataID",
|
||||
column: x => x.MetadataID,
|
||||
principalTable: "BeatmapMetadata",
|
||||
principalColumn: "ID",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "BeatmapInfo",
|
||||
columns: table => new
|
||||
{
|
||||
ID = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
AudioLeadIn = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
BaseDifficultyID = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
BeatDivisor = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
BeatmapSetInfoID = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
Countdown = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||
DistanceSpacing = table.Column<double>(type: "REAL", nullable: false),
|
||||
GridSize = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
Hash = table.Column<string>(type: "TEXT", nullable: true),
|
||||
Hidden = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||
LetterboxInBreaks = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||
MD5Hash = table.Column<string>(type: "TEXT", nullable: true),
|
||||
MetadataID = table.Column<int>(type: "INTEGER", nullable: true),
|
||||
OnlineBeatmapID = table.Column<int>(type: "INTEGER", nullable: true),
|
||||
Path = table.Column<string>(type: "TEXT", nullable: true),
|
||||
RulesetID = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
SpecialStyle = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||
StackLeniency = table.Column<float>(type: "REAL", nullable: false),
|
||||
StarDifficulty = table.Column<double>(type: "REAL", nullable: false),
|
||||
StoredBookmarks = table.Column<string>(type: "TEXT", nullable: true),
|
||||
TimelineZoom = table.Column<double>(type: "REAL", nullable: false),
|
||||
Version = table.Column<string>(type: "TEXT", nullable: true),
|
||||
WidescreenStoryboard = table.Column<bool>(type: "INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_BeatmapInfo", x => x.ID);
|
||||
table.ForeignKey(
|
||||
name: "FK_BeatmapInfo_BeatmapDifficulty_BaseDifficultyID",
|
||||
column: x => x.BaseDifficultyID,
|
||||
principalTable: "BeatmapDifficulty",
|
||||
principalColumn: "ID",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_BeatmapInfo_BeatmapSetInfo_BeatmapSetInfoID",
|
||||
column: x => x.BeatmapSetInfoID,
|
||||
principalTable: "BeatmapSetInfo",
|
||||
principalColumn: "ID",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_BeatmapInfo_BeatmapMetadata_MetadataID",
|
||||
column: x => x.MetadataID,
|
||||
principalTable: "BeatmapMetadata",
|
||||
principalColumn: "ID",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
table.ForeignKey(
|
||||
name: "FK_BeatmapInfo_RulesetInfo_RulesetID",
|
||||
column: x => x.RulesetID,
|
||||
principalTable: "RulesetInfo",
|
||||
principalColumn: "ID",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "BeatmapSetFileInfo",
|
||||
columns: table => new
|
||||
{
|
||||
ID = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
BeatmapSetInfoID = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
FileInfoID = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
Filename = table.Column<string>(type: "TEXT", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_BeatmapSetFileInfo", x => x.ID);
|
||||
table.ForeignKey(
|
||||
name: "FK_BeatmapSetFileInfo_BeatmapSetInfo_BeatmapSetInfoID",
|
||||
column: x => x.BeatmapSetInfoID,
|
||||
principalTable: "BeatmapSetInfo",
|
||||
principalColumn: "ID",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_BeatmapSetFileInfo_FileInfo_FileInfoID",
|
||||
column: x => x.FileInfoID,
|
||||
principalTable: "FileInfo",
|
||||
principalColumn: "ID",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_BeatmapInfo_BaseDifficultyID",
|
||||
table: "BeatmapInfo",
|
||||
column: "BaseDifficultyID");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_BeatmapInfo_BeatmapSetInfoID",
|
||||
table: "BeatmapInfo",
|
||||
column: "BeatmapSetInfoID");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_BeatmapInfo_Hash",
|
||||
table: "BeatmapInfo",
|
||||
column: "Hash");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_BeatmapInfo_MD5Hash",
|
||||
table: "BeatmapInfo",
|
||||
column: "MD5Hash");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_BeatmapInfo_MetadataID",
|
||||
table: "BeatmapInfo",
|
||||
column: "MetadataID");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_BeatmapInfo_RulesetID",
|
||||
table: "BeatmapInfo",
|
||||
column: "RulesetID");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_BeatmapSetFileInfo_BeatmapSetInfoID",
|
||||
table: "BeatmapSetFileInfo",
|
||||
column: "BeatmapSetInfoID");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_BeatmapSetFileInfo_FileInfoID",
|
||||
table: "BeatmapSetFileInfo",
|
||||
column: "FileInfoID");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_BeatmapSetInfo_DeletePending",
|
||||
table: "BeatmapSetInfo",
|
||||
column: "DeletePending");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_BeatmapSetInfo_Hash",
|
||||
table: "BeatmapSetInfo",
|
||||
column: "Hash");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_BeatmapSetInfo_MetadataID",
|
||||
table: "BeatmapSetInfo",
|
||||
column: "MetadataID");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_FileInfo_Hash",
|
||||
table: "FileInfo",
|
||||
column: "Hash",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_FileInfo_ReferenceCount",
|
||||
table: "FileInfo",
|
||||
column: "ReferenceCount");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_KeyBinding_Action",
|
||||
table: "KeyBinding",
|
||||
column: "Action");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_KeyBinding_Variant",
|
||||
table: "KeyBinding",
|
||||
column: "Variant");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_RulesetInfo_Available",
|
||||
table: "RulesetInfo",
|
||||
column: "Available");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "BeatmapInfo");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "BeatmapSetFileInfo");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "KeyBinding");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "BeatmapDifficulty");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "RulesetInfo");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "BeatmapSetInfo");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "FileInfo");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "BeatmapMetadata");
|
||||
}
|
||||
}
|
||||
}
|
292
osu.Game/Migrations/OsuDbContextModelSnapshot.cs
Normal file
292
osu.Game/Migrations/OsuDbContextModelSnapshot.cs
Normal file
@ -0,0 +1,292 @@
|
||||
// <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))]
|
||||
partial class OsuDbContextModelSnapshot : ModelSnapshot
|
||||
{
|
||||
protected override void BuildModel(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");
|
||||
|
||||
b.HasIndex("MD5Hash");
|
||||
|
||||
b.HasIndex("MetadataID");
|
||||
|
||||
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");
|
||||
|
||||
b.HasIndex("MetadataID");
|
||||
|
||||
b.ToTable("BeatmapSetInfo");
|
||||
});
|
||||
|
||||
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("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.HasKey("ID");
|
||||
|
||||
b.HasIndex("Available");
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
@ -16,7 +16,6 @@ using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Cursor;
|
||||
using osu.Game.Online.API;
|
||||
using SQLite.Net;
|
||||
using osu.Framework.Graphics.Performance;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Input;
|
||||
@ -81,23 +80,28 @@ namespace osu.Game
|
||||
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) =>
|
||||
dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
|
||||
|
||||
private SQLiteConnection createConnection()
|
||||
{
|
||||
var conn = Host.Storage.GetDatabase(@"client");
|
||||
conn.BusyTimeout = new TimeSpan(TimeSpan.TicksPerSecond * 10);
|
||||
return conn;
|
||||
}
|
||||
|
||||
private SQLiteConnection connection;
|
||||
private DatabaseContextFactory contextFactory;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
dependencies.Cache(contextFactory = new DatabaseContextFactory(Host));
|
||||
|
||||
dependencies.Cache(this);
|
||||
dependencies.Cache(LocalConfig);
|
||||
|
||||
connection = createConnection();
|
||||
connection.CreateTable<StoreVersion>();
|
||||
try
|
||||
{
|
||||
using (var context = contextFactory.GetContext())
|
||||
context.Migrate();
|
||||
}
|
||||
catch (MigrationFailedException)
|
||||
{
|
||||
using (var context = contextFactory.GetContext())
|
||||
context.Database.EnsureDeleted();
|
||||
using (var context = contextFactory.GetContext())
|
||||
context.Migrate();
|
||||
}
|
||||
|
||||
dependencies.Cache(API = new APIAccess
|
||||
{
|
||||
@ -105,11 +109,11 @@ namespace osu.Game
|
||||
Token = LocalConfig.Get<string>(OsuSetting.Token)
|
||||
});
|
||||
|
||||
dependencies.Cache(RulesetStore = new RulesetStore(connection));
|
||||
dependencies.Cache(FileStore = new FileStore(connection, Host.Storage));
|
||||
dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, FileStore, connection, RulesetStore, API, Host));
|
||||
dependencies.Cache(ScoreStore = new ScoreStore(Host.Storage, connection, Host, BeatmapManager, RulesetStore));
|
||||
dependencies.Cache(KeyBindingStore = new KeyBindingStore(connection, RulesetStore));
|
||||
dependencies.Cache(RulesetStore = new RulesetStore(contextFactory.GetContext));
|
||||
dependencies.Cache(FileStore = new FileStore(contextFactory.GetContext, Host.Storage));
|
||||
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(KeyBindingStore = new KeyBindingStore(contextFactory.GetContext, RulesetStore));
|
||||
dependencies.Cache(new OsuColour());
|
||||
|
||||
//this completely overrides the framework default. will need to change once we make a proper FontStore.
|
||||
@ -165,6 +169,8 @@ namespace osu.Game
|
||||
};
|
||||
|
||||
API.Register(this);
|
||||
|
||||
FileStore.Cleanup();
|
||||
}
|
||||
|
||||
private WorkingBeatmap lastBeatmap;
|
||||
@ -207,10 +213,7 @@ namespace osu.Game
|
||||
// TODO: This is temporary until we reimplement the local FPS display.
|
||||
// It's just to allow end-users to access the framework FPS display without knowing the shortcut key.
|
||||
fpsDisplayVisible = LocalConfig.GetBindable<bool>(OsuSetting.ShowFpsDisplay);
|
||||
fpsDisplayVisible.ValueChanged += val =>
|
||||
{
|
||||
FrameStatisticsMode = val ? FrameStatisticsMode.Minimal : FrameStatisticsMode.None;
|
||||
};
|
||||
fpsDisplayVisible.ValueChanged += val => { FrameStatisticsMode = val ? FrameStatisticsMode.Minimal : FrameStatisticsMode.None; };
|
||||
fpsDisplayVisible.TriggerChange();
|
||||
}
|
||||
|
||||
@ -236,8 +239,6 @@ namespace osu.Game
|
||||
LocalConfig.Save();
|
||||
}
|
||||
|
||||
connection?.Dispose();
|
||||
|
||||
base.Dispose(isDisposing);
|
||||
}
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ namespace osu.Game.Overlays.Direct
|
||||
DisplayStyleControl.Dropdown.AccentColour = colours.BlueDark;
|
||||
|
||||
Ruleset.BindTo(game?.Ruleset ?? new Bindable<RulesetInfo> { Value = rulesets.GetRuleset(0) });
|
||||
foreach (var r in rulesets.AllRulesets)
|
||||
foreach (var r in rulesets.AvailableRulesets)
|
||||
{
|
||||
modeButtons.Add(new RulesetToggleButton(Ruleset, r));
|
||||
}
|
||||
|
@ -37,8 +37,10 @@ namespace osu.Game.Overlays.KeyBinding
|
||||
|
||||
foreach (var defaultGroup in Defaults.GroupBy(d => d.Action))
|
||||
{
|
||||
int intKey = (int)defaultGroup.Key;
|
||||
|
||||
// one row per valid action.
|
||||
Add(new KeyBindingRow(defaultGroup.Key, bindings.Where(b => b.Action.Equals((int)defaultGroup.Key)))
|
||||
Add(new KeyBindingRow(defaultGroup.Key, bindings.Where(b => ((int)b.Action).Equals(intKey)))
|
||||
{
|
||||
AllowMainMouseButtons = Ruleset != null,
|
||||
Defaults = defaultGroup.Select(d => d.KeyCombination)
|
||||
|
@ -19,7 +19,7 @@ namespace osu.Game.Overlays
|
||||
{
|
||||
AddSection(new GlobalKeyBindingsSection(global));
|
||||
|
||||
foreach (var ruleset in rulesets.AllRulesets)
|
||||
foreach (var ruleset in rulesets.AvailableRulesets)
|
||||
AddSection(new RulesetBindingsSection(ruleset));
|
||||
}
|
||||
|
||||
|
@ -56,7 +56,7 @@ namespace osu.Game.Overlays.Mods
|
||||
if (osu != null)
|
||||
Ruleset.BindTo(osu.Ruleset);
|
||||
else
|
||||
Ruleset.Value = rulesets.AllRulesets.First();
|
||||
Ruleset.Value = rulesets.AvailableRulesets.First();
|
||||
|
||||
Ruleset.ValueChanged += rulesetChanged;
|
||||
Ruleset.TriggerChange();
|
||||
|
@ -27,7 +27,7 @@ namespace osu.Game.Overlays.Settings.Sections
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(RulesetStore rulesets)
|
||||
{
|
||||
foreach(Ruleset ruleset in rulesets.AllRulesets.Select(info => info.CreateInstance()))
|
||||
foreach(Ruleset ruleset in rulesets.AvailableRulesets.Select(info => info.CreateInstance()))
|
||||
{
|
||||
SettingsSubsection section = ruleset.CreateSettings();
|
||||
if (section != null)
|
||||
|
@ -27,7 +27,7 @@ namespace osu.Game.Overlays.Settings
|
||||
|
||||
var modes = new List<Drawable>();
|
||||
|
||||
foreach (var ruleset in rulesets.AllRulesets)
|
||||
foreach (var ruleset in rulesets.AvailableRulesets)
|
||||
{
|
||||
var icon = new ConstrainedIconContainer
|
||||
{
|
||||
|
@ -67,7 +67,7 @@ namespace osu.Game.Overlays.Toolbar
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(RulesetStore rulesets, OsuGame game)
|
||||
{
|
||||
foreach (var r in rulesets.AllRulesets)
|
||||
foreach (var r in rulesets.AvailableRulesets)
|
||||
{
|
||||
modeButtons.Add(new ToolbarModeButton
|
||||
{
|
||||
|
@ -2,22 +2,19 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using SQLite.Net.Attributes;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace osu.Game.Rulesets
|
||||
{
|
||||
public class RulesetInfo : IEquatable<RulesetInfo>
|
||||
{
|
||||
[PrimaryKey, AutoIncrement]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public int? ID { get; set; }
|
||||
|
||||
[Indexed(Unique = true)]
|
||||
public string Name { get; set; }
|
||||
|
||||
[Indexed(Unique = true)]
|
||||
public string InstantiationInfo { get; set; }
|
||||
|
||||
[Indexed]
|
||||
public bool Available { get; set; }
|
||||
|
||||
public virtual Ruleset CreateInstance() => (Ruleset)Activator.CreateInstance(Type.GetType(InstantiationInfo), this);
|
||||
|
@ -6,8 +6,8 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using osu.Game.Database;
|
||||
using SQLite.Net;
|
||||
|
||||
namespace osu.Game.Rulesets
|
||||
{
|
||||
@ -18,12 +18,6 @@ namespace osu.Game.Rulesets
|
||||
{
|
||||
private static readonly Dictionary<Assembly, Type> loaded_assemblies = new Dictionary<Assembly, Type>();
|
||||
|
||||
public IEnumerable<RulesetInfo> AllRulesets => Query<RulesetInfo>().Where(r => r.Available);
|
||||
|
||||
public RulesetStore(SQLiteConnection connection) : base(connection)
|
||||
{
|
||||
}
|
||||
|
||||
static RulesetStore()
|
||||
{
|
||||
AppDomain.CurrentDomain.AssemblyResolve += currentDomain_AssemblyResolve;
|
||||
@ -32,59 +26,78 @@ namespace osu.Game.Rulesets
|
||||
loadRulesetFromFile(file);
|
||||
}
|
||||
|
||||
public RulesetStore(Func<OsuDbContext> factory)
|
||||
: base(factory)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve a ruleset using a known ID.
|
||||
/// </summary>
|
||||
/// <param name="id">The ruleset's internal ID.</param>
|
||||
/// <returns>A ruleset, if available, else null.</returns>
|
||||
public RulesetInfo GetRuleset(int id) => AvailableRulesets.FirstOrDefault(r => r.ID == id);
|
||||
|
||||
/// <summary>
|
||||
/// All available rulesets.
|
||||
/// </summary>
|
||||
public IEnumerable<RulesetInfo> AvailableRulesets => GetContext().RulesetInfo.Where(r => r.Available);
|
||||
|
||||
private static Assembly currentDomain_AssemblyResolve(object sender, ResolveEventArgs args) => loaded_assemblies.Keys.FirstOrDefault(a => a.FullName == args.Name);
|
||||
|
||||
private const string ruleset_library_prefix = "osu.Game.Rulesets";
|
||||
|
||||
protected override void Prepare(bool reset = false)
|
||||
{
|
||||
Connection.CreateTable<RulesetInfo>();
|
||||
var context = GetContext();
|
||||
|
||||
if (reset)
|
||||
{
|
||||
Connection.DeleteAll<RulesetInfo>();
|
||||
context.Database.ExecuteSqlCommand("DELETE FROM RulesetInfo");
|
||||
}
|
||||
|
||||
var instances = loaded_assemblies.Values.Select(r => (Ruleset)Activator.CreateInstance(r, new RulesetInfo()));
|
||||
var instances = loaded_assemblies.Values.Select(r => (Ruleset)Activator.CreateInstance(r, new RulesetInfo())).ToList();
|
||||
|
||||
Connection.RunInTransaction(() =>
|
||||
//add all legacy modes in correct order
|
||||
foreach (var r in instances.Where(r => r.LegacyID >= 0).OrderBy(r => r.LegacyID))
|
||||
{
|
||||
//add all legacy modes in correct order
|
||||
foreach (var r in instances.Where(r => r.LegacyID >= 0).OrderBy(r => r.LegacyID))
|
||||
var rulesetInfo = createRulesetInfo(r);
|
||||
if (context.RulesetInfo.SingleOrDefault(rsi => rsi.ID == rulesetInfo.ID) == null)
|
||||
{
|
||||
Connection.InsertOrReplace(createRulesetInfo(r));
|
||||
context.RulesetInfo.Add(rulesetInfo);
|
||||
}
|
||||
}
|
||||
|
||||
//add any other modes
|
||||
foreach (var r in instances.Where(r => r.LegacyID < 0))
|
||||
{
|
||||
var us = createRulesetInfo(r);
|
||||
context.SaveChanges();
|
||||
|
||||
var existing = Query<RulesetInfo>().Where(ri => ri.InstantiationInfo == us.InstantiationInfo).FirstOrDefault();
|
||||
|
||||
if (existing == null)
|
||||
Connection.Insert(us);
|
||||
}
|
||||
});
|
||||
|
||||
Connection.RunInTransaction(() =>
|
||||
//add any other modes
|
||||
foreach (var r in instances.Where(r => r.LegacyID < 0))
|
||||
{
|
||||
//perform a consistency check
|
||||
foreach (var r in Query<RulesetInfo>())
|
||||
{
|
||||
try
|
||||
{
|
||||
r.CreateInstance();
|
||||
r.Available = true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
r.Available = false;
|
||||
}
|
||||
var us = createRulesetInfo(r);
|
||||
|
||||
Connection.Update(r);
|
||||
var existing = context.RulesetInfo.FirstOrDefault(ri => ri.InstantiationInfo == us.InstantiationInfo);
|
||||
|
||||
if (existing == null)
|
||||
context.RulesetInfo.Add(us);
|
||||
}
|
||||
|
||||
context.SaveChanges();
|
||||
|
||||
//perform a consistency check
|
||||
foreach (var r in context.RulesetInfo)
|
||||
{
|
||||
try
|
||||
{
|
||||
r.CreateInstance();
|
||||
r.Available = true;
|
||||
}
|
||||
});
|
||||
catch
|
||||
{
|
||||
r.Available = false;
|
||||
}
|
||||
}
|
||||
|
||||
context.SaveChanges();
|
||||
}
|
||||
|
||||
private static void loadRulesetFromFile(string file)
|
||||
@ -99,7 +112,9 @@ namespace osu.Game.Rulesets
|
||||
var assembly = Assembly.LoadFrom(file);
|
||||
loaded_assemblies[assembly] = assembly.GetTypes().First(t => t.IsSubclassOf(typeof(Ruleset)));
|
||||
}
|
||||
catch (Exception) { }
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private RulesetInfo createRulesetInfo(Ruleset ruleset) => new RulesetInfo
|
||||
@ -108,9 +123,5 @@ namespace osu.Game.Rulesets
|
||||
InstantiationInfo = ruleset.GetType().AssemblyQualifiedName,
|
||||
ID = ruleset.LegacyID
|
||||
};
|
||||
|
||||
protected override Type[] ValidTypes => new[] { typeof(RulesetInfo) };
|
||||
|
||||
public RulesetInfo GetRuleset(int id) => Query<RulesetInfo>().First(r => r.ID == id);
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,6 @@ using osu.Game.IO.Legacy;
|
||||
using osu.Game.IPC;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
using SharpCompress.Compressors.LZMA;
|
||||
using SQLite.Net;
|
||||
|
||||
namespace osu.Game.Rulesets.Scoring
|
||||
{
|
||||
@ -27,7 +26,7 @@ namespace osu.Game.Rulesets.Scoring
|
||||
// ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised)
|
||||
private ScoreIPCChannel ipc;
|
||||
|
||||
public ScoreStore(Storage storage, SQLiteConnection connection, IIpcHost importHost = null, BeatmapManager beatmaps = null, RulesetStore rulesets = null) : base(connection)
|
||||
public ScoreStore(Storage storage, Func<OsuDbContext> factory, IIpcHost importHost = null, BeatmapManager beatmaps = null, RulesetStore rulesets = null) : base(factory)
|
||||
{
|
||||
this.storage = storage;
|
||||
this.beatmaps = beatmaps;
|
||||
@ -148,7 +147,5 @@ namespace osu.Game.Rulesets.Scoring
|
||||
protected override void Prepare(bool reset = false)
|
||||
{
|
||||
}
|
||||
|
||||
protected override Type[] ValidTypes => new[] { typeof(Score) };
|
||||
}
|
||||
}
|
||||
|
@ -172,11 +172,11 @@ namespace osu.Game.Rulesets.UI
|
||||
|
||||
// Apply difficulty adjustments from mods before using Difficulty.
|
||||
foreach (var mod in Mods.OfType<IApplicableToDifficulty>())
|
||||
mod.ApplyToDifficulty(Beatmap.BeatmapInfo.Difficulty);
|
||||
mod.ApplyToDifficulty(Beatmap.BeatmapInfo.BaseDifficulty);
|
||||
|
||||
// Apply defaults
|
||||
foreach (var h in Beatmap.HitObjects)
|
||||
h.ApplyDefaults(Beatmap.ControlPointInfo, Beatmap.BeatmapInfo.Difficulty);
|
||||
h.ApplyDefaults(Beatmap.ControlPointInfo, Beatmap.BeatmapInfo.BaseDifficulty);
|
||||
|
||||
// Post-process the beatmap
|
||||
processor.PostProcess(Beatmap);
|
||||
|
@ -22,7 +22,7 @@ namespace osu.Game.Screens.Menu
|
||||
{
|
||||
private readonly OsuLogo logo;
|
||||
|
||||
private const string menu_music_beatmap_hash = "715a09144f885d746644c1983e285044";
|
||||
private const string menu_music_beatmap_hash = "3c8b1fcc9434dbb29e2fb613d3b9eada9d7bb6c125ceb32396c3b53437280c83";
|
||||
|
||||
/// <summary>
|
||||
/// Whether we have loaded the menu previously.
|
||||
@ -76,7 +76,7 @@ namespace osu.Game.Screens.Menu
|
||||
|
||||
if (!menuMusic)
|
||||
{
|
||||
var sets = beatmaps.GetAllUsableBeatmapSets(false);
|
||||
var sets = beatmaps.GetAllUsableBeatmapSets();
|
||||
if (sets.Count > 0)
|
||||
setInfo = beatmaps.QueryBeatmapSet(s => s.ID == sets[RNG.Next(0, sets.Count - 1)].ID);
|
||||
}
|
||||
@ -89,9 +89,7 @@ namespace osu.Game.Screens.Menu
|
||||
{
|
||||
// we need to import the default menu background beatmap
|
||||
setInfo = beatmaps.Import(new OszArchiveReader(game.Resources.GetStream(@"Tracks/circles.osz")));
|
||||
|
||||
setInfo.Protected = true;
|
||||
beatmaps.Delete(setInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@ -101,6 +99,7 @@ namespace osu.Game.Screens.Menu
|
||||
|
||||
welcome = audio.Sample.Get(@"welcome");
|
||||
seeya = audio.Sample.Get(@"seeya");
|
||||
beatmaps.Delete(setInfo);
|
||||
}
|
||||
|
||||
protected override void OnEntering(Screen last)
|
||||
|
@ -33,10 +33,7 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
public IEnumerable<BeatmapSetInfo> Beatmaps
|
||||
{
|
||||
get
|
||||
{
|
||||
return groups.Select(g => g.BeatmapSet);
|
||||
}
|
||||
get { return groups.Select(g => g.BeatmapSet); }
|
||||
|
||||
set
|
||||
{
|
||||
@ -100,12 +97,10 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
public void AddBeatmap(BeatmapSetInfo beatmapSet)
|
||||
{
|
||||
var group = createGroup(beatmapSet);
|
||||
|
||||
//for the time being, let's completely load the difficulty panels in the background.
|
||||
//this likely won't scale so well, but allows us to completely async the loading flow.
|
||||
Schedule(delegate
|
||||
Schedule(() =>
|
||||
{
|
||||
var group = createGroup(beatmapSet);
|
||||
|
||||
addGroup(group);
|
||||
computeYPositions();
|
||||
if (selectedGroup == null)
|
||||
@ -113,7 +108,10 @@ namespace osu.Game.Screens.Select
|
||||
});
|
||||
}
|
||||
|
||||
public void RemoveBeatmap(BeatmapSetInfo beatmapSet) => removeGroup(groups.Find(b => b.BeatmapSet.ID == beatmapSet.ID));
|
||||
public void RemoveBeatmap(BeatmapSetInfo beatmapSet)
|
||||
{
|
||||
Schedule(() => removeGroup(groups.Find(b => b.BeatmapSet.ID == beatmapSet.ID)));
|
||||
}
|
||||
|
||||
internal void UpdateBeatmap(BeatmapInfo beatmap)
|
||||
{
|
||||
@ -519,7 +517,7 @@ namespace osu.Game.Screens.Select
|
||||
float drawHeight = DrawHeight;
|
||||
|
||||
// Remove all panels that should no longer be on-screen
|
||||
scrollableContent.RemoveAll(delegate (Panel p)
|
||||
scrollableContent.RemoveAll(delegate(Panel p)
|
||||
{
|
||||
float panelPosY = p.Position.Y;
|
||||
bool remove = panelPosY < Current - p.DrawHeight || panelPosY > Current + drawHeight || !p.IsPresent;
|
||||
|
@ -264,7 +264,7 @@ namespace osu.Game.Screens.Select
|
||||
advanced.Beatmap = new BeatmapInfo
|
||||
{
|
||||
StarDifficulty = 0,
|
||||
Difficulty = new BeatmapDifficulty
|
||||
BaseDifficulty = new BeatmapDifficulty
|
||||
{
|
||||
CircleSize = 0,
|
||||
DrainRate = 0,
|
||||
|
@ -32,17 +32,17 @@ namespace osu.Game.Screens.Select.Details
|
||||
if ((Beatmap?.Ruleset?.ID ?? 0) == 3)
|
||||
{
|
||||
firstValue.Title = "Key Amount";
|
||||
firstValue.Value = (int)Math.Round(Beatmap?.Difficulty?.CircleSize ?? 0);
|
||||
firstValue.Value = (int)Math.Round(Beatmap?.BaseDifficulty?.CircleSize ?? 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
firstValue.Title = "Circle Size";
|
||||
firstValue.Value = Beatmap?.Difficulty?.CircleSize ?? 0;
|
||||
firstValue.Value = Beatmap?.BaseDifficulty?.CircleSize ?? 0;
|
||||
}
|
||||
|
||||
hpDrain.Value = beatmap.Difficulty?.DrainRate ?? 0;
|
||||
accuracy.Value = beatmap.Difficulty?.OverallDifficulty ?? 0;
|
||||
approachRate.Value = beatmap.Difficulty?.ApproachRate ?? 0;
|
||||
hpDrain.Value = beatmap.BaseDifficulty?.DrainRate ?? 0;
|
||||
accuracy.Value = beatmap.BaseDifficulty?.OverallDifficulty ?? 0;
|
||||
approachRate.Value = beatmap.BaseDifficulty?.ApproachRate ?? 0;
|
||||
starDifficulty.Value = (float)beatmap.StarDifficulty;
|
||||
}
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ namespace osu.Game.Screens.Select
|
||||
{
|
||||
public abstract class SongSelect : OsuScreen
|
||||
{
|
||||
private BeatmapManager manager;
|
||||
private BeatmapManager beatmaps;
|
||||
protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap();
|
||||
|
||||
private readonly BeatmapCarousel carousel;
|
||||
@ -108,9 +108,9 @@ namespace osu.Game.Screens.Select
|
||||
SelectionChanged = carouselSelectionChanged,
|
||||
BeatmapsChanged = carouselBeatmapsLoaded,
|
||||
DeleteRequested = promptDelete,
|
||||
RestoreRequested = s => { foreach (var b in s.Beatmaps) manager.Restore(b); },
|
||||
RestoreRequested = s => { foreach (var b in s.Beatmaps) beatmaps.Restore(b); },
|
||||
EditRequested = editRequested,
|
||||
HideDifficultyRequested = b => manager.Hide(b),
|
||||
HideDifficultyRequested = b => beatmaps.Hide(b),
|
||||
StartRequested = () => carouselRaisedStart(),
|
||||
});
|
||||
Add(FilterControl = new FilterControl
|
||||
@ -171,16 +171,16 @@ namespace osu.Game.Screens.Select
|
||||
BeatmapOptions.AddButton(@"Delete", @"Beatmap", FontAwesome.fa_trash, colours.Pink, () => promptDelete(Beatmap.Value.BeatmapSetInfo), Key.Number4, float.MaxValue);
|
||||
}
|
||||
|
||||
if (manager == null)
|
||||
manager = beatmaps;
|
||||
if (this.beatmaps == null)
|
||||
this.beatmaps = beatmaps;
|
||||
|
||||
if (osu != null)
|
||||
Ruleset.BindTo(osu.Ruleset);
|
||||
|
||||
manager.BeatmapSetAdded += onBeatmapSetAdded;
|
||||
manager.BeatmapSetRemoved += onBeatmapSetRemoved;
|
||||
manager.BeatmapHidden += onBeatmapHidden;
|
||||
manager.BeatmapRestored += onBeatmapRestored;
|
||||
this.beatmaps.BeatmapSetAdded += onBeatmapSetAdded;
|
||||
this.beatmaps.BeatmapSetRemoved += onBeatmapSetRemoved;
|
||||
this.beatmaps.BeatmapHidden += onBeatmapHidden;
|
||||
this.beatmaps.BeatmapRestored += onBeatmapRestored;
|
||||
|
||||
dialogOverlay = dialog;
|
||||
|
||||
@ -189,7 +189,7 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
initialAddSetsTask = new CancellationTokenSource();
|
||||
|
||||
carousel.Beatmaps = manager.GetAllUsableBeatmapSets();
|
||||
carousel.Beatmaps = this.beatmaps.GetAllUsableBeatmapSets();
|
||||
|
||||
Beatmap.ValueChanged += beatmap_ValueChanged;
|
||||
|
||||
@ -199,7 +199,7 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
private void editRequested(BeatmapInfo beatmap)
|
||||
{
|
||||
Beatmap.Value = manager.GetWorkingBeatmap(beatmap, Beatmap);
|
||||
Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap, Beatmap);
|
||||
Push(new Editor());
|
||||
}
|
||||
|
||||
@ -248,7 +248,7 @@ namespace osu.Game.Screens.Select
|
||||
{
|
||||
bool preview = beatmap?.BeatmapSetInfoID != Beatmap.Value.BeatmapInfo.BeatmapSetInfoID;
|
||||
|
||||
Beatmap.Value = manager.GetWorkingBeatmap(beatmap, Beatmap);
|
||||
Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap, Beatmap);
|
||||
ensurePlayingSelected(preview);
|
||||
}
|
||||
|
||||
@ -357,12 +357,12 @@ namespace osu.Game.Screens.Select
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
if (manager != null)
|
||||
if (beatmaps != null)
|
||||
{
|
||||
manager.BeatmapSetAdded -= onBeatmapSetAdded;
|
||||
manager.BeatmapSetRemoved -= onBeatmapSetRemoved;
|
||||
manager.BeatmapHidden -= onBeatmapHidden;
|
||||
manager.BeatmapRestored -= onBeatmapRestored;
|
||||
beatmaps.BeatmapSetAdded -= onBeatmapSetAdded;
|
||||
beatmaps.BeatmapSetRemoved -= onBeatmapSetRemoved;
|
||||
beatmaps.BeatmapHidden -= onBeatmapHidden;
|
||||
beatmaps.BeatmapRestored -= onBeatmapRestored;
|
||||
}
|
||||
|
||||
initialAddSetsTask?.Cancel();
|
||||
|
@ -1,12 +1,7 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework;
|
||||
using osu.Framework.Platform;
|
||||
using SQLite.Net;
|
||||
using SQLite.Net.Interop;
|
||||
using SQLite.Net.Platform.Generic;
|
||||
using SQLite.Net.Platform.Win32;
|
||||
|
||||
namespace osu.Game.Tests.Platform
|
||||
{
|
||||
@ -16,14 +11,9 @@ namespace osu.Game.Tests.Platform
|
||||
{
|
||||
}
|
||||
|
||||
public override SQLiteConnection GetDatabase(string name)
|
||||
public override string GetDatabaseConnectionString(string name)
|
||||
{
|
||||
ISQLitePlatform platform;
|
||||
if (RuntimeInfo.IsWindows)
|
||||
platform = new SQLitePlatformWin32();
|
||||
else
|
||||
platform = new SQLitePlatformGeneric();
|
||||
return new SQLiteConnection(platform, @":memory:");
|
||||
return "DataSource=:memory:";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,8 +11,13 @@ namespace osu.Game.Tests.Visual
|
||||
{
|
||||
public override void RunTest()
|
||||
{
|
||||
using (var host = new HeadlessGameHost(AppDomain.CurrentDomain.FriendlyName.Replace(' ', '-'), realtime: false))
|
||||
using (var host = new HeadlessGameHost($"test-{Guid.NewGuid()}", realtime: false))
|
||||
{
|
||||
host.Run(new OsuTestCaseTestRunner(this));
|
||||
}
|
||||
|
||||
// clean up after each run
|
||||
//storage.DeleteDirectory(string.Empty);
|
||||
}
|
||||
|
||||
public class OsuTestCaseTestRunner : OsuGameBase
|
||||
|
@ -29,7 +29,7 @@ namespace osu.Game.Tests.Visual
|
||||
Source = "osu!lazer",
|
||||
Tags = "this beatmap has all the metrics",
|
||||
},
|
||||
Difficulty = new BeatmapDifficulty
|
||||
BaseDifficulty = new BeatmapDifficulty
|
||||
{
|
||||
CircleSize = 7,
|
||||
DrainRate = 1,
|
||||
@ -53,7 +53,7 @@ namespace osu.Game.Tests.Visual
|
||||
Source = "osu!lazer",
|
||||
Tags = "this beatmap has ratings metrics but not retries or fails",
|
||||
},
|
||||
Difficulty = new BeatmapDifficulty
|
||||
BaseDifficulty = new BeatmapDifficulty
|
||||
{
|
||||
CircleSize = 6,
|
||||
DrainRate = 9,
|
||||
@ -75,7 +75,7 @@ namespace osu.Game.Tests.Visual
|
||||
Source = "osu!lazer",
|
||||
Tags = "this beatmap has retries and fails but no ratings",
|
||||
},
|
||||
Difficulty = new BeatmapDifficulty
|
||||
BaseDifficulty = new BeatmapDifficulty
|
||||
{
|
||||
CircleSize = 3.7f,
|
||||
DrainRate = 6,
|
||||
@ -98,7 +98,7 @@ namespace osu.Game.Tests.Visual
|
||||
Source = "osu!lazer",
|
||||
Tags = "this beatmap has no metrics",
|
||||
},
|
||||
Difficulty = new BeatmapDifficulty
|
||||
BaseDifficulty = new BeatmapDifficulty
|
||||
{
|
||||
CircleSize = 5,
|
||||
DrainRate = 5,
|
||||
|
@ -65,7 +65,7 @@ namespace osu.Game.Tests.Visual
|
||||
StarDifficulty = 1.36,
|
||||
Version = @"BASIC",
|
||||
Ruleset = mania,
|
||||
Difficulty = new BeatmapDifficulty
|
||||
BaseDifficulty = new BeatmapDifficulty
|
||||
{
|
||||
CircleSize = 4,
|
||||
DrainRate = 6.5f,
|
||||
@ -93,7 +93,7 @@ namespace osu.Game.Tests.Visual
|
||||
StarDifficulty = 2.22,
|
||||
Version = @"NOVICE",
|
||||
Ruleset = mania,
|
||||
Difficulty = new BeatmapDifficulty
|
||||
BaseDifficulty = new BeatmapDifficulty
|
||||
{
|
||||
CircleSize = 4,
|
||||
DrainRate = 7,
|
||||
@ -121,7 +121,7 @@ namespace osu.Game.Tests.Visual
|
||||
StarDifficulty = 3.49,
|
||||
Version = @"ADVANCED",
|
||||
Ruleset = mania,
|
||||
Difficulty = new BeatmapDifficulty
|
||||
BaseDifficulty = new BeatmapDifficulty
|
||||
{
|
||||
CircleSize = 4,
|
||||
DrainRate = 7.5f,
|
||||
@ -149,7 +149,7 @@ namespace osu.Game.Tests.Visual
|
||||
StarDifficulty = 4.24,
|
||||
Version = @"EXHAUST",
|
||||
Ruleset = mania,
|
||||
Difficulty = new BeatmapDifficulty
|
||||
BaseDifficulty = new BeatmapDifficulty
|
||||
{
|
||||
CircleSize = 4,
|
||||
DrainRate = 8,
|
||||
@ -177,7 +177,7 @@ namespace osu.Game.Tests.Visual
|
||||
StarDifficulty = 5.26,
|
||||
Version = @"GRAVITY",
|
||||
Ruleset = mania,
|
||||
Difficulty = new BeatmapDifficulty
|
||||
BaseDifficulty = new BeatmapDifficulty
|
||||
{
|
||||
CircleSize = 4,
|
||||
DrainRate = 8.5f,
|
||||
@ -239,7 +239,7 @@ namespace osu.Game.Tests.Visual
|
||||
StarDifficulty = 1.40,
|
||||
Version = @"yzrin's Kantan",
|
||||
Ruleset = taiko,
|
||||
Difficulty = new BeatmapDifficulty
|
||||
BaseDifficulty = new BeatmapDifficulty
|
||||
{
|
||||
CircleSize = 2,
|
||||
DrainRate = 7,
|
||||
@ -267,7 +267,7 @@ namespace osu.Game.Tests.Visual
|
||||
StarDifficulty = 2.23,
|
||||
Version = @"Futsuu",
|
||||
Ruleset = taiko,
|
||||
Difficulty = new BeatmapDifficulty
|
||||
BaseDifficulty = new BeatmapDifficulty
|
||||
{
|
||||
CircleSize = 2,
|
||||
DrainRate = 6,
|
||||
@ -295,7 +295,7 @@ namespace osu.Game.Tests.Visual
|
||||
StarDifficulty = 3.19,
|
||||
Version = @"Muzukashii",
|
||||
Ruleset = taiko,
|
||||
Difficulty = new BeatmapDifficulty
|
||||
BaseDifficulty = new BeatmapDifficulty
|
||||
{
|
||||
CircleSize = 2,
|
||||
DrainRate = 6,
|
||||
@ -323,7 +323,7 @@ namespace osu.Game.Tests.Visual
|
||||
StarDifficulty = 3.97,
|
||||
Version = @"Charlotte's Oni",
|
||||
Ruleset = taiko,
|
||||
Difficulty = new BeatmapDifficulty
|
||||
BaseDifficulty = new BeatmapDifficulty
|
||||
{
|
||||
CircleSize = 5,
|
||||
DrainRate = 6,
|
||||
@ -351,7 +351,7 @@ namespace osu.Game.Tests.Visual
|
||||
StarDifficulty = 5.08,
|
||||
Version = @"Labyrinth Oni",
|
||||
Ruleset = taiko,
|
||||
Difficulty = new BeatmapDifficulty
|
||||
BaseDifficulty = new BeatmapDifficulty
|
||||
{
|
||||
CircleSize = 5,
|
||||
DrainRate = 5,
|
||||
|
@ -49,7 +49,7 @@ namespace osu.Game.Tests.Visual
|
||||
|
||||
AddStep("Toggle", modSelect.ToggleVisibility);
|
||||
|
||||
foreach (var ruleset in rulesets.AllRulesets)
|
||||
foreach (var ruleset in rulesets.AvailableRulesets)
|
||||
AddStep(ruleset.CreateInstance().Description, () => modSelect.Ruleset.Value = ruleset);
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,14 @@
|
||||
// Copyright (c) 2007-2017 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 Microsoft.EntityFrameworkCore;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.IO;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Screens.Select;
|
||||
using osu.Game.Screens.Select.Filter;
|
||||
@ -24,8 +26,6 @@ namespace osu.Game.Tests.Visual
|
||||
|
||||
private DependencyContainer dependencies;
|
||||
|
||||
private FileStore files;
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) => dependencies = new DependencyContainer(parent);
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@ -37,12 +37,14 @@ namespace osu.Game.Tests.Visual
|
||||
{
|
||||
var storage = new TestStorage(@"TestCasePlaySongSelect");
|
||||
|
||||
var backingDatabase = storage.GetDatabase(@"client");
|
||||
backingDatabase.CreateTable<StoreVersion>();
|
||||
// this is by no means clean. should be replacing inside of OsuGameBase somehow.
|
||||
var context = new OsuDbContext(storage.GetDatabaseConnectionString(@"client"));
|
||||
context.Database.Migrate();
|
||||
|
||||
dependencies.Cache(rulesets = new RulesetStore(backingDatabase));
|
||||
dependencies.Cache(files = new FileStore(backingDatabase, storage));
|
||||
dependencies.Cache(manager = new BeatmapManager(storage, files, backingDatabase, rulesets, null));
|
||||
Func<OsuDbContext> contextFactory = () => context;
|
||||
|
||||
dependencies.Cache(rulesets = new RulesetStore(contextFactory));
|
||||
dependencies.Cache(manager = new BeatmapManager(storage, contextFactory, rulesets, null));
|
||||
|
||||
for (int i = 0; i < 100; i += 10)
|
||||
manager.Import(createTestBeatmapSet(i));
|
||||
@ -75,10 +77,10 @@ namespace osu.Game.Tests.Visual
|
||||
new BeatmapInfo
|
||||
{
|
||||
OnlineBeatmapID = 1234 + i,
|
||||
Ruleset = rulesets.Query<RulesetInfo>().First(),
|
||||
Ruleset = rulesets.AvailableRulesets.First(),
|
||||
Path = "normal.osu",
|
||||
Version = "Normal",
|
||||
Difficulty = new BeatmapDifficulty
|
||||
BaseDifficulty = new BeatmapDifficulty
|
||||
{
|
||||
OverallDifficulty = 3.5f,
|
||||
}
|
||||
@ -86,10 +88,10 @@ namespace osu.Game.Tests.Visual
|
||||
new BeatmapInfo
|
||||
{
|
||||
OnlineBeatmapID = 1235 + i,
|
||||
Ruleset = rulesets.Query<RulesetInfo>().First(),
|
||||
Ruleset = rulesets.AvailableRulesets.First(),
|
||||
Path = "hard.osu",
|
||||
Version = "Hard",
|
||||
Difficulty = new BeatmapDifficulty
|
||||
BaseDifficulty = new BeatmapDifficulty
|
||||
{
|
||||
OverallDifficulty = 5,
|
||||
}
|
||||
@ -97,10 +99,10 @@ namespace osu.Game.Tests.Visual
|
||||
new BeatmapInfo
|
||||
{
|
||||
OnlineBeatmapID = 1236 + i,
|
||||
Ruleset = rulesets.Query<RulesetInfo>().First(),
|
||||
Ruleset = rulesets.AvailableRulesets.First(),
|
||||
Path = "insane.osu",
|
||||
Version = "Insane",
|
||||
Difficulty = new BeatmapDifficulty
|
||||
BaseDifficulty = new BeatmapDifficulty
|
||||
{
|
||||
OverallDifficulty = 7,
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ namespace osu.Game.Tests.Visual
|
||||
|
||||
string instantiation = ruleset?.AssemblyQualifiedName;
|
||||
|
||||
foreach (var r in rulesets.Query<RulesetInfo>(rs => rs.Available && (instantiation == null || rs.InstantiationInfo == instantiation)))
|
||||
foreach (var r in rulesets.AvailableRulesets.Where(rs => instantiation == null || rs.InstantiationInfo == instantiation))
|
||||
AddStep(r.Name, () => loadPlayerFor(r));
|
||||
}
|
||||
|
||||
|
@ -48,7 +48,7 @@ namespace osu.Game.Tests.Visual
|
||||
HitObjects = objects,
|
||||
BeatmapInfo = new BeatmapInfo
|
||||
{
|
||||
Difficulty = new BeatmapDifficulty(),
|
||||
BaseDifficulty = new BeatmapDifficulty(),
|
||||
Metadata = new BeatmapMetadata()
|
||||
}
|
||||
};
|
||||
|
@ -10,6 +10,10 @@
|
||||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral"/>
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0"/>
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
@ -103,6 +103,48 @@
|
||||
<HintPath>$(SolutionDir)\packages\DotNetZip.1.10.1\lib\net20\DotNetZip.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Data.Sqlite, Version=2.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\Microsoft.Data.Sqlite.Core.2.0.0\lib\netstandard2.0\Microsoft.Data.Sqlite.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.EntityFrameworkCore, Version=2.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\Microsoft.EntityFrameworkCore.2.0.0\lib\netstandard2.0\Microsoft.EntityFrameworkCore.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.EntityFrameworkCore.Design">
|
||||
<HintPath>$(SolutionDir)\packages\Microsoft.EntityFrameworkCore.Design.2.0.0\lib\net461\Microsoft.EntityFrameworkCore.Design.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.EntityFrameworkCore.Relational, Version=2.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\Microsoft.EntityFrameworkCore.Relational.2.0.0\lib\netstandard2.0\Microsoft.EntityFrameworkCore.Relational.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.EntityFrameworkCore.Sqlite, Version=2.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\Microsoft.EntityFrameworkCore.Sqlite.Core.2.0.0\lib\netstandard2.0\Microsoft.EntityFrameworkCore.Sqlite.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Extensions.Caching.Abstractions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\Microsoft.Extensions.Caching.Abstractions.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Caching.Abstractions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Extensions.Caching.Memory, Version=2.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\Microsoft.Extensions.Caching.Memory.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Caching.Memory.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Extensions.Configuration.Abstractions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\Microsoft.Extensions.Configuration.Abstractions.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Configuration.Abstractions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Extensions.DependencyInjection, Version=2.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\Microsoft.Extensions.DependencyInjection.2.0.0\lib\netstandard2.0\Microsoft.Extensions.DependencyInjection.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\Microsoft.Extensions.DependencyInjection.Abstractions.2.0.0\lib\netstandard2.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Extensions.Logging, Version=2.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\Microsoft.Extensions.Logging.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Logging.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Extensions.Logging.Abstractions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\Microsoft.Extensions.Logging.Abstractions.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Logging.Abstractions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Extensions.Options, Version=2.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\Microsoft.Extensions.Options.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Options.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Extensions.Primitives, Version=2.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\Microsoft.Extensions.Primitives.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Primitives.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="mscorlib" />
|
||||
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
@ -116,27 +158,52 @@
|
||||
<HintPath>$(SolutionDir)\packages\OpenTK.3.0.0-git00009\lib\net20\OpenTK.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Remotion.Linq, Version=2.1.0.0, Culture=neutral, PublicKeyToken=fee00910d6e5f53b, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\Remotion.Linq.2.1.2\lib\net45\Remotion.Linq.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SharpCompress, Version=0.18.1.0, Culture=neutral, PublicKeyToken=afb0a02973931d96, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SharpCompress.0.18.1\lib\net45\SharpCompress.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="SQLite.Net, Version=3.1.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SQLite.Net.Core-PCL.3.1.1\lib\portable-win8+net45+wp8+wpa81+MonoAndroid1+MonoTouch1\SQLite.Net.dll</HintPath>
|
||||
<Reference Include="SQLitePCLRaw.batteries_green, Version=1.0.0.0, Culture=neutral, PublicKeyToken=a84b7dcfb1391f7f, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SQLitePCLRaw.bundle_green.1.1.8\lib\net45\SQLitePCLRaw.batteries_green.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="SQLite.Net.Platform.Generic, Version=3.1.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SQLite.Net-PCL.3.1.1\lib\net40\SQLite.Net.Platform.Generic.dll</HintPath>
|
||||
<Reference Include="SQLitePCLRaw.batteries_v2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=8226ea5df37bcae9, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SQLitePCLRaw.bundle_green.1.1.8\lib\net45\SQLitePCLRaw.batteries_v2.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="SQLite.Net.Platform.Win32, Version=3.1.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SQLite.Net-PCL.3.1.1\lib\net4\SQLite.Net.Platform.Win32.dll</HintPath>
|
||||
<Reference Include="SQLitePCLRaw.core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1488e028ca7ab535, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SQLitePCLRaw.core.1.1.8\lib\net45\SQLitePCLRaw.core.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="SQLiteNetExtensions, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SQLiteNetExtensions.1.3.0\lib\portable-net45+netcore45+wpa81+wp8+MonoAndroid1+MonoTouch1\SQLiteNetExtensions.dll</HintPath>
|
||||
<Reference Include="SQLitePCLRaw.provider.e_sqlite3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=9c301db686d0bd12, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SQLitePCLRaw.provider.e_sqlite3.net45.1.1.8\lib\net45\SQLitePCLRaw.provider.e_sqlite3.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Collections.Immutable, Version=1.2.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\System.Collections.Immutable.1.4.0\lib\netstandard2.0\System.Collections.Immutable.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.ComponentModel.Annotations, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\System.ComponentModel.Annotations.4.4.0\lib\net461\System.ComponentModel.Annotations.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.ComponentModel.Composition" />
|
||||
<Reference Include="System.ComponentModel.DataAnnotations" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Diagnostics.DiagnosticSource, Version=4.0.2.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\System.Diagnostics.DiagnosticSource.4.4.1\lib\net46\System.Diagnostics.DiagnosticSource.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Interactive.Async, Version=3.0.3000.0, Culture=neutral, PublicKeyToken=94bc3704cddfc263, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\System.Interactive.Async.3.1.1\lib\net46\System.Interactive.Async.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=4.0.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\System.Runtime.CompilerServices.Unsafe.4.4.0\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.ValueTuple, Version=4.0.2.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51">
|
||||
<HintPath>$(SolutionDir)\packages\System.ValueTuple.4.4.0\lib\net461\System.ValueTuple.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\osu.licenseheader">
|
||||
@ -213,6 +280,12 @@
|
||||
<Compile Include="Beatmaps\Drawables\BeatmapPanel.cs" />
|
||||
<Compile Include="Beatmaps\Drawables\BeatmapSetCover.cs" />
|
||||
<Compile Include="Beatmaps\Drawables\BeatmapSetHeader.cs" />
|
||||
<Compile Include="Database\DatabaseContextFactory.cs" />
|
||||
<Compile Include="Migrations\20171019041408_InitialCreate.cs" />
|
||||
<Compile Include="Migrations\20171019041408_InitialCreate.designer.cs">
|
||||
<DependentUpon>20171019041408_InitialCreate.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Migrations\OsuDbContextModelSnapshot.cs" />
|
||||
<Compile Include="Online\API\Requests\GetBeatmapSetRequest.cs" />
|
||||
<Compile Include="Online\API\Requests\GetBeatmapSetsResponse.cs" />
|
||||
<Compile Include="Screens\Edit\Screens\Compose\Timeline\BeatmapWaveformGraph.cs" />
|
||||
@ -247,7 +320,7 @@
|
||||
<Compile Include="Configuration\ScreenshotFormat.cs" />
|
||||
<Compile Include="Configuration\SelectionRandomType.cs" />
|
||||
<Compile Include="Database\DatabaseBackedStore.cs" />
|
||||
<Compile Include="Database\StoreVersion.cs" />
|
||||
<Compile Include="Database\OsuDbContext.cs" />
|
||||
<Compile Include="Graphics\Backgrounds\Background.cs" />
|
||||
<Compile Include="Graphics\Backgrounds\Triangles.cs" />
|
||||
<Compile Include="Graphics\Containers\BeatSyncedContainer.cs" />
|
||||
@ -767,6 +840,7 @@
|
||||
<Compile Include="Overlays\BeatmapSet\PreviewButton.cs" />
|
||||
<Compile Include="Tests\Visual\TestCaseBeatmapSetOverlay.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
@ -787,4 +861,7 @@
|
||||
<PostBuildEvent>
|
||||
</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets" Condition="Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets')" />
|
||||
<Import Project="$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets" Condition="Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets')" />
|
||||
<Import Project="$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets" Condition="Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets')" />
|
||||
</Project>
|
@ -5,11 +5,49 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste
|
||||
-->
|
||||
<packages>
|
||||
<package id="DotNetZip" version="1.10.1" targetFramework="net461" />
|
||||
<package id="Microsoft.CSharp" version="4.4.0" targetFramework="net461" />
|
||||
<package id="Microsoft.Data.Sqlite.Core" version="2.0.0" targetFramework="net461" />
|
||||
<package id="Microsoft.EntityFrameworkCore" version="2.0.0" targetFramework="net461" />
|
||||
<package id="Microsoft.EntityFrameworkCore.Design" version="2.0.0" targetFramework="net461" />
|
||||
<package id="Microsoft.EntityFrameworkCore.Relational" version="2.0.0" targetFramework="net461" />
|
||||
<package id="Microsoft.EntityFrameworkCore.Sqlite" version="2.0.0" targetFramework="net461" />
|
||||
<package id="Microsoft.EntityFrameworkCore.Sqlite.Core" version="2.0.0" targetFramework="net461" />
|
||||
<package id="Microsoft.EntityFrameworkCore.Tools" version="2.0.0" targetFramework="net461" developmentDependency="true" />
|
||||
<package id="Microsoft.Extensions.Caching.Abstractions" version="2.0.0" targetFramework="net461" />
|
||||
<package id="Microsoft.Extensions.Caching.Memory" version="2.0.0" targetFramework="net461" />
|
||||
<package id="Microsoft.Extensions.Configuration.Abstractions" version="2.0.0" targetFramework="net461" />
|
||||
<package id="Microsoft.Extensions.DependencyInjection" version="2.0.0" targetFramework="net461" />
|
||||
<package id="Microsoft.Extensions.DependencyInjection.Abstractions" version="2.0.0" targetFramework="net461" />
|
||||
<package id="Microsoft.Extensions.Logging" version="2.0.0" targetFramework="net461" />
|
||||
<package id="Microsoft.Extensions.Logging.Abstractions" version="2.0.0" targetFramework="net461" />
|
||||
<package id="Microsoft.Extensions.Options" version="2.0.0" targetFramework="net461" />
|
||||
<package id="Microsoft.Extensions.Primitives" version="2.0.0" targetFramework="net461" />
|
||||
<package id="Newtonsoft.Json" version="10.0.3" targetFramework="net461" />
|
||||
<package id="NUnit" version="3.8.1" targetFramework="net461" />
|
||||
<package id="OpenTK" version="3.0.0-git00009" targetFramework="net461" />
|
||||
<package id="Remotion.Linq" version="2.1.2" targetFramework="net461" />
|
||||
<package id="SharpCompress" version="0.18.1" targetFramework="net461" />
|
||||
<package id="SQLite.Net.Core-PCL" version="3.1.1" targetFramework="net461" />
|
||||
<package id="SQLite.Net-PCL" version="3.1.1" targetFramework="net461" />
|
||||
<package id="SQLiteNetExtensions" version="1.3.0" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.bundle_green" version="1.1.8" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.core" version="1.1.8" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.lib.e_sqlite3.linux" version="1.1.8" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.lib.e_sqlite3.osx" version="1.1.8" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.lib.e_sqlite3.v110_xp" version="1.1.8" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.provider.e_sqlite3.net45" version="1.1.8" targetFramework="net461" />
|
||||
<package id="System.Collections" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.Collections.Immutable" version="1.4.0" targetFramework="net461" />
|
||||
<package id="System.ComponentModel.Annotations" version="4.4.0" targetFramework="net461" />
|
||||
<package id="System.Diagnostics.Debug" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.Diagnostics.DiagnosticSource" version="4.4.1" targetFramework="net461" />
|
||||
<package id="System.Interactive.Async" version="3.1.1" targetFramework="net461" />
|
||||
<package id="System.Linq" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.Linq.Expressions" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.Linq.Queryable" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.ObjectModel" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.Reflection" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.Reflection.Extensions" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.Runtime" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.Runtime.CompilerServices.Unsafe" version="4.4.0" targetFramework="net461" />
|
||||
<package id="System.Runtime.Extensions" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.Threading" version="4.3.0" targetFramework="net461" />
|
||||
<package id="System.ValueTuple" version="4.4.0" targetFramework="net461" />
|
||||
</packages>
|
@ -3,6 +3,7 @@
|
||||
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Emp3/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Epng/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Ewav/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=2A66DD92_002DADB1_002D4994_002D89E2_002DC94E04ACDA0D_002Fd_003AMigrations/@EntryIndexedValue">ExplicitlyExcluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=D9A367C9_002D4C1A_002D489F_002D9B05_002DA0CEA2B53B58/@EntryIndexedValue">ExplicitlyExcluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/AnalysisEnabled/@EntryValue">SOLUTION</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ArrangeAccessorOwnerBody/@EntryIndexedValue">HINT</s:String>
|
||||
|
Loading…
Reference in New Issue
Block a user