mirror of
https://github.com/osukey/osukey.git
synced 2025-07-02 00:40:09 +09:00
Replay loading via drag-drop huzzah!
This commit is contained in:
Submodule osu-framework updated: 798409058a...7193ee4f91
@ -26,88 +26,13 @@ using osu.Game.Screens.Play;
|
|||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
using OpenTK.Input;
|
using OpenTK.Input;
|
||||||
using SharpCompress.Archives.SevenZip;
|
using SharpCompress.Archives.SevenZip;
|
||||||
|
using SharpCompress.Compressors.LZMA;
|
||||||
using SharpCompress.Readers;
|
using SharpCompress.Readers;
|
||||||
using KeyboardState = osu.Framework.Input.KeyboardState;
|
using KeyboardState = osu.Framework.Input.KeyboardState;
|
||||||
using MouseState = osu.Framework.Input.MouseState;
|
using MouseState = osu.Framework.Input.MouseState;
|
||||||
|
|
||||||
namespace osu.Desktop.VisualTests.Tests
|
namespace osu.Desktop.VisualTests.Tests
|
||||||
{
|
{
|
||||||
public class ScoreDatabase
|
|
||||||
{
|
|
||||||
private readonly Storage storage;
|
|
||||||
|
|
||||||
private const string replay_folder = @"replays";
|
|
||||||
|
|
||||||
public ScoreDatabase(Storage storage)
|
|
||||||
{
|
|
||||||
this.storage = storage;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Score ReadReplayFile(string replayFilename)
|
|
||||||
{
|
|
||||||
Score score;
|
|
||||||
|
|
||||||
using (Stream s = storage.GetStream(Path.Combine(replay_folder, replayFilename)))
|
|
||||||
using (SerializationReader sr = new SerializationReader(s))
|
|
||||||
{
|
|
||||||
var ruleset = Ruleset.GetRuleset((PlayMode)sr.ReadByte());
|
|
||||||
var processor = ruleset.CreateScoreProcessor();
|
|
||||||
|
|
||||||
score = processor.GetScore();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* score.Pass = true;*/
|
|
||||||
var version = sr.ReadInt32();
|
|
||||||
/* score.FileChecksum = */ sr.ReadString();
|
|
||||||
/* 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();
|
|
||||||
|
|
||||||
//new ASCIIEncoding().GetString(SevenZipHelper.Decompress(ReplayCompressed)));
|
|
||||||
|
|
||||||
score.Replay = new LegacyReplay();
|
|
||||||
|
|
||||||
//float lastTime = 0;
|
|
||||||
//foreach (var l in File.ReadAllText(@"C:\Users\Dean\Desktop\2157025197").Split(','))
|
|
||||||
//{
|
|
||||||
// var split = l.Split('|');
|
|
||||||
|
|
||||||
// if (split.Length < 4 || float.Parse(split[0]) < 0) continue;
|
|
||||||
|
|
||||||
// lastTime += float.Parse(split[0]);
|
|
||||||
|
|
||||||
// list.Add(new LegacyReplay.LegacyReplayInputHandler.LegacyReplayFrame(
|
|
||||||
// lastTime,
|
|
||||||
// float.Parse(split[1]),
|
|
||||||
// 384 - float.Parse(split[2]),
|
|
||||||
// (LegacyReplay.LegacyReplayInputHandler.LegacyButtonState)int.Parse(split[3])
|
|
||||||
// ));
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
|
|
||||||
return score;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class TestCaseReplay : TestCasePlayer
|
class TestCaseReplay : TestCasePlayer
|
||||||
{
|
{
|
||||||
private WorkingBeatmap beatmap;
|
private WorkingBeatmap beatmap;
|
||||||
@ -134,235 +59,4 @@ namespace osu.Desktop.VisualTests.Tests
|
|||||||
return player;
|
return player;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class LegacyReplay : Replay
|
|
||||||
{
|
|
||||||
private new List<LegacyReplayFrame> frames = new List<LegacyReplayFrame>();
|
|
||||||
|
|
||||||
public override ReplayInputHandler GetInputHandler() => new LegacyReplayInputHandler(frames);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The ReplayHandler will take a replay and handle the propagation of updates to the input stack.
|
|
||||||
/// It handles logic of any frames which *must* be executed.
|
|
||||||
/// </summary>
|
|
||||||
public class LegacyReplayInputHandler : ReplayInputHandler
|
|
||||||
{
|
|
||||||
private readonly List<LegacyReplayFrame> replayContent;
|
|
||||||
int currentFrameIndex;
|
|
||||||
|
|
||||||
public LegacyReplayFrame CurrentFrame => !hasFrames ? null : replayContent[currentFrameIndex];
|
|
||||||
public LegacyReplayFrame NextFrame => !hasFrames ? null : replayContent[MathHelper.Clamp(currentDirection > 0 ? currentFrameIndex + 1 : currentFrameIndex - 1, 0, replayContent.Count - 1)];
|
|
||||||
|
|
||||||
public LegacyReplayInputHandler(List<LegacyReplayFrame> replayContent)
|
|
||||||
{
|
|
||||||
this.replayContent = replayContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool nextFrame()
|
|
||||||
{
|
|
||||||
int newFrame = MathHelper.Clamp(currentFrameIndex + (currentDirection > 0 ? 1 : -1), 0, replayContent.Count - 1);
|
|
||||||
|
|
||||||
//ensure we aren't at an extent.
|
|
||||||
if (newFrame == currentFrameIndex) return false;
|
|
||||||
|
|
||||||
currentFrameIndex = newFrame;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetPosition(Vector2 pos)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
private Vector2? position
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (!hasFrames)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
if (AtLastFrame)
|
|
||||||
return CurrentFrame.Position;
|
|
||||||
|
|
||||||
return Interpolation.ValueAt(currentTime, CurrentFrame.Position, NextFrame.Position, CurrentFrame.Time, NextFrame.Time);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override List<InputState> GetPendingStates()
|
|
||||||
{
|
|
||||||
return new List<InputState>
|
|
||||||
{
|
|
||||||
new InputState
|
|
||||||
{
|
|
||||||
Mouse = new ReplayMouseState(
|
|
||||||
ToScreenSpace(position ?? Vector2.Zero),
|
|
||||||
new List<MouseState.ButtonState>
|
|
||||||
{
|
|
||||||
new MouseState.ButtonState(MouseButton.Left)
|
|
||||||
{
|
|
||||||
State = CurrentFrame?.MouseLeft ?? false
|
|
||||||
},
|
|
||||||
new MouseState.ButtonState(MouseButton.Right)
|
|
||||||
{
|
|
||||||
State = CurrentFrame?.MouseRight ?? false
|
|
||||||
},
|
|
||||||
}
|
|
||||||
),
|
|
||||||
Keyboard = new ReplayKeyboardState(new List<Key>())
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool AtLastFrame => currentFrameIndex == replayContent.Count - 1;
|
|
||||||
public bool AtFirstFrame => currentFrameIndex == 0;
|
|
||||||
|
|
||||||
public Vector2 Size => new Vector2(512, 384);
|
|
||||||
|
|
||||||
private const double sixty_frame_time = 1000 / 60;
|
|
||||||
|
|
||||||
double currentTime;
|
|
||||||
int currentDirection;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// When set, we will ensure frames executed by nested drawables are frame-accurate to replay data.
|
|
||||||
/// Disabling this can make replay playback smoother (useful for autoplay, currently).
|
|
||||||
/// </summary>
|
|
||||||
public bool FrameAccuratePlayback = true;
|
|
||||||
|
|
||||||
private bool hasFrames => replayContent.Count > 0;
|
|
||||||
|
|
||||||
bool inImportantSection =>
|
|
||||||
FrameAccuratePlayback &&
|
|
||||||
//a button is in a pressed state
|
|
||||||
(currentDirection > 0 ? CurrentFrame : NextFrame)?.ButtonState > LegacyButtonState.None &&
|
|
||||||
//the next frame is within an allowable time span
|
|
||||||
Math.Abs(currentTime - NextFrame?.Time ?? 0) <= sixty_frame_time * 1.2;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Update the current frame based on an incoming time value.
|
|
||||||
/// There are cases where we return a "must-use" time value that is different from the input.
|
|
||||||
/// This is to ensure accurate playback of replay data.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="time">The time which we should use for finding the current frame.</param>
|
|
||||||
/// <returns>The usable time value. If null, we shouldn't be running components reliant on this data.</returns>
|
|
||||||
public override double? SetFrameFromTime(double time)
|
|
||||||
{
|
|
||||||
currentDirection = time.CompareTo(currentTime);
|
|
||||||
if (currentDirection == 0) currentDirection = 1;
|
|
||||||
|
|
||||||
if (hasFrames)
|
|
||||||
{
|
|
||||||
//if we changed frames, we want to execute once *exactly* on the frame's time.
|
|
||||||
if (currentDirection == time.CompareTo(NextFrame.Time) && nextFrame())
|
|
||||||
return currentTime = CurrentFrame.Time;
|
|
||||||
|
|
||||||
//if we didn't change frames, we need to ensure we are allowed to run frames in between, else return null.
|
|
||||||
if (inImportantSection)
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return currentTime = time;
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ReplayMouseState : MouseState
|
|
||||||
{
|
|
||||||
public ReplayMouseState(Vector2 position, List<ButtonState> list)
|
|
||||||
{
|
|
||||||
Position = position;
|
|
||||||
ButtonStates = list;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ReplayKeyboardState : KeyboardState
|
|
||||||
{
|
|
||||||
public ReplayKeyboardState(List<Key> keys)
|
|
||||||
{
|
|
||||||
Keys = keys;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Flags]
|
|
||||||
public enum LegacyButtonState
|
|
||||||
{
|
|
||||||
None = 0,
|
|
||||||
Left1 = 1,
|
|
||||||
Right1 = 2,
|
|
||||||
Left2 = 4,
|
|
||||||
Right2 = 8,
|
|
||||||
Smoke = 16
|
|
||||||
}
|
|
||||||
|
|
||||||
public class LegacyReplayFrame
|
|
||||||
{
|
|
||||||
public Vector2 Position => new Vector2(MouseX, MouseY);
|
|
||||||
|
|
||||||
public float MouseX;
|
|
||||||
public float MouseY;
|
|
||||||
public bool MouseLeft;
|
|
||||||
public bool MouseRight;
|
|
||||||
public bool MouseLeft1;
|
|
||||||
public bool MouseRight1;
|
|
||||||
public bool MouseLeft2;
|
|
||||||
public bool MouseRight2;
|
|
||||||
public LegacyButtonState ButtonState;
|
|
||||||
public double Time;
|
|
||||||
|
|
||||||
public LegacyReplayFrame(double time, float posX, float posY, LegacyButtonState buttonState)
|
|
||||||
{
|
|
||||||
MouseX = posX;
|
|
||||||
MouseY = posY;
|
|
||||||
ButtonState = buttonState;
|
|
||||||
SetButtonStates(buttonState);
|
|
||||||
Time = time;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetButtonStates(LegacyButtonState buttonState)
|
|
||||||
{
|
|
||||||
ButtonState = buttonState;
|
|
||||||
MouseLeft = (buttonState & (LegacyButtonState.Left1 | LegacyButtonState.Left2)) > 0;
|
|
||||||
MouseLeft1 = (buttonState & LegacyButtonState.Left1) > 0;
|
|
||||||
MouseLeft2 = (buttonState & LegacyButtonState.Left2) > 0;
|
|
||||||
MouseRight = (buttonState & (LegacyButtonState.Right1 | LegacyButtonState.Right2)) > 0;
|
|
||||||
MouseRight1 = (buttonState & LegacyButtonState.Right1) > 0;
|
|
||||||
MouseRight2 = (buttonState & LegacyButtonState.Right2) > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public LegacyReplayFrame(Stream s) : this(new SerializationReader(s))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public LegacyReplayFrame(SerializationReader sr)
|
|
||||||
{
|
|
||||||
ButtonState = (LegacyButtonState)sr.ReadByte();
|
|
||||||
SetButtonStates(ButtonState);
|
|
||||||
|
|
||||||
byte bt = sr.ReadByte();
|
|
||||||
if (bt > 0)//Handle Pre-Taiko compatible replays.
|
|
||||||
SetButtonStates(LegacyButtonState.Right1);
|
|
||||||
|
|
||||||
MouseX = sr.ReadSingle();
|
|
||||||
MouseY = sr.ReadSingle();
|
|
||||||
Time = sr.ReadInt32();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ReadFromStream(SerializationReader sr)
|
|
||||||
{
|
|
||||||
throw new System.NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void WriteToStream(SerializationWriter sw)
|
|
||||||
{
|
|
||||||
sw.Write((byte)ButtonState);
|
|
||||||
sw.Write((byte)0);
|
|
||||||
sw.Write(MouseX);
|
|
||||||
sw.Write(MouseY);
|
|
||||||
sw.Write(Time);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return $"{Time}\t({MouseX},{MouseY})\t{MouseLeft}\t{MouseRight}\t{MouseLeft1}\t{MouseRight1}\t{MouseLeft2}\t{MouseRight2}\t{ButtonState}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ using osu.Framework.Desktop.Platform;
|
|||||||
using osu.Desktop.Overlays;
|
using osu.Desktop.Overlays;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
|
using System.IO;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using osu.Game.Screens.Menu;
|
using osu.Game.Screens.Menu;
|
||||||
|
|
||||||
@ -55,19 +56,29 @@ namespace osu.Desktop
|
|||||||
private void dragDrop(DragEventArgs e)
|
private void dragDrop(DragEventArgs e)
|
||||||
{
|
{
|
||||||
// this method will only be executed if e.Effect in dragEnter gets set to something other that None.
|
// this method will only be executed if e.Effect in dragEnter gets set to something other that None.
|
||||||
var dropData = e.Data.GetData(DataFormats.FileDrop) as object[];
|
var dropData = (object[])e.Data.GetData(DataFormats.FileDrop);
|
||||||
var filePaths = dropData.Select(f => f.ToString()).ToArray();
|
var filePaths = dropData.Select(f => f.ToString()).ToArray();
|
||||||
Task.Run(() => BeatmapDatabase.Import(filePaths));
|
|
||||||
|
if (filePaths.All(f => Path.GetExtension(f) == @".osz"))
|
||||||
|
Task.Run(() => BeatmapDatabase.Import(filePaths));
|
||||||
|
else if (filePaths.All(f => Path.GetExtension(f) == @".osr"))
|
||||||
|
Task.Run(() =>
|
||||||
|
{
|
||||||
|
var score = ScoreDatabase.ReadReplayFile(filePaths.First());
|
||||||
|
Schedule(() => LoadScore(score));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static readonly string[] allowed_extensions = { @".osz", @".osr" };
|
||||||
|
|
||||||
private void dragEnter(DragEventArgs e)
|
private void dragEnter(DragEventArgs e)
|
||||||
{
|
{
|
||||||
// dragDrop will only be executed if e.Effect gets set to something other that None in this method.
|
// dragDrop will only be executed if e.Effect gets set to something other that None in this method.
|
||||||
bool isFile = e.Data.GetDataPresent(DataFormats.FileDrop);
|
bool isFile = e.Data.GetDataPresent(DataFormats.FileDrop);
|
||||||
if (isFile)
|
if (isFile)
|
||||||
{
|
{
|
||||||
var paths = (e.Data.GetData(DataFormats.FileDrop) as object[]).Select(f => f.ToString()).ToArray();
|
var paths = ((object[])e.Data.GetData(DataFormats.FileDrop)).Select(f => f.ToString()).ToArray();
|
||||||
if (paths.Any(p => !p.EndsWith(".osz")))
|
if (paths.Any(p => !allowed_extensions.Any(ext => p.EndsWith(ext))))
|
||||||
e.Effect = DragDropEffects.None;
|
e.Effect = DragDropEffects.None;
|
||||||
else
|
else
|
||||||
e.Effect = DragDropEffects.Copy;
|
e.Effect = DragDropEffects.Copy;
|
||||||
|
@ -7,6 +7,7 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
|
using osu.Framework.Extensions;
|
||||||
using osu.Framework.Logging;
|
using osu.Framework.Logging;
|
||||||
using osu.Framework.Platform;
|
using osu.Framework.Platform;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
@ -162,7 +163,7 @@ namespace osu.Game.Database
|
|||||||
/// <param name="path">Location on disk</param>
|
/// <param name="path">Location on disk</param>
|
||||||
public void Import(string path)
|
public void Import(string path)
|
||||||
{
|
{
|
||||||
Import(new [] { path });
|
Import(new[] { path });
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -181,10 +182,9 @@ namespace osu.Game.Database
|
|||||||
|
|
||||||
if (File.Exists(path)) // Not always the case, i.e. for LegacyFilesystemReader
|
if (File.Exists(path)) // Not always the case, i.e. for LegacyFilesystemReader
|
||||||
{
|
{
|
||||||
using (var md5 = MD5.Create())
|
|
||||||
using (var input = storage.GetStream(path))
|
using (var input = storage.GetStream(path))
|
||||||
{
|
{
|
||||||
hash = BitConverter.ToString(md5.ComputeHash(input)).Replace("-", "").ToLowerInvariant();
|
hash = input.GetMd5Hash();
|
||||||
input.Seek(0, SeekOrigin.Begin);
|
input.Seek(0, SeekOrigin.Begin);
|
||||||
path = Path.Combine(@"beatmaps", hash.Remove(1), hash.Remove(2), hash);
|
path = Path.Combine(@"beatmaps", hash.Remove(1), hash.Remove(2), hash);
|
||||||
if (!storage.Exists(path))
|
if (!storage.Exists(path))
|
||||||
@ -216,22 +216,29 @@ namespace osu.Game.Database
|
|||||||
Metadata = metadata
|
Metadata = metadata
|
||||||
};
|
};
|
||||||
|
|
||||||
using (var reader = ArchiveReader.GetReader(storage, path))
|
using (var archive = ArchiveReader.GetReader(storage, path))
|
||||||
{
|
{
|
||||||
string[] mapNames = reader.BeatmapFilenames;
|
string[] mapNames = archive.BeatmapFilenames;
|
||||||
foreach (var name in mapNames)
|
foreach (var name in mapNames)
|
||||||
using (var stream = new StreamReader(reader.GetStream(name)))
|
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))
|
||||||
{
|
{
|
||||||
var decoder = BeatmapDecoder.GetDecoder(stream);
|
raw.CopyTo(ms);
|
||||||
Beatmap beatmap = decoder.Decode(stream);
|
ms.Position = 0;
|
||||||
|
|
||||||
|
var decoder = BeatmapDecoder.GetDecoder(sr);
|
||||||
|
Beatmap beatmap = decoder.Decode(sr);
|
||||||
|
|
||||||
beatmap.BeatmapInfo.Path = name;
|
beatmap.BeatmapInfo.Path = name;
|
||||||
|
beatmap.BeatmapInfo.Hash = ms.GetMd5Hash();
|
||||||
|
|
||||||
// TODO: Diff beatmap metadata with set metadata and leave it here if necessary
|
// TODO: Diff beatmap metadata with set metadata and leave it here if necessary
|
||||||
beatmap.BeatmapInfo.Metadata = null;
|
beatmap.BeatmapInfo.Metadata = null;
|
||||||
|
|
||||||
beatmapSet.Beatmaps.Add(beatmap.BeatmapInfo);
|
beatmapSet.Beatmaps.Add(beatmap.BeatmapInfo);
|
||||||
}
|
}
|
||||||
beatmapSet.StoryboardFile = reader.StoryboardFilename;
|
beatmapSet.StoryboardFile = archive.StoryboardFilename;
|
||||||
}
|
}
|
||||||
|
|
||||||
return beatmapSet;
|
return beatmapSet;
|
||||||
|
@ -39,6 +39,8 @@ namespace osu.Game.Database
|
|||||||
|
|
||||||
public string Path { get; set; }
|
public string Path { get; set; }
|
||||||
|
|
||||||
|
public string Hash { get; set; }
|
||||||
|
|
||||||
// General
|
// General
|
||||||
public int AudioLeadIn { get; set; }
|
public int AudioLeadIn { get; set; }
|
||||||
public bool Countdown { get; set; }
|
public bool Countdown { get; set; }
|
||||||
@ -64,6 +66,7 @@ namespace osu.Game.Database
|
|||||||
StoredBookmarks = string.Join(",", value);
|
StoredBookmarks = string.Join(",", value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public double DistanceSpacing { get; set; }
|
public double DistanceSpacing { get; set; }
|
||||||
public int BeatDivisor { get; set; }
|
public int BeatDivisor { get; set; }
|
||||||
public int GridSize { get; set; }
|
public int GridSize { get; set; }
|
||||||
|
110
osu.Game/Database/ScoreDatabase.cs
Normal file
110
osu.Game/Database/ScoreDatabase.cs
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
// 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.IO;
|
||||||
|
using osu.Framework.Platform;
|
||||||
|
using osu.Game.IO.Legacy;
|
||||||
|
using osu.Game.IPC;
|
||||||
|
using osu.Game.Modes;
|
||||||
|
using SharpCompress.Compressors.LZMA;
|
||||||
|
|
||||||
|
namespace osu.Game.Database
|
||||||
|
{
|
||||||
|
public class ScoreDatabase
|
||||||
|
{
|
||||||
|
private readonly Storage storage;
|
||||||
|
private readonly BeatmapDatabase beatmaps;
|
||||||
|
|
||||||
|
private const string replay_folder = @"replays";
|
||||||
|
|
||||||
|
private ScoreIPCChannel ipc;
|
||||||
|
|
||||||
|
public ScoreDatabase(Storage storage, IIpcHost importHost = null, BeatmapDatabase beatmaps = null)
|
||||||
|
{
|
||||||
|
this.storage = storage;
|
||||||
|
this.beatmaps = beatmaps;
|
||||||
|
|
||||||
|
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))
|
||||||
|
{
|
||||||
|
var ruleset = Ruleset.GetRuleset((PlayMode)sr.ReadByte());
|
||||||
|
var processor = ruleset.CreateScoreProcessor();
|
||||||
|
|
||||||
|
score = processor.GetScore();
|
||||||
|
|
||||||
|
/* score.Pass = true;*/
|
||||||
|
var version = sr.ReadInt32();
|
||||||
|
/* score.FileChecksum = */
|
||||||
|
var beatmapHash = sr.ReadString();
|
||||||
|
score.Beatmap = beatmaps.Query<BeatmapInfo>().Where(b => b.Hash == beatmapHash).FirstOrDefault();
|
||||||
|
/* 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 Exception("input .lzma is too short"));
|
||||||
|
long outSize = 0;
|
||||||
|
for (int i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
int v = replayInStream.ReadByte();
|
||||||
|
if (v < 0)
|
||||||
|
throw (new Exception("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 = new LegacyReplay(reader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
43
osu.Game/IPC/ScoreIPCChannel.cs
Normal file
43
osu.Game/IPC/ScoreIPCChannel.cs
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
// 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.Diagnostics;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using osu.Framework.Platform;
|
||||||
|
using osu.Game.Database;
|
||||||
|
|
||||||
|
namespace osu.Game.IPC
|
||||||
|
{
|
||||||
|
public class ScoreIPCChannel : IpcChannel<ScoreImportMessage>
|
||||||
|
{
|
||||||
|
private ScoreDatabase scores;
|
||||||
|
|
||||||
|
public ScoreIPCChannel(IIpcHost host, ScoreDatabase scores = null)
|
||||||
|
: base(host)
|
||||||
|
{
|
||||||
|
this.scores = scores;
|
||||||
|
MessageReceived += (msg) =>
|
||||||
|
{
|
||||||
|
Debug.Assert(scores != null);
|
||||||
|
ImportAsync(msg.Path);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ImportAsync(string path)
|
||||||
|
{
|
||||||
|
if (scores == null)
|
||||||
|
{
|
||||||
|
//we want to contact a remote osu! to handle the import.
|
||||||
|
await SendMessageAsync(new ScoreImportMessage { Path = path });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
scores.ReadReplayFile(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ScoreImportMessage
|
||||||
|
{
|
||||||
|
public string Path;
|
||||||
|
}
|
||||||
|
}
|
269
osu.Game/Modes/LegacyReplay.cs
Normal file
269
osu.Game/Modes/LegacyReplay.cs
Normal file
@ -0,0 +1,269 @@
|
|||||||
|
// 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 osu.Framework.Input;
|
||||||
|
using osu.Framework.MathUtils;
|
||||||
|
using osu.Game.Input.Handlers;
|
||||||
|
using osu.Game.IO.Legacy;
|
||||||
|
using OpenTK;
|
||||||
|
using OpenTK.Input;
|
||||||
|
using KeyboardState = osu.Framework.Input.KeyboardState;
|
||||||
|
using MouseState = osu.Framework.Input.MouseState;
|
||||||
|
|
||||||
|
namespace osu.Game.Modes
|
||||||
|
{
|
||||||
|
public class LegacyReplay : Replay
|
||||||
|
{
|
||||||
|
private List<LegacyReplayFrame> frames = new List<LegacyReplayFrame>();
|
||||||
|
|
||||||
|
public LegacyReplay(StreamReader reader)
|
||||||
|
{
|
||||||
|
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 LegacyReplayFrame(
|
||||||
|
lastTime,
|
||||||
|
float.Parse(split[1]),
|
||||||
|
384 - float.Parse(split[2]),
|
||||||
|
(LegacyButtonState)int.Parse(split[3])
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override ReplayInputHandler GetInputHandler() => new LegacyReplayInputHandler(frames);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The ReplayHandler will take a replay and handle the propagation of updates to the input stack.
|
||||||
|
/// It handles logic of any frames which *must* be executed.
|
||||||
|
/// </summary>
|
||||||
|
public class LegacyReplayInputHandler : ReplayInputHandler
|
||||||
|
{
|
||||||
|
private readonly List<LegacyReplayFrame> replayContent;
|
||||||
|
int currentFrameIndex;
|
||||||
|
|
||||||
|
public LegacyReplayFrame CurrentFrame => !hasFrames ? null : replayContent[currentFrameIndex];
|
||||||
|
public LegacyReplayFrame NextFrame => !hasFrames ? null : replayContent[MathHelper.Clamp(currentDirection > 0 ? currentFrameIndex + 1 : currentFrameIndex - 1, 0, replayContent.Count - 1)];
|
||||||
|
|
||||||
|
public LegacyReplayInputHandler(List<LegacyReplayFrame> replayContent)
|
||||||
|
{
|
||||||
|
this.replayContent = replayContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool nextFrame()
|
||||||
|
{
|
||||||
|
int newFrame = MathHelper.Clamp(currentFrameIndex + (currentDirection > 0 ? 1 : -1), 0, replayContent.Count - 1);
|
||||||
|
|
||||||
|
//ensure we aren't at an extent.
|
||||||
|
if (newFrame == currentFrameIndex) return false;
|
||||||
|
|
||||||
|
currentFrameIndex = newFrame;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetPosition(Vector2 pos)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private Vector2? position
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!hasFrames)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (AtLastFrame)
|
||||||
|
return CurrentFrame.Position;
|
||||||
|
|
||||||
|
return Interpolation.ValueAt(currentTime, CurrentFrame.Position, NextFrame.Position, CurrentFrame.Time, NextFrame.Time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override List<InputState> GetPendingStates()
|
||||||
|
{
|
||||||
|
return new List<InputState>
|
||||||
|
{
|
||||||
|
new InputState
|
||||||
|
{
|
||||||
|
Mouse = new ReplayMouseState(
|
||||||
|
ToScreenSpace(position ?? Vector2.Zero),
|
||||||
|
new List<MouseState.ButtonState>
|
||||||
|
{
|
||||||
|
new MouseState.ButtonState(MouseButton.Left)
|
||||||
|
{
|
||||||
|
State = CurrentFrame?.MouseLeft ?? false
|
||||||
|
},
|
||||||
|
new MouseState.ButtonState(MouseButton.Right)
|
||||||
|
{
|
||||||
|
State = CurrentFrame?.MouseRight ?? false
|
||||||
|
},
|
||||||
|
}
|
||||||
|
),
|
||||||
|
Keyboard = new ReplayKeyboardState(new List<Key>())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool AtLastFrame => currentFrameIndex == replayContent.Count - 1;
|
||||||
|
public bool AtFirstFrame => currentFrameIndex == 0;
|
||||||
|
|
||||||
|
public Vector2 Size => new Vector2(512, 384);
|
||||||
|
|
||||||
|
private const double sixty_frame_time = 1000 / 60;
|
||||||
|
|
||||||
|
double currentTime;
|
||||||
|
int currentDirection;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When set, we will ensure frames executed by nested drawables are frame-accurate to replay data.
|
||||||
|
/// Disabling this can make replay playback smoother (useful for autoplay, currently).
|
||||||
|
/// </summary>
|
||||||
|
public bool FrameAccuratePlayback = true;
|
||||||
|
|
||||||
|
private bool hasFrames => replayContent.Count > 0;
|
||||||
|
|
||||||
|
bool inImportantSection =>
|
||||||
|
FrameAccuratePlayback &&
|
||||||
|
//a button is in a pressed state
|
||||||
|
(currentDirection > 0 ? CurrentFrame : NextFrame)?.ButtonState > LegacyButtonState.None &&
|
||||||
|
//the next frame is within an allowable time span
|
||||||
|
Math.Abs(currentTime - NextFrame?.Time ?? 0) <= sixty_frame_time * 1.2;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Update the current frame based on an incoming time value.
|
||||||
|
/// There are cases where we return a "must-use" time value that is different from the input.
|
||||||
|
/// This is to ensure accurate playback of replay data.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="time">The time which we should use for finding the current frame.</param>
|
||||||
|
/// <returns>The usable time value. If null, we shouldn't be running components reliant on this data.</returns>
|
||||||
|
public override double? SetFrameFromTime(double time)
|
||||||
|
{
|
||||||
|
currentDirection = time.CompareTo(currentTime);
|
||||||
|
if (currentDirection == 0) currentDirection = 1;
|
||||||
|
|
||||||
|
if (hasFrames)
|
||||||
|
{
|
||||||
|
//if we changed frames, we want to execute once *exactly* on the frame's time.
|
||||||
|
if (currentDirection == time.CompareTo(NextFrame.Time) && nextFrame())
|
||||||
|
return currentTime = CurrentFrame.Time;
|
||||||
|
|
||||||
|
//if we didn't change frames, we need to ensure we are allowed to run frames in between, else return null.
|
||||||
|
if (inImportantSection)
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return currentTime = time;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ReplayMouseState : MouseState
|
||||||
|
{
|
||||||
|
public ReplayMouseState(Vector2 position, List<ButtonState> list)
|
||||||
|
{
|
||||||
|
Position = position;
|
||||||
|
ButtonStates = list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ReplayKeyboardState : KeyboardState
|
||||||
|
{
|
||||||
|
public ReplayKeyboardState(List<Key> keys)
|
||||||
|
{
|
||||||
|
Keys = keys;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
public enum LegacyButtonState
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
Left1 = 1,
|
||||||
|
Right1 = 2,
|
||||||
|
Left2 = 4,
|
||||||
|
Right2 = 8,
|
||||||
|
Smoke = 16
|
||||||
|
}
|
||||||
|
|
||||||
|
public class LegacyReplayFrame
|
||||||
|
{
|
||||||
|
public Vector2 Position => new Vector2(MouseX, MouseY);
|
||||||
|
|
||||||
|
public float MouseX;
|
||||||
|
public float MouseY;
|
||||||
|
public bool MouseLeft;
|
||||||
|
public bool MouseRight;
|
||||||
|
public bool MouseLeft1;
|
||||||
|
public bool MouseRight1;
|
||||||
|
public bool MouseLeft2;
|
||||||
|
public bool MouseRight2;
|
||||||
|
public LegacyButtonState ButtonState;
|
||||||
|
public double Time;
|
||||||
|
|
||||||
|
public LegacyReplayFrame(double time, float posX, float posY, LegacyButtonState buttonState)
|
||||||
|
{
|
||||||
|
MouseX = posX;
|
||||||
|
MouseY = posY;
|
||||||
|
ButtonState = buttonState;
|
||||||
|
SetButtonStates(buttonState);
|
||||||
|
Time = time;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetButtonStates(LegacyButtonState buttonState)
|
||||||
|
{
|
||||||
|
ButtonState = buttonState;
|
||||||
|
MouseLeft = (buttonState & (LegacyButtonState.Left1 | LegacyButtonState.Left2)) > 0;
|
||||||
|
MouseLeft1 = (buttonState & LegacyButtonState.Left1) > 0;
|
||||||
|
MouseLeft2 = (buttonState & LegacyButtonState.Left2) > 0;
|
||||||
|
MouseRight = (buttonState & (LegacyButtonState.Right1 | LegacyButtonState.Right2)) > 0;
|
||||||
|
MouseRight1 = (buttonState & LegacyButtonState.Right1) > 0;
|
||||||
|
MouseRight2 = (buttonState & LegacyButtonState.Right2) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LegacyReplayFrame(Stream s) : this(new SerializationReader(s))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public LegacyReplayFrame(SerializationReader sr)
|
||||||
|
{
|
||||||
|
ButtonState = (LegacyButtonState)sr.ReadByte();
|
||||||
|
SetButtonStates(ButtonState);
|
||||||
|
|
||||||
|
byte bt = sr.ReadByte();
|
||||||
|
if (bt > 0)//Handle Pre-Taiko compatible replays.
|
||||||
|
SetButtonStates(LegacyButtonState.Right1);
|
||||||
|
|
||||||
|
MouseX = sr.ReadSingle();
|
||||||
|
MouseY = sr.ReadSingle();
|
||||||
|
Time = sr.ReadInt32();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReadFromStream(SerializationReader sr)
|
||||||
|
{
|
||||||
|
throw new System.NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteToStream(SerializationWriter sw)
|
||||||
|
{
|
||||||
|
sw.Write((byte)ButtonState);
|
||||||
|
sw.Write((byte)0);
|
||||||
|
sw.Write(MouseX);
|
||||||
|
sw.Write(MouseY);
|
||||||
|
sw.Write(Time);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return $"{Time}\t({MouseX},{MouseY})\t{MouseLeft}\t{MouseRight}\t{MouseLeft1}\t{MouseRight1}\t{MouseLeft2}\t{MouseRight2}\t{ButtonState}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
12
osu.Game/Modes/Replay.cs
Normal file
12
osu.Game/Modes/Replay.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using osu.Game.Input.Handlers;
|
||||||
|
|
||||||
|
namespace osu.Game.Modes
|
||||||
|
{
|
||||||
|
public abstract class Replay
|
||||||
|
{
|
||||||
|
public virtual ReplayInputHandler GetInputHandler() => null;
|
||||||
|
}
|
||||||
|
}
|
@ -2,7 +2,8 @@
|
|||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using osu.Framework.Input.Handlers;
|
using osu.Framework.Input.Handlers;
|
||||||
using osu.Game.Input.Handlers;
|
using osu.Game.Database;
|
||||||
|
using SQLite.Net;
|
||||||
|
|
||||||
namespace osu.Game.Modes
|
namespace osu.Game.Modes
|
||||||
{
|
{
|
||||||
@ -15,10 +16,6 @@ namespace osu.Game.Modes
|
|||||||
public double Health { get; set; }
|
public double Health { get; set; }
|
||||||
|
|
||||||
public Replay Replay;
|
public Replay Replay;
|
||||||
}
|
public BeatmapInfo Beatmap;
|
||||||
|
|
||||||
public class Replay
|
|
||||||
{
|
|
||||||
public virtual ReplayInputHandler GetInputHandler() => null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,9 +24,10 @@ using osu.Game.Screens.Menu;
|
|||||||
using OpenTK;
|
using OpenTK;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Graphics.Primitives;
|
using osu.Framework.Graphics.Primitives;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Overlays.Notifications;
|
using osu.Game.Overlays.Notifications;
|
||||||
|
using osu.Game.Screens.Play;
|
||||||
|
|
||||||
namespace osu.Game
|
namespace osu.Game
|
||||||
{
|
{
|
||||||
@ -92,6 +93,39 @@ namespace osu.Game
|
|||||||
PlayMode = LocalConfig.GetBindable<PlayMode>(OsuConfig.PlayMode);
|
PlayMode = LocalConfig.GetBindable<PlayMode>(OsuConfig.PlayMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void LoadScore(Score s)
|
||||||
|
{
|
||||||
|
var menu = intro.ChildScreen;
|
||||||
|
|
||||||
|
if (menu == null)
|
||||||
|
{
|
||||||
|
Schedule(() => LoadScore(s));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!menu.IsCurrentScreen)
|
||||||
|
{
|
||||||
|
menu.MakeCurrent();
|
||||||
|
Delay(500);
|
||||||
|
Schedule(() => LoadScore(s));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s.Beatmap == null)
|
||||||
|
{
|
||||||
|
notificationManager.Post(new SimpleNotification
|
||||||
|
{
|
||||||
|
Text = @"Tried to load a score for a beatmap we don't have!",
|
||||||
|
Icon = FontAwesome.fa_life_saver,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Beatmap.Value = BeatmapDatabase.GetWorkingBeatmap(s.Beatmap);
|
||||||
|
|
||||||
|
menu.Push(new PlayerLoader(new Player { ReplayInputHandler = s.Replay.GetInputHandler() }));
|
||||||
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
@ -25,6 +25,8 @@ namespace osu.Game
|
|||||||
|
|
||||||
protected BeatmapDatabase BeatmapDatabase;
|
protected BeatmapDatabase BeatmapDatabase;
|
||||||
|
|
||||||
|
protected ScoreDatabase ScoreDatabase;
|
||||||
|
|
||||||
protected override string MainResourceFile => @"osu.Game.Resources.dll";
|
protected override string MainResourceFile => @"osu.Game.Resources.dll";
|
||||||
|
|
||||||
public APIAccess API;
|
public APIAccess API;
|
||||||
@ -43,6 +45,7 @@ namespace osu.Game
|
|||||||
Dependencies.Cache(this);
|
Dependencies.Cache(this);
|
||||||
Dependencies.Cache(LocalConfig);
|
Dependencies.Cache(LocalConfig);
|
||||||
Dependencies.Cache(BeatmapDatabase = new BeatmapDatabase(Host.Storage, Host));
|
Dependencies.Cache(BeatmapDatabase = new BeatmapDatabase(Host.Storage, Host));
|
||||||
|
Dependencies.Cache(ScoreDatabase = new ScoreDatabase(Host.Storage, Host, BeatmapDatabase));
|
||||||
Dependencies.Cache(new OsuColour());
|
Dependencies.Cache(new OsuColour());
|
||||||
|
|
||||||
//this completely overrides the framework default. will need to change once we make a proper FontStore.
|
//this completely overrides the framework default. will need to change once we make a proper FontStore.
|
||||||
|
15
osu.Game/app.config
Normal file
15
osu.Game/app.config
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
-->
|
||||||
|
<configuration>
|
||||||
|
<runtime>
|
||||||
|
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
||||||
|
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
|
||||||
|
</dependentAssembly>
|
||||||
|
</assemblyBinding>
|
||||||
|
</runtime>
|
||||||
|
</configuration>
|
@ -43,6 +43,10 @@
|
|||||||
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1340\lib\net45\OpenTK.dll</HintPath>
|
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1340\lib\net45\OpenTK.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="SharpCompress, Version=0.15.1.0, Culture=neutral, PublicKeyToken=afb0a02973931d96, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\SharpCompress.0.15.1\lib\net45\SharpCompress.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
<Reference Include="SQLite.Net, Version=3.1.0.0, Culture=neutral, processorArchitecture=MSIL">
|
<Reference Include="SQLite.Net, Version=3.1.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||||
<HintPath>$(SolutionDir)\packages\SQLite.Net.Core-PCL.3.1.1\lib\portable-win8+net45+wp8+wpa81+MonoAndroid1+MonoTouch1\SQLite.Net.dll</HintPath>
|
<HintPath>$(SolutionDir)\packages\SQLite.Net.Core-PCL.3.1.1\lib\portable-win8+net45+wp8+wpa81+MonoAndroid1+MonoTouch1\SQLite.Net.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
@ -69,6 +73,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Beatmaps\Drawables\BeatmapBackgroundSprite.cs" />
|
<Compile Include="Beatmaps\Drawables\BeatmapBackgroundSprite.cs" />
|
||||||
<Compile Include="Beatmaps\DifficultyCalculator.cs" />
|
<Compile Include="Beatmaps\DifficultyCalculator.cs" />
|
||||||
|
<Compile Include="Database\ScoreDatabase.cs" />
|
||||||
<Compile Include="Graphics\Backgrounds\Triangles.cs" />
|
<Compile Include="Graphics\Backgrounds\Triangles.cs" />
|
||||||
<Compile Include="Graphics\Cursor\CursorTrail.cs" />
|
<Compile Include="Graphics\Cursor\CursorTrail.cs" />
|
||||||
<Compile Include="Graphics\Sprites\OsuSpriteText.cs" />
|
<Compile Include="Graphics\Sprites\OsuSpriteText.cs" />
|
||||||
@ -83,9 +88,12 @@
|
|||||||
<Compile Include="IO\Legacy\ILegacySerializable.cs" />
|
<Compile Include="IO\Legacy\ILegacySerializable.cs" />
|
||||||
<Compile Include="IO\Legacy\SerializationReader.cs" />
|
<Compile Include="IO\Legacy\SerializationReader.cs" />
|
||||||
<Compile Include="IO\Legacy\SerializationWriter.cs" />
|
<Compile Include="IO\Legacy\SerializationWriter.cs" />
|
||||||
|
<Compile Include="IPC\ScoreIPCChannel.cs" />
|
||||||
|
<Compile Include="Modes\LegacyReplay.cs" />
|
||||||
<Compile Include="Modes\Objects\Drawables\IDrawableHitObjectWithProxiedApproach.cs" />
|
<Compile Include="Modes\Objects\Drawables\IDrawableHitObjectWithProxiedApproach.cs" />
|
||||||
<Compile Include="Modes\Objects\HitObjectParser.cs" />
|
<Compile Include="Modes\Objects\HitObjectParser.cs" />
|
||||||
<Compile Include="Modes\Objects\NullHitObjectParser.cs" />
|
<Compile Include="Modes\Objects\NullHitObjectParser.cs" />
|
||||||
|
<Compile Include="Modes\Replay.cs" />
|
||||||
<Compile Include="Modes\Score.cs" />
|
<Compile Include="Modes\Score.cs" />
|
||||||
<Compile Include="Modes\ScoreProcesssor.cs" />
|
<Compile Include="Modes\ScoreProcesssor.cs" />
|
||||||
<Compile Include="Modes\UI\HealthDisplay.cs" />
|
<Compile Include="Modes\UI\HealthDisplay.cs" />
|
||||||
@ -329,6 +337,7 @@
|
|||||||
<None Include="..\osu.licenseheader">
|
<None Include="..\osu.licenseheader">
|
||||||
<Link>osu.licenseheader</Link>
|
<Link>osu.licenseheader</Link>
|
||||||
</None>
|
</None>
|
||||||
|
<None Include="app.config" />
|
||||||
<None Include="packages.config" />
|
<None Include="packages.config" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup />
|
<ItemGroup />
|
||||||
|
@ -7,6 +7,7 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste
|
|||||||
<package id="DotNetZip" version="1.10.1" targetFramework="net45" />
|
<package id="DotNetZip" version="1.10.1" targetFramework="net45" />
|
||||||
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net45" />
|
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net45" />
|
||||||
<package id="ppy.OpenTK" version="2.0.50727.1340" targetFramework="net45" />
|
<package id="ppy.OpenTK" version="2.0.50727.1340" targetFramework="net45" />
|
||||||
|
<package id="SharpCompress" version="0.15.1" targetFramework="net45" />
|
||||||
<package id="SQLite.Net.Core-PCL" version="3.1.1" targetFramework="net45" />
|
<package id="SQLite.Net.Core-PCL" version="3.1.1" targetFramework="net45" />
|
||||||
<package id="SQLite.Net-PCL" version="3.1.1" targetFramework="net45" />
|
<package id="SQLite.Net-PCL" version="3.1.1" targetFramework="net45" />
|
||||||
<package id="SQLiteNetExtensions" version="1.3.0" targetFramework="net45" />
|
<package id="SQLiteNetExtensions" version="1.3.0" targetFramework="net45" />
|
||||||
|
Reference in New Issue
Block a user