mirror of
https://github.com/ppy/osu.git
synced 2024-11-11 09:07:52 +08:00
Merge branch 'master' into taiko-don
This commit is contained in:
commit
d697de29a6
Binary file not shown.
After Width: | Height: | Size: 183 KiB |
Binary file not shown.
After Width: | Height: | Size: 185 KiB |
Binary file not shown.
After Width: | Height: | Size: 100 KiB |
Binary file not shown.
After Width: | Height: | Size: 94 KiB |
Binary file not shown.
After Width: | Height: | Size: 60 KiB |
Binary file not shown.
After Width: | Height: | Size: 64 KiB |
@ -24,6 +24,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
typeof(TaikoHitTarget),
|
typeof(TaikoHitTarget),
|
||||||
typeof(TaikoLegacyHitTarget),
|
typeof(TaikoLegacyHitTarget),
|
||||||
typeof(PlayfieldBackgroundRight),
|
typeof(PlayfieldBackgroundRight),
|
||||||
|
typeof(LegacyTaikoScroller),
|
||||||
}).ToList();
|
}).ToList();
|
||||||
|
|
||||||
[Cached(typeof(IScrollingInfo))]
|
[Cached(typeof(IScrollingInfo))]
|
||||||
@ -51,6 +52,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
{
|
{
|
||||||
Anchor = Anchor.CentreLeft,
|
Anchor = Anchor.CentreLeft,
|
||||||
Origin = Anchor.CentreLeft,
|
Origin = Anchor.CentreLeft,
|
||||||
|
Height = 0.6f,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
AddRepeatStep("change height", () => this.ChildrenOfType<TaikoPlayfield>().ForEach(p => p.Height = Math.Max(0.2f, (p.Height + 0.2f) % 1f)), 50);
|
AddRepeatStep("change height", () => this.ChildrenOfType<TaikoPlayfield>().ForEach(p => p.Height = Math.Max(0.2f, (p.Height + 0.2f) % 1f)), 50);
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Rulesets.Judgements;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using osu.Game.Rulesets.Taiko.Skinning;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
||||||
|
{
|
||||||
|
public class TestSceneTaikoScroller : TaikoSkinnableTestScene
|
||||||
|
{
|
||||||
|
public TestSceneTaikoScroller()
|
||||||
|
{
|
||||||
|
AddStep("Load scroller", () => SetContents(() => new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.TaikoScroller), _ => Empty())));
|
||||||
|
AddToggleStep("Toggle passing", passing => this.ChildrenOfType<LegacyTaikoScroller>().ForEach(s => s.LastResult.Value =
|
||||||
|
new JudgementResult(null, new Judgement()) { Type = passing ? HitResult.Perfect : HitResult.Miss }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
149
osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs
Normal file
149
osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Game.Rulesets.Judgements;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using osu.Game.Screens.Play;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Taiko.Skinning
|
||||||
|
{
|
||||||
|
public class LegacyTaikoScroller : CompositeDrawable
|
||||||
|
{
|
||||||
|
public LegacyTaikoScroller()
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader(true)]
|
||||||
|
private void load(GameplayBeatmap gameplayBeatmap)
|
||||||
|
{
|
||||||
|
if (gameplayBeatmap != null)
|
||||||
|
((IBindable<JudgementResult>)LastResult).BindTo(gameplayBeatmap.LastJudgementResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool passing;
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
LastResult.BindValueChanged(result =>
|
||||||
|
{
|
||||||
|
var r = result.NewValue;
|
||||||
|
|
||||||
|
// always ignore hitobjects that don't affect combo (drumroll ticks etc.)
|
||||||
|
if (r?.Judgement.AffectsCombo == false)
|
||||||
|
return;
|
||||||
|
|
||||||
|
passing = r == null || r.Type > HitResult.Miss;
|
||||||
|
|
||||||
|
foreach (var sprite in InternalChildren.OfType<ScrollerSprite>())
|
||||||
|
sprite.Passing = passing;
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Bindable<JudgementResult> LastResult = new Bindable<JudgementResult>();
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
float? additiveX = null;
|
||||||
|
|
||||||
|
foreach (var sprite in InternalChildren)
|
||||||
|
{
|
||||||
|
// add the x coordinates and perform re-layout on all sprites as spacing may change with gameplay scale.
|
||||||
|
sprite.X = additiveX ??= sprite.X - (float)Time.Elapsed * 0.1f;
|
||||||
|
|
||||||
|
additiveX += sprite.DrawWidth - 1;
|
||||||
|
|
||||||
|
if (sprite.X + sprite.DrawWidth < 0)
|
||||||
|
sprite.Expire();
|
||||||
|
}
|
||||||
|
|
||||||
|
var last = InternalChildren.LastOrDefault();
|
||||||
|
|
||||||
|
// only break from this loop once we have saturated horizontal space completely.
|
||||||
|
if (last != null && last.ScreenSpaceDrawQuad.TopRight.X >= ScreenSpaceDrawQuad.TopRight.X)
|
||||||
|
break;
|
||||||
|
|
||||||
|
AddInternal(new ScrollerSprite
|
||||||
|
{
|
||||||
|
Passing = passing
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ScrollerSprite : CompositeDrawable
|
||||||
|
{
|
||||||
|
private Sprite passingSprite;
|
||||||
|
private Sprite failingSprite;
|
||||||
|
|
||||||
|
private bool passing = true;
|
||||||
|
|
||||||
|
public bool Passing
|
||||||
|
{
|
||||||
|
get => passing;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == passing)
|
||||||
|
return;
|
||||||
|
|
||||||
|
passing = value;
|
||||||
|
|
||||||
|
if (IsLoaded)
|
||||||
|
updatePassing();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(ISkinSource skin)
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.X;
|
||||||
|
RelativeSizeAxes = Axes.Y;
|
||||||
|
|
||||||
|
FillMode = FillMode.Fit;
|
||||||
|
|
||||||
|
InternalChildren = new Drawable[]
|
||||||
|
{
|
||||||
|
passingSprite = new Sprite { Texture = skin.GetTexture("taiko-slider") },
|
||||||
|
failingSprite = new Sprite { Texture = skin.GetTexture("taiko-slider-fail"), Alpha = 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
updatePassing();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
foreach (var c in InternalChildren)
|
||||||
|
c.Scale = new Vector2(DrawHeight / c.Height);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updatePassing()
|
||||||
|
{
|
||||||
|
if (passing)
|
||||||
|
{
|
||||||
|
passingSprite.Show();
|
||||||
|
failingSprite.FadeOut(200);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
failingSprite.FadeIn(200);
|
||||||
|
passingSprite.Delay(200).FadeOut();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -87,6 +87,10 @@ namespace osu.Game.Rulesets.Taiko.Skinning
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
case TaikoSkinComponents.TaikoScroller:
|
||||||
|
if (GetTexture("taiko-slider") != null)
|
||||||
|
return new LegacyTaikoScroller();
|
||||||
|
|
||||||
case TaikoSkinComponents.TaikoDon:
|
case TaikoSkinComponents.TaikoDon:
|
||||||
if (GetTexture("pippidonclear0") != null)
|
if (GetTexture("pippidonclear0") != null)
|
||||||
return new DrawableTaikoMascot();
|
return new DrawableTaikoMascot();
|
||||||
|
@ -18,6 +18,7 @@ namespace osu.Game.Rulesets.Taiko
|
|||||||
TaikoExplosionMiss,
|
TaikoExplosionMiss,
|
||||||
TaikoExplosionGood,
|
TaikoExplosionGood,
|
||||||
TaikoExplosionGreat,
|
TaikoExplosionGreat,
|
||||||
|
TaikoScroller,
|
||||||
TaikoDon,
|
TaikoDon,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Taiko.Objects;
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
@ -16,11 +17,15 @@ using osu.Game.Replays;
|
|||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.UI.Scrolling;
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.UI
|
namespace osu.Game.Rulesets.Taiko.UI
|
||||||
{
|
{
|
||||||
public class DrawableTaikoRuleset : DrawableScrollingRuleset<TaikoHitObject>
|
public class DrawableTaikoRuleset : DrawableScrollingRuleset<TaikoHitObject>
|
||||||
{
|
{
|
||||||
|
private SkinnableDrawable scroller;
|
||||||
|
|
||||||
protected override ScrollVisualisationMethod VisualisationMethod => ScrollVisualisationMethod.Overlapping;
|
protected override ScrollVisualisationMethod VisualisationMethod => ScrollVisualisationMethod.Overlapping;
|
||||||
|
|
||||||
protected override bool UserScrollSpeedAdjustment => false;
|
protected override bool UserScrollSpeedAdjustment => false;
|
||||||
@ -36,6 +41,20 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
new BarLineGenerator<BarLine>(Beatmap).BarLines.ForEach(bar => Playfield.Add(bar.Major ? new DrawableBarLineMajor(bar) : new DrawableBarLine(bar)));
|
new BarLineGenerator<BarLine>(Beatmap).BarLines.ForEach(bar => Playfield.Add(bar.Major ? new DrawableBarLineMajor(bar) : new DrawableBarLine(bar)));
|
||||||
|
|
||||||
|
AddInternal(scroller = new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.TaikoScroller), _ => Empty())
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Depth = float.MaxValue
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UpdateAfterChildren()
|
||||||
|
{
|
||||||
|
base.UpdateAfterChildren();
|
||||||
|
|
||||||
|
var playfieldScreen = Playfield.ScreenSpaceDrawQuad;
|
||||||
|
scroller.Height = ToLocalSpace(playfieldScreen.TopLeft + new Vector2(0, playfieldScreen.Height / 20)).Y;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new TaikoPlayfieldAdjustmentContainer();
|
public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new TaikoPlayfieldAdjustmentContainer();
|
||||||
|
129
osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs
Normal file
129
osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Platform;
|
||||||
|
using osu.Game.Configuration;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.NonVisual
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class CustomDataDirectoryTest
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void TestDefaultDirectory()
|
||||||
|
{
|
||||||
|
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestDefaultDirectory)))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var osu = loadOsu(host);
|
||||||
|
var storage = osu.Dependencies.Get<Storage>();
|
||||||
|
|
||||||
|
string defaultStorageLocation = Path.Combine(Environment.CurrentDirectory, "headless", nameof(TestDefaultDirectory));
|
||||||
|
|
||||||
|
Assert.That(storage.GetFullPath("."), Is.EqualTo(defaultStorageLocation));
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
host.Exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string customPath => Path.Combine(Environment.CurrentDirectory, "custom-path");
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestCustomDirectory()
|
||||||
|
{
|
||||||
|
using (var host = new HeadlessGameHost(nameof(TestCustomDirectory)))
|
||||||
|
{
|
||||||
|
string headlessPrefix = Path.Combine("headless", nameof(TestCustomDirectory));
|
||||||
|
|
||||||
|
// need access before the game has constructed its own storage yet.
|
||||||
|
Storage storage = new DesktopStorage(headlessPrefix, host);
|
||||||
|
// manual cleaning so we can prepare a config file.
|
||||||
|
storage.DeleteDirectory(string.Empty);
|
||||||
|
|
||||||
|
using (var storageConfig = new StorageConfigManager(storage))
|
||||||
|
storageConfig.Set(StorageConfig.FullPath, customPath);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var osu = loadOsu(host);
|
||||||
|
|
||||||
|
// switch to DI'd storage
|
||||||
|
storage = osu.Dependencies.Get<Storage>();
|
||||||
|
|
||||||
|
Assert.That(storage.GetFullPath("."), Is.EqualTo(customPath));
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
host.Exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestSubDirectoryLookup()
|
||||||
|
{
|
||||||
|
using (var host = new HeadlessGameHost(nameof(TestSubDirectoryLookup)))
|
||||||
|
{
|
||||||
|
string headlessPrefix = Path.Combine("headless", nameof(TestSubDirectoryLookup));
|
||||||
|
|
||||||
|
// need access before the game has constructed its own storage yet.
|
||||||
|
Storage storage = new DesktopStorage(headlessPrefix, host);
|
||||||
|
// manual cleaning so we can prepare a config file.
|
||||||
|
storage.DeleteDirectory(string.Empty);
|
||||||
|
|
||||||
|
using (var storageConfig = new StorageConfigManager(storage))
|
||||||
|
storageConfig.Set(StorageConfig.FullPath, customPath);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var osu = loadOsu(host);
|
||||||
|
|
||||||
|
// switch to DI'd storage
|
||||||
|
storage = osu.Dependencies.Get<Storage>();
|
||||||
|
|
||||||
|
string actualTestFile = Path.Combine(customPath, "rulesets", "test");
|
||||||
|
|
||||||
|
File.WriteAllText(actualTestFile, "test");
|
||||||
|
|
||||||
|
var rulesetStorage = storage.GetStorageForDirectory("rulesets");
|
||||||
|
var lookupPath = rulesetStorage.GetFiles(".").Single();
|
||||||
|
|
||||||
|
Assert.That(lookupPath, Is.EqualTo("test"));
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
host.Exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private OsuGameBase loadOsu(GameHost host)
|
||||||
|
{
|
||||||
|
var osu = new OsuGameBase();
|
||||||
|
Task.Run(() => host.Run(osu));
|
||||||
|
waitForOrAssert(() => osu.IsLoaded, @"osu! failed to start in a reasonable amount of time");
|
||||||
|
return osu;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void waitForOrAssert(Func<bool> result, string failureMessage, int timeout = 60000)
|
||||||
|
{
|
||||||
|
Task task = Task.Run(() =>
|
||||||
|
{
|
||||||
|
while (!result()) Thread.Sleep(200);
|
||||||
|
});
|
||||||
|
|
||||||
|
Assert.IsTrue(task.Wait(timeout), failureMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@ -84,7 +85,7 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
public IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList<Mod> mods = null, TimeSpan? timeout = null)
|
public IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList<Mod> mods = null, TimeSpan? timeout = null)
|
||||||
{
|
{
|
||||||
using (var cancellationSource = new CancellationTokenSource(timeout ?? TimeSpan.FromSeconds(10)))
|
using (var cancellationSource = createCancellationTokenSource(timeout))
|
||||||
{
|
{
|
||||||
mods ??= Array.Empty<Mod>();
|
mods ??= Array.Empty<Mod>();
|
||||||
|
|
||||||
@ -181,6 +182,15 @@ namespace osu.Game.Beatmaps
|
|||||||
beatmapLoadTask = null;
|
beatmapLoadTask = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private CancellationTokenSource createCancellationTokenSource(TimeSpan? timeout)
|
||||||
|
{
|
||||||
|
if (Debugger.IsAttached)
|
||||||
|
// ignore timeout when debugger is attached (may be breakpointing / debugging).
|
||||||
|
return new CancellationTokenSource();
|
||||||
|
|
||||||
|
return new CancellationTokenSource(timeout ?? TimeSpan.FromSeconds(10));
|
||||||
|
}
|
||||||
|
|
||||||
private Task<IBeatmap> loadBeatmapAsync() => beatmapLoadTask ??= Task.Factory.StartNew(() =>
|
private Task<IBeatmap> loadBeatmapAsync() => beatmapLoadTask ??= Task.Factory.StartNew(() =>
|
||||||
{
|
{
|
||||||
// Todo: Handle cancellation during beatmap parsing
|
// Todo: Handle cancellation during beatmap parsing
|
||||||
|
30
osu.Game/Configuration/StorageConfigManager.cs
Normal file
30
osu.Game/Configuration/StorageConfigManager.cs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Configuration;
|
||||||
|
using osu.Framework.Platform;
|
||||||
|
|
||||||
|
namespace osu.Game.Configuration
|
||||||
|
{
|
||||||
|
public class StorageConfigManager : IniConfigManager<StorageConfig>
|
||||||
|
{
|
||||||
|
protected override string Filename => "storage.ini";
|
||||||
|
|
||||||
|
public StorageConfigManager(Storage storage)
|
||||||
|
: base(storage)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void InitialiseDefaults()
|
||||||
|
{
|
||||||
|
base.InitialiseDefaults();
|
||||||
|
|
||||||
|
Set(StorageConfig.FullPath, string.Empty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum StorageConfig
|
||||||
|
{
|
||||||
|
FullPath,
|
||||||
|
}
|
||||||
|
}
|
26
osu.Game/IO/OsuStorage.cs
Normal file
26
osu.Game/IO/OsuStorage.cs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Logging;
|
||||||
|
using osu.Framework.Platform;
|
||||||
|
using osu.Game.Configuration;
|
||||||
|
|
||||||
|
namespace osu.Game.IO
|
||||||
|
{
|
||||||
|
public class OsuStorage : WrappedStorage
|
||||||
|
{
|
||||||
|
public OsuStorage(GameHost host)
|
||||||
|
: base(host.Storage, string.Empty)
|
||||||
|
{
|
||||||
|
var storageConfig = new StorageConfigManager(host.Storage);
|
||||||
|
|
||||||
|
var customStoragePath = storageConfig.Get<string>(StorageConfig.FullPath);
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(customStoragePath))
|
||||||
|
{
|
||||||
|
ChangeTargetStorage(host.GetStorage(customStoragePath));
|
||||||
|
Logger.Storage = UnderlyingStorage.GetStorageForDirectory("logs");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
88
osu.Game/IO/WrappedStorage.cs
Normal file
88
osu.Game/IO/WrappedStorage.cs
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using osu.Framework.Platform;
|
||||||
|
|
||||||
|
namespace osu.Game.IO
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A storage which wraps another storage and delegates implementation, potentially mutating the lookup path.
|
||||||
|
/// </summary>
|
||||||
|
public class WrappedStorage : Storage
|
||||||
|
{
|
||||||
|
protected Storage UnderlyingStorage { get; private set; }
|
||||||
|
|
||||||
|
private readonly string subPath;
|
||||||
|
|
||||||
|
public WrappedStorage(Storage underlyingStorage, string subPath = null)
|
||||||
|
: base(string.Empty)
|
||||||
|
{
|
||||||
|
ChangeTargetStorage(underlyingStorage);
|
||||||
|
|
||||||
|
this.subPath = subPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual string MutatePath(string path) => !string.IsNullOrEmpty(subPath) ? Path.Combine(subPath, path) : path;
|
||||||
|
|
||||||
|
protected void ChangeTargetStorage(Storage newStorage)
|
||||||
|
{
|
||||||
|
UnderlyingStorage = newStorage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string GetFullPath(string path, bool createIfNotExisting = false) =>
|
||||||
|
UnderlyingStorage.GetFullPath(MutatePath(path), createIfNotExisting);
|
||||||
|
|
||||||
|
public override bool Exists(string path) =>
|
||||||
|
UnderlyingStorage.Exists(MutatePath(path));
|
||||||
|
|
||||||
|
public override bool ExistsDirectory(string path) =>
|
||||||
|
UnderlyingStorage.ExistsDirectory(MutatePath(path));
|
||||||
|
|
||||||
|
public override void DeleteDirectory(string path) =>
|
||||||
|
UnderlyingStorage.DeleteDirectory(MutatePath(path));
|
||||||
|
|
||||||
|
public override void Delete(string path) =>
|
||||||
|
UnderlyingStorage.Delete(MutatePath(path));
|
||||||
|
|
||||||
|
public override IEnumerable<string> GetDirectories(string path) =>
|
||||||
|
ToLocalRelative(UnderlyingStorage.GetDirectories(MutatePath(path)));
|
||||||
|
|
||||||
|
public IEnumerable<string> ToLocalRelative(IEnumerable<string> paths)
|
||||||
|
{
|
||||||
|
string localRoot = GetFullPath(string.Empty);
|
||||||
|
|
||||||
|
foreach (var path in paths)
|
||||||
|
yield return Path.GetRelativePath(localRoot, UnderlyingStorage.GetFullPath(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IEnumerable<string> GetFiles(string path, string pattern = "*") =>
|
||||||
|
ToLocalRelative(UnderlyingStorage.GetFiles(MutatePath(path), pattern));
|
||||||
|
|
||||||
|
public override Stream GetStream(string path, FileAccess access = FileAccess.Read, FileMode mode = FileMode.OpenOrCreate) =>
|
||||||
|
UnderlyingStorage.GetStream(MutatePath(path), access, mode);
|
||||||
|
|
||||||
|
public override string GetDatabaseConnectionString(string name) =>
|
||||||
|
UnderlyingStorage.GetDatabaseConnectionString(MutatePath(name));
|
||||||
|
|
||||||
|
public override void DeleteDatabase(string name) => UnderlyingStorage.DeleteDatabase(MutatePath(name));
|
||||||
|
|
||||||
|
public override void OpenInNativeExplorer() => UnderlyingStorage.OpenInNativeExplorer();
|
||||||
|
|
||||||
|
public override Storage GetStorageForDirectory(string path)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(path))
|
||||||
|
throw new ArgumentException("Must be non-null and not empty string", nameof(path));
|
||||||
|
|
||||||
|
if (!path.EndsWith(Path.DirectorySeparatorChar))
|
||||||
|
path += Path.DirectorySeparatorChar;
|
||||||
|
|
||||||
|
// create non-existing path.
|
||||||
|
GetFullPath(path, true);
|
||||||
|
|
||||||
|
return new WrappedStorage(this, path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -132,6 +132,8 @@ namespace osu.Game
|
|||||||
|
|
||||||
dependencies.Cache(contextFactory = new DatabaseContextFactory(Storage));
|
dependencies.Cache(contextFactory = new DatabaseContextFactory(Storage));
|
||||||
|
|
||||||
|
dependencies.CacheAs(Storage);
|
||||||
|
|
||||||
var largeStore = new LargeTextureStore(Host.CreateTextureLoaderStore(new NamespacedResourceStore<byte[]>(Resources, @"Textures")));
|
var largeStore = new LargeTextureStore(Host.CreateTextureLoaderStore(new NamespacedResourceStore<byte[]>(Resources, @"Textures")));
|
||||||
largeStore.AddStore(Host.CreateTextureLoaderStore(new OnlineStore()));
|
largeStore.AddStore(Host.CreateTextureLoaderStore(new OnlineStore()));
|
||||||
dependencies.Cache(largeStore);
|
dependencies.Cache(largeStore);
|
||||||
@ -300,8 +302,8 @@ namespace osu.Game
|
|||||||
{
|
{
|
||||||
base.SetHost(host);
|
base.SetHost(host);
|
||||||
|
|
||||||
if (Storage == null)
|
if (Storage == null) // may be non-null for certain tests
|
||||||
Storage = host.Storage;
|
Storage = new OsuStorage(host);
|
||||||
|
|
||||||
if (LocalConfig == null)
|
if (LocalConfig == null)
|
||||||
LocalConfig = new OsuConfigManager(Storage);
|
LocalConfig = new OsuConfigManager(Storage);
|
||||||
|
@ -2,10 +2,12 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Beatmaps.Timing;
|
using osu.Game.Beatmaps.Timing;
|
||||||
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Play
|
namespace osu.Game.Screens.Play
|
||||||
@ -38,5 +40,11 @@ namespace osu.Game.Screens.Play
|
|||||||
public IEnumerable<BeatmapStatistic> GetStatistics() => PlayableBeatmap.GetStatistics();
|
public IEnumerable<BeatmapStatistic> GetStatistics() => PlayableBeatmap.GetStatistics();
|
||||||
|
|
||||||
public IBeatmap Clone() => PlayableBeatmap.Clone();
|
public IBeatmap Clone() => PlayableBeatmap.Clone();
|
||||||
|
|
||||||
|
private readonly Bindable<JudgementResult> lastJudgementResult = new Bindable<JudgementResult>();
|
||||||
|
|
||||||
|
public IBindable<JudgementResult> LastJudgementResult => lastJudgementResult;
|
||||||
|
|
||||||
|
public void ApplyResult(JudgementResult result) => lastJudgementResult.Value = result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -200,6 +200,7 @@ namespace osu.Game.Screens.Play
|
|||||||
{
|
{
|
||||||
HealthProcessor.ApplyResult(r);
|
HealthProcessor.ApplyResult(r);
|
||||||
ScoreProcessor.ApplyResult(r);
|
ScoreProcessor.ApplyResult(r);
|
||||||
|
gameplayBeatmap.ApplyResult(r);
|
||||||
};
|
};
|
||||||
|
|
||||||
DrawableRuleset.OnRevertResult += r =>
|
DrawableRuleset.OnRevertResult += r =>
|
||||||
|
Loading…
Reference in New Issue
Block a user