Reshuffle namespaces

This commit is contained in:
Dean Herbert
2017-07-26 13:22:46 +09:00
parent d441a7a6f9
commit fce580d717
90 changed files with 90 additions and 104 deletions

View File

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

View File

@ -1,41 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using SQLite.Net.Attributes;
namespace osu.Game.Database
{
public class BeatmapDifficulty
{
/// <summary>
/// The default value used for all difficulty settings except <see cref="SliderMultiplier"/> and <see cref="SliderTickRate"/>.
/// </summary>
public const float DEFAULT_DIFFICULTY = 5;
[PrimaryKey, AutoIncrement]
public int ID { get; set; }
public float DrainRate { get; set; } = DEFAULT_DIFFICULTY;
public float CircleSize { get; set; } = DEFAULT_DIFFICULTY;
public float OverallDifficulty { get; set; } = DEFAULT_DIFFICULTY;
public float ApproachRate { get; set; } = DEFAULT_DIFFICULTY;
public float SliderMultiplier { get; set; } = 1;
public float SliderTickRate { get; set; } = 1;
/// <summary>
/// Maps a difficulty value [0, 10] to a two-piece linear range of values.
/// </summary>
/// <param name="difficulty">The difficulty value to be mapped.</param>
/// <param name="min">Minimum of the resulting range which will be achieved by a difficulty value of 0.</param>
/// <param name="mid">Midpoint of the resulting range which will be achieved by a difficulty value of 5.</param>
/// <param name="max">Maximum of the resulting range which will be achieved by a difficulty value of 10.</param>
/// <returns>Value to which the difficulty value maps in the specified range.</returns>
public static double DifficultyRange(double difficulty, double min, double mid, double max)
{
if (difficulty > 5)
return mid + (max - mid) * (difficulty - 5) / 5;
if (difficulty < 5)
return mid - (mid - min) * (5 - difficulty) / 5;
return mid;
}
}
}

View File

@ -1,104 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using Newtonsoft.Json;
using osu.Game.IO.Serialization;
using SQLite.Net.Attributes;
using SQLiteNetExtensions.Attributes;
using System;
using System.Linq;
namespace osu.Game.Database
{
public class BeatmapInfo : IEquatable<BeatmapInfo>, IJsonSerializable
{
[PrimaryKey, AutoIncrement]
public int ID { get; set; }
//TODO: should be in database
public int BeatmapVersion;
public int? OnlineBeatmapID { get; set; }
public int? OnlineBeatmapSetID { get; set; }
[ForeignKey(typeof(BeatmapSetInfo))]
public int BeatmapSetInfoID { get; set; }
[ManyToOne]
public BeatmapSetInfo BeatmapSet { get; set; }
[ForeignKey(typeof(BeatmapMetadata))]
public int BeatmapMetadataID { get; set; }
[OneToOne(CascadeOperations = CascadeOperation.All)]
public BeatmapMetadata Metadata { get; set; }
[ForeignKey(typeof(BeatmapDifficulty)), NotNull]
public int BaseDifficultyID { get; set; }
[OneToOne(CascadeOperations = CascadeOperation.All)]
public BeatmapDifficulty Difficulty { get; set; }
[Ignore]
public BeatmapMetrics Metrics { get; set; }
[Ignore]
public BeatmapOnlineInfo OnlineInfo { get; set; }
public string Path { get; set; }
[JsonProperty("file_md5")]
public string Hash { get; set; }
// General
public int AudioLeadIn { get; set; }
public bool Countdown { get; set; }
public float StackLeniency { get; set; }
public bool SpecialStyle { get; set; }
[ForeignKey(typeof(RulesetInfo))]
public int RulesetID { get; set; }
[OneToOne(CascadeOperations = CascadeOperation.All)]
public RulesetInfo Ruleset { get; set; }
public bool LetterboxInBreaks { get; set; }
public bool WidescreenStoryboard { get; set; }
// Editor
// This bookmarks stuff is necessary because DB doesn't know how to store int[]
public string StoredBookmarks { get; internal set; }
[Ignore]
[JsonIgnore]
public int[] Bookmarks
{
get { return StoredBookmarks.Split(',').Select(int.Parse).ToArray(); }
set { StoredBookmarks = string.Join(",", value); }
}
public double DistanceSpacing { get; set; }
public int BeatDivisor { get; set; }
public int GridSize { get; set; }
public double TimelineZoom { get; set; }
// Metadata
public string Version { get; set; }
public double StarDifficulty { get; set; }
public bool Equals(BeatmapInfo other)
{
return ID == other?.ID;
}
public bool AudioEquals(BeatmapInfo other) => other != null && BeatmapSet != null && other.BeatmapSet != null &&
BeatmapSet.Path == other.BeatmapSet.Path &&
(Metadata ?? BeatmapSet.Metadata).AudioFile == (other.Metadata ?? other.BeatmapSet.Metadata).AudioFile;
public bool BackgroundEquals(BeatmapInfo other) => other != null && BeatmapSet != null && other.BeatmapSet != null &&
BeatmapSet.Path == other.BeatmapSet.Path &&
(Metadata ?? BeatmapSet.Metadata).BackgroundFile == (other.Metadata ?? other.BeatmapSet.Metadata).BackgroundFile;
}
}

View File

@ -1,44 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Linq;
using Newtonsoft.Json;
using SQLite.Net.Attributes;
namespace osu.Game.Database
{
public class BeatmapMetadata
{
[PrimaryKey, AutoIncrement]
public int ID { get; set; }
public int? OnlineBeatmapSetID { get; set; }
public string Title { get; set; }
public string TitleUnicode { get; set; }
public string Artist { get; set; }
public string ArtistUnicode { get; set; }
[JsonProperty(@"creator")]
public string Author { get; set; }
public string Source { get; set; }
[JsonProperty(@"tags")]
public string Tags { get; set; }
public int PreviewTime { get; set; }
public string AudioFile { get; set; }
public string BackgroundFile { get; set; }
public string[] SearchableTerms => new[]
{
Author,
Artist,
ArtistUnicode,
Title,
TitleUnicode,
Source,
Tags
}.Where(s => !string.IsNullOrEmpty(s)).ToArray();
}
}

View File

@ -1,31 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using Newtonsoft.Json;
namespace osu.Game.Database
{
/// <summary>
/// Beatmap metrics based on acculumated online data from community plays.
/// </summary>
public class BeatmapMetrics
{
/// <summary>
/// Total vote counts of user ratings on a scale of 0..length.
/// </summary>
public IEnumerable<int> Ratings { get; set; }
/// <summary>
/// Points of failure on a relative time scale (usually 0..100).
/// </summary>
[JsonProperty(@"fail")]
public IEnumerable<int> Fails { get; set; }
/// <summary>
/// Points of retry on a relative time scale (usually 0..100).
/// </summary>
[JsonProperty(@"exit")]
public IEnumerable<int> Retries { get; set; }
}
}

View File

@ -1,25 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using Newtonsoft.Json;
namespace osu.Game.Database
{
/// <summary>
/// Beatmap info retrieved for previewing locally without having the beatmap downloaded.
/// </summary>
public class BeatmapOnlineInfo
{
/// <summary>
/// The amount of plays this beatmap has.
/// </summary>
[JsonProperty(@"playcount")]
public int PlayCount { get; set; }
/// <summary>
/// The amount of passes this beatmap has.
/// </summary>
[JsonProperty(@"passcount")]
public int PassCount { get; set; }
}
}

View File

@ -1,41 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
using SQLite.Net.Attributes;
using SQLiteNetExtensions.Attributes;
namespace osu.Game.Database
{
public class BeatmapSetInfo
{
[PrimaryKey, AutoIncrement]
public int ID { get; set; }
public int? OnlineBeatmapSetID { get; set; }
[OneToOne(CascadeOperations = CascadeOperation.All)]
public BeatmapMetadata Metadata { get; set; }
[NotNull, ForeignKey(typeof(BeatmapMetadata))]
public int BeatmapMetadataID { get; set; }
[OneToMany(CascadeOperations = CascadeOperation.All)]
public List<BeatmapInfo> Beatmaps { get; set; }
[Ignore]
public BeatmapSetOnlineInfo OnlineInfo { get; set; }
public double MaxStarDifficulty => Beatmaps.Max(b => b.StarDifficulty);
[Indexed]
public bool DeletePending { get; set; }
public string Hash { get; set; }
public string Path { get; set; }
public string StoryboardFile { get; set; }
}
}

View File

@ -1,55 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using Newtonsoft.Json;
namespace osu.Game.Database
{
/// <summary>
/// Beatmap set info retrieved for previewing locally without having the set downloaded.
/// </summary>
public class BeatmapSetOnlineInfo
{
/// <summary>
/// The different sizes of cover art for this beatmap set.
/// </summary>
[JsonProperty(@"covers")]
public BeatmapSetOnlineCovers Covers { get; set; }
/// <summary>
/// A small sample clip of this beatmap set's song.
/// </summary>
[JsonProperty(@"previewUrl")]
public string Preview { get; set; }
/// <summary>
/// The amount of plays this beatmap set has.
/// </summary>
[JsonProperty(@"play_count")]
public int PlayCount { get; set; }
/// <summary>
/// The amount of people who have favourited this beatmap set.
/// </summary>
[JsonProperty(@"favourite_count")]
public int FavouriteCount { get; set; }
}
public class BeatmapSetOnlineCovers
{
public string CoverLowRes { get; set; }
[JsonProperty(@"cover@2x")]
public string Cover { get; set; }
public string CardLowRes { get; set; }
[JsonProperty(@"card@2x")]
public string Card { get; set; }
public string ListLowRes { get; set; }
[JsonProperty(@"list@2x")]
public string List { get; set; }
}
}

View File

@ -12,12 +12,12 @@ using SQLiteNetExtensions.Extensions;
namespace osu.Game.Database
{
public abstract class Database
public abstract class DatabaseBacking
{
protected SQLiteConnection Connection { get; }
protected Storage Storage { get; }
protected Database(Storage storage, SQLiteConnection connection)
protected DatabaseBacking(Storage storage, SQLiteConnection connection)
{
Storage = storage;
Connection = connection;

View File

@ -1,75 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.IO;
using osu.Framework.Audio.Track;
using osu.Framework.Graphics.Textures;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Formats;
using osu.Game.Beatmaps.IO;
namespace osu.Game.Database
{
internal class DatabaseWorkingBeatmap : WorkingBeatmap
{
private readonly BeatmapDatabase database;
public DatabaseWorkingBeatmap(BeatmapDatabase database, BeatmapInfo beatmapInfo)
: base(beatmapInfo)
{
this.database = database;
}
private ArchiveReader getReader() => database?.GetReader(BeatmapSetInfo);
protected override Beatmap GetBeatmap()
{
try
{
Beatmap beatmap;
using (var reader = getReader())
{
BeatmapDecoder decoder;
using (var stream = new StreamReader(reader.GetStream(BeatmapInfo.Path)))
{
decoder = BeatmapDecoder.GetDecoder(stream);
beatmap = decoder.Decode(stream);
}
if (beatmap == null || BeatmapSetInfo.StoryboardFile == null)
return beatmap;
using (var stream = new StreamReader(reader.GetStream(BeatmapSetInfo.StoryboardFile)))
decoder.Decode(stream, beatmap);
}
return beatmap;
}
catch { return null; }
}
protected override Texture GetBackground()
{
if (Metadata?.BackgroundFile == null)
return null;
try
{
using (var reader = getReader())
return new TextureStore(new RawTextureLoaderStore(reader), false).Get(Metadata.BackgroundFile);
}
catch { return null; }
}
protected override Track GetTrack()
{
try
{
var trackData = getReader()?.GetStream(Metadata.AudioFile);
return trackData == null ? null : new TrackBass(trackData);
}
catch { return new TrackVirtual(); }
}
}
}

View File

@ -1,23 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.ComponentModel;
namespace osu.Game.Database
{
public enum RankStatus
{
Any = 7,
[Description("Ranked & Approved")]
RankedApproved = 0,
Approved = 1,
Loved = 8,
Favourites = 2,
[Description("Mod Requests")]
ModRequests = 3,
Pending = 4,
Graveyard = 5,
[Description("My Maps")]
MyMaps = 6,
}
}

View File

@ -1,103 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using osu.Framework.Platform;
using osu.Game.Rulesets;
using SQLite.Net;
namespace osu.Game.Database
{
/// <summary>
/// Todo: All of this needs to be moved to a RulesetDatabase.
/// </summary>
public class RulesetDatabase : Database
{
public IEnumerable<RulesetInfo> AllRulesets => Query<RulesetInfo>().Where(r => r.Available);
public RulesetDatabase(Storage storage, SQLiteConnection connection)
: base(storage, connection)
{
}
protected override void Prepare(bool reset = false)
{
Connection.CreateTable<RulesetInfo>();
if (reset)
{
Connection.DeleteAll<RulesetInfo>();
}
List<Ruleset> instances = new List<Ruleset>();
foreach (string file in Directory.GetFiles(Environment.CurrentDirectory, @"osu.Game.Rulesets.*.dll"))
{
try
{
var assembly = Assembly.LoadFile(file);
var rulesets = assembly.GetTypes().Where(t => t.IsSubclassOf(typeof(Ruleset)));
if (rulesets.Count() != 1)
continue;
foreach (Type rulesetType in rulesets)
instances.Add((Ruleset)Activator.CreateInstance(rulesetType));
}
catch (Exception) { }
}
Connection.BeginTransaction();
//add all legacy modes in correct order
foreach (var r in instances.Where(r => r.LegacyID >= 0).OrderBy(r => r.LegacyID))
{
Connection.InsertOrReplace(createRulesetInfo(r));
}
//add any other modes
foreach (var r in instances.Where(r => r.LegacyID < 0))
{
var us = createRulesetInfo(r);
var existing = Query<RulesetInfo>().FirstOrDefault(ri => ri.InstantiationInfo == us.InstantiationInfo);
if (existing == null)
Connection.Insert(us);
}
//perform a consistency check
foreach (var r in Query<RulesetInfo>())
{
try
{
r.CreateInstance();
r.Available = true;
}
catch
{
r.Available = false;
}
Connection.Update(r);
}
Connection.Commit();
}
private RulesetInfo createRulesetInfo(Ruleset ruleset) => new RulesetInfo
{
Name = ruleset.Description,
InstantiationInfo = ruleset.GetType().AssemblyQualifiedName,
ID = ruleset.LegacyID
};
protected override Type[] ValidTypes => new[] { typeof(RulesetInfo) };
public RulesetInfo GetRuleset(int id) => Query<RulesetInfo>().First(r => r.ID == id);
}
}

View File

@ -1,26 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Game.Rulesets;
using SQLite.Net.Attributes;
namespace osu.Game.Database
{
public class RulesetInfo
{
[PrimaryKey, AutoIncrement]
public int? ID { get; set; }
[Indexed(Unique = true)]
public string Name { get; set; }
[Indexed(Unique = true)]
public string InstantiationInfo { get; set; }
[Indexed]
public bool Available { get; set; }
public virtual Ruleset CreateInstance() => (Ruleset)Activator.CreateInstance(Type.GetType(InstantiationInfo));
}
}

View File

@ -1,154 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using osu.Framework.Platform;
using osu.Game.IO.Legacy;
using osu.Game.IPC;
using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Scoring;
using SharpCompress.Compressors.LZMA;
using SQLite.Net;
namespace osu.Game.Database
{
public class ScoreDatabase : Database
{
private readonly Storage storage;
private readonly BeatmapDatabase beatmaps;
private readonly RulesetDatabase rulesets;
private const string replay_folder = @"replays";
// ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised)
private ScoreIPCChannel ipc;
public ScoreDatabase(Storage storage, SQLiteConnection connection, IIpcHost importHost = null, BeatmapDatabase beatmaps = null, RulesetDatabase rulesets = null) : base(storage, connection)
{
this.storage = storage;
this.beatmaps = beatmaps;
this.rulesets = rulesets;
if (importHost != null)
ipc = new ScoreIPCChannel(importHost, this);
}
public Score ReadReplayFile(string replayFilename)
{
Score score;
using (Stream s = storage.GetStream(Path.Combine(replay_folder, replayFilename)))
using (SerializationReader sr = new SerializationReader(s))
{
score = new Score
{
Ruleset = rulesets.GetRuleset(sr.ReadByte())
};
/* score.Pass = true;*/
var version = sr.ReadInt32();
/* score.FileChecksum = */
var beatmapHash = sr.ReadString();
score.Beatmap = beatmaps.Query<BeatmapInfo>().FirstOrDefault(b => b.Hash == beatmapHash);
/* score.PlayerName = */
sr.ReadString();
/* var localScoreChecksum = */
sr.ReadString();
/* score.Count300 = */
sr.ReadUInt16();
/* score.Count100 = */
sr.ReadUInt16();
/* score.Count50 = */
sr.ReadUInt16();
/* score.CountGeki = */
sr.ReadUInt16();
/* score.CountKatu = */
sr.ReadUInt16();
/* score.CountMiss = */
sr.ReadUInt16();
score.TotalScore = sr.ReadInt32();
score.MaxCombo = sr.ReadUInt16();
/* score.Perfect = */
sr.ReadBoolean();
/* score.EnabledMods = (Mods)*/
sr.ReadInt32();
/* score.HpGraphString = */
sr.ReadString();
/* score.Date = */
sr.ReadDateTime();
var compressedReplay = sr.ReadByteArray();
if (version >= 20140721)
/*OnlineId =*/
sr.ReadInt64();
else if (version >= 20121008)
/*OnlineId =*/
sr.ReadInt32();
using (var replayInStream = new MemoryStream(compressedReplay))
{
byte[] properties = new byte[5];
if (replayInStream.Read(properties, 0, 5) != 5)
throw new IOException("input .lzma is too short");
long outSize = 0;
for (int i = 0; i < 8; i++)
{
int v = replayInStream.ReadByte();
if (v < 0)
throw new IOException("Can't Read 1");
outSize |= (long)(byte)v << (8 * i);
}
long compressedSize = replayInStream.Length - replayInStream.Position;
using (var lzma = new LzmaStream(properties, replayInStream, compressedSize, outSize))
using (var reader = new StreamReader(lzma))
score.Replay = createLegacyReplay(reader);
}
}
return score;
}
/// <summary>
/// Creates a legacy replay which is read from a stream.
/// </summary>
/// <param name="reader">The stream reader.</param>
/// <returns>The legacy replay.</returns>
private Replay createLegacyReplay(StreamReader reader)
{
var frames = new List<ReplayFrame>();
float lastTime = 0;
foreach (var l in reader.ReadToEnd().Split(','))
{
var split = l.Split('|');
if (split.Length < 4 || float.Parse(split[0]) < 0) continue;
lastTime += float.Parse(split[0]);
frames.Add(new ReplayFrame(
lastTime,
float.Parse(split[1]),
384 - float.Parse(split[2]),
(ReplayButtonState)int.Parse(split[3])
));
}
return new Replay { Frames = frames };
}
protected override void Prepare(bool reset = false)
{
}
protected override Type[] ValidTypes => new[] { typeof(Score) };
}
}