5 Commits

22 changed files with 426 additions and 123 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

@@ -0,0 +1,128 @@
// 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.Linq;
using System.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Logging;
using osu.Framework.Platform;
using osu.Framework.Utils;
using osu.Game.Configuration;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Online.API.Requests.Responses;
namespace osu.Game.Audio
{
[Cached]
public partial class WelcomeMusicManager : Drawable
{
public event Action<Exception> OnLoadFailure;
public event Action OnCategoriesRefreshed;
public readonly Bindable<IEnumerable<string>> AvailableCategories = new Bindable<IEnumerable<string>>();
private ITrack preloadedTrack;
private List<APIWelcomeMusic> currentTracks;
[Resolved]
private IAPIProvider api { get; set; }
[Resolved]
private AudioManager audioManager { get; set; }
[Resolved]
private OsuConfigManager config { get; set; }
[Resolved]
private GameHost host { get; set; }
private Bindable<WelcomeMusicMode> musicMode;
private Bindable<string> selectedCategory;
[BackgroundDependencyLoader]
private void load()
{
musicMode = config.GetBindable<WelcomeMusicMode>(OsuSetting.WelcomeMusicMode);
selectedCategory = config.GetBindable<string>(OsuSetting.WelcomeMusicCategory);
fetchCategories();
}
public void RefreshCategories() => fetchCategories();
private void fetchCategories()
{
var request = new GetMusicCategoriesRequest();
request.Success += response =>
{
var serverCategories = response.Categories ?? Enumerable.Empty<string>();
AvailableCategories.Value = serverCategories.ToList();
OnCategoriesRefreshed?.Invoke();
};
request.Failure += exception =>
{
Logger.Error(exception, "ОШИБКА: Не удалось загрузить категории музыки!");
AvailableCategories.Value = new[] { "Не удалось загрузить..." };
OnLoadFailure?.Invoke(exception);
};
api.PerformAsync(request);
}
public async Task PreloadCurrentTrack()
{
if (musicMode.Value == WelcomeMusicMode.Default)
{
preloadedTrack = audioManager.Tracks.Get("Samples/welcome.ogg");
return;
}
if (string.IsNullOrEmpty(selectedCategory.Value) || selectedCategory.Value.Contains("Не удалось"))
return;
var request = new GetWelcomeMusicRequest(selectedCategory.Value);
var tcs = new TaskCompletionSource<bool>();
request.Success += response =>
{
currentTracks = response;
tcs.SetResult(true);
};
request.Failure += exception =>
{
Logger.Error(exception, "ОШИБКА: Не удалось загрузить список треков!");
tcs.SetResult(false);
};
api.PerformAsync(request);
await tcs.Task;
if (currentTracks?.Any() != true)
return;
var randomTrackInfo = currentTracks[RNG.Next(0, currentTracks.Count)];
try
{
preloadedTrack = audioManager.Tracks.Get(randomTrackInfo.Url);
if (preloadedTrack != null)
preloadedTrack.Looping = false;
}
catch (Exception e)
{
Logger.Error(e, $"ОШИБКА: Не удалось загрузить трек по URL: {randomTrackInfo.Url}");
}
}
public ITrack GetPreloadedTrack() => preloadedTrack;
public void RequestRestart()
{
host.Exit();
}
}
}

View File

@@ -213,6 +213,8 @@ namespace osu.Game.Configuration
SetDefault(OsuSetting.MultiplayerShowInProgressFilter, true); SetDefault(OsuSetting.MultiplayerShowInProgressFilter, true);
SetDefault(OsuSetting.LastProcessedMetadataId, -1); SetDefault(OsuSetting.LastProcessedMetadataId, -1);
SetDefault(OsuSetting.WelcomeMusicMode, WelcomeMusicMode.Default);
SetDefault(OsuSetting.WelcomeMusicCategory, "Default");
SetDefault(OsuSetting.ComboColourNormalisationAmount, 0.2f, 0f, 1f, 0.01f); SetDefault(OsuSetting.ComboColourNormalisationAmount, 0.2f, 0f, 1f, 0.01f);
SetDefault(OsuSetting.UserOnlineStatus, UserStatus.Online); SetDefault(OsuSetting.UserOnlineStatus, UserStatus.Online);
@@ -382,6 +384,8 @@ namespace osu.Game.Configuration
AudioOffset, AudioOffset,
VolumeInactive, VolumeInactive,
WelcomeMusicMode,
WelcomeMusicCategory,
MenuMusic, MenuMusic,
MenuVoice, MenuVoice,
MenuTips, MenuTips,

View File

@@ -0,0 +1,11 @@
// 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.
namespace osu.Game.Configuration
{
public enum WelcomeMusicMode
{
Default,
Custom
}
}

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,16 +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> /// <summary>
/// "Seasonal backgrounds" /// "Background categories"
/// </summary>*/ /// </summary>
/* public static LocalisableString SeasonalBackgroundsCategories => new TranslatableString(getKey(@"seasonal_backgrounds_categories"), @"Seasonal backgrounds categories"); 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

@@ -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

@@ -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 GetMusicCategoriesRequest : APIRequest<APIBackgroundCategories>
{
protected override string Target => @"https://osu.jvnko.boats/welcome-music/categories";
}
}

View File

@@ -0,0 +1,21 @@
// 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 System.Net;
using osu.Game.Online.API.Requests.Responses;
namespace osu.Game.Online.API.Requests
{
public class GetWelcomeMusicRequest : APIRequest<List<APIWelcomeMusic>>
{
private readonly string category;
public GetWelcomeMusicRequest(string category)
{
this.category = category;
}
protected override string Target => $"https://osu.jvnko.boats/welcome-music/list?category={WebUtility.UrlEncode(category)}";
}
}

View File

@@ -0,0 +1,17 @@
// 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 Newtonsoft.Json;
namespace osu.Game.Online.API.Requests.Responses
{
// Описывает один трек, как он приходит с сервера
public class APIWelcomeMusic
{
[JsonProperty("name")]
public string? Name { get; set; }
[JsonProperty("url")]
public string? Url { get; set; }
}
}

View File

@@ -32,6 +32,7 @@ using osu.Framework.Logging;
using osu.Framework.Platform; using osu.Framework.Platform;
using osu.Framework.Screens; using osu.Framework.Screens;
using osu.Framework.Threading; using osu.Framework.Threading;
using osu.Game.Audio;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Collections; using osu.Game.Collections;
using osu.Game.Configuration; using osu.Game.Configuration;
@@ -170,8 +171,11 @@ namespace osu.Game
[Cached] [Cached]
private readonly ScreenshotManager screenshotManager = new ScreenshotManager(); private readonly ScreenshotManager screenshotManager = new ScreenshotManager();
// --- ÍÀØÈ ÍÎÂÛÅ ÊÎÌÏÎÍÅÍÒÛ ---
[Cached] [Cached]
private readonly SeasonalBackgroundLoader backgroundLoader; private readonly SeasonalBackgroundLoader backgroundLoader;
[Cached]
private readonly WelcomeMusicManager musicManager;
protected SentryLogger SentryLogger; protected SentryLogger SentryLogger;
@@ -253,9 +257,11 @@ namespace osu.Game
public OsuGame(string[] args = null) public OsuGame(string[] args = null)
{ {
// --- ÑÎÇÄÀÅÌ ÍÀØÈ ÊÎÌÏÎÍÅÍÒÛ È ÏÎÄÏÈÑÛÂÀÅÌÑß ÍÀ ÑÎÁÛÒÈß ---
backgroundLoader = new SeasonalBackgroundLoader(); backgroundLoader = new SeasonalBackgroundLoader();
backgroundLoader.OnLoadFailure += handleBackgroundLoadFailure; backgroundLoader.OnLoadFailure += handleBackgroundLoadFailure;
backgroundLoader.OnCategoriesRefreshed += handleCategoriesRefreshed; backgroundLoader.OnCategoriesRefreshed += handleCategoriesRefreshed;
musicManager = new WelcomeMusicManager();
this.args = args; this.args = args;
@@ -470,7 +476,8 @@ namespace osu.Game
IsActive.BindValueChanged(active => updateActiveState(active.NewValue), true); IsActive.BindValueChanged(active => updateActiveState(active.NewValue), true);
Audio.AddAdjustment(AdjustableProperty.Volume, inactiveVolumeFade); Audio.AddAdjustment(AdjustableProperty.Volume, inactiveVolumeFade);
dependencies.CacheAs(musicManager);
Add(musicManager);
SelectedMods.BindValueChanged(modsChanged); SelectedMods.BindValueChanged(modsChanged);
Beatmap.BindValueChanged(beatmapChanged, true); Beatmap.BindValueChanged(beatmapChanged, true);
configUserActivity.BindValueChanged(_ => updateWindowTitle()); configUserActivity.BindValueChanged(_ => updateWindowTitle());
@@ -946,7 +953,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"/>.
@@ -1205,8 +1212,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, _ =>
{ {
@@ -1286,7 +1293,12 @@ namespace osu.Game
}, rightFloatingOverlayContent.Add, true); }, rightFloatingOverlayContent.Add, true);
loadComponentSingleFile(new AccountCreationOverlay(), topMostOverlayContent.Add, true); loadComponentSingleFile(new AccountCreationOverlay(), topMostOverlayContent.Add, true);
loadComponentSingleFile<IDialogOverlay>(new DialogOverlay(), topMostOverlayContent.Add, true);
var dialogOverlay = new DialogOverlay();
dependencies.CacheAs<IDialogOverlay>(dialogOverlay);
dependencies.Cache(dialogOverlay);
loadComponentSingleFile(dialogOverlay, topMostOverlayContent.Add);
loadComponentSingleFile(new MedalOverlay(), topMostOverlayContent.Add); loadComponentSingleFile(new MedalOverlay(), topMostOverlayContent.Add);
loadComponentSingleFile(new BackgroundDataStoreProcessor(), Add); loadComponentSingleFile(new BackgroundDataStoreProcessor(), Add);
@@ -1358,10 +1370,11 @@ namespace osu.Game
{ {
Schedule(() => Schedule(() =>
{ {
Notifications?.Post(new SimpleNotification Notifications?.Post(new SimpleErrorNotification
{ {
Text = "Íå óäàëîñü çàãðóçèòü ôîíû. Ïðîâåðüòå ïîäêëþ÷åíèå ê èíòåðíåòó.", Text = ButtonSystemStrings.SeasonalBackgroundsFail,
Icon = FontAwesome.Solid.ExclamationTriangle Icon = FontAwesome.Solid.ExclamationTriangle,
Transient = true
}); });
}); });
} }
@@ -1735,7 +1748,8 @@ namespace osu.Game
introScreen = intro; introScreen = intro;
SimpleNotification notification = new SimpleNotification SimpleNotification notification = new SimpleNotification
{ {
Text = "Welcome to jvnkosu!lazer!", Text = ButtonSystemStrings.GreetingNotification,
Transient = true,
}; };
Notifications?.Post(notification); Notifications?.Post(notification);
devBuildBanner?.Show(); devBuildBanner?.Show();

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

@@ -7,12 +7,13 @@ using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Localisation; using osu.Framework.Localisation;
using osu.Game.Audio;
using osu.Game.Configuration; using osu.Game.Configuration;
using osu.Game.Graphics.Backgrounds; 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; using osu.Game.Overlays.Dialog;
namespace osu.Game.Overlays.Settings.Sections.UserInterface namespace osu.Game.Overlays.Settings.Sections.UserInterface
{ {
@@ -22,42 +23,43 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface
[Resolved] [Resolved]
private SeasonalBackgroundLoader backgroundLoader { get; set; } private SeasonalBackgroundLoader backgroundLoader { get; set; }
[Resolved]
private WelcomeMusicManager musicManager { get; set; }
[Resolved]
private DialogOverlay dialogOverlay { get; set; }
private IBindable<APIUser> user; private IBindable<APIUser> user;
private SettingsEnumDropdown<BackgroundSource> backgroundSourceDropdown; private SettingsEnumDropdown<BackgroundSource> backgroundSourceDropdown;
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuConfigManager config, IAPIProvider api) private void load(OsuConfigManager config, IAPIProvider api)
{ {
AutoSizeAxes = Axes.Y;
user = api.LocalUser.GetBoundCopy(); user = api.LocalUser.GetBoundCopy();
var backgroundModeBindable = config.GetBindable<SeasonalBackgroundMode>(OsuSetting.SeasonalBackgroundMode); var backgroundModeBindable = config.GetBindable<SeasonalBackgroundMode>(OsuSetting.SeasonalBackgroundMode);
var enabledProxyBindable = new Bindable<bool>(); var enabledProxyBindable = new Bindable<bool>();
backgroundModeBindable.BindValueChanged(mode => enabledProxyBindable.Value = mode.NewValue == SeasonalBackgroundMode.Always, true); backgroundModeBindable.BindValueChanged(mode => enabledProxyBindable.Value = mode.NewValue == SeasonalBackgroundMode.Always, true);
enabledProxyBindable.BindValueChanged(enabled => backgroundModeBindable.Value = enabled.NewValue ? SeasonalBackgroundMode.Always : SeasonalBackgroundMode.Never); enabledProxyBindable.BindValueChanged(enabled => backgroundModeBindable.Value = enabled.NewValue ? SeasonalBackgroundMode.Always : SeasonalBackgroundMode.Never);
var backgroundToggle = new SettingsCheckbox var backgroundToggle = new SettingsCheckbox
{ {
LabelText = "Пользовательские фоны", LabelText = UserInterfaceStrings.UseSeasonalBackgrounds,
Current = enabledProxyBindable Current = enabledProxyBindable
}; };
var categoryDropdown = new SettingsDropdown<string> var categoryDropdown = new SettingsDropdown<string>
{ {
LabelText = "Категория фонов", LabelText = UserInterfaceStrings.SeasonalBackgroundsCategories,
Current = config.GetBindable<string>(OsuSetting.BackgroundCategory) Current = config.GetBindable<string>(OsuSetting.BackgroundCategory)
}; };
var refreshButton = new SettingsButton var refreshButton = new SettingsButton
{ {
Text = "Обновить список категорий", Text = UserInterfaceStrings.SeasonalBackgroundsRefresh,
Action = () => backgroundLoader.RefreshCategories() Action = () => backgroundLoader.RefreshCategories()
}; };
backgroundLoader.AvailableCategories.BindValueChanged(categories => categoryDropdown.Items = categories.NewValue, true); backgroundLoader.AvailableCategories.BindValueChanged(categories => categoryDropdown.Items = categories.NewValue, true);
backgroundModeBindable.BindValueChanged(mode => backgroundModeBindable.BindValueChanged(mode =>
{ {
if (mode.NewValue == SeasonalBackgroundMode.Always) if (mode.NewValue == SeasonalBackgroundMode.Always)
@@ -72,6 +74,51 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface
} }
}, true); }, true);
var musicModeDropdown = new SettingsEnumDropdown<WelcomeMusicMode>
{
LabelText = "Музыкальное приветствие",
Current = config.GetBindable<WelcomeMusicMode>(OsuSetting.WelcomeMusicMode)
};
var musicCategoryDropdown = new SettingsDropdown<string>
{
LabelText = "Категория музыки",
Current = config.GetBindable<string>(OsuSetting.WelcomeMusicCategory)
};
var refreshMusicButton = new SettingsButton
{
Text = "Обновить категории музыки",
Action = () => musicManager.RefreshCategories()
};
musicModeDropdown.Current.BindValueChanged(mode =>
{
if (mode.NewValue == WelcomeMusicMode.Custom)
{
musicCategoryDropdown.Show();
refreshMusicButton.Show();
}
else
{
musicCategoryDropdown.Hide();
refreshMusicButton.Hide();
}
}, true);
musicCategoryDropdown.Current.BindValueChanged(category =>
{
if (category.OldValue != null &&
musicModeDropdown.Current.Value == WelcomeMusicMode.Custom &&
!category.OldValue.Equals(category.NewValue))
{
dialogOverlay.Push(new ConfirmDialog("Для применения этой настройки требуется перезапуск.",
() => musicManager.RequestRestart()));
}
});
musicManager.AvailableCategories.BindValueChanged(categories => musicCategoryDropdown.Items = categories.NewValue, true);
Children = new Drawable[] Children = new Drawable[]
{ {
new SettingsCheckbox new SettingsCheckbox
@@ -91,6 +138,9 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface
LabelText = UserInterfaceStrings.OsuMusicTheme, LabelText = UserInterfaceStrings.OsuMusicTheme,
Current = config.GetBindable<bool>(OsuSetting.MenuMusic) Current = config.GetBindable<bool>(OsuSetting.MenuMusic)
}, },
musicModeDropdown,
musicCategoryDropdown,
refreshMusicButton,
new SettingsEnumDropdown<IntroSequence> new SettingsEnumDropdown<IntroSequence>
{ {
LabelText = UserInterfaceStrings.IntroSequence, LabelText = UserInterfaceStrings.IntroSequence,

View File

@@ -3,13 +3,10 @@
#nullable disable #nullable disable
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Development; using osu.Framework.Development;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shaders;
using osu.Framework.Utils; using osu.Framework.Utils;
using osu.Game.Screens.Menu; using osu.Game.Screens.Menu;
using osu.Framework.Screens; using osu.Framework.Screens;
@@ -18,11 +15,19 @@ using osu.Game.Configuration;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Seasonal; using osu.Game.Seasonal;
using IntroSequence = osu.Game.Configuration.IntroSequence; using IntroSequence = osu.Game.Configuration.IntroSequence;
using osu.Game.Audio;
namespace osu.Game.Screens namespace osu.Game.Screens
{ {
public partial class Loader : StartupScreen public partial class Loader : StartupScreen
{ {
[Resolved]
private OsuConfigManager config { get; set; }
[Resolved]
private WelcomeMusicManager musicManager { get; set; }
private WelcomeMusicMode musicMode;
public Loader() public Loader()
{ {
ValidForResume = false; ValidForResume = false;
@@ -30,17 +35,16 @@ namespace osu.Game.Screens
private OsuScreen loadableScreen; private OsuScreen loadableScreen;
private ShaderPrecompiler precompiler; private ShaderPrecompiler precompiler;
private IntroSequence introSequence;
private LoadingSpinner spinner; private LoadingSpinner spinner;
private ScheduledDelegate spinnerShow; private ScheduledDelegate spinnerShow;
protected virtual OsuScreen CreateLoadableScreen() => getIntroSequence(); protected virtual OsuScreen CreateLoadableScreen()
private IntroScreen getIntroSequence()
{ {
// Headless tests run too fast to load non-circles intros correctly. var introSequence = config.Get<IntroSequence>(OsuSetting.IntroSequence);
// They will hit the "audio can't play" notification and cause random test failures.
if (musicMode == WelcomeMusicMode.Custom)
return new IntroFade();
if (SeasonalUIConfig.ENABLED && !DebugUtils.IsNUnitRunning) if (SeasonalUIConfig.ENABLED && !DebugUtils.IsNUnitRunning)
return new IntroChristmas(createMainMenu); return new IntroChristmas(createMainMenu);
@@ -51,27 +55,28 @@ namespace osu.Game.Screens
{ {
case IntroSequence.Circles: case IntroSequence.Circles:
return new IntroCircles(createMainMenu); return new IntroCircles(createMainMenu);
case IntroSequence.Welcome: case IntroSequence.Welcome:
return new IntroWelcome(createMainMenu); return new IntroWelcome(createMainMenu);
default: default:
return new IntroTriangles(createMainMenu); return new IntroTriangles(createMainMenu);
} }
MainMenu createMainMenu() => new MainMenu();
} }
private static MainMenu createMainMenu() => new MainMenu();
protected virtual ShaderPrecompiler CreateShaderPrecompiler() => new ShaderPrecompiler(); protected virtual ShaderPrecompiler CreateShaderPrecompiler() => new ShaderPrecompiler();
public override void OnEntering(ScreenTransitionEvent e) public override async void OnEntering(ScreenTransitionEvent e)
{ {
base.OnEntering(e); base.OnEntering(e);
LoadComponentAsync(precompiler = CreateShaderPrecompiler(), AddInternal); musicMode = config.Get<WelcomeMusicMode>(OsuSetting.WelcomeMusicMode);
await musicManager.PreloadCurrentTrack().ConfigureAwait(true);
LoadComponentAsync(loadableScreen = CreateLoadableScreen()); LoadComponentAsync(loadableScreen = CreateLoadableScreen());
LoadComponentAsync(precompiler = CreateShaderPrecompiler(), AddInternal);
LoadComponentAsync(spinner = new LoadingSpinner(true, true) LoadComponentAsync(spinner = new LoadingSpinner(true, true)
{ {
Anchor = Anchor.BottomRight, Anchor = Anchor.BottomRight,
@@ -88,7 +93,7 @@ namespace osu.Game.Screens
private void checkIfLoaded() private void checkIfLoaded()
{ {
if (loadableScreen?.LoadState != LoadState.Ready || !precompiler.FinishedCompiling) if (loadableScreen?.LoadState != LoadState.Ready || !precompiler.IsLoaded)
{ {
Schedule(checkIfLoaded); Schedule(checkIfLoaded);
return; return;
@@ -105,55 +110,9 @@ namespace osu.Game.Screens
this.Push(loadableScreen); this.Push(loadableScreen);
} }
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
{
introSequence = config.Get<IntroSequence>(OsuSetting.IntroSequence);
}
/// <summary>
/// Compiles a set of shaders before continuing. Attempts to draw some frames between compilation by limiting to one compile per draw frame.
/// </summary>
public partial class ShaderPrecompiler : Drawable public partial class ShaderPrecompiler : Drawable
{ {
private readonly List<IShader> loadTargets = new List<IShader>(); // ... код ShaderPrecompiler остается без изменений ... (Блять, а где он?)
public bool FinishedCompiling { get; private set; }
[BackgroundDependencyLoader]
private void load(ShaderManager manager)
{
loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE));
loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.BLUR));
loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_3, FragmentShaderDescriptor.TEXTURE));
loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_2, @"TriangleBorder"));
loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_2, @"FastCircle"));
loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_2, @"CircularProgress"));
loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_2, @"ArgonBarPath"));
loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_2, @"ArgonBarPathBackground"));
loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_2, @"SaturationSelectorBackground"));
loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_2, @"HueSelectorBackground"));
loadTargets.Add(manager.Load(@"LogoAnimation", @"LogoAnimation"));
// Ruleset local shader usage (should probably move somewhere else).
loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_2, @"SpinnerGlow"));
loadTargets.Add(manager.Load(@"CursorTrail", FragmentShaderDescriptor.TEXTURE));
}
protected virtual bool AllLoaded => loadTargets.All(s => s.IsLoaded);
protected override void Update()
{
base.Update();
// if our target is null we are done.
if (AllLoaded)
{
FinishedCompiling = true;
Expire();
}
}
} }
} }
} }

View File

@@ -0,0 +1,30 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Screens;
using osu.Game.Audio;
namespace osu.Game.Screens.Menu
{
public partial class IntroFade : OsuScreen
{
[Resolved]
private WelcomeMusicManager? musicManager { get; set; }
public override void OnEntering(ScreenTransitionEvent e)
{
base.OnEntering(e);
this.FadeInFromZero(1000, Easing.OutQuint);
#pragma warning disable CS8602 // Разыменование вероятной пустой ссылки.
var track = musicManager.GetPreloadedTrack();
#pragma warning restore CS8602 // Разыменование вероятной пустой ссылки.
track?.Start();
Scheduler.AddDelayed(() => this.Push(new MainMenu()), 2000);
}
}
}

View File

@@ -105,7 +105,7 @@ namespace osu.Game.Screens.Menu
private ParallaxContainer buttonsContainer; private ParallaxContainer buttonsContainer;
private SongTicker songTicker; private SongTicker songTicker;
private Container logoTarget; private Container logoTarget;
private OnlineMenuBanner onlineMenuBanner; /*private OnlineMenuBanner onlineMenuBanner;*/
private MenuTipDisplay menuTipDisplay; private MenuTipDisplay menuTipDisplay;
private FillFlowContainer bottomElementsFlow; private FillFlowContainer bottomElementsFlow;
private SupporterDisplay supporterDisplay; private SupporterDisplay supporterDisplay;
@@ -198,12 +198,12 @@ namespace osu.Game.Screens.Menu
{ {
Anchor = Anchor.TopCentre, Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre, Origin = Anchor.TopCentre,
}, }
onlineMenuBanner = new OnlineMenuBanner /*onlineMenuBanner = new OnlineMenuBanner
{ {
Anchor = Anchor.TopCentre, Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre, Origin = Anchor.TopCentre,
} }*/
} }
}, },
supporterDisplay = new SupporterDisplay supporterDisplay = new SupporterDisplay
@@ -224,12 +224,12 @@ namespace osu.Game.Screens.Menu
case ButtonSystemState.Initial: case ButtonSystemState.Initial:
case ButtonSystemState.Exit: case ButtonSystemState.Exit:
ApplyToBackground(b => b.FadeColour(OsuColour.Gray(baseDim), 500, Easing.OutSine)); ApplyToBackground(b => b.FadeColour(OsuColour.Gray(baseDim), 500, Easing.OutSine));
onlineMenuBanner.State.Value = Visibility.Hidden; /*onlineMenuBanner.State.Value = Visibility.Hidden;*/
break; break;
default: default:
ApplyToBackground(b => b.FadeColour(OsuColour.Gray(baseDim * 0.8f), 500, Easing.OutSine)); ApplyToBackground(b => b.FadeColour(OsuColour.Gray(baseDim * 0.8f), 500, Easing.OutSine));
onlineMenuBanner.State.Value = Visibility.Visible; /*onlineMenuBanner.State.Value = Visibility.Visible;*/
break; break;
} }
}; };

View File

@@ -195,7 +195,6 @@ namespace osu.Game.Tests.Visual
{ {
base.Update(); base.Update();
// when running in visual tests and the window loses focus, we generally don't want the game to pause.
((Bindable<bool>)IsActive).Value = true; ((Bindable<bool>)IsActive).Value = true;
} }
} }
@@ -206,7 +205,18 @@ namespace osu.Game.Tests.Visual
private partial class TestShaderPrecompiler : ShaderPrecompiler private partial class TestShaderPrecompiler : ShaderPrecompiler
{ {
protected override bool AllLoaded => true; // Старый код, который больше не работает:
// protected override bool AllLoaded => true;
// НОВЫЙ, ПРАВИЛЬНЫЙ КОД:
[BackgroundDependencyLoader]
private void load()
{
// Этот "фальшивый" компилятор не должен ничего делать.
// Мы сразу же вызываем Expire(), чтобы он считался "загруженным" (IsLoaded станет true).
// Это позволит нашему основному Loader'у продолжить работу, как и было задумано.
Expire();
}
} }
} }
} }

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
{ {