1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-06 06:57:39 +08:00

Merge branch 'master' into musiccontroller-canbeatmapchange

This commit is contained in:
Dean Herbert 2017-08-01 11:54:05 +09:00 committed by GitHub
commit 209d024caa
248 changed files with 2485 additions and 2045 deletions

@ -1 +1 @@
Subproject commit 6b44a9f807fadcb3b3f044780d7e27d62ffe80ac
Subproject commit 2204764944ff693e857649253547054cc91764a0

View File

@ -177,9 +177,7 @@ namespace osu.Desktop.VisualTests.Tests
beatsPerMinute.Value = 60000 / timingPoint.BeatLength;
adjustedBeatLength.Value = timingPoint.BeatLength;
flashLayer.ClearTransforms();
flashLayer.FadeTo(1);
flashLayer.FadeTo(0, timingPoint.BeatLength);
flashLayer.FadeOutFromOne(timingPoint.BeatLength);
}
}

View File

@ -3,9 +3,9 @@
using osu.Framework.Graphics;
using osu.Framework.Testing;
using osu.Game.Database;
using osu.Game.Screens.Select;
using System.Linq;
using osu.Game.Beatmaps;
namespace osu.Desktop.VisualTests.Tests
{

View File

@ -59,20 +59,13 @@ namespace osu.Desktop.VisualTests.Tests
{
base.LoadComplete();
using (container.BeginLoopedSequence())
{
container.MoveTo(new Vector2(0, 100), duration);
using (container.BeginDelayedSequence(duration))
{
container.MoveTo(new Vector2(100, 100), duration);
using (container.BeginDelayedSequence(duration))
{
container.MoveTo(new Vector2(100, 0), duration);
using (container.BeginDelayedSequence(duration))
container.MoveTo(Vector2.Zero, duration);
}
}
}
// Move box along a square trajectory
container.Loop(c => c
.MoveTo(new Vector2(0, 100), duration).Then()
.MoveTo(new Vector2(100, 100), duration).Then()
.MoveTo(new Vector2(100, 0), duration).Then()
.MoveTo(Vector2.Zero, duration)
);
}
private class MyContextMenuContainer : Container, IHasContextMenu
@ -95,10 +88,10 @@ namespace osu.Desktop.VisualTests.Tests
{
new OsuContextMenuItem(@"Simple option"),
new OsuContextMenuItem(@"Simple very very long option"),
new OsuContextMenuItem(@"Change width", MenuItemType.Highlighted) { Action = () => ResizeWidthTo(Width * 2, 100, EasingTypes.OutQuint) },
new OsuContextMenuItem(@"Change height", MenuItemType.Highlighted) { Action = () => ResizeHeightTo(Height * 2, 100, EasingTypes.OutQuint) },
new OsuContextMenuItem(@"Change width back", MenuItemType.Destructive) { Action = () => ResizeWidthTo(Width / 2, 100, EasingTypes.OutQuint) },
new OsuContextMenuItem(@"Change height back", MenuItemType.Destructive) { Action = () => ResizeHeightTo(Height / 2, 100, EasingTypes.OutQuint) },
new OsuContextMenuItem(@"Change width", MenuItemType.Highlighted) { Action = () => this.ResizeWidthTo(Width * 2, 100, Easing.OutQuint) },
new OsuContextMenuItem(@"Change height", MenuItemType.Highlighted) { Action = () => this.ResizeHeightTo(Height * 2, 100, Easing.OutQuint) },
new OsuContextMenuItem(@"Change width back", MenuItemType.Destructive) { Action = () => this.ResizeWidthTo(Width / 2, 100, Easing.OutQuint) },
new OsuContextMenuItem(@"Change height back", MenuItemType.Destructive) { Action = () => this.ResizeHeightTo(Height / 2, 100, Easing.OutQuint) },
};
}
}

View File

@ -4,8 +4,9 @@
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Testing;
using osu.Game.Database;
using osu.Game.Beatmaps;
using osu.Game.Overlays;
using osu.Game.Rulesets;
namespace osu.Desktop.VisualTests.Tests
{
@ -14,7 +15,7 @@ namespace osu.Desktop.VisualTests.Tests
public override string Description => @"osu!direct overlay";
private DirectOverlay direct;
private RulesetDatabase rulesets;
private RulesetStore rulesets;
protected override void LoadComplete()
{
@ -28,7 +29,7 @@ namespace osu.Desktop.VisualTests.Tests
}
[BackgroundDependencyLoader]
private void load(RulesetDatabase rulesets)
private void load(RulesetStore rulesets)
{
this.rulesets = rulesets;
}

View File

@ -7,8 +7,9 @@ using osu.Framework.Testing;
using osu.Game.Screens.Multiplayer;
using osu.Game.Online.Multiplayer;
using osu.Game.Users;
using osu.Game.Database;
using osu.Framework.Allocation;
using osu.Game.Beatmaps;
using osu.Game.Rulesets;
namespace osu.Desktop.VisualTests.Tests
{
@ -16,7 +17,7 @@ namespace osu.Desktop.VisualTests.Tests
{
public override string Description => @"Select your favourite room";
private RulesetDatabase rulesets;
private RulesetStore rulesets;
protected override void LoadComplete()
{
@ -124,7 +125,7 @@ namespace osu.Desktop.VisualTests.Tests
}
[BackgroundDependencyLoader]
private void load(RulesetDatabase rulesets)
private void load(RulesetStore rulesets)
{
this.rulesets = rulesets;
}

View File

@ -8,7 +8,6 @@ using osu.Framework.MathUtils;
using osu.Framework.Testing;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Rulesets.Catch.UI;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Objects;
@ -19,17 +18,18 @@ using System.Collections.Generic;
using osu.Desktop.VisualTests.Beatmaps;
using osu.Framework.Allocation;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets;
namespace osu.Desktop.VisualTests.Tests
{
internal class TestCaseGamefield : TestCase
{
private RulesetDatabase rulesets;
private RulesetStore rulesets;
public override string Description => @"Showing hitobjects and what not.";
[BackgroundDependencyLoader]
private void load(RulesetDatabase rulesets)
private void load(RulesetStore rulesets)
{
this.rulesets = rulesets;
}

View File

@ -96,7 +96,7 @@ namespace osu.Desktop.VisualTests.Tests
{
SelectionBox.ScaleTo(
new Vector2(value, 1),
300, EasingTypes.OutQuint);
300, Easing.OutQuint);
}
}
}

View File

@ -17,7 +17,7 @@ namespace osu.Desktop.VisualTests.Tests
{
Add(new Box
{
ColourInfo = ColourInfo.GradientVertical(Color4.Gray, Color4.WhiteSmoke),
Colour = ColourInfo.GradientVertical(Color4.Gray, Color4.WhiteSmoke),
RelativeSizeAxes = Framework.Graphics.Axes.Both,
});
Add(new ButtonSystem());

View File

@ -5,7 +5,7 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Overlays.Mods;
using osu.Framework.Testing;
using osu.Game.Database;
using osu.Game.Rulesets;
using osu.Game.Screens.Play.HUD;
using OpenTK;
@ -18,11 +18,11 @@ namespace osu.Desktop.VisualTests.Tests
private ModSelectOverlay modSelect;
private ModDisplay modDisplay;
private RulesetDatabase rulesets;
private RulesetStore rulesets;
[BackgroundDependencyLoader]
private void load(RulesetDatabase rulesets)
private void load(RulesetStore rulesets)
{
this.rulesets = rulesets;
}

View File

@ -12,17 +12,17 @@ using osu.Framework.Graphics.Containers;
namespace osu.Desktop.VisualTests.Tests
{
internal class TestCaseNotificationManager : TestCase
internal class TestCaseNotificationOverlay : TestCase
{
public override string Description => @"I handle notifications";
private readonly NotificationManager manager;
private readonly NotificationOverlay manager;
public TestCaseNotificationManager()
public TestCaseNotificationOverlay()
{
progressingNotifications.Clear();
Content.Add(manager = new NotificationManager
Content.Add(manager = new NotificationOverlay
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
@ -56,10 +56,7 @@ namespace osu.Desktop.VisualTests.Tests
}
if (remaining > 0)
{
Delay(80);
Schedule(() => sendBarrage(remaining - 1));
}
Scheduler.AddDelayed(() => sendBarrage(remaining - 1), 80);
}
protected override void Update()

View File

@ -5,7 +5,9 @@ using System.Collections.Generic;
using osu.Desktop.VisualTests.Platform;
using osu.Framework.Testing;
using osu.Framework.MathUtils;
using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Rulesets;
using osu.Game.Screens.Select;
using osu.Game.Screens.Select.Filter;
@ -13,31 +15,28 @@ namespace osu.Desktop.VisualTests.Tests
{
internal class TestCasePlaySongSelect : TestCase
{
private readonly BeatmapDatabase db;
private readonly BeatmapManager manager;
public override string Description => @"with fake data";
private readonly RulesetDatabase rulesets;
private readonly RulesetStore rulesets;
public TestCasePlaySongSelect()
{
PlaySongSelect songSelect;
if (db == null)
if (manager == null)
{
var storage = new TestStorage(@"TestCasePlaySongSelect");
var backingDatabase = storage.GetDatabase(@"client");
backingDatabase.CreateTable<StoreVersion>();
rulesets = new RulesetDatabase(storage, backingDatabase);
db = new BeatmapDatabase(storage, backingDatabase, rulesets);
var sets = new List<BeatmapSetInfo>();
rulesets = new RulesetStore(backingDatabase);
manager = new BeatmapManager(storage, null, backingDatabase, rulesets);
for (int i = 0; i < 100; i += 10)
sets.Add(createTestBeatmapSet(i));
db.Import(sets);
manager.Import(createTestBeatmapSet(i));
}
Add(songSelect = new PlaySongSelect());
@ -48,21 +47,12 @@ namespace osu.Desktop.VisualTests.Tests
AddStep(@"Sort by Difficulty", delegate { songSelect.FilterControl.Sort = SortMode.Difficulty; });
}
//protected override void Dispose(bool isDisposing)
//{
// if (oldDb != null)
// db = null;
// base.Dispose(isDisposing);
//}
private BeatmapSetInfo createTestBeatmapSet(int i)
{
return new BeatmapSetInfo
{
OnlineBeatmapSetID = 1234 + i,
Hash = "d8e8fca2dc0f896fd7cb4cb0031ba249",
Path = string.Empty,
Metadata = new BeatmapMetadata
{
OnlineBeatmapSetID = 1234 + i,

View File

@ -6,7 +6,6 @@ using osu.Framework.Allocation;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using OpenTK;
using osu.Game.Database;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Screens.Play;
@ -14,18 +13,19 @@ using OpenTK.Graphics;
using osu.Desktop.VisualTests.Beatmaps;
using osu.Game.Rulesets.Osu.UI;
using osu.Framework.Graphics.Shapes;
using osu.Game.Rulesets;
namespace osu.Desktop.VisualTests.Tests
{
internal class TestCasePlayer : TestCase
{
protected Player Player;
private RulesetDatabase rulesets;
private RulesetStore rulesets;
public override string Description => @"Showing everything to play the game.";
[BackgroundDependencyLoader]
private void load(RulesetDatabase rulesets)
private void load(RulesetStore rulesets)
{
this.rulesets = rulesets;
}

View File

@ -3,11 +3,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Ranking;
using osu.Game.Users;
@ -16,14 +14,14 @@ namespace osu.Desktop.VisualTests.Tests
{
internal class TestCaseResults : TestCase
{
private BeatmapDatabase db;
private BeatmapManager beatmaps;
public override string Description => @"Results after playing.";
[BackgroundDependencyLoader]
private void load(BeatmapDatabase db)
private void load(BeatmapManager beatmaps)
{
this.db = db;
this.beatmaps = beatmaps;
}
private WorkingBeatmap beatmap;
@ -34,9 +32,9 @@ namespace osu.Desktop.VisualTests.Tests
if (beatmap == null)
{
var beatmapInfo = db.Query<BeatmapInfo>().FirstOrDefault(b => b.RulesetID == 0);
var beatmapInfo = beatmaps.QueryBeatmap(b => b.RulesetID == 0);
if (beatmapInfo != null)
beatmap = db.GetWorkingBeatmap(beatmapInfo);
beatmap = beatmaps.GetWorkingBeatmap(beatmapInfo);
}
Add(new Results(new Score

View File

@ -4,10 +4,11 @@
using osu.Framework.Testing;
using osu.Framework.Graphics;
using osu.Game.Screens.Multiplayer;
using osu.Game.Database;
using osu.Game.Online.Multiplayer;
using osu.Game.Users;
using osu.Framework.Allocation;
using osu.Game.Beatmaps;
using osu.Game.Rulesets;
namespace osu.Desktop.VisualTests.Tests
{
@ -15,7 +16,7 @@ namespace osu.Desktop.VisualTests.Tests
{
public override string Description => @"from the multiplayer lobby";
private RulesetDatabase rulesets;
private RulesetStore rulesets;
protected override void LoadComplete()
{
@ -135,7 +136,7 @@ namespace osu.Desktop.VisualTests.Tests
}
[BackgroundDependencyLoader]
private void load(RulesetDatabase rulesets)
private void load(RulesetStore rulesets)
{
this.rulesets = rulesets;
}

View File

@ -196,7 +196,7 @@ namespace osu.Desktop.VisualTests.Tests
protected override void LoadComplete()
{
base.LoadComplete();
FadeInFromZero(250, EasingTypes.OutQuint);
this.FadeInFromZero(250, Easing.OutQuint);
}
protected override void Update()

View File

@ -67,6 +67,8 @@ namespace osu.Desktop.VisualTests.Tests
private void changePlayfieldSize(int step)
{
double delay = 0;
// Add new hits
switch (step)
{
@ -84,7 +86,7 @@ namespace osu.Desktop.VisualTests.Tests
break;
case 5:
addSwell(1000);
playfieldContainer.Delay(scroll_time - 100);
delay = scroll_time - 100;
break;
}
@ -92,10 +94,10 @@ namespace osu.Desktop.VisualTests.Tests
switch (step)
{
default:
playfieldContainer.ResizeTo(new Vector2(1, rng.Next(25, 400)), 500);
playfieldContainer.Delay(delay).ResizeTo(new Vector2(1, rng.Next(25, 400)), 500);
break;
case 6:
playfieldContainer.ResizeTo(new Vector2(1, TaikoPlayfield.DEFAULT_PLAYFIELD_HEIGHT), 500);
playfieldContainer.Delay(delay).ResizeTo(new Vector2(1, TaikoPlayfield.DEFAULT_PLAYFIELD_HEIGHT), 500);
break;
}
}

View File

@ -198,7 +198,7 @@
<Compile Include="Tests\TestCaseManiaPlayfield.cs" />
<Compile Include="Tests\TestCaseMenuOverlays.cs" />
<Compile Include="Tests\TestCaseMusicController.cs" />
<Compile Include="Tests\TestCaseNotificationManager.cs" />
<Compile Include="Tests\TestCaseNotificationOverlay.cs" />
<Compile Include="Tests\TestCaseOnScreenDisplay.cs" />
<Compile Include="Tests\TestCaseReplaySettingsOverlay.cs" />
<Compile Include="Tests\TestCasePlayer.cs" />

View File

@ -1,43 +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 System.IO;
using System.Linq;
using osu.Game.Beatmaps.IO;
namespace osu.Desktop.Beatmaps.IO
{
/// <summary>
/// Reads an extracted legacy beatmap from disk.
/// </summary>
public class LegacyFilesystemReader : ArchiveReader
{
public static void Register() => AddReader<LegacyFilesystemReader>((storage, path) => Directory.Exists(path));
private readonly string basePath;
public LegacyFilesystemReader(string path)
{
basePath = path;
BeatmapFilenames = Directory.GetFiles(basePath, @"*.osu").Select(Path.GetFileName).ToArray();
if (BeatmapFilenames.Length == 0)
throw new FileNotFoundException(@"This directory contains no beatmaps");
StoryboardFilename = Directory.GetFiles(basePath, @"*.osb").Select(Path.GetFileName).FirstOrDefault();
}
public override Stream GetStream(string name)
{
return File.OpenRead(Path.Combine(basePath, name));
}
public override void Dispose()
{
// no-op
}
public override Stream GetUnderlyingStream() => null;
}
}

View File

@ -65,11 +65,11 @@ namespace osu.Desktop
var filePaths = dropData.Select(f => f.ToString()).ToArray();
if (filePaths.All(f => Path.GetExtension(f) == @".osz"))
Task.Run(() => BeatmapDatabase.Import(filePaths));
Task.Run(() => BeatmapManager.Import(filePaths));
else if (filePaths.All(f => Path.GetExtension(f) == @".osr"))
Task.Run(() =>
{
var score = ScoreDatabase.ReadReplayFile(filePaths.First());
var score = ScoreStore.ReadReplayFile(filePaths.First());
Schedule(() => LoadScore(score));
});
}

View File

@ -25,16 +25,16 @@ namespace osu.Desktop.Overlays
public class VersionManager : OverlayContainer
{
private UpdateManager updateManager;
private NotificationManager notificationManager;
private NotificationOverlay notificationOverlay;
protected override bool HideOnEscape => false;
public override bool HandleInput => false;
[BackgroundDependencyLoader]
private void load(NotificationManager notification, OsuColour colours, TextureStore textures, OsuGameBase game)
private void load(NotificationOverlay notification, OsuColour colours, TextureStore textures, OsuGameBase game)
{
notificationManager = notification;
notificationOverlay = notification;
AutoSizeAxes = Axes.Both;
Anchor = Anchor.BottomCentre;
@ -116,7 +116,7 @@ namespace osu.Desktop.Overlays
if (notification == null)
{
notification = new UpdateProgressNotification { State = ProgressNotificationState.Active };
Schedule(() => notificationManager.Post(notification));
Schedule(() => notificationOverlay.Post(notification));
}
Schedule(() =>
@ -175,7 +175,7 @@ namespace osu.Desktop.Overlays
protected override void PopIn()
{
FadeIn(1000);
this.FadeIn(1000);
}
protected override void PopOut()
@ -207,7 +207,7 @@ namespace osu.Desktop.Overlays
new Box
{
RelativeSizeAxes = Axes.Both,
ColourInfo = ColourInfo.GradientVertical(colours.YellowDark, colours.Yellow)
Colour = ColourInfo.GradientVertical(colours.YellowDark, colours.Yellow)
},
new TextAwesome
{

View File

@ -3,7 +3,6 @@
using System;
using System.IO;
using osu.Desktop.Beatmaps.IO;
using osu.Framework.Desktop;
using osu.Framework.Desktop.Platform;
using osu.Game.IPC;
@ -15,8 +14,6 @@ namespace osu.Desktop
[STAThread]
public static int Main(string[] args)
{
LegacyFilesystemReader.Register();
// Back up the cwd before DesktopGameHost changes it
var cwd = Environment.CurrentDirectory;

View File

@ -228,7 +228,6 @@
<Compile Include="OsuGameDesktop.cs" />
<Compile Include="Overlays\VersionManager.cs" />
<Compile Include="Program.cs" />
<Compile Include="Beatmaps\IO\LegacyFilesystemReader.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>

View File

@ -10,7 +10,6 @@ using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Mania.Beatmaps.Patterns;
using osu.Game.Rulesets.Mania.MathUtils;
using osu.Game.Database;
using osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy;
using OpenTK;
using osu.Game.Audio;

View File

@ -4,7 +4,6 @@
using System;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Rulesets.Mania.MathUtils;
using osu.Game.Rulesets.Objects;
using OpenTK;

View File

@ -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 osu.Game.Database;
using osu.Game.Beatmaps;
namespace osu.Game.Rulesets.Mania.Judgements
{

View File

@ -2,8 +2,8 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Database;
using osu.Game.Rulesets.Objects.Types;
namespace osu.Game.Rulesets.Mania.Objects

View File

@ -1,8 +1,8 @@
// 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.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Database;
using osu.Game.Rulesets.Mania.Judgements;
namespace osu.Game.Rulesets.Mania.Objects

View File

@ -4,7 +4,6 @@
using System;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Objects.Drawables;

View File

@ -122,7 +122,7 @@ namespace osu.Game.Rulesets.Mania.UI
{
Name = "Key gradient",
RelativeSizeAxes = Axes.Both,
ColourInfo = ColourInfo.GradientVertical(Color4.Black, Color4.Black.Opacity(0)),
Colour = ColourInfo.GradientVertical(Color4.Black, Color4.Black.Opacity(0)),
Alpha = 0.5f
},
keyIcon = new Container
@ -206,8 +206,8 @@ namespace osu.Game.Rulesets.Mania.UI
if (args.Key == Key)
{
background.FadeTo(background.Alpha + 0.2f, 50, EasingTypes.OutQuint);
keyIcon.ScaleTo(1.4f, 50, EasingTypes.OutQuint);
background.FadeTo(background.Alpha + 0.2f, 50, Easing.OutQuint);
keyIcon.ScaleTo(1.4f, 50, Easing.OutQuint);
}
return false;
@ -217,8 +217,8 @@ namespace osu.Game.Rulesets.Mania.UI
{
if (args.Key == Key)
{
background.FadeTo(0.2f, 800, EasingTypes.OutQuart);
keyIcon.ScaleTo(1f, 400, EasingTypes.OutQuart);
background.FadeTo(0.2f, 800, Easing.OutQuart);
keyIcon.ScaleTo(1f, 400, Easing.OutQuart);
}
return false;

View File

@ -16,8 +16,6 @@ using System.Linq;
using System.Collections.Generic;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Framework.Input;
using osu.Framework.Graphics.Transforms;
using osu.Framework.MathUtils;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Timing;
using osu.Framework.Configuration;
@ -222,10 +220,10 @@ namespace osu.Game.Rulesets.Mania.UI
switch (args.Key)
{
case Key.Minus:
transformVisibleTimeRangeTo(visibleTimeRange + time_span_step, 200, EasingTypes.OutQuint);
transformVisibleTimeRangeTo(visibleTimeRange + time_span_step, 200, Easing.OutQuint);
break;
case Key.Plus:
transformVisibleTimeRangeTo(visibleTimeRange - time_span_step, 200, EasingTypes.OutQuint);
transformVisibleTimeRangeTo(visibleTimeRange - time_span_step, 200, Easing.OutQuint);
break;
}
}
@ -233,9 +231,9 @@ namespace osu.Game.Rulesets.Mania.UI
return false;
}
private void transformVisibleTimeRangeTo(double newTimeRange, double duration = 0, EasingTypes easing = EasingTypes.None)
private void transformVisibleTimeRangeTo(double newTimeRange, double duration = 0, Easing easing = Easing.None)
{
TransformTo(newTimeRange, duration, easing, new TransformTimeSpan());
this.TransformTo(nameof(visibleTimeRange), newTimeRange, duration, easing);
}
protected override void Update()
@ -244,23 +242,5 @@ namespace osu.Game.Rulesets.Mania.UI
// While masking on effectively only the Y-axis, so we need to set the width of the bar line container manually
barLineContainer.Width = columns.Width;
}
private class TransformTimeSpan : Transform<double, Drawable>
{
public double CurrentValue
{
get
{
double time = Time?.Current ?? 0;
if (time < StartTime) return StartValue;
if (time >= EndTime) return EndValue;
return Interpolation.ValueAt(time, StartValue, EndValue, StartTime, EndTime, Easing);
}
}
public override void Apply(Drawable d) => ((ManiaPlayfield)d).visibleTimeRange.Value = (float)CurrentValue;
public override void ReadIntoStartValue(Drawable d) => StartValue = ((ManiaPlayfield)d).visibleTimeRange.Value;
}
}
}

View File

@ -94,12 +94,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
using (fp.BeginAbsoluteSequence(fadeInTime))
{
fp.FadeIn(DrawableOsuHitObject.TIME_FADEIN);
fp.ScaleTo(1, DrawableOsuHitObject.TIME_FADEIN, EasingTypes.Out);
fp.ScaleTo(1, DrawableOsuHitObject.TIME_FADEIN, Easing.Out);
fp.MoveTo(pointEndPosition, DrawableOsuHitObject.TIME_FADEIN, EasingTypes.Out);
fp.MoveTo(pointEndPosition, DrawableOsuHitObject.TIME_FADEIN, Easing.Out);
fp.Delay(fadeOutTime - fadeInTime);
fp.FadeOut(DrawableOsuHitObject.TIME_FADEIN);
fp.Delay(fadeOutTime - fadeInTime).FadeOut(DrawableOsuHitObject.TIME_FADEIN);
}
fp.Expire(true);

View File

@ -112,29 +112,26 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
double duration = ((HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime) - HitObject.StartTime;
using (glow.BeginDelayedSequence(duration))
glow.FadeOut(400);
glow.Delay(duration).FadeOut(400);
switch (state)
{
case ArmedState.Idle:
using (BeginDelayedSequence(duration + TIME_PREEMPT))
FadeOut(TIME_FADEOUT);
this.Delay(duration + TIME_PREEMPT).FadeOut(TIME_FADEOUT);
Expire(true);
break;
case ArmedState.Miss:
ApproachCircle.FadeOut(50);
FadeOut(TIME_FADEOUT / 5);
this.FadeOut(TIME_FADEOUT / 5);
Expire();
break;
case ArmedState.Hit:
ApproachCircle.FadeOut(50);
const double flash_in = 40;
flash.FadeTo(0.8f, flash_in);
using (flash.BeginDelayedSequence(flash_in))
flash.FadeOut(100);
flash.FadeTo(0.8f, flash_in)
.Then()
.FadeOut(100);
explode.FadeIn(flash_in);
@ -145,8 +142,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
circle.FadeOut();
number.FadeOut();
FadeOut(800);
ScaleTo(Scale * 1.5f, 400, EasingTypes.OutQuad);
this.FadeOut(800)
.ScaleTo(Scale * 1.5f, 400, Easing.OutQuad);
}
Expire();

View File

@ -4,6 +4,7 @@
using System.ComponentModel;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Framework.Graphics;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
@ -24,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected sealed override void UpdateState(ArmedState state)
{
Flush();
FinishTransforms();
using (BeginAbsoluteSequence(HitObject.StartTime - TIME_PREEMPT, true))
{
@ -44,7 +45,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected virtual void UpdatePreemptState()
{
FadeIn(TIME_FADEIN);
this.FadeIn(TIME_FADEIN);
}
protected virtual void UpdateCurrentState(ArmedState state)

View File

@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected override void LoadComplete()
{
if (Judgement.Result != HitResult.Miss)
JudgementText.TransformSpacingTo(new Vector2(14, 0), 1800, EasingTypes.OutQuint);
JudgementText.TransformSpacingTo(new Vector2(14, 0), 1800, Easing.OutQuint);
base.LoadComplete();
}

View File

@ -158,14 +158,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
ball.FadeIn();
Delay(slider.Duration, true);
using (BeginDelayedSequence(slider.Duration, true))
{
body.FadeOut(160);
ball.FadeOut(160);
body.FadeOut(160);
ball.FadeOut(160);
FadeOut(800);
Expire();
this.FadeOut(800)
.Expire();
}
}
public Drawable ProxiedLayer => initialCircle.ApproachCircle;

View File

@ -62,14 +62,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
var animIn = Math.Min(150, sliderTick.StartTime - FadeInTime);
ScaleTo(0.5f);
ScaleTo(1.2f, animIn);
FadeIn(animIn);
Delay(animIn);
ScaleTo(1, 150, EasingTypes.Out);
Delay(-animIn);
this.Animate(
d => d.FadeIn(animIn),
d => d.ScaleTo(0.5f).ScaleTo(1.2f, animIn)
).Then(
d => d.ScaleTo(1, 150, Easing.Out)
);
}
protected override void UpdateCurrentState(ArmedState state)
@ -77,16 +75,15 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
switch (state)
{
case ArmedState.Idle:
Delay(FadeOutTime - sliderTick.StartTime);
FadeOut();
this.Delay(FadeOutTime - sliderTick.StartTime).FadeOut();
break;
case ArmedState.Miss:
FadeOut(160);
FadeColour(Color4.Red, 80);
this.FadeOut(160)
.FadeColour(Color4.Red, 80);
break;
case ArmedState.Hit:
FadeOut(120, EasingTypes.OutQuint);
ScaleTo(Scale * 1.5f, 120, EasingTypes.OutQuint);
this.FadeOut(120, Easing.OutQuint)
.ScaleTo(Scale * 1.5f, 120, Easing.OutQuint);
break;
}
}

View File

@ -174,9 +174,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
ticks.Rotation = disc.Rotation;
float relativeCircleScale = spinner.Scale * circle.DrawHeight / mainContainer.DrawHeight;
disc.ScaleTo(relativeCircleScale + (1 - relativeCircleScale) * Progress, 200, EasingTypes.OutQuint);
disc.ScaleTo(relativeCircleScale + (1 - relativeCircleScale) * Progress, 200, Easing.OutQuint);
symbol.RotateTo(disc.Rotation / 2, 500, EasingTypes.OutQuint);
symbol.RotateTo(disc.Rotation / 2, 500, Easing.OutQuint);
}
protected override void UpdatePreemptState()
@ -184,35 +184,33 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
base.UpdatePreemptState();
circleContainer.ScaleTo(spinner.Scale * 0.3f);
circleContainer.ScaleTo(spinner.Scale, TIME_PREEMPT / 1.4f, EasingTypes.OutQuint);
circleContainer.ScaleTo(spinner.Scale, TIME_PREEMPT / 1.4f, Easing.OutQuint);
disc.RotateTo(-720);
symbol.RotateTo(-720);
mainContainer.ScaleTo(0);
mainContainer.ScaleTo(spinner.Scale * circle.DrawHeight / DrawHeight * 1.4f, TIME_PREEMPT - 150, EasingTypes.OutQuint);
mainContainer.Delay(TIME_PREEMPT - 150);
mainContainer.ScaleTo(1, 500, EasingTypes.OutQuint);
mainContainer
.ScaleTo(0)
.ScaleTo(spinner.Scale * circle.DrawHeight / DrawHeight * 1.4f, TIME_PREEMPT - 150, Easing.OutQuint)
.Then()
.ScaleTo(1, 500, Easing.OutQuint);
}
protected override void UpdateCurrentState(ArmedState state)
{
Delay(spinner.Duration, true);
FadeOut(160);
var sequence = this.Delay(spinner.Duration).FadeOut(160);
switch (state)
{
case ArmedState.Hit:
ScaleTo(Scale * 1.2f, 320, EasingTypes.Out);
Expire();
sequence.ScaleTo(Scale * 1.2f, 320, Easing.Out);
break;
case ArmedState.Miss:
ScaleTo(Scale * 0.8f, 320, EasingTypes.In);
Expire();
sequence.ScaleTo(Scale * 0.8f, 320, Easing.In);
break;
}
Expire();
}
}
}

View File

@ -106,8 +106,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
tracking = value;
follow.ScaleTo(tracking ? 2.8f : 1, 300, EasingTypes.OutQuint);
follow.FadeTo(tracking ? 0.2f : 0, 300, EasingTypes.OutQuint);
follow.ScaleTo(tracking ? 2.8f : 1, 300, Easing.OutQuint);
follow.FadeTo(tracking ? 0.2f : 0, 300, Easing.OutQuint);
}
}

View File

@ -37,8 +37,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
protected override void LoadComplete()
{
base.LoadComplete();
using (icon.BeginLoopedSequence())
icon.RotateTo(360, 1000);
icon.Spin(1000, RotationDirection.Clockwise);
}
public void UpdateProgress(double progress, int repeat)

View File

@ -4,7 +4,6 @@
using System;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Transforms;
using osu.Framework.Input;
using osu.Game.Graphics;
using OpenTK;
@ -127,13 +126,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
if (Complete && updateCompleteTick())
{
background.Flush(flushType: typeof(TransformAlpha));
background.FadeTo(tracking_alpha + 0.2f, 60, EasingTypes.OutExpo);
background.Delay(60);
background.FadeTo(tracking_alpha, 250, EasingTypes.OutQuint);
background.FinishTransforms(false, nameof(Alpha));
background
.FadeTo(tracking_alpha + 0.2f, 60, Easing.OutExpo)
.Then()
.FadeTo(tracking_alpha, 250, Easing.OutQuint);
}
RotateTo(currentRotation / 2, validAndTracking ? 500 : 1500, EasingTypes.OutExpo);
this.RotateTo(currentRotation / 2, validAndTracking ? 500 : 1500, Easing.OutExpo);
}
}
}

View File

@ -1,12 +1,12 @@
// 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.Game.Beatmaps;
using osu.Game.Rulesets.Objects;
using OpenTK;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types;
using OpenTK.Graphics;
using osu.Game.Database;
using osu.Game.Beatmaps.ControlPoints;
namespace osu.Game.Rulesets.Osu.Objects

View File

@ -6,9 +6,9 @@ using osu.Game.Rulesets.Objects.Types;
using System;
using System.Collections.Generic;
using osu.Game.Rulesets.Objects;
using osu.Game.Database;
using System.Linq;
using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
namespace osu.Game.Rulesets.Osu.Objects

View File

@ -1,8 +1,8 @@
// 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.Game.Beatmaps;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Database;
using osu.Game.Beatmaps.ControlPoints;
namespace osu.Game.Rulesets.Osu.Objects

View File

@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Osu.Replays
/// <summary>
/// What easing to use when moving between hitobjects
/// </summary>
private EasingTypes preferredEasing => DelayedMovements ? EasingTypes.InOutCubic : EasingTypes.Out;
private Easing preferredEasing => DelayedMovements ? Easing.InOutCubic : Easing.Out;
#endregion
@ -110,7 +110,7 @@ namespace osu.Game.Rulesets.Osu.Replays
{
// Default values for circles/sliders
Vector2 startPosition = h.StackedPosition;
EasingTypes easing = preferredEasing;
Easing easing = preferredEasing;
float spinnerDirection = -1;
// The startPosition for the slider should not be its .Position, but the point on the circle whose tangent crosses the current cursor position
@ -125,7 +125,7 @@ namespace osu.Game.Rulesets.Osu.Replays
if (spinCentreOffset.Length > SPIN_RADIUS)
{
// If moving in from the outside, don't ease out (default eases out). This means auto will "start" spinning immediately after moving into position.
easing = EasingTypes.In;
easing = Easing.In;
}
}
@ -190,7 +190,7 @@ namespace osu.Game.Rulesets.Osu.Replays
}
}
private void moveToHitObject(double targetTime, Vector2 targetPos, double hitObjectRadius, EasingTypes easing)
private void moveToHitObject(double targetTime, Vector2 targetPos, double hitObjectRadius, Easing easing)
{
ReplayFrame lastFrame = Frames[Frames.Count - 1];

View File

@ -8,7 +8,6 @@ using osu.Game.Rulesets.Taiko.Objects;
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Game.Database;
using osu.Game.IO.Serialization;
using osu.Game.Audio;
using osu.Game.Rulesets.Beatmaps;

View File

@ -64,11 +64,10 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
protected override void LoadComplete()
{
base.LoadComplete();
Delay(BarLine.StartTime - Time.Current);
FadeOut(base_fadeout_time * BarLine.ScrollTime / 1000);
this.Delay(BarLine.StartTime - Time.Current).FadeOut(base_fadeout_time * BarLine.ScrollTime / 1000);
}
private void updateScrollPosition(double time) => MoveToX((float)((BarLine.StartTime - time) / BarLine.ScrollTime));
private void updateScrollPosition(double time) => this.MoveToX((float)((BarLine.StartTime - time) / BarLine.ScrollTime));
protected override void Update()
{

View File

@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
switch (state)
{
case ArmedState.Hit:
Content.ScaleTo(0, 100, EasingTypes.OutQuint);
Content.ScaleTo(0, 100, Easing.OutQuint);
break;
}
}

View File

@ -65,45 +65,42 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
protected override void UpdateState(ArmedState state)
{
Delay(HitObject.StartTime - Time.Current + Judgement.TimeOffset, true);
var circlePiece = MainPiece as CirclePiece;
circlePiece?.FlashBox.FinishTransforms();
circlePiece?.FlashBox.Flush();
switch (State)
using (BeginDelayedSequence(HitObject.StartTime - Time.Current + Judgement.TimeOffset, true))
{
case ArmedState.Idle:
Delay(HitObject.HitWindowMiss);
break;
case ArmedState.Miss:
FadeOut(100);
break;
case ArmedState.Hit:
FadeOut(600);
switch (State)
{
case ArmedState.Idle:
this.Delay(HitObject.HitWindowMiss).Expire();
break;
case ArmedState.Miss:
this.FadeOut(100)
.Expire();
break;
case ArmedState.Hit:
var flash = circlePiece?.FlashBox;
if (flash != null)
{
flash.FadeTo(0.9f);
flash.FadeOut(300);
}
var flash = circlePiece?.FlashBox;
if (flash != null)
{
flash.FadeTo(0.9f);
flash.FadeOut(300);
}
const float gravity_time = 300;
const float gravity_travel_height = 200;
Content.ScaleTo(0.8f, gravity_time * 2, Easing.OutQuad);
FadeOut(800);
this.FadeOut(800)
.MoveToY(-gravity_travel_height, gravity_time, Easing.Out)
.Then()
.MoveToY(gravity_travel_height * 2, gravity_time * 2, Easing.In);
const float gravity_time = 300;
const float gravity_travel_height = 200;
Content.ScaleTo(0.8f, gravity_time * 2, EasingTypes.OutQuad);
MoveToY(-gravity_travel_height, gravity_time, EasingTypes.Out);
Delay(gravity_time, true);
MoveToY(gravity_travel_height * 2, gravity_time * 2, EasingTypes.In);
break;
Expire();
break;
}
}
Expire();
}
}
}

View File

@ -147,13 +147,14 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
var completion = (float)userHits / HitObject.RequiredHits;
expandingRing.FadeTo(expandingRing.Alpha + MathHelper.Clamp(completion / 16, 0.1f, 0.6f), 50);
using (expandingRing.BeginDelayedSequence(50))
expandingRing.FadeTo(completion / 8, 2000, EasingTypes.OutQuint);
expandingRing
.FadeTo(expandingRing.Alpha + MathHelper.Clamp(completion / 16, 0.1f, 0.6f), 50)
.Then()
.FadeTo(completion / 8, 2000, Easing.OutQuint);
symbol.RotateTo((float)(completion * HitObject.Duration / 8), 4000, EasingTypes.OutQuint);
symbol.RotateTo((float)(completion * HitObject.Duration / 8), 4000, Easing.OutQuint);
expandingRing.ScaleTo(1f + Math.Min(target_ring_scale - 1f, (target_ring_scale - 1f) * completion * 1.3f), 260, EasingTypes.OutQuint);
expandingRing.ScaleTo(1f + Math.Min(target_ring_scale - 1f, (target_ring_scale - 1f) * completion * 1.3f), 260, Easing.OutQuint);
if (userHits == HitObject.RequiredHits)
{
@ -180,26 +181,21 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
protected override void UpdateState(ArmedState state)
{
const float preempt = 100;
Delay(HitObject.StartTime - Time.Current - preempt, true);
targetRing.ScaleTo(target_ring_scale, preempt * 4, EasingTypes.OutQuint);
Delay(preempt, true);
Delay(Judgement.TimeOffset + HitObject.Duration, true);
const float out_transition_time = 300;
double untilStartTime = HitObject.StartTime - Time.Current;
double untilJudgement = untilStartTime + Judgement.TimeOffset + HitObject.Duration;
targetRing.Delay(untilStartTime - preempt).ScaleTo(target_ring_scale, preempt * 4, Easing.OutQuint);
this.Delay(untilJudgement).FadeOut(out_transition_time, Easing.Out);
switch (state)
{
case ArmedState.Hit:
bodyContainer.ScaleTo(1.4f, out_transition_time);
bodyContainer.Delay(untilJudgement).ScaleTo(1.4f, out_transition_time);
break;
}
FadeOut(out_transition_time, EasingTypes.Out);
Expire();
}

View File

@ -166,9 +166,10 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces
double duration = timingPoint.BeatLength * 2;
background.FadeEdgeEffectTo(1, pre_beat_transition_time, EasingTypes.OutQuint);
using (background.BeginDelayedSequence(pre_beat_transition_time))
background.FadeEdgeEffectTo(edge_alpha_kiai, duration, EasingTypes.OutQuint);
background
.FadeEdgeEffectTo(1, pre_beat_transition_time, Easing.OutQuint)
.Then()
.FadeEdgeEffectTo(edge_alpha_kiai, duration, Easing.OutQuint);
}
}
}

View File

@ -5,8 +5,8 @@ using osu.Game.Rulesets.Objects.Types;
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Game.Database;
using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
namespace osu.Game.Rulesets.Taiko.Objects

View File

@ -1,8 +1,8 @@
// 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.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Database;
namespace osu.Game.Rulesets.Taiko.Objects
{

View File

@ -1,8 +1,8 @@
// 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.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Database;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Taiko.UI;

View File

@ -3,7 +3,6 @@
using System;
using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Judgements;

View File

@ -6,6 +6,7 @@ using osu.Game.Rulesets.Objects.Drawables;
using osu.Framework.Allocation;
using osu.Game.Graphics;
using osu.Game.Rulesets.Judgements;
using osu.Framework.Graphics;
namespace osu.Game.Rulesets.Taiko.UI
{
@ -47,7 +48,7 @@ namespace osu.Game.Rulesets.Taiko.UI
switch (Judgement.Result)
{
case HitResult.Hit:
MoveToY(-100, 500);
this.MoveToY(-100, 500);
break;
}

View File

@ -62,8 +62,8 @@ namespace osu.Game.Rulesets.Taiko.UI
{
base.LoadComplete();
ScaleTo(3f, 1000, EasingTypes.OutQuint);
FadeOut(500);
this.ScaleTo(3f, 1000, Easing.OutQuint);
this.FadeOut(500);
Expire();
}
@ -73,7 +73,7 @@ namespace osu.Game.Rulesets.Taiko.UI
/// </summary>
public void VisualiseSecondHit()
{
ResizeTo(new Vector2(TaikoPlayfield.HIT_TARGET_OFFSET + TaikoHitObject.DEFAULT_STRONG_CIRCLE_DIAMETER), 50);
this.ResizeTo(new Vector2(TaikoPlayfield.HIT_TARGET_OFFSET + TaikoHitObject.DEFAULT_STRONG_CIRCLE_DIAMETER), 50);
}
}
}

View File

@ -149,15 +149,17 @@ namespace osu.Game.Rulesets.Taiko.UI
const float down_time = 40;
const float up_time = 1000;
back.ScaleTo(target.Scale.X - scale_amount, down_time, EasingTypes.OutQuint);
back.Delay(down_time);
back.ScaleTo(1, up_time, EasingTypes.OutQuint);
back.ScaleTo(target.Scale.X - scale_amount, down_time, Easing.OutQuint)
.Then()
.ScaleTo(1, up_time, Easing.OutQuint);
target.ScaleTo(target.Scale.X - scale_amount, down_time, EasingTypes.OutQuint);
target.FadeTo(Math.Min(target.Alpha + alpha_amount, 1), down_time, EasingTypes.OutQuint);
target.Delay(down_time);
target.ScaleTo(1, up_time, EasingTypes.OutQuint);
target.FadeOut(up_time, EasingTypes.OutQuint);
target.Animate(
t => t.ScaleTo(target.Scale.X - scale_amount, down_time, Easing.OutQuint),
t => t.FadeTo(Math.Min(target.Alpha + alpha_amount, 1), down_time, Easing.OutQuint)
).Then(
t => t.ScaleTo(1, up_time, Easing.OutQuint),
t => t.FadeOut(up_time, Easing.OutQuint)
);
}
return false;

View File

@ -59,8 +59,8 @@ namespace osu.Game.Rulesets.Taiko.UI
{
base.LoadComplete();
ScaleTo(new Vector2(1, 3f), 500, EasingTypes.OutQuint);
FadeOut(250);
this.ScaleTo(new Vector2(1, 3f), 500, Easing.OutQuint);
this.FadeOut(250);
Expire();
}

View File

@ -158,7 +158,7 @@ namespace osu.Game.Rulesets.Taiko.UI
Anchor = Anchor.TopRight,
RelativeSizeAxes = Axes.Y,
Width = 10,
ColourInfo = Framework.Graphics.Colour.ColourInfo.GradientHorizontal(Color4.Black.Opacity(0.6f), Color4.Black.Opacity(0)),
Colour = Framework.Graphics.Colour.ColourInfo.GradientHorizontal(Color4.Black.Opacity(0.6f), Color4.Black.Opacity(0)),
},
}
},

View File

@ -16,12 +16,6 @@ namespace osu.Game.Tests.Beatmaps.Formats
[TestFixture]
public class OsuLegacyDecoderTest
{
[OneTimeSetUpAttribute]
public void SetUp()
{
OsuLegacyDecoder.Register();
}
[Test]
public void TestDecodeMetadata()
{

View File

@ -10,8 +10,10 @@ using System.Threading.Tasks;
using NUnit.Framework;
using osu.Framework.Desktop.Platform;
using osu.Framework.Platform;
using osu.Game.Database;
using osu.Game.IPC;
using osu.Framework.Allocation;
using osu.Game.Beatmaps;
using osu.Game.Rulesets;
namespace osu.Game.Tests.Beatmaps.IO
{
@ -26,15 +28,15 @@ namespace osu.Game.Tests.Beatmaps.IO
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
using (HeadlessGameHost host = new HeadlessGameHost())
{
loadOsu(host);
var osu = loadOsu(host);
var temp = prepareTempCopy(osz_path);
Assert.IsTrue(File.Exists(temp));
host.Dependencies.Get<BeatmapDatabase>().Import(temp);
osu.Dependencies.Get<BeatmapManager>().Import(temp);
ensureLoaded(host);
ensureLoaded(osu);
Assert.IsFalse(File.Exists(temp));
}
@ -49,7 +51,7 @@ namespace osu.Game.Tests.Beatmaps.IO
Assert.IsTrue(host.IsPrimaryInstance);
Assert.IsTrue(!client.IsPrimaryInstance);
loadOsu(host);
var osu = loadOsu(host);
var temp = prepareTempCopy(osz_path);
@ -59,7 +61,7 @@ namespace osu.Game.Tests.Beatmaps.IO
if (!importer.ImportAsync(temp).Wait(10000))
Assert.Fail(@"IPC took too long to send");
ensureLoaded(host);
ensureLoaded(osu);
Assert.IsFalse(File.Exists(temp));
}
@ -71,16 +73,16 @@ namespace osu.Game.Tests.Beatmaps.IO
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
using (HeadlessGameHost host = new HeadlessGameHost())
{
loadOsu(host);
var osu = loadOsu(host);
var temp = prepareTempCopy(osz_path);
Assert.IsTrue(File.Exists(temp), "Temporary file copy never substantiated");
using (File.OpenRead(temp))
host.Dependencies.Get<BeatmapDatabase>().Import(temp);
osu.Dependencies.Get<BeatmapManager>().Import(temp);
ensureLoaded(host);
ensureLoaded(osu);
File.Delete(temp);
@ -103,20 +105,21 @@ namespace osu.Game.Tests.Beatmaps.IO
Thread.Sleep(1);
//reset beatmap database (sqlite and storage backing)
host.Dependencies.Get<RulesetDatabase>().Reset();
host.Dependencies.Get<BeatmapDatabase>().Reset();
osu.Dependencies.Get<RulesetStore>().Reset();
osu.Dependencies.Get<BeatmapManager>().Reset();
return osu;
}
private void ensureLoaded(GameHost host, int timeout = 60000)
private void ensureLoaded(OsuGameBase osu, int timeout = 60000)
{
IEnumerable<BeatmapSetInfo> resultSets = null;
var store = osu.Dependencies.Get<BeatmapManager>();
Action waitAction = () =>
{
while (!(resultSets = host.Dependencies.Get<BeatmapDatabase>()
.Query<BeatmapSetInfo>().Where(s => s.OnlineBeatmapSetID == 241526)).Any())
while (!(resultSets = store.QueryBeatmapSets(s => s.OnlineBeatmapSetID == 241526)).Any())
Thread.Sleep(50);
};
@ -132,15 +135,14 @@ namespace osu.Game.Tests.Beatmaps.IO
//if we don't re-check here, the set will be inserted but the beatmaps won't be present yet.
waitAction = () =>
{
while ((resultBeatmaps = host.Dependencies.Get<BeatmapDatabase>()
.GetAllWithChildren<BeatmapInfo>(s => s.OnlineBeatmapSetID == 241526 && s.BaseDifficultyID > 0)).Count() != 12)
while ((resultBeatmaps = store.QueryBeatmaps(s => s.OnlineBeatmapSetID == 241526 && s.BaseDifficultyID > 0)).Count() != 12)
Thread.Sleep(50);
};
Assert.IsTrue(waitAction.BeginInvoke(null, null).AsyncWaitHandle.WaitOne(timeout),
@"Beatmaps did not import to the database in allocated time");
var set = host.Dependencies.Get<BeatmapDatabase>().GetChildren(resultSets.First());
var set = store.QueryBeatmapSets(s => s.OnlineBeatmapSetID == 241526).First();
Assert.IsTrue(set.Beatmaps.Count == resultBeatmaps.Count(),
$@"Incorrect database beatmap count post-import ({resultBeatmaps.Count()} but should be {set.Beatmaps.Count}).");
@ -150,16 +152,16 @@ namespace osu.Game.Tests.Beatmaps.IO
Assert.IsTrue(set.Beatmaps.Count > 0);
var beatmap = host.Dependencies.Get<BeatmapDatabase>().GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 0))?.Beatmap;
var beatmap = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 0))?.Beatmap;
Assert.IsTrue(beatmap?.HitObjects.Count > 0);
beatmap = host.Dependencies.Get<BeatmapDatabase>().GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 1))?.Beatmap;
beatmap = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 1))?.Beatmap;
Assert.IsTrue(beatmap?.HitObjects.Count > 0);
beatmap = host.Dependencies.Get<BeatmapDatabase>().GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 2))?.Beatmap;
beatmap = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 2))?.Beatmap;
Assert.IsTrue(beatmap?.HitObjects.Count > 0);
beatmap = host.Dependencies.Get<BeatmapDatabase>().GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 3))?.Beatmap;
beatmap = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 3))?.Beatmap;
Assert.IsTrue(beatmap?.HitObjects.Count > 0);
}
}

View File

@ -2,23 +2,18 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.IO;
using System.Linq;
using NUnit.Framework;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.IO;
using osu.Game.Tests.Resources;
using osu.Game.Beatmaps.Formats;
using osu.Game.Database;
namespace osu.Game.Tests.Beatmaps.IO
{
[TestFixture]
public class OszArchiveReaderTest
{
[OneTimeSetUpAttribute]
public void SetUp()
{
OszArchiveReader.Register();
}
[Test]
public void TestReadBeatmaps()
{
@ -40,7 +35,7 @@ namespace osu.Game.Tests.Beatmaps.IO
"Soleily - Renatus (MMzz) [Muzukashii].osu",
"Soleily - Renatus (MMzz) [Oni].osu"
};
var maps = reader.BeatmapFilenames;
var maps = reader.Filenames.ToArray();
foreach (var map in expected)
Assert.Contains(map, maps);
}

View File

@ -3,7 +3,6 @@
using OpenTK.Graphics;
using osu.Game.Beatmaps.Timing;
using osu.Game.Database;
using osu.Game.Rulesets.Objects;
using System.Collections.Generic;
using System.Linq;

View File

@ -3,7 +3,7 @@
using SQLite.Net.Attributes;
namespace osu.Game.Database
namespace osu.Game.Beatmaps
{
public class BeatmapDifficulty
{

View File

@ -1,14 +1,15 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using Newtonsoft.Json;
using osu.Game.IO.Serialization;
using SQLite.Net.Attributes;
using SQLiteNetExtensions.Attributes;
using System;
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.Database
namespace osu.Game.Beatmaps
{
public class BeatmapInfo : IEquatable<BeatmapInfo>, IJsonSerializable
{
@ -60,7 +61,7 @@ namespace osu.Game.Database
[ForeignKey(typeof(RulesetInfo))]
public int RulesetID { get; set; }
[OneToOne(CascadeOperations = CascadeOperation.All)]
[OneToOne(CascadeOperations = CascadeOperation.CascadeRead)]
public RulesetInfo Ruleset { get; set; }
public bool LetterboxInBreaks { get; set; }
@ -94,11 +95,11 @@ namespace osu.Game.Database
}
public bool AudioEquals(BeatmapInfo other) => other != null && BeatmapSet != null && other.BeatmapSet != null &&
BeatmapSet.Path == other.BeatmapSet.Path &&
BeatmapSet.Hash == other.BeatmapSet.Hash &&
(Metadata ?? BeatmapSet.Metadata).AudioFile == (other.Metadata ?? other.BeatmapSet.Metadata).AudioFile;
public bool BackgroundEquals(BeatmapInfo other) => other != null && BeatmapSet != null && other.BeatmapSet != null &&
BeatmapSet.Path == other.BeatmapSet.Path &&
BeatmapSet.Hash == other.BeatmapSet.Hash &&
(Metadata ?? BeatmapSet.Metadata).BackgroundFile == (other.Metadata ?? other.BeatmapSet.Metadata).BackgroundFile;
}
}

View File

@ -0,0 +1,499 @@
// 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.IO;
using System.Linq;
using System.Linq.Expressions;
using Ionic.Zip;
using osu.Framework.Audio.Track;
using osu.Framework.Extensions;
using osu.Framework.Graphics.Textures;
using osu.Framework.IO.Stores;
using osu.Framework.Logging;
using osu.Framework.Platform;
using osu.Game.Beatmaps.Formats;
using osu.Game.Beatmaps.IO;
using osu.Game.IO;
using osu.Game.IPC;
using osu.Game.Overlays.Notifications;
using osu.Game.Rulesets;
using SQLite.Net;
namespace osu.Game.Beatmaps
{
/// <summary>
/// Handles the storage and retrieval of Beatmaps/WorkingBeatmaps.
/// </summary>
public class BeatmapManager
{
/// <summary>
/// Fired when a new <see cref="BeatmapSetInfo"/> becomes available in the database.
/// </summary>
public event Action<BeatmapSetInfo> BeatmapSetAdded;
/// <summary>
/// Fired when a <see cref="BeatmapSetInfo"/> is removed from the database.
/// </summary>
public event Action<BeatmapSetInfo> BeatmapSetRemoved;
/// <summary>
/// A default representation of a WorkingBeatmap to use when no beatmap is available.
/// </summary>
public WorkingBeatmap DefaultBeatmap { private get; set; }
private readonly Storage storage;
private readonly FileStore files;
private readonly RulesetStore rulesets;
private readonly BeatmapStore beatmaps;
// ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised)
private BeatmapIPCChannel ipc;
/// <summary>
/// Set an endpoint for notifications to be posted to.
/// </summary>
public Action<Notification> PostNotification { private get; set; }
public BeatmapManager(Storage storage, FileStore files, SQLiteConnection connection, RulesetStore rulesets, IIpcHost importHost = null)
{
beatmaps = new BeatmapStore(connection);
beatmaps.BeatmapSetAdded += s => BeatmapSetAdded?.Invoke(s);
beatmaps.BeatmapSetRemoved += s => BeatmapSetRemoved?.Invoke(s);
this.storage = storage;
this.files = files;
this.rulesets = rulesets;
if (importHost != null)
ipc = new BeatmapIPCChannel(importHost, this);
}
/// <summary>
/// Import one or more <see cref="BeatmapSetInfo"/> from filesystem <paramref name="paths"/>.
/// This will post a notification tracking import progress.
/// </summary>
/// <param name="paths">One or more beatmap locations on disk.</param>
public void Import(params string[] paths)
{
var notification = new ProgressNotification
{
Text = "Beatmap import is initialising...",
Progress = 0,
State = ProgressNotificationState.Active,
};
PostNotification?.Invoke(notification);
int i = 0;
foreach (string path in paths)
{
if (notification.State == ProgressNotificationState.Cancelled)
// user requested abort
return;
try
{
notification.Text = $"Importing ({i} of {paths.Length})\n{Path.GetFileName(path)}";
using (ArchiveReader reader = getReaderFrom(path))
Import(reader);
notification.Progress = (float)++i / paths.Length;
// We may or may not want to delete the file depending on where it is stored.
// e.g. reconstructing/repairing database with beatmaps from default storage.
// Also, not always a single file, i.e. for LegacyFilesystemReader
// TODO: Add a check to prevent files from storage to be deleted.
try
{
if (File.Exists(path))
File.Delete(path);
}
catch (Exception e)
{
Logger.Error(e, $@"Could not delete original file after import ({Path.GetFileName(path)})");
}
}
catch (Exception e)
{
e = e.InnerException ?? e;
Logger.Error(e, @"Could not import beatmap set");
}
}
notification.State = ProgressNotificationState.Completed;
}
private readonly object importLock = new object();
/// <summary>
/// Import a beatmap from an <see cref="ArchiveReader"/>.
/// </summary>
/// <param name="archiveReader">The beatmap to be imported.</param>
public BeatmapSetInfo Import(ArchiveReader archiveReader)
{
// let's only allow one concurrent import at a time for now.
lock (importLock)
{
BeatmapSetInfo set = importToStorage(archiveReader);
Import(set);
return set;
}
}
/// <summary>
/// Import a beatmap from a <see cref="BeatmapSetInfo"/>.
/// </summary>
/// <param name="beatmapSetInfo">The beatmap to be imported.</param>
public void Import(BeatmapSetInfo beatmapSetInfo)
{
// If we have an ID then we already exist in the database.
if (beatmapSetInfo.ID != 0) return;
lock (beatmaps)
beatmaps.Add(beatmapSetInfo);
}
/// <summary>
/// Delete a beatmap from the manager.
/// Is a no-op for already deleted beatmaps.
/// </summary>
/// <param name="beatmapSet">The beatmap to delete.</param>
public void Delete(BeatmapSetInfo beatmapSet)
{
lock (beatmaps)
if (!beatmaps.Delete(beatmapSet)) return;
if (!beatmapSet.Protected)
files.Dereference(beatmapSet.Files.Select(f => f.FileInfo));
}
/// <summary>
/// Returns a <see cref="BeatmapSetInfo"/> to a usable state if it has previously been deleted but not yet purged.
/// Is a no-op for already usable beatmaps.
/// </summary>
/// <param name="beatmapSet">The beatmap to restore.</param>
public void Undelete(BeatmapSetInfo beatmapSet)
{
lock (beatmaps)
if (!beatmaps.Undelete(beatmapSet)) return;
if (!beatmapSet.Protected)
files.Reference(beatmapSet.Files.Select(f => f.FileInfo));
}
/// <summary>
/// Retrieve a <see cref="WorkingBeatmap"/> instance for the provided <see cref="BeatmapInfo"/>
/// </summary>
/// <param name="beatmapInfo">The beatmap to lookup.</param>
/// <param name="previous">The currently loaded <see cref="WorkingBeatmap"/>. Allows for optimisation where elements are shared with the new beatmap.</param>
/// <returns>A <see cref="WorkingBeatmap"/> instance correlating to the provided <see cref="BeatmapInfo"/>.</returns>
public WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo, WorkingBeatmap previous = null)
{
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.");
if (beatmapInfo.Metadata == null)
beatmapInfo.Metadata = beatmapInfo.BeatmapSet.Metadata;
WorkingBeatmap working = new BeatmapManagerWorkingBeatmap(files.Store, beatmapInfo);
previous?.TransferTo(working);
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;
}
}
/// <summary>
/// Perform a lookup query on available <see cref="BeatmapSetInfo"/>s.
/// </summary>
/// <param name="query">The query.</param>
/// <returns>Results from the provided query.</returns>
public List<BeatmapSetInfo> QueryBeatmapSets(Expression<Func<BeatmapSetInfo, bool>> query)
{
lock (beatmaps) return beatmaps.QueryAndPopulate(query);
}
/// <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)
{
lock (beatmaps)
{
BeatmapInfo set = beatmaps.Query<BeatmapInfo>().FirstOrDefault(query);
if (set != null)
beatmaps.Populate(set);
return set;
}
}
/// <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);
}
/// <summary>
/// Creates an <see cref="ArchiveReader"/> from a valid storage path.
/// </summary>
/// <param name="path">A file or folder path resolving the beatmap content.</param>
/// <returns>A reader giving access to the beatmap's content.</returns>
private ArchiveReader getReaderFrom(string path)
{
if (ZipFile.IsZipFile(path))
return new OszArchiveReader(storage.GetStream(path));
else
return new LegacyFilesystemReader(path);
}
/// <summary>
/// Import a beamap into our local <see cref="FileStore"/> storage.
/// If the beatmap is already imported, the existing instance will be returned.
/// </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)
{
// for now, concatenate all .osu files in the set to create a unique hash.
MemoryStream hashable = new MemoryStream();
foreach (string file in reader.Filenames.Where(f => f.EndsWith(".osu")))
using (Stream s = reader.GetStream(file))
s.CopyTo(hashable);
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();
if (beatmapSet != null)
{
Undelete(beatmapSet);
return beatmapSet;
}
List<BeatmapSetFileInfo> fileInfos = new List<BeatmapSetFileInfo>();
// import files to manager
foreach (string file in reader.Filenames)
using (Stream s = reader.GetStream(file))
fileInfos.Add(new BeatmapSetFileInfo
{
Filename = file,
FileInfo = files.Add(s)
});
BeatmapMetadata metadata;
using (var stream = new StreamReader(reader.GetStream(reader.Filenames.First(f => f.EndsWith(".osu")))))
metadata = BeatmapDecoder.GetDecoder(stream).Decode(stream).Metadata;
beatmapSet = new BeatmapSetInfo
{
OnlineBeatmapSetID = metadata.OnlineBeatmapSetID,
Beatmaps = new List<BeatmapInfo>(),
Hash = hash,
Files = fileInfos,
Metadata = metadata
};
var mapNames = reader.Filenames.Where(f => f.EndsWith(".osu"));
foreach (var name in mapNames)
{
using (var raw = reader.GetStream(name))
using (var ms = new MemoryStream()) //we need a memory stream so we can seek and shit
using (var sr = new StreamReader(ms))
{
raw.CopyTo(ms);
ms.Position = 0;
var decoder = BeatmapDecoder.GetDecoder(sr);
Beatmap beatmap = decoder.Decode(sr);
beatmap.BeatmapInfo.Path = name;
beatmap.BeatmapInfo.Hash = ms.ComputeSHA2Hash();
// TODO: Diff beatmap metadata with set metadata and leave it here if necessary
beatmap.BeatmapInfo.Metadata = null;
// 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;
beatmapSet.Beatmaps.Add(beatmap.BeatmapInfo);
}
}
return beatmapSet;
}
/// <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)
{
lock (beatmaps)
{
if (populate)
return beatmaps.QueryAndPopulate<BeatmapSetInfo>(b => !b.DeletePending).ToList();
else
return beatmaps.Query<BeatmapSetInfo>(b => !b.DeletePending).ToList();
}
}
protected class BeatmapManagerWorkingBeatmap : WorkingBeatmap
{
private readonly IResourceStore<byte[]> store;
public BeatmapManagerWorkingBeatmap(IResourceStore<byte[]> store, BeatmapInfo beatmapInfo)
: base(beatmapInfo)
{
this.store = store;
}
protected override Beatmap GetBeatmap()
{
try
{
Beatmap beatmap;
BeatmapDecoder decoder;
using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo.Path))))
{
decoder = BeatmapDecoder.GetDecoder(stream);
beatmap = decoder.Decode(stream);
}
if (beatmap == null || BeatmapSetInfo.StoryboardFile == null)
return beatmap;
using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapSetInfo.StoryboardFile))))
decoder.Decode(stream, beatmap);
return beatmap;
}
catch { return null; }
}
private string getPathForFile(string filename) => BeatmapSetInfo.Files.First(f => f.Filename == filename).FileInfo.StoragePath;
protected override Texture GetBackground()
{
if (Metadata?.BackgroundFile == null)
return null;
try
{
return new TextureStore(new RawTextureLoaderStore(store), false).Get(getPathForFile(Metadata.BackgroundFile));
}
catch { return null; }
}
protected override Track GetTrack()
{
try
{
var trackData = store.GetStream(getPathForFile(Metadata.AudioFile));
return trackData == null ? null : new TrackBass(trackData);
}
catch { return new TrackVirtual(); }
}
}
public void ImportFromStable()
{
string stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!", "Songs");
if (!Directory.Exists(stableInstallPath))
stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".osu", "Songs");
if (!Directory.Exists(stableInstallPath))
{
Logger.Log("Couldn't find an osu!stable installation!", LoggingTarget.Information, LogLevel.Error);
return;
}
Import(Directory.GetDirectories(stableInstallPath));
}
public void DeleteAll()
{
var maps = GetAllUsableBeatmapSets().ToArray();
if (maps.Length == 0) return;
var notification = new ProgressNotification
{
Progress = 0,
State = ProgressNotificationState.Active,
};
PostNotification?.Invoke(notification);
int i = 0;
foreach (var b in maps)
{
if (notification.State == ProgressNotificationState.Cancelled)
// user requested abort
return;
notification.Text = $"Deleting ({i} of {maps.Length})";
notification.Progress = (float)++i / maps.Length;
Delete(b);
}
notification.State = ProgressNotificationState.Completed;
}
}
}

View File

@ -5,7 +5,7 @@ using System.Linq;
using Newtonsoft.Json;
using SQLite.Net.Attributes;
namespace osu.Game.Database
namespace osu.Game.Beatmaps
{
public class BeatmapMetadata
{

View File

@ -4,7 +4,7 @@
using System.Collections.Generic;
using Newtonsoft.Json;
namespace osu.Game.Database
namespace osu.Game.Beatmaps
{
/// <summary>
/// Beatmap metrics based on acculumated online data from community plays.

View File

@ -3,7 +3,7 @@
using Newtonsoft.Json;
namespace osu.Game.Database
namespace osu.Game.Beatmaps
{
/// <summary>
/// Beatmap info retrieved for previewing locally without having the beatmap downloaded.

View File

@ -0,0 +1,27 @@
// 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.Game.IO;
using SQLite.Net.Attributes;
using SQLiteNetExtensions.Attributes;
namespace osu.Game.Beatmaps
{
public class BeatmapSetFileInfo
{
[PrimaryKey, AutoIncrement]
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]
public string Filename { get; set; }
}
}

View File

@ -6,7 +6,7 @@ using System.Linq;
using SQLite.Net.Attributes;
using SQLiteNetExtensions.Attributes;
namespace osu.Game.Database
namespace osu.Game.Beatmaps
{
public class BeatmapSetInfo
{
@ -34,8 +34,11 @@ namespace osu.Game.Database
public string Hash { get; set; }
public string Path { get; set; }
public string StoryboardFile => Files.FirstOrDefault(f => f.Filename.EndsWith(".osb"))?.Filename;
public string StoryboardFile { get; set; }
[OneToMany(CascadeOperations = CascadeOperation.All)]
public List<BeatmapSetFileInfo> Files { get; set; }
public bool Protected { get; set; }
}
}

View File

@ -3,7 +3,7 @@
using Newtonsoft.Json;
namespace osu.Game.Database
namespace osu.Game.Beatmaps
{
/// <summary>
/// Beatmap set info retrieved for previewing locally without having the set downloaded.

View File

@ -0,0 +1,152 @@
// 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 osu.Framework.Logging;
using osu.Game.Database;
using SQLite.Net;
using SQLiteNetExtensions.Extensions;
namespace osu.Game.Beatmaps
{
/// <summary>
/// Handles the storage and retrieval of Beatmaps/BeatmapSets to the database backing
/// </summary>
public class BeatmapStore : DatabaseBackedStore
{
public event Action<BeatmapSetInfo> BeatmapSetAdded;
public event Action<BeatmapSetInfo> BeatmapSetRemoved;
/// <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 => 2;
public BeatmapStore(SQLiteConnection connection)
: base(connection)
{
}
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>();
}
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;
}
}
}
/// <summary>
/// Add a <see cref="BeatmapSetInfo"/> to the database.
/// </summary>
/// <param name="beatmapSet">The beatmap to add.</param>
public void Add(BeatmapSetInfo beatmapSet)
{
Connection.RunInTransaction(() =>
{
Connection.InsertOrReplaceWithChildren(beatmapSet, true);
});
BeatmapSetAdded?.Invoke(beatmapSet);
}
/// <summary>
/// Delete a <see cref="BeatmapSetInfo"/> to the database.
/// </summary>
/// <param name="beatmapSet">The beatmap to delete.</param>
/// <returns>Whether the beatmap's <see cref="BeatmapSetInfo.DeletePending"/> was changed.</returns>
public bool Delete(BeatmapSetInfo beatmapSet)
{
if (beatmapSet.DeletePending) return false;
beatmapSet.DeletePending = true;
Connection.Update(beatmapSet);
BeatmapSetRemoved?.Invoke(beatmapSet);
return true;
}
/// <summary>
/// Restore a previously deleted <see cref="BeatmapSetInfo"/>.
/// </summary>
/// <param name="beatmapSet">The beatmap to restore.</param>
/// <returns>Whether the beatmap's <see cref="BeatmapSetInfo.DeletePending"/> was changed.</returns>
public bool Undelete(BeatmapSetInfo beatmapSet)
{
if (!beatmapSet.DeletePending) return false;
beatmapSet.DeletePending = false;
Connection.Update(beatmapSet);
BeatmapSetAdded?.Invoke(beatmapSet);
return true;
}
private void cleanupPendingDeletions()
{
foreach (var b in QueryAndPopulate<BeatmapSetInfo>(b => b.DeletePending && !b.Protected))
{
try
{
// many-to-many join table entries are not automatically tidied.
Connection.Table<BeatmapSetFileInfo>().Delete(f => f.BeatmapSetInfoID == b.ID);
Connection.Delete(b, true);
}
catch (Exception e)
{
Logger.Error(e, $@"Could not delete beatmap {b}");
}
}
//this is required because sqlite migrations don't work, initially inserting nulls into this field.
//see https://github.com/praeclarum/sqlite-net/issues/326
Connection.Query<BeatmapSetInfo>("UPDATE BeatmapSetInfo SET DeletePending = 0 WHERE DeletePending IS NULL");
}
}
}

View File

@ -6,7 +6,6 @@ using System.Collections.Generic;
using System.Linq;
using osu.Framework;
using osu.Framework.Graphics;
using osu.Game.Database;
namespace osu.Game.Beatmaps.Drawables
{
@ -59,10 +58,10 @@ namespace osu.Game.Beatmaps.Drawables
}
}
public BeatmapGroup(BeatmapSetInfo beatmapSet, BeatmapDatabase database)
public BeatmapGroup(BeatmapSetInfo beatmapSet, BeatmapManager manager)
{
BeatmapSet = beatmapSet;
WorkingBeatmap beatmap = database.GetWorkingBeatmap(BeatmapSet.Beatmaps.FirstOrDefault());
WorkingBeatmap beatmap = manager.GetWorkingBeatmap(BeatmapSet.Beatmaps.FirstOrDefault());
Header = new BeatmapSetHeader(beatmap)
{

View File

@ -6,7 +6,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Game.Database;
using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds;
using osu.Game.Graphics.UserInterface;
@ -34,7 +33,7 @@ namespace osu.Game.Beatmaps.Drawables
GainedSelection?.Invoke(this);
background.ColourInfo = ColourInfo.GradientVertical(
background.Colour = ColourInfo.GradientVertical(
new Color4(20, 43, 51, 255),
new Color4(40, 86, 102, 255));

View File

@ -4,7 +4,6 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Game.Database;
namespace osu.Game.Beatmaps.Drawables
{

View File

@ -35,7 +35,7 @@ namespace osu.Game.Beatmaps.Drawables
new PanelBackground(beatmap)
{
RelativeSizeAxes = Axes.Both,
OnLoadComplete = d => d.FadeInFromZero(400, EasingTypes.Out),
OnLoadComplete = d => d.FadeInFromZero(400, Easing.Out),
}
)
{
@ -119,21 +119,21 @@ namespace osu.Game.Beatmaps.Drawables
new Box
{
RelativeSizeAxes = Axes.Both,
ColourInfo = ColourInfo.GradientHorizontal(
Colour = ColourInfo.GradientHorizontal(
Color4.Black, new Color4(0f, 0f, 0f, 0.9f)),
Width = 0.05f,
},
new Box
{
RelativeSizeAxes = Axes.Both,
ColourInfo = ColourInfo.GradientHorizontal(
Colour = ColourInfo.GradientHorizontal(
new Color4(0f, 0f, 0f, 0.9f), new Color4(0f, 0f, 0f, 0.1f)),
Width = 0.2f,
},
new Box
{
RelativeSizeAxes = Axes.Both,
ColourInfo = ColourInfo.GradientHorizontal(
Colour = ColourInfo.GradientHorizontal(
new Color4(0f, 0f, 0f, 0.1f), new Color4(0, 0, 0, 0)),
Width = 0.05f,
},

View File

@ -3,7 +3,6 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics.Containers;
using osu.Game.Database;
using osu.Game.Graphics;
using OpenTK.Graphics;

View File

@ -3,7 +3,6 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Database;
using osu.Game.Graphics;
using OpenTK;
using OpenTK.Graphics;

View File

@ -64,9 +64,9 @@ namespace osu.Game.Beatmaps.Drawables
}
if (state == PanelSelectedState.Hidden)
FadeOut(300, EasingTypes.OutQuint);
this.FadeOut(300, Easing.OutQuint);
else
FadeIn(250);
this.FadeIn(250);
}
private PanelSelectedState state = PanelSelectedState.NotSelected;

View File

@ -5,7 +5,6 @@ using System;
using System.Collections.Generic;
using osu.Framework.Audio.Track;
using osu.Framework.Graphics.Textures;
using osu.Game.Database;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;

View File

@ -5,7 +5,6 @@ using System;
using System.Collections.Generic;
using System.IO;
using osu.Game.Rulesets.Objects;
using osu.Game.Database;
namespace osu.Game.Beatmaps.Formats
{
@ -13,6 +12,11 @@ namespace osu.Game.Beatmaps.Formats
{
private static readonly Dictionary<string, Type> decoders = new Dictionary<string, Type>();
static BeatmapDecoder()
{
OsuLegacyDecoder.Register();
}
public static BeatmapDecoder GetDecoder(StreamReader stream)
{
string line = stream.ReadLine()?.Trim();

View File

@ -5,45 +5,11 @@ using System;
using System.Collections.Generic;
using System.IO;
using osu.Framework.IO.Stores;
using osu.Framework.Platform;
namespace osu.Game.Beatmaps.IO
{
public abstract class ArchiveReader : IDisposable, IResourceStore<byte[]>
{
private class Reader
{
public Func<Storage, string, bool> Test;
public Type Type;
}
private static readonly List<Reader> readers = new List<Reader>();
public static ArchiveReader GetReader(Storage storage, string path)
{
foreach (var reader in readers)
{
if (reader.Test(storage, path))
return (ArchiveReader)Activator.CreateInstance(reader.Type, storage.GetStream(path));
}
throw new IOException(@"Unknown file format");
}
protected static void AddReader<T>(Func<Storage, string, bool> test) where T : ArchiveReader
{
readers.Add(new Reader { Test = test, Type = typeof(T) });
}
/// <summary>
/// Gets a list of beatmap file names.
/// </summary>
public string[] BeatmapFilenames { get; protected set; }
/// <summary>
/// The storyboard filename. Null if no storyboard is present.
/// </summary>
public string StoryboardFilename { get; protected set; }
/// <summary>
/// Opens a stream for reading a specific file from this archive.
/// </summary>
@ -51,6 +17,8 @@ namespace osu.Game.Beatmaps.IO
public abstract void Dispose();
public abstract IEnumerable<string> Filenames { get; }
public virtual byte[] Get(string name)
{
using (Stream input = GetStream(name))

View File

@ -0,0 +1,33 @@
// 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.IO;
using System.Linq;
namespace osu.Game.Beatmaps.IO
{
/// <summary>
/// Reads an extracted legacy beatmap from disk.
/// </summary>
public class LegacyFilesystemReader : ArchiveReader
{
private readonly string path;
public LegacyFilesystemReader(string path)
{
this.path = path;
}
public override Stream GetStream(string name) => File.OpenRead(Path.Combine(path, name));
public override void Dispose()
{
// no-op
}
public override IEnumerable<string> Filenames => Directory.GetFiles(path).Select(Path.GetFileName).ToArray();
public override Stream GetUnderlyingStream() => null;
}
}

View File

@ -1,25 +1,15 @@
// 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.IO;
using System.Linq;
using Ionic.Zip;
using osu.Game.Beatmaps.Formats;
namespace osu.Game.Beatmaps.IO
{
public sealed class OszArchiveReader : ArchiveReader
{
public static void Register()
{
AddReader<OszArchiveReader>((storage, path) =>
{
using (var stream = storage.GetStream(path))
return stream != null && ZipFile.IsZipFile(stream, false);
});
OsuLegacyDecoder.Register();
}
private readonly Stream archiveStream;
private readonly ZipFile archive;
@ -27,13 +17,6 @@ namespace osu.Game.Beatmaps.IO
{
this.archiveStream = archiveStream;
archive = ZipFile.Read(archiveStream);
BeatmapFilenames = archive.Entries.Where(e => e.FileName.EndsWith(@".osu")).Select(e => e.FileName).ToArray();
if (BeatmapFilenames.Length == 0)
throw new FileNotFoundException(@"This directory contains no beatmaps");
StoryboardFilename = archive.Entries.Where(e => e.FileName.EndsWith(@".osb")).Select(e => e.FileName).FirstOrDefault();
}
public override Stream GetStream(string name)
@ -41,7 +24,16 @@ namespace osu.Game.Beatmaps.IO
ZipEntry entry = archive.Entries.SingleOrDefault(e => e.FileName == name);
if (entry == null)
throw new FileNotFoundException();
return entry.OpenReader();
// allow seeking
MemoryStream copy = new MemoryStream();
using (Stream s = entry.OpenReader())
s.CopyTo(copy);
copy.Position = 0;
return copy;
}
public override void Dispose()
@ -50,6 +42,8 @@ namespace osu.Game.Beatmaps.IO
archiveStream.Dispose();
}
public override IEnumerable<string> Filenames => archive.Entries.Select(e => e.FileName).ToArray();
public override Stream GetUnderlyingStream() => archiveStream;
}
}

View File

@ -3,7 +3,7 @@
using System.ComponentModel;
namespace osu.Game.Database
namespace osu.Game.Beatmaps
{
public enum RankStatus
{

View File

@ -4,7 +4,6 @@
using osu.Framework.Audio.Track;
using osu.Framework.Configuration;
using osu.Framework.Graphics.Textures;
using osu.Game.Database;
using osu.Game.Rulesets.Mods;
using System;
using System.Collections.Generic;
@ -22,14 +21,11 @@ namespace osu.Game.Beatmaps
public readonly Bindable<IEnumerable<Mod>> Mods = new Bindable<IEnumerable<Mod>>(new Mod[] { });
public readonly bool WithStoryboard;
protected WorkingBeatmap(BeatmapInfo beatmapInfo, bool withStoryboard = false)
protected WorkingBeatmap(BeatmapInfo beatmapInfo)
{
BeatmapInfo = beatmapInfo;
BeatmapSetInfo = beatmapInfo.BeatmapSet;
Metadata = beatmapInfo.Metadata ?? BeatmapSetInfo.Metadata;
WithStoryboard = withStoryboard;
Mods.ValueChanged += mods => applyRateAdjustments();
}

View File

@ -1,303 +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 System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using osu.Framework.Extensions;
using osu.Framework.Logging;
using osu.Framework.Platform;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Formats;
using osu.Game.Beatmaps.IO;
using osu.Game.IPC;
using osu.Game.Screens.Menu;
using SQLite.Net;
using SQLiteNetExtensions.Extensions;
namespace osu.Game.Database
{
public class BeatmapDatabase : Database
{
private readonly RulesetDatabase rulesets;
public event Action<BeatmapSetInfo> BeatmapSetAdded;
public event Action<BeatmapSetInfo> BeatmapSetRemoved;
// ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised)
private BeatmapIPCChannel ipc;
/// <summary>
/// A default representation of a WorkingBeatmap to use when no beatmap is available.
/// </summary>
public WorkingBeatmap DefaultBeatmap { private get; set; }
public BeatmapDatabase(Storage storage, SQLiteConnection connection, RulesetDatabase rulesets, IIpcHost importHost = null) : base(storage, connection)
{
this.rulesets = rulesets;
if (importHost != null)
ipc = new BeatmapIPCChannel(importHost, this);
}
private void deletePending()
{
foreach (var b in GetAllWithChildren<BeatmapSetInfo>(b => b.DeletePending))
{
if (b.Hash == Intro.MENU_MUSIC_BEATMAP_HASH)
// this is a bit hacky, but will do for now.
continue;
try
{
Storage.Delete(b.Path);
foreach (var i in b.Beatmaps)
{
if (i.Metadata != null) Connection.Delete(i.Metadata);
if (i.Difficulty != null) Connection.Delete(i.Difficulty);
Connection.Delete(i);
}
if (b.Metadata != null) Connection.Delete(b.Metadata);
Connection.Delete(b);
}
catch (Exception e)
{
Logger.Error(e, $@"Could not delete beatmap {b}");
}
}
//this is required because sqlite migrations don't work, initially inserting nulls into this field.
//see https://github.com/praeclarum/sqlite-net/issues/326
Connection.Query<BeatmapSetInfo>("UPDATE BeatmapSetInfo SET DeletePending = 0 WHERE DeletePending IS NULL");
}
protected override void Prepare(bool reset = false)
{
Connection.CreateTable<BeatmapMetadata>();
Connection.CreateTable<BeatmapDifficulty>();
Connection.CreateTable<BeatmapSetInfo>();
Connection.CreateTable<BeatmapInfo>();
if (reset)
{
Storage.DeleteDatabase(@"beatmaps");
foreach (var setInfo in Query<BeatmapSetInfo>())
{
if (Storage.Exists(setInfo.Path))
Storage.Delete(setInfo.Path);
}
Connection.DeleteAll<BeatmapMetadata>();
Connection.DeleteAll<BeatmapDifficulty>();
Connection.DeleteAll<BeatmapSetInfo>();
Connection.DeleteAll<BeatmapInfo>();
}
deletePending();
}
protected override Type[] ValidTypes => new[] {
typeof(BeatmapSetInfo),
typeof(BeatmapInfo),
typeof(BeatmapMetadata),
typeof(BeatmapDifficulty),
};
public void Import(string path)
{
try
{
Import(ArchiveReader.GetReader(Storage, path));
// We may or may not want to delete the file depending on where it is stored.
// e.g. reconstructing/repairing database with beatmaps from default storage.
// Also, not always a single file, i.e. for LegacyFilesystemReader
// TODO: Add a check to prevent files from storage to be deleted.
try
{
File.Delete(path);
}
catch (Exception e)
{
Logger.Error(e, $@"Could not delete file at {path}");
}
}
catch (Exception e)
{
e = e.InnerException ?? e;
Logger.Error(e, @"Could not import beatmap set");
}
}
public void Import(ArchiveReader archiveReader)
{
BeatmapSetInfo set = getBeatmapSet(archiveReader);
//If we have an ID then we already exist in the database.
if (set.ID == 0)
Import(new[] { set });
}
/// <summary>
/// Import multiple <see cref="BeatmapSetInfo"/> from <paramref name="paths"/>.
/// </summary>
/// <param name="paths">Multiple locations on disk</param>
public void Import(params string[] paths)
{
foreach (string p in paths)
{
//In case the file was imported twice and deleted after the first time
if (File.Exists(p))
Import(p);
}
}
/// <summary>
/// Duplicates content from <paramref name="path"/> to storage and returns a representing <see cref="BeatmapSetInfo"/>.
/// </summary>
/// <param name="path">Content location</param>
/// <returns><see cref="BeatmapSetInfo"/></returns>
private BeatmapSetInfo getBeatmapSet(string path) => getBeatmapSet(ArchiveReader.GetReader(Storage, path));
private BeatmapSetInfo getBeatmapSet(ArchiveReader archiveReader)
{
BeatmapMetadata metadata;
using (var stream = new StreamReader(archiveReader.GetStream(archiveReader.BeatmapFilenames[0])))
metadata = BeatmapDecoder.GetDecoder(stream).Decode(stream).Metadata;
string hash;
string path;
using (var input = archiveReader.GetUnderlyingStream())
{
hash = input.GetMd5Hash();
input.Seek(0, SeekOrigin.Begin);
path = Path.Combine(@"beatmaps", hash.Remove(1), hash.Remove(2), hash);
if (!Storage.Exists(path))
using (var output = Storage.GetStream(path, FileAccess.Write))
input.CopyTo(output);
}
var existing = Connection.Table<BeatmapSetInfo>().FirstOrDefault(b => b.Hash == hash);
if (existing != null)
{
GetChildren(existing);
if (existing.DeletePending)
{
existing.DeletePending = false;
Update(existing, false);
BeatmapSetAdded?.Invoke(existing);
}
return existing;
}
var beatmapSet = new BeatmapSetInfo
{
OnlineBeatmapSetID = metadata.OnlineBeatmapSetID,
Beatmaps = new List<BeatmapInfo>(),
Path = path,
Hash = hash,
Metadata = metadata
};
using (var archive = ArchiveReader.GetReader(Storage, path))
{
string[] mapNames = archive.BeatmapFilenames;
foreach (var name in mapNames)
using (var raw = archive.GetStream(name))
using (var ms = new MemoryStream()) //we need a memory stream so we can seek and shit
using (var sr = new StreamReader(ms))
{
raw.CopyTo(ms);
ms.Position = 0;
var decoder = BeatmapDecoder.GetDecoder(sr);
Beatmap beatmap = decoder.Decode(sr);
beatmap.BeatmapInfo.Path = name;
beatmap.BeatmapInfo.Hash = ms.GetMd5Hash();
// TODO: Diff beatmap metadata with set metadata and leave it here if necessary
beatmap.BeatmapInfo.Metadata = null;
// 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;
beatmapSet.Beatmaps.Add(beatmap.BeatmapInfo);
}
beatmapSet.StoryboardFile = archive.StoryboardFilename;
}
return beatmapSet;
}
public void Import(IEnumerable<BeatmapSetInfo> beatmapSets)
{
lock (Connection)
{
Connection.BeginTransaction();
foreach (var s in beatmapSets)
{
Connection.InsertOrReplaceWithChildren(s, true);
BeatmapSetAdded?.Invoke(s);
}
Connection.Commit();
}
}
public void Delete(BeatmapSetInfo beatmapSet)
{
beatmapSet.DeletePending = true;
Update(beatmapSet, false);
BeatmapSetRemoved?.Invoke(beatmapSet);
}
public ArchiveReader GetReader(BeatmapSetInfo beatmapSet)
{
if (string.IsNullOrEmpty(beatmapSet.Path))
return null;
return ArchiveReader.GetReader(Storage, beatmapSet.Path);
}
public BeatmapSetInfo GetBeatmapSet(int id)
{
return Query<BeatmapSetInfo>().FirstOrDefault(s => s.OnlineBeatmapSetID == id);
}
public WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo, WorkingBeatmap previous = null, bool withStoryboard = false)
{
if (beatmapInfo == null || beatmapInfo == DefaultBeatmap?.BeatmapInfo)
return DefaultBeatmap;
if (beatmapInfo.BeatmapSet == null || beatmapInfo.Ruleset == null)
beatmapInfo = GetChildren(beatmapInfo, true);
if (beatmapInfo.BeatmapSet == null)
throw new InvalidOperationException($@"Beatmap set {beatmapInfo.BeatmapSetInfoID} is not in the local database.");
if (beatmapInfo.Metadata == null)
beatmapInfo.Metadata = beatmapInfo.BeatmapSet.Metadata;
WorkingBeatmap working = new DatabaseWorkingBeatmap(this, beatmapInfo, withStoryboard);
previous?.TransferTo(working);
return working;
}
public bool Exists(BeatmapSetInfo beatmapSet) => Storage.Exists(beatmapSet.Path);
}
}

View File

@ -1,80 +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 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 Database
{
protected SQLiteConnection Connection { get; }
protected Storage Storage { get; }
protected Database(Storage storage, SQLiteConnection connection)
{
Storage = storage;
Connection = connection;
try
{
Prepare();
}
catch (Exception e)
{
Logger.Error(e, $@"Failed to initialise the {GetType()}! Trying again with a clean database...");
Prepare(true);
}
}
/// <summary>
/// Prepare this database for use.
/// </summary>
protected abstract void Prepare(bool reset = false);
/// <summary>
/// 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>() where T : class
{
return Connection.Table<T>();
}
/// <summary>
/// This is expensive. Use with caution.
/// </summary>
public List<T> GetAllWithChildren<T>(Expression<Func<T, bool>> filter = null, bool recursive = true)
where T : class
{
return Connection.GetAllWithChildren(filter, recursive);
}
public T GetChildren<T>(T item, bool recursive = false)
{
if (item == null) return default(T);
Connection.GetChildren(item, recursive);
return item;
}
protected abstract Type[] ValidTypes { get; }
public void Update<T>(T record, bool cascade = true) where T : class
{
if (ValidTypes.All(t => t != typeof(T)))
throw new ArgumentException("Must be a type managed by BeatmapDatabase", nameof(T));
if (cascade)
Connection.UpdateWithChildren(record);
else
Connection.Update(record);
}
}
}

View File

@ -0,0 +1,131 @@
// 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 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 DatabaseBackedStore(SQLiteConnection connection, Storage storage = null)
{
Storage = storage;
Connection = connection;
try
{
Prepare();
}
catch (Exception e)
{
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>().FirstOrDefault(s => s.StoreName == storeName) ?? 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.
/// </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)
{
}
/// <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>
protected abstract void Prepare(bool reset = false);
/// <summary>
/// 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; }
}
}

View File

@ -1,75 +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 System.IO;
using osu.Framework.Audio.Track;
using osu.Framework.Graphics.Textures;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Formats;
using osu.Game.Beatmaps.IO;
namespace osu.Game.Database
{
internal class DatabaseWorkingBeatmap : WorkingBeatmap
{
private readonly BeatmapDatabase database;
public DatabaseWorkingBeatmap(BeatmapDatabase database, BeatmapInfo beatmapInfo, bool withStoryboard = false)
: base(beatmapInfo, withStoryboard)
{
this.database = database;
}
private ArchiveReader getReader() => database?.GetReader(BeatmapSetInfo);
protected override Beatmap GetBeatmap()
{
try
{
Beatmap beatmap;
using (var reader = getReader())
{
BeatmapDecoder decoder;
using (var stream = new StreamReader(reader.GetStream(BeatmapInfo.Path)))
{
decoder = BeatmapDecoder.GetDecoder(stream);
beatmap = decoder.Decode(stream);
}
if (beatmap == null || !WithStoryboard || BeatmapSetInfo.StoryboardFile == null)
return beatmap;
using (var stream = new StreamReader(reader.GetStream(BeatmapSetInfo.StoryboardFile)))
decoder.Decode(stream, beatmap);
}
return beatmap;
}
catch { return null; }
}
protected override Texture GetBackground()
{
if (Metadata?.BackgroundFile == null)
return null;
try
{
using (var reader = getReader())
return new TextureStore(new RawTextureLoaderStore(reader), false).Get(Metadata.BackgroundFile);
}
catch { return null; }
}
protected override Track GetTrack()
{
try
{
var trackData = getReader()?.GetStream(Metadata.AudioFile);
return trackData == null ? null : new TrackBass(trackData);
}
catch { return new TrackVirtual(); }
}
}
}

View File

@ -0,0 +1,15 @@
// 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; }
}
}

View File

@ -42,7 +42,7 @@ namespace osu.Game.Graphics.Containers
{
if (!parallaxEnabled)
{
content.MoveTo(Vector2.Zero, firstUpdate ? 0 : 1000, EasingTypes.OutQuint);
content.MoveTo(Vector2.Zero, firstUpdate ? 0 : 1000, Easing.OutQuint);
content.Scale = new Vector2(1 + ParallaxAmount);
}
};
@ -57,7 +57,7 @@ namespace osu.Game.Graphics.Containers
if (parallaxEnabled)
{
Vector2 offset = input.CurrentState.Mouse == null ? Vector2.Zero : ToLocalSpace(input.CurrentState.Mouse.NativeState.Position) - DrawSize / 2;
content.MoveTo(offset * ParallaxAmount, firstUpdate ? 0 : 1000, EasingTypes.OutQuint);
content.MoveTo(offset * ParallaxAmount, firstUpdate ? 0 : 1000, Easing.OutQuint);
content.Scale = new Vector2(1 + ParallaxAmount);
}

View File

@ -13,7 +13,6 @@ using osu.Framework.Graphics.Shapes;
using osu.Framework.Input;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Database;
namespace osu.Game.Graphics.Cursor
{
@ -29,14 +28,14 @@ namespace osu.Game.Graphics.Cursor
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
{
ActiveCursor.Scale = new Vector2(1);
ActiveCursor.ScaleTo(1.2f, 100, EasingTypes.OutQuad);
ActiveCursor.ScaleTo(1.2f, 100, Easing.OutQuad);
return base.OnMouseDown(state, args);
}
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
{
if (!state.Mouse.HasMainButtonPressed)
ActiveCursor.ScaleTo(1, 200, EasingTypes.OutQuad);
ActiveCursor.ScaleTo(1, 200, Easing.OutQuad);
return base.OnMouseUp(state, args);
}

View File

@ -34,7 +34,7 @@ namespace osu.Game.Graphics.Cursor
if (diff > 180) diff -= 360;
degrees = ActiveCursor.Rotation + diff;
ActiveCursor.RotateTo(degrees, 600, EasingTypes.OutQuint);
ActiveCursor.RotateTo(degrees, 600, Easing.OutQuint);
}
return base.OnMouseMove(state);
@ -49,10 +49,10 @@ namespace osu.Game.Graphics.Cursor
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
{
ActiveCursor.Scale = new Vector2(1);
ActiveCursor.ScaleTo(0.90f, 800, EasingTypes.OutQuint);
ActiveCursor.ScaleTo(0.90f, 800, Easing.OutQuint);
((Cursor)ActiveCursor).AdditiveLayer.Alpha = 0;
((Cursor)ActiveCursor).AdditiveLayer.FadeInFromZero(800, EasingTypes.OutQuint);
((Cursor)ActiveCursor).AdditiveLayer.FadeInFromZero(800, Easing.OutQuint);
return base.OnMouseDown(state, args);
}
@ -62,9 +62,9 @@ namespace osu.Game.Graphics.Cursor
{
dragging = false;
((Cursor)ActiveCursor).AdditiveLayer.FadeOut(500, EasingTypes.OutQuint);
ActiveCursor.RotateTo(0, 600 * (1 + Math.Abs(ActiveCursor.Rotation / 720)), EasingTypes.OutElasticHalf);
ActiveCursor.ScaleTo(1, 500, EasingTypes.OutElastic);
((Cursor)ActiveCursor).AdditiveLayer.FadeOut(500, Easing.OutQuint);
ActiveCursor.RotateTo(0, 600 * (1 + Math.Abs(ActiveCursor.Rotation / 720)), Easing.OutElasticHalf);
ActiveCursor.ScaleTo(1, 500, Easing.OutElastic);
}
return base.OnMouseUp(state, args);
@ -72,21 +72,21 @@ namespace osu.Game.Graphics.Cursor
protected override bool OnClick(InputState state)
{
((Cursor)ActiveCursor).AdditiveLayer.FadeOutFromOne(500, EasingTypes.OutQuint);
((Cursor)ActiveCursor).AdditiveLayer.FadeOutFromOne(500, Easing.OutQuint);
return base.OnClick(state);
}
protected override void PopIn()
{
ActiveCursor.FadeTo(1, 250, EasingTypes.OutQuint);
ActiveCursor.ScaleTo(1, 400, EasingTypes.OutQuint);
ActiveCursor.FadeTo(1, 250, Easing.OutQuint);
ActiveCursor.ScaleTo(1, 400, Easing.OutQuint);
}
protected override void PopOut()
{
ActiveCursor.FadeTo(0, 900, EasingTypes.OutQuint);
ActiveCursor.ScaleTo(0, 500, EasingTypes.In);
ActiveCursor.FadeTo(0, 900, Easing.OutQuint);
ActiveCursor.ScaleTo(0, 500, Easing.In);
}
public class Cursor : Container

View File

@ -37,7 +37,7 @@ namespace osu.Game.Graphics.Cursor
if (IsPresent)
{
AutoSizeDuration = 250;
background.FlashColour(OsuColour.Gray(0.4f), 1000, EasingTypes.OutQuint);
background.FlashColour(OsuColour.Gray(0.4f), 1000, Easing.OutQuint);
}
else
AutoSizeDuration = 0;
@ -48,7 +48,7 @@ namespace osu.Game.Graphics.Cursor
public OsuTooltip()
{
AutoSizeEasing = EasingTypes.OutQuint;
AutoSizeEasing = Easing.OutQuint;
CornerRadius = 5;
Masking = true;
@ -83,16 +83,10 @@ namespace osu.Game.Graphics.Cursor
protected override void PopIn()
{
instantMovement |= !IsPresent;
ClearTransforms();
FadeIn(500, EasingTypes.OutQuint);
this.FadeIn(500, Easing.OutQuint);
}
protected override void PopOut()
{
using (BeginDelayedSequence(150))
FadeOut(500, EasingTypes.OutQuint);
}
protected override void PopOut() => this.Delay(150).FadeOut(500, Easing.OutQuint);
public override void Move(Vector2 pos)
{
@ -103,7 +97,7 @@ namespace osu.Game.Graphics.Cursor
}
else
{
MoveTo(pos, 200, EasingTypes.OutQuint);
this.MoveTo(pos, 200, Easing.OutQuint);
}
}
}

View File

@ -4,7 +4,6 @@
using OpenTK.Graphics;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Transforms;
using osu.Game.Graphics.Transforms;
namespace osu.Game.Graphics
{
@ -27,10 +26,8 @@ namespace osu.Game.Graphics
/// <param name="newColour">The new accent colour.</param>
/// <param name="duration">The tween duration.</param>
/// <param name="easing">The tween easing.</param>
public static void FadeAccent<T>(this T accentedDrawable, Color4 newColour, double duration = 0, EasingTypes easing = EasingTypes.None)
where T : Transformable<Drawable>, IHasAccentColour
{
accentedDrawable.TransformTo(newColour, duration, easing, new TransformAccent());
}
public static TransformSequence<T> FadeAccent<T>(this T accentedDrawable, Color4 newColour, double duration = 0, Easing easing = Easing.None)
where T : IHasAccentColour
=> accentedDrawable.TransformTo(nameof(accentedDrawable.AccentColour), newColour, duration, easing);
}
}

View File

@ -13,6 +13,9 @@ namespace osu.Game.Graphics
public static Color4 FromHex(string hex)
{
if (hex[0] == '#')
hex = hex.Substring(1);
switch (hex.Length)
{
default:

View File

@ -1,31 +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 OpenTK.Graphics;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Transforms;
using osu.Framework.MathUtils;
namespace osu.Game.Graphics.Transforms
{
public class TransformAccent : Transform<Color4, Drawable>
{
/// <summary>
/// Current value of the transformed colour in linear colour space.
/// </summary>
public virtual Color4 CurrentValue
{
get
{
double time = Time?.Current ?? 0;
if (time < StartTime) return StartValue;
if (time >= EndTime) return EndValue;
return Interpolation.ValueAt(time, StartValue, EndValue, StartTime, EndTime, Easing);
}
}
public override void Apply(Drawable d) => ((IHasAccentColour)d).AccentColour = CurrentValue;
public override void ReadIntoStartValue(Drawable d) => StartValue = ((IHasAccentColour)d).AccentColour;
}
}

Some files were not shown because too many files have changed in this diff Show More