1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-15 01:52:44 +08:00

Compare commits

...

151 Commits

48 changed files with 584 additions and 159 deletions
+3 -1
View File
@@ -24,7 +24,9 @@ Clone the repository including submodules
Build and run
- Using Visual Studio 2017, Rider or Visual Studio Code (configurations are included)
- From command line using `dotnet run --project osu.Desktop`
- From command line using `dotnet run --project osu.Desktop`. When building for non-development purposes, add `-c Release` to gain higher performance.
Note: If you run from command line under linux, you will need to prefix the output folder to your `LD_LIBRARY_PATH`. See `.vscode/launch.json` for an example
If you run into issues building you may need to restore nuget packages (commonly via `dotnet restore`). Visual Studio Code users must run `Restore` task from debug tab before attempt to build.
+3 -3
View File
@@ -27,9 +27,9 @@
</ItemGroup>
<ItemGroup Label="Package References">
<PackageReference Include="System.IO.Packaging" Version="4.5.0" />
<PackageReference Include="ppy.squirrel.windows" Version="1.8.0.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.1.2" />
<PackageReference Include="ppy.squirrel.windows" Version="1.8.0.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.1.3" />
</ItemGroup>
<ItemGroup Label="Resources">
<EmbeddedResource Include="lazer.ico" />
@@ -74,42 +74,42 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
private void initialiseHyperDash(List<CatchHitObject> objects)
{
// todo: add difficulty adjust.
double halfCatcherWidth = CatcherArea.CATCHER_SIZE * (objects.FirstOrDefault()?.Scale ?? 1) / CatchPlayfield.BASE_WIDTH / 2;
List<CatchHitObject> objectWithDroplets = new List<CatchHitObject>();
foreach (var currentObject in objects)
{
if (currentObject is Fruit)
objectWithDroplets.Add(currentObject);
if (currentObject is JuiceStream)
foreach (var currentJuiceElement in currentObject.NestedHitObjects)
if (!(currentJuiceElement is TinyDroplet))
objectWithDroplets.Add((CatchHitObject)currentJuiceElement);
}
objectWithDroplets.Sort((h1, h2) => h1.StartTime.CompareTo(h2.StartTime));
double halfCatcherWidth = CatcherArea.GetCatcherSize(Beatmap.BeatmapInfo.BaseDifficulty) / 2;
int lastDirection = 0;
double lastExcess = halfCatcherWidth;
int objCount = objects.Count;
for (int i = 0; i < objCount - 1; i++)
for (int i = 0; i < objectWithDroplets.Count - 1; i++)
{
CatchHitObject currentObject = objects[i];
// not needed?
// if (currentObject is TinyDroplet) continue;
CatchHitObject nextObject = objects[i + 1];
// while (nextObject is TinyDroplet)
// {
// if (++i == objCount - 1) break;
// nextObject = objects[i + 1];
// }
CatchHitObject currentObject = objectWithDroplets[i];
CatchHitObject nextObject = objectWithDroplets[i + 1];
int thisDirection = nextObject.X > currentObject.X ? 1 : -1;
double timeToNext = nextObject.StartTime - ((currentObject as IHasEndTime)?.EndTime ?? currentObject.StartTime) - 4;
double timeToNext = nextObject.StartTime - currentObject.StartTime;
double distanceToNext = Math.Abs(nextObject.X - currentObject.X) - (lastDirection == thisDirection ? lastExcess : halfCatcherWidth);
if (timeToNext * CatcherArea.Catcher.BASE_SPEED < distanceToNext)
float distanceToHyper = (float)(timeToNext * CatcherArea.Catcher.BASE_SPEED - distanceToNext);
if (distanceToHyper < 0)
{
currentObject.HyperDashTarget = nextObject;
lastExcess = halfCatcherWidth;
}
else
{
//currentObject.DistanceToHyperDash = timeToNext - distanceToNext;
lastExcess = MathHelper.Clamp(timeToNext - distanceToNext, 0, halfCatcherWidth);
currentObject.DistanceToHyperDash = distanceToHyper;
lastExcess = MathHelper.Clamp(distanceToHyper, 0, halfCatcherWidth);
}
lastDirection = thisDirection;
@@ -27,7 +27,9 @@ namespace osu.Game.Rulesets.Catch.Objects
public int ComboIndex { get; set; }
/// <summary>
/// The distance for a fruit to to next hyper if it's not a hyper.
/// Difference between the distance to the next object
/// and the distance that would have triggered a hyper dash.
/// A value close to 0 indicates a difficult jump (for difficulty calculation).
/// </summary>
public float DistanceToHyperDash { get; set; }
@@ -22,6 +22,8 @@ namespace osu.Game.Rulesets.Catch.UI
private readonly CatcherArea catcherArea;
protected override bool UserScrollSpeedAdjustment => false;
public CatchPlayfield(BeatmapDifficulty difficulty, Func<CatchHitObject, DrawableHitObject<CatchHitObject>> getVisualRepresentation)
: base(BASE_WIDTH)
{
@@ -107,6 +107,11 @@ namespace osu.Game.Rulesets.Catch.UI
public bool AttemptCatch(CatchHitObject obj) => MovableCatcher.AttemptCatch(obj);
public static float GetCatcherSize(BeatmapDifficulty difficulty)
{
return CATCHER_SIZE / CatchPlayfield.BASE_WIDTH * (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5);
}
public class Catcher : Container, IKeyBindingHandler<CatchAction>
{
/// <summary>
@@ -111,6 +111,18 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
bodyPiece.Height = DrawHeight - Head.Height / 2 + Tail.Height / 2;
}
protected void BeginHold()
{
holdStartTime = Time.Current;
bodyPiece.Hitting = true;
}
protected void EndHold()
{
holdStartTime = null;
bodyPiece.Hitting = false;
}
public bool OnPressed(ManiaAction action)
{
// Make sure the action happened within the body of the hold note
@@ -123,8 +135,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
// The user has pressed during the body of the hold note, after the head note and its hit windows have passed
// and within the limited range of the above if-statement. This state will be managed by the head note if the
// user has pressed during the hit windows of the head note.
holdStartTime = Time.Current;
BeginHold();
return true;
}
@@ -137,7 +148,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
if (action != Action.Value)
return false;
holdStartTime = null;
EndHold();
// If the key has been released too early, the user should not receive full score for the release
if (!Tail.IsHit)
@@ -170,7 +181,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
// The head note also handles early hits before the body, but we want accurate early hits to count as the body being held
// The body doesn't handle these early early hits, so we have to explicitly set the holding state here
holdNote.holdStartTime = Time.Current;
holdNote.BeginHold();
return true;
}
@@ -32,6 +32,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
background = new Box { RelativeSizeAxes = Axes.Both },
foreground = new BufferedContainer
{
Blending = BlendingMode.Additive,
RelativeSizeAxes = Axes.Both,
CacheDrawnFrameBuffer = true,
Children = new Drawable[]
@@ -73,6 +74,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
}
private Color4 accentColour;
public Color4 AccentColour
{
get { return accentColour; }
@@ -86,6 +88,16 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
}
}
public bool Hitting
{
get { return hitting; }
set
{
hitting = value;
updateAccentColour();
}
}
private Cached subtractionCache = new Cached();
public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true)
@@ -118,13 +130,26 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
}
}
private bool hitting;
private void updateAccentColour()
{
if (!IsLoaded)
return;
foreground.Colour = AccentColour.Opacity(0.9f);
background.Colour = AccentColour.Opacity(0.6f);
foreground.Colour = AccentColour.Opacity(0.5f);
background.Colour = AccentColour.Opacity(0.7f);
const float animation_length = 50;
foreground.ClearTransforms(false, nameof(foreground.Colour));
if (hitting)
{
// wait for the next sync point
double synchronisedOffset = animation_length * 2 - Time.Current % (animation_length * 2);
using (foreground.BeginDelayedSequence(synchronisedOffset))
foreground.FadeColour(AccentColour.Lighten(0.2f), animation_length).Then().FadeColour(foreground.Colour, animation_length).Loop();
}
subtractionCache.Invalidate();
}
@@ -0,0 +1,51 @@
// Copyright (c) 2007-2018 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 osu.Framework.Graphics;
using osu.Game.Graphics;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects;
using OpenTK;
namespace osu.Game.Rulesets.Osu.Mods
{
internal class OsuModTransform : Mod, IApplicableToDrawableHitObjects
{
public override string Name => "Transform";
public override string ShortenedName => "TR";
public override FontAwesome Icon => FontAwesome.fa_arrows;
public override ModType Type => ModType.Fun;
public override string Description => "Everything rotates. EVERYTHING.";
public override double ScoreMultiplier => 1;
private float theta;
public void ApplyToDrawableHitObjects(IEnumerable<DrawableHitObject> drawables)
{
foreach (var drawable in drawables)
{
var hitObject = (OsuHitObject) drawable.HitObject;
float appearDistance = (float)(hitObject.TimePreempt - hitObject.TimeFadeIn) / 2;
Vector2 originalPosition = drawable.Position;
Vector2 appearOffset = new Vector2((float)Math.Cos(theta), (float)Math.Sin(theta)) * appearDistance;
//the - 1 and + 1 prevents the hit objects to appear in the wrong position.
double appearTime = hitObject.StartTime - hitObject.TimePreempt - 1;
double moveDuration = hitObject.TimePreempt + 1;
using (drawable.BeginAbsoluteSequence(appearTime, true))
{
drawable
.MoveToOffset(appearOffset)
.MoveTo(originalPosition, moveDuration, Easing.InOutSine);
}
theta += (float) hitObject.TimeFadeIn / 1000;
}
}
}
}
@@ -0,0 +1,66 @@
// Copyright (c) 2007-2018 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 osu.Framework.Graphics;
using osu.Game.Graphics;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Objects;
using OpenTK;
namespace osu.Game.Rulesets.Osu.Mods
{
internal class OsuModWiggle : Mod, IApplicableToDrawableHitObjects
{
public override string Name => "Wiggle";
public override string ShortenedName => "WG";
public override FontAwesome Icon => FontAwesome.fa_certificate;
public override ModType Type => ModType.Fun;
public override string Description => "They just won't stay still...";
public override double ScoreMultiplier => 1;
private const int wiggle_duration = 90; // (ms) Higher = fewer wiggles
private const int wiggle_strength = 10; // Higher = stronger wiggles
public void ApplyToDrawableHitObjects(IEnumerable<DrawableHitObject> drawables)
{
foreach (var drawable in drawables)
drawable.ApplyCustomUpdateState += drawableOnApplyCustomUpdateState;
}
private void drawableOnApplyCustomUpdateState(DrawableHitObject drawable, ArmedState state)
{
var osuObject = (OsuHitObject)drawable.HitObject;
Vector2 origin = drawable.Position;
Random objRand = new Random((int)osuObject.StartTime);
// Wiggle all objects during TimePreempt
int amountWiggles = (int)osuObject.TimePreempt / wiggle_duration;
void wiggle()
{
float nextAngle = (float)(objRand.NextDouble() * 2 * Math.PI);
float nextDist = (float)(objRand.NextDouble() * wiggle_strength);
drawable.MoveTo(new Vector2((float)(nextDist * Math.Cos(nextAngle) + origin.X), (float)(nextDist * Math.Sin(nextAngle) + origin.Y)), wiggle_duration);
}
for (int i = 0; i < amountWiggles; i++)
using (drawable.BeginAbsoluteSequence(osuObject.StartTime - osuObject.TimePreempt + i * wiggle_duration, true))
wiggle();
// Keep wiggling sliders and spinners for their duration
if (!(osuObject is IHasEndTime endTime))
return;
amountWiggles = (int)(endTime.Duration / wiggle_duration);
for (int i = 0; i < amountWiggles; i++)
using (drawable.BeginAbsoluteSequence(osuObject.StartTime + i * wiggle_duration, true))
wiggle();
}
}
}
@@ -7,14 +7,15 @@ using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.OpenGL.Textures;
using osu.Framework.Graphics.Lines;
using osu.Framework.Graphics.Textures;
using OpenTK;
using OpenTK.Graphics.ES30;
using OpenTK.Graphics;
using osu.Framework.Graphics.Primitives;
using osu.Game.Rulesets.Objects.Types;
using OpenTK;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
@@ -43,6 +44,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
public double? SnakedEnd { get; private set; }
private Color4 accentColour = Color4.White;
/// <summary>
/// Used to colour the path.
/// </summary>
@@ -61,6 +63,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
}
private Color4 borderColour = Color4.White;
/// <summary>
/// Used to colour the path border.
/// </summary>
@@ -85,6 +88,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
private Vector2 topLeftOffset;
private readonly Slider slider;
public SliderBody(Slider s)
{
slider = s;
@@ -139,8 +143,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
var texture = new Texture(textureWidth, 1);
//initialise background
var raw = new RawTexture(textureWidth, 1);
var bytes = raw.Data;
var raw = new Image<Rgba32>(textureWidth, 1);
const float aa_portion = 0.02f;
const float border_portion = 0.128f;
@@ -155,19 +158,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
if (progress <= border_portion)
{
bytes[i * 4] = (byte)(BorderColour.R * 255);
bytes[i * 4 + 1] = (byte)(BorderColour.G * 255);
bytes[i * 4 + 2] = (byte)(BorderColour.B * 255);
bytes[i * 4 + 3] = (byte)(Math.Min(progress / aa_portion, 1) * (BorderColour.A * 255));
raw[i, 0] = new Rgba32(BorderColour.R, BorderColour.G, BorderColour.B, Math.Min(progress / aa_portion, 1) * BorderColour.A);
}
else
{
progress -= border_portion;
bytes[i * 4] = (byte)(AccentColour.R * 255);
bytes[i * 4 + 1] = (byte)(AccentColour.G * 255);
bytes[i * 4 + 2] = (byte)(AccentColour.B * 255);
bytes[i * 4 + 3] = (byte)((opacity_at_edge - (opacity_at_edge - opacity_at_centre) * progress / gradient_portion) * (AccentColour.A * 255));
raw[i, 0] = new Rgba32(AccentColour.R, AccentColour.G, AccentColour.B,
(opacity_at_edge - (opacity_at_edge - opacity_at_centre) * progress / gradient_portion) * AccentColour.A);
}
}
+5
View File
@@ -117,6 +117,11 @@ namespace osu.Game.Rulesets.Osu
new OsuModRelax(),
new OsuModAutopilot(),
};
case ModType.Fun:
return new Mod[] {
new OsuModTransform(),
new OsuModWiggle(),
};
default:
return new Mod[] { };
}
+65 -1
View File
@@ -1,9 +1,14 @@
// Copyright (c) 2007-2018 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 NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Input.EventArgs;
using osu.Framework.MathUtils;
using osu.Framework.Timing;
using osu.Game.Screens.Play;
using OpenTK.Input;
@@ -12,21 +17,30 @@ namespace osu.Game.Tests.Visual
[TestFixture]
public class TestCaseKeyCounter : OsuTestCase
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(KeyCounterKeyboard),
typeof(KeyCounterMouse),
typeof(KeyCounterCollection)
};
public TestCaseKeyCounter()
{
KeyCounterKeyboard rewindTestKeyCounterKeyboard;
KeyCounterCollection kc = new KeyCounterCollection
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Children = new KeyCounter[]
{
new KeyCounterKeyboard(Key.Z),
rewindTestKeyCounterKeyboard = new KeyCounterKeyboard(Key.X),
new KeyCounterKeyboard(Key.X),
new KeyCounterMouse(MouseButton.Left),
new KeyCounterMouse(MouseButton.Right),
},
};
AddStep("Add random", () =>
{
Key key = (Key)((int)Key.A + RNG.Next(26));
@@ -34,7 +48,57 @@ namespace osu.Game.Tests.Visual
});
AddSliderStep("Fade time", 0, 200, 50, v => kc.FadeTime = v);
Key testKey = ((KeyCounterKeyboard)kc.Children.First()).Key;
double time1 = 0;
AddStep($"Press {testKey} key", () =>
{
rewindTestKeyCounterKeyboard.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = testKey, Repeat = false });
rewindTestKeyCounterKeyboard.TriggerOnKeyUp(null, new KeyUpEventArgs { Key = testKey });
});
AddAssert($"Check {testKey} counter after keypress", () => rewindTestKeyCounterKeyboard.CountPresses == 1);
AddStep($"Press {testKey} key", () =>
{
rewindTestKeyCounterKeyboard.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = testKey, Repeat = false });
rewindTestKeyCounterKeyboard.TriggerOnKeyUp(null, new KeyUpEventArgs { Key = testKey });
time1 = Clock.CurrentTime;
});
AddAssert($"Check {testKey} counter after keypress", () => rewindTestKeyCounterKeyboard.CountPresses == 2);
IFrameBasedClock oldClock = null;
AddStep($"Rewind {testKey} counter once", () =>
{
oldClock = rewindTestKeyCounterKeyboard.Clock;
rewindTestKeyCounterKeyboard.Clock = new FramedOffsetClock(new FixedClock(time1 - 10));
});
AddAssert($"Check {testKey} counter after rewind", () => rewindTestKeyCounterKeyboard.CountPresses == 1);
AddStep($"Rewind {testKey} counter to zero", () => rewindTestKeyCounterKeyboard.Clock = new FramedOffsetClock(new FixedClock(0)));
AddAssert($"Check {testKey} counter after rewind", () => rewindTestKeyCounterKeyboard.CountPresses == 0);
AddStep("Restore clock", () => rewindTestKeyCounterKeyboard.Clock = oldClock);
Add(kc);
}
private class FixedClock : IClock
{
private readonly double time;
public FixedClock(double time)
{
this.time = time;
}
public double CurrentTime => time;
public double Rate => 1;
public bool IsRunning => false;
}
}
}
+10 -27
View File
@@ -11,6 +11,7 @@ using Microsoft.EntityFrameworkCore;
using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.Extensions;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics.Textures;
using osu.Framework.Logging;
using osu.Framework.Platform;
@@ -62,6 +63,8 @@ namespace osu.Game.Beatmaps
public override string[] HandledExtensions => new[] { ".osz" };
protected override string ImportFromStablePath => "Songs";
private readonly RulesetStore rulesets;
private readonly BeatmapStore beatmaps;
@@ -72,11 +75,6 @@ namespace osu.Game.Beatmaps
private readonly List<DownloadBeatmapSetRequest> currentDownloads = new List<DownloadBeatmapSetRequest>();
/// <summary>
/// Set a storage with access to an osu-stable install for import purposes.
/// </summary>
public Func<Storage> GetStableStorage { private get; set; }
public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, APIAccess api, AudioManager audioManager, IIpcHost importHost = null)
: base(storage, contextFactory, new BeatmapStore(contextFactory), importHost)
{
@@ -195,7 +193,7 @@ namespace osu.Game.Beatmaps
downloadNotification.CompletionClickAction = () =>
{
PresentBeatmap?.Invoke(importedBeatmap);
PresentCompletedImport(importedBeatmap.Yield());
return true;
};
downloadNotification.State = ProgressNotificationState.Completed;
@@ -231,6 +229,12 @@ namespace osu.Game.Beatmaps
BeatmapDownloadBegan?.Invoke(request);
}
protected override void PresentCompletedImport(IEnumerable<BeatmapSetInfo> imported)
{
base.PresentCompletedImport(imported);
PresentBeatmap?.Invoke(imported.LastOrDefault());
}
/// <summary>
/// Get an existing download request if it exists.
/// </summary>
@@ -311,27 +315,6 @@ namespace osu.Game.Beatmaps
/// <returns>Results from the provided query.</returns>
public IQueryable<BeatmapInfo> QueryBeatmaps(Expression<Func<BeatmapInfo, bool>> query) => beatmaps.Beatmaps.AsNoTracking().Where(query);
/// <summary>
/// Denotes whether an osu-stable installation is present to perform automated imports from.
/// </summary>
public bool StableInstallationAvailable => GetStableStorage?.Invoke() != null;
/// <summary>
/// This is a temporary method and will likely be replaced by a full-fledged (and more correctly placed) migration process in the future.
/// </summary>
public Task ImportFromStable()
{
var stable = GetStableStorage?.Invoke();
if (stable == null)
{
Logger.Log("No osu!stable installation available!", LoggingTarget.Information, LogLevel.Error);
return Task.CompletedTask;
}
return Task.Factory.StartNew(() => Import(stable.GetDirectories("Songs").Select(f => stable.GetFullPath(f)).ToArray()), TaskCreationOptions.LongRunning);
}
/// <summary>
/// Create a SHA-2 hash from the provided archive based on contained beatmap (.osu) file content.
/// </summary>
@@ -55,7 +55,7 @@ namespace osu.Game.Beatmaps
try
{
return (textureStore ?? (textureStore = new LargeTextureStore(new RawTextureLoaderStore(store)))).Get(getPathForFile(Metadata.BackgroundFile));
return (textureStore ?? (textureStore = new LargeTextureStore(new TextureLoaderStore(store)))).Get(getPathForFile(Metadata.BackgroundFile));
}
catch
{
+1 -1
View File
@@ -135,7 +135,7 @@ namespace osu.Game.Beatmaps
public bool BackgroundLoaded => background.IsResultAvailable;
public Texture Background => background.Value;
protected virtual bool BackgroundStillValid(Texture b) => b == null || !b.IsDisposed;
protected virtual bool BackgroundStillValid(Texture b) => b == null || b.Available;
protected abstract Texture GetBackground();
private readonly RecyclableLazy<Texture> background;
+67 -9
View File
@@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore;
using osu.Framework.IO.File;
@@ -58,7 +59,7 @@ namespace osu.Game.Database
// ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised)
private ArchiveImportIPCChannel ipc;
private readonly List<Action> cachedEvents = new List<Action>();
private readonly List<Action> queuedEvents = new List<Action>();
/// <summary>
/// Allows delaying of outwards events until an operation is confirmed (at a database level).
@@ -76,20 +77,26 @@ namespace osu.Game.Database
/// <param name="perform">Whether the flushed events should be performed.</param>
private void flushEvents(bool perform)
{
Action[] events;
lock (queuedEvents)
{
events = queuedEvents.ToArray();
queuedEvents.Clear();
}
if (perform)
{
foreach (var a in cachedEvents)
foreach (var a in events)
a.Invoke();
}
cachedEvents.Clear();
delayingEvents = false;
}
private void handleEvent(Action a)
{
if (delayingEvents)
cachedEvents.Add(a);
lock (queuedEvents) queuedEvents.Add(a);
else
a.Invoke();
}
@@ -129,7 +136,6 @@ namespace osu.Game.Database
List<TModel> imported = new List<TModel>();
int current = 0;
int errors = 0;
foreach (string path in paths)
{
if (notification.State == ProgressNotificationState.Cancelled)
@@ -162,12 +168,29 @@ namespace osu.Game.Database
{
e = e.InnerException ?? e;
Logger.Error(e, $@"Could not import ({Path.GetFileName(path)})");
errors++;
}
}
notification.Text = errors > 0 ? $"Import complete with {errors} errors" : "Import successful!";
notification.State = ProgressNotificationState.Completed;
if (imported.Count == 0)
{
notification.Text = "Import failed!";
notification.State = ProgressNotificationState.Cancelled;
}
else
{
notification.CompletionText = $"Imported {current} {typeof(TModel).Name.Replace("Info", "").ToLower()}s!";
notification.CompletionClickAction += () =>
{
if (imported.Count > 0)
PresentCompletedImport(imported);
return true;
};
notification.State = ProgressNotificationState.Completed;
}
}
protected virtual void PresentCompletedImport(IEnumerable<TModel> imported)
{
}
/// <summary>
@@ -283,7 +306,7 @@ namespace osu.Game.Database
var notification = new ProgressNotification
{
Progress = 0,
CompletionText = "Deleted all beatmaps!",
CompletionText = $"Deleted all {typeof(TModel).Name.Replace("Info", "").ToLower()}s!",
State = ProgressNotificationState.Active,
};
@@ -385,6 +408,41 @@ namespace osu.Game.Database
return fileInfos;
}
#region osu-stable import
/// <summary>
/// Set a storage with access to an osu-stable install for import purposes.
/// </summary>
public Func<Storage> GetStableStorage { private get; set; }
/// <summary>
/// Denotes whether an osu-stable installation is present to perform automated imports from.
/// </summary>
public bool StableInstallationAvailable => GetStableStorage?.Invoke() != null;
/// <summary>
/// The relative path from osu-stable's data directory to import items from.
/// </summary>
protected virtual string ImportFromStablePath => null;
/// <summary>
/// This is a temporary method and will likely be replaced by a full-fledged (and more correctly placed) migration process in the future.
/// </summary>
public Task ImportFromStableAsync()
{
var stable = GetStableStorage?.Invoke();
if (stable == null)
{
Logger.Log("No osu!stable installation available!", LoggingTarget.Information, LogLevel.Error);
return Task.CompletedTask;
}
return Task.Factory.StartNew(() => Import(stable.GetDirectories(ImportFromStablePath).Select(f => stable.GetFullPath(f)).ToArray()), TaskCreationOptions.LongRunning);
}
#endregion
/// <summary>
/// Create a barebones model from the provided archive.
/// Actual expensive population should be done in <see cref="Populate"/>; this should just prepare for duplicate checking.
+1 -5
View File
@@ -15,11 +15,7 @@ namespace osu.Game.Online.Chat
Timestamp = DateTimeOffset.Now;
Content = message;
Sender = new User
{
Username = @"system",
Colour = @"0000ff",
};
Sender = User.SYSTEM_USER;
}
}
}
+4 -2
View File
@@ -299,11 +299,13 @@ namespace osu.Game
// This prevents the cursor from showing until we have a screen with CursorVisible = true
MenuCursorContainer.CanShowCursor = currentScreen?.CursorVisible ?? false;
// hook up notifications to components.
// todo: all archive managers should be able to be looped here.
SkinManager.PostNotification = n => notifications?.Post(n);
BeatmapManager.PostNotification = n => notifications?.Post(n);
SkinManager.GetStableStorage = GetStorageForStableInstall;
BeatmapManager.PostNotification = n => notifications?.Post(n);
BeatmapManager.GetStableStorage = GetStorageForStableInstall;
BeatmapManager.PresentBeatmap = PresentBeatmap;
AddRange(new Drawable[]
+3 -1
View File
@@ -108,7 +108,9 @@ namespace osu.Game
dependencies.Cache(contextFactory = new DatabaseContextFactory(Host.Storage));
dependencies.Cache(new LargeTextureStore(new RawTextureLoaderStore(new NamespacedResourceStore<byte[]>(Resources, @"Textures"))));
var largeStore = new LargeTextureStore(new TextureLoaderStore(new NamespacedResourceStore<byte[]>(Resources, @"Textures")));
largeStore.AddStore(new TextureLoaderStore(new OnlineStore()));
dependencies.Cache(largeStore);
dependencies.CacheAs(this);
dependencies.Cache(LocalConfig);
-2
View File
@@ -104,8 +104,6 @@ namespace osu.Game.Overlays.Direct
beatmaps.ItemAdded += setAdded;
}
public override bool DisposeOnDeathRemoval => true;
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
@@ -118,9 +118,9 @@ namespace osu.Game.Overlays.MedalSplash
}
[BackgroundDependencyLoader]
private void load(OsuColour colours, TextureStore textures)
private void load(OsuColour colours, TextureStore textures, LargeTextureStore largeTextures)
{
medalSprite.Texture = textures.Get(medal.ImageUrl);
medalSprite.Texture = largeTextures.Get(medal.ImageUrl);
medalGlow.Texture = textures.Get(@"MedalSplash/medal-glow");
description.Colour = colours.BlueLight;
}
@@ -169,7 +169,7 @@ namespace osu.Game.Overlays.Notifications
public Action<Notification> CompletionTarget { get; set; }
/// <summary>
/// An action to complete when the completion notification is clicked.
/// An action to complete when the completion notification is clicked. Return true to close.
/// </summary>
public Func<bool> CompletionClickAction;
@@ -176,7 +176,7 @@ namespace osu.Game.Overlays.Profile.Header
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
private void load(LargeTextureStore textures)
{
Child = new Sprite
{
@@ -30,7 +30,7 @@ namespace osu.Game.Overlays.Profile.Sections.Recent
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
private void load(LargeTextureStore textures)
{
sprite.Texture = textures.Get(url);
}
@@ -1,8 +1,12 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -15,21 +19,36 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
private FillFlowContainer letterboxSettings;
private Bindable<bool> letterboxing;
private Bindable<Size> sizeFullscreen;
private OsuGameBase game;
private SettingsDropdown<Size> resolutionDropdown;
private SettingsEnumDropdown<WindowMode> windowModeDropdown;
private const int transition_duration = 400;
[BackgroundDependencyLoader]
private void load(FrameworkConfigManager config)
private void load(FrameworkConfigManager config, OsuGameBase game)
{
this.game = game;
letterboxing = config.GetBindable<bool>(FrameworkSetting.Letterboxing);
sizeFullscreen = config.GetBindable<Size>(FrameworkSetting.SizeFullscreen);
Children = new Drawable[]
{
new SettingsEnumDropdown<WindowMode>
windowModeDropdown = new SettingsEnumDropdown<WindowMode>
{
LabelText = "Screen mode",
Bindable = config.GetBindable<WindowMode>(FrameworkSetting.WindowMode),
},
resolutionDropdown = new SettingsDropdown<Size>
{
LabelText = "Resolution",
ShowsDefaultIndicator = false,
Items = getResolutions(),
Bindable = sizeFullscreen
},
new SettingsCheckbox
{
LabelText = "Letterboxing",
@@ -62,15 +81,39 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
},
};
letterboxing.ValueChanged += isVisible =>
windowModeDropdown.Bindable.BindValueChanged(windowMode =>
{
if (windowMode == WindowMode.Fullscreen)
{
resolutionDropdown.Show();
sizeFullscreen.TriggerChange();
}
else
resolutionDropdown.Hide();
}, true);
letterboxing.BindValueChanged(isVisible =>
{
letterboxSettings.ClearTransforms();
letterboxSettings.AutoSizeAxes = isVisible ? Axes.Y : Axes.None;
if (!isVisible)
letterboxSettings.ResizeHeightTo(0, transition_duration, Easing.OutQuint);
};
letterboxing.TriggerChange();
}, true);
}
private IEnumerable<KeyValuePair<string, Size>> getResolutions()
{
var resolutions = new KeyValuePair<string, Size>("Default", new Size(9999, 9999)).Yield();
if (game.Window != null)
resolutions = resolutions.Concat(game.Window.AvailableResolutions
.Where(r => r.Width >= 800 && r.Height >= 600)
.OrderByDescending(r => r.Width)
.ThenByDescending(r => r.Height)
.Select(res => new KeyValuePair<string, Size>($"{res.Width}x{res.Height}", new Size(res.Width, res.Height)))
.Distinct()).ToList();
return resolutions;
}
}
}
@@ -7,6 +7,7 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Graphics.UserInterface;
using osu.Game.Skinning;
namespace osu.Game.Overlays.Settings.Sections.Maintenance
{
@@ -20,7 +21,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
protected override string Header => "General";
[BackgroundDependencyLoader]
private void load(BeatmapManager beatmaps, DialogOverlay dialogOverlay)
private void load(BeatmapManager beatmaps, SkinManager skins, DialogOverlay dialogOverlay)
{
Children = new Drawable[]
{
@@ -30,7 +31,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
Action = () =>
{
importButton.Enabled.Value = false;
beatmaps.ImportFromStable().ContinueWith(t => Schedule(() => importButton.Enabled.Value = true));
beatmaps.ImportFromStableAsync().ContinueWith(t => Schedule(() => importButton.Enabled.Value = true));
}
},
deleteButton = new DangerousSettingsButton
@@ -45,6 +46,27 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
}));
}
},
importButton = new SettingsButton
{
Text = "Import skins from stable",
Action = () =>
{
importButton.Enabled.Value = false;
skins.ImportFromStableAsync().ContinueWith(t => Schedule(() => importButton.Enabled.Value = true));
}
},
deleteButton = new DangerousSettingsButton
{
Text = "Delete ALL skins",
Action = () =>
{
dialogOverlay?.Push(new DeleteAllBeatmapsDialog(() =>
{
deleteButton.Enabled.Value = false;
Task.Run(() => skins.Delete(skins.GetAllUserSkins())).ContinueWith(t => Schedule(() => deleteButton.Enabled.Value = true));
}));
}
},
restoreButton = new SettingsButton
{
Text = "Restore all hidden difficulties",
@@ -51,10 +51,10 @@ namespace osu.Game.Overlays.Settings.Sections
},
};
skins.ItemAdded += onItemsChanged;
skins.ItemRemoved += onItemsChanged;
skins.ItemAdded += itemAdded;
skins.ItemRemoved += itemRemoved;
reloadSkins();
skinDropdown.Items = skins.GetAllUsableSkins().Select(s => new KeyValuePair<string, int>(s.ToString(), s.ID));
var skinBindable = config.GetBindable<int>(OsuSetting.Skin);
@@ -65,9 +65,8 @@ namespace osu.Game.Overlays.Settings.Sections
skinDropdown.Bindable = skinBindable;
}
private void reloadSkins() => skinDropdown.Items = skins.GetAllUsableSkins().Select(s => new KeyValuePair<string, int>(s.ToString(), s.ID));
private void onItemsChanged(SkinInfo _) => Schedule(reloadSkins);
private void itemRemoved(SkinInfo s) => skinDropdown.Items = skinDropdown.Items.Where(i => i.Value != s.ID);
private void itemAdded(SkinInfo s) => skinDropdown.Items = skinDropdown.Items.Append(new KeyValuePair<string, int>(s.ToString(), s.ID));
protected override void Dispose(bool isDisposing)
{
@@ -75,8 +74,8 @@ namespace osu.Game.Overlays.Settings.Sections
if (skins != null)
{
skins.ItemAdded -= onItemsChanged;
skins.ItemRemoved -= onItemsChanged;
skins.ItemAdded -= itemAdded;
skins.ItemRemoved -= itemRemoved;
}
}
+3 -1
View File
@@ -77,9 +77,11 @@ namespace osu.Game.Overlays
public void ShowUser(User user, bool fetchOnline = true)
{
if (user == User.SYSTEM_USER) return;
Show();
if (user.Id == Header?.User.Id)
if (user.Id == Header?.User?.Id)
return;
userReq?.Cancel();
@@ -7,6 +7,8 @@ using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Extensions.TypeExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Primitives;
using osu.Game.Audio;
using osu.Game.Graphics;
using osu.Game.Rulesets.Judgements;
@@ -165,6 +167,14 @@ namespace osu.Game.Rulesets.Objects.Drawables
}
}
public override bool UpdateSubTreeMasking(Drawable source, RectangleF maskingBounds)
{
if (!AllJudged)
return false;
return base.UpdateSubTreeMasking(source, maskingBounds);
}
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
+2 -3
View File
@@ -84,11 +84,10 @@ namespace osu.Game.Screens.Menu
private const double early_activation = 60;
public override bool IsPresent => base.IsPresent || Scheduler.HasPendingTasks;
public OsuLogo()
{
// Required to make Schedule calls run in OsuScreen even when we are not visible.
AlwaysPresent = true;
EarlyActivationMilliseconds = early_activation;
Size = new Vector2(default_size);
+3 -2
View File
@@ -69,7 +69,7 @@ namespace osu.Game.Screens.Play
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
KeyCounter = CreateKeyCounter(),
KeyCounter = CreateKeyCounter(adjustableClock as IFrameBasedClock),
HoldToQuit = CreateQuitButton(),
}
}
@@ -194,12 +194,13 @@ namespace osu.Game.Screens.Play
Margin = new MarginPadding { Top = 20 }
};
protected virtual KeyCounterCollection CreateKeyCounter() => new KeyCounterCollection
protected virtual KeyCounterCollection CreateKeyCounter(IFrameBasedClock offsetClock) => new KeyCounterCollection
{
FadeTime = 50,
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
Margin = new MarginPadding(10),
AudioClock = offsetClock
};
protected virtual ScoreCounter CreateScoreCounter() => new ScoreCounter(6)
+35 -1
View File
@@ -1,6 +1,8 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -19,6 +21,9 @@ namespace osu.Game.Screens.Play
private Container textLayer;
private SpriteText countSpriteText;
private readonly List<KeyCounterState> states = new List<KeyCounterState>();
private KeyCounterState currentState;
public bool IsCounting { get; set; } = true;
private int countPresses;
public int CountPresses
@@ -45,7 +50,10 @@ namespace osu.Game.Screens.Play
isLit = value;
updateGlowSprite(value);
if (value && IsCounting)
{
CountPresses++;
saveState();
}
}
}
}
@@ -128,6 +136,32 @@ namespace osu.Game.Screens.Play
}
}
public void ResetCount() => CountPresses = 0;
public void ResetCount()
{
CountPresses = 0;
states.Clear();
}
protected override void Update()
{
base.Update();
if (currentState?.Time > Clock.CurrentTime)
restoreStateTo(Clock.CurrentTime);
}
private void saveState()
{
if (currentState == null || currentState.Time < Clock.CurrentTime)
states.Add(currentState = new KeyCounterState(Clock.CurrentTime, CountPresses));
}
private void restoreStateTo(double time)
{
states.RemoveAll(state => state.Time > time);
currentState = states.LastOrDefault();
CountPresses = currentState?.Count ?? 0;
}
}
}
@@ -3,15 +3,16 @@
using System;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using OpenTK.Graphics;
using osu.Framework.Configuration;
using osu.Framework.Allocation;
using osu.Framework.Input.EventArgs;
using osu.Framework.Input.States;
using osu.Framework.Timing;
using osu.Game.Configuration;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Screens.Play
{
@@ -37,6 +38,9 @@ namespace osu.Game.Screens.Play
key.FadeTime = FadeTime;
key.KeyDownTextColor = KeyDownTextColor;
key.KeyUpTextColor = KeyUpTextColor;
// Use the same clock object as SongProgress for saving KeyCounter state
if (AudioClock != null)
key.Clock = AudioClock;
}
public void ResetCount()
@@ -118,6 +122,8 @@ namespace osu.Game.Screens.Play
public override bool HandleKeyboardInput => receptor == null;
public override bool HandleMouseInput => receptor == null;
public IFrameBasedClock AudioClock { get; set; }
private Receptor receptor;
public Receptor GetReceptor()
+17
View File
@@ -0,0 +1,17 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Screens.Play
{
public class KeyCounterState
{
public KeyCounterState(double time, int count)
{
Time = time;
Count = count;
}
public readonly double Time;
public readonly int Count;
}
}
+12 -4
View File
@@ -29,6 +29,7 @@ namespace osu.Game.Screens.Ranking
{
public class ResultsPageScore : ResultsPage
{
private Container scoreContainer;
private ScoreCounter scoreCounter;
public ResultsPageScore(Score score, WorkingBeatmap beatmap) : base(score, beatmap) { }
@@ -76,7 +77,7 @@ namespace osu.Game.Screens.Ranking
Size = new Vector2(150, 60),
Margin = new MarginPadding(20),
},
new Container
scoreContainer = new Container
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
@@ -92,8 +93,8 @@ namespace osu.Game.Screens.Ranking
},
scoreCounter = new SlowScoreCounter(6)
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Colour = colours.PinkDarker,
Y = 10,
TextSize = 56,
@@ -185,6 +186,13 @@ namespace osu.Game.Screens.Ranking
});
}
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
scoreCounter.Scale = new Vector2(Math.Min(1f, (scoreContainer.DrawWidth - 20) / scoreCounter.DrawWidth));
}
private class DrawableScoreStatistic : Container
{
private readonly KeyValuePair<HitResult, object> statistic;
@@ -368,7 +376,7 @@ namespace osu.Game.Screens.Ranking
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
private void load(LargeTextureStore textures)
{
if (!string.IsNullOrEmpty(user.CoverUrl))
cover.Texture = textures.Get(user.CoverUrl);
@@ -12,7 +12,7 @@ namespace osu.Game.Screens.Select
public ImportFromStablePopup(Action importFromStable)
{
HeaderText = @"You have no beatmaps!";
BodyText = "An existing copy of osu! was found, though.\nWould you like to import your beatmaps?";
BodyText = "An existing copy of osu! was found, though.\nWould you like to import your beatmaps (and skins)?";
Icon = FontAwesome.fa_plane;
+6 -3
View File
@@ -3,7 +3,6 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using OpenTK.Input;
using osu.Framework.Allocation;
using osu.Framework.Audio;
@@ -20,6 +19,7 @@ using osu.Game.Rulesets.Mods;
using osu.Game.Screens.Edit;
using osu.Game.Screens.Play;
using osu.Game.Screens.Ranking;
using osu.Game.Skinning;
namespace osu.Game.Screens.Select
{
@@ -55,7 +55,7 @@ namespace osu.Game.Screens.Select
private readonly Bindable<IEnumerable<Mod>> selectedMods = new Bindable<IEnumerable<Mod>>(new Mod[] { });
[BackgroundDependencyLoader(true)]
private void load(OsuColour colours, AudioManager audio, BeatmapManager beatmaps, DialogOverlay dialogOverlay, Bindable<IEnumerable<Mod>> selectedMods)
private void load(OsuColour colours, AudioManager audio, BeatmapManager beatmaps, SkinManager skins, DialogOverlay dialogOverlay, Bindable<IEnumerable<Mod>> selectedMods)
{
if (selectedMods != null) this.selectedMods.BindTo(selectedMods);
@@ -78,7 +78,10 @@ namespace osu.Game.Screens.Select
// if we have no beatmaps but osu-stable is found, let's prompt the user to import.
if (!beatmaps.GetAllUsableBeatmapSets().Any() && beatmaps.StableInstallationAvailable)
dialogOverlay.Push(new ImportFromStablePopup(() =>
Task.Factory.StartNew(beatmaps.ImportFromStable, TaskCreationOptions.LongRunning)));
{
beatmaps.ImportFromStableAsync();
skins.ImportFromStableAsync();
}));
});
}
}
+1 -1
View File
@@ -59,7 +59,7 @@ namespace osu.Game.Screens.Tournament
TextureStore flagStore = new TextureStore();
// Local flag store
flagStore.AddStore(new RawTextureLoaderStore(new NamespacedResourceStore<byte[]>(new StorageBackedResourceStore(storage), "Drawings")));
flagStore.AddStore(new TextureLoaderStore(new NamespacedResourceStore<byte[]>(new StorageBackedResourceStore(storage), "Drawings")));
// Default texture store
flagStore.AddStore(textures);
+1 -1
View File
@@ -37,7 +37,7 @@ namespace osu.Game.Skinning
Configuration = new SkinConfiguration();
Samples = audioManager.GetSampleManager(storage);
Textures = new TextureStore(new RawTextureLoaderStore(storage));
Textures = new TextureStore(new TextureLoaderStore(storage));
}
public override Drawable GetDrawableComponent(string componentName)
+17 -12
View File
@@ -26,17 +26,25 @@ namespace osu.Game.Skinning
public override string[] HandledExtensions => new[] { ".osk" };
protected override string ImportFromStablePath => "Skins";
/// <summary>
/// Returns a list of all usable <see cref="SkinInfo"/>s.
/// Returns a list of all usable <see cref="SkinInfo"/>s. Includes the special default skin plus all skins from <see cref="GetAllUserSkins"/>.
/// </summary>
/// <returns>A list of available <see cref="SkinInfo"/>.</returns>
public List<SkinInfo> GetAllUsableSkins()
{
var userSkins = ModelStore.ConsumableItems.Where(s => !s.DeletePending).ToList();
var userSkins = GetAllUserSkins();
userSkins.Insert(0, SkinInfo.Default);
return userSkins;
}
/// <summary>
/// Returns a list of all usable <see cref="SkinInfo"/>s that have been loaded by the user.
/// </summary>
/// <returns>A list of available <see cref="SkinInfo"/>.</returns>
public List<SkinInfo> GetAllUserSkins() => ModelStore.ConsumableItems.Where(s => !s.DeletePending).ToList();
protected override SkinInfo CreateModel(ArchiveReader archive) => new SkinInfo
{
Name = archive.Name
@@ -85,6 +93,13 @@ namespace osu.Game.Skinning
{
this.audio = audio;
ItemRemoved += removedInfo =>
{
// check the removed skin is not the current user choice. if it is, switch back to default.
if (removedInfo.ID == CurrentSkinInfo.Value.ID)
CurrentSkinInfo.Value = SkinInfo.Default;
};
CurrentSkinInfo.ValueChanged += info => CurrentSkin.Value = GetSkin(info);
CurrentSkin.ValueChanged += skin =>
{
@@ -93,16 +108,6 @@ namespace osu.Game.Skinning
SourceChanged?.Invoke();
};
// migrate older imports which didn't have access to skin.ini
using (ContextFactory.GetForWrite())
{
foreach (var skinInfo in ModelStore.ConsumableItems.Where(s => s.Name.EndsWith(".osk")))
{
populate(skinInfo);
Update(skinInfo);
}
}
}
/// <summary>
@@ -22,7 +22,6 @@ namespace osu.Game.Storyboards.Drawables
public override bool HandleMouseInput => false;
private bool passing = true;
public bool Passing
{
get { return passing; }
@@ -37,7 +36,6 @@ namespace osu.Game.Storyboards.Drawables
public override bool RemoveCompletedTransforms => false;
private DependencyContainer dependencies;
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) =>
dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
@@ -59,7 +57,7 @@ namespace osu.Game.Storyboards.Drawables
[BackgroundDependencyLoader]
private void load(FileStore fileStore)
{
dependencies.Cache(new TextureStore(new RawTextureLoaderStore(fileStore.Store), false, scaleAdjust: 1));
dependencies.Cache(new TextureStore(new TextureLoaderStore(fileStore.Store), false, scaleAdjust: 1));
foreach (var layer in Storyboard.Layers)
Add(layer.CreateDrawable());
+4 -4
View File
@@ -54,7 +54,7 @@ namespace osu.Game.Tests.Visual
AddStep(r.Name, () => p = loadPlayerFor(r));
AddUntilStep(() => ContinueCondition(p));
AddAssert("no leaked beatmaps", () =>
AddUntilStep(() =>
{
p = null;
@@ -64,9 +64,9 @@ namespace osu.Game.Tests.Visual
workingWeakReferences.ForEachAlive(_ => count++);
return count == 1;
});
}, "no leaked beatmaps");
AddAssert("no leaked players", () =>
AddUntilStep(() =>
{
GC.Collect();
GC.WaitForPendingFinalizers();
@@ -74,7 +74,7 @@ namespace osu.Game.Tests.Visual
playerWeakReferences.ForEachAlive(_ => count++);
return count == 1;
});
}, "no leaked players");
}
}
}
+1 -1
View File
@@ -24,7 +24,7 @@ namespace osu.Game.Users
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
private void load(LargeTextureStore textures)
{
if (textures == null)
throw new ArgumentNullException(nameof(textures));
+9
View File
@@ -144,5 +144,14 @@ namespace osu.Game.Users
public Badge[] Badges;
public override string ToString() => Username;
/// <summary>
/// A user instance for displaying locally created system messages.
/// </summary>
public static readonly User SYSTEM_USER = new User
{
Username = "system",
Id = 0
};
}
}
+1 -1
View File
@@ -18,7 +18,7 @@ namespace osu.Game.Users
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
private void load(LargeTextureStore textures)
{
if (textures == null)
throw new ArgumentNullException(nameof(textures));
+2 -2
View File
@@ -16,9 +16,9 @@
<ItemGroup Label="Package References">
<PackageReference Include="Humanizer" Version="2.4.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.1.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.1.3" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="ppy.osu.Framework" Version="2018.906.1" />
<PackageReference Include="ppy.osu.Framework" Version="2018.913.0" />
<PackageReference Include="SharpCompress" Version="0.22.0" />
<PackageReference Include="NUnit" Version="3.10.1" />
<PackageReference Include="SharpRaven" Version="2.4.0" />
+1 -1
View File
@@ -11,7 +11,7 @@
<ProjectReference Include="..\osu-resources\osu.Game.Resources\osu.Game.Resources.csproj" />
</ItemGroup>
<ItemGroup Label="Package References">
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.3" />
<PackageReference Include="DeepEqual" Version="1.6.0" />
</ItemGroup>
<ItemGroup>