From f73b32232c4f591989da2dff692cba6ab04fd1e1 Mon Sep 17 00:00:00 2001 From: Guerro323 Date: Wed, 5 May 2021 21:06:13 +0200 Subject: [PATCH] update --- CHANGELOG.md | 8 +- .../KeyboardListenerTest.cs | 9 + .../PataNext.Export.Desktop.Tests.csproj | 2 +- .../Visual/GithubChangelogScene.cs | 31 ++ .../Visual/LauncherScene.cs | 295 +++-------------- .../BringIntegratedClientInFrontSystem.cs | 56 +++- ...dLauncherProvidersFromSimulationFeature.cs | 29 ++ .../Features/AddMasterServerFeature.cs | 6 +- .../AddReceiveGameHostClientFeature.cs | 8 +- .../PataNext.Export.Desktop.csproj | 8 +- PataNext.Export.Desktop/Program.cs | 6 +- .../Providers/StandardAccountProvider.cs | 103 ++++++ .../Updater/GameUpdater.cs | 12 +- .../Updater/SquirrelUpdater.cs | 102 ++++-- .../Visual/Dependencies/IAccountProvider.cs | 27 ++ .../Visual/Dependencies/IChangelogProvider.cs | 76 +++++ .../Dependencies/IGlobalNotifications.cs | 20 -- .../Dependencies/INotificationsProvider.cs | 20 ++ ...datePatchDownload.cs => IPatchProvider.cs} | 2 +- .../Visual/GameHostApplicationRunner.cs | 5 +- .../LauncherConfigurationManager.cs | 27 ++ .../Launcher/Configuration/UpdateChannel.cs | 9 + .../Visual/Launcher/HomeChangelogControl.cs | 2 +- .../Visual/Launcher/HomePlayScreen.cs | 126 ------- .../Visual/Launcher/LauncherMainScene.cs | 211 ++++++++++++ .../Visual/Launcher/NotificationBase.cs | 23 +- .../Visual/Launcher/NotificationPopup.cs | 8 +- .../Visual/Launcher/ProgressPlayBarControl.cs | 28 ++ .../{ => Screens}/HomeChangelogScreen.cs | 44 ++- .../Visual/Launcher/Screens/HomePlayScreen.cs | 170 ++++++++++ .../Launcher/Screens/HomeSettingsScreen.cs | 172 ++++++++++ .../Visual/Launcher/SettingsButtonBase.cs | 12 + .../Visual/Launcher/SettingsButtonHome.cs | 78 +++++ .../Visual/Launcher/SettingsControl.cs | 117 +++++++ .../SettingsScreen/SettingsScreenBase.cs | 68 ++++ .../SettingsScreen/SettingsScreenInput.cs | 265 +++++++++++++++ .../SettingsScreen/SettingsScreenUpdate.cs | 33 ++ .../Visual/Launcher/Sidebar.cs | 4 +- .../Visual/Launcher/SidebarAccountDropdown.cs | 309 ++++++++++++++++++ .../Visual/Launcher/SidebarMenuTabControl.cs | 2 - PataNext.Export.Desktop/Visual/VisualGame.cs | 81 ++++- .../Visual/VisualGameBase.cs | 98 +++++- .../Systems/Inputs/RegisterInputSystemBase.cs | 2 + .../GameModes/RuntimeTestCoopMission.cs | 13 +- .../RuntimeTests/Scenars/TestScenar.cs | 8 +- StormiumTeam.GameBase/Module.cs | 1 + .../ConnectionServiceComponents.cs | 36 ++ .../MasterServer/MasterServerRequestHub.cs | 3 - .../StandardAuthService/Components.cs | 32 +- .../MasterServer/User/CurrentUserSystem.cs | 16 +- 50 files changed, 2314 insertions(+), 509 deletions(-) create mode 100644 PataNext.Export.Desktop.Tests/KeyboardListenerTest.cs create mode 100644 PataNext.Export.Desktop.Tests/Visual/GithubChangelogScene.cs create mode 100644 PataNext.Export.Desktop/Features/AddLauncherProvidersFromSimulationFeature.cs create mode 100644 PataNext.Export.Desktop/Providers/StandardAccountProvider.cs create mode 100644 PataNext.Export.Desktop/Visual/Dependencies/IAccountProvider.cs create mode 100644 PataNext.Export.Desktop/Visual/Dependencies/IChangelogProvider.cs delete mode 100644 PataNext.Export.Desktop/Visual/Dependencies/IGlobalNotifications.cs create mode 100644 PataNext.Export.Desktop/Visual/Dependencies/INotificationsProvider.cs rename PataNext.Export.Desktop/Visual/Dependencies/{IUpdatePatchDownload.cs => IPatchProvider.cs} (88%) create mode 100644 PataNext.Export.Desktop/Visual/Launcher/Configuration/LauncherConfigurationManager.cs create mode 100644 PataNext.Export.Desktop/Visual/Launcher/Configuration/UpdateChannel.cs delete mode 100644 PataNext.Export.Desktop/Visual/Launcher/HomePlayScreen.cs create mode 100644 PataNext.Export.Desktop/Visual/Launcher/LauncherMainScene.cs rename PataNext.Export.Desktop/Visual/Launcher/{ => Screens}/HomeChangelogScreen.cs (60%) create mode 100644 PataNext.Export.Desktop/Visual/Launcher/Screens/HomePlayScreen.cs create mode 100644 PataNext.Export.Desktop/Visual/Launcher/Screens/HomeSettingsScreen.cs create mode 100644 PataNext.Export.Desktop/Visual/Launcher/SettingsButtonBase.cs create mode 100644 PataNext.Export.Desktop/Visual/Launcher/SettingsButtonHome.cs create mode 100644 PataNext.Export.Desktop/Visual/Launcher/SettingsControl.cs create mode 100644 PataNext.Export.Desktop/Visual/Launcher/SettingsScreen/SettingsScreenBase.cs create mode 100644 PataNext.Export.Desktop/Visual/Launcher/SettingsScreen/SettingsScreenInput.cs create mode 100644 PataNext.Export.Desktop/Visual/Launcher/SettingsScreen/SettingsScreenUpdate.cs create mode 100644 PataNext.Export.Desktop/Visual/Launcher/SidebarAccountDropdown.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 077d9057..1c3a83a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,10 @@ -# 2021.05.04.1640 +# 2021.505.2105 +This version is an official release + +## Client ++ Fixed embedding/integration of the client in the game window. + +# 2021.504.1640 ## Launcher + Launcher Visual overhaul (work in progress) + Added Settings pages diff --git a/PataNext.Export.Desktop.Tests/KeyboardListenerTest.cs b/PataNext.Export.Desktop.Tests/KeyboardListenerTest.cs new file mode 100644 index 00000000..751c82bb --- /dev/null +++ b/PataNext.Export.Desktop.Tests/KeyboardListenerTest.cs @@ -0,0 +1,9 @@ +using System; +using System.Collections.Generic; +using GameHost.Inputs.Systems; +using SharpInputSystem; + +namespace PataNext.Export.Desktop.Tests +{ + +} \ No newline at end of file diff --git a/PataNext.Export.Desktop.Tests/PataNext.Export.Desktop.Tests.csproj b/PataNext.Export.Desktop.Tests/PataNext.Export.Desktop.Tests.csproj index 3743b1f0..dbe7ea7c 100644 --- a/PataNext.Export.Desktop.Tests/PataNext.Export.Desktop.Tests.csproj +++ b/PataNext.Export.Desktop.Tests/PataNext.Export.Desktop.Tests.csproj @@ -14,7 +14,7 @@ - + diff --git a/PataNext.Export.Desktop.Tests/Visual/GithubChangelogScene.cs b/PataNext.Export.Desktop.Tests/Visual/GithubChangelogScene.cs new file mode 100644 index 00000000..a225b2d2 --- /dev/null +++ b/PataNext.Export.Desktop.Tests/Visual/GithubChangelogScene.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Net; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Testing; +using osuTK; +using PataNext.Export.Desktop.Visual; +using PataNext.Export.Desktop.Visual.Dependencies; + +namespace PataNext.Export.Desktop.Tests.Visual +{ + public class GithubChangelogScene : TestScene + { + [BackgroundDependencyLoader] + private void load() + { + var changelog = new HomeChangelogControl {Size = Vector2.One, RelativeSizeAxes = Axes.Both}; + Child = changelog; + + var provider = new WebChangelogProvider(new("https://raw.githubusercontent.com/guerro323/patanext/master/CHANGELOG.md"), Scheduler); + provider.Current.BindValueChanged(ev => + { + if (ev.NewValue == null) + return; + + changelog.Set("test", ev.NewValue); + }); + } + } +} \ No newline at end of file diff --git a/PataNext.Export.Desktop.Tests/Visual/LauncherScene.cs b/PataNext.Export.Desktop.Tests/Visual/LauncherScene.cs index 4c600fc5..e19a2cec 100644 --- a/PataNext.Export.Desktop.Tests/Visual/LauncherScene.cs +++ b/PataNext.Export.Desktop.Tests/Visual/LauncherScene.cs @@ -1,7 +1,10 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Drawing; using System.Linq; +using GameHost.Game; +using GameHost.IO; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -15,15 +18,19 @@ using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Framework.Logging; +using osu.Framework.Platform; using osu.Framework.Platform.Windows; using osu.Framework.Screens; using osu.Framework.Testing; using osu.Framework.Threading; using osuTK; using PataNext.Export.Desktop.Visual; +using PataNext.Export.Desktop.Visual.Configuration; using PataNext.Export.Desktop.Visual.Dependencies; using PataNext.Export.Desktop.Visual.Screens; using PataNext.Export.Desktop.Visual.Screens.Section; +using SharpInputSystem; +using SharpInputSystem.DirectX; namespace PataNext.Export.Desktop.Tests.Visual { @@ -39,278 +46,60 @@ protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnl { return dependencies = new(base.CreateChildDependencies(parent)); } - - public LauncherScene() - { - //var t = typeof(ClientSectionScreen); - - /*Add(new Box - { - Anchor = Anchor.TopCentre, - Shear = new(0.5f, 0), - Size = new Vector2(120, 60) - });*/ - - /*RelativeSizeAxes = Axes.None; - Size = new(1920, 1080);*/ - } + [Resolved] private TextureStore textures { get; set; } [BackgroundDependencyLoader] - private void load() + private void load(Storage storage) { - Sidebar sidebar; - HomeLogoVisual logo; - - ScreenStack screenStack; - dependencies.CacheAs(new CurrentVersion() { Current = {Value = "2021.04.15"} }); - dependencies.CacheAs(new UpdatePatchDownload(Scheduler) + dependencies.CacheAs(new PatchProvider(Scheduler) { + }); + dependencies.CacheAs(new NotificationsProvider()); + dependencies.CacheAs(new WebChangelogProvider(new("https://raw.githubusercontent.com/guerro323/patanext/master/CHANGELOG.md"), Scheduler)); + dependencies.Cache(new LauncherConfigurationManager(storage)); + + var gameBootstrap = new GameBootstrap(); // we don't need to have a full bootstrap here, we just need some dependencies data + gameBootstrap.GameEntity.Set(new GameName("PataNext")); + gameBootstrap.GameEntity.Set(new GameUserStorage(new LocalStorage(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "/PataNext"))); + + var inputManager = InputManager.CreateInputSystem(typeof(DirectXInputManagerFactory), new ParameterList + { + new("WINDOW", Process.GetCurrentProcess().MainWindowHandle) }); - dependencies.CacheAs(new GlobalNotifications()); + dependencies.CacheAs(inputManager); + + gameBootstrap.Setup(); + + dependencies.Cache(gameBootstrap); - Add(new DrawSizePreservingFillContainer() + Add(new LauncherMainScene() { /*Strategy = DrawSizePreservationStrategy.Minimum, FillAspectRatio = 16 / 9f, FillMode = FillMode.Fit,*/ - - Children = new Drawable[] - { - new Box - { - Size = Vector2.One, - RelativeSizeAxes = Axes.Both, - Colour = Colour4.FromHex("be5a7f"), - }, - new Sprite - { - Size = Vector2.One, - RelativeSizeAxes = Axes.Both, - Colour = Colour4.White, - - Scale = new(1.03f), - Position = new(0, -31f), - - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - FillMode = FillMode.Fill, - - Texture = textures.Get("patanext_background_2") - }, - - new DrawSizePreservingFillContainer - { - RelativeSizeAxes = Axes.Both, - Size = new Vector2(0.97f, 0.8f), - - Origin = Anchor.TopCentre, - Anchor = Anchor.TopCentre, - - RelativePositionAxes = Axes.Both, - RelativeAnchorPosition = new Vector2(0.5f, 0.2f), - - Child = screenStack = new() - { - Size = new(1), - RelativeSizeAxes = Axes.Both - } - }, - - /*new DrawSizePreservingFillContainer - { - RelativeSizeAxes = Axes.X, - Size = new(1, 500f), - - Position = new(0, 50), - - Origin = Anchor.BottomCentre, - Anchor = Anchor.BottomCentre, - - FillAspectRatio = 16f / 9f, - FillMode = FillMode.Stretch, - - Children = new Drawable[] - { - new NotificationPopup - { - FillAspectRatio = 906f / 89f, - FillMode = FillMode.Fit, - - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - - RelativeSizeAxes = Axes.Both, - Size = new(0.47f, 0.1f), - - Position = new(0, 65), - - Font = new FontUsage("ar_cena", size: 21.5f), - - Title = "Information", - Text = "Update \"2021.04.15b\" is available!" - }, - - playBarControl = new ProgressPlayBarControl - { - FillAspectRatio = 1303f / 125f, - FillMode = FillMode.Fit, - - RelativeSizeAxes = Axes.Both, - Size = new(0.68f, 0.1425f), - - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - }, - } - },*/ - - new Container() - { - Origin = Anchor.TopCentre, - Anchor = Anchor.TopCentre, - - RelativeSizeAxes = Axes.Both, - Size = new(1, 0.25f), - - Children = new Drawable[] - { - sidebar = new Sidebar() - { - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - Padding = new() {Left = 16}, - - RelativeSizeAxes = Axes.X, - Size = new(0.38f, 43f), - }, - logo = new HomeLogoVisual(textures.Get("patanext_logo"), textures.Get("patanext_logo_blur")) - { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - - Size = new Vector2(358, 218), - Position = new Vector2(0, -20) - }, - new Sidebar() - { - Origin = Anchor.CentreRight, - Anchor = Anchor.CentreRight, - Padding = new() {Right = 16}, - - RelativeSizeAxes = Axes.X, - Size = new(0.38f, 43f), - - Flip = true, - } - } - } - } }); - sidebar.Menu.AddItem(new(textures.Get("sidebar_settings"), "Settings", () => new())); - sidebar.Menu.AddItem(new(textures.Get("sidebar_changelogs"), "Changelogs", () => - { - var screen = new HomeChangelogScreen(); - screen.Control.Set("2021.04.15", new() - { - { - "2021.04.15", new[] - { - "#Launcher", - "+ New launcher visual overhaul", - "+ Inputs Settings Interface", - "#Online", - "+ Connection to MasterServer", - } - }, - { - "2021.03.10", new[] - { - "+ Some random new stuff added" - } - }, - { - "2021.03.09", new[] - { - "+ New launcher visual overhaul", - "+ Connection to MasterServer", - "+ Inputs Settings Interface" - } - }, - { - "2021.03.08", new[] - { - "+ Some random new stuff added" - } - }, - { - "2021.03.07", new[] - { - "+ New launcher visual overhaul", - "+ Connection to MasterServer", - "+ Inputs Settings Interface" - } - }, - { - "2021.03.06", new[] - { - "+ Some random new stuff added" - } - } - }); - - return screen; - })); - - sidebar.Menu.SelectFirstTabByDefault = false; - - sidebar.Menu.Current.SetDefault(); - - sidebar.Menu.Current.BindValueChanged(ev => - { - if (screenStack.CurrentScreen is not null) - screenStack.Exit(); - - if (ev.NewValue is { } entry) - { - // todo: some other stuff - - logo.Active.Value = false; - - screenStack.Push(ev.NewValue.ScreenFactory()); - } - else - { - logo.Active.Value = true; - - var homePlay = new HomePlayScreen(); - screenStack.Push(homePlay); - } - }, true); - - logo.Action = () => { sidebar.Menu.Current.SetDefault(); }; - /*n.Clicked += () => { Logger.Log("launch update!", LoggingTarget.Information, LogLevel.Important); };*/ - AddToggleStep("IsUpdate", b => Dependencies.Get().IsUpdating.Value = b); - AddSliderStep("UpdateProgress", 0, 1, 0f, f => Dependencies.Get().Progress.Value = f); + AddToggleStep("IsUpdate", b => Dependencies.Get().IsUpdating.Value = b); + AddSliderStep("UpdateProgress", 0, 1, 0f, f => Dependencies.Get().Progress.Value = f); AddStep("Add Update", () => { - var notifications = Dependencies.Get(); + var notifications = Dependencies.Get(); - var updateNotification = new NotificationPopup + var updateNotification = new Notification { Title = "Information", Text = "Update Available!", @@ -319,7 +108,7 @@ private void load() updateNotification.Action = () => { - var updatePatch = Dependencies.Get(); + var updatePatch = Dependencies.Get(); updatePatch.StartDownload(); notifications.Remove(updateNotification); @@ -335,7 +124,7 @@ class CurrentVersion : ICurrentVersion public Bindable Current { get; } = new(); } - class UpdatePatchDownload : IUpdatePatchDownload + class PatchProvider : IPatchProvider { public Bindable Version { get; } = new(); public BindableBool IsUpdating { get; } = new(); @@ -344,7 +133,7 @@ class UpdatePatchDownload : IUpdatePatchDownload private Scheduler scheduler; - public UpdatePatchDownload(Scheduler scheduler) + public PatchProvider(Scheduler scheduler) { this.scheduler = scheduler; } @@ -370,13 +159,13 @@ public void UpdateAndRestart() } } - class GlobalNotifications : IGlobalNotifications + class NotificationsProvider : INotificationsProvider { - private List notifications = new(); + private List notifications = new(); - public IReadOnlyList GetAll() => notifications; + public IReadOnlyList GetAll() => notifications; - public void Push(NotificationBase notification) + public void Push(Notification notification) { notifications.Add(notification); OnNotificationAdded?.Invoke(notification); @@ -396,13 +185,13 @@ public void Clear(Type type) notifications.RemoveAll(n => n.GetType().IsSubclassOf(type)); } - public void Remove(NotificationBase notification) + public void Remove(Notification notification) { if (notifications.Remove(notification)) OnNotificationRemoved?.Invoke(notification); } - public event Action OnNotificationAdded; - public event Action OnNotificationRemoved; + public event Action OnNotificationAdded; + public event Action OnNotificationRemoved; } } \ No newline at end of file diff --git a/PataNext.Export.Desktop/BringIntegratedClientInFrontSystem.cs b/PataNext.Export.Desktop/BringIntegratedClientInFrontSystem.cs index 5f0dccec..a007d20f 100644 --- a/PataNext.Export.Desktop/BringIntegratedClientInFrontSystem.cs +++ b/PataNext.Export.Desktop/BringIntegratedClientInFrontSystem.cs @@ -1,5 +1,6 @@ using System; using System.Runtime.InteropServices; +using System.Threading; using GameHost.Applications; using GameHost.Core.Client; using GameHost.Core.Ecs; @@ -33,16 +34,16 @@ public override bool CanUpdate() protected override void OnUpdate() { base.OnUpdate(); - return; // quite buggy - if (timeBeforeEnablingMainWindow > 0 + // quite buggy + /*if (timeBeforeEnablingMainWindow > 0 && timeBeforeEnablingMainWindow < Environment.TickCount) { timeBeforeEnablingMainWindow = -1; SetFocus(IntPtr.Zero); EnableWindow(visualHwnd.Value, false); return; - } + }*/ visualHwnd = World.Mgr.Get()[0]; foreach (var gameClient in World.Mgr.Get()) @@ -54,30 +55,56 @@ protected override void OnUpdate() { if (visualHwnd.ShowIntegratedWindows) { - SetWindowPos(gameClient.Hwnd, IntPtr.Zero, 0, 0, visualHwnd.Size.X, visualHwnd.Size.Y, (uint) 0); + SetWindowPos(gameClient.Hwnd, IntPtr.Zero, 0, 0,visualHwnd.Size.X, visualHwnd.Size.Y, (uint) 0); SetFocus(IntPtr.Zero); SetFocus(visualHwnd.Value); EnableWindow(visualHwnd.Value, true); + + SendMessage(gameClient.Hwnd, WM_ACTIVATE, WA_ACTIVE, 0); + SendMessage(visualHwnd.Value, WM_ACTIVATE, WA_INACTIVE, 0); } else { DeactivateClientWindow(gameClient.Hwnd); - SetWindowPos(gameClient.Hwnd, IntPtr.Zero, -visualHwnd.Size.X, 0, visualHwnd.Size.X, visualHwnd.Size.Y, (uint) 0); - SetFocus(visualHwnd.Value); + //SetWindowPos(gameClient.Hwnd, IntPtr.Zero, -visualHwnd.Size.X, 0, visualHwnd.Size.X, visualHwnd.Size.Y, (uint) 0x0010); + ShowWindow(gameClient.Hwnd, 0); + ShowWindow(visualHwnd.Value, 0); + EnableWindow(visualHwnd.Value, true); gameClient.IsHwndInFront = false; + + SendMessage(visualHwnd.Value, WM_ACTIVATE, WA_ACTIVE, 0); + + Console.WriteLine("disable!"); + + visualHwnd.RequireSwap = true; + World.Mgr.Get()[0] = visualHwnd; } } if (gameClient.IsHwndInFront || !visualHwnd.ShowIntegratedWindows) continue; + Thread.Sleep(100); + EnumChildWindows(visualHwnd.Value, (hwnd, lparam) => { gameClient.Hwnd = hwnd; gameClient.IsHwndInFront = true; - ActivateClientWindow(gameClient.Hwnd); + /*ActivateClientWindow(gameClient.Hwnd); + EnableWindow(gameClient.Hwnd, true); + SetFocus(hwnd);*/ + //ActivateClientWindow(hwnd); + SendMessage(hwnd, 6, 1, 0); return 0; }, IntPtr.Zero); + + SetWindowPos(gameClient.Hwnd, IntPtr.Zero, 0, 0, 10, 10, (uint) 0); + ActivateClientWindow(gameClient.Hwnd); + EnableWindow(gameClient.Hwnd, true); + SetFocus(gameClient.Hwnd); + + SetWindowPos(gameClient.Hwnd, IntPtr.Zero, 0, 0, 10, 10, (uint) 0); + ShowWindow(gameClient.Hwnd, 1); } } @@ -88,23 +115,25 @@ private void ActivateClientWindow(IntPtr hwnd) EnableWindow(hwnd, true); EnableWindow(visualHwnd.Value, false); SetFocus(visualHwnd.Value); - SendMessage(hwnd, WM_ACTIVATE, WA_ACTIVE, IntPtr.Zero); + SendMessage(hwnd, WM_ACTIVATE, WA_ACTIVE, 0); } private void DeactivateClientWindow(IntPtr hwnd) { - SendMessage(hwnd, WM_ACTIVATE, WA_INACTIVE, IntPtr.Zero); + SendMessage(hwnd, WM_ACTIVATE, WA_INACTIVE, 0); } private const int WM_ACTIVATE = 0x0006; - private readonly IntPtr WA_ACTIVE = new IntPtr(1); - private readonly IntPtr WA_INACTIVE = new IntPtr(0); + private readonly int WA_ACTIVE = 1; + private readonly int WA_INACTIVE = 0; [DllImport("User32.dll")] static extern bool SetWindowPos(IntPtr hdwd, IntPtr insertAfter, int x, int y, int width, int height, uint flags); [DllImport("User32.dll")] static extern bool EnableWindow(IntPtr hdwd, bool value); [DllImport("User32.dll")] + static extern IntPtr SetActiveWindow (IntPtr hdwd); + [DllImport("User32.dll")] static extern IntPtr SetFocus(IntPtr hdwn); internal delegate int WindowEnumProc(IntPtr hwnd, IntPtr lparam); @@ -112,9 +141,12 @@ private void DeactivateClientWindow(IntPtr hwnd) internal static extern bool EnumChildWindows(IntPtr hwnd, WindowEnumProc func, IntPtr lParam); [DllImport("user32.dll")] - static extern int SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam); + static extern int SendMessage(IntPtr hWnd, int msg, int wParam, int lParam); [DllImport("user32.dll")] static extern bool ShowWindow(IntPtr hWnd, int msg); + + [DllImport("user32.dll")] + static extern bool HideWindow(IntPtr hWnd, int msg); } } \ No newline at end of file diff --git a/PataNext.Export.Desktop/Features/AddLauncherProvidersFromSimulationFeature.cs b/PataNext.Export.Desktop/Features/AddLauncherProvidersFromSimulationFeature.cs new file mode 100644 index 00000000..bf11f9df --- /dev/null +++ b/PataNext.Export.Desktop/Features/AddLauncherProvidersFromSimulationFeature.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using GameHost.Core.Ecs; +using GameHost.Injection; +using GameHost.Simulation.Application; +using GameHost.Worlds; +using PataNext.Export.Desktop.Providers; +using PataNext.Export.Desktop.Visual.Dependencies; + +namespace PataNext.Export.Desktop +{ + [RestrictToApplication(typeof(SimulationApplication))] + public class AddLauncherProvidersFromSimulationFeature : AppSystem + { + private GlobalWorld globalWorld; + + public AddLauncherProvidersFromSimulationFeature(WorldCollection collection) : base(collection) + { + DependencyResolver.Add(() => ref globalWorld); + } + + protected override void OnDependenciesResolved(IEnumerable dependencies) + { + base.OnDependenciesResolved(dependencies); + + var accountProvider = new ContextBindingStrategy(Context, true).Resolve(); + accountProvider?.SetBackend(globalWorld.Scheduler, World); + } + } +} \ No newline at end of file diff --git a/PataNext.Export.Desktop/Features/AddMasterServerFeature.cs b/PataNext.Export.Desktop/Features/AddMasterServerFeature.cs index a68920b9..a4fa1c61 100644 --- a/PataNext.Export.Desktop/Features/AddMasterServerFeature.cs +++ b/PataNext.Export.Desktop/Features/AddMasterServerFeature.cs @@ -28,10 +28,10 @@ public AddMasterServerFeature(WorldCollection collection) : base(collection) AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true); - collection.Mgr.CreateEntity().Set(new MasterServerFeature("http://localhost:5000")); + collection.Mgr.CreateEntity().Set(new MasterServerFeature("http://82.65.166.186:5000")); - World.Mgr.CreateEntity().Set(new DisconnectUserRequest("12345689")); - World.Mgr.CreateEntity().Set(ConnectUserRequest.ViaLogin("guerro323", "337117463")); + /*World.Mgr.CreateEntity().Set(new DisconnectUserRequest("12345689")); + World.Mgr.CreateEntity().Set(ConnectUserRequest.ViaLogin("guerro323", "337117463"));*/ return; diff --git a/PataNext.Export.Desktop/Features/AddReceiveGameHostClientFeature.cs b/PataNext.Export.Desktop/Features/AddReceiveGameHostClientFeature.cs index 6367fd40..5fcc541f 100644 --- a/PataNext.Export.Desktop/Features/AddReceiveGameHostClientFeature.cs +++ b/PataNext.Export.Desktop/Features/AddReceiveGameHostClientFeature.cs @@ -58,7 +58,7 @@ protected override void OnUpdate() base.OnUpdate(); if (startGameHostListener.Server.Value == null) return; - + var server = startGameHostListener.Server.Value; var visualHwnd = World.Mgr.Get()[0].Value; foreach (var ent in launchSet.GetEntities()) @@ -101,7 +101,7 @@ protected override void OnUpdate() var gameClient = new GameClient { ProcessId = process.Id, - IsHwndIntegrated = false // + IsHwndIntegrated = true // }; var clientEntity = World.Mgr.CreateEntity(); clientEntity.Set(gameClient); @@ -153,13 +153,13 @@ class KeyboardListener : IKeyboardListener { public bool KeyPressed(KeyEventArgs e) { - Console.WriteLine($"{e.Key} ({e.Text}) pressed!"); + //Console.WriteLine($"{e.Key} ({e.Text}) pressed!"); return true; } public bool KeyReleased(KeyEventArgs e) { - Console.WriteLine($"{e.Key} ({e.Text}) released!"); + //Console.WriteLine($"{e.Key} ({e.Text}) released!"); return true; } } diff --git a/PataNext.Export.Desktop/PataNext.Export.Desktop.csproj b/PataNext.Export.Desktop/PataNext.Export.Desktop.csproj index 725d477e..f37adf97 100644 --- a/PataNext.Export.Desktop/PataNext.Export.Desktop.csproj +++ b/PataNext.Export.Desktop/PataNext.Export.Desktop.csproj @@ -1,7 +1,7 @@ WinExe - 2020.816.1709 + 2021.505.1700 PataNext PataNext guerro @@ -9,7 +9,7 @@ game.ico game.ico true - 2020.816.1709 + 2021.505.1700 PataNext PataNext https://github.com/guerro323/patanext/blob/master/PataNext.Export.Desktop/game.ico @@ -36,8 +36,10 @@ - + + + diff --git a/PataNext.Export.Desktop/Program.cs b/PataNext.Export.Desktop/Program.cs index acfc05d7..b591ccaf 100644 --- a/PataNext.Export.Desktop/Program.cs +++ b/PataNext.Export.Desktop/Program.cs @@ -84,7 +84,8 @@ static async Task Main(string[] args) gameBootstrap.GameEntity.Set(typeof(Feature.RhythmEngineAudio.CustomModule)); gameBootstrap.GameEntity.Set(typeof(PataNext.Game.Module)); gameBootstrap.GameEntity.Set(typeof(PataNext.Game.Client.Resources.Module)); - + + Console.WriteLine("getting clients at " + clientDirectory.FullName); foreach (var clientData in clientDirectory.GetFiles("*.json", SearchOption.TopDirectoryOnly)) { var client = JsonSerializer.Deserialize(File.ReadAllText(clientData.FullName)); @@ -95,7 +96,8 @@ static async Task Main(string[] args) var clientBootstrap = gameBootstrap.Global.World.CreateEntity(); clientBootstrap.Set(client); - + + Console.WriteLine($"found {client.ExecutablePath} ({clientData.Name})"); if (launchClientJson != string.Empty && clientData.FullName == new FileInfo(launchClientJson).FullName) { gameBootstrap.Global.World.CreateEntity().Set(new LaunchClient(clientBootstrap)); diff --git a/PataNext.Export.Desktop/Providers/StandardAccountProvider.cs b/PataNext.Export.Desktop/Providers/StandardAccountProvider.cs new file mode 100644 index 00000000..d9aefdb4 --- /dev/null +++ b/PataNext.Export.Desktop/Providers/StandardAccountProvider.cs @@ -0,0 +1,103 @@ +using System; +using DefaultEcs; +using GameHost.Core.Ecs; +using GameHost.Core.Threading; +using GameHost.Injection; +using GameHost.Simulation.Application; +using GameHost.Threading; +using osu.Framework.Bindables; +using PataNext.Export.Desktop.Visual; +using PataNext.Export.Desktop.Visual.Dependencies; +using StormiumTeam.GameBase.Network.MasterServer.StandardAuthService; +using StormiumTeam.GameBase.Network.MasterServer.User; +using StormiumTeam.GameBase.Network.MasterServer.UserService; +using StormiumTeam.GameBase.Network.MasterServer.Utility; + +namespace PataNext.Export.Desktop.Providers +{ + public class StandardAccountProvider : IAccountProvider, IHasDiscordAccountSupport + { + private readonly INotificationsProvider notifications; + + public StandardAccountProvider(INotificationsProvider notifications) + { + this.notifications = notifications; + } + + public Bindable Current { get; } = new(); + + public void ConnectTraditional(string login, string password) + { + simulationScheduler.Schedule(args => + { + args.CreateEntity().Set(ConnectUserRequest.ViaLogin(login, password)); + }, simulationWc.Mgr, SchedulingParametersWithArgs.AsOnce); + + Console.WriteLine("1."); + simulationScheduler.Schedule(args => + { + Console.WriteLine("2."); + /*RequestUtility.New(simulationWc.Mgr, ConnectUserRequest.ViaLogin(args.login, args.password)) + .GetAsync() + .ContinueWithScheduler(uiScheduler, error => + { + Current.Value = new() + { + Error = true, + Message = error.Message + }; + + notifications.Push(new() + { + Title = "Connection Error!", + Text = error.Message, + Action = () => { } + }); + });*/ + }, (simulationWc.Mgr, login, password), SchedulingParametersWithArgs.AsOnce); + } + + public void Disconnect() + { + simulationScheduler.Schedule(args => { args.CreateEntity().Set(new DisconnectUserRequest()); }, simulationWc.Mgr, SchedulingParametersWithArgs.AsOnce); + } + + public void ConnectDiscord() + { + throw new NotImplementedException("not yet implemented"); + } + + private WorldCollection simulationWc; + private IScheduler simulationScheduler; + + private IScheduler uiScheduler; + + internal void SetBackend(IScheduler uiScheduler, WorldCollection simulationWc) + { + this.uiScheduler = uiScheduler; + + this.simulationWc = simulationWc; + this.simulationScheduler = new ContextBindingStrategy(simulationWc.Ctx, true).Resolve(); + + simulationWc.Mgr.SubscribeComponentChanged((in Entity _, in CurrentUser _, in CurrentUser curr) => + { + if (curr.Value.Token is null) + { + uiScheduler.Schedule(() => {Current.SetDefault();}, default); + return; + } + + RequestUtility.New(simulationWc.Mgr, new GetUserLoginRequest(curr.Value.Representation)) + .GetAsync() + .ContinueWithScheduler(uiScheduler, result => + { + Current.Value = new LauncherAccount + { + IsConnected = true, + Nickname = result.Login + }; + }); + }); + } + } +} \ No newline at end of file diff --git a/PataNext.Export.Desktop/Updater/GameUpdater.cs b/PataNext.Export.Desktop/Updater/GameUpdater.cs index f3b9f1ee..6e19a678 100644 --- a/PataNext.Export.Desktop/Updater/GameUpdater.cs +++ b/PataNext.Export.Desktop/Updater/GameUpdater.cs @@ -1,6 +1,8 @@ using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Graphics.Containers; +using PataNext.Export.Desktop.Visual.Configuration; +using PataNext.Export.Desktop.Visual.Dependencies; using PataNext.Export.Desktop.Visual.Overlays; namespace PataNext.Game.Updater @@ -8,8 +10,14 @@ namespace PataNext.Game.Updater public abstract class GameUpdater : CompositeDrawable { [Resolved] - protected NotificationOverlay Notifications { get; private set; } - + protected INotificationsProvider Notifications { get; private set; } + + [Resolved] + protected IPatchProvider Patch { get; private set; } + + [Resolved] + protected LauncherConfigurationManager Configuration { get; private set; } + protected override void LoadComplete() { base.LoadComplete(); diff --git a/PataNext.Export.Desktop/Updater/SquirrelUpdater.cs b/PataNext.Export.Desktop/Updater/SquirrelUpdater.cs index 9c3190bd..b007bcf9 100644 --- a/PataNext.Export.Desktop/Updater/SquirrelUpdater.cs +++ b/PataNext.Export.Desktop/Updater/SquirrelUpdater.cs @@ -1,4 +1,4 @@ -/*using System; +using System; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -8,12 +8,16 @@ using osu.Framework.Logging; using osuTK; using osuTK.Graphics; -using PataNext.Export.Desktop.Visual.Overlays; +using PataNext.Export.Desktop.Visual; +using PataNext.Export.Desktop.Visual.Configuration; using PataNext.Game.Updater; using Squirrel; namespace PataNext.Export.Desktop.Updater { + public class UpdateNotification : Notification {} + public class InstallingNotification : Notification {} + public class SquirrelUpdater : GameUpdater { private UpdateManager updateManager; @@ -21,11 +25,56 @@ public class SquirrelUpdater : GameUpdater private static readonly Logger logger = Logger.GetLogger("updater"); public override async Task CheckForUpdate() => await checkForUpdate(true); - - private UpdateProgressNotification notification; public Task PrepareUpdateAsync() => UpdateManager.RestartAppWhenExited(); + private async Task startUpdate(UpdateInfo info) + { + try + { + Schedule(() => Notifications.Clear(typeof(InstallingNotification))); + + Schedule(() => Patch.Progress.Value = 0); + + logger.Add("Update Found!"); + await updateManager.DownloadReleases(info.ReleasesToApply, p => Schedule(() => Patch.Progress.Value = p / 100f)); + + Schedule(() => Patch.Progress.Value = 0); + + Schedule(() => Notifications.Push(new InstallingNotification() + { + Title = "Installing Update", + Text = "Update is being installed, don't restart the game..." + })); + + + await updateManager.ApplyReleases(info, p => Schedule(() => Patch.Progress.Value = p / 100f)); + + Schedule(() => Notifications.Clear(typeof(InstallingNotification))); + Schedule(() => Patch.IsUpdating.Value = false); + Schedule(() => Patch.RequireRestart.Value = true); + logger.Add("Finished"); + } + catch (Exception ex) + { + if (info.FutureReleaseEntry.IsDelta) + { + await checkForUpdate(false); + return; + } + + Schedule(() => + { + Notifications.Push(new() + { + Title = "Error", + Text = "Update failed, reinstalling the game may help to fix this issue." + }); + }); + Logger.Error(ex, "Update failed!"); + } + } + private async Task checkForUpdate(bool deltaPatching = true) { var rescheduleRecheck = true; @@ -44,24 +93,24 @@ private async Task checkForUpdate(bool deltaPatching = true) return; } - if (notification == null) + Schedule(() => { - notification = new UpdateProgressNotification(this) {State = ProgressNotificationState.Active}; - Schedule(() => Notifications.Post(notification)); - } - - notification.Progress = 0; - notification.Text = @"Downloading update..."; - - logger.Add("Update Found!"); - await updateManager.DownloadReleases(info.ReleasesToApply, p => notification.Progress = p / 100f); - - notification.Progress = 0; - notification.Text = @"Installing update..."; - - await updateManager.ApplyReleases(info, p => notification.Progress = p / 100f); - notification.State = ProgressNotificationState.Completed; - logger.Add("Finished"); + Notifications.Clear(typeof(UpdateNotification)); + Notifications.Push(new UpdateNotification() + { + Title = "Information", + Text = $"Update to '{info.FutureReleaseEntry.Version}' is available!", + Action = () => + { + Patch.Version.Value = info.FutureReleaseEntry.Version.ToString(); + Patch.IsUpdating.Value = true; + + Notifications.Clear(typeof(UpdateNotification)); + + Task.Run(async () => await startUpdate(info)); + } + }); + }); } catch (Exception ex) { @@ -74,8 +123,6 @@ private async Task checkForUpdate(bool deltaPatching = true) } else { - notification.State = ProgressNotificationState.Cancelled; - notification.Text = "Update failed :(\nRe-Downloading the full setup is recommended."; Logger.Error(ex, "Update failed!"); } } @@ -101,10 +148,11 @@ protected override void Dispose(bool isDisposing) private async Task getUpdateManager() { - return await UpdateManager.GitHubUpdateManager(@"https://github.com/guerro323/patanext", "PataNext"); + var isPreRelease = Configuration.Get(LauncherSetting.UpdateChannel) == UpdateChannel.Beta; + return await UpdateManager.GitHubUpdateManager(@"https://github.com/guerro323/patanext", "PataNext", prerelease: isPreRelease); } - private class UpdateProgressNotification : ProgressNotification + /*private class UpdateProgressNotification : ProgressNotification { private readonly SquirrelUpdater updateManager; private VisualGame game; @@ -150,6 +198,6 @@ private void load(VisualGame game) } }); } - } + }*/ } -}*/ \ No newline at end of file +} \ No newline at end of file diff --git a/PataNext.Export.Desktop/Visual/Dependencies/IAccountProvider.cs b/PataNext.Export.Desktop/Visual/Dependencies/IAccountProvider.cs new file mode 100644 index 00000000..c58b0225 --- /dev/null +++ b/PataNext.Export.Desktop/Visual/Dependencies/IAccountProvider.cs @@ -0,0 +1,27 @@ +using osu.Framework.Bindables; + +namespace PataNext.Export.Desktop.Visual.Dependencies +{ + public struct LauncherAccount + { + public bool IsConnected; + + public string Nickname; + + public bool Error; + public string Message; + } + + public interface IAccountProvider + { + public Bindable Current { get; } + + void ConnectTraditional(string login, string password); + void Disconnect(); + } + + public interface IHasDiscordAccountSupport + { + void ConnectDiscord(); + } +} \ No newline at end of file diff --git a/PataNext.Export.Desktop/Visual/Dependencies/IChangelogProvider.cs b/PataNext.Export.Desktop/Visual/Dependencies/IChangelogProvider.cs new file mode 100644 index 00000000..0cebdf40 --- /dev/null +++ b/PataNext.Export.Desktop/Visual/Dependencies/IChangelogProvider.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using System.Net; +using osu.Framework.Bindables; +using osu.Framework.Threading; + +namespace PataNext.Export.Desktop.Visual.Dependencies +{ + public interface IChangelogProvider + { + public Bindable> Current { get; } + } + + public class WebChangelogProvider : IChangelogProvider + { + public Bindable?> Current { get; } = new(); + + private readonly Scheduler scheduler; + private readonly Uri uri; + + public WebChangelogProvider(Uri uri, Scheduler scheduler) + { + this.scheduler = scheduler; + this.uri = uri; + + fetch(); + } + + private void fetch() + { + using var client = new WebClient(); + client.DownloadStringTaskAsync(uri) + .ContinueWith(str => + { + parse(str.Result); + + scheduler.AddDelayed(fetch, 30_000); + }); + } + + private void parse(string raw) + { + var map = new Dictionary(); + var inVersion = false; + + string version = ""; + + var list = new List(); + foreach (var txt in raw.Split('\n')) + { + if (txt.Length > 2 && txt[0] == '#' && txt[1] == ' ') + { + if (inVersion) + { + map[version] = list.ToArray(); + list.Clear(); + } + + inVersion = true; + version = txt[2..]; + continue; + } + + if (!inVersion) + continue; + + list.Add(txt); + } + + if (list.Count > 0) + map[version] = list.ToArray(); + + scheduler.AddOnce(() => Current.Value = map); + } + } +} \ No newline at end of file diff --git a/PataNext.Export.Desktop/Visual/Dependencies/IGlobalNotifications.cs b/PataNext.Export.Desktop/Visual/Dependencies/IGlobalNotifications.cs deleted file mode 100644 index 84240a8e..00000000 --- a/PataNext.Export.Desktop/Visual/Dependencies/IGlobalNotifications.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace PataNext.Export.Desktop.Visual.Dependencies -{ - public interface IGlobalNotifications - { - IReadOnlyList GetAll(); - - void Push(NotificationBase notification); - - void ClearAll(); - void Clear(Type type); - - void Remove(NotificationBase notification); - - event Action OnNotificationAdded; - event Action OnNotificationRemoved; - } -} \ No newline at end of file diff --git a/PataNext.Export.Desktop/Visual/Dependencies/INotificationsProvider.cs b/PataNext.Export.Desktop/Visual/Dependencies/INotificationsProvider.cs new file mode 100644 index 00000000..503f78f1 --- /dev/null +++ b/PataNext.Export.Desktop/Visual/Dependencies/INotificationsProvider.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; + +namespace PataNext.Export.Desktop.Visual.Dependencies +{ + public interface INotificationsProvider + { + IReadOnlyList GetAll(); + + void Push(Notification notification); + + void ClearAll(); + void Clear(Type type); + + void Remove(Notification notification); + + event Action OnNotificationAdded; + event Action OnNotificationRemoved; + } +} \ No newline at end of file diff --git a/PataNext.Export.Desktop/Visual/Dependencies/IUpdatePatchDownload.cs b/PataNext.Export.Desktop/Visual/Dependencies/IPatchProvider.cs similarity index 88% rename from PataNext.Export.Desktop/Visual/Dependencies/IUpdatePatchDownload.cs rename to PataNext.Export.Desktop/Visual/Dependencies/IPatchProvider.cs index 171042bb..e5043cf8 100644 --- a/PataNext.Export.Desktop/Visual/Dependencies/IUpdatePatchDownload.cs +++ b/PataNext.Export.Desktop/Visual/Dependencies/IPatchProvider.cs @@ -2,7 +2,7 @@ namespace PataNext.Export.Desktop.Visual.Dependencies { - public interface IUpdatePatchDownload + public interface IPatchProvider { Bindable Version { get; } diff --git a/PataNext.Export.Desktop/Visual/GameHostApplicationRunner.cs b/PataNext.Export.Desktop/Visual/GameHostApplicationRunner.cs index c371761e..f6f6e385 100644 --- a/PataNext.Export.Desktop/Visual/GameHostApplicationRunner.cs +++ b/PataNext.Export.Desktop/Visual/GameHostApplicationRunner.cs @@ -15,9 +15,10 @@ namespace PataNext.Export.Desktop.Visual { public struct VisualHWND { - public IntPtr Value; + public IntPtr Value; public Vector2I Size; - public bool ShowIntegratedWindows; + public bool ShowIntegratedWindows; + public bool RequireSwap { get; set; } } public class GameHostApplicationRunner : Drawable diff --git a/PataNext.Export.Desktop/Visual/Launcher/Configuration/LauncherConfigurationManager.cs b/PataNext.Export.Desktop/Visual/Launcher/Configuration/LauncherConfigurationManager.cs new file mode 100644 index 00000000..90513cde --- /dev/null +++ b/PataNext.Export.Desktop/Visual/Launcher/Configuration/LauncherConfigurationManager.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using osu.Framework.Configuration; +using osu.Framework.Platform; + +namespace PataNext.Export.Desktop.Visual.Configuration +{ + public class LauncherConfigurationManager : IniConfigManager + { + protected override void InitialiseDefaults() + { + SetDefault(LauncherSetting.UpdateChannel, UpdateChannel.Beta); + + base.InitialiseDefaults(); + + + } + + public LauncherConfigurationManager(Storage storage, IDictionary defaultOverrides = null) : base(storage, defaultOverrides) + { + } + } + + public enum LauncherSetting + { + UpdateChannel + } +} \ No newline at end of file diff --git a/PataNext.Export.Desktop/Visual/Launcher/Configuration/UpdateChannel.cs b/PataNext.Export.Desktop/Visual/Launcher/Configuration/UpdateChannel.cs new file mode 100644 index 00000000..ef558bc7 --- /dev/null +++ b/PataNext.Export.Desktop/Visual/Launcher/Configuration/UpdateChannel.cs @@ -0,0 +1,9 @@ +namespace PataNext.Export.Desktop.Visual.Configuration +{ + public enum UpdateChannel + { + Invalid, + Release, + Beta + } +} \ No newline at end of file diff --git a/PataNext.Export.Desktop/Visual/Launcher/HomeChangelogControl.cs b/PataNext.Export.Desktop/Visual/Launcher/HomeChangelogControl.cs index 0f4e568b..56e20a25 100644 --- a/PataNext.Export.Desktop/Visual/Launcher/HomeChangelogControl.cs +++ b/PataNext.Export.Desktop/Visual/Launcher/HomeChangelogControl.cs @@ -67,7 +67,7 @@ public void Set(string currentVersion, Dictionary changelogVer if (line.StartsWith("##")) // header { logs.AddText("\n", text => text.Font = new FontUsage("ar_cena", 5)); - logs.AddParagraph(" " + line[1..], text => + logs.AddParagraph(" " + line[2..], text => { text.Font = new("ar_cena", 30f); text.Shadow = true; diff --git a/PataNext.Export.Desktop/Visual/Launcher/HomePlayScreen.cs b/PataNext.Export.Desktop/Visual/Launcher/HomePlayScreen.cs deleted file mode 100644 index 73bd4fc7..00000000 --- a/PataNext.Export.Desktop/Visual/Launcher/HomePlayScreen.cs +++ /dev/null @@ -1,126 +0,0 @@ -using System; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Textures; -using osu.Framework.Screens; -using osuTK; -using PataNext.Export.Desktop.Visual.Dependencies; - -namespace PataNext.Export.Desktop.Visual -{ - public class HomePlayScreen : Screen - { - public readonly ProgressPlayBarControl ProgressPlayBar; - - private FillFlowContainer flow; - - public HomePlayScreen() - { - AddRangeInternal(new Drawable[] - { - flow = new() - { - Margin = new() {Bottom = 30}, - - Size = new(1), - RelativeSizeAxes = Axes.Both, - - Spacing = new Vector2(0, 20), - Direction = FillDirection.Vertical, - - Origin = Anchor.BottomCentre, - Anchor = Anchor.BottomCentre, - - Children = new Drawable[] - { - ProgressPlayBar = new() - { - FillAspectRatio = 1303f / 125f, - FillMode = FillMode.Fit, - - RelativeSizeAxes = Axes.Both, - Size = new(0.68f, 0.1425f), - - Origin = Anchor.BottomCentre, - Anchor = Anchor.BottomCentre, - } - } - } - }); - - ProgressPlayBar.PlayAction = () => - { - if (ProgressPlayBar.RequireRestart.Value && Dependencies.TryGet(out IUpdatePatchDownload patch)) - Scheduler.AddOnce(() => patch.UpdateAndRestart()); - else if (!ProgressPlayBar.RequireRestart.Value) - { - Console.WriteLine("launch game"); - } - }; - } - - public void PushPopup(string title, string description, Action action, Texture texture = null) - { - flow.Add(new NotificationPopup - { - FillAspectRatio = 906f / 89f, - FillMode = FillMode.Fit, - - Origin = Anchor.BottomCentre, - Anchor = Anchor.BottomCentre, - - RelativeSizeAxes = Axes.Both, - Size = new(0.47f, 0.1f), - - Font = new("ar_cena", size: 21.5f), - - Title = title, - Text = description, - Icon = texture - }); - } - - [Resolved] - private IGlobalNotifications notifications { get; set; } - - [BackgroundDependencyLoader] - private void load(IUpdatePatchDownload updatePatch, ICurrentVersion currentVersion) - { - updatePatch.IsUpdating.BindValueChanged(ev => ProgressPlayBar.IsUpdating.Value = ev.NewValue, true); - updatePatch.Progress.BindValueChanged(ev => ProgressPlayBar.UpdateProgress.Value = ev.NewValue, true); - - updatePatch.RequireRestart.BindValueChanged(ev => - { - ProgressPlayBar.RequireRestart.Value = ev.NewValue; - }, true); - - currentVersion.Current.BindValueChanged(ev => ProgressPlayBar.CurrentVersion.Value = ev.NewValue, true); - - notifications.OnNotificationAdded += addNotification; - } - - private void addNotification(NotificationBase n) - { - if (!(n is NotificationPopup popup)) - return; - - popup.FillAspectRatio = 906f / 89f; - popup.FillMode = FillMode.Fit; - - popup.RelativeSizeAxes = Axes.Both; - popup.Size = new(0.47f, 0.1f); - - popup.Origin = Anchor.BottomCentre; - popup.Anchor = Anchor.BottomCentre; - flow.Add(popup); - } - - protected override void Dispose(bool isDisposing) - { - notifications.OnNotificationAdded -= addNotification; - - base.Dispose(isDisposing); - } - } -} \ No newline at end of file diff --git a/PataNext.Export.Desktop/Visual/Launcher/LauncherMainScene.cs b/PataNext.Export.Desktop/Visual/Launcher/LauncherMainScene.cs new file mode 100644 index 00000000..37e4e97f --- /dev/null +++ b/PataNext.Export.Desktop/Visual/Launcher/LauncherMainScene.cs @@ -0,0 +1,211 @@ +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Framework.Platform; +using osu.Framework.Screens; +using osuTK; + +namespace PataNext.Export.Desktop.Visual +{ + public class LauncherMainScene : Screen + { + [Resolved] + private TextureStore textures { get; set; } + + private Sidebar sidebar; + private HomeLogoVisual logo; + + private ScreenStack screenStack; + + [BackgroundDependencyLoader] + private void load(Storage storage) + { + AddInternal(new DrawSizePreservingFillContainer() + { + Children = new Drawable[] + { + new Box + { + Size = Vector2.One, + RelativeSizeAxes = Axes.Both, + Colour = Colour4.FromHex("be5a7f"), + }, + new Sprite + { + Size = Vector2.One, + RelativeSizeAxes = Axes.Both, + Colour = Colour4.White, + + Scale = new(1.03f), + Position = new(0, -31f), + + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + FillMode = FillMode.Fill, + + Texture = textures.Get("patanext_background_2") + }, + + new DrawSizePreservingFillContainer + { + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.97f, 0.8f), + + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + + RelativePositionAxes = Axes.Both, + RelativeAnchorPosition = new Vector2(0.5f, 0.2f), + + Child = screenStack = new() + { + Size = new(1), + RelativeSizeAxes = Axes.Both + } + }, + + new Container() + { + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + + RelativeSizeAxes = Axes.Both, + Size = new(1, 0.25f), + + Children = new Drawable[] + { + sidebar = new Sidebar() + { + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + Padding = new() {Left = 16}, + + RelativeSizeAxes = Axes.X, + Size = new(0.38f, 43f), + }, + logo = new HomeLogoVisual(textures.Get("patanext_logo"), textures.Get("patanext_logo_blur")) + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + + Size = new Vector2(358, 218), + Position = new Vector2(0, -20) + }, + new Sidebar() + { + Origin = Anchor.CentreRight, + Anchor = Anchor.CentreRight, + Padding = new() {Right = 16}, + + RelativeSizeAxes = Axes.X, + Size = new(0.38f, 43f), + + Flip = true, + } + } + } + } + }); + + initStuff(); + } + + private void initStuff() + { + sidebar.Add(new SidebarAccountDropdown + { + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + + Size = new(250, 1), + RelativeSizeAxes = Axes.Y + }); + + sidebar.Menu.AddItem(new(textures.Get("sidebar_settings"), "Settings", () => new HomeSettingsScreen())); + sidebar.Menu.AddItem(new(textures.Get("sidebar_changelogs"), "Changelogs", () => + { + var screen = new HomeChangelogScreen(); + screen.Control.Set("2021.04.15", new() + { + { + "2021.04.15", new[] + { + "#Launcher", + "+ New launcher visual overhaul", + "+ Inputs Settings Interface", + "#Online", + "+ Connection to MasterServer", + } + }, + { + "2021.03.10", new[] + { + "+ Some random new stuff added" + } + }, + { + "2021.03.09", new[] + { + "+ New launcher visual overhaul", + "+ Connection to MasterServer", + "+ Inputs Settings Interface" + } + }, + { + "2021.03.08", new[] + { + "+ Some random new stuff added" + } + }, + { + "2021.03.07", new[] + { + "+ New launcher visual overhaul", + "+ Connection to MasterServer", + "+ Inputs Settings Interface" + } + }, + { + "2021.03.06", new[] + { + "+ Some random new stuff added" + } + } + }); + + return screen; + })); + + sidebar.Menu.SelectFirstTabByDefault = false; + + sidebar.Menu.Current.SetDefault(); + + sidebar.Menu.Current.BindValueChanged(ev => + { + if (screenStack.CurrentScreen is not null) + screenStack.Exit(); + + if (ev.NewValue is { } entry) + { + // todo: some other stuff + + logo.Active.Value = false; + + screenStack.Push(ev.NewValue.ScreenFactory()); + } + else + { + logo.Active.Value = true; + + var homePlay = new HomePlayScreen(); + screenStack.Push(homePlay); + } + }, true); + + logo.Action = () => { sidebar.Menu.Current.SetDefault(); }; + } + } +} \ No newline at end of file diff --git a/PataNext.Export.Desktop/Visual/Launcher/NotificationBase.cs b/PataNext.Export.Desktop/Visual/Launcher/NotificationBase.cs index c7e6ec19..91a7711a 100644 --- a/PataNext.Export.Desktop/Visual/Launcher/NotificationBase.cs +++ b/PataNext.Export.Desktop/Visual/Launcher/NotificationBase.cs @@ -1,4 +1,5 @@ -using osu.Framework.Graphics.Textures; +using System; +using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.UserInterface; using osu.Framework.Localisation; @@ -9,5 +10,25 @@ public abstract class NotificationBase : Button public abstract LocalisableString Title { get; set; } public abstract LocalisableString Text { get; set; } public abstract Texture Icon { get; set; } + + public Notification Source { get; } + + public NotificationBase(Notification notification) + { + Source = notification; + } + } + + public class Notification + { + public LocalisableString Title { get; set; } + public LocalisableString Text { get; set; } + public Texture Icon { get; set; } + public Action Action { get; set; } + + public virtual NotificationBase ProvideDrawable() + { + return null; + } } } \ No newline at end of file diff --git a/PataNext.Export.Desktop/Visual/Launcher/NotificationPopup.cs b/PataNext.Export.Desktop/Visual/Launcher/NotificationPopup.cs index 761de58b..d31e352c 100644 --- a/PataNext.Export.Desktop/Visual/Launcher/NotificationPopup.cs +++ b/PataNext.Export.Desktop/Visual/Launcher/NotificationPopup.cs @@ -18,7 +18,7 @@ public class NotificationPopup : NotificationBase private SpriteText title = new() { Origin = Anchor.TopLeft, - Font = new("ar_cena", size: 34f), + Font = new("ar_cena", size: 21.5f), Text = "Information", Colour = Colour4.FromRGBA(0xd53535ff) @@ -27,7 +27,7 @@ public class NotificationPopup : NotificationBase private SpriteText text = new() { Origin = Anchor.TopLeft, - Font = new("ar_cena", size: 34f), + Font = new("ar_cena", size: 21.5f), Text = "Update \"2021.04.15b\" is available!", Colour = Colour4.FromRGBA(0x25110aff) @@ -168,5 +168,9 @@ protected override bool OnClick(ClickEvent e) return base.OnClick(e); } + + public NotificationPopup(Notification notification) : base(notification) + { + } } } \ No newline at end of file diff --git a/PataNext.Export.Desktop/Visual/Launcher/ProgressPlayBarControl.cs b/PataNext.Export.Desktop/Visual/Launcher/ProgressPlayBarControl.cs index c852a9c0..d3d768bb 100644 --- a/PataNext.Export.Desktop/Visual/Launcher/ProgressPlayBarControl.cs +++ b/PataNext.Export.Desktop/Visual/Launcher/ProgressPlayBarControl.cs @@ -58,6 +58,11 @@ public ProgressPlayBarControl() Anchor = Anchor.CentreRight, } }; + + CurrentVersion.BindValueChanged(ev => + { + playButton.CurrentVersion.Value = ev.NewValue; + }); IsUpdating.BindValueChanged(ev => { @@ -163,6 +168,8 @@ public class PlayButton : Button { public Bindable CurrentVersion = new(); + private SpriteText versionText; + private SpriteText[] texts = new SpriteText[3]; private SpriteText[] effects = new SpriteText[2]; private Container effectContainer; @@ -202,6 +209,22 @@ public PlayButton() Position = new(0, 0) }, + + versionText = new SpriteText + { + Size = new(1), + RelativeSizeAxes = Axes.Both, + + Origin = Anchor.BottomRight, + Anchor = Anchor.BottomRight, + + Font = new("ar_cena", size: 18f), + Text = "2021.05.04.1659", + + Colour = Colour4.White.Opacity(0.4f), + + Position = new Vector2(5) + }, effectContainer = new() { @@ -267,6 +290,11 @@ public PlayButton() t.Current.Value = ev.NewValue.ToString(); } }); + + CurrentVersion.BindValueChanged(ev => + { + versionText.Text = string.IsNullOrEmpty(ev.NewValue) ? "?" : ev.NewValue; + }, true); } protected override bool OnHover(HoverEvent e) diff --git a/PataNext.Export.Desktop/Visual/Launcher/HomeChangelogScreen.cs b/PataNext.Export.Desktop/Visual/Launcher/Screens/HomeChangelogScreen.cs similarity index 60% rename from PataNext.Export.Desktop/Visual/Launcher/HomeChangelogScreen.cs rename to PataNext.Export.Desktop/Visual/Launcher/Screens/HomeChangelogScreen.cs index 22c788aa..ec4548c4 100644 --- a/PataNext.Export.Desktop/Visual/Launcher/HomeChangelogScreen.cs +++ b/PataNext.Export.Desktop/Visual/Launcher/Screens/HomeChangelogScreen.cs @@ -1,20 +1,24 @@ -using osu.Framework.Graphics; +using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Screens; +using PataNext.Export.Desktop.Visual.Dependencies; namespace PataNext.Export.Desktop.Visual { public class HomeChangelogScreen : Screen { public readonly HomeChangelogControl Control; - + public HomeChangelogScreen() { Masking = true; CornerRadius = 8; - + AddRangeInternal(new Drawable[] { new Box {Size = new(1), RelativeSizeAxes = Axes.Both, Colour = Colour4.Black.Opacity(0.5f)}, @@ -51,12 +55,12 @@ public HomeChangelogScreen() }, new Container { - Margin = new() {Horizontal = 40, Top = 11}, + Padding = new() {Horizontal = 40, Top = 11}, Masking = true, Size = new(1, 1), RelativeSizeAxes = Axes.Both, - + Child = Control = new() { Size = new(1, 1), @@ -67,5 +71,35 @@ public HomeChangelogScreen() } }); } + + private ICurrentVersion currentVersion; + private IChangelogProvider changelogProvider; + + private void onChangelogChange(ValueChangedEvent> ev) + { + if (ev.NewValue == null) + { + Control.Set(currentVersion.Current.Value, new()); + return; + } + + Control.Set(currentVersion.Current.Value, ev.NewValue); + } + + [BackgroundDependencyLoader] + private void load(ICurrentVersion currentVersion, IChangelogProvider changelogProvider) + { + this.currentVersion = currentVersion; + this.changelogProvider = changelogProvider; + + changelogProvider.Current.BindValueChanged(onChangelogChange, true); + } + + protected override void Dispose(bool isDisposing) + { + changelogProvider.Current.ValueChanged -= onChangelogChange; + + base.Dispose(isDisposing); + } } } \ No newline at end of file diff --git a/PataNext.Export.Desktop/Visual/Launcher/Screens/HomePlayScreen.cs b/PataNext.Export.Desktop/Visual/Launcher/Screens/HomePlayScreen.cs new file mode 100644 index 00000000..91416e1f --- /dev/null +++ b/PataNext.Export.Desktop/Visual/Launcher/Screens/HomePlayScreen.cs @@ -0,0 +1,170 @@ +using System; +using GameHost.Core.Client; +using GameHost.Game; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Textures; +using osu.Framework.Screens; +using osuTK; +using PataNext.Export.Desktop.Visual.Dependencies; + +namespace PataNext.Export.Desktop.Visual +{ + public class HomePlayScreen : Screen + { + public readonly ProgressPlayBarControl ProgressPlayBar; + + private FillFlowContainer flow; + + [Resolved] + private GameBootstrap gameBootstrap { get; set; } + + public HomePlayScreen() + { + AddRangeInternal(new Drawable[] + { + flow = new() + { + Margin = new() {Bottom = 30}, + + Size = new(1), + RelativeSizeAxes = Axes.Both, + + Spacing = new Vector2(0, 20), + Direction = FillDirection.Vertical, + + Origin = Anchor.BottomCentre, + Anchor = Anchor.BottomCentre, + + Children = new Drawable[] + { + ProgressPlayBar = new() + { + FillAspectRatio = 1303f / 125f, + FillMode = FillMode.Fit, + + RelativeSizeAxes = Axes.Both, + Size = new(0.68f, 0.1425f), + + Origin = Anchor.BottomCentre, + Anchor = Anchor.BottomCentre, + } + } + } + }); + + ProgressPlayBar.PlayAction = () => + { + if (ProgressPlayBar.RequireRestart.Value && Dependencies.TryGet(out IPatchProvider patch)) + Scheduler.AddOnce(() => patch.UpdateAndRestart()); + else if (!ProgressPlayBar.RequireRestart.Value) + { + Console.WriteLine("launch game"); + + var world = gameBootstrap.Global.World; + foreach (var entity in world) + { + if (!entity.Has()) + continue; + + Console.WriteLine("create"); + var launch = world.CreateEntity(); + launch.Set(new LaunchClient(entity)); + break; + } + } + }; + } + + public void PushPopup(string title, string description, Action action, Texture texture = null, Notification notification = null) + { + flow.Add(new NotificationPopup(notification) + { + FillAspectRatio = 906f / 89f, + FillMode = FillMode.Fit, + + Origin = Anchor.BottomCentre, + Anchor = Anchor.BottomCentre, + + RelativeSizeAxes = Axes.Both, + Size = new(0.47f, 0.1f), + + Font = new("ar_cena", size: 21.5f), + + Title = title, + Text = description, + Icon = texture, + + Action = action + }); + } + + [Resolved] + private INotificationsProvider NotificationsProvider { get; set; } + + [BackgroundDependencyLoader] + private void load(IPatchProvider patch, ICurrentVersion currentVersion) + { + patch.IsUpdating.BindValueChanged(ev => ProgressPlayBar.IsUpdating.Value = ev.NewValue, true); + patch.Progress.BindValueChanged(ev => ProgressPlayBar.UpdateProgress.Value = ev.NewValue, true); + + patch.RequireRestart.BindValueChanged(ev => + { + ProgressPlayBar.RequireRestart.Value = ev.NewValue; + }, true); + + currentVersion.Current.BindValueChanged(ev => ProgressPlayBar.CurrentVersion.Value = ev.NewValue, true); + + NotificationsProvider.OnNotificationAdded += addNotification; + NotificationsProvider.OnNotificationRemoved += removeNotification; + foreach (var notification in NotificationsProvider.GetAll()) + { + addNotification(notification); + } + } + + private void removeNotification(Notification n) + { + Console.WriteLine("remove " + n.Title); + foreach (var drawable in flow.Children) + { + if (drawable is NotificationPopup notificationDrawable && notificationDrawable.Source == n) + { + Schedule(() => flow.Remove(drawable)); + } + } + } + + private void addNotification(Notification n) + { + var provided = n.ProvideDrawable(); + if (provided is { } and not NotificationPopup) + return; + + if (provided == null) + { + PushPopup(n.Title.ToString(), n.Text.ToString(), n.Action, n.Icon, n); + return; + } + + provided.FillAspectRatio = 906f / 89f; + provided.FillMode = FillMode.Fit; + + provided.RelativeSizeAxes = Axes.Both; + provided.Size = new(0.47f, 0.1f); + + provided.Origin = Anchor.BottomCentre; + provided.Anchor = Anchor.BottomCentre; + flow.Add(provided); + } + + protected override void Dispose(bool isDisposing) + { + NotificationsProvider.OnNotificationAdded -= addNotification; + NotificationsProvider.OnNotificationRemoved -= removeNotification; + + base.Dispose(isDisposing); + } + } +} \ No newline at end of file diff --git a/PataNext.Export.Desktop/Visual/Launcher/Screens/HomeSettingsScreen.cs b/PataNext.Export.Desktop/Visual/Launcher/Screens/HomeSettingsScreen.cs new file mode 100644 index 00000000..70ed1db8 --- /dev/null +++ b/PataNext.Export.Desktop/Visual/Launcher/Screens/HomeSettingsScreen.cs @@ -0,0 +1,172 @@ +using System; +using System.Collections.Generic; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Containers.Markdown; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Screens; +using osuTK; +using PataNext.Export.Desktop.Visual.Overlays; +using PataNext.Export.Desktop.Visual.SettingsScreen; + +namespace PataNext.Export.Desktop.Visual +{ + public class HomeSettingsScreen : Screen + { + public readonly ScreenStack ScreenStack = new() + { + Origin = Anchor.TopRight, + Anchor = Anchor.TopRight, + + RelativeSizeAxes = Axes.Both, + Size = new(0.95f, 1), + }; + + public readonly SettingsControl Control = new() + { + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + + Size = new(SettingsControl.WIDTH, 1), + RelativeSizeAxes = Axes.Y, + + Margin = new() {Top = 60}, + }; + + private readonly Container backContainer; + + private void goBack() + { + ScreenStack.Exit(); + } + + public HomeSettingsScreen() + { + Masking = true; + CornerRadius = 8; + + /*mainFlow = new AlwaysUpdateFillFlowContainer + { + Size = new(WIDTH, 800), + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + + Direction = FillDirection.Vertical, + + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + + Spacing = new(0, 30) + };*/ + + AddRangeInternal(new Drawable[] + { + new Box {Size = new(1), RelativeSizeAxes = Axes.Both, Colour = Colour4.FromRGBA(0xffedd2ff)}, + new Container + { + Size = new(1, 50), + RelativeSizeAxes = Axes.X, + + Children = new Drawable[] + { + new SpriteText + { + Origin = Anchor.Centre, Anchor = Anchor.Centre, + + Text = "Settings", + Font = new("mojipon"), + Colour = Colour4.FromRGBA(0x25110aff) + } + } + }, + Control, + new Container + { + Size = new(1), + RelativeSizeAxes = Axes.Both, + + Padding = new() {Top = 60}, + + Child = ScreenStack + }, + backContainer = new() + { + RelativeSizeAxes = Axes.Both, + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + + Size = new (0.05f, 1f), + Scale = new (0, 1), + + Children = new Drawable[] + { + new BasicButton() + { + BackgroundColour = Colour4.FromRGBA(0x25110aff), + + Size = new Vector2(1), + RelativeSizeAxes = Axes.Both, + Action = () => goBack() + }, + new Box() + { + RelativeSizeAxes = Axes.Both, + Size = new(0.15f, 1), + + Origin = Anchor.CentreRight, + Anchor = Anchor.CentreRight, + + Colour = Colour4.Black.Opacity(0.5f) + }, + new SpriteIcon + { + Icon = FontAwesome.Solid.Backward, + Size = new(0.4f), + RelativeSizeAxes = Axes.Both, + Colour = Colour4.Black.Opacity(0.5f), + + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + + X = -0, + Y = 5 + }, + new SpriteIcon + { + Icon = FontAwesome.Solid.Backward, + Size = new(0.4f), + RelativeSizeAxes = Axes.Both, + Colour = Colour4.FromRGBA(0xffedd2ff), + + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + + X = -5 + } + } + } + }); + + Control.AddButton("Launcher", new SettingsButtonHome {Title = "Updates", Action = () => ScreenStack.Push(new SettingsScreenUpdate())}); + Control.AddButton("Launcher", new SettingsButtonHome {Title = "Directory"}); + Control.AddButton("Game", new SettingsButtonHome {Title = "Inputs", Action = () => ScreenStack.Push(new SettingsScreenInput())}); + /*Control.AddButton("Game", new SettingsButtonHome {Title = "Rhythm Engine"}); + Control.AddButton("Game", new SettingsButtonHome {Title = "Networking"}); + Control.AddButton("Discord", new SettingsButtonHome {Title = "Rich Presence"}); + Control.AddButton("Discord", new SettingsButtonHome {Title = "MasterServer Auto-Connect"});*/ + + ScreenStack.ScreenPushed += (screen, newScreen) => + { + Control.Alpha = 0; + backContainer.ScaleTo(new Vector2(1, 1), 100); + }; + ScreenStack.ScreenExited += (screen, newScreen) => + { + Control.Alpha = 1; + backContainer.ScaleTo(new Vector2(0, 1), 100); + }; + } + } +} \ No newline at end of file diff --git a/PataNext.Export.Desktop/Visual/Launcher/SettingsButtonBase.cs b/PataNext.Export.Desktop/Visual/Launcher/SettingsButtonBase.cs new file mode 100644 index 00000000..45c0c655 --- /dev/null +++ b/PataNext.Export.Desktop/Visual/Launcher/SettingsButtonBase.cs @@ -0,0 +1,12 @@ +using osu.Framework.Graphics.Textures; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Localisation; + +namespace PataNext.Export.Desktop.Visual +{ + public abstract class SettingsButtonBase : Button + { + public abstract LocalisableString Title { get; set; } + public abstract Texture Icon { get; set; } + } +} \ No newline at end of file diff --git a/PataNext.Export.Desktop/Visual/Launcher/SettingsButtonHome.cs b/PataNext.Export.Desktop/Visual/Launcher/SettingsButtonHome.cs new file mode 100644 index 00000000..173a2e76 --- /dev/null +++ b/PataNext.Export.Desktop/Visual/Launcher/SettingsButtonHome.cs @@ -0,0 +1,78 @@ +using osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Framework.Input.Events; +using osu.Framework.Localisation; + +namespace PataNext.Export.Desktop.Visual +{ + public class SettingsButtonHome : SettingsButtonBase + { + private SpriteText titleSprite; + private Sprite iconSprite; + + public override LocalisableString Title + { + get => titleSprite.Text; + set => titleSprite.Text = value; + } + + public override Texture Icon + { + get => iconSprite.Texture; + set => iconSprite.Texture = value; + } + + private Box backgroundBox; + public SettingsButtonHome() + { + Size = new(1, 50); + + titleSprite = new() + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + + Padding = new() {Left = 20}, + + Font = new("ar_cena", 30), + Colour = Colour4.FromRGBA(0xffedd2ff) + }; + iconSprite = new(); + + Masking = true; + CornerRadius = 8; + AddRange(new Drawable[] + { + backgroundBox = new Box + { + Size = new(1), + RelativeSizeAxes = Axes.Both, + + //Colour = Colour4.FromRGBA(0x25110aff).Lighten(4f) + Colour = Colour4.FromRGBA(0xffedd2ff).Darken(0.2f) + }, + titleSprite + }); + + OnHoverLost(default); + } + + protected override bool OnHover(HoverEvent e) + { + backgroundBox.FadeColour(Colour4.FromRGBA(0x25110aff)); + titleSprite.FadeColour(Colour4.FromRGBA(0xffedd2ff)); + + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + backgroundBox.FadeColour(Colour4.FromRGBA(0xffedd2ff).Darken(0.2f)); + titleSprite.FadeColour(Colour4.FromRGBA(0x25110aff)); + + base.OnHoverLost(e); + } + } +} \ No newline at end of file diff --git a/PataNext.Export.Desktop/Visual/Launcher/SettingsControl.cs b/PataNext.Export.Desktop/Visual/Launcher/SettingsControl.cs new file mode 100644 index 00000000..07814f2d --- /dev/null +++ b/PataNext.Export.Desktop/Visual/Launcher/SettingsControl.cs @@ -0,0 +1,117 @@ +using System.Collections.Generic; +using GameHost.Injection; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osuTK; +using PataNext.Export.Desktop.Visual.Overlays; +using Container = osu.Framework.Graphics.Containers.Container; + +namespace PataNext.Export.Desktop.Visual +{ + public class SettingsControl : BasicScrollContainer + { + private Dictionary> flowCategories = new(); + + public const float WIDTH = 500; + + public readonly AlwaysUpdateFillFlowContainer MainFlow = new() + { + Size = new(1, 800), + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + + Direction = FillDirection.Vertical, + + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + + Spacing = new(0, 30) + }; + + public SettingsControl() + { + Masking = true; + Child = MainFlow; + } + + private void addCategory(string category, FillFlowContainer flow) + { + flow.Direction = FillDirection.Vertical; + flow.Anchor = Anchor.TopCentre; + flow.Origin = Anchor.TopCentre; + + flow.RelativeSizeAxes = Axes.X; + flow.Size = new Vector2(1, 0); + + flow.Spacing = new(0, 5); + + flow.AutoSizeAxes = Axes.Y; + + flow.Add(new Container + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + + Size = new(1, 20), + RelativeSizeAxes = Axes.X, + + Children = new Drawable[] + { + new SpriteText + { + Size = new(1f), + RelativeSizeAxes = Axes.Both, + + Text = category, + Font = new("ar_cena", 22), + Colour = Colour4.FromRGBA(0x25110aff), + + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + }, + new Box + { + Size = new(1f, 2f), + RelativeSizeAxes = Axes.X, + + Colour = Colour4.FromRGBA(0x25110aff), + + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + + Position = new(0, 11) + } + } + }); + + MainFlow.Add(flow); + } + + public FillFlowContainer GetOrCreateCategoryFlow(string category) + { + if (flowCategories.TryGetValue(category, out var flow)) + return flow; + + addCategory(category, flowCategories[category] = flow = new AlwaysUpdateFillFlowContainer()); + return flow; + } + + public void AddButton(string category, SettingsButtonBase button) + { + button.RelativeSizeAxes = Axes.X; + button.Size = new(1, button.Size.Y); + button.Anchor = Anchor.TopCentre; + button.Origin = Anchor.TopCentre; + GetOrCreateCategoryFlow(category) + .Add(button); + } + + public void AddDrawable(string category, Drawable drawable) + { + GetOrCreateCategoryFlow(category) + .Add(drawable); + } + } +} \ No newline at end of file diff --git a/PataNext.Export.Desktop/Visual/Launcher/SettingsScreen/SettingsScreenBase.cs b/PataNext.Export.Desktop/Visual/Launcher/SettingsScreen/SettingsScreenBase.cs new file mode 100644 index 00000000..0ce03f6f --- /dev/null +++ b/PataNext.Export.Desktop/Visual/Launcher/SettingsScreen/SettingsScreenBase.cs @@ -0,0 +1,68 @@ +using System; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Screens; + +namespace PataNext.Export.Desktop.Visual.SettingsScreen +{ + public class SettingsScreenBase : Screen + { + public const float WIDTH = 600; + + protected SettingsControl Control; + + public SettingsScreenBase() + { + Padding = new() {Horizontal = 40}; + + /*AddInternal(mainFlow);*/ + AddInternal(Control = new() + { + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + + Size = new(WIDTH, 1), + RelativeSizeAxes = Axes.Y, + }); + } + + protected void AddSetting(string name, Drawable drawable, float? height = null, string category = null) + { + drawable.Origin = Anchor.TopRight; + drawable.Anchor = Anchor.TopRight; + + var container = new Container + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + + Size = new(WIDTH, height ?? Math.Max(drawable.Size.Y, 24)), + Children = new[] + { + new SpriteText + { + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + Text = name, + + Font = new("ar_cena", size: 24), + Colour = Colour4.FromRGBA(0x25110aff), + + Size = new(1), + RelativeSizeAxes = Axes.Both + }, + drawable + } + }; + + if (category is null) + Control.MainFlow.Add(container); + else + { + Control.GetOrCreateCategoryFlow(category) + .Add(container); + } + } + } +} \ No newline at end of file diff --git a/PataNext.Export.Desktop/Visual/Launcher/SettingsScreen/SettingsScreenInput.cs b/PataNext.Export.Desktop/Visual/Launcher/SettingsScreen/SettingsScreenInput.cs new file mode 100644 index 00000000..9d70f5c5 --- /dev/null +++ b/PataNext.Export.Desktop/Visual/Launcher/SettingsScreen/SettingsScreenInput.cs @@ -0,0 +1,265 @@ +using System; +using System.IO; +using System.Linq; +using System.Text; +using GameHost.Core.IO; +using GameHost.Core.Modules; +using GameHost.Game; +using GameHost.Inputs.Systems; +using GameHost.IO; +using JetBrains.Annotations; +using Newtonsoft.Json; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input.Events; +using osu.Framework.Screens; +using osuTK; +using PataNext.Export.Desktop.Visual.Configuration; +using PataNext.Export.Desktop.Visual.Overlays; +using PataNext.Simulation.Client.Systems.Inputs; +using SharpInputSystem; + +namespace PataNext.Export.Desktop.Visual.SettingsScreen +{ + public class SettingsScreenInput : SettingsScreenBase + { + public SettingsScreenInput() + { + + } + + [CanBeNull] private IStorage inputStorage; + + + private InputManager inputManager; + private Keyboard kb; + + private DependencyContainer dependencies; + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) + { + return dependencies = new (parent); + } + + [BackgroundDependencyLoader(true)] + private void load([CanBeNull] GameBootstrap bootstrap, InputManager inputManager) + { + if (inputManager is null) + throw new NullReferenceException(nameof(inputManager)); + + this.inputManager = inputManager; + (kb = inputManager.CreateInputObject(true, "")).EventListener = new SharpDxInputSystem.KeyboardListenerSimple(); + + dependencies.Cache(kb.EventListener as SharpDxInputSystem.KeyboardListenerSimple); + + if (bootstrap != null) + { + var module = new PataNext.Simulation.Client.Module(default, bootstrap.Global.Context, new() + { + DisplayName = "TestModule", + Author = "Test", + NameId = typeof(PataNext.Simulation.Client.Module).Assembly.GetName().Name + }); + + module.Storage.Subscribe((_, curr) => + { + if (curr == null) + return; + inputStorage = new StorageCollection() + { + module.DllStorage, + curr, + }.GetOrCreateDirectoryAsync("Inputs").Result; + Scheduler.Add(() => createRhythm()); + }, true); + } + else + createRhythm(); + } + + [CanBeNull] + private IWriteFile getFile() + { + IFile readableFile = null; + IWriteFile writableFile = null; + if (inputStorage != null) + { + foreach (var file in inputStorage.GetFilesAsync($"{typeof(T).Name}.json").Result) + { + readableFile = file; + + if (file is IWriteFile) + writableFile = file as IWriteFile; + } + + // We need to create one + if (writableFile == null && readableFile != null) + { + var writableStorage = (inputStorage as StorageCollection)!.AllStorage.Last() as LocalStorage; + if (writableStorage == null) + throw new InvalidOperationException("the last storage should be a localstorage"); + + using var file = File.Create($"{writableStorage.CurrentPath}/{typeof(T).Name}.json"); + file.Write(readableFile.GetContentAsync().Result); + } + + if (writableFile == null && readableFile == null) + throw new InvalidOperationException("No input file found"); + } + + return writableFile; + } + + private T deserialize(byte[] data) + { + using var stream = new MemoryStream(data); + using var reader = new JsonTextReader(new StreamReader(stream)); + + var serializer = new JsonSerializer(); + return serializer.Deserialize(reader); + } + + private Bindable rhythmInputDescription = new(); + private void createRhythm() + { + var file = getFile(); + if (file is {} readableFile) + { + rhythmInputDescription.Value = deserialize(readableFile.GetContentAsync().Result); + } + + ReactiveInputButton provideButton(ReactiveInputButton.func getProperty) + { + return new() + { + GetProperty = getProperty, + Bindable = rhythmInputDescription, + + Size = new(200, 30), + Action = () => Console.WriteLine("woo") + }; + } + + var category = "Rhythm Session (Drums)"; + AddSetting("Pata (Left)", provideButton((ref RhythmInputDescription input) => ref input.PataKeys), category: category); + AddSetting("Pon (Right)", provideButton((ref RhythmInputDescription input) => ref input.PonKeys), category: category); + AddSetting("Don (Down)", provideButton((ref RhythmInputDescription input) => ref input.DonKeys), category: category); + AddSetting("Chaka (Up)", provideButton((ref RhythmInputDescription input) => ref input.ChakaKeys), category: category); + + category = "Rhythm Session (Abilities)"; + AddSetting("Horizontal", provideButton((ref RhythmInputDescription input) => ref input.Ability0Keys), category: category); + AddSetting("Up", provideButton((ref RhythmInputDescription input) => ref input.Ability1Keys), category: category); + AddSetting("Down", provideButton((ref RhythmInputDescription input) => ref input.Ability2Keys), category: category); + + category = "InGame (Camera)"; + AddSetting("Panning Left", provideButton((ref RhythmInputDescription input) => ref input.PanningNegativeKeys), category: category); + AddSetting("Panning Right", provideButton((ref RhythmInputDescription input) => ref input.PanningPositiveKeys), category: category); + + rhythmInputDescription.BindValueChanged(ev => + { + if (file is null) + return; + + file.WriteContentAsync(Encoding.Default.GetBytes(JsonConvert.SerializeObject(ev.NewValue))); + }); + } + + protected override void Update() + { + foreach (var kvp in (kb.EventListener as SharpDxInputSystem.KeyboardListenerSimple).ControlMap) + kvp.Value.IsPressed = false; + + kb.Capture(); + + base.Update(); + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + kb?.Dispose(); + } + } + + public class ReactiveInputButton : BasicButton + { + public delegate ref string[] func(ref TInput input); + + public Bindable Bindable; + + public func GetProperty; + + public ReactiveInputButton() + { + BackgroundColour = FrameworkColour.BlueGreen; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + Bindable.BindValueChanged(ev => + { + var v = Bindable.Value; + foreach (var p in GetProperty(ref v)) + { + Text = p; + break; + } + + isBinding = false; + BackgroundColour = FrameworkColour.BlueGreen; + }, true); + } + + private SharpDxInputSystem.KeyboardListenerSimple kb; + + [BackgroundDependencyLoader] + private void load(SharpDxInputSystem.KeyboardListenerSimple kb) + { + this.kb = kb; + } + + protected override void Update() + { + if (isBinding) + { + foreach (var (_, input) in kb.ControlMap) + { + if (input.IsPressed) + { + Console.WriteLine(input.Id); + + var v = Bindable.Value; + ref var array = ref GetProperty(ref v); + array = array.Length == 0 ? new string[1] : array.ToArray(); // clone (so that the bindable can get updated) + + array[0] = input.Id; + + Bindable.Value = v; + + isBinding = false; + break; + } + } + } + + base.Update(); + } + + private bool isBinding; + protected override bool OnClick(ClickEvent e) + { + Text = "Waiting for new binding"; + BackgroundColour = FrameworkColour.BlueGreenDark; + + isBinding = true; + return true; + } + } +} \ No newline at end of file diff --git a/PataNext.Export.Desktop/Visual/Launcher/SettingsScreen/SettingsScreenUpdate.cs b/PataNext.Export.Desktop/Visual/Launcher/SettingsScreen/SettingsScreenUpdate.cs new file mode 100644 index 00000000..3f24a58d --- /dev/null +++ b/PataNext.Export.Desktop/Visual/Launcher/SettingsScreen/SettingsScreenUpdate.cs @@ -0,0 +1,33 @@ +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Screens; +using osuTK; +using PataNext.Export.Desktop.Visual.Configuration; +using PataNext.Export.Desktop.Visual.Overlays; + +namespace PataNext.Export.Desktop.Visual.SettingsScreen +{ + public class SettingsScreenUpdate : SettingsScreenBase + { + [BackgroundDependencyLoader] + private void load(LauncherConfigurationManager config) + { + var dropdown = new BasicDropdown(); + dropdown.AddDropdownItem(UpdateChannel.Release); + dropdown.AddDropdownItem(UpdateChannel.Beta); + dropdown.Current.Value = config.GetBindable(LauncherSetting.UpdateChannel).Value; + dropdown.Width = 200; + + dropdown.Current.BindValueChanged(ev => + { + config.GetBindable(LauncherSetting.UpdateChannel).Value = ev.NewValue; + }); + + AddSetting("Update Channel", dropdown, 30, "Strategy"); + } + } +} \ No newline at end of file diff --git a/PataNext.Export.Desktop/Visual/Launcher/Sidebar.cs b/PataNext.Export.Desktop/Visual/Launcher/Sidebar.cs index 4c7dfbf7..65e92219 100644 --- a/PataNext.Export.Desktop/Visual/Launcher/Sidebar.cs +++ b/PataNext.Export.Desktop/Visual/Launcher/Sidebar.cs @@ -16,8 +16,8 @@ public Sidebar() { } - private const float Shearing = 0.35f; - private const float ShearingPos = Shearing * 60; + public const float Shearing = 0.35f; + public const float ShearingPos = Shearing * 60; private bool flip; public bool Flip diff --git a/PataNext.Export.Desktop/Visual/Launcher/SidebarAccountDropdown.cs b/PataNext.Export.Desktop/Visual/Launcher/SidebarAccountDropdown.cs new file mode 100644 index 00000000..b2a4567f --- /dev/null +++ b/PataNext.Export.Desktop/Visual/Launcher/SidebarAccountDropdown.cs @@ -0,0 +1,309 @@ +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input.Events; +using osu.Framework.Localisation; +using osuTK; +using PataNext.Export.Desktop.Visual.Dependencies; +using SharpInputSystem; + +namespace PataNext.Export.Desktop.Visual +{ + public class SidebarAccountDropdown : Container + { + private readonly Container background; + private readonly SpriteIcon dropdownIcon; + private readonly SpriteText nameText; + + private readonly Dropdown dropdown; + + public SidebarAccountDropdown() + { + Children = new Drawable[] + { + background = new Container + { + Masking = true, + + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + + Size = new (1), + RelativeSizeAxes = Axes.Both, + + Alpha = 0, + + Child = new Box() + { + Shear = new(-Sidebar.Shearing, 0), + Position = new(-Sidebar.ShearingPos, 0), + Colour = Colour4.Black, + + Size = new(1), + RelativeSizeAxes = Axes.Both + } + }, + + dropdown = new Dropdown() + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + + Size = new (1.5f, 1), + RelativeSizeAxes = Axes.Both, + }, + + new Container { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + + Size = new (12, 12), + Margin = new() {Left = 13}, + + Child = dropdownIcon = new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Icon = FontAwesome.Solid.AngleDown, + + Size = new(1), + RelativeSizeAxes = Axes.Both + } + }, + new Container + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + + Margin = new() {Left = 50}, + Size = new(1, 0.5f), + RelativeSizeAxes = Axes.Both, + + Child = nameText = new SpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Text = "Not Connected", + + Font = new("ar_cena", 29f), + + Size = new(1, 1), + RelativeSizeAxes = Axes.Both, + + Position = new(0, -4) + } + } + }; + + dropdown.Active.BindValueChanged(ev => + { + dropdownIcon.RotateTo(ev.NewValue ? 180 : 0, 300, Easing.InOutQuint); + }); + } + + protected override bool OnHover(HoverEvent e) + { + background.FadeTo(0.4f, 75); + + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + background.FadeTo(0, 75); + + base.OnHoverLost(e); + } + + private IAccountProvider accountProvider; + + [BackgroundDependencyLoader] + private void load(IAccountProvider accountProvider) + { + this.accountProvider = accountProvider; + + accountProvider.Current.BindValueChanged(onAccountChange, true); + } + + private void onAccountChange(ValueChangedEvent ev) + { + if (ev.NewValue.IsConnected) + { + nameText.Text = ev.NewValue.Nickname; + } + else + { + nameText.Text = "Not Connected"; + } + } + + protected override void Dispose(bool isDisposing) + { + if (accountProvider != null) + accountProvider.Current.ValueChanged -= onAccountChange; + + base.Dispose(isDisposing); + } + + class Dropdown : Container + { + public BindableBool Active { get; } = new(); + + private Container container; + + private FillFlowContainer connectContainer; + private FillFlowContainer disconnectContainer; + + private TextBox loginField, passwordField; + + public Dropdown() + { + Children = new Drawable[] + { + container = new() + { + Size = new Vector2(1, 200), + RelativeSizeAxes = Axes.X, + + Anchor = Anchor.BottomLeft, + Origin = Anchor.TopLeft, + + Children = new Drawable[] + { + new Box() + { + Size = new (1), + RelativeSizeAxes = Axes.Both, + + Colour = Colour4.Black.Opacity(0.9f) + }, + + connectContainer = new() + { + Size = new (1), + RelativeSizeAxes = Axes.Both, + + Direction = FillDirection.Vertical, + + Padding = new(10), + + Children = new Drawable[] + { + new SpriteText + { + Text = "Login", + Font = new("ar_cena", 25) + }, + loginField = new BasicTextBox + { + Size = new Vector2(1, 30), + RelativeSizeAxes = Axes.X + }, + new Sprite {Height = 10}, + new SpriteText + { + Text = "Password", + Font = new("ar_cena", 25) + }, + passwordField = new BasicPasswordTextBox() + { + Size = new Vector2(1, 30), + RelativeSizeAxes = Axes.X + }, + new Sprite {Height = 20}, + new BasicButton() + { + Size = new Vector2(1, 40), + RelativeSizeAxes = Axes.X, + + Text = "Connect", + + Action = connect + } + } + }, + + disconnectContainer = new() + { + Size = new (1), + RelativeSizeAxes = Axes.Both, + + Direction = FillDirection.Vertical, + + Padding = new(10), + + Children = new Drawable[] + { + new BasicButton() + { + Size = new Vector2(1, 40), + RelativeSizeAxes = Axes.X, + + Text = "Disconnect", + + BackgroundColour = Colour4.DarkRed, + + Action = disconnect + } + } + } + } + } + }; + + Active.BindValueChanged(ev => + { + container.FadeTo(ev.NewValue ? 1 : 0, 50); + }, true); + } + + private void connect() + { + accountProvider.ConnectTraditional(loginField.Text, passwordField.Text); + Active.Value = false; + } + + private void disconnect() + { + accountProvider.Disconnect(); + } + + protected override bool OnClick(ClickEvent e) + { + Active.Value = !Active.Value; + + return base.OnClick(e); + } + + [Resolved] + private IAccountProvider accountProvider { get; set; } + + protected override void LoadComplete() + { + base.LoadComplete(); + + accountProvider.Current.BindValueChanged(ev => + { + if (ev.NewValue.IsConnected) + { + connectContainer.Alpha = 0; + disconnectContainer.Alpha = 1; + + container.Height = 60; + } + else + { + connectContainer.Alpha = 1; + disconnectContainer.Alpha = 0; + + container.Height = 200; + } + }, true); + } + } + } +} \ No newline at end of file diff --git a/PataNext.Export.Desktop/Visual/Launcher/SidebarMenuTabControl.cs b/PataNext.Export.Desktop/Visual/Launcher/SidebarMenuTabControl.cs index 463f6dd5..cbaba908 100644 --- a/PataNext.Export.Desktop/Visual/Launcher/SidebarMenuTabControl.cs +++ b/PataNext.Export.Desktop/Visual/Launcher/SidebarMenuTabControl.cs @@ -134,8 +134,6 @@ protected override void OnActivated() backgroundBox.FadeTo(0.8f); iconSprite.FadeTo(1, 100f); outlineBox.FadeTo(1, 100f); - - Console.WriteLine("active!"); } protected override void OnDeactivated() diff --git a/PataNext.Export.Desktop/Visual/VisualGame.cs b/PataNext.Export.Desktop/Visual/VisualGame.cs index f464bb43..e792474f 100644 --- a/PataNext.Export.Desktop/Visual/VisualGame.cs +++ b/PataNext.Export.Desktop/Visual/VisualGame.cs @@ -1,8 +1,11 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Drawing; using System.Reflection; +using GameHost.Core.Ecs; using GameHost.Game; +using GameHost.Inputs.Systems; using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; @@ -10,13 +13,21 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Framework.IO.Stores; +using osu.Framework.Logging; using osu.Framework.Platform; using osu.Framework.Screens; using osuTK; using osuTK.Input; +using PataNext.Export.Desktop.Providers; +using PataNext.Export.Desktop.Updater; using PataNext.Export.Desktop.Visual; +using PataNext.Export.Desktop.Visual.Configuration; +using PataNext.Export.Desktop.Visual.Dependencies; using PataNext.Export.Desktop.Visual.Overlays; using PataNext.Export.Desktop.Visual.Screens; +using SharpInputSystem; +using SharpInputSystem.DirectX; +using Keyboard = SharpInputSystem.Keyboard; namespace PataNext.Export.Desktop { @@ -31,14 +42,11 @@ public VisualGame(GameBootstrap gameBootstrap) { this.gameBootstrap = gameBootstrap; } - - private DependencyContainer dependencies; - - protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) => - dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); + + private Keyboard kb; [BackgroundDependencyLoader] - private void load() + private void load(FrameworkConfigManager configManager, Storage storage) { IntPtr handle; handle = Window switch @@ -48,9 +56,41 @@ private void load() }; gameBootstrap.GameEntity.Set(new VisualHWND {Value = handle}); + dependencies.Cache(this); dependencies.Cache(gameBootstrap); dependencies.Cache(notifications); + + var notificationsProvider = new NotificationsProvider(); + var accountProvider = new StandardAccountProvider(notificationsProvider); + gameBootstrap.Global.Context.BindExisting(accountProvider); + + var version = Assembly.GetAssembly(typeof(VisualGame)).GetName().Version.ToString(); + if (version.EndsWith(".0")) + version = version[..^2]; + + dependencies.CacheAs(new CurrentVersion() + { + Current = {Value =version} + }); + dependencies.CacheAs(new PatchProvider(Scheduler, this) + { + + }); + dependencies.CacheAs(notificationsProvider); + dependencies.CacheAs(new WebChangelogProvider(new("https://raw.githubusercontent.com/guerro323/patanext/master/CHANGELOG.md"), Scheduler)); + dependencies.Cache(new LauncherConfigurationManager(storage)); + dependencies.CacheAs(accountProvider); + + var inputManager = InputManager.CreateInputSystem(typeof(DirectXInputManagerFactory), new ParameterList + { + new("WINDOW", Process.GetCurrentProcess().MainWindowHandle) + }); + dependencies.CacheAs(inputManager); + + (kb = inputManager.CreateInputObject(true, "")).EventListener = new SharpDxInputSystem.KeyboardListenerSimple(); + + configManager.GetBindable(FrameworkSetting.FrameSync).Value = FrameSync.VSync; } private DateTime begin; @@ -97,7 +137,7 @@ protected override void LoadComplete() { base.LoadComplete(); - //LoadComponentAsync(new SquirrelUpdater(), Add); + LoadComponentAsync(new SquirrelUpdater(), Add); begin = DateTime.Now; Child = new DrawSizePreservingFillContainer @@ -121,7 +161,7 @@ protected override void LoadComplete() }; Add(ScreenStack = new ScreenStack()); - ScreenStack.Push(new MainScreen()); + ScreenStack.Push(new LauncherMainScene()); } private bool showIntegrated = true; @@ -140,9 +180,18 @@ protected override void Update() OsuTKWindow tkWindow => tkWindow.WindowInfo.Handle }; + if (gameBootstrap.GameEntity.TryGet(out VisualHWND prev)) + { + if (prev.RequireSwap) + { + //(Window as SDL2DesktopWindow).Visible = false; + (Window as SDL2DesktopWindow).Visible = true; + } + } + gameBootstrap.GameEntity.Set(new VisualHWND { - Value = handle, + Value = Process.GetCurrentProcess().MainWindowHandle, Size = { X = Window.ClientSize.Width, @@ -150,15 +199,21 @@ protected override void Update() }, ShowIntegratedWindows = showIntegrated && begin.AddSeconds(2) < DateTime.Now }); - } - protected override bool OnKeyDown(KeyDownEvent e) - { - if (e.Key == Key.G && e.ControlPressed) + kb.Capture(); + + var listener = kb.EventListener as SharpDxInputSystem.KeyboardListenerSimple; + if (listener.ControlMap[KeyCode.Key_G].IsPressed && kb.IsShiftState(Keyboard.ShiftState.Ctrl)) { showIntegrated = !showIntegrated; } + foreach (var c in listener.ControlMap) + c.Value.IsPressed = false; + } + + protected override bool OnKeyDown(KeyDownEvent e) + { return base.OnKeyDown(e); } diff --git a/PataNext.Export.Desktop/Visual/VisualGameBase.cs b/PataNext.Export.Desktop/Visual/VisualGameBase.cs index ff64f522..08b8365c 100644 --- a/PataNext.Export.Desktop/Visual/VisualGameBase.cs +++ b/PataNext.Export.Desktop/Visual/VisualGameBase.cs @@ -1,20 +1,114 @@ using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.IO.Stores; +using osu.Framework.Platform; +using osu.Framework.Threading; +using PataNext.Export.Desktop.Visual.Configuration; +using PataNext.Export.Desktop.Visual.Dependencies; +using SharpInputSystem; +using SharpInputSystem.DirectX; +using Squirrel; namespace PataNext.Export.Desktop.Visual { public class VisualGameBase : osu.Framework.Game { + protected DependencyContainer dependencies; + + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) => dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); + [BackgroundDependencyLoader] - private void load() + private void load(Storage storage) { Resources.AddStore(new DllResourceStore(typeof(VisualGameBase).Assembly)); Resources.AddStore(new DllResourceStore(typeof(PataNext.Game.Client.Resources.Module).Assembly)); - + AddFont(Resources, @"Fonts/ar_cena"); AddFont(Resources, @"Fonts/mojipon"); AddFont(Resources, @"Fonts/roboto"); } + + protected class CurrentVersion : ICurrentVersion + { + public Bindable Current { get; } = new(); + } + + protected class PatchProvider : IPatchProvider + { + public Bindable Version { get; } = new(); + public BindableBool IsUpdating { get; } = new(); + public BindableBool RequireRestart { get; } = new(); + public BindableFloat Progress { get; } = new(); + + private Scheduler scheduler; + private VisualGame gameHost; + + public PatchProvider(Scheduler scheduler, VisualGame gameHost) + { + this.scheduler = scheduler; + this.gameHost = gameHost; + } + + public void StartDownload() + { + IsUpdating.Value = true; + Progress.Value = 0f; + scheduler.AddDelayed(() => { Progress.Value = 0.25f; }, 1000); + scheduler.AddDelayed(() => { Progress.Value = 0.5f; }, 1500); + scheduler.AddDelayed(() => { Progress.Value = 0.75f; }, 2000); + scheduler.AddDelayed(() => + { + Progress.Value = 1f; + IsUpdating.Value = false; + RequireRestart.Value = true; + }, 2500); + } + + public void UpdateAndRestart() + { + UpdateManager.RestartAppWhenExited() + .ContinueWith(_ => scheduler.Add(() => gameHost.GracefullyExit())); + } + } + + protected class NotificationsProvider : INotificationsProvider + { + private List notifications = new(); + + public IReadOnlyList GetAll() => notifications; + + public void Push(Notification notification) + { + notifications.Add(notification); + OnNotificationAdded?.Invoke(notification); + } + + public void ClearAll() + { + foreach (var notification in notifications) + OnNotificationRemoved?.Invoke(notification); + notifications.Clear(); + } + + public void Clear(Type type) + { + foreach (var notification in notifications.Where(notification => notification.GetType().IsAssignableTo(type))) + OnNotificationRemoved?.Invoke(notification); + notifications.RemoveAll(n => n.GetType().IsAssignableTo(type)); + } + + public void Remove(Notification notification) + { + if (notifications.Remove(notification)) + OnNotificationRemoved?.Invoke(notification); + } + + public event Action OnNotificationAdded; + public event Action OnNotificationRemoved; + } } } \ No newline at end of file diff --git a/PataNext.Simulation.Client/Systems/Inputs/RegisterInputSystemBase.cs b/PataNext.Simulation.Client/Systems/Inputs/RegisterInputSystemBase.cs index a2a7d7d7..0b3f6352 100644 --- a/PataNext.Simulation.Client/Systems/Inputs/RegisterInputSystemBase.cs +++ b/PataNext.Simulation.Client/Systems/Inputs/RegisterInputSystemBase.cs @@ -18,6 +18,8 @@ namespace PataNext.Simulation.Client.Systems.Inputs { public abstract class RegisterInputSystemBase : GameAppSystem { + public TInputSettings InputSettingsReadOnly => InputSettings; + protected bool InputsAreCreated { get; private set; } protected InputDatabase InputDatabase; diff --git a/PataNext.Simulation.Mixed/RuntimeTests/GameModes/RuntimeTestCoopMission.cs b/PataNext.Simulation.Mixed/RuntimeTests/GameModes/RuntimeTestCoopMission.cs index 864a54eb..1b16af3f 100644 --- a/PataNext.Simulation.Mixed/RuntimeTests/GameModes/RuntimeTestCoopMission.cs +++ b/PataNext.Simulation.Mixed/RuntimeTests/GameModes/RuntimeTestCoopMission.cs @@ -61,13 +61,24 @@ protected override void OnDependenciesResolved(IEnumerable dependencies) { base.OnDependenciesResolved(dependencies); - DependencyResolver.AddDependency(new TaskDependency(currentUserSystem.AsTask)); + DependencyResolver.AddDependency(new TaskDependency(taskScheduler.StartUnwrap(async () => + { + while (currentUserSystem.User.Token is null) + { + await Task.Delay(10); + } + }))); DependencyResolver.OnComplete(_ => { TestWithMasterServer(); }); } + protected override void OnUpdate() + { + base.OnUpdate(); + } + private void TestWithMasterServer() { RequestUtility.CreateTracked(World.Mgr, new GetFavoriteGameSaveRequest(), (Entity _, GetFavoriteGameSaveRequest.Response response) => diff --git a/PataNext.Simulation.Mixed/RuntimeTests/Scenars/TestScenar.cs b/PataNext.Simulation.Mixed/RuntimeTests/Scenars/TestScenar.cs index 466ecc2a..90751010 100644 --- a/PataNext.Simulation.Mixed/RuntimeTests/Scenars/TestScenar.cs +++ b/PataNext.Simulation.Mixed/RuntimeTests/Scenars/TestScenar.cs @@ -114,7 +114,7 @@ protected override Task OnStart() } } - /*using (var colliderEntity = World.Mgr.CreateEntity()) + using (var colliderEntity = World.Mgr.CreateEntity()) { var target = Focus(Safe(CreateEntity())); target.AddData(new UnitTargetDescription()) @@ -129,8 +129,8 @@ protected override Task OnStart() Direction = UnitDirection.Left, Statistics = new UnitStatistics() { - Health = 100, - Attack = 10, + Health = 400, + Attack = 12, AttackSpeed = 2, BaseWalkSpeed = 1, FeverWalkSpeed = 1, @@ -181,7 +181,7 @@ protected override Task OnStart() Console.WriteLine($"{target.Entity}; {unitFocus.Entity}"); GameWorld.Link(unitFocus.Handle, enemyTeam.Handle, true); - }*/ + } foreach (var child in GameWorld.Boards.Entity.GetLinkedEntities(enemyTeam.Id)) AddComponent(child, new Relative(enemyTeam)); diff --git a/StormiumTeam.GameBase/Module.cs b/StormiumTeam.GameBase/Module.cs index f978ce66..6202f126 100644 --- a/StormiumTeam.GameBase/Module.cs +++ b/StormiumTeam.GameBase/Module.cs @@ -106,6 +106,7 @@ public Module(Entity source, Context ctxParent, GameHostModuleDescription descri simulationApplication.Data.Collection.GetOrCreate(typeof(MasterServerManageSystem)); simulationApplication.Data.Collection.GetOrCreate(typeof(CurrentUserSystem)); + simulationApplication.Data.Collection.GetOrCreate(typeof(GetUserLoginRequest.Process)); simulationApplication.Data.Collection.GetOrCreate(typeof(DisconnectUserRequest.Process)); simulationApplication.Data.Collection.GetOrCreate(typeof(ConnectUserRequest.Process)); diff --git a/StormiumTeam.GameBase/Network/MasterServer/ConnectionService/ConnectionServiceComponents.cs b/StormiumTeam.GameBase/Network/MasterServer/ConnectionService/ConnectionServiceComponents.cs index e5775649..cd4f3693 100644 --- a/StormiumTeam.GameBase/Network/MasterServer/ConnectionService/ConnectionServiceComponents.cs +++ b/StormiumTeam.GameBase/Network/MasterServer/ConnectionService/ConnectionServiceComponents.cs @@ -2,10 +2,40 @@ using System.Threading.Tasks; using DefaultEcs; using GameHost.Core.Ecs; +using JetBrains.Annotations; using STMasterServer.Shared.Services; +using StormiumTeam.GameBase.Network.MasterServer.User; namespace StormiumTeam.GameBase.Network.MasterServer.UserService { + public struct GetUserLoginRequest + { + public string Representation; + + public GetUserLoginRequest(string representation) + { + Representation = representation; + } + + public struct Result + { + public string Login; + } + + public class Process : MasterServerRequestService + { + public Process([NotNull] WorldCollection collection) : base(collection) + { + } + + protected override async Task> OnUnprocessedRequest(Entity entity, RequestCallerStatus callerStatus) + { + var login = await Service.GetLogin(entity.Get().Representation); + return ent => ent.Set(new Result {Login = login}); + } + } + } + public struct DisconnectUserRequest { public string Token; @@ -19,13 +49,19 @@ public class Process : MasterServerRequestService ref currentUserSystem); } private readonly Action none = _ => { }; + private CurrentUserSystem currentUserSystem; + protected override async Task> OnUnprocessedRequest(Entity entity, RequestCallerStatus callerStatus) { await Service.Disconnect(entity.Get().Token); + + currentUserSystem.Unset(); + return none; } } diff --git a/StormiumTeam.GameBase/Network/MasterServer/MasterServerRequestHub.cs b/StormiumTeam.GameBase/Network/MasterServer/MasterServerRequestHub.cs index 61c8bc4c..63c75436 100644 --- a/StormiumTeam.GameBase/Network/MasterServer/MasterServerRequestHub.cs +++ b/StormiumTeam.GameBase/Network/MasterServer/MasterServerRequestHub.cs @@ -3,9 +3,6 @@ using GameHost.Core.Ecs; using JetBrains.Annotations; using MagicOnion; -using MagicOnion.Client; -using MagicOnion.Server; -using NetFabric.Hyperlinq; using STMasterServer.Shared.Services; using StormiumTeam.GameBase.Network.MasterServer.User; using StormiumTeam.GameBase.Network.MasterServer.Utility; diff --git a/StormiumTeam.GameBase/Network/MasterServer/StandardAuthService/Components.cs b/StormiumTeam.GameBase/Network/MasterServer/StandardAuthService/Components.cs index 9d096c96..6eb034d8 100644 --- a/StormiumTeam.GameBase/Network/MasterServer/StandardAuthService/Components.cs +++ b/StormiumTeam.GameBase/Network/MasterServer/StandardAuthService/Components.cs @@ -1,5 +1,6 @@ using System; using System.Threading.Tasks; +using Cysharp.Text; using DefaultEcs; using GameHost.Core.Ecs; using Microsoft.Extensions.Logging; @@ -22,6 +23,11 @@ public enum EType public string Data; public string HashedPassword; + public struct Error + { + public string Message; + } + public static ConnectUserRequest ViaGuid(string guid, string password) => new ConnectUserRequest { @@ -48,31 +54,37 @@ public Process(WorldCollection collection) : base(collection) DependencyResolver.Add(() => ref currentUserSystem); DependencyResolver.Add(() => ref logger); } - - private readonly Action none = _ => { }; protected override async Task> OnUnprocessedRequest(Entity entity, RequestCallerStatus callerStatus) { Console.WriteLine("yo0"); var component = entity.Get(); - ConnectResult result = component.Type switch + ConnectResult result = default; + try { - EType.Login => await Service.ConnectViaLogin(component.Data, component.HashedPassword), - EType.Guid => await Service.ConnectViaGuid(component.Data, component.HashedPassword), - _ => throw new ArgumentOutOfRangeException() - }; - + result = component.Type switch + { + EType.Login => await Service.ConnectViaLogin(component.Data, component.HashedPassword), + EType.Guid => await Service.ConnectViaGuid(component.Data, component.HashedPassword), + _ => throw new ArgumentOutOfRangeException() + }; + } + catch (Exception ex) + { + logger.ZLogError(ex, ""); + } + Console.WriteLine("yo1"); if (result.Token == null) { logger.ZLogCritical("Couldn't connect as '{0}' user (via {1} method)", component.Data, component.Type); - return none; + return e => { e.Set(new Error {Message = ZString.Format("Couldn't connect as '{0}' user (via {1} method)", component.Data, component.Type)}); }; } Console.WriteLine("yo3"); - return e => { currentUserSystem.Set(new (result.Guid, result.Token)); }; + return e => { currentUserSystem.Set(new(result.Guid, result.Token)); }; } } } diff --git a/StormiumTeam.GameBase/Network/MasterServer/User/CurrentUserSystem.cs b/StormiumTeam.GameBase/Network/MasterServer/User/CurrentUserSystem.cs index 7c8dc0ce..d096ddc3 100644 --- a/StormiumTeam.GameBase/Network/MasterServer/User/CurrentUserSystem.cs +++ b/StormiumTeam.GameBase/Network/MasterServer/User/CurrentUserSystem.cs @@ -22,8 +22,6 @@ public class CurrentUserSystem : AppSystem private ILogger logger; private Entity singletonEntity; - private TaskCompletionSource taskCompletionSource; - public CurrentUserSystem(WorldCollection collection) : base(collection) { DependencyResolver.Add(() => ref logger); @@ -31,12 +29,9 @@ public CurrentUserSystem(WorldCollection collection) : base(collection) collection.Mgr.SetMaxCapacity(1); singletonEntity.Set(new CurrentUser()); - - taskCompletionSource = new TaskCompletionSource(); } public UserToken User { get; private set; } - public Task AsTask => taskCompletionSource.Task; public void Set(UserToken userToken) { @@ -44,8 +39,17 @@ public void Set(UserToken userToken) User = userToken; singletonEntity.Set(new CurrentUser(userToken)); + } + + public void Unset() + { + if (User.Token == null) + return; + + logger.ZLogInformation("Disconnected User: {0}", User.Representation); - taskCompletionSource.SetResult(userToken); + User = default; + singletonEntity.Set(new CurrentUser()); } } } \ No newline at end of file