6 Commits

21 changed files with 310 additions and 91 deletions

1
.gitignore vendored
View File

@@ -19,6 +19,7 @@ bld/
[Bb]in/ [Bb]in/
[Oo]bj/ [Oo]bj/
[Ll]og/ [Ll]og/
[Pp]ub/
# Visual Studio 2015 cache/options directory # Visual Studio 2015 cache/options directory
.vs/ .vs/

36
.vscode/launch.json vendored
View File

@@ -7,9 +7,9 @@
"request": "launch", "request": "launch",
"program": "dotnet", "program": "dotnet",
"args": [ "args": [
"${workspaceRoot}/osu.Desktop/bin/Debug/net8.0/osu!.dll" "${workspaceFolder}/osu.Desktop/bin/Debug/net8.0/osu!.dll"
], ],
"cwd": "${workspaceRoot}", "cwd": "${workspaceFolder}",
"preLaunchTask": "Build osu! (Debug)", "preLaunchTask": "Build osu! (Debug)",
"console": "internalConsole" "console": "internalConsole"
}, },
@@ -19,9 +19,9 @@
"request": "launch", "request": "launch",
"program": "dotnet", "program": "dotnet",
"args": [ "args": [
"${workspaceRoot}/osu.Desktop/bin/Release/net8.0/osu!.dll" "${workspaceFolder}/osu.Desktop/bin/Release/net8.0/osu!.dll"
], ],
"cwd": "${workspaceRoot}", "cwd": "${workspaceFolder}",
"preLaunchTask": "Build osu! (Release)", "preLaunchTask": "Build osu! (Release)",
"console": "internalConsole" "console": "internalConsole"
}, },
@@ -31,9 +31,9 @@
"request": "launch", "request": "launch",
"program": "dotnet", "program": "dotnet",
"args": [ "args": [
"${workspaceRoot}/osu.Game.Tests/bin/Debug/net8.0/osu.Game.Tests.dll" "${workspaceFolder}/osu.Game.Tests/bin/Debug/net8.0/osu.Game.Tests.dll"
], ],
"cwd": "${workspaceRoot}", "cwd": "${workspaceFolder}",
"preLaunchTask": "Build tests (Debug)", "preLaunchTask": "Build tests (Debug)",
"console": "internalConsole" "console": "internalConsole"
}, },
@@ -43,9 +43,9 @@
"request": "launch", "request": "launch",
"program": "dotnet", "program": "dotnet",
"args": [ "args": [
"${workspaceRoot}/osu.Game.Tests/bin/Release/net8.0/osu.Game.Tests.dll" "${workspaceFolder}/osu.Game.Tests/bin/Release/net8.0/osu.Game.Tests.dll"
], ],
"cwd": "${workspaceRoot}", "cwd": "${workspaceFolder}",
"preLaunchTask": "Build tests (Release)", "preLaunchTask": "Build tests (Release)",
"console": "internalConsole" "console": "internalConsole"
}, },
@@ -55,10 +55,10 @@
"request": "launch", "request": "launch",
"program": "dotnet", "program": "dotnet",
"args": [ "args": [
"${workspaceRoot}/osu.Desktop/bin/Debug/net8.0/osu!.dll", "${workspaceFolder}/osu.Desktop/bin/Debug/net8.0/osu!.dll",
"--tournament" "--tournament"
], ],
"cwd": "${workspaceRoot}", "cwd": "${workspaceFolder}",
"preLaunchTask": "Build osu! (Debug)", "preLaunchTask": "Build osu! (Debug)",
"console": "internalConsole" "console": "internalConsole"
}, },
@@ -68,10 +68,10 @@
"request": "launch", "request": "launch",
"program": "dotnet", "program": "dotnet",
"args": [ "args": [
"${workspaceRoot}/osu.Desktop/bin/Release/net8.0/osu!.dll", "${workspaceFolder}/osu.Desktop/bin/Release/net8.0/osu!.dll",
"--tournament" "--tournament"
], ],
"cwd": "${workspaceRoot}", "cwd": "${workspaceFolder}",
"preLaunchTask": "Build osu! (Release)", "preLaunchTask": "Build osu! (Release)",
"console": "internalConsole" "console": "internalConsole"
}, },
@@ -81,10 +81,10 @@
"request": "launch", "request": "launch",
"program": "dotnet", "program": "dotnet",
"args": [ "args": [
"${workspaceRoot}/osu.Game.Tournament.Tests/bin/Debug/net8.0/osu.Game.Tournament.Tests.dll", "${workspaceFolder}/osu.Game.Tournament.Tests/bin/Debug/net8.0/osu.Game.Tournament.Tests.dll",
"--tournament" "--tournament"
], ],
"cwd": "${workspaceRoot}", "cwd": "${workspaceFolder}",
"preLaunchTask": "Build tournament tests (Debug)", "preLaunchTask": "Build tournament tests (Debug)",
"console": "internalConsole" "console": "internalConsole"
}, },
@@ -94,10 +94,10 @@
"request": "launch", "request": "launch",
"program": "dotnet", "program": "dotnet",
"args": [ "args": [
"${workspaceRoot}/osu.Game.Tournament.Tests/bin/Debug/net8.0/osu.Game.Tournament.Tests.dll", "${workspaceFolder}/osu.Game.Tournament.Tests/bin/Debug/net8.0/osu.Game.Tournament.Tests.dll",
"--tournament" "--tournament"
], ],
"cwd": "${workspaceRoot}", "cwd": "${workspaceFolder}",
"preLaunchTask": "Build tournament tests (Release)", "preLaunchTask": "Build tournament tests (Release)",
"console": "internalConsole" "console": "internalConsole"
}, },
@@ -105,12 +105,12 @@
"name": "Benchmark", "name": "Benchmark",
"type": "coreclr", "type": "coreclr",
"request": "launch", "request": "launch",
"program": "${workspaceRoot}/osu.Game.Benchmarks/bin/Release/net8.0/osu.Game.Benchmarks.dll", "program": "${workspaceFolder}/osu.Game.Benchmarks/bin/Release/net8.0/osu.Game.Benchmarks.dll",
"args": [ "args": [
"--filter", "--filter",
"*" "*"
], ],
"cwd": "${workspaceRoot}", "cwd": "${workspaceFolder}",
"preLaunchTask": "Build benchmarks", "preLaunchTask": "Build benchmarks",
"console": "internalConsole" "console": "internalConsole"
} }

19
MakeInstaller.ps1 Normal file
View File

@@ -0,0 +1,19 @@
#!/usr/bin/env powershell
param (
[string]$Version,
[string]$BuildConfig = "Release"
)
if ($Version -eq "") {
Write-Host "Usage: .\MakeInstaller.ps1 <VERSION_NUMBER> [-BuildConfig <BUILD_CONFIG>]"
Write-Host "Example: .\MakeInstaller.ps1 2025.823.0 -BuildConfig Debug"
exit
}
$tmpPub = ".\pub"
if (-not (Test-Path -Path $tmpPub)) {
New-Item -ItemType Directory -path $tmpPub
}
dotnet publish -c $BuildConfig osu.Desktop --self-contained -r win-x64 -o $tmpPub -verbosity:m /p:Version=$Version
vpk pack --packId jvnkosu.Client --packTitle "jvnkosu!lazer" --packVersion $Version --packDir ./pub --mainExe="osu!.exe"

View File

@@ -115,10 +115,12 @@ namespace osu.Desktop
if (IsFirstRun) if (IsFirstRun)
LocalConfig.SetValue(OsuSetting.ReleaseStream, ReleaseStream.Lazer); LocalConfig.SetValue(OsuSetting.ReleaseStream, ReleaseStream.Lazer);
if (IsPackageManaged) // if (IsPackageManaged)
return new NoActionUpdateManager(); // return new NoActionUpdateManager();
return new VelopackUpdateManager(); // return new VelopackUpdateManager();
return new NoActionUpdateManager(); // for now, APIs are useless for actually downloading the releases. TODO: adapt UpdateManager for gitea
} }
public override bool RestartAppWhenExited() public override bool RestartAppWhenExited()

View File

@@ -92,7 +92,7 @@ namespace osu.Game.Configuration
SetDefault(OsuSetting.ExternalLinkWarning, true); SetDefault(OsuSetting.ExternalLinkWarning, true);
SetDefault(OsuSetting.PreferNoVideo, false); SetDefault(OsuSetting.PreferNoVideo, false);
SetDefault(OsuSetting.BackgroundCategory, "Default");
SetDefault(OsuSetting.ShowOnlineExplicitContent, false); SetDefault(OsuSetting.ShowOnlineExplicitContent, false);
SetDefault(OsuSetting.NotifyOnUsernameMentioned, true); SetDefault(OsuSetting.NotifyOnUsernameMentioned, true);
@@ -193,7 +193,7 @@ namespace osu.Game.Configuration
SetDefault(OsuSetting.IntroSequence, IntroSequence.Triangles); SetDefault(OsuSetting.IntroSequence, IntroSequence.Triangles);
SetDefault(OsuSetting.MenuBackgroundSource, BackgroundSource.Skin); SetDefault(OsuSetting.MenuBackgroundSource, BackgroundSource.Skin);
SetDefault(OsuSetting.SeasonalBackgroundMode, SeasonalBackgroundMode.Sometimes); SetDefault(OsuSetting.SeasonalBackgroundMode, SeasonalBackgroundMode.Never);
SetDefault(OsuSetting.DiscordRichPresence, DiscordRichPresenceMode.Full); SetDefault(OsuSetting.DiscordRichPresence, DiscordRichPresenceMode.Full);
@@ -436,6 +436,7 @@ namespace osu.Game.Configuration
MenuBackgroundSource, MenuBackgroundSource,
GameplayDisableWinKey, GameplayDisableWinKey,
SeasonalBackgroundMode, SeasonalBackgroundMode,
BackgroundCategory,
EditorWaveformOpacity, EditorWaveformOpacity,
EditorShowHitMarkers, EditorShowHitMarkers,
EditorAutoSeekOnPlacement, EditorAutoSeekOnPlacement,

View File

@@ -14,12 +14,6 @@ namespace osu.Game.Configuration
[LocalisableDescription(typeof(UserInterfaceStrings), nameof(UserInterfaceStrings.AlwaysSeasonalBackground))] [LocalisableDescription(typeof(UserInterfaceStrings), nameof(UserInterfaceStrings.AlwaysSeasonalBackground))]
Always, Always,
/// <summary>
/// Seasonal backgrounds are shown only during their corresponding season.
/// </summary>
[LocalisableDescription(typeof(UserInterfaceStrings), nameof(UserInterfaceStrings.SometimesSeasonalBackground))]
Sometimes,
/// <summary> /// <summary>
/// Seasonal backgrounds are never shown. /// Seasonal backgrounds are never shown.
/// </summary> /// </summary>

View File

@@ -4,6 +4,7 @@
#nullable disable #nullable disable
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
@@ -19,79 +20,120 @@ namespace osu.Game.Graphics.Backgrounds
{ {
public partial class SeasonalBackgroundLoader : Component public partial class SeasonalBackgroundLoader : Component
{ {
public event Action<Exception> OnLoadFailure;
public event Action BackgroundChanged;
/// <summary> /// <summary>
/// Fired when background should be changed due to receiving backgrounds from API /// Fired when categories have been successfully refreshed from the server.
/// or when the user setting is changed (as it might require unloading the seasonal background).
/// </summary> /// </summary>
public event Action SeasonalBackgroundChanged; public event Action OnCategoriesRefreshed;
public readonly Bindable<IEnumerable<string>> AvailableCategories = new Bindable<IEnumerable<string>>(new List<string>());
[Resolved] [Resolved]
private IAPIProvider api { get; set; } private IAPIProvider api { get; set; }
private Bindable<SeasonalBackgroundMode> seasonalBackgroundMode; private Bindable<SeasonalBackgroundMode> backgroundMode;
private Bindable<APISeasonalBackgrounds> seasonalBackgrounds; private Bindable<string> selectedCategory;
private Bindable<APISeasonalBackgrounds> currentBackgrounds;
private int current; private int currentBackgroundIndex;
private bool shouldShowCustomBackgrounds => backgroundMode.Value != SeasonalBackgroundMode.Never;
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuConfigManager config, SessionStatics sessionStatics) private void load(OsuConfigManager config, SessionStatics sessionStatics)
{ {
seasonalBackgroundMode = config.GetBindable<SeasonalBackgroundMode>(OsuSetting.SeasonalBackgroundMode); backgroundMode = config.GetBindable<SeasonalBackgroundMode>(OsuSetting.SeasonalBackgroundMode);
seasonalBackgroundMode.BindValueChanged(_ => SeasonalBackgroundChanged?.Invoke()); backgroundMode.BindValueChanged(_ => BackgroundChanged?.Invoke());
seasonalBackgrounds = sessionStatics.GetBindable<APISeasonalBackgrounds>(Static.SeasonalBackgrounds); selectedCategory = config.GetBindable<string>(OsuSetting.BackgroundCategory);
seasonalBackgrounds.BindValueChanged(_ => selectedCategory.BindValueChanged(_ => fetchBackgroundsForSelectedCategory());
{
if (shouldShowSeasonal)
SeasonalBackgroundChanged?.Invoke();
});
fetchSeasonalBackgrounds(); currentBackgrounds = sessionStatics.GetBindable<APISeasonalBackgrounds>(Static.SeasonalBackgrounds);
if (shouldShowCustomBackgrounds)
fetchCategories();
} }
private void fetchSeasonalBackgrounds() /// <summary>
/// Public method to trigger a refresh of categories from the UI.
/// </summary>
public void RefreshCategories()
{ {
if (seasonalBackgrounds.Value != null) fetchCategories();
return; }
private void fetchCategories()
{
if (!shouldShowCustomBackgrounds) return;
var request = new GetBackgroundCategoriesRequest();
var request = new GetSeasonalBackgroundsRequest();
request.Success += response => request.Success += response =>
{ {
seasonalBackgrounds.Value = response; var serverCategories = response.Categories ?? Enumerable.Empty<string>();
current = RNG.Next(0, response.Backgrounds?.Count ?? 0);
AvailableCategories.Value = new[] { "Default" }.Concat(serverCategories)
.Distinct(StringComparer.OrdinalIgnoreCase).ToList();
if (!AvailableCategories.Value.Contains(selectedCategory.Value))
selectedCategory.Value = "Default";
else
fetchBackgroundsForSelectedCategory();
OnCategoriesRefreshed?.Invoke();
};
request.Failure += exception =>
{
AvailableCategories.Value = new[] { "Íå óäàëîñü çàãðóçèòü..." };
OnLoadFailure?.Invoke(exception);
}; };
api.PerformAsync(request); api.PerformAsync(request);
} }
public SeasonalBackground LoadNextBackground() private void fetchBackgroundsForSelectedCategory()
{ {
if (!shouldShowSeasonal) if (!shouldShowCustomBackgrounds) return;
if (AvailableCategories.Value.Count() == 1 && AvailableCategories.Value.First().Contains("Íå óäàëîñü"))
{
currentBackgrounds.Value = new APISeasonalBackgrounds { Backgrounds = new List<APISeasonalBackground>() };
BackgroundChanged?.Invoke();
return;
}
string categoryToFetch = selectedCategory.Value == "Default" ? null : selectedCategory.Value;
var request = new GetSeasonalBackgroundsRequest(categoryToFetch);
request.Success += response =>
{
currentBackgrounds.Value = response;
currentBackgroundIndex = RNG.Next(0, response.Backgrounds?.Count ?? 0);
BackgroundChanged?.Invoke();
};
request.Failure += exception =>
{
OnLoadFailure?.Invoke(exception);
};
api.PerformAsync(request);
}
public Background LoadNextBackground()
{
if (!shouldShowCustomBackgrounds || currentBackgrounds.Value?.Backgrounds?.Any() != true)
return null; return null;
var backgrounds = seasonalBackgrounds.Value.Backgrounds; var backgrounds = currentBackgrounds.Value.Backgrounds;
currentBackgroundIndex = (currentBackgroundIndex + 1) % backgrounds.Count;
current = (current + 1) % backgrounds.Count; string url = backgrounds[currentBackgroundIndex].Url;
string url = backgrounds[current].Url;
return new SeasonalBackground(url); return new SeasonalBackground(url);
} }
private bool shouldShowSeasonal
{
get
{
if (seasonalBackgroundMode.Value == SeasonalBackgroundMode.Never)
return false;
if (seasonalBackgroundMode.Value == SeasonalBackgroundMode.Sometimes && !isInSeason)
return false;
return seasonalBackgrounds.Value?.Backgrounds?.Any() == true;
}
}
private bool isInSeason => seasonalBackgrounds.Value != null && DateTimeOffset.Now < seasonalBackgrounds.Value.EndDate;
} }
[LongRunningLoad] [LongRunningLoad]

View File

@@ -78,6 +78,20 @@ Your experience will not be perfect, and may even feel subpar compared to games
Please bear with us as we continue to improve the game for you!"); Please bear with us as we continue to improve the game for you!");
/// <summary>
/// "Welcome to jvnkosu!lazer!"
/// </summary>
public static LocalisableString GreetingNotification => new TranslatableString(getKey(@"greeting_notification"), @"Welcome to jvnkosu!lazer!");
/// <summary>
/// "Failed to load backgrounds!\nCheck your internet connection"
/// </summary>
public static LocalisableString SeasonalBackgroundsFail => new TranslatableString(getKey(@"seasonal_backgrounds_fail"), @"Failed to load backgrounds!\nCheck your internet connection"); // TODO: implement l10n in osu-resources
/// <summary>
/// "Successfully refreshed background categories!"
/// </summary>
public static LocalisableString SeasonalBackgroundsRefreshed => new TranslatableString(getKey(@"seasonal_backgrounds_refreshed"), @"Successfully refreshed background categories!");
private static string getKey(string key) => $@"{prefix}:{key}"; private static string getKey(string key) => $@"{prefix}:{key}";
} }
} }

View File

@@ -64,11 +64,26 @@ namespace osu.Game.Localisation
/// </summary> /// </summary>
public static LocalisableString BackgroundSource => new TranslatableString(getKey(@"background_source"), @"Background source"); public static LocalisableString BackgroundSource => new TranslatableString(getKey(@"background_source"), @"Background source");
/// <summary>
/// "Use custom backgrounds from server"
/// </summary>
public static LocalisableString UseSeasonalBackgrounds => new TranslatableString(getKey(@"use_seasonal_backgrounds"), @"Use custom backgrounds from server");
/// <summary> /// <summary>
/// "Seasonal backgrounds" /// "Seasonal backgrounds"
/// </summary> /// </summary>
public static LocalisableString SeasonalBackgrounds => new TranslatableString(getKey(@"seasonal_backgrounds"), @"Seasonal backgrounds"); public static LocalisableString SeasonalBackgrounds => new TranslatableString(getKey(@"seasonal_backgrounds"), @"Seasonal backgrounds");
/// <summary>
/// "Background categories"
/// </summary>
public static LocalisableString SeasonalBackgroundsCategories => new TranslatableString(getKey(@"seasonal_backgrounds_categories"), @"Background categories");
/// <summary>
/// "Refresh categories"
/// </summary>
public static LocalisableString SeasonalBackgroundsRefresh => new TranslatableString(getKey(@"seasonal_backgrounds_refresh"), @"Refresh categories");
/// <summary> /// <summary>
/// "Changes to this setting will only apply with an active osu!supporter tag." /// "Changes to this setting will only apply with an active osu!supporter tag."
/// </summary> /// </summary>

View File

@@ -0,0 +1,12 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Online.API.Requests.Responses;
namespace osu.Game.Online.API.Requests
{
public class GetBackgroundCategoriesRequest : APIRequest<APIBackgroundCategories>
{
protected override string Target => @"seasonal-backgrounds-categories";
}
}

View File

@@ -8,7 +8,7 @@ namespace osu.Game.Online.API.Requests
public class GetMenuContentRequest : OsuJsonWebRequest<APIMenuContent> public class GetMenuContentRequest : OsuJsonWebRequest<APIMenuContent>
{ {
public GetMenuContentRequest() public GetMenuContentRequest()
: base(@"https://assets.ppy.sh/menu-content.json") : base(@"https://osu.jvnko.boats/uploads/menu-content.json") // TODO: backend
{ {
} }
} }

View File

@@ -1,12 +1,33 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
#nullable enable
using System.Net;
using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.API.Requests.Responses;
namespace osu.Game.Online.API.Requests namespace osu.Game.Online.API.Requests
{ {
public class GetSeasonalBackgroundsRequest : APIRequest<APISeasonalBackgrounds> public class GetSeasonalBackgroundsRequest : APIRequest<APISeasonalBackgrounds>
{ {
protected override string Target => @"seasonal-backgrounds"; private readonly string? category;
public GetSeasonalBackgroundsRequest()
{
}
public GetSeasonalBackgroundsRequest(string? category)
{
this.category = category;
}
protected override string Target => getPath();
private string getPath()
{
if (string.IsNullOrEmpty(category))
return @"seasonal-backgrounds";
return $@"seasonal-backgrounds?category={WebUtility.UrlEncode(category)}";
}
} }
} }

View File

@@ -0,0 +1,14 @@
// 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.Collections.Generic;
using Newtonsoft.Json;
namespace osu.Game.Online.API.Requests.Responses
{
public class APIBackgroundCategories
{
[JsonProperty("categories")]
public List<string>? Categories { get; set; }
}
}

View File

@@ -11,11 +11,11 @@ namespace osu.Game.Online
{ {
protected override string GetLookupUrl(string url) protected override string GetLookupUrl(string url)
{ {
/* if (!Uri.TryCreate(url, UriKind.Absolute, out Uri? uri) || !uri.Host.EndsWith(@".ppy.sh", StringComparison.OrdinalIgnoreCase)) if (!Uri.TryCreate(url, UriKind.Absolute, out Uri? uri) || !uri.Host.EndsWith(@".jvnko.boats", StringComparison.OrdinalIgnoreCase))
{ {
Logger.Log($@"Blocking resource lookup from external website: {url}", LoggingTarget.Network, LogLevel.Important); Logger.Log($@"Blocking resource lookup from external website: {url}", LoggingTarget.Network, LogLevel.Important);
return string.Empty; return string.Empty;
}*/ }
return url; return url;
} }

View File

@@ -38,6 +38,7 @@ using osu.Game.Configuration;
using osu.Game.Database; using osu.Game.Database;
using osu.Game.Extensions; using osu.Game.Extensions;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Input; using osu.Game.Input;
@@ -169,6 +170,9 @@ namespace osu.Game
[Cached] [Cached]
private readonly ScreenshotManager screenshotManager = new ScreenshotManager(); private readonly ScreenshotManager screenshotManager = new ScreenshotManager();
[Cached]
private readonly SeasonalBackgroundLoader backgroundLoader;
protected SentryLogger SentryLogger; protected SentryLogger SentryLogger;
public virtual StableStorage GetStorageForStableInstall() => null; public virtual StableStorage GetStorageForStableInstall() => null;
@@ -249,6 +253,10 @@ namespace osu.Game
public OsuGame(string[] args = null) public OsuGame(string[] args = null)
{ {
backgroundLoader = new SeasonalBackgroundLoader();
backgroundLoader.OnLoadFailure += handleBackgroundLoadFailure;
backgroundLoader.OnCategoriesRefreshed += handleCategoriesRefreshed;
this.args = args; this.args = args;
Logger.NewEntry += forwardGeneralLogToNotifications; Logger.NewEntry += forwardGeneralLogToNotifications;
@@ -401,6 +409,18 @@ namespace osu.Game
} }
} }
} }
private void handleCategoriesRefreshed()
{
Schedule(() =>
{
Notifications?.Post(new SimpleNotification
{
Text = ButtonSystemStrings.SeasonalBackgroundsRefreshed,
Icon = FontAwesome.Solid.CheckCircle,
Transient = true
});
});
}
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
@@ -927,7 +947,7 @@ namespace osu.Game
protected virtual Loader CreateLoader() => new Loader(); protected virtual Loader CreateLoader() => new Loader();
protected virtual UpdateManager CreateUpdateManager() => new UpdateManager(); protected virtual UpdateManager CreateUpdateManager() => new NoActionUpdateManager();
/// <summary> /// <summary>
/// Adjust the globally applied <see cref="DrawSizePreservingFillContainer.TargetDrawSize"/> in every <see cref="ScalingContainer"/>. /// Adjust the globally applied <see cref="DrawSizePreservingFillContainer.TargetDrawSize"/> in every <see cref="ScalingContainer"/>.
@@ -1186,8 +1206,8 @@ namespace osu.Game
Margin = new MarginPadding(5), Margin = new MarginPadding(5),
}, topMostOverlayContent.Add); }, topMostOverlayContent.Add);
if (!IsDeployedBuild) // if (!IsDeployedBuild) // we're going to have the "developer build" banner for a while
loadComponentSingleFile(devBuildBanner = new DevBuildBanner(), ScreenContainer.Add); loadComponentSingleFile(devBuildBanner = new DevBuildBanner(), ScreenContainer.Add);
loadComponentSingleFile(osuLogo, _ => loadComponentSingleFile(osuLogo, _ =>
{ {
@@ -1232,6 +1252,8 @@ namespace osu.Game
loadComponentSingleFile(screenshotManager, Add); loadComponentSingleFile(screenshotManager, Add);
loadComponentSingleFile(backgroundLoader, Add);
// dependency on notification overlay, dependent by settings overlay // dependency on notification overlay, dependent by settings overlay
loadComponentSingleFile(CreateUpdateManager(), Add, true); loadComponentSingleFile(CreateUpdateManager(), Add, true);
@@ -1333,6 +1355,18 @@ namespace osu.Game
handleStartupImport(); handleStartupImport();
} }
private void handleBackgroundLoadFailure(Exception exception)
{
Schedule(() =>
{
Notifications?.Post(new SimpleErrorNotification
{
Text = ButtonSystemStrings.SeasonalBackgroundsFail,
Icon = FontAwesome.Solid.ExclamationTriangle,
Transient = true
});
});
}
private void handleBackButton() private void handleBackButton()
{ {
// TODO: this is SUPER SUPER bad. // TODO: this is SUPER SUPER bad.
@@ -1701,6 +1735,12 @@ namespace osu.Game
{ {
case IntroScreen intro: case IntroScreen intro:
introScreen = intro; introScreen = intro;
SimpleNotification notification = new SimpleNotification
{
Text = ButtonSystemStrings.GreetingNotification,
Transient = true,
};
Notifications?.Post(notification);
devBuildBanner?.Show(); devBuildBanner?.Show();
break; break;

View File

@@ -32,7 +32,7 @@ namespace osu.Game.Overlays
Origin = Anchor.BottomCentre, Origin = Anchor.BottomCentre,
Font = OsuFont.Torus.With(size: 12), Font = OsuFont.Torus.With(size: 12),
Colour = colours.GrayF, Colour = colours.GrayF,
Text = "jvnkosu! development build", Text = $@"jvnkosu! " + game.Version,
Y = -12, Y = -12,
}, },
new OsuSpriteText new OsuSpriteText

View File

@@ -8,9 +8,11 @@ using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Localisation; using osu.Framework.Localisation;
using osu.Game.Configuration; using osu.Game.Configuration;
using osu.Game.Graphics.Backgrounds;
using osu.Game.Localisation; using osu.Game.Localisation;
using osu.Game.Online.API; using osu.Game.Online.API;
using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.API.Requests.Responses;
using osu.Game.Overlays.Settings;
namespace osu.Game.Overlays.Settings.Sections.UserInterface namespace osu.Game.Overlays.Settings.Sections.UserInterface
{ {
@@ -18,6 +20,9 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface
{ {
protected override LocalisableString Header => UserInterfaceStrings.MainMenuHeader; protected override LocalisableString Header => UserInterfaceStrings.MainMenuHeader;
[Resolved]
private SeasonalBackgroundLoader backgroundLoader { get; set; }
private IBindable<APIUser> user; private IBindable<APIUser> user;
private SettingsEnumDropdown<BackgroundSource> backgroundSourceDropdown; private SettingsEnumDropdown<BackgroundSource> backgroundSourceDropdown;
@@ -27,6 +32,46 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface
{ {
user = api.LocalUser.GetBoundCopy(); user = api.LocalUser.GetBoundCopy();
var backgroundModeBindable = config.GetBindable<SeasonalBackgroundMode>(OsuSetting.SeasonalBackgroundMode);
var enabledProxyBindable = new Bindable<bool>();
backgroundModeBindable.BindValueChanged(mode => enabledProxyBindable.Value = mode.NewValue == SeasonalBackgroundMode.Always, true);
enabledProxyBindable.BindValueChanged(enabled => backgroundModeBindable.Value = enabled.NewValue ? SeasonalBackgroundMode.Always : SeasonalBackgroundMode.Never);
var backgroundToggle = new SettingsCheckbox
{
LabelText = UserInterfaceStrings.UseSeasonalBackgrounds,
Current = enabledProxyBindable
};
var categoryDropdown = new SettingsDropdown<string>
{
LabelText = UserInterfaceStrings.SeasonalBackgroundsCategories,
Current = config.GetBindable<string>(OsuSetting.BackgroundCategory)
};
var refreshButton = new SettingsButton
{
Text = UserInterfaceStrings.SeasonalBackgroundsRefresh,
Action = () => backgroundLoader.RefreshCategories()
};
backgroundLoader.AvailableCategories.BindValueChanged(categories => categoryDropdown.Items = categories.NewValue, true);
backgroundModeBindable.BindValueChanged(mode =>
{
if (mode.NewValue == SeasonalBackgroundMode.Always)
{
categoryDropdown.Show();
refreshButton.Show();
}
else
{
categoryDropdown.Hide();
refreshButton.Hide();
}
}, true);
Children = new Drawable[] Children = new Drawable[]
{ {
new SettingsCheckbox new SettingsCheckbox
@@ -56,11 +101,9 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface
LabelText = UserInterfaceStrings.BackgroundSource, LabelText = UserInterfaceStrings.BackgroundSource,
Current = config.GetBindable<BackgroundSource>(OsuSetting.MenuBackgroundSource), Current = config.GetBindable<BackgroundSource>(OsuSetting.MenuBackgroundSource),
}, },
new SettingsEnumDropdown<SeasonalBackgroundMode> backgroundToggle,
{ categoryDropdown,
LabelText = UserInterfaceStrings.SeasonalBackgrounds, refreshButton,
Current = config.GetBindable<SeasonalBackgroundMode>(OsuSetting.SeasonalBackgroundMode),
}
}; };
} }

View File

@@ -62,7 +62,7 @@ namespace osu.Game.Screens.Backgrounds
source.ValueChanged += _ => Scheduler.AddOnce(next); source.ValueChanged += _ => Scheduler.AddOnce(next);
beatmap.ValueChanged += _ => Scheduler.AddOnce(next); beatmap.ValueChanged += _ => Scheduler.AddOnce(next);
introSequence.ValueChanged += _ => Scheduler.AddOnce(next); introSequence.ValueChanged += _ => Scheduler.AddOnce(next);
seasonalBackgroundLoader.SeasonalBackgroundChanged += () => Scheduler.AddOnce(next); seasonalBackgroundLoader.BackgroundChanged += () => Scheduler.AddOnce(next);
currentDisplay = RNG.Next(0, background_count); currentDisplay = RNG.Next(0, background_count);
Next(); Next();

View File

@@ -36,7 +36,7 @@ namespace osu.Game.Updater
ReleaseStream stream = externalReleaseStream ?? ReleaseStream.Value; ReleaseStream stream = externalReleaseStream ?? ReleaseStream.Value;
bool includePrerelease = stream == Configuration.ReleaseStream.Tachyon; bool includePrerelease = stream == Configuration.ReleaseStream.Tachyon;
OsuJsonWebRequest<GitHubRelease[]> releasesRequest = new OsuJsonWebRequest<GitHubRelease[]>("https://api.github.com/repos/ppy/osu/releases?per_page=10&page=1"); OsuJsonWebRequest<GitHubRelease[]> releasesRequest = new OsuJsonWebRequest<GitHubRelease[]>("https://gitea.jvnko.boats/api/v1/repos/jvnkosu/client/releases?limit=10&page=1");
await releasesRequest.PerformAsync(cancellationToken).ConfigureAwait(false); await releasesRequest.PerformAsync(cancellationToken).ConfigureAwait(false);
GitHubRelease[] releases = releasesRequest.ResponseObject; GitHubRelease[] releases = releasesRequest.ResponseObject;

View File

@@ -69,9 +69,10 @@ namespace osu.Game.Updater
if (FixedReleaseStream != null) if (FixedReleaseStream != null)
config.SetValue(OsuSetting.ReleaseStream, FixedReleaseStream.Value); config.SetValue(OsuSetting.ReleaseStream, FixedReleaseStream.Value);
// notify the user if they're using a build that is not officially sanctioned. // nope, doesn't matter, we're already not official
if (RuntimeInfo.EntryAssembly.GetCustomAttribute<OfficialBuildAttribute>() == null) // // notify the user if they're using a build that is not officially sanctioned.
Notifications.Post(new SimpleNotification { Text = NotificationsStrings.NotOfficialBuild }); // if (RuntimeInfo.EntryAssembly.GetCustomAttribute<OfficialBuildAttribute>() == null)
// Notifications.Post(new SimpleNotification { Text = NotificationsStrings.NotOfficialBuild });
} }
else else
{ {

View File

@@ -36,7 +36,7 @@ namespace osu.Game.Users.Drawables
if (user != null && user.OnlineID > 1) if (user != null && user.OnlineID > 1)
// TODO: The fallback here should not need to exist. Users should be looked up and populated via UserLookupCache or otherwise // TODO: The fallback here should not need to exist. Users should be looked up and populated via UserLookupCache or otherwise
// in remaining cases where this is required (chat tabs, local leaderboard), at which point this should be removed. // in remaining cases where this is required (chat tabs, local leaderboard), at which point this should be removed.
Texture = textures.Get((user as APIUser)?.AvatarUrl ?? $@"https://a.ppy.sh/{user.OnlineID}"); Texture = textures.Get((user as APIUser)?.AvatarUrl ?? $@"https://osu.jvnko.boats/uploads-avatar/{user.OnlineID}");
Texture ??= textures.Get(@"Online/avatar-guest"); Texture ??= textures.Get(@"Online/avatar-guest");
} }