Merge pull request #61 from SirCmpwn/song-select

Song select
This commit is contained in:
Dean Herbert
2016-10-27 13:54:34 +09:00
committed by GitHub
30 changed files with 1056 additions and 155 deletions

View File

@ -0,0 +1,29 @@
using System;
using System.IO;
using osu.Framework;
using osu.Framework.Desktop.Platform;
using SQLite.Net;
using SQLite.Net.Platform.Generic;
using SQLite.Net.Interop;
using SQLite.Net.Platform.Win32;
namespace osu.Desktop.Platform
{
public class TestStorage : DesktopStorage
{
public TestStorage(string baseName) : base(baseName)
{
}
public override SQLiteConnection GetDatabase(string name)
{
Directory.CreateDirectory(BasePath);
ISQLitePlatform platform;
if (RuntimeInfo.IsWindows)
platform = new SQLitePlatformWin32();
else
platform = new SQLitePlatformGeneric();
return new SQLiteConnection(platform, $@":memory:");
}
}
}

View File

@ -3,6 +3,7 @@
using System; using System;
using osu.Framework.Desktop; using osu.Framework.Desktop;
using osu.Framework.Desktop.Platform;
using osu.Framework.Platform; using osu.Framework.Platform;
namespace osu.Desktop.VisualTests namespace osu.Desktop.VisualTests

View File

@ -0,0 +1,31 @@
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Framework.GameModes.Testing;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Threading;
using osu.Game;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using osu.Framework.Graphics.Sprites;
using osu.Game.Online.Chat.Display;
using osu.Framework;
using osu.Game.GameModes.Play;
namespace osu.Desktop.Tests
{
class TestCasePlaySongSelect : TestCase
{
public override string Name => @"Song Select";
public override string Description => @"Testing song selection UI";
public override void Reset()
{
base.Reset();
Add(new PlaySongSelect());
}
}
}

View File

@ -3,16 +3,81 @@
using osu.Framework; using osu.Framework;
using osu.Framework.GameModes.Testing; using osu.Framework.GameModes.Testing;
using osu.Framework.Graphics.Cursor;
using osu.Game.Database;
using osu.Game; using osu.Game;
using osu.Framework.Desktop.Platform;
using System.Reflection;
using System.IO;
using System.Collections.Generic;
using osu.Game.GameModes.Play;
using SQLiteNetExtensions.Extensions;
using osu.Desktop.Platform;
namespace osu.Desktop.VisualTests namespace osu.Desktop.VisualTests
{ {
class VisualTestGame : OsuGameBase class VisualTestGame : OsuGameBase
{ {
private void InsertTestMap(int i)
{
var beatmapSet = new BeatmapSetInfo
{
BeatmapSetID = 1234 + i,
Hash = "d8e8fca2dc0f896fd7cb4cb0031ba249",
Path = "/foo/bar/baz",
Metadata = new BeatmapMetadata
{
BeatmapSetID = 1234 + i,
Artist = "MONACA",
Title = "Black Song",
Author = "Some Guy",
},
Beatmaps = new List<BeatmapInfo>(new[]
{
new BeatmapInfo
{
BeatmapID = 1234 + i,
Mode = PlayMode.Osu,
Path = "normal.osu",
Version = "Normal",
BaseDifficulty = new BaseDifficulty
{
OverallDifficulty = 3.5f,
}
},
new BeatmapInfo
{
BeatmapID = 1235 + i,
Mode = PlayMode.Osu,
Path = "hard.osu",
Version = "Hard",
BaseDifficulty = new BaseDifficulty
{
OverallDifficulty = 5,
}
},
new BeatmapInfo
{
BeatmapID = 1236 + i,
Mode = PlayMode.Osu,
Path = "insane.osu",
Version = "Insane",
BaseDifficulty = new BaseDifficulty
{
OverallDifficulty = 7,
}
},
}),
};
BeatmapDatabase.Connection.InsertWithChildren(beatmapSet, true);
}
public override void Load(BaseGame game) public override void Load(BaseGame game)
{ {
Host.Storage = new TestStorage(@"visual-tests");
base.Load(game); base.Load(game);
for (int i = 0; i < 100; i += 10)
InsertTestMap(i);
Add(new TestBrowser()); Add(new TestBrowser());
} }
} }

View File

@ -81,15 +81,32 @@
<ItemGroup> <ItemGroup>
<Reference Include="mscorlib" /> <Reference Include="mscorlib" />
<Reference Include="OpenTK, Version=2.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL"> <Reference Include="OpenTK, Version=2.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL">
<HintPath>..\packages\ppy.OpenTK.2.0.50727.1337\lib\net20\OpenTK.dll</HintPath> <HintPath>$(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1337\lib\net20\OpenTK.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="SQLite.Net, Version=3.1.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\SQLite.Net.Core-PCL.3.1.1\lib\portable-win8+net45+wp8+wpa81+MonoAndroid1+MonoTouch1\SQLite.Net.dll</HintPath>
</Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="Newtonsoft.Json">
<HintPath>$(SolutionDir)\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="SQLiteNetExtensions">
<HintPath>$(SolutionDir)\packages\SQLiteNetExtensions.1.3.0\lib\portable-net45+netcore45+wpa81+wp8+MonoAndroid1+MonoTouch1\SQLiteNetExtensions.dll</HintPath>
</Reference>
<Reference Include="SQLite.Net.Platform.Win32">
<HintPath>$(SolutionDir)\packages\SQLite.Net-PCL.3.1.1\lib\net4\SQLite.Net.Platform.Win32.dll</HintPath>
</Reference>
<Reference Include="SQLite.Net.Platform.Generic">
<HintPath>$(SolutionDir)\packages\SQLite.Net-PCL.3.1.1\lib\net40\SQLite.Net.Platform.Generic.dll</HintPath>
</Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="..\osu.licenseheader"> <None Include="..\osu.licenseheader">
<Link>osu.licenseheader</Link> <Link>osu.licenseheader</Link>
</None> </None>
<None Include="packages.config" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<BootstrapperPackage Include="Microsoft.Net.Client.3.5"> <BootstrapperPackage Include="Microsoft.Net.Client.3.5">
@ -146,9 +163,14 @@
<Compile Include="Tests\TestCaseMenuButtonSystem.cs" /> <Compile Include="Tests\TestCaseMenuButtonSystem.cs" />
<Compile Include="Tests\TestCaseScoreCounter.cs" /> <Compile Include="Tests\TestCaseScoreCounter.cs" />
<Compile Include="Tests\TestCaseTextAwesome.cs" /> <Compile Include="Tests\TestCaseTextAwesome.cs" />
<Compile Include="Tests\TestCasePlaySongSelect.cs" />
<Compile Include="VisualTestGame.cs" /> <Compile Include="VisualTestGame.cs" />
<Compile Include="Platform\TestStorage.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup /> <ItemGroup />
<ItemGroup>
<Folder Include="Platform\" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net45" />
<package id="SQLite.Net.Core-PCL" version="3.1.1" targetFramework="net45" />
<package id="SQLite.Net-PCL" version="3.1.1" targetFramework="net45" />
<package id="SQLiteNetExtensions" version="1.3.0" targetFramework="net45" />
</packages>

View File

@ -2,20 +2,41 @@
//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE //Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System; using System;
using System.IO;
using System.Linq;
using osu.Framework;
using osu.Framework.Desktop; using osu.Framework.Desktop;
using osu.Framework.Desktop.Platform;
using osu.Framework.Platform; using osu.Framework.Platform;
using osu.Game; using osu.Game;
using osu.Game.IPC;
namespace osu.Desktop namespace osu.Desktop
{ {
public static class Program public static class Program
{ {
[STAThread] [STAThread]
public static void Main(string[] args) public static int Main(string[] args)
{ {
BasicGameHost host = Host.GetSuitableHost(@"osu"); DesktopGameHost host = Host.GetSuitableHost(@"osu", true);
host.Add(new OsuGame(args));
host.Run(); if (!host.IsPrimaryInstance)
{
var importer = new BeatmapImporter(host);
foreach (var file in args)
if (!importer.Import(file).Wait(1000))
throw new TimeoutException(@"IPC took too long to send");
Console.WriteLine(@"Sent import requests to running instance");
}
else
{
BaseGame osu = new OsuGame(args);
host.Add(osu);
host.Run();
}
return 0;
} }
} }
} }

View File

@ -0,0 +1,109 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using NUnit.Framework;
using osu.Framework.Desktop.Platform;
using osu.Framework.Platform;
using osu.Game.Database;
using osu.Game.IPC;
namespace osu.Game.Tests.Beatmaps.IO
{
[TestFixture]
public class ImportBeatmapTest
{
const string osz_path = @"../../../osu-resources/osu.Game.Resources/Beatmaps/241526 Soleily - Renatus.osz";
[OneTimeSetUp]
public void SetUp()
{
}
[Test]
public void TestImportWhenClosed()
{
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
HeadlessGameHost host = new HeadlessGameHost();
var osu = loadOsu(host);
osu.Beatmaps.Import(osz_path);
ensureLoaded(osu);
}
[Test]
public void TestImportOverIPC()
{
HeadlessGameHost host = new HeadlessGameHost("host", true);
HeadlessGameHost client = new HeadlessGameHost("client", true);
Assert.IsTrue(host.IsPrimaryInstance);
Assert.IsTrue(!client.IsPrimaryInstance);
var osu = loadOsu(host);
var importer = new BeatmapImporter(client);
if (!importer.Import(osz_path).Wait(1000))
Assert.Fail(@"IPC took too long to send");
ensureLoaded(osu, 10000);
}
private OsuGameBase loadOsu(BasicGameHost host)
{
var osu = new OsuGameBase();
host.Add(osu);
//reset beatmap database (sqlite and storage backing)
osu.Beatmaps.Reset();
return osu;
}
private void ensureLoaded(OsuGameBase osu, int timeout = 100)
{
IEnumerable<BeatmapSetInfo> resultSets = null;
Action waitAction = () =>
{
while ((resultSets = osu.Beatmaps.Query<BeatmapSetInfo>().Where(s => s.BeatmapSetID == 241526)).Count() != 1)
Thread.Sleep(1);
};
Assert.IsTrue(waitAction.BeginInvoke(null, null).AsyncWaitHandle.WaitOne(timeout),
@"BeatmapSet did not import to the database");
//ensure we were stored to beatmap database backing...
Assert.IsTrue(resultSets.Count() == 1);
IEnumerable<BeatmapInfo> resultBeatmaps = null;
//if we don't re-check here, the set will be inserted but the beatmaps won't be present yet.
waitAction = () =>
{
while ((resultBeatmaps = osu.Beatmaps.Query<BeatmapInfo>().Where(s => s.BeatmapSetID == 241526 && s.BaseDifficultyID > 0)).Count() != 12)
Thread.Sleep(1);
};
Assert.IsTrue(waitAction.BeginInvoke(null, null).AsyncWaitHandle.WaitOne(timeout),
@"Beatmaps did not import to the database");
//fetch children and check we can load from the post-storage path...
var set = osu.Beatmaps.GetChildren(resultSets.First());
Assert.IsTrue(set.Beatmaps.Count == resultBeatmaps.Count());
foreach (BeatmapInfo b in resultBeatmaps)
Assert.IsTrue(set.Beatmaps.Any(c => c.BeatmapID == b.BeatmapID));
Assert.IsTrue(set.Beatmaps.Count > 0);
var beatmap = osu.Beatmaps.GetBeatmap(set.Beatmaps[0]);
Assert.IsTrue(beatmap.HitObjects.Count > 0);
}
}
}

11
osu.Game.Tests/app.config Normal file
View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@ -28,22 +28,44 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="nunit.framework, Version=3.5.0.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL"> <Reference Include="nunit.framework, Version=3.5.0.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
<HintPath>..\packages\NUnit.3.5.0\lib\net45\nunit.framework.dll</HintPath> <HintPath>$(SolutionDir)\packages\NUnit.3.5.0\lib\net45\nunit.framework.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="System" />
<Reference Include="nunit.framework">
<HintPath>$(SolutionDir)\packages\NUnit.2.6.4\lib\nunit.framework.dll</HintPath>
</Reference>
<Reference Include="OpenTK, Version=2.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL"> <Reference Include="OpenTK, Version=2.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL">
<HintPath>..\packages\ppy.OpenTK.2.0.50727.1339\lib\net45\OpenTK.dll</HintPath> <HintPath>$(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1339\lib\net45\OpenTK.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Drawing" /> <Reference Include="System.Drawing" />
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
<Reference Include="SQLite.Net">
<HintPath>$(SolutionDir)\packages\SQLite.Net.Core-PCL.3.1.1\lib\portable-win8+net45+wp8+wpa81+MonoAndroid1+MonoTouch1\SQLite.Net.dll</HintPath>
</Reference>
<Reference Include="SQLite.Net.Platform.Win32">
<HintPath>$(SolutionDir)\packages\SQLite.Net-PCL.3.1.1\lib\net4\SQLite.Net.Platform.Win32.dll</HintPath>
</Reference>
<Reference Include="SQLite.Net.Platform.Generic">
<HintPath>$(SolutionDir)\packages\SQLite.Net-PCL.3.1.1\lib\net40\SQLite.Net.Platform.Generic.dll</HintPath>
</Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="app.config" />
<None Include="packages.config" /> <None Include="packages.config" />
<None Include="OpenTK.dll.config" /> <None Include="OpenTK.dll.config" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\osu-framework\osu.Framework.Desktop\osu.Framework.Desktop.csproj">
<Project>{65DC628F-A640-4111-AB35-3A5652BC1E17}</Project>
<Name>osu.Framework.Desktop</Name>
</ProjectReference>
<ProjectReference Include="..\osu-framework\osu.Framework\osu.Framework.csproj">
<Project>{c76bf5b3-985e-4d39-95fe-97c9c879b83a}</Project>
<Name>osu.Framework</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game\osu.Game.csproj"> <ProjectReference Include="..\osu.Game\osu.Game.csproj">
<Project>{0D3FBF8A-7464-4CF7-8C90-3E7886DF2D4D}</Project> <Project>{0D3FBF8A-7464-4CF7-8C90-3E7886DF2D4D}</Project>
<Name>osu.Game</Name> <Name>osu.Game</Name>
@ -53,14 +75,10 @@
<Name>osu.Game.Resources</Name> <Name>osu.Game.Resources</Name>
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup />
<Folder Include="Resources\" />
<Folder Include="Beatmaps\" />
<Folder Include="Beatmaps\IO\" />
<Folder Include="Beatmaps\Formats\" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Beatmaps\IO\OszArchiveReaderTest.cs" /> <Compile Include="Beatmaps\IO\OszArchiveReaderTest.cs" />
<Compile Include="Beatmaps\IO\ImportBeatmapTest.cs" />
<Compile Include="Resources\Resource.cs" /> <Compile Include="Resources\Resource.cs" />
<Compile Include="Beatmaps\Formats\OsuLegacyDecoderTest.cs" /> <Compile Include="Beatmaps\Formats\OsuLegacyDecoderTest.cs" />
</ItemGroup> </ItemGroup>

View File

@ -2,4 +2,6 @@
<packages> <packages>
<package id="NUnit" version="3.5.0" targetFramework="net45" /> <package id="NUnit" version="3.5.0" targetFramework="net45" />
<package id="ppy.OpenTK" version="2.0.50727.1339" targetFramework="net45" /> <package id="ppy.OpenTK" version="2.0.50727.1339" targetFramework="net45" />
<package id="SQLite.Net.Core-PCL" version="3.1.1" targetFramework="net45" />
<package id="SQLite.Net-PCL" version="3.1.1" targetFramework="net45" />
</packages> </packages>

View File

@ -0,0 +1,141 @@
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Linq;
using osu.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Input;
using osu.Game.Database;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Beatmaps.Drawable
{
class BeatmapGroup : Container, IStateful<BeatmapGroupState>
{
private const float collapsedAlpha = 0.5f;
private const float collapsedWidth = 0.8f;
private BeatmapPanel selectedPanel;
/// <summary>
/// Fires when one of our difficulties was selected. Will fire on first expand.
/// </summary>
public Action<BeatmapGroup, BeatmapInfo> SelectionChanged;
private BeatmapSetInfo beatmapSet;
private BeatmapSetHeader header;
private FlowContainer difficulties;
private BeatmapGroupState state;
public BeatmapGroupState State
{
get { return state; }
set
{
state = value;
switch (state)
{
case BeatmapGroupState.Expanded:
FadeTo(1, 250);
difficulties.Show();
//todo: header should probably have a state, with this logic moved inside it.
header.Width = 1;
header.GlowRadius = 5;
header.BorderColour = new Color4(header.BorderColour.R, header.BorderColour.G, header.BorderColour.B, 255);
if (selectedPanel == null)
(difficulties.Children.FirstOrDefault() as BeatmapPanel).Selected = true;
SelectionChanged?.Invoke(this, selectedPanel?.Beatmap);
break;
case BeatmapGroupState.Collapsed:
FadeTo(collapsedAlpha, 250);
difficulties.Hide();
//todo: header should probably have a state, with this logic moved inside it.
header.Width = collapsedWidth;
header.GlowRadius = 0;
header.BorderColour = new Color4(header.BorderColour.R, header.BorderColour.G, header.BorderColour.B, 0);
break;
}
}
}
public BeatmapGroup(BeatmapSetInfo beatmapSet)
{
this.beatmapSet = beatmapSet;
Alpha = 0;
AutoSizeAxes = Axes.Y;
RelativeSizeAxes = Axes.X;
Children = new[]
{
new FlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FlowDirection.VerticalOnly,
Children = new Framework.Graphics.Drawable[]
{
header = new BeatmapSetHeader(beatmapSet)
{
RelativeSizeAxes = Axes.X,
Width = collapsedWidth,
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
},
difficulties = new FlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Margin = new MarginPadding { Top = 5 },
Padding = new MarginPadding { Left = 75 },
Spacing = new Vector2(0, 5),
Direction = FlowDirection.VerticalOnly,
Alpha = 0,
Children = this.beatmapSet.Beatmaps.Select(b =>
new BeatmapPanel(this.beatmapSet, b)
{
GainedSelection = panelGainedSelection,
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
RelativeSizeAxes = Axes.X,
}
)
}
}
}
};
}
public override void Load(BaseGame game)
{
base.Load(game);
State = BeatmapGroupState.Collapsed;
}
private void panelGainedSelection(BeatmapPanel panel)
{
if (selectedPanel != null) selectedPanel.Selected = false;
selectedPanel = panel;
SelectionChanged?.Invoke(this, panel.Beatmap);
}
protected override bool OnClick(InputState state)
{
State = BeatmapGroupState.Expanded;
return true;
}
}
public enum BeatmapGroupState
{
Collapsed,
Expanded,
}
}

View File

@ -0,0 +1,110 @@
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input;
using osu.Game.Database;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Beatmaps.Drawable
{
class BeatmapPanel : Container
{
public BeatmapInfo Beatmap;
public Action<BeatmapPanel> GainedSelection;
private bool selected;
public bool Selected
{
get { return selected; }
set
{
if (selected == value)
return;
selected = value;
BorderColour = new Color4(
BorderColour.R,
BorderColour.G,
BorderColour.B,
selected ? 255 : 0);
GlowRadius = selected ? 3 : 0;
if (selected) GainedSelection?.Invoke(this);
}
}
public BeatmapPanel(BeatmapSetInfo set, BeatmapInfo beatmap)
{
Beatmap = beatmap;
AutoSizeAxes = Axes.Y;
Masking = true;
CornerRadius = 5;
BorderThickness = 2;
BorderColour = new Color4(221, 255, 255, 0);
GlowColour = new Color4(166, 221, 251, 0.75f); // TODO: Get actual color for this
Children = new Framework.Graphics.Drawable[]
{
new Box
{
Colour = new Color4(40, 86, 102, 255), // TODO: gradient
RelativeSizeAxes = Axes.Both,
Size = Vector2.One,
},
new FlowContainer
{
Padding = new MarginPadding(5),
Direction = FlowDirection.HorizontalOnly,
AutoSizeAxes = Axes.Both,
Children = new Framework.Graphics.Drawable[]
{
new DifficultyIcon(FontAwesome.dot_circle_o, new Color4(159, 198, 0, 255)),
new FlowContainer
{
Padding = new MarginPadding { Left = 10 },
Direction = FlowDirection.VerticalOnly,
AutoSizeAxes = Axes.Both,
Children = new Framework.Graphics.Drawable[]
{
new FlowContainer
{
Direction = FlowDirection.HorizontalOnly,
AutoSizeAxes = Axes.Both,
Children = new[]
{
new SpriteText
{
Text = beatmap.Version,
TextSize = 20,
},
new SpriteText
{
Text = string.Format(" mapped by {0}",
(beatmap.Metadata ?? set.Metadata).Author),
TextSize = 16,
},
}
},
new StarCounter { Count = beatmap.BaseDifficulty?.OverallDifficulty ?? 5, StarSize = 8 }
}
}
}
}
};
}
protected override bool OnClick(InputState state)
{
Selected = true;
return true;
}
}
}

View File

@ -0,0 +1,80 @@
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Sprites;
using osu.Game.Database;
using osu.Game.Graphics;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Beatmaps.Drawable
{
class BeatmapSetHeader : Container
{
public BeatmapSetHeader(BeatmapSetInfo beatmapSet)
{
AutoSizeAxes = Axes.Y;
Masking = true;
CornerRadius = 5;
BorderThickness = 2;
BorderColour = new Color4(221, 255, 255, 0);
GlowColour = new Color4(166, 221, 251, 0.5f); // TODO: Get actual color for this
Children = new Framework.Graphics.Drawable[]
{
new Box
{
Colour = new Color4(85, 85, 85, 255),
RelativeSizeAxes = Axes.Both,
Size = Vector2.One,
},
new Container
{
RelativeSizeAxes = Axes.Both,
Size = Vector2.One,
Children = new Framework.Graphics.Drawable[]
{
new Box // TODO: Gradient
{
Colour = new Color4(0, 0, 0, 100),
RelativeSizeAxes = Axes.Both,
Size = Vector2.One,
}
}
},
new FlowContainer
{
Direction = FlowDirection.VerticalOnly,
Spacing = new Vector2(0, 2),
Padding = new MarginPadding { Top = 3, Left = 20, Right = 20, Bottom = 3 },
AutoSizeAxes = Axes.Both,
Children = new[]
{
// TODO: Make these italic
new SpriteText
{
Text = beatmapSet.Metadata.Title ?? beatmapSet.Metadata.TitleUnicode,
TextSize = 20
},
new SpriteText
{
Text = beatmapSet.Metadata.Artist ?? beatmapSet.Metadata.ArtistUnicode,
TextSize = 16
},
new FlowContainer
{
AutoSizeAxes = Axes.Both,
Children = new[]
{
new DifficultyIcon(FontAwesome.dot_circle_o, new Color4(159, 198, 0, 255)),
new DifficultyIcon(FontAwesome.dot_circle_o, new Color4(246, 101, 166, 255)),
}
}
}
}
};
}
}
}

View File

@ -0,0 +1,30 @@
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Beatmaps.Drawable
{
class DifficultyIcon : Container
{
public DifficultyIcon(FontAwesome icon, Color4 color)
{
const float size = 20;
Size = new Vector2(size);
Children = new[]
{
new TextAwesome
{
Anchor = Anchor.Centre,
TextSize = size,
Colour = color,
Icon = icon
}
};
}
}
}

View File

@ -21,6 +21,7 @@ namespace osu.Game.Beatmaps.Formats
AddDecoder<OsuLegacyDecoder>(@"osu file format v12"); AddDecoder<OsuLegacyDecoder>(@"osu file format v12");
AddDecoder<OsuLegacyDecoder>(@"osu file format v11"); AddDecoder<OsuLegacyDecoder>(@"osu file format v11");
AddDecoder<OsuLegacyDecoder>(@"osu file format v10"); AddDecoder<OsuLegacyDecoder>(@"osu file format v10");
AddDecoder<OsuLegacyDecoder>(@"osu file format v9");
// TODO: Not sure how far back to go, or differences between versions // TODO: Not sure how far back to go, or differences between versions
} }
@ -213,7 +214,11 @@ namespace osu.Game.Beatmaps.Formats
HitObjects = new List<HitObject>(), HitObjects = new List<HitObject>(),
ControlPoints = new List<ControlPoint>(), ControlPoints = new List<ControlPoint>(),
ComboColors = new List<Color4>(), ComboColors = new List<Color4>(),
BeatmapInfo = new BeatmapInfo(), BeatmapInfo = new BeatmapInfo
{
Metadata = new BeatmapMetadata(),
BaseDifficulty = new BaseDifficulty(),
},
}; };
var section = Section.None; var section = Section.None;
@ -223,7 +228,6 @@ namespace osu.Game.Beatmaps.Formats
line = stream.ReadLine(); line = stream.ReadLine();
if (line == null) if (line == null)
break; break;
line = line.Trim();
if (string.IsNullOrEmpty(line)) if (string.IsNullOrEmpty(line))
continue; continue;
if (line.StartsWith(@"osu file format v")) if (line.StartsWith(@"osu file format v"))

View File

@ -20,12 +20,14 @@ namespace osu.Game.Beatmaps.IO
OsuLegacyDecoder.Register(); OsuLegacyDecoder.Register();
} }
private ZipFile archive { get; set; } private Stream archiveStream;
private string[] beatmaps { get; set; } private ZipFile archive;
private Beatmap firstMap { get; set; } private string[] beatmaps;
private Beatmap firstMap;
public OszArchiveReader(Stream archiveStream) public OszArchiveReader(Stream archiveStream)
{ {
this.archiveStream = archiveStream;
archive = ZipFile.Read(archiveStream); archive = ZipFile.Read(archiveStream);
beatmaps = archive.Entries.Where(e => e.FileName.EndsWith(@".osu")) beatmaps = archive.Entries.Where(e => e.FileName.EndsWith(@".osu"))
.Select(e => e.FileName).ToArray(); .Select(e => e.FileName).ToArray();
@ -59,6 +61,7 @@ namespace osu.Game.Beatmaps.IO
public override void Dispose() public override void Dispose()
{ {
archive.Dispose(); archive.Dispose();
archiveStream.Dispose();
} }
} }
} }

View File

@ -1,12 +1,15 @@
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Linq.Expressions;
using System.Security.Cryptography; using System.Security.Cryptography;
using osu.Framework.Platform; using osu.Framework.Platform;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Formats; using osu.Game.Beatmaps.Formats;
using osu.Game.Beatmaps.IO; using osu.Game.Beatmaps.IO;
using osu.Game.IPC;
using SQLite.Net; using SQLite.Net;
using SQLiteNetExtensions.Extensions; using SQLiteNetExtensions.Extensions;
@ -14,67 +17,96 @@ namespace osu.Game.Database
{ {
public class BeatmapDatabase public class BeatmapDatabase
{ {
private static SQLiteConnection connection { get; set; } public static SQLiteConnection Connection { get; set; }
private BasicStorage storage; private BasicStorage storage;
public event Action<BeatmapSetInfo> BeatmapSetAdded;
public BeatmapDatabase(BasicStorage storage) private BeatmapImporter ipc;
public BeatmapDatabase(BasicGameHost host)
{ {
this.storage = storage; this.storage = host.Storage;
if (connection == null)
ipc = new BeatmapImporter(host, this);
if (Connection == null)
{ {
connection = storage.GetDatabase(@"beatmaps"); Connection = storage.GetDatabase(@"beatmaps");
connection.CreateTable<BeatmapMetadata>(); Connection.CreateTable<BeatmapMetadata>();
connection.CreateTable<BaseDifficulty>(); Connection.CreateTable<BaseDifficulty>();
connection.CreateTable<BeatmapSetInfo>(); Connection.CreateTable<BeatmapSetInfo>();
connection.CreateTable<BeatmapInfo>(); Connection.CreateTable<BeatmapInfo>();
} }
} }
public void ImportBeatmap(string path) public void Reset()
{ {
string hash = null; foreach (var setInfo in Query<BeatmapSetInfo>())
var reader = ArchiveReader.GetReader(storage, path); storage.Delete(setInfo.Path);
var metadata = reader.ReadMetadata();
if (connection.Table<BeatmapSetInfo>().Count(b => b.BeatmapSetID == metadata.BeatmapSetID) != 0) Connection.DeleteAll<BeatmapMetadata>();
return; // TODO: Update this beatmap instead Connection.DeleteAll<BaseDifficulty>();
if (File.Exists(path)) // Not always the case, i.e. for LegacyFilesystemReader Connection.DeleteAll<BeatmapSetInfo>();
Connection.DeleteAll<BeatmapInfo>();
}
public void Import(params string[] paths)
{
foreach (string p in paths)
{ {
using (var md5 = MD5.Create()) var path = p;
using (var input = storage.GetStream(path)) string hash = null;
BeatmapMetadata metadata;
using (var reader = ArchiveReader.GetReader(storage, path))
metadata = reader.ReadMetadata();
if (Connection.Table<BeatmapSetInfo>().Count(b => b.BeatmapSetID == metadata.BeatmapSetID) != 0)
return; // TODO: Update this beatmap instead
if (File.Exists(path)) // Not always the case, i.e. for LegacyFilesystemReader
{ {
hash = BitConverter.ToString(md5.ComputeHash(input)).Replace("-", "").ToLowerInvariant(); using (var md5 = MD5.Create())
input.Seek(0, SeekOrigin.Begin); using (var input = storage.GetStream(path))
var outputPath = Path.Combine(@"beatmaps", hash.Remove(1), hash.Remove(2), hash); {
using (var output = storage.GetStream(outputPath, FileAccess.Write)) hash = BitConverter.ToString(md5.ComputeHash(input)).Replace("-", "").ToLowerInvariant();
input.CopyTo(output); input.Seek(0, SeekOrigin.Begin);
path = Path.Combine(@"beatmaps", hash.Remove(1), hash.Remove(2), hash);
using (var output = storage.GetStream(path, FileAccess.Write))
input.CopyTo(output);
}
} }
} var beatmapSet = new BeatmapSetInfo
string[] mapNames = reader.ReadBeatmaps();
var beatmapSet = new BeatmapSetInfo
{
BeatmapSetID = metadata.BeatmapSetID,
Path = path,
Hash = hash,
};
var maps = new List<BeatmapInfo>();
foreach (var name in mapNames)
{
using (var stream = new StreamReader(reader.ReadFile(name)))
{ {
var decoder = BeatmapDecoder.GetDecoder(stream); BeatmapSetID = metadata.BeatmapSetID,
Beatmap beatmap = decoder.Decode(stream); Beatmaps = new List<BeatmapInfo>(),
beatmap.BeatmapInfo.Path = name; Path = path,
// TODO: Diff beatmap metadata with set metadata and insert if necessary Hash = hash,
beatmap.BeatmapInfo.Metadata = null; Metadata = metadata
maps.Add(beatmap.BeatmapInfo); };
connection.Insert(beatmap.BeatmapInfo.BaseDifficulty);
connection.Insert(beatmap.BeatmapInfo); using (var reader = ArchiveReader.GetReader(storage, path))
connection.UpdateWithChildren(beatmap.BeatmapInfo); {
string[] mapNames = reader.ReadBeatmaps();
foreach (var name in mapNames)
{
using (var stream = new StreamReader(reader.ReadFile(name)))
{
var decoder = BeatmapDecoder.GetDecoder(stream);
Beatmap beatmap = decoder.Decode(stream);
beatmap.BeatmapInfo.Path = name;
// TODO: Diff beatmap metadata with set metadata and leave it here if necessary
beatmap.BeatmapInfo.Metadata = null;
beatmapSet.Beatmaps.Add(beatmap.BeatmapInfo);
}
}
} }
Connection.InsertWithChildren(beatmapSet, true);
BeatmapSetAdded?.Invoke(beatmapSet);
} }
connection.Insert(beatmapSet);
beatmapSet.BeatmapMetadataID = connection.Insert(metadata);
connection.UpdateWithChildren(beatmapSet);
} }
public ArchiveReader GetReader(BeatmapSetInfo beatmapSet) public ArchiveReader GetReader(BeatmapSetInfo beatmapSet)
@ -104,7 +136,26 @@ namespace osu.Game.Database
public TableQuery<T> Query<T>() where T : class public TableQuery<T> Query<T>() where T : class
{ {
return connection.Table<T>(); return Connection.Table<T>();
}
public T GetWithChildren<T>(object id) where T : class
{
return Connection.GetWithChildren<T>(id);
}
public List<T> GetAllWithChildren<T>(Expression<Func<T, bool>> filter = null,
bool recursive = true) where T : class
{
return Connection.GetAllWithChildren<T>(filter, recursive);
}
public T GetChildren<T>(T item, bool recursive = true)
{
if (item == null) return default(T);
Connection.GetChildren(item, recursive);
return item;
} }
readonly Type[] validTypes = new[] readonly Type[] validTypes = new[]
@ -120,9 +171,9 @@ namespace osu.Game.Database
if (!validTypes.Any(t => t == typeof(T))) if (!validTypes.Any(t => t == typeof(T)))
throw new ArgumentException(nameof(T), "Must be a type managed by BeatmapDatabase"); throw new ArgumentException(nameof(T), "Must be a type managed by BeatmapDatabase");
if (cascade) if (cascade)
connection.UpdateWithChildren(record); Connection.UpdateWithChildren(record);
else else
connection.Update(record); Connection.Update(record);
} }
} }
} }

View File

@ -9,23 +9,25 @@ namespace osu.Game.Database
{ {
public class BeatmapInfo public class BeatmapInfo
{ {
public BeatmapInfo()
{
BaseDifficulty = new BaseDifficulty();
Metadata = new BeatmapMetadata();
}
[PrimaryKey] [PrimaryKey]
public int BeatmapID { get; set; } public int BeatmapID { get; set; }
[NotNull, Indexed]
[ForeignKey(typeof(BeatmapSetInfo))]
public int BeatmapSetID { get; set; } public int BeatmapSetID { get; set; }
[ManyToOne]
public BeatmapSetInfo BeatmapSet { get; set; }
[ForeignKey(typeof(BeatmapMetadata))] [ForeignKey(typeof(BeatmapMetadata))]
public int BeatmapMetadataID { get; set; } public int BeatmapMetadataID { get; set; }
[OneToOne(CascadeOperations = CascadeOperation.All)]
public BeatmapMetadata Metadata { get; set; }
[ForeignKey(typeof(BaseDifficulty)), NotNull] [ForeignKey(typeof(BaseDifficulty)), NotNull]
public int BaseDifficultyID { get; set; } public int BaseDifficultyID { get; set; }
[OneToOne]
public BeatmapMetadata Metadata { get; set; } [OneToOne(CascadeOperations = CascadeOperation.All)]
[OneToOne]
public BaseDifficulty BaseDifficulty { get; set; } public BaseDifficulty BaseDifficulty { get; set; }
public string Path { get; set; } public string Path { get; set; }

View File

@ -8,7 +8,7 @@ namespace osu.Game.Database
{ {
public class BeatmapMetadata public class BeatmapMetadata
{ {
[PrimaryKey] [PrimaryKey, AutoIncrement]
public int ID { get; set; } public int ID { get; set; }
public int BeatmapSetID { get; set; } public int BeatmapSetID { get; set; }

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using SQLite.Net.Attributes; using SQLite.Net.Attributes;
using SQLiteNetExtensions.Attributes; using SQLiteNetExtensions.Attributes;
@ -8,11 +9,18 @@ namespace osu.Game.Database
{ {
[PrimaryKey] [PrimaryKey]
public int BeatmapSetID { get; set; } public int BeatmapSetID { get; set; }
[OneToOne]
[OneToOne(CascadeOperations = CascadeOperation.All)]
public BeatmapMetadata Metadata { get; set; } public BeatmapMetadata Metadata { get; set; }
[NotNull, ForeignKey(typeof(BeatmapMetadata))] [NotNull, ForeignKey(typeof(BeatmapMetadata))]
public int BeatmapMetadataID { get; set; } public int BeatmapMetadataID { get; set; }
[OneToMany(CascadeOperations = CascadeOperation.All)]
public List<BeatmapInfo> Beatmaps { get; set; }
public string Hash { get; set; } public string Hash { get; set; }
public string Path { get; set; } public string Path { get; set; }
} }
} }

View File

@ -14,6 +14,7 @@ namespace osu.Game.GameModes.Play.Osu
protected override Playfield CreatePlayfield() => new OsuPlayfield(); protected override Playfield CreatePlayfield() => new OsuPlayfield();
protected override DrawableHitObject GetVisualRepresentation(OsuBaseHit h) => new DrawableCircle(h as Circle); protected override DrawableHitObject GetVisualRepresentation(OsuBaseHit h)
=> h is Circle ? new DrawableCircle(h as Circle) : null;
} }
} }

View File

@ -2,43 +2,173 @@
//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE //Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System; using System;
using System.Collections.Generic;
using osu.Framework.Configuration; using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Game.GameModes.Backgrounds; using osu.Game.GameModes.Backgrounds;
using osu.Framework; using osu.Framework;
using osu.Game.Database;
using osu.Framework.Graphics.Primitives;
using System.Linq;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Graphics.UserInterface;
using System.Threading.Tasks;
using osu.Game.Beatmaps.Drawable;
namespace osu.Game.GameModes.Play namespace osu.Game.GameModes.Play
{ {
class PlaySongSelect : GameModeWhiteBox public class PlaySongSelect : OsuGameMode
{ {
private Bindable<PlayMode> playMode; private Bindable<PlayMode> playMode;
private BeatmapDatabase beatmaps;
private BeatmapGroup selectedBeatmapGroup;
private BeatmapInfo selectedBeatmap;
// TODO: use currently selected track as bg
protected override BackgroundMode CreateBackground() => new BackgroundModeCustom(@"Backgrounds/bg4"); protected override BackgroundMode CreateBackground() => new BackgroundModeCustom(@"Backgrounds/bg4");
private ScrollContainer scrollContainer;
private FlowContainer setList;
protected override IEnumerable<Type> PossibleChildren => new[] { public PlaySongSelect()
typeof(ModSelect), {
typeof(Player) const float scrollWidth = 640;
}; const float bottomToolHeight = 50;
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.Both,
Size = Vector2.One,
Padding = new MarginPadding { Right = scrollWidth - 200 },
Children = new[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Size = new Vector2(1, 0.5f),
Colour = new Color4(0, 0, 0, 0.5f),
Shear = new Vector2(0.15f, 0),
},
new Box
{
RelativeSizeAxes = Axes.Both,
RelativePositionAxes = Axes.Y,
Size = new Vector2(1, -0.5f),
Position = new Vector2(0, 1),
Colour = new Color4(0, 0, 0, 0.5f),
Shear = new Vector2(-0.15f, 0),
},
}
},
scrollContainer = new ScrollContainer
{
RelativeSizeAxes = Axes.Y,
Size = new Vector2(scrollWidth, 1),
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Children = new Drawable[]
{
setList = new FlowContainer
{
Padding = new MarginPadding { Left = 25, Top = 25, Bottom = 25 + bottomToolHeight },
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FlowDirection.VerticalOnly,
Spacing = new Vector2(0, 5),
}
}
},
new Container
{
RelativeSizeAxes = Axes.X,
Height = bottomToolHeight,
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Size = Vector2.One,
Colour = new Color4(0, 0, 0, 0.5f),
},
new Button
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
RelativeSizeAxes = Axes.Y,
Width = 100,
Text = "Play",
Colour = new Color4(238, 51, 153, 255),
Action = () => Push(new Player { Beatmap = beatmaps.GetBeatmap(selectedBeatmap) }),
},
}
}
};
}
public override void Load(BaseGame game) public override void Load(BaseGame game)
{ {
base.Load(game); base.Load(game);
OsuGame osu = game as OsuGame; OsuGame osuGame = game as OsuGame;
if (osuGame != null)
{
playMode = osuGame.PlayMode;
playMode.ValueChanged += PlayMode_ValueChanged;
// Temporary:
scrollContainer.Padding = new MarginPadding { Top = osuGame.Toolbar.Height };
}
playMode = osu.PlayMode; beatmaps = (game as OsuGameBase).Beatmaps;
playMode.ValueChanged += PlayMode_ValueChanged; beatmaps.BeatmapSetAdded += bset => Scheduler.Add(() => addBeatmapSet(bset));
Task.Factory.StartNew(addBeatmapSets);
} }
protected override void Dispose(bool isDisposing) protected override void Dispose(bool isDisposing)
{ {
base.Dispose(isDisposing); base.Dispose(isDisposing);
if (playMode != null)
playMode.ValueChanged -= PlayMode_ValueChanged; playMode.ValueChanged -= PlayMode_ValueChanged;
} }
private void PlayMode_ValueChanged(object sender, EventArgs e) private void PlayMode_ValueChanged(object sender, EventArgs e)
{ {
} }
private void selectBeatmap(BeatmapGroup group, BeatmapInfo beatmap)
{
if (selectedBeatmapGroup == group)
return;
if (selectedBeatmapGroup != null)
selectedBeatmapGroup.State = BeatmapGroupState.Collapsed;
selectedBeatmapGroup = group;
selectedBeatmap = beatmap;
}
private void addBeatmapSet(BeatmapSetInfo beatmapSet)
{
beatmapSet = beatmaps.GetWithChildren<BeatmapSetInfo>(beatmapSet.BeatmapSetID);
beatmapSet.Beatmaps.ForEach(b => beatmaps.GetChildren(b));
beatmapSet.Beatmaps = beatmapSet.Beatmaps.OrderBy(b => b.BaseDifficulty.OverallDifficulty).ToList();
Schedule(() =>
{
var group = new BeatmapGroup(beatmapSet) { SelectionChanged = selectBeatmap };
setList.Add(group);
if (setList.Children.Count() == 1)
{
group.State = BeatmapGroupState.Expanded;
}
});
}
private void addBeatmapSets()
{
foreach (var beatmapSet in beatmaps.Query<BeatmapSetInfo>())
addBeatmapSet(beatmapSet);
}
} }
} }

View File

@ -0,0 +1,46 @@
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Diagnostics;
using System.Threading.Tasks;
using osu.Framework.Platform;
using osu.Game.Database;
namespace osu.Game.IPC
{
public class BeatmapImporter
{
private IpcChannel<BeatmapImportMessage> channel;
private BeatmapDatabase beatmaps;
public BeatmapImporter(BasicGameHost host, BeatmapDatabase beatmaps = null)
{
this.beatmaps = beatmaps;
channel = new IpcChannel<BeatmapImportMessage>(host);
channel.MessageReceived += messageReceived;
}
public async Task Import(string path)
{
if (beatmaps != null)
beatmaps.Import(path);
else
{
await channel.SendMessage(new BeatmapImportMessage { Path = path });
}
}
private void messageReceived(BeatmapImportMessage msg)
{
Debug.Assert(beatmaps != null);
Import(msg.Path);
}
}
public class BeatmapImportMessage
{
public string Path;
}
}

View File

@ -9,19 +9,13 @@ using osu.Game.GameModes.Menu;
using OpenTK; using OpenTK;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Platform; using osu.Framework.Platform;
using osu.Game.GameModes;
using osu.Game.Graphics.Background;
using osu.Game.GameModes.Play; using osu.Game.GameModes.Play;
using osu.Game.Graphics.Containers;
using osu.Game.Overlays; using osu.Game.Overlays;
using osu.Framework; using osu.Framework;
using osu.Framework.Input; using osu.Framework.Input;
using osu.Game.Input; using osu.Game.Input;
using OpenTK.Input; using OpenTK.Input;
using System.IO;
using osu.Game.Beatmaps.IO;
using osu.Framework.Logging; using osu.Framework.Logging;
using osu.Game.Graphics.UserInterface.Volume; using osu.Game.Graphics.UserInterface.Volume;
@ -29,23 +23,18 @@ namespace osu.Game
{ {
public class OsuGame : OsuGameBase public class OsuGame : OsuGameBase
{ {
private class ImportBeatmap
{
public string Path;
}
public Toolbar Toolbar; public Toolbar Toolbar;
public ChatConsole Chat; public ChatConsole Chat;
public MainMenu MainMenu => intro?.ChildGameMode as MainMenu; public MainMenu MainMenu => intro?.ChildGameMode as MainMenu;
private Intro intro; private Intro intro;
private string[] args;
private IpcChannel<ImportBeatmap> BeatmapIPC;
private VolumeControl volume; private VolumeControl volume;
public Bindable<PlayMode> PlayMode; public Bindable<PlayMode> PlayMode;
public OsuGame(string[] args) string[] args;
public OsuGame(string[] args = null)
{ {
this.args = args; this.args = args;
} }
@ -59,36 +48,17 @@ namespace osu.Game
public override void Load(BaseGame game) public override void Load(BaseGame game)
{ {
BeatmapIPC = new IpcChannel<ImportBeatmap>(Host);
if (!Host.IsPrimaryInstance) if (!Host.IsPrimaryInstance)
{ {
if (args.Length == 1 && File.Exists(args[0])) Logger.Log(@"osu! does not support multiple running instances.", LoggingTarget.Runtime, LogLevel.Error);
{
BeatmapIPC.SendMessage(new ImportBeatmap { Path = args[0] }).Wait();
Logger.Log(@"Sent file to running instance");
}
else
Logger.Log(@"osu! does not support multiple running instances.", LoggingTarget.Runtime, LogLevel.Error);
Environment.Exit(0); Environment.Exit(0);
} }
BeatmapIPC.MessageReceived += message =>
{
try
{
Beatmaps.ImportBeatmap(message.Path);
// TODO: Switch to beatmap list and select the new song
}
catch (Exception ex)
{
// TODO: Show the user some info?
Logger.Log($@"Failed to import beatmap: {ex}", LoggingTarget.Runtime, LogLevel.Error);
}
};
base.Load(game); base.Load(game);
if (args?.Length > 0)
Schedule(delegate { Beatmaps.Import(args); });
//attach our bindables to the audio subsystem. //attach our bindables to the audio subsystem.
Audio.Volume.Weld(Config.GetBindable<double>(OsuConfig.VolumeGlobal)); Audio.Volume.Weld(Config.GetBindable<double>(OsuConfig.VolumeGlobal));
Audio.VolumeSample.Weld(Config.GetBindable<double>(OsuConfig.VolumeEffect)); Audio.VolumeSample.Weld(Config.GetBindable<double>(OsuConfig.VolumeEffect));

View File

@ -10,6 +10,7 @@ using osu.Game.Configuration;
using osu.Game.Database; using osu.Game.Database;
using osu.Game.Graphics.Cursor; using osu.Game.Graphics.Cursor;
using osu.Game.Graphics.Processing; using osu.Game.Graphics.Processing;
using osu.Game.IPC;
using osu.Game.Online.API; using osu.Game.Online.API;
using osu.Game.Overlays; using osu.Game.Overlays;
@ -18,7 +19,7 @@ namespace osu.Game
public class OsuGameBase : BaseGame public class OsuGameBase : BaseGame
{ {
internal OsuConfigManager Config = new OsuConfigManager(); internal OsuConfigManager Config = new OsuConfigManager();
internal BeatmapDatabase Beatmaps { get; private set; } public BeatmapDatabase Beatmaps { get; private set; }
protected override string MainResourceFile => @"osu.Game.Resources.dll"; protected override string MainResourceFile => @"osu.Game.Resources.dll";
@ -47,7 +48,7 @@ namespace osu.Game
base.Load(game); base.Load(game);
OszArchiveReader.Register(); OszArchiveReader.Register();
Beatmaps = new BeatmapDatabase(Host.Storage); Beatmaps = new BeatmapDatabase(Host);
//this completely overrides the framework default. will need to change once we make a proper FontStore. //this completely overrides the framework default. will need to change once we make a proper FontStore.
Fonts = new TextureStore() { ScaleAdjust = 0.01f }; Fonts = new TextureStore() { ScaleAdjust = 0.01f };
@ -72,8 +73,11 @@ namespace osu.Game
protected override void Dispose(bool isDisposing) protected override void Dispose(bool isDisposing)
{ {
//refresh token may have changed. //refresh token may have changed.
Config.Set(OsuConfig.Token, API.Token); if (Config != null && API != null)
Config.Save(); {
Config.Set(OsuConfig.Token, API.Token);
Config.Save();
}
base.Dispose(isDisposing); base.Dispose(isDisposing);
} }

View File

@ -64,6 +64,8 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Beatmaps\Beatmap.cs" /> <Compile Include="Beatmaps\Beatmap.cs" />
<Compile Include="Beatmaps\Drawable\BeatmapSetHeader.cs" />
<Compile Include="Beatmaps\Drawable\DifficultyIcon.cs" />
<Compile Include="Beatmaps\Objects\Catch\CatchConverter.cs" /> <Compile Include="Beatmaps\Objects\Catch\CatchConverter.cs" />
<Compile Include="Beatmaps\Objects\Catch\Drawable\DrawableFruit.cs" /> <Compile Include="Beatmaps\Objects\Catch\Drawable\DrawableFruit.cs" />
<Compile Include="Beatmaps\Objects\DrawableHitObject.cs" /> <Compile Include="Beatmaps\Objects\DrawableHitObject.cs" />
@ -116,6 +118,8 @@
<Compile Include="GameModes\OsuGameMode.cs" /> <Compile Include="GameModes\OsuGameMode.cs" />
<Compile Include="GameModes\Play\ModSelect.cs" /> <Compile Include="GameModes\Play\ModSelect.cs" />
<Compile Include="GameModes\Play\Osu\ScoreOverlayOsu.cs" /> <Compile Include="GameModes\Play\Osu\ScoreOverlayOsu.cs" />
<Compile Include="Beatmaps\Drawable\BeatmapGroup.cs" />
<Compile Include="Beatmaps\Drawable\BeatmapPanel.cs" />
<Compile Include="GameModes\Play\Player.cs" /> <Compile Include="GameModes\Play\Player.cs" />
<Compile Include="GameModes\Charts\ChartListing.cs" /> <Compile Include="GameModes\Charts\ChartListing.cs" />
<Compile Include="GameModes\Play\PlayMode.cs" /> <Compile Include="GameModes\Play\PlayMode.cs" />
@ -155,6 +159,7 @@
<Compile Include="GameModes\Play\Catch\CatchComboCounter.cs" /> <Compile Include="GameModes\Play\Catch\CatchComboCounter.cs" />
<Compile Include="GameModes\Play\Osu\OsuComboCounter.cs" /> <Compile Include="GameModes\Play\Osu\OsuComboCounter.cs" />
<Compile Include="Graphics\UserInterface\StarCounter.cs" /> <Compile Include="Graphics\UserInterface\StarCounter.cs" />
<Compile Include="IPC\BeatmapImporter.cs" />
<Compile Include="Online\API\APIAccess.cs" /> <Compile Include="Online\API\APIAccess.cs" />
<Compile Include="Online\API\APIRequest.cs" /> <Compile Include="Online\API\APIRequest.cs" />
<Compile Include="Online\API\OAuth.cs" /> <Compile Include="Online\API\OAuth.cs" />