mirror of
https://github.com/osukey/osukey.git
synced 2025-07-02 00:40:09 +09:00
Basic partial replay support.
This commit is contained in:
Submodule osu-framework updated: 4c0762eec2...b32d1542d4
15
osu.Desktop.VisualTests/Tests/TestCaseReplay.cs
Normal file
15
osu.Desktop.VisualTests/Tests/TestCaseReplay.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// 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.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace osu.Desktop.VisualTests.Tests
|
||||||
|
{
|
||||||
|
class TestCaseReplay
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@ -186,6 +186,7 @@
|
|||||||
<Compile Include="Tests\TestCaseHitObjects.cs" />
|
<Compile Include="Tests\TestCaseHitObjects.cs" />
|
||||||
<Compile Include="Tests\TestCaseKeyCounter.cs" />
|
<Compile Include="Tests\TestCaseKeyCounter.cs" />
|
||||||
<Compile Include="Tests\TestCaseMenuButtonSystem.cs" />
|
<Compile Include="Tests\TestCaseMenuButtonSystem.cs" />
|
||||||
|
<Compile Include="Tests\TestCaseReplay.cs" />
|
||||||
<Compile Include="Tests\TestCaseScoreCounter.cs" />
|
<Compile Include="Tests\TestCaseScoreCounter.cs" />
|
||||||
<Compile Include="Tests\TestCaseTextAwesome.cs" />
|
<Compile Include="Tests\TestCaseTextAwesome.cs" />
|
||||||
<Compile Include="Tests\TestCasePlaySongSelect.cs" />
|
<Compile Include="Tests\TestCasePlaySongSelect.cs" />
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// 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;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Modes.Catch.UI;
|
using osu.Game.Modes.Catch.UI;
|
||||||
using osu.Game.Modes.Objects;
|
using osu.Game.Modes.Objects;
|
||||||
@ -14,7 +15,11 @@ namespace osu.Game.Modes.Catch
|
|||||||
{
|
{
|
||||||
public override ScoreOverlay CreateScoreOverlay() => new OsuScoreOverlay();
|
public override ScoreOverlay CreateScoreOverlay() => new OsuScoreOverlay();
|
||||||
|
|
||||||
public override HitRenderer CreateHitRendererWith(Beatmap beatmap) => new CatchHitRenderer { Beatmap = beatmap };
|
public override HitRenderer CreateHitRendererWith(Beatmap beatmap, InputManager input = null) => new CatchHitRenderer
|
||||||
|
{
|
||||||
|
Beatmap = beatmap,
|
||||||
|
InputManager = input,
|
||||||
|
};
|
||||||
|
|
||||||
protected override PlayMode PlayMode => PlayMode.Catch;
|
protected override PlayMode PlayMode => PlayMode.Catch;
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// 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;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Modes.Mania.UI;
|
using osu.Game.Modes.Mania.UI;
|
||||||
using osu.Game.Modes.Objects;
|
using osu.Game.Modes.Objects;
|
||||||
@ -14,7 +15,11 @@ namespace osu.Game.Modes.Mania
|
|||||||
{
|
{
|
||||||
public override ScoreOverlay CreateScoreOverlay() => new OsuScoreOverlay();
|
public override ScoreOverlay CreateScoreOverlay() => new OsuScoreOverlay();
|
||||||
|
|
||||||
public override HitRenderer CreateHitRendererWith(Beatmap beatmap) => new ManiaHitRenderer { Beatmap = beatmap };
|
public override HitRenderer CreateHitRendererWith(Beatmap beatmap, InputManager input = null) => new ManiaHitRenderer
|
||||||
|
{
|
||||||
|
Beatmap = beatmap,
|
||||||
|
InputManager = input,
|
||||||
|
};
|
||||||
|
|
||||||
protected override PlayMode PlayMode => PlayMode.Mania;
|
protected override PlayMode PlayMode => PlayMode.Mania;
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using osu.Framework.Input;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Modes.Objects;
|
using osu.Game.Modes.Objects;
|
||||||
@ -16,7 +17,11 @@ namespace osu.Game.Modes.Osu
|
|||||||
{
|
{
|
||||||
public override ScoreOverlay CreateScoreOverlay() => new OsuScoreOverlay();
|
public override ScoreOverlay CreateScoreOverlay() => new OsuScoreOverlay();
|
||||||
|
|
||||||
public override HitRenderer CreateHitRendererWith(Beatmap beatmap) => new OsuHitRenderer { Beatmap = beatmap };
|
public override HitRenderer CreateHitRendererWith(Beatmap beatmap, InputManager input = null) => new OsuHitRenderer
|
||||||
|
{
|
||||||
|
Beatmap = beatmap,
|
||||||
|
InputManager = input
|
||||||
|
};
|
||||||
|
|
||||||
public override IEnumerable<BeatmapStatistic> GetBeatmapStatistics(WorkingBeatmap beatmap) => new[]
|
public override IEnumerable<BeatmapStatistic> GetBeatmapStatistics(WorkingBeatmap beatmap) => new[]
|
||||||
{
|
{
|
||||||
|
@ -10,6 +10,7 @@ using osu.Game.Modes.Osu.Objects.Drawables;
|
|||||||
using osu.Game.Modes.Osu.Objects.Drawables.Connections;
|
using osu.Game.Modes.Osu.Objects.Drawables.Connections;
|
||||||
using osu.Game.Modes.UI;
|
using osu.Game.Modes.UI;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using osu.Game.Graphics.Cursor;
|
||||||
|
|
||||||
namespace osu.Game.Modes.Osu.UI
|
namespace osu.Game.Modes.Osu.UI
|
||||||
{
|
{
|
||||||
@ -53,7 +54,8 @@ namespace osu.Game.Modes.Osu.UI
|
|||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Depth = -1,
|
Depth = -1,
|
||||||
}
|
},
|
||||||
|
new OsuCursorContainer()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// 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;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Modes.Objects;
|
using osu.Game.Modes.Objects;
|
||||||
using osu.Game.Modes.Osu.UI;
|
using osu.Game.Modes.Osu.UI;
|
||||||
@ -14,7 +15,11 @@ namespace osu.Game.Modes.Taiko
|
|||||||
{
|
{
|
||||||
public override ScoreOverlay CreateScoreOverlay() => new OsuScoreOverlay();
|
public override ScoreOverlay CreateScoreOverlay() => new OsuScoreOverlay();
|
||||||
|
|
||||||
public override HitRenderer CreateHitRendererWith(Beatmap beatmap) => new TaikoHitRenderer { Beatmap = beatmap };
|
public override HitRenderer CreateHitRendererWith(Beatmap beatmap, InputManager input = null) => new TaikoHitRenderer
|
||||||
|
{
|
||||||
|
Beatmap = beatmap,
|
||||||
|
InputManager = input,
|
||||||
|
};
|
||||||
|
|
||||||
protected override PlayMode PlayMode => PlayMode.Taiko;
|
protected override PlayMode PlayMode => PlayMode.Taiko;
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ using System;
|
|||||||
|
|
||||||
namespace osu.Game.Graphics.Cursor
|
namespace osu.Game.Graphics.Cursor
|
||||||
{
|
{
|
||||||
class OsuCursorContainer : CursorContainer
|
public class OsuCursorContainer : CursorContainer
|
||||||
{
|
{
|
||||||
protected override Drawable CreateCursor() => new OsuCursor();
|
protected override Drawable CreateCursor() => new OsuCursor();
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ namespace osu.Game.Graphics.Cursor
|
|||||||
return base.OnMouseUp(state, args);
|
return base.OnMouseUp(state, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
class OsuCursor : Container
|
public class OsuCursor : Container
|
||||||
{
|
{
|
||||||
private Container cursorContainer;
|
private Container cursorContainer;
|
||||||
private Bindable<double> cursorScale;
|
private Bindable<double> cursorScale;
|
||||||
|
11
osu.Game/IO/Legacy/ILegacySerializable.cs
Normal file
11
osu.Game/IO/Legacy/ILegacySerializable.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
// 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.IO.Legacy
|
||||||
|
{
|
||||||
|
public interface ILegacySerializable
|
||||||
|
{
|
||||||
|
void ReadFromStream(SerializationReader sr);
|
||||||
|
void WriteToStream(SerializationWriter sw);
|
||||||
|
}
|
||||||
|
}
|
279
osu.Game/IO/Legacy/SerializationReader.cs
Normal file
279
osu.Game/IO/Legacy/SerializationReader.cs
Normal file
@ -0,0 +1,279 @@
|
|||||||
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.Serialization;
|
||||||
|
using System.Runtime.Serialization.Formatters;
|
||||||
|
using System.Runtime.Serialization.Formatters.Binary;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace osu.Game.IO.Legacy
|
||||||
|
{
|
||||||
|
/// <summary> SerializationReader. Extends BinaryReader to add additional data types,
|
||||||
|
/// handle null strings and simplify use with ISerializable. </summary>
|
||||||
|
public class SerializationReader : BinaryReader
|
||||||
|
{
|
||||||
|
Stream stream;
|
||||||
|
|
||||||
|
public SerializationReader(Stream s)
|
||||||
|
: base(s, Encoding.UTF8)
|
||||||
|
{
|
||||||
|
stream = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int RemainingBytes => (int)(stream.Length - stream.Position);
|
||||||
|
|
||||||
|
/// <summary> Static method to take a SerializationInfo object (an input to an ISerializable constructor)
|
||||||
|
/// and produce a SerializationReader from which serialized objects can be read </summary>.
|
||||||
|
public static SerializationReader GetReader(SerializationInfo info)
|
||||||
|
{
|
||||||
|
byte[] byteArray = (byte[])info.GetValue("X", typeof(byte[]));
|
||||||
|
MemoryStream ms = new MemoryStream(byteArray);
|
||||||
|
return new SerializationReader(ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Reads a string from the buffer. Overrides the base implementation so it can cope with nulls. </summary>
|
||||||
|
public override string ReadString()
|
||||||
|
{
|
||||||
|
if (0 == ReadByte()) return null;
|
||||||
|
return base.ReadString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Reads a byte array from the buffer, handling nulls and the array length. </summary>
|
||||||
|
public byte[] ReadByteArray()
|
||||||
|
{
|
||||||
|
int len = ReadInt32();
|
||||||
|
if (len > 0) return ReadBytes(len);
|
||||||
|
if (len < 0) return null;
|
||||||
|
return new byte[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Reads a char array from the buffer, handling nulls and the array length. </summary>
|
||||||
|
public char[] ReadCharArray()
|
||||||
|
{
|
||||||
|
int len = ReadInt32();
|
||||||
|
if (len > 0) return ReadChars(len);
|
||||||
|
if (len < 0) return null;
|
||||||
|
return new char[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Reads a DateTime from the buffer. </summary>
|
||||||
|
public DateTime ReadDateTime()
|
||||||
|
{
|
||||||
|
long ticks = ReadInt64();
|
||||||
|
if (ticks < 0) throw new AbandonedMutexException("oops");
|
||||||
|
return new DateTime(ticks, DateTimeKind.Utc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Reads a generic list from the buffer. </summary>
|
||||||
|
public IList<T> ReadBList<T>(bool skipErrors = false) where T : ILegacySerializable, new()
|
||||||
|
{
|
||||||
|
int count = ReadInt32();
|
||||||
|
if (count < 0) return null;
|
||||||
|
IList<T> d = new List<T>(count);
|
||||||
|
|
||||||
|
SerializationReader sr = new SerializationReader(BaseStream);
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
T obj = new T();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
obj.ReadFromStream(sr);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
if (skipErrors)
|
||||||
|
continue;
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
d.Add(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Reads a generic list from the buffer. </summary>
|
||||||
|
public IList<T> ReadList<T>()
|
||||||
|
{
|
||||||
|
int count = ReadInt32();
|
||||||
|
if (count < 0) return null;
|
||||||
|
IList<T> d = new List<T>(count);
|
||||||
|
for (int i = 0; i < count; i++) d.Add((T)ReadObject());
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Reads a generic Dictionary from the buffer. </summary>
|
||||||
|
public IDictionary<T, U> ReadDictionary<T, U>()
|
||||||
|
{
|
||||||
|
int count = ReadInt32();
|
||||||
|
if (count < 0) return null;
|
||||||
|
IDictionary<T, U> d = new Dictionary<T, U>();
|
||||||
|
for (int i = 0; i < count; i++) d[(T)ReadObject()] = (U)ReadObject();
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Reads an object which was added to the buffer by WriteObject. </summary>
|
||||||
|
public object ReadObject()
|
||||||
|
{
|
||||||
|
ObjType t = (ObjType)ReadByte();
|
||||||
|
switch (t)
|
||||||
|
{
|
||||||
|
case ObjType.boolType:
|
||||||
|
return ReadBoolean();
|
||||||
|
case ObjType.byteType:
|
||||||
|
return ReadByte();
|
||||||
|
case ObjType.uint16Type:
|
||||||
|
return ReadUInt16();
|
||||||
|
case ObjType.uint32Type:
|
||||||
|
return ReadUInt32();
|
||||||
|
case ObjType.uint64Type:
|
||||||
|
return ReadUInt64();
|
||||||
|
case ObjType.sbyteType:
|
||||||
|
return ReadSByte();
|
||||||
|
case ObjType.int16Type:
|
||||||
|
return ReadInt16();
|
||||||
|
case ObjType.int32Type:
|
||||||
|
return ReadInt32();
|
||||||
|
case ObjType.int64Type:
|
||||||
|
return ReadInt64();
|
||||||
|
case ObjType.charType:
|
||||||
|
return ReadChar();
|
||||||
|
case ObjType.stringType:
|
||||||
|
return base.ReadString();
|
||||||
|
case ObjType.singleType:
|
||||||
|
return ReadSingle();
|
||||||
|
case ObjType.doubleType:
|
||||||
|
return ReadDouble();
|
||||||
|
case ObjType.decimalType:
|
||||||
|
return ReadDecimal();
|
||||||
|
case ObjType.dateTimeType:
|
||||||
|
return ReadDateTime();
|
||||||
|
case ObjType.byteArrayType:
|
||||||
|
return ReadByteArray();
|
||||||
|
case ObjType.charArrayType:
|
||||||
|
return ReadCharArray();
|
||||||
|
case ObjType.otherType:
|
||||||
|
return DynamicDeserializer.Deserialize(BaseStream);
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DynamicDeserializer
|
||||||
|
{
|
||||||
|
private static VersionConfigToNamespaceAssemblyObjectBinder versionBinder;
|
||||||
|
private static BinaryFormatter formatter;
|
||||||
|
|
||||||
|
private static void initialize()
|
||||||
|
{
|
||||||
|
versionBinder = new VersionConfigToNamespaceAssemblyObjectBinder();
|
||||||
|
formatter = new BinaryFormatter();
|
||||||
|
formatter.AssemblyFormat = FormatterAssemblyStyle.Simple;
|
||||||
|
formatter.Binder = versionBinder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static object Deserialize(Stream stream)
|
||||||
|
{
|
||||||
|
if (formatter == null)
|
||||||
|
initialize();
|
||||||
|
return formatter.Deserialize(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Nested type: VersionConfigToNamespaceAssemblyObjectBinder
|
||||||
|
|
||||||
|
public sealed class VersionConfigToNamespaceAssemblyObjectBinder : SerializationBinder
|
||||||
|
{
|
||||||
|
private readonly Dictionary<string, Type> cache = new Dictionary<string, Type>();
|
||||||
|
|
||||||
|
public override Type BindToType(string assemblyName, string typeName)
|
||||||
|
{
|
||||||
|
Type typeToDeserialize;
|
||||||
|
|
||||||
|
if (cache.TryGetValue(assemblyName + typeName, out typeToDeserialize))
|
||||||
|
return typeToDeserialize;
|
||||||
|
|
||||||
|
List<Type> tmpTypes = new List<Type>();
|
||||||
|
Type genType = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (typeName.Contains("System.Collections.Generic") && typeName.Contains("[["))
|
||||||
|
{
|
||||||
|
string[] splitTyps = typeName.Split('[');
|
||||||
|
|
||||||
|
foreach (string typ in splitTyps)
|
||||||
|
{
|
||||||
|
if (typ.Contains("Version"))
|
||||||
|
{
|
||||||
|
string asmTmp = typ.Substring(typ.IndexOf(',') + 1);
|
||||||
|
string asmName = asmTmp.Remove(asmTmp.IndexOf(']')).Trim();
|
||||||
|
string typName = typ.Remove(typ.IndexOf(','));
|
||||||
|
tmpTypes.Add(BindToType(asmName, typName));
|
||||||
|
}
|
||||||
|
else if (typ.Contains("Generic"))
|
||||||
|
{
|
||||||
|
genType = BindToType(assemblyName, typ);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (genType != null && tmpTypes.Count > 0)
|
||||||
|
{
|
||||||
|
return genType.MakeGenericType(tmpTypes.ToArray());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string toAssemblyName = assemblyName.Split(',')[0];
|
||||||
|
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
|
||||||
|
foreach (Assembly a in assemblies)
|
||||||
|
{
|
||||||
|
if (a.FullName.Split(',')[0] == toAssemblyName)
|
||||||
|
{
|
||||||
|
typeToDeserialize = a.GetType(typeName);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
throw exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
cache.Add(assemblyName + typeName, typeToDeserialize);
|
||||||
|
|
||||||
|
return typeToDeserialize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ObjType : byte
|
||||||
|
{
|
||||||
|
nullType,
|
||||||
|
boolType,
|
||||||
|
byteType,
|
||||||
|
uint16Type,
|
||||||
|
uint32Type,
|
||||||
|
uint64Type,
|
||||||
|
sbyteType,
|
||||||
|
int16Type,
|
||||||
|
int32Type,
|
||||||
|
int64Type,
|
||||||
|
charType,
|
||||||
|
stringType,
|
||||||
|
singleType,
|
||||||
|
doubleType,
|
||||||
|
decimalType,
|
||||||
|
dateTimeType,
|
||||||
|
byteArrayType,
|
||||||
|
charArrayType,
|
||||||
|
otherType,
|
||||||
|
ILegacySerializableType
|
||||||
|
}
|
||||||
|
}
|
255
osu.Game/IO/Legacy/SerializationWriter.cs
Normal file
255
osu.Game/IO/Legacy/SerializationWriter.cs
Normal file
@ -0,0 +1,255 @@
|
|||||||
|
// 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.Runtime.Serialization;
|
||||||
|
using System.Runtime.Serialization.Formatters;
|
||||||
|
using System.Runtime.Serialization.Formatters.Binary;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace osu.Game.IO.Legacy
|
||||||
|
{
|
||||||
|
/// <summary> SerializationWriter. Extends BinaryWriter to add additional data types,
|
||||||
|
/// handle null strings and simplify use with ISerializable. </summary>
|
||||||
|
public class SerializationWriter : BinaryWriter
|
||||||
|
{
|
||||||
|
public SerializationWriter(Stream s)
|
||||||
|
: base(s, Encoding.UTF8)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Static method to initialise the writer with a suitable MemoryStream. </summary>
|
||||||
|
public static SerializationWriter GetWriter()
|
||||||
|
{
|
||||||
|
MemoryStream ms = new MemoryStream(1024);
|
||||||
|
return new SerializationWriter(ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Writes a string to the buffer. Overrides the base implementation so it can cope with nulls </summary>
|
||||||
|
public override void Write(string str)
|
||||||
|
{
|
||||||
|
if (str == null)
|
||||||
|
{
|
||||||
|
Write((byte)ObjType.nullType);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Write((byte)ObjType.stringType);
|
||||||
|
base.Write(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Writes a byte array to the buffer. Overrides the base implementation to
|
||||||
|
/// send the length of the array which is needed when it is retrieved </summary>
|
||||||
|
public override void Write(byte[] b)
|
||||||
|
{
|
||||||
|
if (b == null)
|
||||||
|
{
|
||||||
|
Write(-1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int len = b.Length;
|
||||||
|
Write(len);
|
||||||
|
if (len > 0) base.Write(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Writes a char array to the buffer. Overrides the base implementation to
|
||||||
|
/// sends the length of the array which is needed when it is read. </summary>
|
||||||
|
public override void Write(char[] c)
|
||||||
|
{
|
||||||
|
if (c == null)
|
||||||
|
{
|
||||||
|
Write(-1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int len = c.Length;
|
||||||
|
Write(len);
|
||||||
|
if (len > 0) base.Write(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Writes a DateTime to the buffer. <summary>
|
||||||
|
public void Write(DateTime dt)
|
||||||
|
{
|
||||||
|
Write(dt.ToUniversalTime().Ticks);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Writes a generic ICollection (such as an IList<T>) to the buffer. </summary>
|
||||||
|
public void Write<T>(List<T> c) where T : ILegacySerializable
|
||||||
|
{
|
||||||
|
if (c == null)
|
||||||
|
{
|
||||||
|
Write(-1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int count = c.Count;
|
||||||
|
Write(count);
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
c[i].WriteToStream(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Writes a generic IDictionary to the buffer. </summary>
|
||||||
|
public void Write<T, U>(IDictionary<T, U> d)
|
||||||
|
{
|
||||||
|
if (d == null)
|
||||||
|
{
|
||||||
|
Write(-1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Write(d.Count);
|
||||||
|
foreach (KeyValuePair<T, U> kvp in d)
|
||||||
|
{
|
||||||
|
WriteObject(kvp.Key);
|
||||||
|
WriteObject(kvp.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Writes an arbitrary object to the buffer. Useful where we have something of type "object"
|
||||||
|
/// and don't know how to treat it. This works out the best method to use to write to the buffer. </summary>
|
||||||
|
public void WriteObject(object obj)
|
||||||
|
{
|
||||||
|
if (obj == null)
|
||||||
|
{
|
||||||
|
Write((byte)ObjType.nullType);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (obj.GetType().Name)
|
||||||
|
{
|
||||||
|
case "Boolean":
|
||||||
|
Write((byte)ObjType.boolType);
|
||||||
|
Write((bool)obj);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "Byte":
|
||||||
|
Write((byte)ObjType.byteType);
|
||||||
|
Write((byte)obj);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "UInt16":
|
||||||
|
Write((byte)ObjType.uint16Type);
|
||||||
|
Write((ushort)obj);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "UInt32":
|
||||||
|
Write((byte)ObjType.uint32Type);
|
||||||
|
Write((uint)obj);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "UInt64":
|
||||||
|
Write((byte)ObjType.uint64Type);
|
||||||
|
Write((ulong)obj);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "SByte":
|
||||||
|
Write((byte)ObjType.sbyteType);
|
||||||
|
Write((sbyte)obj);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "Int16":
|
||||||
|
Write((byte)ObjType.int16Type);
|
||||||
|
Write((short)obj);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "Int32":
|
||||||
|
Write((byte)ObjType.int32Type);
|
||||||
|
Write((int)obj);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "Int64":
|
||||||
|
Write((byte)ObjType.int64Type);
|
||||||
|
Write((long)obj);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "Char":
|
||||||
|
Write((byte)ObjType.charType);
|
||||||
|
base.Write((char)obj);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "String":
|
||||||
|
Write((byte)ObjType.stringType);
|
||||||
|
base.Write((string)obj);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "Single":
|
||||||
|
Write((byte)ObjType.singleType);
|
||||||
|
Write((float)obj);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "Double":
|
||||||
|
Write((byte)ObjType.doubleType);
|
||||||
|
Write((double)obj);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "Decimal":
|
||||||
|
Write((byte)ObjType.decimalType);
|
||||||
|
Write((decimal)obj);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "DateTime":
|
||||||
|
Write((byte)ObjType.dateTimeType);
|
||||||
|
Write((DateTime)obj);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "Byte[]":
|
||||||
|
Write((byte)ObjType.byteArrayType);
|
||||||
|
base.Write((byte[])obj);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "Char[]":
|
||||||
|
Write((byte)ObjType.charArrayType);
|
||||||
|
base.Write((char[])obj);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Write((byte)ObjType.otherType);
|
||||||
|
BinaryFormatter b = new BinaryFormatter();
|
||||||
|
b.AssemblyFormat = FormatterAssemblyStyle.Simple;
|
||||||
|
b.TypeFormat = FormatterTypeStyle.TypesWhenNeeded;
|
||||||
|
b.Serialize(BaseStream, obj);
|
||||||
|
break;
|
||||||
|
} // switch
|
||||||
|
} // if obj==null
|
||||||
|
} // WriteObject
|
||||||
|
|
||||||
|
/// <summary> Adds the SerializationWriter buffer to the SerializationInfo at the end of GetObjectData(). </summary>
|
||||||
|
public void AddToInfo(SerializationInfo info)
|
||||||
|
{
|
||||||
|
byte[] b = ((MemoryStream)BaseStream).ToArray();
|
||||||
|
info.AddValue("X", b, typeof(byte[]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteRawBytes(byte[] b)
|
||||||
|
{
|
||||||
|
base.Write(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteByteArray(byte[] b)
|
||||||
|
{
|
||||||
|
if (b == null)
|
||||||
|
{
|
||||||
|
Write(-1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int len = b.Length;
|
||||||
|
Write(len);
|
||||||
|
if (len > 0) base.Write(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteUtf8(string str)
|
||||||
|
{
|
||||||
|
WriteRawBytes(Encoding.UTF8.GetBytes(str));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
244
osu.Game/Input/Handlers/LegacyReplayInputHandler.cs
Normal file
244
osu.Game/Input/Handlers/LegacyReplayInputHandler.cs
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
// 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.Handlers;
|
||||||
|
using osu.Framework.MathUtils;
|
||||||
|
using osu.Framework.Platform;
|
||||||
|
using OpenTK;
|
||||||
|
using osu.Framework.Input;
|
||||||
|
using osu.Game.IO.Legacy;
|
||||||
|
using OpenTK.Input;
|
||||||
|
using KeyboardState = osu.Framework.Input.KeyboardState;
|
||||||
|
using MouseState = osu.Framework.Input.MouseState;
|
||||||
|
|
||||||
|
namespace osu.Game.Input.Handlers
|
||||||
|
{
|
||||||
|
/// <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 : InputHandler
|
||||||
|
{
|
||||||
|
public Func<Vector2, Vector2> ToScreenSpace { private get; set; }
|
||||||
|
|
||||||
|
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 override bool Initialize(GameHost host) => true;
|
||||||
|
|
||||||
|
public override bool IsActive => true;
|
||||||
|
|
||||||
|
public override int Priority => 0;
|
||||||
|
|
||||||
|
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 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}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,7 @@ using osu.Game.Modes.Objects;
|
|||||||
using osu.Game.Modes.UI;
|
using osu.Game.Modes.UI;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
using osu.Framework.Input;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
|
|
||||||
@ -28,7 +29,7 @@ namespace osu.Game.Modes
|
|||||||
|
|
||||||
public abstract ScoreProcessor CreateScoreProcessor(int hitObjectCount);
|
public abstract ScoreProcessor CreateScoreProcessor(int hitObjectCount);
|
||||||
|
|
||||||
public abstract HitRenderer CreateHitRendererWith(Beatmap beatmap);
|
public abstract HitRenderer CreateHitRendererWith(Beatmap beatmap, InputManager input = null);
|
||||||
|
|
||||||
public abstract HitObjectParser CreateHitObjectParser();
|
public abstract HitObjectParser CreateHitObjectParser();
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ using System.Linq;
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Input;
|
||||||
using osu.Game.Modes.Objects;
|
using osu.Game.Modes.Objects;
|
||||||
using osu.Game.Modes.Objects.Drawables;
|
using osu.Game.Modes.Objects.Drawables;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
@ -19,6 +20,8 @@ namespace osu.Game.Modes.UI
|
|||||||
|
|
||||||
public event Action OnAllJudged;
|
public event Action OnAllJudged;
|
||||||
|
|
||||||
|
public InputManager InputManager;
|
||||||
|
|
||||||
protected void TriggerOnJudgement(JudgementInfo j)
|
protected void TriggerOnJudgement(JudgementInfo j)
|
||||||
{
|
{
|
||||||
OnJudgement?.Invoke(j);
|
OnJudgement?.Invoke(j);
|
||||||
@ -62,10 +65,10 @@ namespace osu.Game.Modes.UI
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
Children = new Drawable[]
|
Playfield = CreatePlayfield();
|
||||||
{
|
Playfield.InputManager = InputManager;
|
||||||
Playfield = CreatePlayfield()
|
|
||||||
};
|
Add(Playfield);
|
||||||
|
|
||||||
loadObjects();
|
loadObjects();
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// 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.Allocation;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Input;
|
||||||
using osu.Game.Modes.Objects.Drawables;
|
using osu.Game.Modes.Objects.Drawables;
|
||||||
|
|
||||||
namespace osu.Game.Modes.UI
|
namespace osu.Game.Modes.UI
|
||||||
@ -11,7 +13,7 @@ namespace osu.Game.Modes.UI
|
|||||||
public abstract class Playfield : Container
|
public abstract class Playfield : Container
|
||||||
{
|
{
|
||||||
public HitObjectContainer HitObjects;
|
public HitObjectContainer HitObjects;
|
||||||
private Container<Drawable> content;
|
private Container<Drawable> scaledContent;
|
||||||
|
|
||||||
public virtual void Add(DrawableHitObject h) => HitObjects.Add(h);
|
public virtual void Add(DrawableHitObject h) => HitObjects.Add(h);
|
||||||
|
|
||||||
@ -19,11 +21,20 @@ namespace osu.Game.Modes.UI
|
|||||||
|
|
||||||
protected override Container<Drawable> Content => content;
|
protected override Container<Drawable> Content => content;
|
||||||
|
|
||||||
|
private Container content;
|
||||||
|
|
||||||
public Playfield()
|
public Playfield()
|
||||||
{
|
{
|
||||||
AddInternal(content = new ScaledContainer()
|
AddInternal(scaledContent = new ScaledContainer
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Children = new[]
|
||||||
|
{
|
||||||
|
content = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Add(HitObjects = new HitObjectContainer
|
Add(HitObjects = new HitObjectContainer
|
||||||
@ -32,6 +43,23 @@ namespace osu.Game.Modes.UI
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An optional inputManager to provide interactivity etc.
|
||||||
|
/// </summary>
|
||||||
|
public InputManager InputManager;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
if (InputManager != null)
|
||||||
|
{
|
||||||
|
//if we've been provided an InputManager, we want it to sit inside the scaledcontainer
|
||||||
|
scaledContent.Remove(content);
|
||||||
|
scaledContent.Add(InputManager);
|
||||||
|
InputManager.Add(content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public virtual void PostProcess()
|
public virtual void PostProcess()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -19,12 +19,16 @@ using osu.Game.Configuration;
|
|||||||
using osu.Game.Overlays.Pause;
|
using osu.Game.Overlays.Pause;
|
||||||
using osu.Framework.Configuration;
|
using osu.Framework.Configuration;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Transforms;
|
using osu.Framework.Graphics.Transforms;
|
||||||
|
using osu.Framework.Input;
|
||||||
using osu.Framework.Logging;
|
using osu.Framework.Logging;
|
||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
|
using osu.Game.Graphics.Cursor;
|
||||||
|
using osu.Game.Input.Handlers;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Play
|
namespace osu.Game.Screens.Play
|
||||||
{
|
{
|
||||||
@ -57,6 +61,7 @@ namespace osu.Game.Screens.Play
|
|||||||
private bool canPause => Time.Current >= (lastPauseActionTime + pauseCooldown);
|
private bool canPause => Time.Current >= (lastPauseActionTime + pauseCooldown);
|
||||||
|
|
||||||
private IAdjustableClock sourceClock;
|
private IAdjustableClock sourceClock;
|
||||||
|
private IFrameBasedClock interpolatedSourceClock;
|
||||||
|
|
||||||
private Ruleset ruleset;
|
private Ruleset ruleset;
|
||||||
|
|
||||||
@ -67,7 +72,6 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
private ScoreOverlay scoreOverlay;
|
private ScoreOverlay scoreOverlay;
|
||||||
private PauseOverlay pauseOverlay;
|
private PauseOverlay pauseOverlay;
|
||||||
private PlayerInputManager playerInputManager;
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(AudioManager audio, BeatmapDatabase beatmaps, OsuGameBase game, OsuConfigManager config)
|
private void load(AudioManager audio, BeatmapDatabase beatmaps, OsuGameBase game, OsuConfigManager config)
|
||||||
@ -101,6 +105,7 @@ namespace osu.Game.Screens.Play
|
|||||||
}
|
}
|
||||||
|
|
||||||
sourceClock = (IAdjustableClock)track ?? new StopwatchClock();
|
sourceClock = (IAdjustableClock)track ?? new StopwatchClock();
|
||||||
|
interpolatedSourceClock = new InterpolatingFramedClock(sourceClock);
|
||||||
|
|
||||||
Schedule(() =>
|
Schedule(() =>
|
||||||
{
|
{
|
||||||
@ -135,7 +140,20 @@ namespace osu.Game.Screens.Play
|
|||||||
OnQuit = Exit
|
OnQuit = Exit
|
||||||
};
|
};
|
||||||
|
|
||||||
hitRenderer = ruleset.CreateHitRendererWith(beatmap);
|
hitRenderer = ruleset.CreateHitRendererWith(beatmap, new PlayerInputManager
|
||||||
|
{
|
||||||
|
ReplayInputHandler = new LegacyReplayInputHandler(new List<LegacyReplayInputHandler.LegacyReplayFrame>
|
||||||
|
{
|
||||||
|
new LegacyReplayInputHandler.LegacyReplayFrame(0, 0, 0, LegacyReplayInputHandler.LegacyButtonState.None),
|
||||||
|
new LegacyReplayInputHandler.LegacyReplayFrame(500, 512, 0, LegacyReplayInputHandler.LegacyButtonState.None),
|
||||||
|
new LegacyReplayInputHandler.LegacyReplayFrame(1000, 512, 384, LegacyReplayInputHandler.LegacyButtonState.None),
|
||||||
|
new LegacyReplayInputHandler.LegacyReplayFrame(1500, 0, 384, LegacyReplayInputHandler.LegacyButtonState.None),
|
||||||
|
new LegacyReplayInputHandler.LegacyReplayFrame(2000, 0, 0, LegacyReplayInputHandler.LegacyButtonState.None),
|
||||||
|
new LegacyReplayInputHandler.LegacyReplayFrame(2500, 512, 0, LegacyReplayInputHandler.LegacyButtonState.None),
|
||||||
|
new LegacyReplayInputHandler.LegacyReplayFrame(3000, 512, 384, LegacyReplayInputHandler.LegacyButtonState.None),
|
||||||
|
new LegacyReplayInputHandler.LegacyReplayFrame(3500, 0, 384, LegacyReplayInputHandler.LegacyButtonState.None),
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
//bind HitRenderer to ScoreProcessor and ourselves (for a pass situation)
|
//bind HitRenderer to ScoreProcessor and ourselves (for a pass situation)
|
||||||
hitRenderer.OnJudgement += scoreProcessor.AddJudgement;
|
hitRenderer.OnJudgement += scoreProcessor.AddJudgement;
|
||||||
@ -149,14 +167,17 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
playerInputManager = new PlayerInputManager(game.Host)
|
new Container
|
||||||
{
|
{
|
||||||
Clock = new InterpolatingFramedClock(sourceClock),
|
RelativeSizeAxes = Axes.Both,
|
||||||
PassThrough = false,
|
Clock = interpolatedSourceClock,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
hitRenderer,
|
hitRenderer,
|
||||||
skipButton = new SkipButton { Alpha = 0 },
|
skipButton = new SkipButton
|
||||||
|
{
|
||||||
|
Alpha = 0
|
||||||
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
scoreOverlay,
|
scoreOverlay,
|
||||||
@ -196,7 +217,6 @@ namespace osu.Game.Screens.Play
|
|||||||
if (canPause || force)
|
if (canPause || force)
|
||||||
{
|
{
|
||||||
lastPauseActionTime = Time.Current;
|
lastPauseActionTime = Time.Current;
|
||||||
playerInputManager.PassThrough = true;
|
|
||||||
scoreOverlay.KeyCounter.IsCounting = false;
|
scoreOverlay.KeyCounter.IsCounting = false;
|
||||||
pauseOverlay.Retries = RestartCount;
|
pauseOverlay.Retries = RestartCount;
|
||||||
pauseOverlay.Show();
|
pauseOverlay.Show();
|
||||||
@ -212,7 +232,6 @@ namespace osu.Game.Screens.Play
|
|||||||
public void Resume()
|
public void Resume()
|
||||||
{
|
{
|
||||||
lastPauseActionTime = Time.Current;
|
lastPauseActionTime = Time.Current;
|
||||||
playerInputManager.PassThrough = false;
|
|
||||||
scoreOverlay.KeyCounter.IsCounting = true;
|
scoreOverlay.KeyCounter.IsCounting = true;
|
||||||
pauseOverlay.Hide();
|
pauseOverlay.Hide();
|
||||||
sourceClock.Start();
|
sourceClock.Start();
|
||||||
@ -238,8 +257,8 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
if (!Push(newPlayer))
|
if (!Push(newPlayer))
|
||||||
{
|
{
|
||||||
// Error(?)
|
// Error(?)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,46 +1,75 @@
|
|||||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// 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 OpenTK.Input;
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Configuration;
|
using osu.Framework.Configuration;
|
||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
using osu.Framework.Platform;
|
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using osu.Framework.Timing;
|
||||||
|
using osu.Game.Input.Handlers;
|
||||||
|
using OpenTK.Input;
|
||||||
|
using KeyboardState = osu.Framework.Input.KeyboardState;
|
||||||
|
using MouseState = osu.Framework.Input.MouseState;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Play
|
namespace osu.Game.Screens.Play
|
||||||
{
|
{
|
||||||
class PlayerInputManager : UserInputManager
|
public class PlayerInputManager : PassThroughInputManager
|
||||||
{
|
{
|
||||||
public PlayerInputManager(GameHost host)
|
private bool leftViaKeyboard;
|
||||||
: base(host)
|
private bool rightViaKeyboard;
|
||||||
|
private Bindable<bool> mouseDisabled;
|
||||||
|
|
||||||
|
private ManualClock clock = new ManualClock();
|
||||||
|
private IFrameBasedClock parentClock;
|
||||||
|
|
||||||
|
private LegacyReplayInputHandler replayInputHandler;
|
||||||
|
public LegacyReplayInputHandler ReplayInputHandler
|
||||||
{
|
{
|
||||||
|
get { return replayInputHandler; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (replayInputHandler != null) RemoveHandler(replayInputHandler);
|
||||||
|
|
||||||
|
replayInputHandler = value;
|
||||||
|
UseParentState = replayInputHandler == null;
|
||||||
|
|
||||||
|
if (replayInputHandler != null)
|
||||||
|
{
|
||||||
|
replayInputHandler.ToScreenSpace = ToScreenSpace;
|
||||||
|
AddHandler(replayInputHandler);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool leftViaKeyboard;
|
protected override void LoadComplete()
|
||||||
bool rightViaKeyboard;
|
{
|
||||||
Bindable<bool> mouseDisabled;
|
base.LoadComplete();
|
||||||
|
|
||||||
|
parentClock = Clock;
|
||||||
|
Clock = new FramedClock(clock);
|
||||||
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuConfigManager config)
|
private void load(OsuConfigManager config)
|
||||||
{
|
{
|
||||||
mouseDisabled = config.GetBindable<bool>(OsuConfig.MouseDisableButtons)
|
mouseDisabled = config.GetBindable<bool>(OsuConfig.MouseDisableButtons);
|
||||||
?? new Bindable<bool>(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void TransformState(InputState state)
|
protected override void TransformState(InputState state)
|
||||||
{
|
{
|
||||||
base.TransformState(state);
|
base.TransformState(state);
|
||||||
|
|
||||||
if (state.Keyboard != null)
|
var mouse = state.Mouse as MouseState;
|
||||||
|
var keyboard = state.Keyboard as KeyboardState;
|
||||||
|
|
||||||
|
if (keyboard != null)
|
||||||
{
|
{
|
||||||
leftViaKeyboard = state.Keyboard.Keys.Contains(Key.Z);
|
leftViaKeyboard = keyboard.Keys.Contains(Key.Z);
|
||||||
rightViaKeyboard = state.Keyboard.Keys.Contains(Key.X);
|
rightViaKeyboard = keyboard.Keys.Contains(Key.X);
|
||||||
}
|
}
|
||||||
|
|
||||||
var mouse = (Framework.Input.MouseState)state.Mouse;
|
if (mouse != null)
|
||||||
if (state.Mouse != null)
|
|
||||||
{
|
{
|
||||||
if (mouseDisabled.Value)
|
if (mouseDisabled.Value)
|
||||||
{
|
{
|
||||||
@ -54,5 +83,38 @@ namespace osu.Game.Screens.Play
|
|||||||
mouse.ButtonStates.Find(s => s.Button == MouseButton.Right).State = true;
|
mouse.ButtonStates.Find(s => s.Button == MouseButton.Right).State = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
if (parentClock == null) return;
|
||||||
|
|
||||||
|
clock.Rate = parentClock.Rate;
|
||||||
|
clock.IsRunning = parentClock.IsRunning;
|
||||||
|
|
||||||
|
//if a replayHandler is not attached, we should just pass-through.
|
||||||
|
if (UseParentState || replayInputHandler == null)
|
||||||
|
{
|
||||||
|
clock.CurrentTime = parentClock.CurrentTime;
|
||||||
|
base.Update();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
double? newTime = replayInputHandler.SetFrameFromTime(parentClock.CurrentTime);
|
||||||
|
|
||||||
|
if (newTime == null)
|
||||||
|
//we shouldn't execute for this time value
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (clock.CurrentTime == parentClock.CurrentTime)
|
||||||
|
break;
|
||||||
|
|
||||||
|
clock.CurrentTime = newTime.Value;
|
||||||
|
base.Update();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,6 +79,10 @@
|
|||||||
<Compile Include="Graphics\UserInterface\OsuSliderBar.cs" />
|
<Compile Include="Graphics\UserInterface\OsuSliderBar.cs" />
|
||||||
<Compile Include="Graphics\UserInterface\OsuTextBox.cs" />
|
<Compile Include="Graphics\UserInterface\OsuTextBox.cs" />
|
||||||
<Compile Include="Graphics\UserInterface\TwoLayerButton.cs" />
|
<Compile Include="Graphics\UserInterface\TwoLayerButton.cs" />
|
||||||
|
<Compile Include="Input\Handlers\LegacyReplayInputHandler.cs" />
|
||||||
|
<Compile Include="IO\Legacy\ILegacySerializable.cs" />
|
||||||
|
<Compile Include="IO\Legacy\SerializationReader.cs" />
|
||||||
|
<Compile Include="IO\Legacy\SerializationWriter.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" />
|
||||||
|
Reference in New Issue
Block a user