mirror of
https://github.com/ppy/osu.git
synced 2024-12-14 07:33:20 +08:00
Merge branch 'master' into editor-timeline-fix
This commit is contained in:
commit
8433813677
38
README.md
38
README.md
@ -16,21 +16,20 @@ The future of [osu!](https://osu.ppy.sh) and the beginning of an open era! Curre
|
||||
|
||||
## Status
|
||||
|
||||
This project is under heavy development, but is in a stable state. Users are encouraged to try it out and keep it installed alongside the stable *osu!* client. It will continue to evolve to the point of eventually replacing the existing stable client as an update.
|
||||
This project is under constant development, but we aim to keep things in a stable state. Users are encouraged to try it out and keep it installed alongside the stable *osu!* client. It will continue to evolve to the point of eventually replacing the existing stable client as an update.
|
||||
|
||||
**IMPORTANT:** Gameplay mechanics (and other features which you may have come to know and love) are in a constant state of flux. Game balance and final quality-of-life passes come at the end of development, preceded by experimentation and changes which may potentially **reduce playability or usability**. This is done in order to allow us to move forward as developers and designers more efficiently. If this offends you, please consider sticking to the stable releases of osu! (found on the website). We are not yet open to heated discussion over game mechanics and will not be using github as a forum for such discussions just yet.
|
||||
**IMPORTANT:** Gameplay mechanics (and other features which you may have come to know and love) are in a constant state of flux. Game balance and final quality-of-life passes come at the end of development, preceded by experimentation and changes which may potentially **reduce playability or usability**. This is done in order to allow us to move forward as developers and designers more efficiently. If this offends you, please consider sticking to a [stable release](https://osu.ppy.sh/home/download) of osu!. We are not yet open to heated discussion over game mechanics and will not be using github as a forum for such discussions just yet.
|
||||
|
||||
We are accepting bug reports (please report with as much detail as possible and follow the existing issue templates). Feature requests are also welcome, but understand that our focus is on completing the game to feature parity before adding new features. A few resources are available as starting points to getting involved and understanding the project:
|
||||
|
||||
- Detailed release changelogs are available on the [official osu! site](https://osu.ppy.sh/home/changelog/lazer).
|
||||
- You can learn more about our approach to [project management](https://github.com/ppy/osu/wiki/Project-management).
|
||||
- Read peppy's [blog post](https://blog.ppy.sh/a-definitive-lazer-faq/) exploring where the project is currently and the roadmap going forward.
|
||||
|
||||
## Running osu!
|
||||
|
||||
If you are looking to install or test osu! without setting up a development environment, you can consume our [binary releases](https://github.com/ppy/osu/releases). Handy links below will download the latest version for your operating system of choice:
|
||||
If you are looking to install or test osu! without setting up a development environment, you can consume our [releases](https://github.com/ppy/osu/releases). You can also generally download a version for your current device from the [osu! site](https://osu.ppy.sh/home/download). Failing that, you may use the links below to download the latest version for your operating system of choice:
|
||||
|
||||
**Latest build:**
|
||||
**Latest release:**
|
||||
|
||||
| [Windows 8.1+ (x64)](https://github.com/ppy/osu/releases/latest/download/install.exe) | macOS 10.15+ ([Intel](https://github.com/ppy/osu/releases/latest/download/osu.app.Intel.zip), [Apple Silicon](https://github.com/ppy/osu/releases/latest/download/osu.app.Apple.Silicon.zip)) | [Linux (x64)](https://github.com/ppy/osu/releases/latest/download/osu.AppImage) | [iOS 13.4+](https://osu.ppy.sh/home/testflight) | [Android 5+](https://github.com/ppy/osu/releases/latest/download/sh.ppy.osulazer.apk) |
|
||||
| ------------- | ------------- | ------------- | ------------- | ------------- |
|
||||
@ -50,9 +49,8 @@ You can see some examples of custom rulesets by visiting the [custom ruleset dir
|
||||
Please make sure you have the following prerequisites:
|
||||
|
||||
- A desktop platform with the [.NET 6.0 SDK](https://dotnet.microsoft.com/download) installed.
|
||||
- When developing with mobile, [Xamarin](https://docs.microsoft.com/en-us/xamarin/) is required, which is shipped together with Visual Studio or [Visual Studio for Mac](https://visualstudio.microsoft.com/vs/mac/).
|
||||
- When working with the codebase, we recommend using an IDE with intelligent code completion and syntax highlighting, such as the latest version of [Visual Studio](https://visualstudio.microsoft.com/vs/), [JetBrains Rider](https://www.jetbrains.com/rider/) or [Visual Studio Code](https://code.visualstudio.com/).
|
||||
- When running on Linux, please have a system-wide FFmpeg installation available to support video decoding.
|
||||
|
||||
When working with the codebase, we recommend using an IDE with intelligent code completion and syntax highlighting, such as the latest version of [Visual Studio](https://visualstudio.microsoft.com/vs/), [JetBrains Rider](https://www.jetbrains.com/rider/) or [Visual Studio Code](https://code.visualstudio.com/).
|
||||
|
||||
### Downloading the source code
|
||||
|
||||
@ -89,7 +87,29 @@ _Due to a historical feature gap between .NET Core and Xamarin, running `dotnet`
|
||||
|
||||
### Testing with resource/framework modifications
|
||||
|
||||
Sometimes it may be necessary to cross-test changes in [osu-resources](https://github.com/ppy/osu-resources) or [osu-framework](https://github.com/ppy/osu-framework). This can be achieved by running some commands as documented on the [osu-resources](https://github.com/ppy/osu-resources/wiki/Testing-local-resources-checkout-with-other-projects) and [osu-framework](https://github.com/ppy/osu-framework/wiki/Testing-local-framework-checkout-with-other-projects) wiki pages.
|
||||
Sometimes it may be necessary to cross-test changes in [osu-resources](https://github.com/ppy/osu-resources) or [osu-framework](https://github.com/ppy/osu-framework). This can be quickly achieved using included commands:
|
||||
|
||||
Windows:
|
||||
|
||||
```ps
|
||||
UseLocalFramework.ps1
|
||||
UseLocalResources.ps1
|
||||
```
|
||||
|
||||
macOS / Linux:
|
||||
|
||||
```ps
|
||||
UseLocalFramework.sh
|
||||
UseLocalResources.sh
|
||||
```
|
||||
|
||||
Note that these commands assume you have the relevant project(s) checked out in adjacent directories:
|
||||
|
||||
```
|
||||
|- osu // this repository
|
||||
|- osu-framework
|
||||
|- osu-resources
|
||||
```
|
||||
|
||||
### Code analysis
|
||||
|
||||
|
@ -11,7 +11,7 @@
|
||||
<AndroidManifestMerger>manifestmerger.jar</AndroidManifestMerger>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2023.521.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2023.531.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidManifestOverlay Include="$(MSBuildThisFileDirectory)osu.Android\Properties\AndroidManifestOverlay.xml" />
|
||||
|
@ -21,18 +21,29 @@ namespace osu.Game.Rulesets.Mania.Configuration
|
||||
{
|
||||
base.InitialiseDefaults();
|
||||
|
||||
SetDefault(ManiaRulesetSetting.ScrollTime, 1500.0, DrawableManiaRuleset.MIN_TIME_RANGE, DrawableManiaRuleset.MAX_TIME_RANGE, 5);
|
||||
SetDefault(ManiaRulesetSetting.ScrollSpeed, 8, 1, 40);
|
||||
SetDefault(ManiaRulesetSetting.ScrollDirection, ManiaScrollingDirection.Down);
|
||||
SetDefault(ManiaRulesetSetting.TimingBasedNoteColouring, false);
|
||||
|
||||
#pragma warning disable CS0618
|
||||
// Although obsolete, this is still required to populate the bindable from the database in case migration is required.
|
||||
SetDefault<double?>(ManiaRulesetSetting.ScrollTime, null);
|
||||
|
||||
if (Get<double?>(ManiaRulesetSetting.ScrollTime) is double scrollTime)
|
||||
{
|
||||
SetValue(ManiaRulesetSetting.ScrollSpeed, (int)Math.Round(DrawableManiaRuleset.MAX_TIME_RANGE / scrollTime));
|
||||
SetValue<double?>(ManiaRulesetSetting.ScrollTime, null);
|
||||
}
|
||||
#pragma warning restore CS0618
|
||||
}
|
||||
|
||||
public override TrackedSettings CreateTrackedSettings() => new TrackedSettings
|
||||
{
|
||||
new TrackedSetting<double>(ManiaRulesetSetting.ScrollTime,
|
||||
scrollTime => new SettingDescription(
|
||||
rawValue: scrollTime,
|
||||
new TrackedSetting<int>(ManiaRulesetSetting.ScrollSpeed,
|
||||
speed => new SettingDescription(
|
||||
rawValue: speed,
|
||||
name: RulesetSettingsStrings.ScrollSpeed,
|
||||
value: RulesetSettingsStrings.ScrollSpeedTooltip(scrollTime, (int)Math.Round(DrawableManiaRuleset.MAX_TIME_RANGE / scrollTime))
|
||||
value: RulesetSettingsStrings.ScrollSpeedTooltip(DrawableManiaRuleset.ComputeScrollTime(speed), speed)
|
||||
)
|
||||
)
|
||||
};
|
||||
@ -40,7 +51,9 @@ namespace osu.Game.Rulesets.Mania.Configuration
|
||||
|
||||
public enum ManiaRulesetSetting
|
||||
{
|
||||
[Obsolete("Use ScrollSpeed instead.")] // Can be removed 2023-11-30
|
||||
ScrollTime,
|
||||
ScrollSpeed,
|
||||
ScrollDirection,
|
||||
TimingBasedNoteColouring
|
||||
}
|
||||
|
@ -1,7 +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.
|
||||
|
||||
using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Localisation;
|
||||
@ -34,10 +33,10 @@ namespace osu.Game.Rulesets.Mania
|
||||
LabelText = RulesetSettingsStrings.ScrollingDirection,
|
||||
Current = config.GetBindable<ManiaScrollingDirection>(ManiaRulesetSetting.ScrollDirection)
|
||||
},
|
||||
new SettingsSlider<double, ManiaScrollSlider>
|
||||
new SettingsSlider<int, ManiaScrollSlider>
|
||||
{
|
||||
LabelText = RulesetSettingsStrings.ScrollSpeed,
|
||||
Current = config.GetBindable<double>(ManiaRulesetSetting.ScrollTime),
|
||||
Current = config.GetBindable<int>(ManiaRulesetSetting.ScrollSpeed),
|
||||
KeyboardStep = 5
|
||||
},
|
||||
new SettingsCheckbox
|
||||
@ -48,9 +47,9 @@ namespace osu.Game.Rulesets.Mania
|
||||
};
|
||||
}
|
||||
|
||||
private partial class ManiaScrollSlider : RoundedSliderBar<double>
|
||||
private partial class ManiaScrollSlider : RoundedSliderBar<int>
|
||||
{
|
||||
public override LocalisableString TooltipText => RulesetSettingsStrings.ScrollSpeedTooltip(Current.Value, (int)Math.Round(DrawableManiaRuleset.MAX_TIME_RANGE / Current.Value));
|
||||
public override LocalisableString TooltipText => RulesetSettingsStrings.ScrollSpeedTooltip(DrawableManiaRuleset.ComputeScrollTime(Current.Value), Current.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -33,12 +33,12 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
public partial class DrawableManiaRuleset : DrawableScrollingRuleset<ManiaHitObject>
|
||||
{
|
||||
/// <summary>
|
||||
/// The minimum time range. This occurs at a <see cref="relativeTimeRange"/> of 40.
|
||||
/// The minimum time range. This occurs at a <see cref="ManiaRulesetSetting.ScrollSpeed"/> of 40.
|
||||
/// </summary>
|
||||
public const double MIN_TIME_RANGE = 290;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum time range. This occurs at a <see cref="relativeTimeRange"/> of 1.
|
||||
/// The maximum time range. This occurs with a <see cref="ManiaRulesetSetting.ScrollSpeed"/> of 1.
|
||||
/// </summary>
|
||||
public const double MAX_TIME_RANGE = 11485;
|
||||
|
||||
@ -69,7 +69,8 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
protected override ScrollVisualisationMethod VisualisationMethod => scrollMethod;
|
||||
|
||||
private readonly Bindable<ManiaScrollingDirection> configDirection = new Bindable<ManiaScrollingDirection>();
|
||||
private readonly BindableDouble configTimeRange = new BindableDouble();
|
||||
private readonly BindableInt configScrollSpeed = new BindableInt();
|
||||
private double smoothTimeRange;
|
||||
|
||||
// Stores the current speed adjustment active in gameplay.
|
||||
private readonly Track speedAdjustmentTrack = new TrackVirtual(0);
|
||||
@ -78,6 +79,9 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
: base(ruleset, beatmap, mods)
|
||||
{
|
||||
BarLines = new BarLineGenerator<BarLine>(Beatmap).BarLines;
|
||||
|
||||
TimeRange.MinValue = 1;
|
||||
TimeRange.MaxValue = MAX_TIME_RANGE;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@ -104,30 +108,28 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
Config.BindWith(ManiaRulesetSetting.ScrollDirection, configDirection);
|
||||
configDirection.BindValueChanged(direction => Direction.Value = (ScrollingDirection)direction.NewValue, true);
|
||||
|
||||
Config.BindWith(ManiaRulesetSetting.ScrollTime, configTimeRange);
|
||||
TimeRange.MinValue = configTimeRange.MinValue;
|
||||
TimeRange.MaxValue = configTimeRange.MaxValue;
|
||||
Config.BindWith(ManiaRulesetSetting.ScrollSpeed, configScrollSpeed);
|
||||
configScrollSpeed.BindValueChanged(speed => this.TransformTo(nameof(smoothTimeRange), ComputeScrollTime(speed.NewValue), 200, Easing.OutQuint));
|
||||
|
||||
TimeRange.Value = smoothTimeRange = ComputeScrollTime(configScrollSpeed.Value);
|
||||
}
|
||||
|
||||
protected override void AdjustScrollSpeed(int amount)
|
||||
{
|
||||
this.TransformTo(nameof(relativeTimeRange), relativeTimeRange + amount, 200, Easing.OutQuint);
|
||||
}
|
||||
|
||||
private double relativeTimeRange
|
||||
{
|
||||
get => MAX_TIME_RANGE / configTimeRange.Value;
|
||||
set => configTimeRange.Value = MAX_TIME_RANGE / value;
|
||||
}
|
||||
protected override void AdjustScrollSpeed(int amount) => configScrollSpeed.Value += amount;
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
updateTimeRange();
|
||||
}
|
||||
|
||||
private void updateTimeRange() => TimeRange.Value = configTimeRange.Value * speedAdjustmentTrack.AggregateTempo.Value * speedAdjustmentTrack.AggregateFrequency.Value;
|
||||
private void updateTimeRange() => TimeRange.Value = smoothTimeRange * speedAdjustmentTrack.AggregateTempo.Value * speedAdjustmentTrack.AggregateFrequency.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Computes a scroll time (in milliseconds) from a scroll speed in the range of 1-40.
|
||||
/// </summary>
|
||||
/// <param name="scrollSpeed">The scroll speed.</param>
|
||||
/// <returns>The scroll time.</returns>
|
||||
public static double ComputeScrollTime(int scrollSpeed) => MAX_TIME_RANGE / scrollSpeed;
|
||||
|
||||
public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new ManiaPlayfieldAdjustmentContainer();
|
||||
|
||||
|
@ -17,6 +17,18 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods
|
||||
{
|
||||
public partial class TestSceneOsuModAutoplay : OsuModTestScene
|
||||
{
|
||||
[Test]
|
||||
public void TestCursorPositionStoredToJudgement()
|
||||
{
|
||||
CreateModTest(new ModTestData
|
||||
{
|
||||
Autoplay = true,
|
||||
PassCondition = () =>
|
||||
Player.ScoreProcessor.JudgedHits >= 1
|
||||
&& Player.ScoreProcessor.HitEvents.Any(e => e.Position != null)
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSpmUnaffectedByRateAdjust()
|
||||
=> runSpmTest(new OsuModDaycore
|
||||
|
@ -24,7 +24,17 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
public override ModType Type => ModType.Automation;
|
||||
public override LocalisableString Description => @"Automatic cursor movement - just follow the rhythm.";
|
||||
public override double ScoreMultiplier => 0.1;
|
||||
public override Type[] IncompatibleMods => new[] { typeof(OsuModSpunOut), typeof(ModRelax), typeof(ModFailCondition), typeof(ModNoFail), typeof(ModAutoplay), typeof(OsuModMagnetised), typeof(OsuModRepel) };
|
||||
|
||||
public override Type[] IncompatibleMods => new[]
|
||||
{
|
||||
typeof(OsuModSpunOut),
|
||||
typeof(ModRelax),
|
||||
typeof(ModFailCondition),
|
||||
typeof(ModNoFail),
|
||||
typeof(ModAutoplay),
|
||||
typeof(OsuModMagnetised),
|
||||
typeof(OsuModRepel)
|
||||
};
|
||||
|
||||
public bool PerformFail() => false;
|
||||
|
||||
@ -34,7 +44,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
|
||||
private List<OsuReplayFrame> replayFrames = null!;
|
||||
|
||||
private int currentFrame;
|
||||
private int currentFrame = -1;
|
||||
|
||||
public void Update(Playfield playfield)
|
||||
{
|
||||
@ -43,8 +53,9 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
double time = playfield.Clock.CurrentTime;
|
||||
|
||||
// Very naive implementation of autopilot based on proximity to replay frames.
|
||||
// Special case for the first frame is required to ensure the mouse is in a sane position until the actual time of the first frame is hit.
|
||||
// TODO: this needs to be based on user interactions to better match stable (pausing until judgement is registered).
|
||||
if (Math.Abs(replayFrames[currentFrame + 1].Time - time) <= Math.Abs(replayFrames[currentFrame].Time - time))
|
||||
if (currentFrame < 0 || Math.Abs(replayFrames[currentFrame + 1].Time - time) <= Math.Abs(replayFrames[currentFrame].Time - time))
|
||||
{
|
||||
currentFrame++;
|
||||
new MousePositionAbsoluteInput { Position = playfield.ToScreenSpace(replayFrames[currentFrame].Position) }.Apply(inputManager.CurrentState, inputManager);
|
||||
|
@ -2,6 +2,8 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Osu.Judgements;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Scoring
|
||||
@ -13,6 +15,9 @@ namespace osu.Game.Rulesets.Osu.Scoring
|
||||
{
|
||||
}
|
||||
|
||||
protected override HitEvent CreateHitEvent(JudgementResult result)
|
||||
=> base.CreateHitEvent(result).With((result as OsuHitCircleJudgementResult)?.CursorPositionAtHit);
|
||||
|
||||
protected override double ComputeTotalScore(double comboProgress, double accuracyProgress, double bonusPortion)
|
||||
{
|
||||
return 700000 * comboProgress
|
||||
|
@ -179,43 +179,9 @@ namespace osu.Game.Database
|
||||
applyFilenameSchemaSuffix(ref Filename);
|
||||
#endif
|
||||
|
||||
string newerVersionFilename = $"{Filename.Replace(realm_extension, string.Empty)}_newer_version{realm_extension}";
|
||||
|
||||
// Attempt to recover a newer database version if available.
|
||||
if (storage.Exists(newerVersionFilename))
|
||||
{
|
||||
Logger.Log(@"A newer realm database has been found, attempting recovery...", LoggingTarget.Database);
|
||||
attemptRecoverFromFile(newerVersionFilename);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// This method triggers the first `getRealmInstance` call, which will implicitly run realm migrations and bring the schema up-to-date.
|
||||
cleanupPendingDeletions();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// See https://github.com/realm/realm-core/blob/master/src%2Frealm%2Fobject-store%2Fobject_store.cpp#L1016-L1022
|
||||
// This is the best way we can detect a schema version downgrade.
|
||||
if (e.Message.StartsWith(@"Provided schema version", StringComparison.Ordinal))
|
||||
{
|
||||
Logger.Error(e, "Your local database is too new to work with this version of osu!. Please close osu! and install the latest release to recover your data.");
|
||||
|
||||
// If a newer version database already exists, don't backup again. We can presume that the first backup is the one we care about.
|
||||
if (!storage.Exists(newerVersionFilename))
|
||||
createBackup(newerVersionFilename);
|
||||
|
||||
storage.Delete(Filename);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Error(e, "Realm startup failed with unrecoverable error; starting with a fresh database. A backup of your database has been made.");
|
||||
createBackup($"{Filename.Replace(realm_extension, string.Empty)}_{DateTimeOffset.UtcNow.ToUnixTimeSeconds()}_corrupt{realm_extension}");
|
||||
storage.Delete(Filename);
|
||||
}
|
||||
|
||||
cleanupPendingDeletions();
|
||||
}
|
||||
// `prepareFirstRealmAccess()` triggers the first `getRealmInstance` call, which will implicitly run realm migrations and bring the schema up-to-date.
|
||||
using (var realm = prepareFirstRealmAccess())
|
||||
cleanupPendingDeletions(realm);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -312,49 +278,93 @@ namespace osu.Game.Database
|
||||
Logger.Log(@"Recovery complete!", LoggingTarget.Database);
|
||||
}
|
||||
|
||||
private void cleanupPendingDeletions()
|
||||
private Realm prepareFirstRealmAccess()
|
||||
{
|
||||
using (var realm = getRealmInstance())
|
||||
using (var transaction = realm.BeginWrite())
|
||||
string newerVersionFilename = $"{Filename.Replace(realm_extension, string.Empty)}_newer_version{realm_extension}";
|
||||
|
||||
// Attempt to recover a newer database version if available.
|
||||
if (storage.Exists(newerVersionFilename))
|
||||
{
|
||||
var pendingDeleteScores = realm.All<ScoreInfo>().Where(s => s.DeletePending);
|
||||
|
||||
foreach (var score in pendingDeleteScores)
|
||||
realm.Remove(score);
|
||||
|
||||
var pendingDeleteSets = realm.All<BeatmapSetInfo>().Where(s => s.DeletePending);
|
||||
|
||||
foreach (var beatmapSet in pendingDeleteSets)
|
||||
{
|
||||
foreach (var beatmap in beatmapSet.Beatmaps)
|
||||
{
|
||||
// Cascade delete related scores, else they will have a null beatmap against the model's spec.
|
||||
foreach (var score in beatmap.Scores)
|
||||
realm.Remove(score);
|
||||
|
||||
realm.Remove(beatmap.Metadata);
|
||||
realm.Remove(beatmap);
|
||||
}
|
||||
|
||||
realm.Remove(beatmapSet);
|
||||
}
|
||||
|
||||
var pendingDeleteSkins = realm.All<SkinInfo>().Where(s => s.DeletePending);
|
||||
|
||||
foreach (var s in pendingDeleteSkins)
|
||||
realm.Remove(s);
|
||||
|
||||
var pendingDeletePresets = realm.All<ModPreset>().Where(s => s.DeletePending);
|
||||
|
||||
foreach (var s in pendingDeletePresets)
|
||||
realm.Remove(s);
|
||||
|
||||
transaction.Commit();
|
||||
Logger.Log(@"A newer realm database has been found, attempting recovery...", LoggingTarget.Database);
|
||||
attemptRecoverFromFile(newerVersionFilename);
|
||||
}
|
||||
|
||||
// clean up files after dropping any pending deletions.
|
||||
// in the future we may want to only do this when the game is idle, rather than on every startup.
|
||||
new RealmFileStore(this, storage).Cleanup();
|
||||
try
|
||||
{
|
||||
return getRealmInstance();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// See https://github.com/realm/realm-core/blob/master/src%2Frealm%2Fobject-store%2Fobject_store.cpp#L1016-L1022
|
||||
// This is the best way we can detect a schema version downgrade.
|
||||
if (e.Message.StartsWith(@"Provided schema version", StringComparison.Ordinal))
|
||||
{
|
||||
Logger.Error(e, "Your local database is too new to work with this version of osu!. Please close osu! and install the latest release to recover your data.");
|
||||
|
||||
// If a newer version database already exists, don't backup again. We can presume that the first backup is the one we care about.
|
||||
if (!storage.Exists(newerVersionFilename))
|
||||
createBackup(newerVersionFilename);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Error(e, "Realm startup failed with unrecoverable error; starting with a fresh database. A backup of your database has been made.");
|
||||
createBackup($"{Filename.Replace(realm_extension, string.Empty)}_{DateTimeOffset.UtcNow.ToUnixTimeSeconds()}_corrupt{realm_extension}");
|
||||
}
|
||||
|
||||
storage.Delete(Filename);
|
||||
return getRealmInstance();
|
||||
}
|
||||
}
|
||||
|
||||
private void cleanupPendingDeletions(Realm realm)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var transaction = realm.BeginWrite())
|
||||
{
|
||||
var pendingDeleteScores = realm.All<ScoreInfo>().Where(s => s.DeletePending);
|
||||
|
||||
foreach (var score in pendingDeleteScores)
|
||||
realm.Remove(score);
|
||||
|
||||
var pendingDeleteSets = realm.All<BeatmapSetInfo>().Where(s => s.DeletePending);
|
||||
|
||||
foreach (var beatmapSet in pendingDeleteSets)
|
||||
{
|
||||
foreach (var beatmap in beatmapSet.Beatmaps)
|
||||
{
|
||||
// Cascade delete related scores, else they will have a null beatmap against the model's spec.
|
||||
foreach (var score in beatmap.Scores)
|
||||
realm.Remove(score);
|
||||
|
||||
realm.Remove(beatmap.Metadata);
|
||||
realm.Remove(beatmap);
|
||||
}
|
||||
|
||||
realm.Remove(beatmapSet);
|
||||
}
|
||||
|
||||
var pendingDeleteSkins = realm.All<SkinInfo>().Where(s => s.DeletePending);
|
||||
|
||||
foreach (var s in pendingDeleteSkins)
|
||||
realm.Remove(s);
|
||||
|
||||
var pendingDeletePresets = realm.All<ModPreset>().Where(s => s.DeletePending);
|
||||
|
||||
foreach (var s in pendingDeletePresets)
|
||||
realm.Remove(s);
|
||||
|
||||
transaction.Commit();
|
||||
}
|
||||
|
||||
// clean up files after dropping any pending deletions.
|
||||
// in the future we may want to only do this when the game is idle, rather than on every startup.
|
||||
new RealmFileStore(this, storage).Cleanup();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error(e, "Failed to clean up unused files. This is not critical but please report if it happens regularly.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -909,7 +919,7 @@ namespace osu.Game.Database
|
||||
|
||||
int attempts = 10;
|
||||
|
||||
while (attempts-- > 0)
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -927,6 +937,9 @@ namespace osu.Game.Database
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
if (attempts-- <= 0)
|
||||
throw;
|
||||
|
||||
// file may be locked during use.
|
||||
Thread.Sleep(500);
|
||||
}
|
||||
|
@ -152,7 +152,7 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load(OverlayColourProvider? colourProvider, OsuColour osuColour)
|
||||
{
|
||||
background.Colour = colourProvider?.Background5 ?? Color4Extensions.FromHex(@"1c2125");
|
||||
background.Colour = colourProvider?.Background4 ?? Color4Extensions.FromHex(@"1c2125");
|
||||
descriptionText.Colour = osuColour.Yellow;
|
||||
}
|
||||
|
||||
|
@ -82,7 +82,7 @@ namespace osu.Game.Localisation
|
||||
/// <summary>
|
||||
/// "{0}ms (speed {1})"
|
||||
/// </summary>
|
||||
public static LocalisableString ScrollSpeedTooltip(double arg0, int arg1) => new TranslatableString(getKey(@"ruleset"), @"{0}ms (speed {1})", arg0, arg1);
|
||||
public static LocalisableString ScrollSpeedTooltip(double scrollTime, int scrollSpeed) => new TranslatableString(getKey(@"ruleset"), @"{0:0}ms (speed {1})", scrollTime, scrollSpeed);
|
||||
|
||||
private static string getKey(string key) => $@"{prefix}:{key}";
|
||||
}
|
||||
|
@ -132,7 +132,12 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
this.ScaleTo(1, 250, Easing.OutSine);
|
||||
|
||||
Debug.Assert(screenStack.CurrentScreen != null);
|
||||
screenStack.CurrentScreen.OnResuming(e);
|
||||
|
||||
// if a subscreen was pushed to the nested stack while the stack was not present, this path will proxy `OnResuming()`
|
||||
// to the subscreen before `OnEntering()` can even be called for the subscreen, breaking ordering expectations.
|
||||
// to work around this, do not proxy resume to screens that haven't loaded yet.
|
||||
if ((screenStack.CurrentScreen as Drawable)?.IsLoaded == true)
|
||||
screenStack.CurrentScreen.OnResuming(e);
|
||||
|
||||
base.OnResuming(e);
|
||||
}
|
||||
@ -143,7 +148,12 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
this.FadeOut(250);
|
||||
|
||||
Debug.Assert(screenStack.CurrentScreen != null);
|
||||
screenStack.CurrentScreen.OnSuspending(e);
|
||||
|
||||
// if a subscreen was pushed to the nested stack while the stack was not present, this path will proxy `OnSuspending()`
|
||||
// to the subscreen before `OnEntering()` can even be called for the subscreen, breaking ordering expectations.
|
||||
// to work around this, do not proxy suspend to screens that haven't loaded yet.
|
||||
if ((screenStack.CurrentScreen as Drawable)?.IsLoaded == true)
|
||||
screenStack.CurrentScreen.OnSuspending(e);
|
||||
}
|
||||
|
||||
public override bool OnExiting(ScreenExitEvent e)
|
||||
|
@ -36,7 +36,7 @@
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Realm" Version="10.20.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2023.521.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2023.531.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2023.510.0" />
|
||||
<PackageReference Include="Sentry" Version="3.28.1" />
|
||||
<PackageReference Include="SharpCompress" Version="0.32.2" />
|
||||
|
@ -16,6 +16,6 @@
|
||||
<RuntimeIdentifier>iossimulator-x64</RuntimeIdentifier>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2023.521.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2023.531.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
Loading…
Reference in New Issue
Block a user