diff --git a/.gitmodules b/.gitmodules index ee1cc80880..f1c4f5d172 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ -[submodule "osu-framework"] - path = osu-framework - url = https://github.com/ppy/osu-framework [submodule "osu-resources"] path = osu-resources - url = https://github.com/ppy/osu-resources + url = https://github.com/ppy/osu-resources \ No newline at end of file diff --git a/.idea/.idea.osu/.idea/runConfigurations/VisualTests__netcoreapp2_0_.xml b/.idea/.idea.osu/.idea/runConfigurations/VisualTests__netcoreapp2_1_.xml similarity index 83% rename from .idea/.idea.osu/.idea/runConfigurations/VisualTests__netcoreapp2_0_.xml rename to .idea/.idea.osu/.idea/runConfigurations/VisualTests__netcoreapp2_1_.xml index 08b4e38667..2d3a848922 100644 --- a/.idea/.idea.osu/.idea/runConfigurations/VisualTests__netcoreapp2_0_.xml +++ b/.idea/.idea.osu/.idea/runConfigurations/VisualTests__netcoreapp2_1_.xml @@ -1,6 +1,6 @@ - - \ No newline at end of file diff --git a/.idea/.idea.osu/.idea/runConfigurations/osu___netcoreapp2_0_.xml b/.idea/.idea.osu/.idea/runConfigurations/osu___netcoreapp2_1_.xml similarity index 83% rename from .idea/.idea.osu/.idea/runConfigurations/osu___netcoreapp2_0_.xml rename to .idea/.idea.osu/.idea/runConfigurations/osu___netcoreapp2_1_.xml index 2f5c137631..36efe211c6 100644 --- a/.idea/.idea.osu/.idea/runConfigurations/osu___netcoreapp2_0_.xml +++ b/.idea/.idea.osu/.idea/runConfigurations/osu___netcoreapp2_1_.xml @@ -1,6 +1,6 @@ - - \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index 32c82685c0..b9bb75d5bb 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -58,12 +58,12 @@ "console": "internalConsole" }, { - "name": "VisualTests (Debug, netcoreapp2.0)", + "name": "VisualTests (Debug, netcoreapp2.1)", "type": "coreclr", "request": "launch", "program": "dotnet", "args": [ - "${workspaceRoot}/osu.Game.Tests/bin/Debug/netcoreapp2.0/osu.Game.Tests.dll" + "${workspaceRoot}/osu.Game.Tests/bin/Debug/netcoreapp2.1/osu.Game.Tests.dll" ], "cwd": "${workspaceRoot}", "preLaunchTask": "Build tests (Debug, dotnet)", @@ -71,12 +71,12 @@ "console": "internalConsole" }, { - "name": "VisualTests (Release, netcoreapp2.0)", + "name": "VisualTests (Release, netcoreapp2.1)", "type": "coreclr", "request": "launch", "program": "dotnet", "args": [ - "${workspaceRoot}/osu.Game.Tests/bin/Release/netcoreapp2.0/osu.Game.Tests.dll" + "${workspaceRoot}/osu.Game.Tests/bin/Release/netcoreapp2.1/osu.Game.Tests.dll" ], "cwd": "${workspaceRoot}", "preLaunchTask": "Build tests (Release, dotnet)", @@ -84,12 +84,12 @@ "console": "internalConsole" }, { - "name": "osu! (Debug, netcoreapp2.0)", + "name": "osu! (Debug, netcoreapp2.1)", "type": "coreclr", "request": "launch", "program": "dotnet", "args": [ - "${workspaceRoot}/osu.Desktop/bin/Debug/netcoreapp2.0/osu!.dll", + "${workspaceRoot}/osu.Desktop/bin/Debug/netcoreapp2.1/osu!.dll", ], "cwd": "${workspaceRoot}", "preLaunchTask": "Build osu! (Debug, dotnet)", @@ -97,12 +97,12 @@ "console": "internalConsole" }, { - "name": "osu! (Release, netcoreapp2.0)", + "name": "osu! (Release, netcoreapp2.1)", "type": "coreclr", "request": "launch", "program": "dotnet", "args": [ - "${workspaceRoot}/osu.Desktop/bin/Release/netcoreapp2.0/osu!.dll", + "${workspaceRoot}/osu.Desktop/bin/Release/netcoreapp2.1/osu!.dll", ], "cwd": "${workspaceRoot}", "preLaunchTask": "Build osu! (Release, dotnet)", diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 0908ff6108..bebad750ca 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -38,7 +38,7 @@ "build", "--no-restore", "osu.Desktop", - "/p:TargetFramework=netcoreapp2.0", + "/p:TargetFramework=netcoreapp2.1", "/p:GenerateFullPaths=true", "/m", "/verbosity:m" @@ -54,7 +54,7 @@ "build", "--no-restore", "osu.Desktop", - "/p:TargetFramework=netcoreapp2.0", + "/p:TargetFramework=netcoreapp2.1", "/p:Configuration=Release", "/p:GenerateFullPaths=true", "/m", @@ -71,7 +71,7 @@ "build", "--no-restore", "osu.Game.Tests", - "/p:TargetFramework=netcoreapp2.0", + "/p:TargetFramework=netcoreapp2.1", "/p:GenerateFullPaths=true", "/m", "/verbosity:m" @@ -87,7 +87,7 @@ "build", "--no-restore", "osu.Game.Tests", - "/p:TargetFramework=netcoreapp2.0", + "/p:TargetFramework=netcoreapp2.1", "/p:Configuration=Release", "/p:GenerateFullPaths=true", "/m", @@ -106,7 +106,7 @@ "problemMatcher": [] }, { - "label": "Restore (netcoreapp2.0)", + "label": "Restore (netcoreapp2.1)", "type": "shell", "command": "dotnet", "args": [ diff --git a/appveyor.yml b/appveyor.yml index 69bc762f4c..ac6d6ebff8 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,11 +2,6 @@ clone_depth: 1 version: '{branch}-{build}' image: Visual Studio 2017 configuration: Debug -cache: - - C:\ProgramData\chocolatey\bin -> appveyor.yml - - C:\ProgramData\chocolatey\lib -> appveyor.yml - - inspectcode -> appveyor.yml - - packages -> **\packages.config install: - cmd: git submodule update --init --recursive --depth=5 - cmd: choco install resharper-clt -y diff --git a/appveyor_deploy.yml b/appveyor_deploy.yml index cadebd9d11..0247714cdf 100644 --- a/appveyor_deploy.yml +++ b/appveyor_deploy.yml @@ -1,29 +1,24 @@ -branches: - only: - - release -skip_tags: true -skip_branch_with_pr: true clone_depth: 1 -version: '{branch}-{build}' +version: '{build}' +skip_non_tags: true image: Visual Studio 2017 -configuration: Debug -cache: - - packages -> **\packages.config install: - - cmd: git submodule update --init --recursive --depth=5 + - git clone https://github.com/ppy/osu-deploy before_build: + - ps: if($env:appveyor_repo_tag -eq 'True') { Update-AppveyorBuild -Version $env:appveyor_repo_tag_name } + - cmd: git submodule update --init --recursive --depth=5 - cmd: nuget restore -verbosity quiet -build: - project: osu.Desktop.Deploy/osu.Desktop.Deploy.csproj - verbosity: minimal -after_build: +build_script: - ps: iex ((New-Object Net.WebClient).DownloadString('https://raw.githubusercontent.com/appveyor/secure-file/master/install.ps1')) - appveyor DownloadFile https://puu.sh/A6g5K/4d08705438.enc # signing certificate - cmd: appveyor-tools\secure-file -decrypt 4d08705438.enc -secret %decode_secret% -out %HOMEPATH%\deanherbert.pfx - appveyor DownloadFile https://puu.sh/A6g75/fdc6f19b04.enc # deploy configuration - - cmd: appveyor-tools\secure-file -decrypt fdc6f19b04.enc -secret %decode_secret% -out osu.Desktop.Deploy\bin\Debug\net471\osu.Desktop.Deploy.exe.config - - cd osu.Desktop.Deploy\bin\Debug\net471\ - - osu.Desktop.Deploy.exe %code_signing_password% + - cd osu-deploy + - nuget restore -verbosity quiet + - msbuild osu.Desktop.Deploy.csproj + - cmd: ..\appveyor-tools\secure-file -decrypt ..\fdc6f19b04.enc -secret %decode_secret% -out bin\Debug\net471\osu.Desktop.Deploy.exe.config + - cd bin\Debug\net471\ + - osu.Desktop.Deploy.exe %code_signing_password% %APPVEYOR_REPO_TAG_NAME% environment: TargetFramework: net471 decode_secret: @@ -31,4 +26,7 @@ environment: code_signing_password: secure: 34tLNqvjmmZEi97MLKfrnQ== artifacts: - - path: 'Releases\*' \ No newline at end of file + - path: 'Releases\*' +deploy: + - provider: Environment + name: github \ No newline at end of file diff --git a/osu-framework b/osu-framework deleted file mode 160000 index 804a4b81b8..0000000000 --- a/osu-framework +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 804a4b81b89cb4569af5221e6fa2296d559c28fb diff --git a/osu.Desktop.Deploy/App.config b/osu.Desktop.Deploy/App.config deleted file mode 100644 index 9ec53d5a31..0000000000 --- a/osu.Desktop.Deploy/App.config +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/osu.Desktop.Deploy/GitHubObject.cs b/osu.Desktop.Deploy/GitHubObject.cs deleted file mode 100644 index 02ff16fa69..0000000000 --- a/osu.Desktop.Deploy/GitHubObject.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using Newtonsoft.Json; - -namespace osu.Desktop.Deploy -{ - public class GitHubObject - { - [JsonProperty(@"id")] - public int Id; - - [JsonProperty(@"name")] - public string Name; - } -} diff --git a/osu.Desktop.Deploy/GitHubRelease.cs b/osu.Desktop.Deploy/GitHubRelease.cs deleted file mode 100644 index faf312d4f7..0000000000 --- a/osu.Desktop.Deploy/GitHubRelease.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using Newtonsoft.Json; - -namespace osu.Desktop.Deploy -{ - public class GitHubRelease - { - [JsonProperty(@"id")] - public int Id; - - [JsonProperty(@"tag_name")] - public string TagName => $"v{Name}"; - - [JsonProperty(@"name")] - public string Name; - - [JsonProperty(@"draft")] - public bool Draft; - - [JsonProperty(@"prerelease")] - public bool PreRelease; - - [JsonProperty(@"upload_url")] - public string UploadUrl; - } -} diff --git a/osu.Desktop.Deploy/Program.cs b/osu.Desktop.Deploy/Program.cs deleted file mode 100644 index a1c2a8aef2..0000000000 --- a/osu.Desktop.Deploy/Program.cs +++ /dev/null @@ -1,471 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using System.Configuration; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Management.Automation; -using Newtonsoft.Json; -using osu.Framework.IO.Network; -using FileWebRequest = osu.Framework.IO.Network.FileWebRequest; -using WebRequest = osu.Framework.IO.Network.WebRequest; - -namespace osu.Desktop.Deploy -{ - internal static class Program - { - private static string packages => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".nuget", "packages"); - private static string nugetPath => Path.Combine(packages, @"nuget.commandline\4.5.1\tools\NuGet.exe"); - private static string squirrelPath => Path.Combine(packages, @"squirrel.windows\1.8.0\tools\Squirrel.exe"); - private const string msbuild_path = @"C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\MSBuild.exe"; - - public static string StagingFolder = ConfigurationManager.AppSettings["StagingFolder"]; - public static string ReleasesFolder = ConfigurationManager.AppSettings["ReleasesFolder"]; - public static string GitHubAccessToken = ConfigurationManager.AppSettings["GitHubAccessToken"]; - public static string GitHubUsername = ConfigurationManager.AppSettings["GitHubUsername"]; - public static string GitHubRepoName = ConfigurationManager.AppSettings["GitHubRepoName"]; - public static string SolutionName = ConfigurationManager.AppSettings["SolutionName"]; - public static string ProjectName = ConfigurationManager.AppSettings["ProjectName"]; - public static string NuSpecName = ConfigurationManager.AppSettings["NuSpecName"]; - public static string TargetNames = ConfigurationManager.AppSettings["TargetName"]; - public static string PackageName = ConfigurationManager.AppSettings["PackageName"]; - public static string IconName = ConfigurationManager.AppSettings["IconName"]; - public static string CodeSigningCertificate = ConfigurationManager.AppSettings["CodeSigningCertificate"]; - - public static string GitHubApiEndpoint => $"https://api.github.com/repos/{GitHubUsername}/{GitHubRepoName}/releases"; - public static string GitHubReleasePage => $"https://github.com/{GitHubUsername}/{GitHubRepoName}/releases"; - - /// - /// How many previous build deltas we want to keep when publishing. - /// - private const int keep_delta_count = 4; - - private static string codeSigningCmd => string.IsNullOrEmpty(codeSigningPassword) ? "" : $"-n \"/a /f {codeSigningCertPath} /p {codeSigningPassword} /t http://timestamp.comodoca.com/authenticode\""; - - private static string homeDir => Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); - private static string codeSigningCertPath => Path.Combine(homeDir, CodeSigningCertificate); - private static string solutionPath => Environment.CurrentDirectory; - private static string stagingPath => Path.Combine(solutionPath, StagingFolder); - private static string iconPath => Path.Combine(solutionPath, ProjectName, IconName); - - private static string nupkgFilename(string ver) => $"{PackageName}.{ver}.nupkg"; - private static string nupkgDistroFilename(string ver) => $"{PackageName}-{ver}-full.nupkg"; - - private static readonly Stopwatch sw = new Stopwatch(); - - private static string codeSigningPassword; - - private static bool interactive; - - public static void Main(string[] args) - { - interactive = args.Length == 0; - - displayHeader(); - - findSolutionPath(); - - if (!Directory.Exists(ReleasesFolder)) - { - write("WARNING: No release directory found. Make sure you want this!", ConsoleColor.Yellow); - Directory.CreateDirectory(ReleasesFolder); - } - - checkGitHubReleases(); - - refreshDirectory(StagingFolder); - - //increment build number until we have a unique one. - string verBase = DateTime.Now.ToString("yyyy.Mdd."); - int increment = 0; - while (Directory.GetFiles(ReleasesFolder, $"*{verBase}{increment}*").Any()) - increment++; - - string version = $"{verBase}{increment}"; - - Console.ForegroundColor = ConsoleColor.White; - Console.Write($"Ready to deploy {version}!"); - pauseIfInteractive(); - - sw.Start(); - - if (!string.IsNullOrEmpty(CodeSigningCertificate)) - { - Console.Write("Enter code signing password: "); - codeSigningPassword = args.Length > 0 ? args[0] : readLineMasked(); - } - - write("Updating AssemblyInfo..."); - updateCsprojVersion(version); - updateAppveyorVersion(version); - - write("Running build process..."); - foreach (string targetName in TargetNames.Split(',')) - runCommand(msbuild_path, $"/v:quiet /m /t:{targetName.Replace('.', '_')} /p:OutputPath={stagingPath};Targets=\"Clean;Build\";Configuration=Release {SolutionName}.sln"); - - write("Creating NuGet deployment package..."); - runCommand(nugetPath, $"pack {NuSpecName} -Version {version} -Properties Configuration=Deploy -OutputDirectory {stagingPath} -BasePath {stagingPath}"); - - //prune once before checking for files so we can avoid erroring on files which aren't even needed for this build. - pruneReleases(); - - checkReleaseFiles(); - - write("Running squirrel build..."); - runCommand(squirrelPath, $"--releasify {stagingPath}\\{nupkgFilename(version)} --framework-version=net471 --setupIcon {iconPath} --icon {iconPath} {codeSigningCmd} --no-msi"); - - //prune again to clean up before upload. - pruneReleases(); - - //rename setup to install. - File.Copy(Path.Combine(ReleasesFolder, "Setup.exe"), Path.Combine(ReleasesFolder, "install.exe"), true); - File.Delete(Path.Combine(ReleasesFolder, "Setup.exe")); - - uploadBuild(version); - - //reset assemblyinfo. - updateCsprojVersion("0.0.0"); - - write("Done!", ConsoleColor.White); - pauseIfInteractive(); - } - - private static void displayHeader() - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine(); - Console.WriteLine(" Please note that OSU! and PPY are registered trademarks and as such covered by trademark law."); - Console.WriteLine(" Do not distribute builds of this project publicly that make use of these."); - Console.ResetColor(); - Console.WriteLine(); - } - - /// - /// Ensure we have all the files in the release directory which are expected to be there. - /// This should have been accounted for in earlier steps, and just serves as a verification step. - /// - private static void checkReleaseFiles() - { - if (!canGitHub) return; - - var releaseLines = getReleaseLines(); - - //ensure we have all files necessary - foreach (var l in releaseLines) - if (!File.Exists(Path.Combine(ReleasesFolder, l.Filename))) - error($"Local file missing {l.Filename}"); - } - - private static IEnumerable getReleaseLines() => File.ReadAllLines(Path.Combine(ReleasesFolder, "RELEASES")).Select(l => new ReleaseLine(l)); - - private static void pruneReleases() - { - if (!canGitHub) return; - - write("Pruning RELEASES..."); - - var releaseLines = getReleaseLines().ToList(); - - var fulls = releaseLines.Where(l => l.Filename.Contains("-full")).Reverse().Skip(1); - - //remove any FULL releases (except most recent) - foreach (var l in fulls) - { - write($"- Removing old release {l.Filename}", ConsoleColor.Yellow); - File.Delete(Path.Combine(ReleasesFolder, l.Filename)); - releaseLines.Remove(l); - } - - //remove excess deltas - var deltas = releaseLines.Where(l => l.Filename.Contains("-delta")).ToArray(); - if (deltas.Length > keep_delta_count) - { - foreach (var l in deltas.Take(deltas.Length - keep_delta_count)) - { - write($"- Removing old delta {l.Filename}", ConsoleColor.Yellow); - File.Delete(Path.Combine(ReleasesFolder, l.Filename)); - releaseLines.Remove(l); - } - } - - var lines = new List(); - releaseLines.ForEach(l => lines.Add(l.ToString())); - File.WriteAllLines(Path.Combine(ReleasesFolder, "RELEASES"), lines); - } - - private static void uploadBuild(string version) - { - if (!canGitHub || string.IsNullOrEmpty(CodeSigningCertificate)) - return; - - write("Publishing to GitHub..."); - - write($"- Creating release {version}...", ConsoleColor.Yellow); - var req = new JsonWebRequest($"{GitHubApiEndpoint}") - { - Method = HttpMethod.POST, - }; - req.AddRaw(JsonConvert.SerializeObject(new GitHubRelease - { - Name = version, - Draft = true, - PreRelease = true - })); - req.AuthenticatedBlockingPerform(); - - var assetUploadUrl = req.ResponseObject.UploadUrl.Replace("{?name,label}", "?name={0}"); - foreach (var a in Directory.GetFiles(ReleasesFolder).Reverse()) //reverse to upload RELEASES first. - { - write($"- Adding asset {a}...", ConsoleColor.Yellow); - var upload = new WebRequest(assetUploadUrl, Path.GetFileName(a)) - { - Method = HttpMethod.POST, - Timeout = 240000, - ContentType = "application/octet-stream", - }; - - upload.AddRaw(File.ReadAllBytes(a)); - upload.AuthenticatedBlockingPerform(); - } - - openGitHubReleasePage(); - } - - private static void openGitHubReleasePage() => Process.Start(GitHubReleasePage); - - private static bool canGitHub => !string.IsNullOrEmpty(GitHubAccessToken); - - private static void checkGitHubReleases() - { - if (!canGitHub) return; - - write("Checking GitHub releases..."); - var req = new JsonWebRequest>($"{GitHubApiEndpoint}"); - req.AuthenticatedBlockingPerform(); - - var lastRelease = req.ResponseObject.FirstOrDefault(); - - if (lastRelease == null) - return; - - if (lastRelease.Draft) - { - openGitHubReleasePage(); - error("There's a pending draft release! You probably don't want to push a build with this present."); - } - - //there's a previous release for this project. - var assetReq = new JsonWebRequest>($"{GitHubApiEndpoint}/{lastRelease.Id}/assets"); - assetReq.AuthenticatedBlockingPerform(); - var assets = assetReq.ResponseObject; - - //make sure our RELEASES file is the same as the last build on the server. - var releaseAsset = assets.FirstOrDefault(a => a.Name == "RELEASES"); - - //if we don't have a RELEASES asset then the previous release likely wasn't a Squirrel one. - if (releaseAsset == null) return; - - write($"Last GitHub release was {lastRelease.Name}."); - - bool requireDownload = false; - - if (!File.Exists(Path.Combine(ReleasesFolder, nupkgDistroFilename(lastRelease.Name)))) - { - write("Last version's package not found locally.", ConsoleColor.Red); - requireDownload = true; - } - else - { - var lastReleases = new RawFileWebRequest($"{GitHubApiEndpoint}/assets/{releaseAsset.Id}"); - lastReleases.AuthenticatedBlockingPerform(); - if (File.ReadAllText(Path.Combine(ReleasesFolder, "RELEASES")) != lastReleases.ResponseString) - { - write("Server's RELEASES differed from ours.", ConsoleColor.Red); - requireDownload = true; - } - } - - if (!requireDownload) return; - - write("Refreshing local releases directory..."); - refreshDirectory(ReleasesFolder); - - foreach (var a in assets) - { - if (a.Name.EndsWith(".exe")) continue; - - write($"- Downloading {a.Name}...", ConsoleColor.Yellow); - new FileWebRequest(Path.Combine(ReleasesFolder, a.Name), $"{GitHubApiEndpoint}/assets/{a.Id}").AuthenticatedBlockingPerform(); - } - } - - private static void refreshDirectory(string directory) - { - if (Directory.Exists(directory)) - Directory.Delete(directory, true); - Directory.CreateDirectory(directory); - } - - private static void updateCsprojVersion(string version) - { - var toUpdate = new[] { "", "" }; - string file = Path.Combine(ProjectName, $"{ProjectName}.csproj"); - - var l1 = File.ReadAllLines(file); - List l2 = new List(); - foreach (var l in l1) - { - string line = l; - - foreach (var tag in toUpdate) - { - int startIndex = l.IndexOf(tag, StringComparison.InvariantCulture); - if (startIndex == -1) - continue; - startIndex += tag.Length; - - int endIndex = l.IndexOf("<", startIndex, StringComparison.InvariantCulture); - line = $"{l.Substring(0, startIndex)}{version}{l.Substring(endIndex)}"; - } - - l2.Add(line); - } - - File.WriteAllLines(file, l2); - } - - /// - /// Find the base path of the active solution (git checkout location) - /// - private static void findSolutionPath() - { - string path = Path.GetDirectoryName(Environment.CommandLine.Replace("\"", "").Trim()); - - if (string.IsNullOrEmpty(path)) - path = Environment.CurrentDirectory; - - while (!File.Exists(Path.Combine(path, $"{SolutionName}.sln"))) - path = path.Remove(path.LastIndexOf(Path.DirectorySeparatorChar)); - path += Path.DirectorySeparatorChar; - - Environment.CurrentDirectory = path; - } - - private static bool runCommand(string command, string args) - { - var psi = new ProcessStartInfo(command, args) - { - WorkingDirectory = solutionPath, - CreateNoWindow = true, - RedirectStandardOutput = true, - RedirectStandardError = true, - UseShellExecute = false, - WindowStyle = ProcessWindowStyle.Hidden - }; - - Process p = Process.Start(psi); - if (p == null) return false; - - string output = p.StandardOutput.ReadToEnd(); - output += p.StandardError.ReadToEnd(); - - if (p.ExitCode == 0) return true; - - write(output); - error($"Command {command} {args} failed!"); - return false; - } - - private static string readLineMasked() - { - var fg = Console.ForegroundColor; - Console.ForegroundColor = Console.BackgroundColor; - var ret = Console.ReadLine(); - Console.ForegroundColor = fg; - - return ret; - } - - private static void error(string message) - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine($"FATAL ERROR: {message}"); - - pauseIfInteractive(); - Environment.Exit(-1); - } - - private static void pauseIfInteractive() - { - if (interactive) - Console.ReadLine(); - else - Console.WriteLine(); - } - - private static bool updateAppveyorVersion(string version) - { - try - { - using (PowerShell ps = PowerShell.Create()) - { - ps.AddScript($"Update-AppveyorBuild -Version \"{version}\""); - ps.Invoke(); - } - return true; - } - catch - { - // we don't have appveyor and don't care - } - - return false; - } - - private static void write(string message, ConsoleColor col = ConsoleColor.Gray) - { - if (sw.ElapsedMilliseconds > 0) - { - Console.ForegroundColor = ConsoleColor.Green; - Console.Write(sw.ElapsedMilliseconds.ToString().PadRight(8)); - } - Console.ForegroundColor = col; - Console.WriteLine(message); - } - - public static void AuthenticatedBlockingPerform(this WebRequest r) - { - r.AddHeader("Authorization", $"token {GitHubAccessToken}"); - r.Perform(); - } - } - - internal class RawFileWebRequest : WebRequest - { - public RawFileWebRequest(string url) : base(url) - { - } - - protected override string Accept => "application/octet-stream"; - } - - internal class ReleaseLine - { - public string Hash; - public string Filename; - public int Filesize; - - public ReleaseLine(string line) - { - var split = line.Split(' '); - Hash = split[0]; - Filename = split[1]; - Filesize = int.Parse(split[2]); - } - - public override string ToString() => $"{Hash} {Filename} {Filesize}"; - } -} diff --git a/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj b/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj deleted file mode 100644 index 063fb89918..0000000000 --- a/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj +++ /dev/null @@ -1,19 +0,0 @@ - - - - net471 - Exe - AnyCPU - true - - - - - - - - - - - - \ No newline at end of file diff --git a/osu.Desktop/Overlays/VersionManager.cs b/osu.Desktop/Overlays/VersionManager.cs index b984c0bbba..d061aa8423 100644 --- a/osu.Desktop/Overlays/VersionManager.cs +++ b/osu.Desktop/Overlays/VersionManager.cs @@ -3,7 +3,6 @@ using System.Diagnostics; using osu.Framework.Allocation; -using osu.Framework.Development; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; @@ -14,6 +13,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Overlays; using osu.Game.Overlays.Notifications; +using osu.Game.Utils; using OpenTK; using OpenTK.Graphics; diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index af027da2fc..766f36fa74 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -1,7 +1,7 @@  - net471;netcoreapp2.0 + net471;netcoreapp2.1 WinExe AnyCPU true @@ -20,9 +20,6 @@ osu.Desktop.Program - - - @@ -38,8 +35,4 @@ - - - - \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch.Tests/.vscode/launch.json b/osu.Game.Rulesets.Catch.Tests/.vscode/launch.json index eb80f4474c..2a82d65014 100644 --- a/osu.Game.Rulesets.Catch.Tests/.vscode/launch.json +++ b/osu.Game.Rulesets.Catch.Tests/.vscode/launch.json @@ -22,7 +22,7 @@ }, "type": "mono", "request": "launch", - "program": "${workspaceRoot}/bin/Debug/net471/osu.Game.Rulesets.Catch.Tests.exe", + "program": "${workspaceRoot}/bin/Release/net471/osu.Game.Rulesets.Catch.Tests.exe", "cwd": "${workspaceRoot}", "preLaunchTask": "Build (Release, msbuild)", "runtimeExecutable": null, @@ -30,12 +30,12 @@ "console": "internalConsole" }, { - "name": "VisualTests (Debug, netcoreapp2.0)", + "name": "VisualTests (Debug, netcoreapp2.1)", "type": "coreclr", "request": "launch", "program": "dotnet", "args": [ - "${workspaceRoot}/bin/Debug/netcoreapp2.0/osu.Game.Rulesets.Catch.Tests.dll" + "${workspaceRoot}/bin/Debug/netcoreapp2.1/osu.Game.Rulesets.Catch.Tests.dll" ], "cwd": "${workspaceRoot}", "preLaunchTask": "Build (Debug, dotnet)", @@ -43,12 +43,12 @@ "console": "internalConsole" }, { - "name": "VisualTests (Release, netcoreapp2.0)", + "name": "VisualTests (Release, netcoreapp2.1)", "type": "coreclr", "request": "launch", "program": "dotnet", "args": [ - "${workspaceRoot}/bin/Debug/netcoreapp2.0/osu.Game.Rulesets.Catch.Tests.dll" + "${workspaceRoot}/bin/Release/netcoreapp2.1/osu.Game.Rulesets.Catch.Tests.dll" ], "cwd": "${workspaceRoot}", "preLaunchTask": "Build (Release, dotnet)", diff --git a/osu.Game.Rulesets.Catch.Tests/.vscode/tasks.json b/osu.Game.Rulesets.Catch.Tests/.vscode/tasks.json index 41ae88f425..6c6d562512 100644 --- a/osu.Game.Rulesets.Catch.Tests/.vscode/tasks.json +++ b/osu.Game.Rulesets.Catch.Tests/.vscode/tasks.json @@ -40,7 +40,7 @@ "build", "--no-restore", "osu.Game.Rulesets.Catch.Tests.csproj", - "/p:TargetFramework=netcoreapp2.0", + "/p:TargetFramework=netcoreapp2.1", "/p:GenerateFullPaths=true", "/m", "/verbosity:m" @@ -56,7 +56,7 @@ "build", "--no-restore", "osu.Game.Rulesets.Catch.Tests.csproj", - "/p:TargetFramework=netcoreapp2.0", + "/p:TargetFramework=netcoreapp2.1", "/p:Configuration=Release", "/p:GenerateFullPaths=true", "/m", @@ -75,7 +75,7 @@ "problemMatcher": [] }, { - "label": "Restore (netcoreapp2.0)", + "label": "Restore (netcoreapp2.1)", "type": "shell", "command": "dotnet", "args": [ diff --git a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj index 3797edde61..93fa2c4d67 100644 --- a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj +++ b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj @@ -2,7 +2,7 @@ WinExe - netcoreapp2.0;net471 + netcoreapp2.1;net471 diff --git a/osu.Game.Rulesets.Mania.Tests/.vscode/launch.json b/osu.Game.Rulesets.Mania.Tests/.vscode/launch.json index fceb403f30..bc41d4ccf9 100644 --- a/osu.Game.Rulesets.Mania.Tests/.vscode/launch.json +++ b/osu.Game.Rulesets.Mania.Tests/.vscode/launch.json @@ -22,7 +22,7 @@ }, "type": "mono", "request": "launch", - "program": "${workspaceRoot}/bin/Debug/net471/osu.Game.Rulesets.Mania.Tests.exe", + "program": "${workspaceRoot}/bin/Release/net471/osu.Game.Rulesets.Mania.Tests.exe", "cwd": "${workspaceRoot}", "preLaunchTask": "Build (Release, msbuild)", "runtimeExecutable": null, @@ -30,12 +30,12 @@ "console": "internalConsole" }, { - "name": "VisualTests (Debug, netcoreapp2.0)", + "name": "VisualTests (Debug, netcoreapp2.1)", "type": "coreclr", "request": "launch", "program": "dotnet", "args": [ - "${workspaceRoot}/bin/Debug/netcoreapp2.0/osu.Game.Rulesets.Mania.Tests.dll" + "${workspaceRoot}/bin/Debug/netcoreapp2.1/osu.Game.Rulesets.Mania.Tests.dll" ], "cwd": "${workspaceRoot}", "preLaunchTask": "Build (Debug, dotnet)", @@ -43,12 +43,12 @@ "console": "internalConsole" }, { - "name": "VisualTests (Release, netcoreapp2.0)", + "name": "VisualTests (Release, netcoreapp2.1)", "type": "coreclr", "request": "launch", "program": "dotnet", "args": [ - "${workspaceRoot}/bin/Debug/netcoreapp2.0/osu.Game.Rulesets.Mania.Tests.dll" + "${workspaceRoot}/bin/Release/netcoreapp2.1/osu.Game.Rulesets.Mania.Tests.dll" ], "cwd": "${workspaceRoot}", "preLaunchTask": "Build (Release, dotnet)", diff --git a/osu.Game.Rulesets.Mania.Tests/.vscode/tasks.json b/osu.Game.Rulesets.Mania.Tests/.vscode/tasks.json index b04b068b0d..7fc2f7b2ef 100644 --- a/osu.Game.Rulesets.Mania.Tests/.vscode/tasks.json +++ b/osu.Game.Rulesets.Mania.Tests/.vscode/tasks.json @@ -40,7 +40,7 @@ "build", "--no-restore", "osu.Game.Rulesets.Mania.Tests.csproj", - "/p:TargetFramework=netcoreapp2.0", + "/p:TargetFramework=netcoreapp2.1", "/p:GenerateFullPaths=true", "/m", "/verbosity:m" @@ -56,7 +56,7 @@ "build", "--no-restore", "osu.Game.Rulesets.Mania.Tests.csproj", - "/p:TargetFramework=netcoreapp2.0", + "/p:TargetFramework=netcoreapp2.1", "/p:Configuration=Release", "/p:GenerateFullPaths=true", "/m", @@ -75,7 +75,7 @@ "problemMatcher": [] }, { - "label": "Restore (netcoreapp2.0)", + "label": "Restore (netcoreapp2.1)", "type": "shell", "command": "dotnet", "args": [ diff --git a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj index e90155568e..77504fdc3c 100644 --- a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj +++ b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj @@ -2,7 +2,7 @@ WinExe - netcoreapp2.0;net471 + netcoreapp2.1;net471 diff --git a/osu.Game.Rulesets.Osu.Tests/.vscode/launch.json b/osu.Game.Rulesets.Osu.Tests/.vscode/launch.json index 714fb6db6f..13aba025fd 100644 --- a/osu.Game.Rulesets.Osu.Tests/.vscode/launch.json +++ b/osu.Game.Rulesets.Osu.Tests/.vscode/launch.json @@ -22,7 +22,7 @@ }, "type": "mono", "request": "launch", - "program": "${workspaceRoot}/bin/Debug/net471/osu.Game.Rulesets.Osu.Tests.exe", + "program": "${workspaceRoot}/bin/Release/net471/osu.Game.Rulesets.Osu.Tests.exe", "cwd": "${workspaceRoot}", "preLaunchTask": "Build (Release, msbuild)", "runtimeExecutable": null, @@ -30,12 +30,12 @@ "console": "internalConsole" }, { - "name": "VisualTests (Debug, netcoreapp2.0)", + "name": "VisualTests (Debug, netcoreapp2.1)", "type": "coreclr", "request": "launch", "program": "dotnet", "args": [ - "${workspaceRoot}/bin/Debug/netcoreapp2.0/osu.Game.Rulesets.Osu.Tests.dll" + "${workspaceRoot}/bin/Debug/netcoreapp2.1/osu.Game.Rulesets.Osu.Tests.dll" ], "cwd": "${workspaceRoot}", "preLaunchTask": "Build (Debug, dotnet)", @@ -43,12 +43,12 @@ "console": "internalConsole" }, { - "name": "VisualTests (Release, netcoreapp2.0)", + "name": "VisualTests (Release, netcoreapp2.1)", "type": "coreclr", "request": "launch", "program": "dotnet", "args": [ - "${workspaceRoot}/bin/Debug/netcoreapp2.0/osu.Game.Rulesets.Osu.Tests.dll" + "${workspaceRoot}/bin/Release/netcoreapp2.1/osu.Game.Rulesets.Osu.Tests.dll" ], "cwd": "${workspaceRoot}", "preLaunchTask": "Build (Release, dotnet)", diff --git a/osu.Game.Rulesets.Osu.Tests/.vscode/tasks.json b/osu.Game.Rulesets.Osu.Tests/.vscode/tasks.json index 657fe07e1a..62cf51382f 100644 --- a/osu.Game.Rulesets.Osu.Tests/.vscode/tasks.json +++ b/osu.Game.Rulesets.Osu.Tests/.vscode/tasks.json @@ -40,7 +40,7 @@ "build", "--no-restore", "osu.Game.Rulesets.Osu.Tests.csproj", - "/p:TargetFramework=netcoreapp2.0", + "/p:TargetFramework=netcoreapp2.1", "/p:GenerateFullPaths=true", "/m", "/verbosity:m" @@ -56,7 +56,7 @@ "build", "--no-restore", "osu.Game.Rulesets.Osu.Tests.csproj", - "/p:TargetFramework=netcoreapp2.0", + "/p:TargetFramework=netcoreapp2.1", "/p:Configuration=Release", "/p:GenerateFullPaths=true", "/m", @@ -75,7 +75,7 @@ "problemMatcher": [] }, { - "label": "Restore (netcoreapp2.0)", + "label": "Restore (netcoreapp2.1)", "type": "shell", "command": "dotnet", "args": [ diff --git a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj index 1695ceacee..c5d9b26145 100644 --- a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj +++ b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj @@ -2,7 +2,7 @@ WinExe - netcoreapp2.0;net471 + netcoreapp2.1;net471 diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs index e72d667c0b..4220b72b16 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs @@ -7,33 +7,34 @@ using System.Linq; using osu.Framework.Graphics; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Osu.Objects.Drawables; namespace osu.Game.Rulesets.Osu.Mods { - public class OsuModHidden : ModHidden, IApplicableToDrawableHitObjects + public class OsuModHidden : ModHidden { public override string Description => @"Play with no approach circles and fading circles/sliders."; public override double ScoreMultiplier => 1.06; - private const double fade_in_duration_multiplier = 0.4; private const double fade_out_duration_multiplier = 0.3; - public void ApplyToDrawableHitObjects(IEnumerable drawables) + public override void ApplyToDrawableHitObjects(IEnumerable drawables) { + void adjustFadeIn(OsuHitObject h) => h.TimeFadein = h.TimePreempt * fade_in_duration_multiplier; + foreach (var d in drawables.OfType()) { - d.ApplyCustomUpdateState += ApplyHiddenState; - - d.HitObject.TimeFadein = d.HitObject.TimePreempt * fade_in_duration_multiplier; + adjustFadeIn(d.HitObject); foreach (var h in d.HitObject.NestedHitObjects.OfType()) - h.TimeFadein = h.TimePreempt * fade_in_duration_multiplier; + adjustFadeIn(h); } + + base.ApplyToDrawableHitObjects(drawables); } - protected void ApplyHiddenState(DrawableHitObject drawable, ArmedState state) + protected override void ApplyHiddenState(DrawableHitObject drawable, ArmedState state) { if (!(drawable is DrawableOsuHitObject d)) return; diff --git a/osu.Game.Rulesets.Taiko.Tests/.vscode/launch.json b/osu.Game.Rulesets.Taiko.Tests/.vscode/launch.json index e1df54e99b..df49e177dc 100644 --- a/osu.Game.Rulesets.Taiko.Tests/.vscode/launch.json +++ b/osu.Game.Rulesets.Taiko.Tests/.vscode/launch.json @@ -22,7 +22,7 @@ }, "type": "mono", "request": "launch", - "program": "${workspaceRoot}/bin/Debug/net471/osu.Game.Rulesets.Taiko.Tests.exe", + "program": "${workspaceRoot}/bin/Release/net471/osu.Game.Rulesets.Taiko.Tests.exe", "cwd": "${workspaceRoot}", "preLaunchTask": "Build (Release, msbuild)", "runtimeExecutable": null, @@ -30,12 +30,12 @@ "console": "internalConsole" }, { - "name": "VisualTests (Debug, netcoreapp2.0)", + "name": "VisualTests (Debug, netcoreapp2.1)", "type": "coreclr", "request": "launch", "program": "dotnet", "args": [ - "${workspaceRoot}/bin/Debug/netcoreapp2.0/osu.Game.Rulesets.Taiko.Tests.dll" + "${workspaceRoot}/bin/Debug/netcoreapp2.1/osu.Game.Rulesets.Taiko.Tests.dll" ], "cwd": "${workspaceRoot}", "preLaunchTask": "Build (Debug, dotnet)", @@ -43,12 +43,12 @@ "console": "internalConsole" }, { - "name": "VisualTests (Release, netcoreapp2.0)", + "name": "VisualTests (Release, netcoreapp2.1)", "type": "coreclr", "request": "launch", "program": "dotnet", "args": [ - "${workspaceRoot}/bin/Debug/netcoreapp2.0/osu.Game.Rulesets.Taiko.Tests.dll" + "${workspaceRoot}/bin/Release/netcoreapp2.1/osu.Game.Rulesets.Taiko.Tests.dll" ], "cwd": "${workspaceRoot}", "preLaunchTask": "Build (Release, dotnet)", diff --git a/osu.Game.Rulesets.Taiko.Tests/.vscode/tasks.json b/osu.Game.Rulesets.Taiko.Tests/.vscode/tasks.json index 8bdbcd8e8e..7c8beed00f 100644 --- a/osu.Game.Rulesets.Taiko.Tests/.vscode/tasks.json +++ b/osu.Game.Rulesets.Taiko.Tests/.vscode/tasks.json @@ -40,7 +40,7 @@ "build", "--no-restore", "osu.Game.Rulesets.Taiko.Tests.csproj", - "/p:TargetFramework=netcoreapp2.0", + "/p:TargetFramework=netcoreapp2.1", "/p:GenerateFullPaths=true", "/m", "/verbosity:m" @@ -56,7 +56,7 @@ "build", "--no-restore", "osu.Game.Rulesets.Taiko.Tests.csproj", - "/p:TargetFramework=netcoreapp2.0", + "/p:TargetFramework=netcoreapp2.1", "/p:Configuration=Release", "/p:GenerateFullPaths=true", "/m", @@ -75,7 +75,7 @@ "problemMatcher": [] }, { - "label": "Restore (netcoreapp2.0)", + "label": "Restore (netcoreapp2.1)", "type": "shell", "command": "dotnet", "args": [ diff --git a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj index 1221584a2b..dea34d25e7 100644 --- a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj +++ b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj @@ -2,7 +2,7 @@ WinExe - netcoreapp2.0;net471 + netcoreapp2.1;net471 diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index 586217a05f..1c9696901c 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -264,7 +264,8 @@ namespace osu.Game.Tests.Beatmaps.IO private string createTemporaryBeatmap() { - var temp = new FileInfo(osz_path).CopyTo(Path.GetTempFileName(), true).FullName; + var temp = Path.GetTempFileName() + ".osz"; + File.Copy(osz_path, temp, true); Assert.IsTrue(File.Exists(temp)); return temp; } @@ -344,12 +345,12 @@ namespace osu.Game.Tests.Beatmaps.IO private void waitForOrAssert(Func result, string failureMessage, int timeout = 60000) { - Action waitAction = () => + Task task = Task.Run(() => { while (!result()) Thread.Sleep(200); - }; + }); - Assert.IsTrue(waitAction.BeginInvoke(null, null).AsyncWaitHandle.WaitOne(timeout), failureMessage); + Assert.IsTrue(task.Wait(timeout), failureMessage); } } } diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs b/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs index f819e882d9..4679fca855 100644 --- a/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs +++ b/osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs @@ -65,11 +65,7 @@ namespace osu.Game.Tests.Visual carousel.SelectionChanged = s => currentSelection = s; - AddStep("Load Beatmaps", () => { carousel.BeatmapSets = beatmapSets; }); - - bool changed = false; - carousel.BeatmapSetsChanged = () => changed = true; - AddUntilStep(() => changed, "Wait for load"); + loadBeatmaps(beatmapSets); testTraversal(); testFiltering(); @@ -84,6 +80,17 @@ namespace osu.Game.Tests.Visual testCarouselRootIsRandom(); } + private void loadBeatmaps(List beatmapSets) + { + bool changed = false; + AddStep($"Load {beatmapSets.Count} Beatmaps", () => + { + carousel.BeatmapSetsChanged = () => changed = true; + carousel.BeatmapSets = beatmapSets; + }); + AddUntilStep(() => changed, "Wait for load"); + } + private void ensureRandomFetchSuccess() => AddAssert("ensure prev random fetch worked", () => selectedSets.Peek() == carousel.SelectedBeatmapSet); @@ -423,7 +430,7 @@ namespace osu.Game.Tests.Visual for (int i = 1; i <= 50; i++) beatmapSets.Add(createTestBeatmapSet(i)); - AddStep("Load 50 Beatmaps", () => { carousel.BeatmapSets = beatmapSets; }); + loadBeatmaps(beatmapSets); advanceSelection(direction: 1, diff: false); checkNonmatchingFilter(); checkNonmatchingFilter(); diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs b/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs index 0d3e08154f..996c3b8695 100644 --- a/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs +++ b/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs @@ -53,7 +53,7 @@ namespace osu.Game.Tests.Visual AddStep("show", () => { infoWedge.State = Visibility.Visible; - infoWedge.UpdateBeatmap(beatmap); + infoWedge.Beatmap = beatmap; }); // select part is redundant, but wait for load isn't @@ -133,7 +133,7 @@ namespace osu.Game.Tests.Visual AddStep($"select {b.Metadata.Title} beatmap", () => { infoBefore = infoWedge.Info; - infoWedge.UpdateBeatmap(beatmap.Value = new TestWorkingBeatmap(b)); + infoWedge.Beatmap = beatmap.Value = new TestWorkingBeatmap(b); }); AddUntilStep(() => infoWedge.Info != infoBefore, "wait for async load"); @@ -144,7 +144,7 @@ namespace osu.Game.Tests.Visual AddStep("select null beatmap", () => { beatmap.Value = beatmap.Default; - infoWedge.UpdateBeatmap(beatmap); + infoWedge.Beatmap = beatmap; }); } diff --git a/osu.Game.Tests/Visual/TestCaseLoaderAnimation.cs b/osu.Game.Tests/Visual/TestCaseLoaderAnimation.cs new file mode 100644 index 0000000000..600784f8db --- /dev/null +++ b/osu.Game.Tests/Visual/TestCaseLoaderAnimation.cs @@ -0,0 +1,115 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; +using osu.Game.Screens; +using osu.Game.Screens.Menu; +using OpenTK.Graphics; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseLoaderAnimation : OsuTestCase + { + private TestLoader loader; + + protected override void LoadComplete() + { + base.LoadComplete(); + + // required to preload the logo in a headless run (so it doesn't delay the loading itself). + Add(new OsuLogo()); + + bool logoVisible = false; + AddStep("almost instant display", () => Child = loader = new TestLoader(250)); + AddUntilStep(() => + { + logoVisible = loader.Logo?.Alpha > 0; + return loader.Logo != null && loader.ScreenLoaded; + }, "loaded"); + AddAssert("logo not visible", () => !logoVisible); + + AddStep("short load", () => Child = loader = new TestLoader(800)); + AddUntilStep(() => + { + logoVisible = loader.Logo?.Alpha > 0; + return loader.Logo != null && loader.ScreenLoaded; + }, "loaded"); + AddAssert("logo visible", () => logoVisible); + AddUntilStep(() => loader.Logo?.Alpha == 0, "logo gone"); + + AddStep("longer load", () => Child = loader = new TestLoader(1400)); + AddUntilStep(() => + { + logoVisible = loader.Logo?.Alpha > 0; + return loader.Logo != null && loader.ScreenLoaded; + }, "loaded"); + AddAssert("logo visible", () => logoVisible); + AddUntilStep(() => loader.Logo?.Alpha == 0, "logo gone"); + } + + private class TestLoader : Loader + { + private readonly double delay; + + public OsuLogo Logo; + private TestScreen screen; + + public bool ScreenLoaded => screen.IsCurrentScreen; + + public TestLoader(double delay) + { + this.delay = delay; + } + + protected override void LogoArriving(OsuLogo logo, bool resuming) + { + Logo = logo; + base.LogoArriving(logo, resuming); + } + + protected override OsuScreen CreateLoadableScreen() => screen = new TestScreen(); + protected override ShaderPrecompiler CreateShaderPrecompiler() => new TestShaderPrecompiler(delay); + + private class TestShaderPrecompiler : ShaderPrecompiler + { + private readonly double delay; + private double startTime; + + public TestShaderPrecompiler(double delay) + { + this.delay = delay; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + startTime = Time.Current; + } + + protected override bool AllLoaded => Time.Current > startTime + delay; + } + + private class TestScreen : OsuScreen + { + public TestScreen() + { + Child = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.DarkSlateGray, + Alpha = 0, + }; + } + + protected override void LogoArriving(OsuLogo logo, bool resuming) + { + base.LogoArriving(logo, resuming); + Child.FadeInFromZero(200); + } + } + } + } +} diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 057c2c2de1..532915100b 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -2,7 +2,7 @@ WinExe - netcoreapp2.0;net471 + netcoreapp2.1;net471 diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index efc0279aa0..806bcc4132 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -170,7 +170,7 @@ namespace osu.Game.Beatmaps { if (error is OperationCanceledException) return; - downloadNotification.State = ProgressNotificationState.Completed; + downloadNotification.State = ProgressNotificationState.Cancelled; Logger.Error(error, "Beatmap download failed!"); currentDownloads.Remove(request); }; diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index b3082e49de..597960c352 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -82,6 +82,8 @@ namespace osu.Game.Configuration Set(OsuSetting.SpeedChangeVisualisation, SpeedChangeVisualisationMethod.Sequential); + Set(OsuSetting.IncreaseFirstObjectVisibility, true); + // Update Set(OsuSetting.ReleaseStream, ReleaseStream.Lazer); @@ -144,6 +146,7 @@ namespace osu.Game.Configuration ScreenshotCaptureMenuCursor, SongSelectRightMouseScroll, BeatmapSkins, - BeatmapHitsounds + BeatmapHitsounds, + IncreaseFirstObjectVisibility } } diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 7741fe9ab3..cbf0df3227 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using Microsoft.EntityFrameworkCore; +using osu.Framework.IO.File; using osu.Framework.Logging; using osu.Framework.Platform; using osu.Game.IO; @@ -364,7 +365,7 @@ namespace osu.Game.Database using (Stream s = reader.GetStream(file)) fileInfos.Add(new TFileModel { - Filename = file, + Filename = FileSafety.PathSanitise(file), FileInfo = files.Add(s) }); diff --git a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs index 11a2034a8f..0186a170c9 100644 --- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs +++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Input; using OpenTK; using osu.Framework.Configuration; +using osu.Game.Overlays; namespace osu.Game.Graphics.Containers { @@ -16,13 +17,13 @@ namespace osu.Game.Graphics.Containers private SampleChannel samplePopIn; private SampleChannel samplePopOut; - private readonly BindableBool allowOpeningOverlays = new BindableBool(true); + protected readonly Bindable OverlayActivationMode = new Bindable(OverlayActivation.All); [BackgroundDependencyLoader(true)] private void load(OsuGame osuGame, AudioManager audio) { if (osuGame != null) - allowOpeningOverlays.BindTo(osuGame.AllowOpeningOverlays); + OverlayActivationMode.BindTo(osuGame.OverlayActivationMode); samplePopIn = audio.Sample.Get(@"UI/overlay-pop-in"); samplePopOut = audio.Sample.Get(@"UI/overlay-pop-out"); @@ -52,20 +53,18 @@ namespace osu.Game.Graphics.Containers private void onStateChanged(Visibility visibility) { - if (allowOpeningOverlays) + switch (visibility) { - switch (visibility) - { - case Visibility.Visible: + case Visibility.Visible: + if (OverlayActivationMode != OverlayActivation.Disabled) samplePopIn?.Play(); - break; - case Visibility.Hidden: - samplePopOut?.Play(); - break; - } + else + State = Visibility.Hidden; + break; + case Visibility.Hidden: + samplePopOut?.Play(); + break; } - else - State = Visibility.Hidden; } } } diff --git a/osu.Game/IO/Archives/LegacyFilesystemReader.cs b/osu.Game/IO/Archives/LegacyFilesystemReader.cs index 7871f1439a..dcaaf41c77 100644 --- a/osu.Game/IO/Archives/LegacyFilesystemReader.cs +++ b/osu.Game/IO/Archives/LegacyFilesystemReader.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using osu.Framework.IO.File; namespace osu.Game.IO.Archives { @@ -15,19 +14,20 @@ namespace osu.Game.IO.Archives { private readonly string path; - public LegacyFilesystemReader(string path) : base(Path.GetFileName(path)) + public LegacyFilesystemReader(string path) + : base(Path.GetFileName(path)) { - this.path = path; + // re-get full path to standardise with Directory.GetFiles return values below. + this.path = Path.GetFullPath(path); } public override Stream GetStream(string name) => File.OpenRead(Path.Combine(path, name)); public override void Dispose() { - // no-op } - public override IEnumerable Filenames => Directory.GetFiles(path, "*", SearchOption.AllDirectories).Select(f => FileSafety.GetRelativePath(f, path)).ToArray(); + public override IEnumerable Filenames => Directory.GetFiles(path, "*", SearchOption.AllDirectories).Select(f => f.Replace(path, string.Empty).Trim(Path.DirectorySeparatorChar)).ToArray(); public override Stream GetUnderlyingStream() => null; } diff --git a/osu.Game/Online/API/APIRequest.cs b/osu.Game/Online/API/APIRequest.cs index 9af142b9e8..dfd181b98a 100644 --- a/osu.Game/Online/API/APIRequest.cs +++ b/osu.Game/Online/API/APIRequest.cs @@ -73,6 +73,7 @@ namespace osu.Game.Online.API throw new TimeoutException(@"API request timeout hit"); WebRequest = CreateWebRequest(); + WebRequest.Failed += Fail; WebRequest.AllowRetryOnTimeout = false; WebRequest.AddHeader("Authorization", $"Bearer {api.AccessToken}"); diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index a43c1507b6..36c76851c6 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -77,8 +77,7 @@ namespace osu.Game public float ToolbarOffset => Toolbar.Position.Y + Toolbar.DrawHeight; - public readonly BindableBool HideOverlaysOnEnter = new BindableBool(); - public readonly BindableBool AllowOpeningOverlays = new BindableBool(true); + public readonly Bindable OverlayActivationMode = new Bindable(); private OsuScreen screenStack; @@ -94,6 +93,8 @@ namespace osu.Game private SettingsOverlay settings; + private readonly List overlays = new List(); + // todo: move this to SongSelect once Screen has the ability to unsuspend. public readonly Bindable> SelectedMods = new Bindable>(new List()); @@ -106,6 +107,17 @@ namespace osu.Game public void ToggleDirect() => direct.ToggleVisibility(); + /// + /// Close all game-wide overlays. + /// + /// Whether the toolbar should also be hidden. + public void CloseAllOverlays(bool toolbar = true) + { + foreach (var o in overlays) + o.State = Visibility.Hidden; + if (toolbar) Toolbar.State = Visibility.Hidden; + } + private DependencyContainer dependencies; protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) => @@ -251,7 +263,7 @@ namespace osu.Game Depth = -5, OnHome = delegate { - hideAllOverlays(); + CloseAllOverlays(false); intro?.ChildScreen?.MakeCurrent(); }, }, overlayContent.Add); @@ -308,6 +320,8 @@ namespace osu.Game // ensure only one of these overlays are open at once. var singleDisplayOverlays = new OverlayContainer[] { chat, social, direct }; + overlays.AddRange(singleDisplayOverlays); + foreach (var overlay in singleDisplayOverlays) { overlay.StateChanged += state => @@ -323,6 +337,8 @@ namespace osu.Game } var singleDisplaySideOverlays = new OverlayContainer[] { settings, notifications }; + overlays.AddRange(singleDisplaySideOverlays); + foreach (var overlay in singleDisplaySideOverlays) { overlay.StateChanged += state => @@ -339,6 +355,8 @@ namespace osu.Game // eventually informational overlays should be displayed in a stack, but for now let's only allow one to stay open at a time. var informationalOverlays = new OverlayContainer[] { beatmapSetOverlay, userProfile }; + overlays.AddRange(informationalOverlays); + foreach (var overlay in informationalOverlays) { overlay.StateChanged += state => @@ -353,6 +371,11 @@ namespace osu.Game }; } + OverlayActivationMode.ValueChanged += v => + { + if (v != OverlayActivation.All) CloseAllOverlays(); + }; + void updateScreenOffset() { float offset = 0; @@ -367,21 +390,6 @@ namespace osu.Game settings.StateChanged += _ => updateScreenOffset(); notifications.StateChanged += _ => updateScreenOffset(); - - notifications.Enabled.BindTo(AllowOpeningOverlays); - - HideOverlaysOnEnter.ValueChanged += hide => - { - //central game screen change logic. - if (hide) - { - hideAllOverlays(); - musicController.State = Visibility.Hidden; - Toolbar.State = Visibility.Hidden; - } - else - Toolbar.State = Visibility.Visible; - }; } private void forwardLoggedErrorsToNotifications() @@ -498,16 +506,6 @@ namespace osu.Game private OsuScreen currentScreen; private FrameworkConfigManager frameworkConfig; - private void hideAllOverlays() - { - settings.State = Visibility.Hidden; - chat.State = Visibility.Hidden; - direct.State = Visibility.Hidden; - social.State = Visibility.Hidden; - userProfile.State = Visibility.Hidden; - notifications.State = Visibility.Hidden; - } - protected override bool OnExiting() { if (screenStack.ChildScreen == null) return false; diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index f6b3935689..6269fcfa76 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -10,7 +10,6 @@ using System.Reflection; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Configuration; -using osu.Framework.Development; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.IO.Stores; @@ -31,6 +30,7 @@ using osu.Game.IO; using osu.Game.Rulesets; using osu.Game.Rulesets.Scoring; using osu.Game.Skinning; +using DebugUtils = osu.Game.Utils.DebugUtils; namespace osu.Game { @@ -59,8 +59,6 @@ namespace osu.Game protected MenuCursorContainer MenuCursorContainer; - protected override string MainResourceFile => @"osu.Game.Resources.dll"; - private Container content; protected override Container Content => content; @@ -100,6 +98,8 @@ namespace osu.Game [BackgroundDependencyLoader] private void load() { + Resources.AddStore(new DllResourceStore(@"osu.Game.Resources.dll")); + dependencies.Cache(contextFactory = new DatabaseContextFactory(Host)); dependencies.Cache(new LargeTextureStore(new RawTextureLoaderStore(new NamespacedResourceStore(Resources, @"Textures")))); diff --git a/osu.Game/Overlays/BeatmapSet/Info.cs b/osu.Game/Overlays/BeatmapSet/Info.cs index cd0b7386e8..53216ad666 100644 --- a/osu.Game/Overlays/BeatmapSet/Info.cs +++ b/osu.Game/Overlays/BeatmapSet/Info.cs @@ -21,7 +21,7 @@ namespace osu.Game.Overlays.BeatmapSet private const float metadata_width = 225; private const float spacing = 20; - private readonly MetadataSection description, source, tags; + private readonly MetadataSection source, tags; private readonly Box successRateBackground; private readonly SuccessRate successRate; @@ -83,7 +83,7 @@ namespace osu.Game.Overlays.BeatmapSet Child = new Container { RelativeSizeAxes = Axes.Both, - Child = description = new MetadataSection("Description"), + Child = new MetadataSection("Description"), }, }, new Container @@ -135,8 +135,6 @@ namespace osu.Game.Overlays.BeatmapSet private void load(OsuColour colours) { successRateBackground.Colour = colours.GrayE; - source.TextColour = description.TextColour = colours.Gray5; - tags.TextColour = colours.BlueDark; updateDisplay(); } @@ -195,7 +193,7 @@ namespace osu.Game.Overlays.BeatmapSet [BackgroundDependencyLoader] private void load(OsuColour colours) { - header.Colour = colours.Gray5; + header.Colour = textFlow.Colour = colours.Gray5; } } } diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs index 7406a9ec4f..ffe1560627 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs @@ -180,7 +180,7 @@ namespace osu.Game.Overlays.KeyBinding return true; } - protected override bool OnWheel(InputState state) + protected override bool OnScroll(InputState state) { if (HasFocus) { @@ -192,7 +192,7 @@ namespace osu.Game.Overlays.KeyBinding } } - return base.OnWheel(state); + return base.OnScroll(state); } protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs index 09b6022ac5..3dc8f5ec15 100644 --- a/osu.Game/Overlays/NotificationOverlay.cs +++ b/osu.Game/Overlays/NotificationOverlay.cs @@ -22,11 +22,6 @@ namespace osu.Game.Overlays public const float TRANSITION_LENGTH = 600; - /// - /// Whether posted notifications should be processed. - /// - public readonly BindableBool Enabled = new BindableBool(true); - private FlowContainer sections; /// @@ -34,27 +29,6 @@ namespace osu.Game.Overlays /// public Func GetToolbarHeight; - public NotificationOverlay() - { - ScheduledDelegate notificationsEnabler = null; - Enabled.ValueChanged += v => - { - if (!IsLoaded) - { - processingPosts = v; - return; - } - - notificationsEnabler?.Cancel(); - - if (v) - // we want a slight delay before toggling notifications on to avoid the user becoming overwhelmed. - notificationsEnabler = Scheduler.AddDelayed(() => processingPosts = true, 1000); - else - processingPosts = false; - }; - } - [BackgroundDependencyLoader] private void load() { @@ -103,6 +77,29 @@ namespace osu.Game.Overlays }; } + private ScheduledDelegate notificationsEnabler; + private void updateProcessingMode() + { + bool enabled = OverlayActivationMode == OverlayActivation.All || State == Visibility.Visible; + + notificationsEnabler?.Cancel(); + + if (enabled) + // we want a slight delay before toggling notifications on to avoid the user becoming overwhelmed. + notificationsEnabler = Scheduler.AddDelayed(() => processingPosts = true, State == Visibility.Visible ? 0 : 1000); + else + processingPosts = false; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + StateChanged += _ => updateProcessingMode(); + OverlayActivationMode.ValueChanged += _ => updateProcessingMode(); + OverlayActivationMode.TriggerChange(); + } + private int totalCount => sections.Select(c => c.DisplayedCount).Sum(); private int unreadCount => sections.Select(c => c.UnreadCount).Sum(); diff --git a/osu.Game/Overlays/OverlayActivation.cs b/osu.Game/Overlays/OverlayActivation.cs new file mode 100644 index 0000000000..da4e153ce9 --- /dev/null +++ b/osu.Game/Overlays/OverlayActivation.cs @@ -0,0 +1,12 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Overlays +{ + public enum OverlayActivation + { + Disabled, + UserTriggered, + All + } +} diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/ModsSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/ModsSettings.cs new file mode 100644 index 0000000000..a9cefa81da --- /dev/null +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/ModsSettings.cs @@ -0,0 +1,26 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Game.Configuration; + +namespace osu.Game.Overlays.Settings.Sections.Gameplay +{ + public class ModsSettings : SettingsSubsection + { + protected override string Header => "Mods"; + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + Children = new[] + { + new SettingsCheckbox + { + LabelText = "Increase visibility of first object with \"Hidden\" mod", + Bindable = config.GetBindable(OsuSetting.IncreaseFirstObjectVisibility) + }, + }; + } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/GameplaySection.cs b/osu.Game/Overlays/Settings/Sections/GameplaySection.cs index 3851a73901..8add0b01ec 100644 --- a/osu.Game/Overlays/Settings/Sections/GameplaySection.cs +++ b/osu.Game/Overlays/Settings/Sections/GameplaySection.cs @@ -21,7 +21,8 @@ namespace osu.Game.Overlays.Settings.Sections { new GeneralSettings(), new SongSelectSettings(), - new ScrollingSettings() + new ScrollingSettings(), + new ModsSettings(), }; } diff --git a/osu.Game/Overlays/Settings/SettingsFooter.cs b/osu.Game/Overlays/Settings/SettingsFooter.cs index 900f03fe7b..909fc20446 100644 --- a/osu.Game/Overlays/Settings/SettingsFooter.cs +++ b/osu.Game/Overlays/Settings/SettingsFooter.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using osu.Framework.Allocation; -using osu.Framework.Development; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; @@ -12,6 +11,7 @@ using osu.Game.Graphics.Sprites; using osu.Game.Rulesets; using OpenTK; using OpenTK.Graphics; +using DebugUtils = osu.Game.Utils.DebugUtils; namespace osu.Game.Overlays.Settings { diff --git a/osu.Game/Overlays/Toolbar/Toolbar.cs b/osu.Game/Overlays/Toolbar/Toolbar.cs index 424a457110..48d0674b3d 100644 --- a/osu.Game/Overlays/Toolbar/Toolbar.cs +++ b/osu.Game/Overlays/Toolbar/Toolbar.cs @@ -10,6 +10,8 @@ using osu.Framework.Input; using osu.Game.Graphics; using OpenTK; using osu.Framework.Graphics.Shapes; +using osu.Framework.Allocation; +using osu.Framework.Configuration; namespace osu.Game.Overlays.Toolbar { @@ -29,6 +31,8 @@ namespace osu.Game.Overlays.Toolbar private const float alpha_hovering = 0.8f; private const float alpha_normal = 0.6f; + private readonly Bindable overlayActivationMode = new Bindable(OverlayActivation.All); + public Toolbar() { Children = new Drawable[] @@ -76,6 +80,19 @@ namespace osu.Game.Overlays.Toolbar Size = new Vector2(1, HEIGHT); } + [BackgroundDependencyLoader(true)] + private void load(OsuGame osuGame) + { + if (osuGame != null) + overlayActivationMode.BindTo(osuGame.OverlayActivationMode); + + StateChanged += visibility => + { + if (overlayActivationMode == OverlayActivation.Disabled) + State = Visibility.Hidden; + }; + } + public class ToolbarBackground : Container { private readonly Box solidBackground; diff --git a/osu.Game/Rulesets/Mods/IReadFromConfig.cs b/osu.Game/Rulesets/Mods/IReadFromConfig.cs new file mode 100644 index 0000000000..93c9ae0c34 --- /dev/null +++ b/osu.Game/Rulesets/Mods/IReadFromConfig.cs @@ -0,0 +1,15 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Configuration; + +namespace osu.Game.Rulesets.Mods +{ + /// + /// An interface for mods that require reading access to the osu! configuration. + /// + public interface IReadFromConfig + { + void ReadFromConfig(OsuConfigManager config); + } +} diff --git a/osu.Game/Rulesets/Mods/ModHidden.cs b/osu.Game/Rulesets/Mods/ModHidden.cs index b489a665d9..45da628ce8 100644 --- a/osu.Game/Rulesets/Mods/ModHidden.cs +++ b/osu.Game/Rulesets/Mods/ModHidden.cs @@ -1,16 +1,37 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Framework.Configuration; +using osu.Game.Configuration; using osu.Game.Graphics; +using osu.Game.Rulesets.Objects.Drawables; +using System.Collections.Generic; +using System.Linq; namespace osu.Game.Rulesets.Mods { - public abstract class ModHidden : Mod + public abstract class ModHidden : Mod, IReadFromConfig, IApplicableToDrawableHitObjects { public override string Name => "Hidden"; public override string ShortenedName => "HD"; public override FontAwesome Icon => FontAwesome.fa_osu_mod_hidden; public override ModType Type => ModType.DifficultyIncrease; public override bool Ranked => true; + + protected Bindable IncreaseFirstObjectVisibility = new Bindable(); + + public void ReadFromConfig(OsuConfigManager config) + { + IncreaseFirstObjectVisibility = config.GetBindable(OsuSetting.IncreaseFirstObjectVisibility); + } + + public virtual void ApplyToDrawableHitObjects(IEnumerable drawables) + { + // todo: fix ordering of objects so we don't have to do this (#2740). + foreach (var d in drawables.Reverse().Skip(IncreaseFirstObjectVisibility ? 1 : 0)) + d.ApplyCustomUpdateState += ApplyHiddenState; + } + + protected virtual void ApplyHiddenState(DrawableHitObject hitObject, ArmedState state) { } } } diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index 1847b63658..8d267f48e9 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; +using osu.Framework.Logging; using osu.Game.Database; namespace osu.Game.Rulesets @@ -114,8 +115,9 @@ namespace osu.Game.Rulesets var assembly = Assembly.LoadFrom(file); loaded_assemblies[assembly] = assembly.GetTypes().First(t => t.IsPublic && t.IsSubclassOf(typeof(Ruleset))); } - catch (Exception) + catch (Exception e) { + Logger.Error(e, "Failed to load ruleset"); } } } diff --git a/osu.Game/Rulesets/UI/RulesetContainer.cs b/osu.Game/Rulesets/UI/RulesetContainer.cs index e42e74c245..384b71cccc 100644 --- a/osu.Game/Rulesets/UI/RulesetContainer.cs +++ b/osu.Game/Rulesets/UI/RulesetContainer.cs @@ -57,6 +57,7 @@ namespace osu.Game.Rulesets.UI public abstract IEnumerable Objects { get; } private readonly Lazy playfield; + /// /// The playfield. /// @@ -130,7 +131,6 @@ namespace osu.Game.Rulesets.UI HasReplayLoaded.Value = ReplayInputManager.ReplayInputHandler != null; } - /// /// Creates the cursor. May be null if the doesn't provide a custom cursor. /// @@ -194,6 +194,7 @@ namespace osu.Game.Rulesets.UI protected override Container Content => content; private Container content; + private IEnumerable mods; /// /// Whether to assume the beatmap passed into this is for the current ruleset. @@ -216,13 +217,10 @@ namespace osu.Game.Rulesets.UI KeyBindingInputManager = CreateInputManager(); KeyBindingInputManager.RelativeSizeAxes = Axes.Both; - - // Add mods, should always be the last thing applied to give full control to mods - applyMods(Mods); } [BackgroundDependencyLoader] - private void load() + private void load(OsuConfigManager config) { KeyBindingInputManager.Add(content = new Container { @@ -235,6 +233,9 @@ namespace osu.Game.Rulesets.UI if (Cursor != null) KeyBindingInputManager.Add(Cursor); + // Apply mods + applyMods(Mods, config); + loadObjects(); } @@ -242,13 +243,16 @@ namespace osu.Game.Rulesets.UI /// Applies the active mods to this RulesetContainer. /// /// - private void applyMods(IEnumerable mods) + private void applyMods(IEnumerable mods, OsuConfigManager config) { if (mods == null) return; foreach (var mod in mods.OfType>()) mod.ApplyToRulesetContainer(this); + + foreach (var mod in mods.OfType()) + mod.ReadFromConfig(config); } public override void SetReplay(Replay replay) diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs index 58a66a5224..b35616985a 100644 --- a/osu.Game/Rulesets/UI/RulesetInputManager.cs +++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs @@ -121,8 +121,6 @@ namespace osu.Game.Rulesets.UI /// private bool validState; - protected override bool RequiresChildrenUpdate => base.RequiresChildrenUpdate && validState; - private bool isAttached => replayInputHandler != null && !UseParentState; private const int max_catch_up_updates_per_frame = 50; diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index be08fffc77..b71d3aee18 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -182,9 +182,9 @@ namespace osu.Game.Screens.Edit LoadComponentAsync(currentScreen, screenContainer.Add); } - protected override bool OnWheel(InputState state) + protected override bool OnScroll(InputState state) { - if (state.Mouse.WheelDelta > 0) + if (state.Mouse.ScrollDelta.X + state.Mouse.ScrollDelta.Y > 0) clock.SeekBackward(true); else clock.SeekForward(true); diff --git a/osu.Game/Screens/Edit/Screens/Compose/Timeline/ScrollingTimelineContainer.cs b/osu.Game/Screens/Edit/Screens/Compose/Timeline/ScrollingTimelineContainer.cs index 83aa86ba61..2902e74e00 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Timeline/ScrollingTimelineContainer.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Timeline/ScrollingTimelineContainer.cs @@ -123,15 +123,15 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Timeline /// private float? localZoomTarget; - protected override bool OnWheel(InputState state) + protected override bool OnScroll(InputState state) { if (!state.Keyboard.ControlPressed) - return base.OnWheel(state); + return base.OnScroll(state); relativeContentZoomTarget = Content.ToLocalSpace(state.Mouse.NativeState.Position).X / Content.DrawSize.X; localZoomTarget = ToLocalSpace(state.Mouse.NativeState.Position).X; - Zoom += state.Mouse.WheelDelta; + Zoom += state.Mouse.ScrollDelta.Y; return true; } diff --git a/osu.Game/Screens/Loader.cs b/osu.Game/Screens/Loader.cs index fb5c5ca84b..c3b3e747fd 100644 --- a/osu.Game/Screens/Loader.cs +++ b/osu.Game/Screens/Loader.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; @@ -30,43 +29,48 @@ namespace osu.Game.Screens { base.LogoArriving(logo, resuming); + logo.BeatMatching = false; logo.Triangles = false; logo.Origin = Anchor.BottomRight; logo.Anchor = Anchor.BottomRight; logo.Position = new Vector2(-40); logo.Scale = new Vector2(0.2f); - logo.FadeInFromZero(5000, Easing.OutQuint); - } - - private OsuScreen loadScreen; - private ShaderPrecompiler precompiler; - - protected override void OnEntering(Screen last) - { - base.OnEntering(last); - - LoadComponentAsync(precompiler = new ShaderPrecompiler(loadIfReady), Add); - LoadComponentAsync(loadScreen = showDisclaimer ? (OsuScreen)new Disclaimer() : new Intro(), s => loadIfReady()); - } - - private void loadIfReady() - { - if (ChildScreen == loadScreen) return; - - if (loadScreen.LoadState != LoadState.Ready) - return; - - if (!precompiler.FinishedCompiling) - return; - - Push(loadScreen); + logo.Delay(500).FadeInFromZero(1000, Easing.OutQuint); } protected override void LogoSuspending(OsuLogo logo) { base.LogoSuspending(logo); - logo.FadeOut(100); + logo.FadeOut(logo.Alpha * 400); + } + + private OsuScreen loadableScreen; + private ShaderPrecompiler precompiler; + + protected virtual OsuScreen CreateLoadableScreen() => showDisclaimer ? (OsuScreen)new Disclaimer() : new Intro(); + + protected virtual ShaderPrecompiler CreateShaderPrecompiler() => new ShaderPrecompiler(); + + protected override void OnEntering(Screen last) + { + base.OnEntering(last); + + LoadComponentAsync(precompiler = CreateShaderPrecompiler(), Add); + LoadComponentAsync(loadableScreen = CreateLoadableScreen()); + + checkIfLoaded(); + } + + private void checkIfLoaded() + { + if (loadableScreen.LoadState != LoadState.Ready || !precompiler.FinishedCompiling) + { + Schedule(checkIfLoaded); + return; + } + + Push(loadableScreen); } [BackgroundDependencyLoader] @@ -80,16 +84,10 @@ namespace osu.Game.Screens /// public class ShaderPrecompiler : Drawable { - private readonly Action onLoaded; private readonly List loadTargets = new List(); public bool FinishedCompiling { get; private set; } - public ShaderPrecompiler(Action onLoaded) - { - this.onLoaded = onLoaded; - } - [BackgroundDependencyLoader] private void load(ShaderManager manager) { @@ -103,16 +101,17 @@ namespace osu.Game.Screens loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_3, FragmentShaderDescriptor.TEXTURE)); } + protected virtual bool AllLoaded => loadTargets.All(s => s.Loaded); + protected override void Update() { base.Update(); // if our target is null we are done. - if (loadTargets.All(s => s.Loaded)) + if (AllLoaded) { FinishedCompiling = true; Expire(); - onLoaded?.Invoke(); } } } diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index f212bfabf3..42e25aad43 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -8,7 +8,6 @@ using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; -using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -17,6 +16,7 @@ using osu.Framework.Input.Bindings; using osu.Framework.Threading; using osu.Game.Graphics; using osu.Game.Input.Bindings; +using osu.Game.Overlays; using OpenTK; using OpenTK.Graphics; using OpenTK.Input; @@ -27,9 +27,6 @@ namespace osu.Game.Screens.Menu { public event Action StateChanged; - private readonly BindableBool hideOverlaysOnEnter = new BindableBool(); - private readonly BindableBool allowOpeningOverlays = new BindableBool(); - public Action OnEdit; public Action OnExit; public Action OnDirect; @@ -133,15 +130,12 @@ namespace osu.Game.Screens.Menu buttonFlow.AddRange(buttonsTopLevel); } + private OsuGame game; + [BackgroundDependencyLoader(true)] private void load(AudioManager audio, OsuGame game) { - if (game != null) - { - hideOverlaysOnEnter.BindTo(game.HideOverlaysOnEnter); - allowOpeningOverlays.BindTo(game.AllowOpeningOverlays); - } - + this.game = game; sampleBack = audio.Sample.Get(@"Menu/button-back-select"); } @@ -328,18 +322,18 @@ namespace osu.Game.Screens.Menu case MenuState.Initial: logoDelayedAction?.Cancel(); logoDelayedAction = Scheduler.AddDelayed(() => - { - logoTracking = false; + { + logoTracking = false; - hideOverlaysOnEnter.Value = true; - allowOpeningOverlays.Value = false; + if (game != null) + game.OverlayActivationMode.Value = state == MenuState.Exit ? OverlayActivation.Disabled : OverlayActivation.UserTriggered; - logo.ClearTransforms(targetMember: nameof(Position)); - logo.RelativePositionAxes = Axes.Both; + logo.ClearTransforms(targetMember: nameof(Position)); + logo.RelativePositionAxes = Axes.Both; - logo.MoveTo(new Vector2(0.5f), 800, Easing.OutExpo); - logo.ScaleTo(1, 800, Easing.OutExpo); - }, buttonArea.Alpha * 150); + logo.MoveTo(new Vector2(0.5f), 800, Easing.OutExpo); + logo.ScaleTo(1, 800, Easing.OutExpo); + }, buttonArea.Alpha * 150); break; case MenuState.TopLevel: case MenuState.Play: @@ -366,8 +360,11 @@ namespace osu.Game.Screens.Menu if (impact) logo.Impact(); - hideOverlaysOnEnter.Value = false; - allowOpeningOverlays.Value = true; + if (game != null) + { + game.OverlayActivationMode.Value = OverlayActivation.All; + game.Toolbar.State = Visibility.Visible; + } }, 200); break; default: diff --git a/osu.Game/Screens/Menu/Disclaimer.cs b/osu.Game/Screens/Menu/Disclaimer.cs index b8cb7f2a4a..0c70dbf570 100644 --- a/osu.Game/Screens/Menu/Disclaimer.cs +++ b/osu.Game/Screens/Menu/Disclaimer.cs @@ -9,6 +9,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using OpenTK; using OpenTK.Graphics; +using osu.Game.Overlays; namespace osu.Game.Screens.Menu { @@ -19,7 +20,7 @@ namespace osu.Game.Screens.Menu private Color4 iconColour; protected override bool HideOverlaysOnEnter => true; - protected override bool AllowOpeningOverlays => false; + protected override OverlayActivation InitialOverlayActivationMode => OverlayActivation.Disabled; public override bool CursorVisible => false; diff --git a/osu.Game/Screens/Menu/Intro.cs b/osu.Game/Screens/Menu/Intro.cs index c174e2d470..c5bd345a31 100644 --- a/osu.Game/Screens/Menu/Intro.cs +++ b/osu.Game/Screens/Menu/Intro.cs @@ -15,6 +15,7 @@ using osu.Game.IO.Archives; using osu.Game.Screens.Backgrounds; using OpenTK; using OpenTK.Graphics; +using osu.Game.Overlays; namespace osu.Game.Screens.Menu { @@ -32,7 +33,7 @@ namespace osu.Game.Screens.Menu private SampleChannel seeya; protected override bool HideOverlaysOnEnter => true; - protected override bool AllowOpeningOverlays => false; + protected override OverlayActivation InitialOverlayActivationMode => OverlayActivation.Disabled; public override bool CursorVisible => false; @@ -79,31 +80,6 @@ namespace osu.Game.Screens.Menu seeya = audio.Sample.Get(@"seeya"); } - protected override void OnEntering(Screen last) - { - base.OnEntering(last); - - Game.Beatmap.Value = beatmap; - - if (menuVoice) - welcome.Play(); - - Scheduler.AddDelayed(delegate - { - // Only start the current track if it is the menu music. A beatmap's track is started when entering the Main Manu. - if (menuMusic) - track.Start(); - - LoadComponentAsync(mainMenu = new MainMenu()); - - Scheduler.AddDelayed(delegate - { - DidLoadMenu = true; - Push(mainMenu); - }, delay_step_one); - }, delay_step_two); - } - private const double delay_step_one = 2300; private const double delay_step_two = 600; @@ -113,6 +89,29 @@ namespace osu.Game.Screens.Menu { base.LogoArriving(logo, resuming); + if (!resuming) + { + Game.Beatmap.Value = beatmap; + + if (menuVoice) + welcome.Play(); + + Scheduler.AddDelayed(delegate + { + // Only start the current track if it is the menu music. A beatmap's track is started when entering the Main Manu. + if (menuMusic) + track.Start(); + + LoadComponentAsync(mainMenu = new MainMenu()); + + Scheduler.AddDelayed(delegate + { + DidLoadMenu = true; + Push(mainMenu); + }, delay_step_one); + }, delay_step_two); + } + logo.RelativePositionAxes = Axes.Both; logo.Colour = Color4.White; logo.Ripple = false; diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index d5f3b11467..cbdd8b4e8b 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -25,7 +25,6 @@ namespace osu.Game.Screens.Menu private readonly ButtonSystem buttons; protected override bool HideOverlaysOnEnter => buttons.State == MenuState.Initial; - protected override bool AllowOpeningOverlays => buttons.State != MenuState.Initial; protected override bool AllowBackButton => buttons.State != MenuState.Initial; diff --git a/osu.Game/Screens/Menu/MenuSideFlashes.cs b/osu.Game/Screens/Menu/MenuSideFlashes.cs index fae3e72552..c321c98b24 100644 --- a/osu.Game/Screens/Menu/MenuSideFlashes.cs +++ b/osu.Game/Screens/Menu/MenuSideFlashes.cs @@ -24,8 +24,8 @@ namespace osu.Game.Screens.Menu private readonly Bindable beatmap = new Bindable(); - private readonly Box leftBox; - private readonly Box rightBox; + private Box leftBox; + private Box rightBox; private const float amplitude_dead_zone = 0.25f; private const float alpha_multiplier = (1 - amplitude_dead_zone) / 0.55f; @@ -42,27 +42,6 @@ namespace osu.Game.Screens.Menu RelativeSizeAxes = Axes.Both; Anchor = Anchor.Centre; Origin = Anchor.Centre; - Children = new Drawable[] - { - leftBox = new Box - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - RelativeSizeAxes = Axes.Y, - Width = box_width, - Alpha = 0, - Blending = BlendingMode.Additive, - }, - rightBox = new Box - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - RelativeSizeAxes = Axes.Y, - Width = box_width, - Alpha = 0, - Blending = BlendingMode.Additive, - } - }; } [BackgroundDependencyLoader] @@ -72,10 +51,34 @@ namespace osu.Game.Screens.Menu // linear colour looks better in this case, so let's use it for now. Color4 gradientDark = colours.Blue.Opacity(0).ToLinear(); - Color4 gradientLight = colours.Blue.Opacity(0.3f).ToLinear(); + Color4 gradientLight = colours.Blue.Opacity(0.6f).ToLinear(); - leftBox.Colour = ColourInfo.GradientHorizontal(gradientLight, gradientDark); - rightBox.Colour = ColourInfo.GradientHorizontal(gradientDark, gradientLight); + Children = new Drawable[] + { + leftBox = new Box + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + RelativeSizeAxes = Axes.Y, + Width = box_width * 2, + // align off-screen to make sure our edges don't become visible during parallax. + X = -box_width, + Alpha = 0, + Blending = BlendingMode.Additive, + Colour = ColourInfo.GradientHorizontal(gradientLight, gradientDark) + }, + rightBox = new Box + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + RelativeSizeAxes = Axes.Y, + Width = box_width * 2, + X = box_width, + Alpha = 0, + Blending = BlendingMode.Additive, + Colour = ColourInfo.GradientHorizontal(gradientDark, gradientLight) + } + }; } protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) diff --git a/osu.Game/Screens/Menu/OsuLogo.cs b/osu.Game/Screens/Menu/OsuLogo.cs index 42a8dbd5da..16482b0e48 100644 --- a/osu.Game/Screens/Menu/OsuLogo.cs +++ b/osu.Game/Screens/Menu/OsuLogo.cs @@ -64,6 +64,8 @@ namespace osu.Game.Screens.Menu set { colourAndTriangles.FadeTo(value ? 1 : 0, transition_length, Easing.OutQuint); } } + public bool BeatMatching = true; + public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => logoContainer.ReceiveMouseInputAt(screenSpacePos); public bool Ripple @@ -264,6 +266,8 @@ namespace osu.Game.Screens.Menu { base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); + if (!BeatMatching) return; + lastBeatIndex = beatIndex; var beatLength = timingPoint.BeatLength; diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index db9807b9ab..d98aac8f84 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -18,6 +18,8 @@ using osu.Game.Rulesets; using osu.Game.Screens.Menu; using OpenTK; using OpenTK.Input; +using osu.Game.Overlays; +using osu.Framework.Graphics.Containers; namespace osu.Game.Screens { @@ -40,19 +42,19 @@ namespace osu.Game.Screens /// protected virtual BackgroundScreen CreateBackground() => null; - private readonly BindableBool hideOverlaysOnEnter = new BindableBool(); + private Action updateOverlayStates; /// - /// Whether overlays should be hidden when this screen is entered or resumed. + /// Whether all overlays should be hidden when this screen is entered or resumed. /// protected virtual bool HideOverlaysOnEnter => false; - private readonly BindableBool allowOpeningOverlays = new BindableBool(); + protected readonly Bindable OverlayActivationMode = new Bindable(); /// - /// Whether overlays should be able to be opened while this screen is active. + /// Whether overlays should be able to be opened once this screen is entered or resumed. /// - protected virtual bool AllowOpeningOverlays => true; + protected virtual OverlayActivation InitialOverlayActivationMode => OverlayActivation.All; /// /// Whether this allows the cursor to be displayed. @@ -103,8 +105,15 @@ namespace osu.Game.Screens if (osuGame != null) { Ruleset.BindTo(osuGame.Ruleset); - hideOverlaysOnEnter.BindTo(osuGame.HideOverlaysOnEnter); - allowOpeningOverlays.BindTo(osuGame.AllowOpeningOverlays); + OverlayActivationMode.BindTo(osuGame.OverlayActivationMode); + + updateOverlayStates = () => + { + if (HideOverlaysOnEnter) + osuGame.CloseAllOverlays(); + else + osuGame.Toolbar.State = Visibility.Visible; + }; } sampleExit = audio.Sample.Get(@"UI/screen-back"); @@ -225,6 +234,7 @@ namespace osu.Game.Screens logo.Anchor = Anchor.TopLeft; logo.Origin = Anchor.Centre; logo.RelativePositionAxes = Axes.None; + logo.BeatMatching = true; logo.Triangles = true; logo.Ripple = true; } @@ -239,8 +249,9 @@ namespace osu.Game.Screens if (backgroundParallaxContainer != null) backgroundParallaxContainer.ParallaxAmount = ParallaxContainer.DEFAULT_PARALLAX_AMOUNT * BackgroundParallaxAmount; - hideOverlaysOnEnter.Value = HideOverlaysOnEnter; - allowOpeningOverlays.Value = AllowOpeningOverlays; + OverlayActivationMode.Value = InitialOverlayActivationMode; + + updateOverlayStates?.Invoke(); } private void onExitingLogo() diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 9985a24cab..54f65e3991 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -21,6 +21,7 @@ using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.Cursor; using osu.Game.Online.API; +using osu.Game.Overlays; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; @@ -37,6 +38,8 @@ namespace osu.Game.Screens.Play protected override bool HideOverlaysOnEnter => true; + protected override OverlayActivation InitialOverlayActivationMode => OverlayActivation.UserTriggered; + public Action RestartRequested; public bool HasFailed { get; private set; } @@ -364,7 +367,7 @@ namespace osu.Game.Screens.Play Background?.FadeTo(1f, fade_out_duration); } - protected override bool OnWheel(InputState state) => mouseWheelDisabled.Value && !pauseContainer.IsPaused; + protected override bool OnScroll(InputState state) => mouseWheelDisabled.Value && !pauseContainer.IsPaused; private void initializeStoryboard(bool asyncLoad) { diff --git a/osu.Game/Screens/Play/PlayerSettings/PlayerSettingsGroup.cs b/osu.Game/Screens/Play/PlayerSettings/PlayerSettingsGroup.cs index 0ffcdf94a6..cff3eca895 100644 --- a/osu.Game/Screens/Play/PlayerSettings/PlayerSettingsGroup.cs +++ b/osu.Game/Screens/Play/PlayerSettings/PlayerSettingsGroup.cs @@ -50,11 +50,11 @@ namespace osu.Game.Screens.Play.PlayerSettings content.ResizeHeightTo(0, transition_duration, Easing.OutQuint); } - button.FadeColour(expanded ? buttonActiveColour : Color4.White, 200, Easing.OutQuint); + updateExpanded(); } } - private Color4 buttonActiveColour; + private Color4 expandedColour; protected PlayerSettingsGroup() { @@ -130,9 +130,13 @@ namespace osu.Game.Screens.Play.PlayerSettings [BackgroundDependencyLoader] private void load(OsuColour colours) { - button.Colour = buttonActiveColour = colours.Yellow; + expandedColour = colours.Yellow; + + updateExpanded(); } + private void updateExpanded() => button.FadeColour(expanded ? expandedColour : Color4.White, 200, Easing.InOutQuint); + protected override Container Content => content; protected override bool OnHover(InputState state) => true; diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs index ca36f94eda..f1bd2b945f 100644 --- a/osu.Game/Screens/Select/BeatmapDetails.cs +++ b/osu.Game/Screens/Select/BeatmapDetails.cs @@ -6,7 +6,6 @@ using OpenTK.Graphics; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using System.Linq; @@ -120,14 +119,8 @@ namespace osu.Game.Screens.Select Margin = new MarginPadding { Top = spacing * 2 }, Children = new[] { - description = new MetadataSection("Description") - { - TextColour = Color4.White.Opacity(0.75f), - }, - source = new MetadataSection("Source") - { - TextColour = Color4.White.Opacity(0.75f), - }, + description = new MetadataSection("Description"), + source = new MetadataSection("Source"), tags = new MetadataSection("Tags"), }, }, @@ -164,10 +157,9 @@ namespace osu.Game.Screens.Select } [BackgroundDependencyLoader] - private void load(OsuColour colours, APIAccess api) + private void load(APIAccess api) { this.api = api; - tags.TextColour = colours.Yellow; } protected override void UpdateAfterChildren() @@ -364,7 +356,7 @@ namespace osu.Game.Screens.Select { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Colour = textFlow.Colour, + Colour = Color4.White.Opacity(0.75f), Text = text }, loaded => { @@ -375,12 +367,6 @@ namespace osu.Game.Screens.Select this.FadeIn(transition_duration); }); } - - public Color4 TextColour - { - get { return textFlow.Colour; } - set { textFlow.Colour = value; } - } } private class DimmedLoadingAnimation : VisibilityContainer diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 97f6371cb2..7950018554 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -57,7 +57,7 @@ namespace osu.Game.Screens.Select { if (osuGame != null) ruleset.BindTo(osuGame.Ruleset); - ruleset.ValueChanged += updateRuleset; + ruleset.ValueChanged += _ => updateDisplay(); } protected override bool BlockPassThroughMouse => false; @@ -78,66 +78,76 @@ namespace osu.Game.Screens.Select private WorkingBeatmap beatmap; - public void UpdateBeatmap(WorkingBeatmap beatmap) + public WorkingBeatmap Beatmap { - this.beatmap = beatmap; - loadBeatmap(); + get => beatmap; + set + { + if (beatmap == value) return; + + beatmap = value; + updateDisplay(); + } } - private void updateRuleset(RulesetInfo ruleset) => loadBeatmap(); + private BufferedWedgeInfo loadingInfo; - private void loadBeatmap() + private void updateDisplay() { - void updateState() + void removeOldInfo() { State = beatmap == null ? Visibility.Hidden : Visibility.Visible; Info?.FadeOut(250); Info?.Expire(); + Info = null; } if (beatmap == null) { - updateState(); + removeOldInfo(); return; } - LoadComponentAsync(new BufferedWedgeInfo(beatmap, ruleset.Value) + LoadComponentAsync(loadingInfo = new BufferedWedgeInfo(beatmap, ruleset.Value) { Shear = -Shear, - Depth = Info?.Depth + 1 ?? 0, - }, newInfo => + Depth = Info?.Depth + 1 ?? 0 + }, loaded => { - updateState(); - Add(Info = newInfo); + // ensure we are the most recent loaded wedge. + if (loaded != loadingInfo) return; + + removeOldInfo(); + Add(Info = loaded); }); } public class BufferedWedgeInfo : BufferedContainer { - private readonly WorkingBeatmap working; public OsuSpriteText VersionLabel { get; private set; } public OsuSpriteText TitleLabel { get; private set; } public OsuSpriteText ArtistLabel { get; private set; } public FillFlowContainer MapperContainer { get; private set; } public FillFlowContainer InfoLabelContainer { get; private set; } + private UnicodeBindableString titleBinding; private UnicodeBindableString artistBinding; + private readonly WorkingBeatmap beatmap; private readonly RulesetInfo ruleset; - public BufferedWedgeInfo(WorkingBeatmap working, RulesetInfo userRuleset) + public BufferedWedgeInfo(WorkingBeatmap beatmap, RulesetInfo userRuleset) { - this.working = working; - - ruleset = userRuleset ?? working.BeatmapInfo.Ruleset; + this.beatmap = beatmap; + ruleset = userRuleset ?? beatmap.BeatmapInfo.Ruleset; } [BackgroundDependencyLoader] private void load(LocalisationEngine localisation) { - var beatmapInfo = working.BeatmapInfo; - var metadata = beatmapInfo.Metadata ?? working.BeatmapSetInfo?.Metadata ?? new BeatmapMetadata(); + var beatmapInfo = beatmap.BeatmapInfo; + var metadata = beatmapInfo.Metadata ?? beatmap.BeatmapSetInfo?.Metadata ?? new BeatmapMetadata(); PixelSnapping = true; CacheDrawnFrameBuffer = true; @@ -165,7 +175,7 @@ namespace osu.Game.Screens.Select Children = new[] { // Zoomed-in and cropped beatmap background - new BeatmapBackgroundSprite(working) + new BeatmapBackgroundSprite(beatmap) { RelativeSizeAxes = Axes.Both, Anchor = Anchor.Centre, @@ -248,27 +258,27 @@ namespace osu.Game.Screens.Select private InfoLabel[] getInfoLabels() { - var beatmap = working.Beatmap; + var b = beatmap.Beatmap; List labels = new List(); - if (beatmap?.HitObjects?.Any() == true) + if (b?.HitObjects?.Any() == true) { - HitObject lastObject = beatmap.HitObjects.LastOrDefault(); + HitObject lastObject = b.HitObjects.LastOrDefault(); double endTime = (lastObject as IHasEndTime)?.EndTime ?? lastObject?.StartTime ?? 0; labels.Add(new InfoLabel(new BeatmapStatistic { Name = "Length", Icon = FontAwesome.fa_clock_o, - Content = TimeSpan.FromMilliseconds(endTime - beatmap.HitObjects.First().StartTime).ToString(@"m\:ss"), + Content = TimeSpan.FromMilliseconds(endTime - b.HitObjects.First().StartTime).ToString(@"m\:ss"), })); labels.Add(new InfoLabel(new BeatmapStatistic { Name = "BPM", Icon = FontAwesome.fa_circle, - Content = getBPMRange(beatmap), + Content = getBPMRange(b), })); IBeatmap playableBeatmap; @@ -276,12 +286,12 @@ namespace osu.Game.Screens.Select try { // Try to get the beatmap with the user's ruleset - playableBeatmap = working.GetPlayableBeatmap(ruleset); + playableBeatmap = beatmap.GetPlayableBeatmap(ruleset); } catch (BeatmapInvalidForRulesetException) { // Can't be converted to the user's ruleset, so use the beatmap's own ruleset - playableBeatmap = working.GetPlayableBeatmap(working.BeatmapInfo.Ruleset); + playableBeatmap = beatmap.GetPlayableBeatmap(beatmap.BeatmapInfo.Ruleset); } labels.AddRange(playableBeatmap.GetStatistics().Select(s => new InfoLabel(s))); diff --git a/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs b/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs index baab973ae5..19732107c7 100644 --- a/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs @@ -8,6 +8,7 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; using osu.Framework.Input; using osu.Game.Graphics; @@ -142,6 +143,8 @@ namespace osu.Game.Screens.Select.Leaderboards { flagBadgeContainer = new Container { + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, Size = new Vector2(87f, 20f), Masking = true, Children = new Drawable[] @@ -155,14 +158,16 @@ namespace osu.Game.Screens.Select.Leaderboards }, new FillFlowContainer { + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, Spacing = new Vector2(10f, 0f), - Margin = new MarginPadding { Left = edge_margin, }, + Margin = new MarginPadding { Left = edge_margin }, Children = new Drawable[] { - maxCombo = new ScoreComponentLabel(FontAwesome.fa_link, Score.MaxCombo.ToString()), - accuracy = new ScoreComponentLabel(FontAwesome.fa_crosshairs, string.Format(Score.Accuracy % 1 == 0 ? @"{0:P0}" : @"{0:P2}", Score.Accuracy)), + maxCombo = new ScoreComponentLabel(FontAwesome.fa_link, Score.MaxCombo.ToString(), "Max Combo"), + accuracy = new ScoreComponentLabel(FontAwesome.fa_crosshairs, string.Format(Score.Accuracy % 1 == 0 ? @"{0:P0}" : @"{0:P2}", Score.Accuracy), "Accuracy"), }, }, }, @@ -305,37 +310,61 @@ namespace osu.Game.Screens.Select.Leaderboards } } - private class ScoreComponentLabel : Container + private class ScoreComponentLabel : Container, IHasTooltip { - public ScoreComponentLabel(FontAwesome icon, string value) - { - Anchor = Anchor.CentreLeft; - Origin = Anchor.CentreLeft; - Size = new Vector2(60f, 20f); - Padding = new MarginPadding { Top = 10f, }; + private const float icon_size = 20; - Children = new Drawable[] + private readonly string name; + private readonly FillFlowContainer content; + + public override bool Contains(Vector2 screenSpacePos) => content.Contains(screenSpacePos); + + public string TooltipText => name; + + public ScoreComponentLabel(FontAwesome icon, string value, string name) + { + this.name = name; + AutoSizeAxes = Axes.Y; + Width = 60; + + Child = content = new FillFlowContainer { - new SpriteIcon + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Children = new Drawable[] { - Origin = Anchor.Centre, - Icon = FontAwesome.fa_square, - Colour = OsuColour.FromHex(@"3087ac"), - Rotation = 45, - Size = new Vector2(20), - Shadow = true, - }, - new SpriteIcon - { - Origin = Anchor.Centre, - Icon = icon, - Colour = OsuColour.FromHex(@"a4edff"), - Size = new Vector2(14), - }, - new GlowingSpriteText(value, @"Exo2.0-Bold", 17, Color4.White, OsuColour.FromHex(@"83ccfa")) - { - Origin = Anchor.CentreLeft, - Margin = new MarginPadding { Left = 15, }, + new Container + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + AutoSizeAxes = Axes.Both, + Children = new[] + { + new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(icon_size), + Rotation = 45, + Colour = OsuColour.FromHex(@"3087ac"), + Icon = FontAwesome.fa_square, + Shadow = true, + }, + new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(icon_size - 6), + Colour = OsuColour.FromHex(@"a4edff"), + Icon = icon, + }, + }, + }, + new GlowingSpriteText(value, @"Exo2.0-Bold", 17, Color4.White, OsuColour.FromHex(@"83ccfa")) + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + }, }, }; } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index e1271aebc4..41ba38cb0f 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -430,7 +430,7 @@ namespace osu.Game.Screens.Select backgroundModeBeatmap.FadeTo(1, 250); } - beatmapInfoWedge.UpdateBeatmap(beatmap); + beatmapInfoWedge.Beatmap = beatmap; } private void ensurePlayingSelected(bool preview = false) diff --git a/osu.Game/Tests/Visual/EditorClockTestCase.cs b/osu.Game/Tests/Visual/EditorClockTestCase.cs index 85d3684530..1ce7023be0 100644 --- a/osu.Game/Tests/Visual/EditorClockTestCase.cs +++ b/osu.Game/Tests/Visual/EditorClockTestCase.cs @@ -59,9 +59,9 @@ namespace osu.Game.Tests.Visual Clock.ProcessFrame(); } - protected override bool OnWheel(InputState state) + protected override bool OnScroll(InputState state) { - if (state.Mouse.WheelDelta > 0) + if (state.Mouse.ScrollDelta.Y > 0) Clock.SeekBackward(true); else Clock.SeekForward(true); diff --git a/osu.Game/Tests/Visual/OsuTestCase.cs b/osu.Game/Tests/Visual/OsuTestCase.cs index 2b677f1f42..fa441d8012 100644 --- a/osu.Game/Tests/Visual/OsuTestCase.cs +++ b/osu.Game/Tests/Visual/OsuTestCase.cs @@ -1,8 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System.IO; -using System.Reflection; using osu.Framework.Testing; namespace osu.Game.Tests.Visual @@ -13,8 +11,6 @@ namespace osu.Game.Tests.Visual public class OsuTestCaseTestRunner : OsuGameBase, ITestCaseTestRunner { - protected override string MainResourceFile => File.Exists(base.MainResourceFile) ? base.MainResourceFile : Assembly.GetExecutingAssembly().Location; - private TestCaseTestRunner.TestRunner runner; protected override void LoadAsyncComplete() diff --git a/osu.Game/Utils/DebugUtils.cs b/osu.Game/Utils/DebugUtils.cs new file mode 100644 index 0000000000..191662c690 --- /dev/null +++ b/osu.Game/Utils/DebugUtils.cs @@ -0,0 +1,21 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Utils +{ + public static class DebugUtils + { + public static bool IsDebug + { + get + { + // ReSharper disable once RedundantAssignment + bool isDebug = false; + // Debug.Assert conditions are only evaluated in debug mode + System.Diagnostics.Debug.Assert(isDebug = true); + // ReSharper disable once ConditionIsAlwaysTrueOrFalse + return isDebug; + } + } + } +} diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index afb656a260..2300ba6a72 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -11,7 +11,6 @@ - @@ -19,6 +18,7 @@ + diff --git a/osu.TestProject.props b/osu.TestProject.props index afdf895eac..b51ca13ed5 100644 --- a/osu.TestProject.props +++ b/osu.TestProject.props @@ -7,7 +7,6 @@ - @@ -18,7 +17,7 @@ - + VisualTestRunner.cs diff --git a/osu.sln b/osu.sln index 5c4b644489..bf1b6d60e1 100644 --- a/osu.sln +++ b/osu.sln @@ -5,8 +5,6 @@ VisualStudioVersion = 15.0.27004.2006 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game", "osu.Game\osu.Game.csproj", "{2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Framework", "osu-framework\osu.Framework\osu.Framework.csproj", "{C76BF5B3-985E-4D39-95FE-97C9C879B83A}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game.Resources", "osu-resources\osu.Game.Resources\osu.Game.Resources.csproj", "{D9A367C9-4C1A-489F-9B05-A0CEA2B53B58}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game.Rulesets.Osu", "osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj", "{C92A607B-1FDD-4954-9F92-03FF547D9080}" @@ -17,8 +15,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game.Rulesets.Taiko", " EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game.Rulesets.Mania", "osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj", "{48F4582B-7687-4621-9CBE-5C24197CB536}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Desktop.Deploy", "osu.Desktop.Deploy\osu.Desktop.Deploy.csproj", "{BAEA2F74-0315-4667-84E0-ACAC0B4BF785}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game.Tests", "osu.Game.Tests\osu.Game.Tests.csproj", "{54377672-20B1-40AF-8087-5CF73BF3953A}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Desktop", "osu.Desktop\osu.Desktop.csproj", "{419659FD-72EA-4678-9EB8-B22A746CED70}" @@ -41,10 +37,6 @@ Global {2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}.Debug|Any CPU.Build.0 = Debug|Any CPU {2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}.Release|Any CPU.ActiveCfg = Release|Any CPU {2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}.Release|Any CPU.Build.0 = Release|Any CPU - {C76BF5B3-985E-4D39-95FE-97C9C879B83A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C76BF5B3-985E-4D39-95FE-97C9C879B83A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C76BF5B3-985E-4D39-95FE-97C9C879B83A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C76BF5B3-985E-4D39-95FE-97C9C879B83A}.Release|Any CPU.Build.0 = Release|Any CPU {D9A367C9-4C1A-489F-9B05-A0CEA2B53B58}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D9A367C9-4C1A-489F-9B05-A0CEA2B53B58}.Debug|Any CPU.Build.0 = Debug|Any CPU {D9A367C9-4C1A-489F-9B05-A0CEA2B53B58}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -65,8 +57,6 @@ Global {48F4582B-7687-4621-9CBE-5C24197CB536}.Debug|Any CPU.Build.0 = Debug|Any CPU {48F4582B-7687-4621-9CBE-5C24197CB536}.Release|Any CPU.ActiveCfg = Release|Any CPU {48F4582B-7687-4621-9CBE-5C24197CB536}.Release|Any CPU.Build.0 = Release|Any CPU - {BAEA2F74-0315-4667-84E0-ACAC0B4BF785}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BAEA2F74-0315-4667-84E0-ACAC0B4BF785}.Release|Any CPU.ActiveCfg = Release|Any CPU {54377672-20B1-40AF-8087-5CF73BF3953A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {54377672-20B1-40AF-8087-5CF73BF3953A}.Debug|Any CPU.Build.0 = Debug|Any CPU {54377672-20B1-40AF-8087-5CF73BF3953A}.Release|Any CPU.ActiveCfg = Release|Any CPU