1
0
mirror of https://github.com/ppy/osu.git synced 2024-09-22 00:47:24 +08:00

Merge branch 'master' into recent-scores

This commit is contained in:
Dean Herbert 2017-10-30 18:48:26 +09:00 committed by GitHub
commit fcb88de626
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 540 additions and 140 deletions

View File

@ -106,7 +106,7 @@ namespace osu.Desktop
var filePaths = new [] { e.FileName };
if (filePaths.All(f => Path.GetExtension(f) == @".osz"))
Task.Run(() => BeatmapManager.Import(filePaths));
Task.Factory.StartNew(() => BeatmapManager.Import(filePaths), TaskCreationOptions.LongRunning);
else if (filePaths.All(f => Path.GetExtension(f) == @".osr"))
Task.Run(() =>
{

View File

@ -3,8 +3,11 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using osu.Framework.Allocation;
using osu.Framework.Extensions;
using osu.Framework.MathUtils;
using osu.Game.Beatmaps;
using osu.Game.Database;
@ -61,7 +64,7 @@ namespace osu.Game.Tests.Visual
return new BeatmapSetInfo
{
OnlineBeatmapSetID = 1234 + i,
Hash = "d8e8fca2dc0f896fd7cb4cb0031ba249",
Hash = new MemoryStream(Encoding.UTF8.GetBytes(Guid.NewGuid().ToString())).ComputeMD5Hash(),
Metadata = new BeatmapMetadata
{
OnlineBeatmapSetID = 1234 + i,

View File

@ -6,12 +6,13 @@ using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using Newtonsoft.Json;
using osu.Game.Database;
using osu.Game.IO.Serialization;
using osu.Game.Rulesets;
namespace osu.Game.Beatmaps
{
public class BeatmapInfo : IEquatable<BeatmapInfo>, IJsonSerializable
public class BeatmapInfo : IEquatable<BeatmapInfo>, IJsonSerializable, IHasPrimaryKey
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
@ -19,12 +20,23 @@ namespace osu.Game.Beatmaps
//TODO: should be in database
public int BeatmapVersion;
private int? onlineBeatmapID;
private int? onlineBeatmapSetID;
[JsonProperty("id")]
public int? OnlineBeatmapID { get; set; }
public int? OnlineBeatmapID
{
get { return onlineBeatmapID; }
set { onlineBeatmapID = value > 0 ? value : null; }
}
[JsonProperty("beatmapset_id")]
[NotMapped]
public int? OnlineBeatmapSetID { get; set; }
public int? OnlineBeatmapSetID
{
get { return onlineBeatmapSetID; }
set { onlineBeatmapSetID = value > 0 ? value : null; }
}
public int BeatmapSetInfoID { get; set; }

View File

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using Ionic.Zip;
using Microsoft.EntityFrameworkCore;
@ -244,12 +245,18 @@ namespace osu.Game.Beatmaps
request.Success += data =>
{
downloadNotification.State = ProgressNotificationState.Completed;
downloadNotification.Text = $"Importing {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}";
Task.Factory.StartNew(() =>
{
// This gets scheduled back to the update thread, but we want the import to run in the background.
using (var stream = new MemoryStream(data))
using (var archive = new OszArchiveReader(stream))
Import(archive);
downloadNotification.State = ProgressNotificationState.Completed;
}, TaskCreationOptions.LongRunning);
currentDownloads.Remove(request);
};
@ -272,7 +279,7 @@ namespace osu.Game.Beatmaps
PostNotification?.Invoke(downloadNotification);
// don't run in the main api queue as this is a long-running task.
Task.Run(() => request.Perform(api));
Task.Factory.StartNew(() => request.Perform(api), TaskCreationOptions.LongRunning);
return request;
}
@ -372,7 +379,7 @@ namespace osu.Game.Beatmaps
/// </summary>
/// <param name="query">The query.</param>
/// <returns>The first result for the provided query, or null if no results were found.</returns>
public BeatmapSetInfo QueryBeatmapSet(Func<BeatmapSetInfo, bool> query) => beatmaps.BeatmapSets.FirstOrDefault(query);
public BeatmapSetInfo QueryBeatmapSet(Expression<Func<BeatmapSetInfo, bool>> query) => beatmaps.BeatmapSets.AsNoTracking().FirstOrDefault(query);
/// <summary>
/// Refresh an existing instance of a <see cref="BeatmapSetInfo"/> from the store.
@ -386,21 +393,21 @@ namespace osu.Game.Beatmaps
/// </summary>
/// <param name="query">The query.</param>
/// <returns>Results from the provided query.</returns>
public List<BeatmapSetInfo> QueryBeatmapSets(Func<BeatmapSetInfo, bool> query) => beatmaps.BeatmapSets.Where(query).ToList();
public IEnumerable<BeatmapSetInfo> QueryBeatmapSets(Expression<Func<BeatmapSetInfo, bool>> query) => beatmaps.BeatmapSets.AsNoTracking().Where(query);
/// <summary>
/// Perform a lookup query on available <see cref="BeatmapInfo"/>s.
/// </summary>
/// <param name="query">The query.</param>
/// <returns>The first result for the provided query, or null if no results were found.</returns>
public BeatmapInfo QueryBeatmap(Func<BeatmapInfo, bool> query) => beatmaps.Beatmaps.FirstOrDefault(query);
public BeatmapInfo QueryBeatmap(Expression<Func<BeatmapInfo, bool>> query) => beatmaps.Beatmaps.AsNoTracking().FirstOrDefault(query);
/// <summary>
/// Perform a lookup query on available <see cref="BeatmapInfo"/>s.
/// </summary>
/// <param name="query">The query.</param>
/// <returns>Results from the provided query.</returns>
public List<BeatmapInfo> QueryBeatmaps(Func<BeatmapInfo, bool> query) => beatmaps.Beatmaps.Where(query).ToList();
public IEnumerable<BeatmapInfo> QueryBeatmaps(Expression<Func<BeatmapInfo, bool>> query) => beatmaps.Beatmaps.AsNoTracking().Where(query);
/// <summary>
/// Creates an <see cref="ArchiveReader"/> from a valid storage path.

View File

@ -14,8 +14,15 @@ namespace osu.Game.Beatmaps
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
private int? onlineBeatmapSetID;
[NotMapped]
public int? OnlineBeatmapSetID { get; set; }
[JsonProperty(@"id")]
public int? OnlineBeatmapSetID
{
get { return onlineBeatmapSetID; }
set { onlineBeatmapSetID = value > 0 ? value : null; }
}
public string Title { get; set; }
public string TitleUnicode { get; set; }

View File

@ -4,10 +4,11 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using osu.Game.Database;
namespace osu.Game.Beatmaps
{
public class BeatmapSetInfo
public class BeatmapSetInfo : IHasPrimaryKey
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }

View File

@ -2,7 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using osu.Game.Database;
@ -25,21 +24,6 @@ namespace osu.Game.Beatmaps
{
}
protected override void Prepare(bool reset = false)
{
if (reset)
{
var context = GetContext();
// https://stackoverflow.com/a/10450893
context.Database.ExecuteSqlCommand("DELETE FROM BeatmapMetadata");
context.Database.ExecuteSqlCommand("DELETE FROM BeatmapDifficulty");
context.Database.ExecuteSqlCommand("DELETE FROM BeatmapSetInfo");
context.Database.ExecuteSqlCommand("DELETE FROM BeatmapSetFileInfo");
context.Database.ExecuteSqlCommand("DELETE FROM BeatmapInfo");
}
}
/// <summary>
/// Add a <see cref="BeatmapSetInfo"/> to the database.
/// </summary>
@ -63,10 +47,10 @@ namespace osu.Game.Beatmaps
{
var context = GetContext();
if (beatmapSet.DeletePending) return false;
Refresh(ref beatmapSet, BeatmapSets);
if (beatmapSet.DeletePending) return false;
beatmapSet.DeletePending = true;
context.Update(beatmapSet);
context.SaveChanges();
BeatmapSetRemoved?.Invoke(beatmapSet);
@ -82,10 +66,10 @@ namespace osu.Game.Beatmaps
{
var context = GetContext();
if (!beatmapSet.DeletePending) return false;
Refresh(ref beatmapSet, BeatmapSets);
if (!beatmapSet.DeletePending) return false;
beatmapSet.DeletePending = false;
context.Update(beatmapSet);
context.SaveChanges();
BeatmapSetAdded?.Invoke(beatmapSet);
@ -101,10 +85,10 @@ namespace osu.Game.Beatmaps
{
var context = GetContext();
if (beatmap.Hidden) return false;
Refresh(ref beatmap, Beatmaps);
if (beatmap.Hidden) return false;
beatmap.Hidden = true;
context.Update(beatmap);
context.SaveChanges();
BeatmapHidden?.Invoke(beatmap);
@ -120,10 +104,10 @@ namespace osu.Game.Beatmaps
{
var context = GetContext();
if (!beatmap.Hidden) return false;
Refresh(ref beatmap, Beatmaps);
if (!beatmap.Hidden) return false;
beatmap.Hidden = false;
context.Update(beatmap);
context.SaveChanges();
BeatmapRestored?.Invoke(beatmap);
@ -151,14 +135,14 @@ namespace osu.Game.Beatmaps
context.SaveChanges();
}
public IEnumerable<BeatmapSetInfo> BeatmapSets => GetContext().BeatmapSetInfo
public IQueryable<BeatmapSetInfo> BeatmapSets => GetContext().BeatmapSetInfo
.Include(s => s.Metadata)
.Include(s => s.Beatmaps).ThenInclude(s => s.Ruleset)
.Include(s => s.Beatmaps).ThenInclude(b => b.BaseDifficulty)
.Include(s => s.Beatmaps).ThenInclude(b => b.Metadata)
.Include(s => s.Files).ThenInclude(f => f.FileInfo);
public IEnumerable<BeatmapInfo> Beatmaps => GetContext().BeatmapInfo
public IQueryable<BeatmapInfo> Beatmaps => GetContext().BeatmapInfo
.Include(b => b.BeatmapSet).ThenInclude(s => s.Metadata)
.Include(b => b.Metadata)
.Include(b => b.Ruleset)

View File

@ -2,8 +2,10 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using osu.Framework.Logging;
using Microsoft.EntityFrameworkCore;
using osu.Framework.Platform;
namespace osu.Game.Database
@ -19,6 +21,22 @@ namespace osu.Game.Database
private readonly ThreadLocal<OsuDbContext> queryContext;
/// <summary>
/// Refresh an instance potentially from a different thread with a local context-tracked instance.
/// </summary>
/// <param name="obj">The object to use as a reference when negotiating a local instance.</param>
/// <param name="lookupSource">An optional lookup source which will be used to query and populate a freshly retrieved replacement. If not provided, the refreshed object will still be returned but will not have any includes.</param>
/// <typeparam name="T">A valid EF-stored type.</typeparam>
protected virtual void Refresh<T>(ref T obj, IEnumerable<T> lookupSource = null) where T : class, IHasPrimaryKey
{
var context = GetContext();
if (context.Entry(obj).State != EntityState.Detached) return;
var id = obj.ID;
obj = lookupSource?.SingleOrDefault(t => t.ID == id) ?? context.Find<T>(id);
}
/// <summary>
/// Retrieve a shared context for performing lookups (or write operations on the update thread, for now).
/// </summary>
@ -32,16 +50,6 @@ namespace osu.Game.Database
queryContext = new ThreadLocal<OsuDbContext>(CreateContext);
Storage = storage;
try
{
Prepare();
}
catch (Exception e)
{
Logger.Error(e, $@"Failed to initialise the {GetType()}! Trying again with a clean database...");
Prepare(true);
}
}
/// <summary>
@ -50,15 +58,5 @@ namespace osu.Game.Database
public virtual void Cleanup()
{
}
/// <summary>
/// Prepare this database for use. Tables should be created here.
/// </summary>
protected abstract void Prepare(bool reset = false);
/// <summary>
/// Reset this database to a default state. Undo all changes to database and storage backings.
/// </summary>
public void Reset() => Prepare(true);
}
}

View File

@ -0,0 +1,10 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Database
{
public interface IHasPrimaryKey
{
int ID { get; set; }
}
}

View File

@ -78,11 +78,12 @@ namespace osu.Game.Database
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<BeatmapInfo>().HasIndex(b => b.MD5Hash);
modelBuilder.Entity<BeatmapInfo>().HasIndex(b => b.Hash);
modelBuilder.Entity<BeatmapInfo>().HasIndex(b => b.MD5Hash).IsUnique();
modelBuilder.Entity<BeatmapInfo>().HasIndex(b => b.Hash).IsUnique();
modelBuilder.Entity<BeatmapSetInfo>().HasIndex(b => b.OnlineBeatmapSetID).IsUnique();
modelBuilder.Entity<BeatmapSetInfo>().HasIndex(b => b.DeletePending);
modelBuilder.Entity<BeatmapSetInfo>().HasIndex(b => b.Hash);
modelBuilder.Entity<BeatmapSetInfo>().HasIndex(b => b.Hash).IsUnique();
modelBuilder.Entity<DatabasedKeyBinding>().HasIndex(b => b.Variant);
modelBuilder.Entity<DatabasedKeyBinding>().HasIndex(b => b.IntAction);
@ -251,7 +252,7 @@ namespace osu.Game.Database
Database.ExecuteSqlCommand("DROP TABLE RulesetInfo_Old");
Database.ExecuteSqlCommand(
"INSERT INTO BeatmapInfo SELECT ID, AudioLeadIn, BaseDifficultyID, BeatDivisor, BeatmapSetInfoID, Countdown, DistanceSpacing, GridSize, Hash, IFNULL(Hidden, 0), LetterboxInBreaks, MD5Hash, NULLIF(BeatmapMetadataID, 0), OnlineBeatmapID, Path, RulesetID, SpecialStyle, StackLeniency, StarDifficulty, StoredBookmarks, TimelineZoom, Version, WidescreenStoryboard FROM BeatmapInfo_Old");
"INSERT INTO BeatmapInfo SELECT ID, AudioLeadIn, BaseDifficultyID, BeatDivisor, BeatmapSetInfoID, Countdown, DistanceSpacing, GridSize, Hash, IFNULL(Hidden, 0), LetterboxInBreaks, MD5Hash, NULLIF(BeatmapMetadataID, 0), NULLIF(OnlineBeatmapID, 0), Path, RulesetID, SpecialStyle, StackLeniency, StarDifficulty, StoredBookmarks, TimelineZoom, Version, WidescreenStoryboard FROM BeatmapInfo_Old");
Database.ExecuteSqlCommand("DROP TABLE BeatmapInfo_Old");
Logger.Log("Migration complete!", LoggingTarget.Database, Framework.Logging.LogLevel.Important);

View File

@ -4,7 +4,6 @@
using System;
using System.IO;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using osu.Framework.Extensions;
using osu.Framework.IO.Stores;
using osu.Framework.Logging;
@ -27,17 +26,6 @@ namespace osu.Game.IO
Store = new StorageBackedResourceStore(Storage);
}
protected override void Prepare(bool reset = false)
{
if (reset)
{
if (Storage.ExistsDirectory(string.Empty))
Storage.DeleteDirectory(string.Empty);
GetContext().Database.ExecuteSqlCommand("DELETE FROM FileInfo");
}
}
public FileInfo Add(Stream data, bool reference = true)
{
var context = GetContext();

View File

@ -3,11 +3,12 @@
using System.ComponentModel.DataAnnotations.Schema;
using osu.Framework.Input.Bindings;
using osu.Game.Database;
namespace osu.Game.Input.Bindings
{
[Table("KeyBinding")]
public class DatabasedKeyBinding : KeyBinding
public class DatabasedKeyBinding : KeyBinding, IHasPrimaryKey
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }

View File

@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using osu.Framework.Input.Bindings;
using osu.Framework.Platform;
using osu.Game.Database;
@ -30,12 +29,6 @@ namespace osu.Game.Input
public void Register(KeyBindingInputManager manager) => insertDefaults(manager.DefaultKeyBindings);
protected override void Prepare(bool reset = false)
{
if (reset)
GetContext().Database.ExecuteSqlCommand("DELETE FROM KeyBinding");
}
private void insertDefaults(IEnumerable<KeyBinding> defaults, int? rulesetId = null, int? variant = null)
{
var context = GetContext();
@ -67,18 +60,24 @@ namespace osu.Game.Input
}
/// <summary>
/// Retrieve <see cref="KeyBinding"/>s for a specified ruleset/variant content.
/// Retrieve <see cref="DatabasedKeyBinding"/>s for a specified ruleset/variant content.
/// </summary>
/// <param name="rulesetId">The ruleset's internal ID.</param>
/// <param name="variant">An optional variant.</param>
/// <returns></returns>
public IEnumerable<KeyBinding> Query(int? rulesetId = null, int? variant = null) =>
GetContext().DatabasedKeyBinding.Where(b => b.RulesetID == rulesetId && b.Variant == variant);
public List<DatabasedKeyBinding> Query(int? rulesetId = null, int? variant = null) =>
GetContext().DatabasedKeyBinding.Where(b => b.RulesetID == rulesetId && b.Variant == variant).ToList();
public void Update(KeyBinding keyBinding)
{
var dbKeyBinding = (DatabasedKeyBinding)keyBinding;
var context = GetContext();
context.Update(keyBinding);
Refresh(ref dbKeyBinding);
dbKeyBinding.KeyCombination = keyBinding.KeyCombination;
context.SaveChanges();
KeyBindingChanged?.Invoke();

View File

@ -0,0 +1,299 @@
// <auto-generated />
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage;
using osu.Game.Database;
using System;
namespace osu.Game.Migrations
{
[DbContext(typeof(OsuDbContext))]
[Migration("20171025071459_AddMissingIndexRules")]
partial class AddMissingIndexRules
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.0.0-rtm-26452");
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<float>("ApproachRate");
b.Property<float>("CircleSize");
b.Property<float>("DrainRate");
b.Property<float>("OverallDifficulty");
b.Property<float>("SliderMultiplier");
b.Property<float>("SliderTickRate");
b.HasKey("ID");
b.ToTable("BeatmapDifficulty");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("AudioLeadIn");
b.Property<int>("BaseDifficultyID");
b.Property<int>("BeatDivisor");
b.Property<int>("BeatmapSetInfoID");
b.Property<bool>("Countdown");
b.Property<double>("DistanceSpacing");
b.Property<int>("GridSize");
b.Property<string>("Hash");
b.Property<bool>("Hidden");
b.Property<bool>("LetterboxInBreaks");
b.Property<string>("MD5Hash");
b.Property<int?>("MetadataID");
b.Property<int?>("OnlineBeatmapID");
b.Property<string>("Path");
b.Property<int>("RulesetID");
b.Property<bool>("SpecialStyle");
b.Property<float>("StackLeniency");
b.Property<double>("StarDifficulty");
b.Property<string>("StoredBookmarks");
b.Property<double>("TimelineZoom");
b.Property<string>("Version");
b.Property<bool>("WidescreenStoryboard");
b.HasKey("ID");
b.HasIndex("BaseDifficultyID");
b.HasIndex("BeatmapSetInfoID");
b.HasIndex("Hash")
.IsUnique();
b.HasIndex("MD5Hash")
.IsUnique();
b.HasIndex("MetadataID");
b.HasIndex("RulesetID");
b.ToTable("BeatmapInfo");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Artist");
b.Property<string>("ArtistUnicode");
b.Property<string>("AudioFile");
b.Property<string>("AuthorString")
.HasColumnName("Author");
b.Property<string>("BackgroundFile");
b.Property<int>("PreviewTime");
b.Property<string>("Source");
b.Property<string>("Tags");
b.Property<string>("Title");
b.Property<string>("TitleUnicode");
b.HasKey("ID");
b.ToTable("BeatmapMetadata");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("BeatmapSetInfoID");
b.Property<int>("FileInfoID");
b.Property<string>("Filename")
.IsRequired();
b.HasKey("ID");
b.HasIndex("BeatmapSetInfoID");
b.HasIndex("FileInfoID");
b.ToTable("BeatmapSetFileInfo");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<bool>("DeletePending");
b.Property<string>("Hash");
b.Property<int?>("MetadataID");
b.Property<int?>("OnlineBeatmapSetID");
b.Property<bool>("Protected");
b.HasKey("ID");
b.HasIndex("DeletePending");
b.HasIndex("Hash")
.IsUnique();
b.HasIndex("MetadataID");
b.HasIndex("OnlineBeatmapSetID")
.IsUnique();
b.ToTable("BeatmapSetInfo");
});
modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("IntAction")
.HasColumnName("Action");
b.Property<string>("KeysString")
.HasColumnName("Keys");
b.Property<int?>("RulesetID");
b.Property<int?>("Variant");
b.HasKey("ID");
b.HasIndex("IntAction");
b.HasIndex("Variant");
b.ToTable("KeyBinding");
});
modelBuilder.Entity("osu.Game.IO.FileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Hash");
b.Property<int>("ReferenceCount");
b.HasKey("ID");
b.HasIndex("Hash")
.IsUnique();
b.HasIndex("ReferenceCount");
b.ToTable("FileInfo");
});
modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b =>
{
b.Property<int?>("ID")
.ValueGeneratedOnAdd();
b.Property<bool>("Available");
b.Property<string>("InstantiationInfo");
b.Property<string>("Name");
b.HasKey("ID");
b.HasIndex("Available");
b.ToTable("RulesetInfo");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty")
.WithMany()
.HasForeignKey("BaseDifficultyID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet")
.WithMany("Beatmaps")
.HasForeignKey("BeatmapSetInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata")
.WithMany("Beatmaps")
.HasForeignKey("MetadataID");
b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset")
.WithMany()
.HasForeignKey("RulesetID")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo")
.WithMany("Files")
.HasForeignKey("BeatmapSetInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
.WithMany()
.HasForeignKey("FileInfoID")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata")
.WithMany("BeatmapSets")
.HasForeignKey("MetadataID");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,82 @@
using Microsoft.EntityFrameworkCore.Migrations;
using System;
using System.Collections.Generic;
namespace osu.Game.Migrations
{
public partial class AddMissingIndexRules : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "IX_BeatmapSetInfo_Hash",
table: "BeatmapSetInfo");
migrationBuilder.DropIndex(
name: "IX_BeatmapInfo_Hash",
table: "BeatmapInfo");
migrationBuilder.DropIndex(
name: "IX_BeatmapInfo_MD5Hash",
table: "BeatmapInfo");
migrationBuilder.CreateIndex(
name: "IX_BeatmapSetInfo_Hash",
table: "BeatmapSetInfo",
column: "Hash",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_BeatmapSetInfo_OnlineBeatmapSetID",
table: "BeatmapSetInfo",
column: "OnlineBeatmapSetID",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_BeatmapInfo_Hash",
table: "BeatmapInfo",
column: "Hash",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_BeatmapInfo_MD5Hash",
table: "BeatmapInfo",
column: "MD5Hash",
unique: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "IX_BeatmapSetInfo_Hash",
table: "BeatmapSetInfo");
migrationBuilder.DropIndex(
name: "IX_BeatmapSetInfo_OnlineBeatmapSetID",
table: "BeatmapSetInfo");
migrationBuilder.DropIndex(
name: "IX_BeatmapInfo_Hash",
table: "BeatmapInfo");
migrationBuilder.DropIndex(
name: "IX_BeatmapInfo_MD5Hash",
table: "BeatmapInfo");
migrationBuilder.CreateIndex(
name: "IX_BeatmapSetInfo_Hash",
table: "BeatmapSetInfo",
column: "Hash");
migrationBuilder.CreateIndex(
name: "IX_BeatmapInfo_Hash",
table: "BeatmapInfo",
column: "Hash");
migrationBuilder.CreateIndex(
name: "IX_BeatmapInfo_MD5Hash",
table: "BeatmapInfo",
column: "MD5Hash");
}
}
}

View File

@ -95,9 +95,11 @@ namespace osu.Game.Migrations
b.HasIndex("BeatmapSetInfoID");
b.HasIndex("Hash");
b.HasIndex("Hash")
.IsUnique();
b.HasIndex("MD5Hash");
b.HasIndex("MD5Hash")
.IsUnique();
b.HasIndex("MetadataID");
@ -177,10 +179,14 @@ namespace osu.Game.Migrations
b.HasIndex("DeletePending");
b.HasIndex("Hash");
b.HasIndex("Hash")
.IsUnique();
b.HasIndex("MetadataID");
b.HasIndex("OnlineBeatmapSetID")
.IsUnique();
b.ToTable("BeatmapSetInfo");
});

View File

@ -9,7 +9,7 @@ using osu.Game.Rulesets;
namespace osu.Game.Online.API.Requests
{
public class GetBeatmapSetsResponse : BeatmapMetadata
public class GetBeatmapSetsResponse : BeatmapMetadata // todo: this is a bit wrong...
{
[JsonProperty(@"covers")]
private BeatmapSetOnlineCovers covers { get; set; }
@ -23,9 +23,6 @@ namespace osu.Game.Online.API.Requests
[JsonProperty(@"favourite_count")]
private int favouriteCount { get; set; }
[JsonProperty(@"id")]
private int onlineId { get; set; }
[JsonProperty(@"user_id")]
private long creatorId {
set { Author.Id = value; }
@ -38,7 +35,7 @@ namespace osu.Game.Online.API.Requests
{
return new BeatmapSetInfo
{
OnlineBeatmapSetID = onlineId,
OnlineBeatmapSetID = OnlineBeatmapSetID,
Metadata = this,
OnlineInfo = new BeatmapSetOnlineInfo
{

View File

@ -88,6 +88,8 @@ namespace osu.Game.Overlays.BeatmapSet
};
}
public void StopPreview() => preview.Playing.Value = false;
private class DetailBox : Container
{
private readonly Container content;

View File

@ -27,7 +27,7 @@ namespace osu.Game.Overlays.BeatmapSet
private readonly Container coverContainer;
private readonly OsuSpriteText title, artist;
private readonly AuthorInfo author;
private readonly Details details;
public Details Details;
private DelayedLoadWrapper cover;
@ -42,7 +42,7 @@ namespace osu.Game.Overlays.BeatmapSet
if (value == beatmapSet) return;
beatmapSet = value;
Picker.BeatmapSet = author.BeatmapSet = details.BeatmapSet = BeatmapSet;
Picker.BeatmapSet = author.BeatmapSet = Details.BeatmapSet = BeatmapSet;
title.Text = BeatmapSet.Metadata.Title;
artist.Text = BeatmapSet.Metadata.Artist;
@ -192,7 +192,7 @@ namespace osu.Game.Overlays.BeatmapSet
},
},
},
details = new Details
Details = new Details
{
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
@ -204,7 +204,7 @@ namespace osu.Game.Overlays.BeatmapSet
Picker.Beatmap.ValueChanged += b =>
{
details.Beatmap = b;
Details.Beatmap = b;
if (b.OnlineInfo.HasVideo)
{

View File

@ -26,7 +26,7 @@ namespace osu.Game.Overlays.BeatmapSet
private readonly PlayButton playButton;
private Track preview => playButton.Preview;
private Bindable<bool> playing => playButton.Playing;
public Bindable<bool> Playing => playButton.Playing;
public BeatmapSetInfo BeatmapSet
{
@ -66,8 +66,8 @@ namespace osu.Game.Overlays.BeatmapSet
},
};
Action = () => playing.Value = !playing.Value;
playing.ValueChanged += newValue => progress.FadeTo(newValue ? 1 : 0, 100);
Action = () => Playing.Value = !Playing.Value;
Playing.ValueChanged += newValue => progress.FadeTo(newValue ? 1 : 0, 100);
}
[BackgroundDependencyLoader]
@ -80,7 +80,7 @@ namespace osu.Game.Overlays.BeatmapSet
{
base.Update();
if (playing.Value && preview != null)
if (Playing.Value && preview != null)
{
progress.Width = (float)(preview.CurrentTime / preview.Length);
}
@ -88,7 +88,7 @@ namespace osu.Game.Overlays.BeatmapSet
protected override void Dispose(bool isDisposing)
{
playing.Value = false;
Playing.Value = false;
base.Dispose(isDisposing);
}

View File

@ -98,6 +98,7 @@ namespace osu.Game.Overlays
protected override void PopOut()
{
base.PopOut();
header.Details.StopPreview();
FadeEdgeEffectTo(0, DISAPPEAR_DURATION, Easing.Out);
}

View File

@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using OpenTK;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
@ -65,8 +66,6 @@ namespace osu.Game.Overlays
ResultAmounts = new ResultCounts(distinctCount(artists), distinctCount(songs), distinctCount(tags));
if (beatmapSets.Any() && panels == null)
// real use case? currently only seems to be for test case
recreatePanels(Filter.DisplayStyleControl.DisplayStyle.Value);
}
}
@ -274,13 +273,17 @@ namespace osu.Game.Overlays
Filter.DisplayStyleControl.Dropdown.Current.Value,
Filter.Tabs.Current.Value); //todo: sort direction (?)
getSetsRequest.Success += r =>
getSetsRequest.Success += response =>
{
BeatmapSets = r?.
Select(response => response.ToBeatmapSet(rulesets)).
Where(b => beatmaps.QueryBeatmapSet(q => q.OnlineBeatmapSetID == b.OnlineBeatmapSetID) == null);
Task.Run(() =>
{
var onlineIds = response.Select(r => r.OnlineBeatmapSetID).ToList();
var presentOnlineIds = beatmaps.QueryBeatmapSets(s => onlineIds.Contains(s.OnlineBeatmapSetID)).Select(r => r.OnlineBeatmapSetID).ToList();
var sets = response.Select(r => r.ToBeatmapSet(rulesets)).Where(b => !presentOnlineIds.Contains(b.OnlineBeatmapSetID)).ToList();
recreatePanels(Filter.DisplayStyleControl.DisplayStyle.Value);
// may not need scheduling; loads async internally.
Schedule(() => BeatmapSets = sets);
});
};
api.Queue(getSetsRequest);

View File

@ -29,7 +29,8 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
Action = () =>
{
importButton.Enabled.Value = false;
Task.Run(() => beatmaps.ImportFromStable()).ContinueWith(t => Schedule(() => importButton.Enabled.Value = true));
Task.Factory.StartNew(beatmaps.ImportFromStable)
.ContinueWith(t => Schedule(() => importButton.Enabled.Value = true), TaskContinuationOptions.LongRunning);
}
},
deleteButton = new SettingsButton

View File

@ -6,7 +6,6 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using Microsoft.EntityFrameworkCore;
using osu.Game.Database;
namespace osu.Game.Rulesets
@ -29,6 +28,7 @@ namespace osu.Game.Rulesets
public RulesetStore(Func<OsuDbContext> factory)
: base(factory)
{
AddMissingRulesets();
}
/// <summary>
@ -41,21 +41,16 @@ namespace osu.Game.Rulesets
/// <summary>
/// All available rulesets.
/// </summary>
public IEnumerable<RulesetInfo> AvailableRulesets => GetContext().RulesetInfo.Where(r => r.Available);
public IEnumerable<RulesetInfo> AvailableRulesets;
private static Assembly currentDomain_AssemblyResolve(object sender, ResolveEventArgs args) => loaded_assemblies.Keys.FirstOrDefault(a => a.FullName == args.Name);
private const string ruleset_library_prefix = "osu.Game.Rulesets";
protected override void Prepare(bool reset = false)
protected void AddMissingRulesets()
{
var context = GetContext();
if (reset)
{
context.Database.ExecuteSqlCommand("DELETE FROM RulesetInfo");
}
var instances = loaded_assemblies.Values.Select(r => (Ruleset)Activator.CreateInstance(r, new RulesetInfo())).ToList();
//add all legacy modes in correct order
@ -98,6 +93,8 @@ namespace osu.Game.Rulesets
}
context.SaveChanges();
AvailableRulesets = context.RulesetInfo.Where(r => r.Available).ToList();
}
private static void loadRulesetFromFile(string file)

View File

@ -143,9 +143,5 @@ namespace osu.Game.Rulesets.Scoring
return new Replay { Frames = frames };
}
protected override void Prepare(bool reset = false)
{
}
}
}

View File

@ -119,7 +119,7 @@ namespace osu.Game.Screens.Select
internal void UpdateBeatmap(BeatmapInfo beatmap)
{
// todo: this method should not run more than once for the same BeatmapSetInfo.
var set = manager.Refresh(beatmap.BeatmapSet);
var set = manager.QueryBeatmapSet(s => s.ID == beatmap.BeatmapSetInfoID);
// todo: this method should be smarter as to not recreate panels that haven't changed, etc.
var group = groups.Find(b => b.BeatmapSet.ID == set.ID);

View File

@ -282,10 +282,15 @@
<Compile Include="Beatmaps\Drawables\BeatmapSetCover.cs" />
<Compile Include="Beatmaps\Drawables\BeatmapSetHeader.cs" />
<Compile Include="Database\DatabaseContextFactory.cs" />
<Compile Include="Database\IHasPrimaryKey.cs" />
<Compile Include="Migrations\20171019041408_InitialCreate.cs" />
<Compile Include="Migrations\20171019041408_InitialCreate.Designer.cs">
<DependentUpon>20171019041408_InitialCreate.cs</DependentUpon>
</Compile>
<Compile Include="Migrations\20171025071459_AddMissingIndexRules.cs" />
<Compile Include="Migrations\20171025071459_AddMissingIndexRules.Designer.cs">
<DependentUpon>20171025071459_AddMissingIndexRules.cs</DependentUpon>
</Compile>
<Compile Include="Migrations\OsuDbContextModelSnapshot.cs" />
<Compile Include="Online\API\Requests\GetBeatmapSetRequest.cs" />
<Compile Include="Online\API\Requests\GetBeatmapSetsResponse.cs" />