Skip to content

Commit d3d4f8c

Browse files
committed
Added methods to allow the implementation do extension casts
Let's say you have a server where the player can pick the menu driver, the player types !menu, selects WASD instead of XYZ, there are a couple ways direct casting could go wrong or be undesirable: - Overselling capabilities: The underlying menu does not support the extension, but the switcher advertises it - Underselling capabilities: The underlying menu supports the extension, but the switcher does not advertise it - The switcher wants to hook into a specific extension (such as controlling priority)
1 parent cf2d1fe commit d3d4f8c

File tree

7 files changed

+58
-10
lines changed

7 files changed

+58
-10
lines changed

src/CSSUniversalMenuAPI/IMenu.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
using System.Diagnostics.CodeAnalysis;
2+
13
using CounterStrikeSharp.API.Core;
24

35
namespace CSSUniversalMenuAPI;
@@ -50,4 +52,28 @@ void Exit()
5052
current = current.Parent;
5153
} while (current is not null);
5254
}
55+
56+
/// <summary>
57+
/// Attempt to get an extension for the current menu. <br/>
58+
///
59+
/// This is preferred over direct casting, as it allows menus to be wrapped,
60+
/// and switching the implementation at runtime for a single player.
61+
/// </summary>
62+
/// <typeparam name="TExtension">The extension's type.</typeparam>
63+
/// <param name="extension">The dynamic casted extension.</param>
64+
/// <returns>If the extension is supported, returns <c>true</c>, otherwise <c>false</c>.</returns>
65+
bool TryGetExtension<TExtension>([NotNullWhen(true)] out TExtension? extension)
66+
where TExtension : class
67+
{
68+
if (this is TExtension ext)
69+
{
70+
extension = ext;
71+
return true;
72+
}
73+
else
74+
{
75+
extension = null;
76+
return false;
77+
}
78+
}
5379
}

src/CSSUniversalMenuAPI/IMenuItem.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
using System.Diagnostics.CodeAnalysis;
2+
13
using CounterStrikeSharp.API.Core;
24

35
namespace CSSUniversalMenuAPI;
@@ -26,6 +28,30 @@ public interface IMenuItem
2628
/// </summary>
2729
event ItemSelectedAction? Selected;
2830
object? Context { get; set; }
31+
32+
/// <summary>
33+
/// Attempt to get an extension for the current menu. <br/>
34+
///
35+
/// This is preferred over direct casting, as it allows menus to be wrapped,
36+
/// and switching the implementation at runtime for a single player.
37+
/// </summary>
38+
/// <typeparam name="TExtension">The extension's type.</typeparam>
39+
/// <param name="extension">The dynamic casted extension.</param>
40+
/// <returns>If the extension is supported, returns <c>true</c>, otherwise <c>false</c>.</returns>
41+
bool TryGetExtension<TExtension>([NotNullWhen(true)] out TExtension? extension)
42+
where TExtension : class
43+
{
44+
if (this is TExtension ext)
45+
{
46+
extension = ext;
47+
return true;
48+
}
49+
else
50+
{
51+
extension = null;
52+
return false;
53+
}
54+
}
2955
}
3056

3157
public delegate void ItemSelectedAction(IMenuItem menuItem);

src/UniversalMenu.Compat.CSSharp/CSSharpCompatPlugin.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ public static bool BaseMenu_Open(BasePlugin? plugin, CCSPlayerController player,
8989
newMenu.PlayerCanClose = menu.ExitButton;
9090

9191
var useHtml = false;
92-
if (newMenu is IHtmlSupportMenuExtension htmlMenu)
92+
if (newMenu.TryGetExtension<IHtmlSupportMenuExtension>(out var htmlMenu))
9393
htmlMenu.UseHtml = useHtml = true;
9494

9595
if (useHtml)

src/UniversalMenu.Compat.MenuManagerApi/MenuManagerTranslator.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,11 +134,11 @@ public void Open(CCSPlayerController player)
134134
menu.Title = Title;
135135
menu.PlayerCanClose = true;// ExitButton;
136136

137-
bool usingHtml = false;
138-
if (menu is IHtmlSupportMenuExtension htmlMenu)
137+
var usingHtml = false;
138+
if (menu.TryGetExtension<IHtmlSupportMenuExtension>(out var htmlMenu))
139139
htmlMenu.UseHtml = usingHtml = true;
140140

141-
if (BackAction is not null && menu is INavigateBackMenuExtension backableMenu)
141+
if (BackAction is not null && menu.TryGetExtension<INavigateBackMenuExtension>(out var backableMenu))
142142
backableMenu.NavigateBack = (_) => BackAction.Invoke(player);
143143

144144
foreach (var option in MenuOptions)

tests/ProofOfConcepts/NumberKeysMenuAPI.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,6 @@ bool IMenuAPI.IsExtensionSupported(Type extension)
6666
{
6767
if (extension == typeof(IMenuPriorityExtension))
6868
return true;
69-
if (extension == typeof(IMenuItemSubtitleExtension))
70-
return true;
7169
return false;
7270
}
7371

tests/ProofOfConcepts/Program.Admin.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ private static void CreateAndShowMenuInternal(this IMenuAPI api, CCSPlayerContro
100100
{
101101
var menu = api.CreateMenu(player);
102102
menu.Title = items.Title;
103-
if (menu is IMenuPriorityExtension priorityExtension)
103+
if (menu.TryGetExtension<IMenuPriorityExtension>(out var priorityExtension))
104104
priorityExtension.Priority = 100.0;
105105
menu.CreateItemsInternal(api, items, donePlayers);
106106
menu.Display();
@@ -109,7 +109,7 @@ private static void CreateAndShowMenuInternal(this IMenuAPI api, IMenu parent, M
109109
{
110110
var menu = api.CreateMenu(parent);
111111
menu.Title = items.Title;
112-
if (menu is IMenuPriorityExtension priorityExtension)
112+
if (menu.TryGetExtension<IMenuPriorityExtension>(out var priorityExtension))
113113
priorityExtension.Priority = 100.0;
114114
menu.CreateItemsInternal(api, items, donePlayers);
115115
menu.Display();

tests/ProofOfConcepts/WASDMenuAPI.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,6 @@ bool IMenuAPI.IsExtensionSupported(Type extension)
6565
{
6666
if (extension == typeof(IMenuPriorityExtension))
6767
return true;
68-
if (extension == typeof(IMenuItemSubtitleExtension))
69-
return true;
7068
return false;
7169
}
7270

0 commit comments

Comments
 (0)