mirror of
https://github.com/ppy/osu.git
synced 2025-01-12 12:22:56 +08:00
Merge branch 'realm-nested-writes' into metadata-client
This commit is contained in:
commit
79bed0abdf
@ -120,10 +120,10 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
lastHyperDashState = Catcher.HyperDashing;
|
||||
}
|
||||
|
||||
public void SetCatcherPosition(float X)
|
||||
public void SetCatcherPosition(float x)
|
||||
{
|
||||
float lastPosition = Catcher.X;
|
||||
float newPosition = Math.Clamp(X, 0, CatchPlayfield.WIDTH);
|
||||
float newPosition = Math.Clamp(x, 0, CatchPlayfield.WIDTH);
|
||||
|
||||
Catcher.X = newPosition;
|
||||
|
||||
|
@ -90,7 +90,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
complete.BindValueChanged(complete => updateComplete(complete.NewValue, 200));
|
||||
complete.BindValueChanged(complete => updateDiscColour(complete.NewValue, 200));
|
||||
drawableSpinner.ApplyCustomUpdateState += updateStateTransforms;
|
||||
|
||||
updateStateTransforms(drawableSpinner, drawableSpinner.State.Value);
|
||||
@ -137,6 +137,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||
this.ScaleTo(initial_scale);
|
||||
this.RotateTo(0);
|
||||
|
||||
updateDiscColour(false);
|
||||
|
||||
using (BeginDelayedSequence(spinner.TimePreempt / 2))
|
||||
{
|
||||
// constant ambient rotation to give the spinner "spinning" character.
|
||||
@ -177,12 +179,14 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||
}
|
||||
}
|
||||
|
||||
// transforms we have from completing the spinner will be rolled back, so reapply immediately.
|
||||
using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt))
|
||||
updateComplete(state == ArmedState.Hit, 0);
|
||||
if (drawableSpinner.Result?.TimeCompleted is double completionTime)
|
||||
{
|
||||
using (BeginAbsoluteSequence(completionTime))
|
||||
updateDiscColour(true, 200);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateComplete(bool complete, double duration)
|
||||
private void updateDiscColour(bool complete, double duration = 0)
|
||||
{
|
||||
var colour = complete ? completeColour : normalColour;
|
||||
|
||||
|
@ -59,6 +59,25 @@ namespace osu.Game.Tests.Database
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNestedWriteCalls()
|
||||
{
|
||||
RunTestWithRealm((realm, _) =>
|
||||
{
|
||||
var beatmap = new BeatmapInfo(CreateRuleset(), new BeatmapDifficulty(), new BeatmapMetadata());
|
||||
|
||||
var liveBeatmap = beatmap.ToLive(realm);
|
||||
|
||||
realm.Run(r =>
|
||||
r.Write(_ =>
|
||||
r.Write(_ =>
|
||||
r.Add(beatmap)))
|
||||
);
|
||||
|
||||
Assert.IsFalse(liveBeatmap.PerformRead(l => l.Hidden));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAccessAfterAttach()
|
||||
{
|
||||
|
@ -9,7 +9,6 @@ using osu.Framework.Audio;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Screens.Play;
|
||||
@ -116,10 +115,10 @@ namespace osu.Game.Tests.Gameplay
|
||||
AddStep($"set audio offset to {userOffset}", () => localConfig.SetValue(OsuSetting.AudioOffset, userOffset));
|
||||
|
||||
AddStep("seek to 2500", () => gameplayClockContainer.Seek(2500));
|
||||
AddAssert("gameplay clock time = 2500", () => Precision.AlmostEquals(gameplayClockContainer.CurrentTime, 2500, 10f));
|
||||
AddStep("gameplay clock time = 2500", () => Assert.AreEqual(gameplayClockContainer.CurrentTime, 2500, 10f));
|
||||
|
||||
AddStep("seek to 10000", () => gameplayClockContainer.Seek(10000));
|
||||
AddAssert("gameplay clock time = 10000", () => Precision.AlmostEquals(gameplayClockContainer.CurrentTime, 10000, 10f));
|
||||
AddStep("gameplay clock time = 10000", () => Assert.AreEqual(gameplayClockContainer.CurrentTime, 10000, 10f));
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
|
@ -18,6 +18,7 @@ using osu.Game.Beatmaps;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Graphics.Cursor;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Models;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.Leaderboards;
|
||||
using osu.Game.Overlays;
|
||||
@ -100,6 +101,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
Rank = ScoreRank.XH,
|
||||
User = new APIUser { Username = "TestUser" },
|
||||
Ruleset = new OsuRuleset().RulesetInfo,
|
||||
Files = { new RealmNamedFileUsage(new RealmFile { Hash = $"{i}" }, string.Empty) }
|
||||
};
|
||||
|
||||
importedScores.Add(scoreManager.Import(score).Value);
|
||||
|
@ -29,7 +29,7 @@ namespace osu.Game.Beatmaps.Formats
|
||||
{
|
||||
Section section = Section.General;
|
||||
|
||||
string line;
|
||||
string? line;
|
||||
|
||||
while ((line = stream.ReadLine()) != null)
|
||||
{
|
||||
|
@ -137,8 +137,17 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
try
|
||||
{
|
||||
using (var stream = new LineBufferedReader(GetStream(BeatmapSetInfo.GetPathForFile(BeatmapInfo.Path))))
|
||||
return Decoder.GetDecoder<Beatmap>(stream).Decode(stream);
|
||||
string fileStorePath = BeatmapSetInfo.GetPathForFile(BeatmapInfo.Path);
|
||||
var stream = GetStream(fileStorePath);
|
||||
|
||||
if (stream == null)
|
||||
{
|
||||
Logger.Log($"Beatmap failed to load (file {BeatmapInfo.Path} not found on disk at expected location {fileStorePath}).", level: LogLevel.Error);
|
||||
return null;
|
||||
}
|
||||
|
||||
using (var reader = new LineBufferedReader(stream))
|
||||
return Decoder.GetDecoder<Beatmap>(reader).Decode(reader);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@ -154,7 +163,16 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
try
|
||||
{
|
||||
return resources.LargeTextureStore.Get(BeatmapSetInfo.GetPathForFile(Metadata.BackgroundFile));
|
||||
string fileStorePath = BeatmapSetInfo.GetPathForFile(Metadata.BackgroundFile);
|
||||
var texture = resources.LargeTextureStore.Get(fileStorePath);
|
||||
|
||||
if (texture == null)
|
||||
{
|
||||
Logger.Log($"Beatmap background failed to load (file {Metadata.BackgroundFile} not found on disk at expected location {fileStorePath}).", level: LogLevel.Error);
|
||||
return null;
|
||||
}
|
||||
|
||||
return texture;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@ -173,7 +191,16 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
try
|
||||
{
|
||||
return resources.Tracks.Get(BeatmapSetInfo.GetPathForFile(Metadata.AudioFile));
|
||||
string fileStorePath = BeatmapSetInfo.GetPathForFile(Metadata.AudioFile);
|
||||
var track = resources.Tracks.Get(fileStorePath);
|
||||
|
||||
if (track == null)
|
||||
{
|
||||
Logger.Log($"Beatmap failed to load (file {Metadata.AudioFile} not found on disk at expected location {fileStorePath}).", level: LogLevel.Error);
|
||||
return null;
|
||||
}
|
||||
|
||||
return track;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@ -192,8 +219,17 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
try
|
||||
{
|
||||
var trackData = GetStream(BeatmapSetInfo.GetPathForFile(Metadata.AudioFile));
|
||||
return trackData == null ? null : new Waveform(trackData);
|
||||
string fileStorePath = BeatmapSetInfo.GetPathForFile(Metadata.AudioFile);
|
||||
|
||||
var trackData = GetStream(fileStorePath);
|
||||
|
||||
if (trackData == null)
|
||||
{
|
||||
Logger.Log($"Beatmap waveform failed to load (file {Metadata.AudioFile} not found on disk at expected location {fileStorePath}).", level: LogLevel.Error);
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Waveform(trackData);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@ -211,20 +247,38 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
try
|
||||
{
|
||||
using (var stream = new LineBufferedReader(GetStream(BeatmapSetInfo.GetPathForFile(BeatmapInfo.Path))))
|
||||
string fileStorePath = BeatmapSetInfo.GetPathForFile(BeatmapInfo.Path);
|
||||
var beatmapFileStream = GetStream(fileStorePath);
|
||||
|
||||
if (beatmapFileStream == null)
|
||||
{
|
||||
var decoder = Decoder.GetDecoder<Storyboard>(stream);
|
||||
Logger.Log($"Beatmap failed to load (file {BeatmapInfo.Path} not found on disk at expected location {fileStorePath})", level: LogLevel.Error);
|
||||
return null;
|
||||
}
|
||||
|
||||
string storyboardFilename = BeatmapSetInfo?.Files.FirstOrDefault(f => f.Filename.EndsWith(".osb", StringComparison.OrdinalIgnoreCase))?.Filename;
|
||||
using (var reader = new LineBufferedReader(beatmapFileStream))
|
||||
{
|
||||
var decoder = Decoder.GetDecoder<Storyboard>(reader);
|
||||
|
||||
// todo: support loading from both set-wide storyboard *and* beatmap specific.
|
||||
if (string.IsNullOrEmpty(storyboardFilename))
|
||||
storyboard = decoder.Decode(stream);
|
||||
else
|
||||
Stream storyboardFileStream = null;
|
||||
|
||||
if (BeatmapSetInfo?.Files.FirstOrDefault(f => f.Filename.EndsWith(".osb", StringComparison.OrdinalIgnoreCase))?.Filename is string storyboardFilename)
|
||||
{
|
||||
using (var secondaryStream = new LineBufferedReader(GetStream(BeatmapSetInfo.GetPathForFile(storyboardFilename))))
|
||||
storyboard = decoder.Decode(stream, secondaryStream);
|
||||
string storyboardFileStorePath = BeatmapSetInfo?.GetPathForFile(storyboardFilename);
|
||||
storyboardFileStream = GetStream(storyboardFileStorePath);
|
||||
|
||||
if (storyboardFileStream == null)
|
||||
Logger.Log($"Storyboard failed to load (file {storyboardFilename} not found on disk at expected location {storyboardFileStorePath})", level: LogLevel.Error);
|
||||
}
|
||||
|
||||
if (storyboardFileStream != null)
|
||||
{
|
||||
// Stand-alone storyboard was found, so parse in addition to the beatmap's local storyboard.
|
||||
using (var secondaryReader = new LineBufferedReader(storyboardFileStream))
|
||||
storyboard = decoder.Decode(reader, secondaryReader);
|
||||
}
|
||||
else
|
||||
storyboard = decoder.Decode(reader);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
|
@ -8,7 +8,9 @@ using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Extensions.TypeExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Statistics;
|
||||
|
||||
namespace osu.Game.Database
|
||||
{
|
||||
@ -20,8 +22,16 @@ namespace osu.Game.Database
|
||||
{
|
||||
private readonly ConcurrentDictionary<TLookup, TValue> cache = new ConcurrentDictionary<TLookup, TValue>();
|
||||
|
||||
private readonly GlobalStatistic<MemoryCachingStatistics> statistics;
|
||||
|
||||
protected virtual bool CacheNullValues => true;
|
||||
|
||||
protected MemoryCachingComponent()
|
||||
{
|
||||
statistics = GlobalStatistics.Get<MemoryCachingStatistics>(nameof(MemoryCachingComponent<TLookup, TValue>), GetType().ReadableName());
|
||||
statistics.Value = new MemoryCachingStatistics();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve the cached value for the given lookup.
|
||||
/// </summary>
|
||||
@ -30,12 +40,20 @@ namespace osu.Game.Database
|
||||
protected async Task<TValue> GetAsync([NotNull] TLookup lookup, CancellationToken token = default)
|
||||
{
|
||||
if (CheckExists(lookup, out TValue performance))
|
||||
{
|
||||
statistics.Value.HitCount++;
|
||||
return performance;
|
||||
}
|
||||
|
||||
var computed = await ComputeValueAsync(lookup, token).ConfigureAwait(false);
|
||||
|
||||
statistics.Value.MissCount++;
|
||||
|
||||
if (computed != null || CacheNullValues)
|
||||
{
|
||||
cache[lookup] = computed;
|
||||
statistics.Value.Usage = cache.Count;
|
||||
}
|
||||
|
||||
return computed;
|
||||
}
|
||||
@ -51,6 +69,8 @@ namespace osu.Game.Database
|
||||
if (matchKeyPredicate(kvp.Key))
|
||||
cache.TryRemove(kvp.Key, out _);
|
||||
}
|
||||
|
||||
statistics.Value.Usage = cache.Count;
|
||||
}
|
||||
|
||||
protected bool CheckExists([NotNull] TLookup lookup, out TValue value) =>
|
||||
@ -63,5 +83,31 @@ namespace osu.Game.Database
|
||||
/// <param name="token">An optional <see cref="CancellationToken"/> to cancel the operation.</param>
|
||||
/// <returns>The computed value.</returns>
|
||||
protected abstract Task<TValue> ComputeValueAsync(TLookup lookup, CancellationToken token = default);
|
||||
|
||||
private class MemoryCachingStatistics
|
||||
{
|
||||
/// <summary>
|
||||
/// Total number of cache hits.
|
||||
/// </summary>
|
||||
public int HitCount;
|
||||
|
||||
/// <summary>
|
||||
/// Total number of cache misses.
|
||||
/// </summary>
|
||||
public int MissCount;
|
||||
|
||||
/// <summary>
|
||||
/// Total number of cached entities.
|
||||
/// </summary>
|
||||
public int Usage;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
int totalAccesses = HitCount + MissCount;
|
||||
double hitRate = totalAccesses == 0 ? 0 : (double)HitCount / totalAccesses;
|
||||
|
||||
return $"i:{Usage} h:{HitCount} m:{MissCount} {hitRate:0%}";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,18 +8,45 @@ namespace osu.Game.Database
|
||||
{
|
||||
public static class RealmExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Perform a write operation against the provided realm instance.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This will automatically start a transaction if not already in one.
|
||||
/// </remarks>
|
||||
/// <param name="realm">The realm to operate on.</param>
|
||||
/// <param name="function">The write operation to run.</param>
|
||||
public static void Write(this Realm realm, Action<Realm> function)
|
||||
{
|
||||
using var transaction = realm.BeginWrite();
|
||||
Transaction? transaction = null;
|
||||
|
||||
if (!realm.IsInTransaction)
|
||||
transaction = realm.BeginWrite();
|
||||
|
||||
function(realm);
|
||||
transaction.Commit();
|
||||
|
||||
transaction?.Commit();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform a write operation against the provided realm instance.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This will automatically start a transaction if not already in one.
|
||||
/// </remarks>
|
||||
/// <param name="realm">The realm to operate on.</param>
|
||||
/// <param name="function">The write operation to run.</param>
|
||||
public static T Write<T>(this Realm realm, Func<Realm, T> function)
|
||||
{
|
||||
using var transaction = realm.BeginWrite();
|
||||
Transaction? transaction = null;
|
||||
|
||||
if (!realm.IsInTransaction)
|
||||
transaction = realm.BeginWrite();
|
||||
|
||||
var result = function(realm);
|
||||
transaction.Commit();
|
||||
|
||||
transaction?.Commit();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -86,7 +86,7 @@ namespace osu.Game.Graphics.Containers
|
||||
TimingControlPoint timingPoint;
|
||||
EffectControlPoint effectPoint;
|
||||
|
||||
IsBeatSyncedWithTrack = BeatSyncSource.Clock?.IsRunning == true;
|
||||
IsBeatSyncedWithTrack = BeatSyncSource.Clock?.IsRunning == true && BeatSyncSource.ControlPoints != null;
|
||||
|
||||
double currentTrackTime;
|
||||
|
||||
|
@ -1,10 +1,7 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
@ -17,34 +14,31 @@ namespace osu.Game.IO
|
||||
public class LineBufferedReader : IDisposable
|
||||
{
|
||||
private readonly StreamReader streamReader;
|
||||
private readonly Queue<string> lineBuffer;
|
||||
|
||||
private string? peekedLine;
|
||||
|
||||
public LineBufferedReader(Stream stream, bool leaveOpen = false)
|
||||
{
|
||||
streamReader = new StreamReader(stream, Encoding.UTF8, true, 1024, leaveOpen);
|
||||
lineBuffer = new Queue<string>();
|
||||
streamReader = new StreamReader(stream, Encoding.UTF8, true, -1, leaveOpen);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the next line from the stream without consuming it.
|
||||
/// Subsequent calls to <see cref="PeekLine"/> without a <see cref="ReadLine"/> will return the same string.
|
||||
/// </summary>
|
||||
public string PeekLine()
|
||||
{
|
||||
if (lineBuffer.Count > 0)
|
||||
return lineBuffer.Peek();
|
||||
|
||||
string line = streamReader.ReadLine();
|
||||
if (line != null)
|
||||
lineBuffer.Enqueue(line);
|
||||
return line;
|
||||
}
|
||||
public string? PeekLine() => peekedLine ??= streamReader.ReadLine();
|
||||
|
||||
/// <summary>
|
||||
/// Reads the next line from the stream and consumes it.
|
||||
/// If a line was peeked, that same line will then be consumed and returned.
|
||||
/// </summary>
|
||||
public string ReadLine() => lineBuffer.Count > 0 ? lineBuffer.Dequeue() : streamReader.ReadLine();
|
||||
public string? ReadLine()
|
||||
{
|
||||
string? line = peekedLine ?? streamReader.ReadLine();
|
||||
|
||||
peekedLine = null;
|
||||
return line;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the stream to its end and returns the text read.
|
||||
@ -53,14 +47,13 @@ namespace osu.Game.IO
|
||||
public string ReadToEnd()
|
||||
{
|
||||
string remainingText = streamReader.ReadToEnd();
|
||||
if (lineBuffer.Count == 0)
|
||||
if (peekedLine == null)
|
||||
return remainingText;
|
||||
|
||||
var builder = new StringBuilder();
|
||||
|
||||
// this might not be completely correct due to varying platform line endings
|
||||
while (lineBuffer.Count > 0)
|
||||
builder.AppendLine(lineBuffer.Dequeue());
|
||||
builder.AppendLine(peekedLine);
|
||||
builder.Append(remainingText);
|
||||
|
||||
return builder.ToString();
|
||||
@ -68,7 +61,7 @@ namespace osu.Game.IO
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
streamReader?.Dispose();
|
||||
streamReader.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -426,10 +426,10 @@ namespace osu.Game.Online.Leaderboards
|
||||
items.Add(new OsuMenuItem("Use these mods", MenuItemType.Highlighted, () => songSelect.Mods.Value = Score.Mods));
|
||||
|
||||
if (Score.Files.Count > 0)
|
||||
{
|
||||
items.Add(new OsuMenuItem("Export", MenuItemType.Standard, () => new LegacyScoreExporter(storage).Export(Score)));
|
||||
|
||||
if (!isOnlineScope)
|
||||
items.Add(new OsuMenuItem(CommonStrings.ButtonsDelete, MenuItemType.Destructive, () => dialogOverlay?.Push(new LocalScoreDeleteDialog(Score))));
|
||||
}
|
||||
|
||||
return items.ToArray();
|
||||
}
|
||||
|
@ -597,7 +597,7 @@ namespace osu.Game
|
||||
Host.ExceptionThrown -= onExceptionThrown;
|
||||
}
|
||||
|
||||
ControlPointInfo IBeatSyncProvider.ControlPoints => Beatmap.Value.Beatmap.ControlPointInfo;
|
||||
ControlPointInfo IBeatSyncProvider.ControlPoints => Beatmap.Value.BeatmapLoaded ? Beatmap.Value.Beatmap.ControlPointInfo : null;
|
||||
IClock IBeatSyncProvider.Clock => Beatmap.Value.TrackLoaded ? Beatmap.Value.Track : (IClock)null;
|
||||
ChannelAmplitudes? IBeatSyncProvider.Amplitudes => Beatmap.Value.TrackLoaded ? Beatmap.Value.Track.CurrentAmplitudes : null;
|
||||
}
|
||||
|
@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.LocalisationExtensions;
|
||||
@ -23,19 +21,19 @@ namespace osu.Game.Overlays.Login
|
||||
{
|
||||
public class LoginForm : FillFlowContainer
|
||||
{
|
||||
private TextBox username;
|
||||
private TextBox password;
|
||||
private ShakeContainer shakeSignIn;
|
||||
private TextBox username = null!;
|
||||
private TextBox password = null!;
|
||||
private ShakeContainer shakeSignIn = null!;
|
||||
|
||||
[Resolved(CanBeNull = true)]
|
||||
private IAPIProvider api { get; set; }
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; } = null!;
|
||||
|
||||
public Action RequestHide;
|
||||
public Action? RequestHide;
|
||||
|
||||
private void performLogin()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(username.Text) && !string.IsNullOrEmpty(password.Text))
|
||||
api?.Login(username.Text, password.Text);
|
||||
api.Login(username.Text, password.Text);
|
||||
else
|
||||
shakeSignIn.Shake();
|
||||
}
|
||||
@ -49,6 +47,7 @@ namespace osu.Game.Overlays.Login
|
||||
RelativeSizeAxes = Axes.X;
|
||||
|
||||
ErrorTextFlowContainer errorText;
|
||||
LinkFlowContainer forgottenPaswordLink;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
@ -56,7 +55,7 @@ namespace osu.Game.Overlays.Login
|
||||
{
|
||||
PlaceholderText = UsersStrings.LoginUsername.ToLower(),
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Text = api?.ProvidedUsername ?? string.Empty,
|
||||
Text = api.ProvidedUsername,
|
||||
TabbableContentContainer = this
|
||||
},
|
||||
password = new OsuPasswordTextBox
|
||||
@ -80,6 +79,12 @@ namespace osu.Game.Overlays.Login
|
||||
LabelText = "Stay signed in",
|
||||
Current = config.GetBindable<bool>(OsuSetting.SavePassword),
|
||||
},
|
||||
forgottenPaswordLink = new LinkFlowContainer
|
||||
{
|
||||
Padding = new MarginPadding { Left = SettingsPanel.CONTENT_MARGINS },
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
},
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
@ -103,15 +108,17 @@ namespace osu.Game.Overlays.Login
|
||||
Text = "Register",
|
||||
Action = () =>
|
||||
{
|
||||
RequestHide();
|
||||
RequestHide?.Invoke();
|
||||
accountCreation.Show();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
forgottenPaswordLink.AddLink(LayoutStrings.PopupLoginLoginForgot, $"{api.WebsiteRootUrl}/home/password-reset");
|
||||
|
||||
password.OnCommit += (_, _) => performLogin();
|
||||
|
||||
if (api?.LastLoginError?.Message is string error)
|
||||
if (api.LastLoginError?.Message is string error)
|
||||
errorText.AddErrors(new[] { error });
|
||||
}
|
||||
|
||||
|
@ -135,7 +135,7 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
// TODO: Add realm queries to hint at which ruleset results are available in (and allow clicking to switch).
|
||||
// TODO: Make this message more certain by ensuring the osu! beatmaps exist before suggesting.
|
||||
if (filter?.Ruleset?.OnlineID > 0 && !filter.AllowConvertedBeatmaps)
|
||||
if (filter?.Ruleset?.OnlineID != 0 && filter?.AllowConvertedBeatmaps == false)
|
||||
{
|
||||
textFlow.AddParagraph("- Try");
|
||||
textFlow.AddLink(" enabling ", () => config.SetValue(OsuSetting.ShowConvertedBeatmaps, true));
|
||||
|
@ -18,7 +18,7 @@ namespace osu.Game.Tests
|
||||
}
|
||||
|
||||
public FlakyTestAttribute(int tryCount)
|
||||
: base(Environment.GetEnvironmentVariable("OSU_TESTS_FAIL_FLAKY") == "1" ? 0 : tryCount)
|
||||
: base(Environment.GetEnvironmentVariable("OSU_TESTS_FAIL_FLAKY") == "1" ? 1 : tryCount)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user