From 57ec4c5bd8882e819330a6c5d8f01b8dc70b5079 Mon Sep 17 00:00:00 2001 From: Ashleigh Adams Date: Tue, 22 Apr 2025 02:33:52 +0100 Subject: [PATCH 1/2] Added first pass MenuManagerApi driver --- CSSUniversalMenuAPI.sln | 7 + README.md | 16 ++ package.sh | 17 ++ .../MenuManagerApiDriver.cs | 154 ++++++++++++++++++ .../MenuManagerApiDriverPlugin.cs | 51 ++++++ ...UniversalMenu.Driver.MenuManagerApi.csproj | 19 +++ .../ScreenMenuAPIDriverPlugin.cs | 10 ++ 7 files changed, 274 insertions(+) create mode 100644 src/UniversalMenu.Driver.MenuManagerApi/MenuManagerApiDriver.cs create mode 100644 src/UniversalMenu.Driver.MenuManagerApi/MenuManagerApiDriverPlugin.cs create mode 100644 src/UniversalMenu.Driver.MenuManagerApi/UniversalMenu.Driver.MenuManagerApi.csproj diff --git a/CSSUniversalMenuAPI.sln b/CSSUniversalMenuAPI.sln index 873b20a..8f17d2e 100644 --- a/CSSUniversalMenuAPI.sln +++ b/CSSUniversalMenuAPI.sln @@ -16,6 +16,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Directory.Build.props = Directory.Build.props Directory.Build.targets = Directory.Build.targets global.json = global.json + package.sh = package.sh README.md = README.md EndProjectSection EndProject @@ -41,6 +42,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "GitHub Actions", "GitHub Ac EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UniversalMenu.Compat.CSSharp", "src\UniversalMenu.Compat.CSSharp\UniversalMenu.Compat.CSSharp.csproj", "{EF2866D7-C411-42ED-B5E0-DCE79CCFCA09}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UniversalMenu.Driver.MenuManagerApi", "src\UniversalMenu.Driver.MenuManagerApi\UniversalMenu.Driver.MenuManagerApi.csproj", "{FB832721-DCE2-4F62-A441-6C3E21A05317}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -75,6 +78,10 @@ Global {EF2866D7-C411-42ED-B5E0-DCE79CCFCA09}.Debug|Any CPU.Build.0 = Debug|Any CPU {EF2866D7-C411-42ED-B5E0-DCE79CCFCA09}.Release|Any CPU.ActiveCfg = Release|Any CPU {EF2866D7-C411-42ED-B5E0-DCE79CCFCA09}.Release|Any CPU.Build.0 = Release|Any CPU + {FB832721-DCE2-4F62-A441-6C3E21A05317}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FB832721-DCE2-4F62-A441-6C3E21A05317}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FB832721-DCE2-4F62-A441-6C3E21A05317}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FB832721-DCE2-4F62-A441-6C3E21A05317}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/README.md b/README.md index 4c4e06f..602961a 100644 --- a/README.md +++ b/README.md @@ -113,3 +113,19 @@ classDiagram UniversalMenu.Compat.ScreenMenuAPI <-- PluginUsingSomeScreenMenu CSSUniversalMenuAPI <-- PluginUsingUniversalAPI ``` + + diff --git a/package.sh b/package.sh index c4489d2..a8c37bf 100644 --- a/package.sh +++ b/package.sh @@ -50,3 +50,20 @@ pushd "$dst" 7z a ../UniversalMenu.Driver.ScreenMenuAPI.zip ./ popd rm -rf "$dst" + + +# build UniversalMenu.Driver.MenuManagerApi.zip +dst="./artifacts/UniversalMenu.Driver.MenuManagerApi" +dst_shared="$dst/addons/counterstrikesharp/shared" +dst_plugins="$dst/addons/counterstrikesharp/plugins" + +# package UniversalMenu.Driver.MenuManagerApi +mkdir -p "$dst_plugins/UniversalMenu.Driver.MenuManagerApi" +src="./src/UniversalMenu.Driver.MenuManagerApi/bin/Release/net8.0/publish" +cp -r "$src/." "$dst_plugins/UniversalMenu.Driver.MenuManagerApi/" + +# zip UniversalMenu.Driver.MenuManagerApi.zip +pushd "$dst" +7z a ../UniversalMenu.Driver.MenuManagerApi.zip ./ +popd +rm -rf "$dst" diff --git a/src/UniversalMenu.Driver.MenuManagerApi/MenuManagerApiDriver.cs b/src/UniversalMenu.Driver.MenuManagerApi/MenuManagerApiDriver.cs new file mode 100644 index 0000000..a062960 --- /dev/null +++ b/src/UniversalMenu.Driver.MenuManagerApi/MenuManagerApiDriver.cs @@ -0,0 +1,154 @@ +using System; +using System.Threading; +using System.Collections.Generic; + +using CounterStrikeSharp.API.Core; + +using CSSUniversalMenuAPI; +using CSSUniversalMenuAPI.Extensions; + +using IMenuManagerApi = MenuManager.IMenuApi; +using IMenuManagerMenu = CounterStrikeSharp.API.Modules.Menu.IMenu; +using IMenu = CSSUniversalMenuAPI.IMenu; + +namespace UniversalMenu.Driver.MenuManagerApi; + +internal class PlayerMenuState +{ + public Menu? ActiveMenu { get; set; } +} + +public sealed class MenuManagerApiDriver : IMenuAPI +{ + internal MenuManagerApiDriverPlugin Plugin { get; } + internal IMenuManagerApi? MenuManagerApi { get; set; } + + public MenuManagerApiDriver(MenuManagerApiDriverPlugin plugin) + { + Plugin = plugin; + } + + private Dictionary MenuStates = new(); + internal PlayerMenuState GetMenuState(CCSPlayerController player) + { + if (!MenuStates.TryGetValue(player.SteamID, out var menuState)) + MenuStates.Add(player.SteamID, menuState = new()); + return menuState; + } + internal void PlayerDisconnected(ulong steamId) + { + MenuStates.Remove(steamId); + } + + IMenu IMenuAPI.CreateMenu(CCSPlayerController player, CancellationToken ct) + { + return new Menu() + { + MenuAPI = this, + Player = player, + Parent = null, + }; + } + + IMenu IMenuAPI.CreateMenu(IMenu parent, CancellationToken ct) + { + if (parent is not Menu parentMenu) + throw new ArgumentException("Menu given belongs to another menu implementation", nameof(parent)); + + return new Menu() + { + MenuAPI = this, + Player = parent.Player, + Parent = parentMenu, + }; + } + + bool IMenuAPI.IsExtensionSupported(Type extension) + { + if (extension == typeof(INavigateBackMenuExtension)) + return true; + return false; + } + + bool IMenuAPI.IsMenuOpen(CCSPlayerController player) + { + return MenuManagerApi?.HasOpenedMenu(player) ?? false; + } +} + +internal class Menu : IMenu, INavigateBackMenuExtension +{ + public required MenuManagerApiDriver MenuAPI { get; set; } + + // IMenu + public required Menu? Parent { get; init; } + IMenu? IMenu.Parent => Parent; + public required CCSPlayerController Player { get; init; } + public bool PlayerCanClose { get; set; } = true; + public bool IsActive { get; private set; } + public string Title { get; set; } = string.Empty; + + // ScreenMenuAPI + internal IMenuManagerMenu? TheMenu { get; set; } + private List MenuItems { get; } = new(); + public Action? NavigateBack { get; set; } + + void IMenu.Close() + { + if (!IsActive) + return; + IsActive = false; + + var state = MenuAPI.GetMenuState(Player); + if (this == state.ActiveMenu) + { + MenuAPI.MenuManagerApi.CloseMenu(Player); + state.ActiveMenu = null; + } + } + + IMenuItem IMenu.CreateItem() + { + var ret = new MenuItem() { Menu = this }; + MenuItems.Add(ret); + return ret; + } + + void IMenu.Display() + { + if (MenuAPI.MenuManagerApi is null) + throw new Exception("MenuManagerApi not found"); + + IsActive = true; + if (TheMenu is not null) + { + TheMenu.Open(Player); + return; + } + + Action? navBack = null; + if (NavigateBack is not null) + navBack = (player) => NavigateBack(this); + + TheMenu = MenuAPI.MenuManagerApi.GetMenu(Title, back_action: navBack!); + + foreach (var item in MenuItems) + TheMenu.AddMenuOption(item.Title, (player, option) => item.RaiseSelected(), !item.Enabled); + + TheMenu.Open(Player); + } +} + +internal class MenuItem : IMenuItem +{ + public required IMenu Menu { get; init; } + public string Title { get; set; } = string.Empty; + public bool Enabled { get; set; } = true; + public object? Context { get; set; } + + public event ItemSelectedAction? Selected; + internal void RaiseSelected() + { + Selected?.Invoke(this); + } +} diff --git a/src/UniversalMenu.Driver.MenuManagerApi/MenuManagerApiDriverPlugin.cs b/src/UniversalMenu.Driver.MenuManagerApi/MenuManagerApiDriverPlugin.cs new file mode 100644 index 0000000..7dd787d --- /dev/null +++ b/src/UniversalMenu.Driver.MenuManagerApi/MenuManagerApiDriverPlugin.cs @@ -0,0 +1,51 @@ +using System; +using System.Threading; + +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Core.Attributes; +using CounterStrikeSharp.API.Core.Attributes.Registration; +using CounterStrikeSharp.API.Core.Capabilities; + +using IMenuManagerApi = MenuManager.IMenuApi; + +namespace UniversalMenu.Driver.MenuManagerApi; + +[MinimumApiVersion(314)] +public class MenuManagerApiDriverPlugin : BasePlugin +{ + public override string ModuleName => "UniversalMenu.DefaultDriver.MenuManagerApi"; + public override string ModuleDescription => "Implement CSSUniversalMenuAPI via MenuManagerApi"; + public override string ModuleVersion => Verlite.Version.Full; + + internal CancellationTokenSource Cts { get; set; } = null!; + private MenuManagerApiDriver? DriverInstance { get; set; } + private static readonly PluginCapability MenuCapability = new("menu:nfcore"); + + public override void Load(bool hotReload) + { + Cts = new CancellationTokenSource(); + DriverInstance = new MenuManagerApiDriver(this); + CSSUniversalMenuAPI.UniversalMenu.RegisterDriver("MenuManagerApi", DriverInstance); + } + + public override void Unload(bool hotReload) + { + Cts.Cancel(); + CSSUniversalMenuAPI.UniversalMenu.UnregisterDriver("MenuManagerApi"); + } + + public override void OnAllPluginsLoaded(bool hotReload) + { + DriverInstance!.MenuManagerApi = MenuCapability.Get(); + } + + [GameEventHandler(HookMode.Pre)] + public HookResult OnPlayerDisconnect(EventPlayerDisconnect e, GameEventInfo info) + { + if (e.Userid is null) + return HookResult.Continue; + + DriverInstance?.PlayerDisconnected(e.Userid.SteamID); + return HookResult.Continue; + } +} diff --git a/src/UniversalMenu.Driver.MenuManagerApi/UniversalMenu.Driver.MenuManagerApi.csproj b/src/UniversalMenu.Driver.MenuManagerApi/UniversalMenu.Driver.MenuManagerApi.csproj new file mode 100644 index 0000000..f86753e --- /dev/null +++ b/src/UniversalMenu.Driver.MenuManagerApi/UniversalMenu.Driver.MenuManagerApi.csproj @@ -0,0 +1,19 @@ + + + + UniversalMenu.Driver.MenuManagerApi + + + + + False + False + + + + false + ..\..\thirdparty\MenuManagerApi\MenuManagerApi.dll + + + + diff --git a/src/UniversalMenu.Driver.ScreenMenuAPI/ScreenMenuAPIDriverPlugin.cs b/src/UniversalMenu.Driver.ScreenMenuAPI/ScreenMenuAPIDriverPlugin.cs index 81378b5..9066066 100644 --- a/src/UniversalMenu.Driver.ScreenMenuAPI/ScreenMenuAPIDriverPlugin.cs +++ b/src/UniversalMenu.Driver.ScreenMenuAPI/ScreenMenuAPIDriverPlugin.cs @@ -34,6 +34,16 @@ public override void Unload(bool hotReload) CSSUniversalMenuAPI.UniversalMenu.UnregisterDriver("ScreenMenuAPI"); } + [GameEventHandler(HookMode.Pre)] + public HookResult OnPlayerDisconnected(EventPlayerDisconnect e, GameEventInfo info) + { + if (e.Userid is null) + return HookResult.Continue; + + DriverInstance?.PlayerDisconnected(e.Userid.SteamID); + return HookResult.Continue; + } + [ConsoleCommand("css_0"), ConsoleCommand("css_1"), ConsoleCommand("css_2"), ConsoleCommand("css_3"), ConsoleCommand("css_4")] [ConsoleCommand("css_5"), ConsoleCommand("css_6"), ConsoleCommand("css_7"), ConsoleCommand("css_8"), ConsoleCommand("css_9")] [ConsoleCommand("css_screenmenu_bound_buttons")] From de33ede0f706c9cb9f1ee3264cd831b5c7eab78f Mon Sep 17 00:00:00 2001 From: Ashleigh Adams Date: Tue, 22 Apr 2025 03:10:06 +0100 Subject: [PATCH 2/2] Simplify packaging script --- package.sh | 118 +++++++++++++++++++++++------------------------------ 1 file changed, 52 insertions(+), 66 deletions(-) diff --git a/package.sh b/package.sh index a8c37bf..a208e5b 100644 --- a/package.sh +++ b/package.sh @@ -1,69 +1,55 @@ #!/bin/bash set -euf -o pipefail -# build CSSUniversalMenuAPI.zip -dst="./artifacts/CSSUniversalMenuAPI" -dst_shared="$dst/addons/counterstrikesharp/shared" -dst_plugins="$dst/addons/counterstrikesharp/plugins" - -# package CSSUniversalMenuAPI -mkdir -p "$dst_shared/CSSUniversalMenuAPI" -src="./src/CSSUniversalMenuAPI/bin/Release/net8.0/publish" -cp -r "$src/." "$dst_shared/CSSUniversalMenuAPI/" - -# package UniversalMenu.Compat.MenuManagerApi -mkdir -p "$dst_plugins/UniversalMenu.Compat.MenuManagerApi" -src="./src/UniversalMenu.Compat.MenuManagerApi/bin/Release/net8.0/publish" -cp -r "$src/." "$dst_plugins/UniversalMenu.Compat.MenuManagerApi/" - -# package UniversalMenu.Compat.CSSharp -mkdir -p "$dst_plugins/UniversalMenu.Compat.CSSharp" -src="./src/UniversalMenu.Compat.CSSharp/bin/Release/net8.0/publish" -cp -r "$src/." "$dst_plugins/UniversalMenu.Compat.CSSharp/" -# shared part: allows modified methods to load the 0Harmony.dll dependency -mkdir -p "$dst_shared/0Harmony" -mv "$dst_plugins/UniversalMenu.Compat.CSSharp/0Harmony.dll" "$dst_shared/0Harmony/" - -# package UniversalMenu.Compat.ScreenMenuAPI # this isn't implemented yet -#mkdir -p "$dst_plugins/UniversalMenu.Compat.ScreenMenuAPI" -#src="./src/UniversalMenu.Compat.ScreenMenuAPI/bin/Release/net8.0/publish" -#cp -r "$src/." "$dst_shared/UniversalMenu.Compat.ScreenMenuAPI/" - -# zip CSSUniversalMenuAPI.zip -pushd "$dst" -7z a ../CSSUniversalMenuAPI.zip ./ -popd -rm -rf "$dst" - -# build UniversalMenu.Driver.ScreenMenuAPI.zip -dst="./artifacts/UniversalMenu.Driver.ScreenMenuAPI" -dst_shared="$dst/addons/counterstrikesharp/shared" -dst_plugins="$dst/addons/counterstrikesharp/plugins" - -# package UniversalMenu.Driver.ScreenMenuAPI -mkdir -p "$dst_plugins/UniversalMenu.Driver.ScreenMenuAPI" -src="./src/UniversalMenu.Driver.ScreenMenuAPI/bin/Release/net8.0/publish" -cp -r "$src/." "$dst_plugins/UniversalMenu.Driver.ScreenMenuAPI/" - -# zip UniversalMenu.Driver.ScreenMenuAPI.zip -pushd "$dst" -7z a ../UniversalMenu.Driver.ScreenMenuAPI.zip ./ -popd -rm -rf "$dst" - - -# build UniversalMenu.Driver.MenuManagerApi.zip -dst="./artifacts/UniversalMenu.Driver.MenuManagerApi" -dst_shared="$dst/addons/counterstrikesharp/shared" -dst_plugins="$dst/addons/counterstrikesharp/plugins" - -# package UniversalMenu.Driver.MenuManagerApi -mkdir -p "$dst_plugins/UniversalMenu.Driver.MenuManagerApi" -src="./src/UniversalMenu.Driver.MenuManagerApi/bin/Release/net8.0/publish" -cp -r "$src/." "$dst_plugins/UniversalMenu.Driver.MenuManagerApi/" - -# zip UniversalMenu.Driver.MenuManagerApi.zip -pushd "$dst" -7z a ../UniversalMenu.Driver.MenuManagerApi.zip ./ -popd -rm -rf "$dst" +zip_name="" +dst="" +dst_css="" + +setup_zip() { + zip_name="$1" + dst="./artifacts/$zip_name" + dst_css="$dst/addons/counterstrikesharp" +} + +copy_dir() { + mkdir -p "$dst_css/$2/" + cp -r "$1/." "$dst_css/$2/" +} + +move_file() { + mkdir -p "$dst_css/$2/" + mv "$dst_css/$1" "$dst_css/$2/" +} + +commit_zip() { + pushd "$dst" + 7z a "../$zip_name.zip" ./ + popd + rm -rf "$dst" +} + +setup_zip CSSUniversalMenuAPI +copy_dir src/CSSUniversalMenuAPI/bin/Release/net8.0/publish shared/CSSUniversalMenuAPI +commit_zip + +setup_zip UniversalMenu.Compat.CSSharp +copy_dir src/UniversalMenu.Compat.CSSharp/bin/Release/net8.0/publish plugins/UniversalMenu.Compat.CSSharp +# we move this into a shared location so that injected code can find the dll +move_file plugins/UniversalMenu.Compat.CSSharp/0Harmony.dll shared/0Harmony +commit_zip + +setup_zip UniversalMenu.Compat.MenuManagerApi +copy_dir src/UniversalMenu.Compat.MenuManagerApi/bin/Release/net8.0/publish plugins/UniversalMenu.Compat.MenuManagerApi +commit_zip + +#setup_zip UniversalMenu.Compat.ScreenMenuAPI +#copy_dir src/UniversalMenu.Compat.ScreenMenuAPI/bin/Release/net8.0/publish plugins/UniversalMenu.Compat.ScreenMenuAPI +#commit_zip + +setup_zip UniversalMenu.Driver.ScreenMenuAPI +copy_dir src/UniversalMenu.Driver.ScreenMenuAPI/bin/Release/net8.0/publish shared/UniversalMenu.Driver.ScreenMenuAPI +commit_zip + +setup_zip UniversalMenu.Driver.MenuManagerApi +copy_dir src/UniversalMenu.Driver.MenuManagerApi/bin/Release/net8.0/publish shared/UniversalMenu.Driver.MenuManagerApi +commit_zip