Compare commits

...

12 Commits

Author SHA1 Message Date
ZoNiCaL
f2b8044b8c Update gamedata for Call To Arms Update (#311)
Co-authored-by: Michael Wilson <roflmuffin@users.noreply.github.com>
2024-02-08 06:03:15 +00:00
Michael Wilson
eb9f5667d8 [skip ci] Update README.md 2024-02-07 20:16:13 +10:00
Michael Wilson
b9ca63a603 fix: default base menu post select action to reset 2024-02-07 20:13:43 +10:00
B3none
8fc926eacf Improved menu closing (#294) 2024-02-05 07:26:08 -05:00
B3none
5695c3f922 Updated css_plugins sub command responses. (#289)
Co-authored-by: Michael Wilson <roflmuffin@users.noreply.github.com>
2024-01-26 06:14:56 +00:00
B3none
9071d51ecd Tidy CCSPlayerController (#287)
Co-authored-by: Michael Wilson <roflmuffin@users.noreply.github.com>
2024-01-26 06:04:57 +00:00
Michael Wilson
0a32962f4a fix: move discord notify into release pipeline 2024-01-26 15:51:39 +10:00
Michael Wilson
271705b377 Update discord-notify.yml 2024-01-26 15:28:58 +10:00
Michael Wilson
cdcddbb5f3 Update discord-notify.yml 2024-01-25 16:47:53 +10:00
Michael Wilson
91f51d0c5c Update discord-notify.yml 2024-01-25 16:47:45 +10:00
Dliix66
e97f804294 HTML Menu improvements (#284)
Co-authored-by: B3none <24966460+B3none@users.noreply.github.com>
2024-01-23 11:15:18 +10:00
Dliix66
4f805b18e2 Added canUse virtual method (#282) 2024-01-22 10:23:02 +10:00
18 changed files with 460 additions and 390 deletions

View File

@@ -196,6 +196,7 @@ jobs:
(cd build/windows && zip -qq -r ../../counterstrikesharp-with-runtime-build-${{ needs.build_managed.outputs.buildnumber }}-windows-${{ env.GITHUB_SHA_SHORT }}.zip *)
- name: Release
id: release
uses: softprops/action-gh-release@v1
with:
tag_name: v${{ needs.build_managed.outputs.buildnumber }}
@@ -208,4 +209,11 @@ jobs:
- name: Publish NuGet package
run: |
dotnet nuget push build/api/CounterStrikeSharp.API.1.0.${{ needs.build_managed.outputs.buildnumber }}.nupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate
dotnet nuget push build/api/CounterStrikeSharp.API.1.0.${{ needs.build_managed.outputs.buildnumber }}.snupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate
dotnet nuget push build/api/CounterStrikeSharp.API.1.0.${{ needs.build_managed.outputs.buildnumber }}.snupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate
- name: Send Notification to Discord
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
uses: Ilshidur/action-discord@0.3.2
with:
args: "A new release of CS# has been tagged (v${{ needs.build_managed.outputs.buildnumber }}) at ${{ steps.release.outputs.url }}"

View File

@@ -1,15 +0,0 @@
name: Notify Discord on Release
on:
release:
types: [published]
jobs:
notify:
runs-on: ubuntu-latest
steps:
- name: Send Notification to Discord
uses: Ilshidur/action-discord@2.4.0
with:
args: "A new release of CS# has been tagged! View it here: ${{ github.event.release.html_url }}"
webhook: ${{ secrets.DISCORD_WEBHOOK }}

View File

@@ -128,4 +128,4 @@ Build
```bash
cmake --build . --config Debug
```
<img src="https://repobeats.axiom.co/api/embed/a96f228b8fa98c032070fa8dd831c967334ee553.svg" width="100%" />

View File

@@ -22,21 +22,21 @@
},
"CCSPlayerController_ChangeTeam": {
"offsets": {
"windows": 90,
"linux": 89
"windows": 93,
"linux": 92
}
},
"CCSPlayerController_Respawn": {
"offsets": {
"windows": 242,
"linux": 244
"windows": 244,
"linux": 246
}
},
"CCSPlayerPawn_Respawn": {
"CBasePlayerController_SetPawn": {
"signatures": {
"library": "server",
"windows": "\\x40\\x53\\x48\\x83\\xEC\\x20\\x8B\\x91\\x38\\x0B\\x00\\x00\\x48\\x8B\\xD9",
"linux": "\\x8B\\x8F\\x40\\x0E\\x00\\x00\\x83\\xF9\\xFF\\x0F\\x84\\xD9\\x01"
"library": "server",
"windows": "\\x44\\x88\\x4C\\x24\\x2A\\x55\\x57",
"linux": "\\x55\\x48\\x89\\xE5\\x41\\x57\\x41\\x56\\x41\\x55\\x41\\x54\\x49\\x89\\xFC\\x53\\x48\\x89\\xF3\\x48\\x81\\xEC\\xC8\\x00\\x00\\x00"
}
},
"CCSPlayerPawnBase_PostThink": {
@@ -70,8 +70,15 @@
"CBaseModelEntity_SetModel": {
"signatures": {
"library": "server",
"windows": "\\x48\\x89\\x5C\\x24\\x10\\x48\\x89\\x7C\\x24\\x20\\x55\\x48\\x8B\\xEC\\x48\\x83\\xEC\\x50",
"linux": "\\x55\\x48\\x89\\xF2\\x48\\x89\\xE5\\x41\\x54\\x49\\x89\\xFC\\x48\\x8D\\x2A\\x2A\\x48\\x83\\x2A\\x2A\\x2A\\x8D\\x05\\x2A\\x2A\\x2A\\x00\\x48\\x8B\\x30\\x48\\x8B\\x06\\xFF\\x2A\\x2A\\x48\\x8B\\x45\\x2A\\x48\\x8D\\x2A\\x2A\\x4C\\x89\\x2A\\x48\\x89\\x45\\x2A\\x2A\\x68\\xFC"
"windows": "\\x48\\x89\\x5C\\x24\\x2A\\x48\\x89\\x7C\\x24\\x2A\\x55\\x48\\x8B\\xEC\\x48\\x83\\xEC\\x50\\x48\\x8B\\xF9\\x4C\\x8B\\xC2",
"linux": "\\x55\\x48\\x89\\xF2\\x48\\x89\\xE5\\x41\\x54\\x49\\x89\\xFC\\x48\\x8D\\x7D\\xE0\\x48\\x83\\xEC\\x18\\x48\\x8D\\x05\\x2A\\x2A\\x2A\\x2A\\x48\\x8B\\x30\\x48\\x8B\\x06"
}
},
"CCSPlayer_WeaponServices_CanUse": {
"signatures": {
"library": "server",
"windows": "\\x48\\x89\\x5C\\x24\\x10\\x48\\x89\\x6C\\x24\\x18\\x56\\x57\\x41\\x56\\x48\\x83\\xEC\\x30\\x80\\xB9\\xA0\\x00\\x00\\x00\\x00",
"linux": "\\x48\\x85\\xF6\\x0F\\x84\\x2A\\x2A\\x2A\\x2A\\x55\\x31\\xC9\\x48\\x89\\xE5\\x41\\x55\\x49\\x89\\xFD"
}
},
"CCSPlayer_ItemServices_DropActivePlayerWeapon": {
@@ -95,14 +102,14 @@
"CCSGameRules_TerminateRound": {
"signatures": {
"library": "server",
"windows": "\\x48\\x8B\\xC4\\x4C\\x89\\x48\\x20\\x55\\x56",
"linux": "\\x55\\x48\\x89\\xE5\\x41\\x57\\x41\\x56\\x41\\x55\\x49\\x89\\xFD\\x41\\x54\\x53\\x48\\x81\\xEC\\xE8"
"windows": "\\x48\\x8B\\xC4\\x4C\\x89\\x48\\x20\\x55\\x57",
"linux": "\\x55\\x48\\x89\\xE5\\x41\\x57\\x41\\x56\\x41\\x55\\x41\\x54\\x49\\x89\\xFC\\x53\\x48\\x81\\xEC\\xE8\\x01\\x00\\x00\\x48\\x8D\\x05\\x2A\\x2A\\x2A\\x2A"
}
},
"UTIL_CreateEntityByName": {
"signatures": {
"library": "server",
"windows": "\\x48\\x83\\xEC\\x48\\xC6\\x44\\x24\\x30\\x00\\x4C\\x8B\\xC1",
"windows": "\\x48\\x83\\xEC\\x48\\xC6\\x44\\x24\\x30\\x00",
"linux": "\\x48\\x8D\\x05\\x2A\\x2A\\x2A\\x2A\\x55\\x48\\x89\\xFA"
}
},
@@ -110,7 +117,7 @@
"signatures": {
"library": "server",
"windows": "\\x48\\x89\\x5C\\x24\\x10\\x57\\x48\\x83\\xEC\\x30\\x48\\x8B\\xDA\\x48\\x8B\\xF9\\x48\\x85\\xC9",
"linux": "\\x48\\x85\\xFF\\x74\\x4B\\x55\\x48\\x89\\xE5\\x41\\x56"
"linux": "\\x48\\x85\\xFF\\x74\\x2A\\x55\\x48\\x89\\xE5\\x41\\x56"
}
},
"CEntityInstance_AcceptInput": {
@@ -123,14 +130,14 @@
"LegacyGameEventListener": {
"signatures": {
"library": "server",
"windows": "\\x48\\x8B\\x15\\x2A\\x2A\\x2A\\x2A\\x48\\x85\\xD2\\x74\\x2A\\x85\\xC9\\x74",
"linux": "\\x48\\x8B\\x05\\x2A\\x2A\\x2A\\x2A\\x48\\x85\\xC0\\x74\\x2A\\x83\\xFF\\x3F\\x76\\x2A\\x31\\xC0"
"windows": "\\x48\\x89\\x5C\\x24\\x10\\x48\\x89\\x74\\x24\\x18\\x57\\x48\\x83\\xEC\\x40\\x49\\x8B\\xF0",
"linux": "\\x55\\x48\\x89\\xE5\\x41\\x57\\x49\\x89\\xFF\\x41\\x56\\x48\\x8D\\x7D\\xC0"
}
},
"CBasePlayerPawn_CommitSuicide": {
"offsets": {
"windows": 357,
"linux": 357
"windows": 359,
"linux": 359
}
},
"CBasePlayerPawn_RemovePlayerItem": {
@@ -189,15 +196,15 @@
},
"GameEventManager": {
"offsets": {
"windows": 91,
"linux": 91
"windows": 93,
"linux": 93
}
},
"CEntityIOOutput_FireOutputInternal": {
"signatures": {
"library": "server",
"windows": "\\x48\\x8B\\xC4\\x4C\\x89\\x48\\x20\\x55\\x57\\x41\\x54\\x41\\x56",
"linux": "\\x55\\x48\\x89\\xE5\\x41\\x57\\x41\\x56\\x41\\x55\\x41\\x54\\x53\\x48\\x83\\xEC\\x58\\x4C\\x8B\\x6F\\x08"
"linux": "\\x55\\x48\\x89\\xE5\\x41\\x57\\x41\\x56\\x41\\x55\\x41\\x54\\x49\\x89\\xD4\\x53\\x48\\x89\\xF3\\x48\\x83\\xEC\\x58"
}
}
}

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- https://learn.microsoft.com/en-us/dotnet/fundamentals/package-validation/diagnostic-ids -->
<Suppressions xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>F:CounterStrikeSharp.API.Modules.Memory.VirtualFunctions.CCSPlayerPawn_Respawn</Target>
<Left>.\ApiCompat\v151.dll</Left>
<Right>obj\Debug\net7.0\CounterStrikeSharp.API.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>F:CounterStrikeSharp.API.Modules.Memory.VirtualFunctions.CCSPlayerPawn_RespawnFunc</Target>
<Left>.\ApiCompat\v151.dll</Left>
<Right>obj\Debug\net7.0\CounterStrikeSharp.API.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:CounterStrikeSharp.API.Core.CCSPlayerPawn.Respawn</Target>
<Left>.\ApiCompat\v151.dll</Left>
<Right>obj\Debug\net7.0\CounterStrikeSharp.API.dll</Right>
</Suppression>
</Suppressions>

View File

@@ -182,7 +182,7 @@ namespace CounterStrikeSharp.API.Core
}
catch (Exception e)
{
info.ReplyToCommand($"Could not load plugin \"{path}\")", true);
info.ReplyToCommand($"Could not load plugin \"{path}\"", true);
Logger.LogError(e, "Could not load plugin \"{Path}\"", path);
}
}
@@ -209,7 +209,7 @@ namespace CounterStrikeSharp.API.Core
IPluginContext? plugin = _pluginContextQueryHandler.FindPluginByIdOrName(pluginIdentifier);
if (plugin == null)
{
info.ReplyToCommand($"Could not unload plugin \"{pluginIdentifier}\")", true);
info.ReplyToCommand($"Could not unload plugin \"{pluginIdentifier}\"", true);
break;
}
@@ -233,7 +233,7 @@ namespace CounterStrikeSharp.API.Core
if (plugin == null)
{
info.ReplyToCommand($"Could not reload plugin \"{pluginIdentifier}\")", true);
info.ReplyToCommand($"Could not reload plugin \"{pluginIdentifier}\"", true);
break;
}

View File

@@ -27,5 +27,5 @@ public partial class CBaseEntity
/// </summary>
public QAngle? AbsRotation => CBodyComponent?.SceneNode?.AbsRotation;
public T? GetVData<T>() where T : CEntitySubclassVDataBase => (T)Activator.CreateInstance(typeof(T), Marshal.ReadIntPtr(SubclassID.Handle + 8));
public T? GetVData<T>() where T : CEntitySubclassVDataBase => (T)Activator.CreateInstance(typeof(T), Marshal.ReadIntPtr(SubclassID.Handle + 4));
}

View File

@@ -0,0 +1,19 @@
using System;
using CounterStrikeSharp.API.Modules.Cvars;
using CounterStrikeSharp.API.Modules.Entities;
using CounterStrikeSharp.API.Modules.Entities.Constants;
using CounterStrikeSharp.API.Modules.Memory;
using CounterStrikeSharp.API.Modules.Utils;
namespace CounterStrikeSharp.API.Core;
public partial class CBasePlayerController
{
public void SetPawn(CBasePlayerPawn? pawn)
{
if (pawn is null) return;
if (!pawn.IsValid) return;
VirtualFunctions.CBasePlayerController_SetPawnFunc.Invoke(this, pawn, true, false);
}
}

View File

@@ -10,19 +10,13 @@ namespace CounterStrikeSharp.API.Core;
public partial class CCSPlayerController
{
public int? UserId
{
get
{
return NativeAPI.GetUseridFromIndex((int)this.Index);
}
}
public CsTeam Team => (CsTeam)this.TeamNum;
public int? UserId => NativeAPI.GetUseridFromIndex((int)Index);
public CsTeam Team => (CsTeam)TeamNum;
public IntPtr GiveNamedItem(string item)
{
if (!PlayerPawn.IsValid) return 0;
if (PlayerPawn.Value == null) return 0;
if (!PlayerPawn.Value.IsValid) return 0;
if (PlayerPawn.Value.ItemServices == null) return 0;
@@ -37,7 +31,7 @@ public partial class CCSPlayerController
return IntPtr.Zero;
}
return this.GiveNamedItem(itemString);
return GiveNamedItem(itemString);
}
public void PrintToConsole(string message)
@@ -47,12 +41,12 @@ public partial class CCSPlayerController
public void PrintToChat(string message)
{
VirtualFunctions.ClientPrint(this.Handle, HudDestination.Chat, message, 0, 0, 0, 0);
VirtualFunctions.ClientPrint(Handle, HudDestination.Chat, message, 0, 0, 0, 0);
}
public void PrintToCenter(string message)
{
VirtualFunctions.ClientPrint(this.Handle, HudDestination.Center, message, 0, 0, 0, 0);
VirtualFunctions.ClientPrint(Handle, HudDestination.Center, message, 0, 0, 0, 0);
}
public void PrintToCenterHtml(string message) => PrintToCenterHtml(message, 5);
@@ -74,14 +68,16 @@ public partial class CCSPlayerController
public void DropActiveWeapon()
{
if (!PlayerPawn.IsValid) return;
if (PlayerPawn.Value == null) return;
if (!PlayerPawn.Value.IsValid) return;
if (PlayerPawn.Value.ItemServices == null) return;
if (PlayerPawn.Value.WeaponServices == null) return;
if (!PlayerPawn.Value.WeaponServices.ActiveWeapon.IsValid) return;
CCSPlayer_ItemServices itemServices = new CCSPlayer_ItemServices(PlayerPawn.Value.ItemServices.Handle);
CCSPlayer_WeaponServices weponServices = new CCSPlayer_WeaponServices(PlayerPawn.Value.WeaponServices.Handle);
itemServices.DropActivePlayerWeapon(weponServices.ActiveWeapon.Value);
CCSPlayer_WeaponServices weaponServices = new CCSPlayer_WeaponServices(PlayerPawn.Value.WeaponServices.Handle);
itemServices.DropActivePlayerWeapon(weaponServices.ActiveWeapon.Value);
}
/// <summary>
@@ -90,6 +86,7 @@ public partial class CCSPlayerController
public void RemoveWeapons()
{
if (!PlayerPawn.IsValid) return;
if (PlayerPawn.Value == null) return;
if (!PlayerPawn.Value.IsValid) return;
if (PlayerPawn.Value.ItemServices == null) return;
@@ -105,6 +102,7 @@ public partial class CCSPlayerController
public void CommitSuicide(bool explode, bool force)
{
if (!PlayerPawn.IsValid) return;
if (PlayerPawn.Value == null) return;
if (!PlayerPawn.Value.IsValid) return;
PlayerPawn.Value.CommitSuicide(explode, force);
@@ -116,9 +114,11 @@ public partial class CCSPlayerController
public void Respawn()
{
if (!PlayerPawn.IsValid) return;
if (PlayerPawn.Value == null) return;
if (!PlayerPawn.Value.IsValid) return;
VirtualFunctions.CCSPlayerPawn_Respawn(PlayerPawn.Value.Handle);
// The Call To Arms update appears to have invalidated the need for CCSPlayerPawn_Respawn.
SetPawn(PlayerPawn.Value);
VirtualFunction.CreateVoid<IntPtr>(Handle, GameData.GetOffset("CCSPlayerController_Respawn"))(Handle);
}
@@ -130,7 +130,7 @@ public partial class CCSPlayerController
/// <param name="team">The team to switch to</param>
public void SwitchTeam(CsTeam team)
{
VirtualFunctions.SwitchTeam(this.Handle, (byte)team);
VirtualFunctions.SwitchTeam(Handle, (byte)team);
}
/// <summary>
@@ -153,7 +153,7 @@ public partial class CCSPlayerController
/// <returns>ConVar string value</returns>
public string GetConVarValue(string conVar)
{
return NativeAPI.GetClientConvarValue(this.Slot, conVar);
return NativeAPI.GetClientConvarValue(Slot, conVar);
}
public string GetConVarValue(ConVar? conVar)
@@ -179,7 +179,7 @@ public partial class CCSPlayerController
throw new InvalidOperationException("'SetFakeClientConVar' can only be called for fake clients (bots)");
}
NativeAPI.SetFakeClientConvarValue(this.Slot, conVar, value);
NativeAPI.SetFakeClientConvarValue(Slot, conVar, value);
}
/// <summary>
@@ -228,8 +228,8 @@ public partial class CCSPlayerController
{
get
{
if (!this.IsValid) return null;
var authorizedSteamId = NativeAPI.GetPlayerAuthorizedSteamid(this.Slot);
if (!IsValid) return null;
var authorizedSteamId = NativeAPI.GetPlayerAuthorizedSteamid(Slot);
if ((long)authorizedSteamId == -1) return null;
return (SteamID)authorizedSteamId;
@@ -244,8 +244,8 @@ public partial class CCSPlayerController
{
get
{
if (!this.IsValid) return null;
var ipAddress = NativeAPI.GetPlayerIpAddress(this.Slot);
if (!IsValid) return null;
var ipAddress = NativeAPI.GetPlayerIpAddress(Slot);
if (string.IsNullOrWhiteSpace(ipAddress)) return null;
return ipAddress;
@@ -258,9 +258,6 @@ public partial class CCSPlayerController
public VoiceFlags VoiceFlags
{
get => (VoiceFlags)NativeAPI.GetClientVoiceFlags(Handle);
set
{
NativeAPI.SetClientVoiceFlags(Handle, (Byte)value);
}
set => NativeAPI.SetClientVoiceFlags(Handle, (Byte)value);
}
}

View File

@@ -1,19 +1,21 @@
using System;
using CounterStrikeSharp.API.Modules.Memory;
using Microsoft.Extensions.Logging;
namespace CounterStrikeSharp.API.Core;
public partial class CCSPlayerPawn
{
/// <summary>
/// Respawn player
/// </summary>
public void Respawn()
{
if (!Controller.IsValid) return;
if (!Controller.Value.IsValid) return;
/// <summary>
/// Respawn player
/// </summary>
VirtualFunctions.CCSPlayerPawn_Respawn(Handle);
VirtualFunction.CreateVoid<IntPtr>(Controller.Value.Handle, GameData.GetOffset("CCSPlayerController_Respawn"))(Controller.Value.Handle);
}
}
[Obsolete("Use CCSPlayerController.Respawn() instead")]
public void Respawn()
{
if (!Controller.IsValid) return;
if (!Controller.Value.IsValid) return;
Application.Instance.Logger.LogWarning("CCSPawn.Respawn is deprecated and does nothing, use CCSPlayerController.Respawn instead");
}
}

View File

@@ -6,6 +6,7 @@
<EnablePackageValidation>true</EnablePackageValidation>
<NoWarn>$(NoWarn);CS1591;CP0003</NoWarn>
<Nullable>enable</Nullable>
<GenerateCompatibilitySuppressionFile>true</GenerateCompatibilitySuppressionFile>
<Authors>Roflmuffin</Authors>
<Description>Official server side runtime assembly for CounterStrikeSharp</Description>
<PackageProjectUrl>http://docs.cssharp.dev/</PackageProjectUrl>

View File

@@ -60,14 +60,15 @@ public static class VirtualFunctions
new(GameData.GetSignature("CBaseEntity_DispatchSpawn"));
public static Action<IntPtr, IntPtr> CBaseEntity_DispatchSpawn = CBaseEntity_DispatchSpawnFunc.Invoke;
public static MemoryFunctionVoid<IntPtr> CCSPlayerPawn_RespawnFunc = new(GameData.GetSignature("CCSPlayerPawn_Respawn"));
public static Action<IntPtr> CCSPlayerPawn_Respawn = CCSPlayerPawn_RespawnFunc.Invoke;
public static MemoryFunctionVoid<CEntityInstance, CTakeDamageInfo> CBaseEntity_TakeDamageOldFunc = new (GameData.GetSignature("CBaseEntity_TakeDamageOld"));
public static MemoryFunctionVoid<CBasePlayerController, CBasePlayerPawn, bool, bool> CBasePlayerController_SetPawnFunc = new (GameData.GetSignature("CBasePlayerController_SetPawn"));
public static MemoryFunctionVoid<CEntityInstance, CTakeDamageInfo> CBaseEntity_TakeDamageOldFunc = new (GameData.GetSignature("CBaseEntity_TakeDamageOld"));
public static Action<CEntityInstance, CTakeDamageInfo> CBaseEntity_TakeDamageOld = CBaseEntity_TakeDamageOldFunc.Invoke;
public static MemoryFunctionWithReturn<CCSPlayer_WeaponServices, CBasePlayerWeapon, bool> CCSPlayer_WeaponServices_CanUseFunc = new(GameData.GetSignature("CCSPlayer_WeaponServices_CanUse"));
public static Func<CCSPlayer_WeaponServices, CBasePlayerWeapon, bool> CCSPlayer_WeaponServices_CanUse = CCSPlayer_WeaponServices_CanUseFunc.Invoke;
public static MemoryFunctionVoid<CCSPlayerPawnBase> CCSPlayerPawnBase_PostThinkFunc = new (GameData.GetSignature("CCSPlayerPawnBase_PostThink"));
public static Action<CCSPlayerPawnBase> CCSPlayerPawnBase_PostThink = CCSPlayerPawnBase_PostThinkFunc.Invoke;

View File

@@ -16,125 +16,149 @@
using System.Collections.Generic;
namespace CounterStrikeSharp.API.Modules.Menu
namespace CounterStrikeSharp.API.Modules.Menu;
public enum PostSelectAction
{
public abstract class BaseMenu : IMenu
{
public string Title { get; set; }
public List<ChatMenuOption> MenuOptions { get; } = new();
Close,
Reset,
Nothing
}
public abstract class BaseMenu : IMenu
{
public string Title { get; set; }
public List<ChatMenuOption> MenuOptions { get; } = new();
public PostSelectAction PostSelectAction { get; set; } = PostSelectAction.Reset;
protected BaseMenu(string title)
{
Title = title;
}
protected BaseMenu(string title)
{
Title = title;
}
public virtual ChatMenuOption AddMenuOption(string display, Action<CCSPlayerController, ChatMenuOption> onSelect, bool disabled = false)
{
var option = new ChatMenuOption(display, disabled, onSelect);
MenuOptions.Add(option);
return option;
}
public virtual ChatMenuOption AddMenuOption(string display, Action<CCSPlayerController, ChatMenuOption> onSelect, bool disabled = false)
{
var option = new ChatMenuOption(display, disabled, onSelect);
MenuOptions.Add(option);
return option;
}
}
// This must be called ChatMenuOption to maintain backwards compatibility with the old API
public class ChatMenuOption
{
public string Text { get; set; }
public bool Disabled { get; set; }
public Action<CCSPlayerController, ChatMenuOption> OnSelect { get; set; }
// This must be called ChatMenuOption to maintain backwards compatibility with the old API
public class ChatMenuOption
{
public string Text { get; set; }
public bool Disabled { get; set; }
public Action<CCSPlayerController, ChatMenuOption> OnSelect { get; set; }
public ChatMenuOption(string display, bool disabled, Action<CCSPlayerController, ChatMenuOption> onSelect)
{
Text = display;
Disabled = disabled;
OnSelect = onSelect;
}
public ChatMenuOption(string display, bool disabled, Action<CCSPlayerController, ChatMenuOption> onSelect)
{
Text = display;
Disabled = disabled;
OnSelect = onSelect;
}
}
public abstract class BaseMenuInstance : IMenuInstance
{
public virtual int NumPerPage => 6;
public bool CloseOnSelect { get; set; } = true;
public Stack<int> PrevPageOffsets { get; } = new();
public IMenu Menu { get; }
public CCSPlayerController Player { get; }
public int Page { get; set; }
public int CurrentOffset { get; set; }
protected BaseMenuInstance(CCSPlayerController player, IMenu menu)
{
Menu = menu;
Player = player;
}
public abstract class BaseMenuInstance : IMenuInstance
protected bool HasPrevButton => Page > 0;
protected bool HasNextButton => CurrentOffset + NumPerPage < Menu.MenuOptions.Count;
protected int MenuItemsPerPage => NumPerPage + 2 - (HasNextButton ? 1 : 0) - (HasPrevButton ? 1 : 0);
public virtual void Display()
{
public int NumPerPage => 6;
public Stack<int> PrevPageOffsets { get; } = new();
public IMenu Menu { get; }
public CCSPlayerController Player { get; }
throw new NotImplementedException();
}
public int Page { get; set; }
public int CurrentOffset { get; set; }
public void OnKeyPress(CCSPlayerController player, int key)
{
if (player.Handle != Player.Handle) return;
protected BaseMenuInstance(CCSPlayerController player, IMenu menu)
if (key == 8 && HasNextButton)
{
Menu = menu;
Player = player;
NextPage();
return;
}
protected bool HasPrevButton => Page > 0;
protected bool HasNextButton => CurrentOffset + NumPerPage < Menu.MenuOptions.Count;
protected int MenuItemsPerPage => NumPerPage + 2 - (HasNextButton ? 1 : 0) - (HasPrevButton ? 1 : 0);
public virtual void Display()
if (key == 7 && HasPrevButton)
{
throw new NotImplementedException();
PrevPage();
return;
}
public void OnKeyPress(CCSPlayerController player, int key)
if (key == 9)
{
if (player.Handle != Player.Handle) return;
Close();
return;
}
if (key == 8 && HasNextButton)
{
NextPage();
return;
}
var desiredValue = key;
if (key == 1 && HasPrevButton)
{
PrevPage();
return;
}
var menuItemIndex = CurrentOffset + desiredValue - 1;
if (key == 9)
{
Reset();
return;
}
var desiredValue = key;
if (HasPrevButton) desiredValue = key - 1;
var menuItemIndex = CurrentOffset + desiredValue - 1;
if (menuItemIndex >= 0 && menuItemIndex < Menu.MenuOptions.Count)
{
var menuOption = Menu.MenuOptions[menuItemIndex];
if (menuItemIndex >= 0 && menuItemIndex < Menu.MenuOptions.Count)
{
var menuOption = Menu.MenuOptions[menuItemIndex];
if (!menuOption.Disabled)
if (!menuOption.Disabled)
{
menuOption.OnSelect(Player, menuOption);
switch (Menu.PostSelectAction)
{
menuOption.OnSelect(Player, menuOption);
Reset();
case PostSelectAction.Close:
Close();
break;
case PostSelectAction.Reset:
Reset();
break;
case PostSelectAction.Nothing:
// Do nothing
break;
default:
throw new NotImplementedException("The specified Select Action is not supported!");
}
}
}
public virtual void Reset()
{
CurrentOffset = 0;
Page = 0;
PrevPageOffsets.Clear();
}
public void NextPage()
{
PrevPageOffsets.Push(CurrentOffset);
CurrentOffset += MenuItemsPerPage;
Page++;
Display();
}
public void PrevPage()
{
Page--;
CurrentOffset = PrevPageOffsets.Pop();
Display();
}
}
}
public virtual void Reset()
{
CurrentOffset = 0;
Page = 0;
PrevPageOffsets.Clear();
}
public void Close()
{
MenuManager.CloseActiveMenu(Player);
}
public void NextPage()
{
PrevPageOffsets.Push(CurrentOffset);
CurrentOffset += MenuItemsPerPage;
Page++;
Display();
}
public void PrevPage()
{
Page--;
CurrentOffset = PrevPageOffsets.Pop();
Display();
}
}

View File

@@ -17,109 +17,109 @@
using System.Text;
using Microsoft.Extensions.Logging;
namespace CounterStrikeSharp.API.Modules.Menu
namespace CounterStrikeSharp.API.Modules.Menu;
public class CenterHtmlMenu : BaseMenu
{
public class CenterHtmlMenu : BaseMenu
public CenterHtmlMenu(string title) : base(ModifyTitle(title))
{
public CenterHtmlMenu(string title) : base(ModifyTitle(title))
{
}
}
public override ChatMenuOption AddMenuOption(string display, Action<CCSPlayerController, ChatMenuOption> onSelect, bool disabled = false)
{
var option = new ChatMenuOption(ModifyOptionDisplay(display), disabled, onSelect);
MenuOptions.Add(option);
return option;
}
public override ChatMenuOption AddMenuOption(string display, Action<CCSPlayerController, ChatMenuOption> onSelect, bool disabled = false)
{
var option = new ChatMenuOption(ModifyOptionDisplay(display), disabled, onSelect);
MenuOptions.Add(option);
return option;
}
private static string ModifyTitle(string title)
private static string ModifyTitle(string title)
{
if (title.Length > 32)
{
if (title.Length > 32)
{
Application.Instance.Logger.LogWarning("Title should not be longer than 32 characters for a CenterHtmlMenu");
return title[..32];
}
return title;
Application.Instance.Logger.LogWarning("Title should not be longer than 32 characters for a CenterHtmlMenu");
return title[..32];
}
private static string ModifyOptionDisplay(string display)
{
if (display.Length > 26)
{
Application.Instance.Logger.LogWarning("Display should not be longer than 26 characters for a CenterHtmlMenu item");
return display[..26];
}
return display;
}
return title;
}
public class CenterHtmlMenuInstance : BaseMenuInstance
private static string ModifyOptionDisplay(string display)
{
private readonly BasePlugin _plugin;
public CenterHtmlMenuInstance(BasePlugin plugin, CCSPlayerController player, IMenu menu) : base(player, menu)
if (display.Length > 26)
{
_plugin = plugin;
RemoveOnTickListener();
plugin.RegisterListener<Core.Listeners.OnTick>(Display);
}
public override void Display()
{
if (MenuManager.GetActiveMenu(Player) != this)
{
Reset();
return;
}
var builder = new StringBuilder();
builder.Append($"<b><font color='yellow'>{Menu.Title}</font></b>");
builder.AppendLine("<br>");
var keyOffset = 1;
if (HasPrevButton)
{
builder.AppendFormat("<font color='green'>!1</font> -> Prev");
builder.AppendLine("<br>");
keyOffset++;
}
for (var i = CurrentOffset; i < Math.Min(CurrentOffset + MenuItemsPerPage, Menu.MenuOptions.Count); i++)
{
var option = Menu.MenuOptions[i];
builder.Append($"<font color='green'>!{keyOffset++}</font> {option.Text}");
builder.AppendLine("<br>");
}
if (HasNextButton)
{
builder.AppendFormat("<font color='yellow'>!8</font> -> Next");
builder.AppendLine("<br>");
}
builder.AppendFormat("<font color='red'>!9</font> -> Close");
builder.AppendLine("<br>");
var currentPageText = builder.ToString();
Player.PrintToCenterHtml(currentPageText);
Application.Instance.Logger.LogWarning("Display should not be longer than 26 characters for a CenterHtmlMenu item");
return display[..26];
}
public override void Reset()
{
base.Reset();
RemoveOnTickListener();
// Send a blank message to clear the menu
Player.PrintToCenterHtml(" ");
}
private void RemoveOnTickListener()
{
var onTick = new Core.Listeners.OnTick(Display);
_plugin.RemoveListener("OnTick", onTick);
}
return display;
}
}
public class CenterHtmlMenuInstance : BaseMenuInstance
{
private readonly BasePlugin _plugin;
public override int NumPerPage => 5; // one less than the actual number of items per page to avoid truncated options
public CenterHtmlMenuInstance(BasePlugin plugin, CCSPlayerController player, IMenu menu) : base(player, menu)
{
_plugin = plugin;
RemoveOnTickListener();
plugin.RegisterListener<Core.Listeners.OnTick>(Display);
}
public override void Display()
{
if (MenuManager.GetActiveMenu(Player) != this)
{
Reset();
return;
}
var builder = new StringBuilder();
builder.Append($"<b><font color='yellow'>{Menu.Title}</font></b>");
builder.AppendLine("<br>");
var keyOffset = 1;
for (var i = CurrentOffset; i < Math.Min(CurrentOffset + MenuItemsPerPage, Menu.MenuOptions.Count); i++)
{
var option = Menu.MenuOptions[i];
string color = option.Disabled ? "grey" : "green";
builder.Append($"<font color='{color}'>!{keyOffset++}</font> {option.Text}");
builder.AppendLine("<br>");
}
if (HasPrevButton)
{
builder.AppendFormat("<font color='yellow'>!7</font> &#60;- Prev");
builder.AppendLine("<br>");
}
if (HasNextButton)
{
builder.AppendFormat("<font color='yellow'>!8</font> -> Next");
builder.AppendLine("<br>");
}
builder.AppendFormat("<font color='red'>!9</font> -> Close");
builder.AppendLine("<br>");
var currentPageText = builder.ToString();
Player.PrintToCenterHtml(currentPageText);
}
public override void Reset()
{
base.Reset();
RemoveOnTickListener();
// Send a blank message to clear the menu
Player.PrintToCenterHtml(" ");
}
private void RemoveOnTickListener()
{
var onTick = new Core.Listeners.OnTick(Display);
_plugin.RemoveListener("OnTick", onTick);
}
}

View File

@@ -16,63 +16,61 @@
using CounterStrikeSharp.API.Modules.Utils;
namespace CounterStrikeSharp.API.Modules.Menu
namespace CounterStrikeSharp.API.Modules.Menu;
public class ChatMenu: BaseMenu
{
public class ChatMenu: BaseMenu
public ChatMenu(string title) : base(title)
{
}
}
public class ChatMenuInstance : BaseMenuInstance
{
public ChatMenuInstance(CCSPlayerController player, ChatMenu menu) : base(player, menu)
{
public ChatMenu(string title) : base(title)
{
}
}
public class ChatMenuInstance : BaseMenuInstance
public override void Display()
{
public ChatMenuInstance(CCSPlayerController player, ChatMenu menu) : base(player, menu)
Player.PrintToChat(Menu.Title);
Player.PrintToChat("---");
var keyOffset = 1;
for (var i = CurrentOffset;
i < Math.Min(CurrentOffset + MenuItemsPerPage, Menu.MenuOptions.Count);
i++)
{
var option = Menu.MenuOptions[i];
Player.PrintToChat(
$" {(option.Disabled ? ChatColors.Grey : ChatColors.Green)} !{keyOffset++} {ChatColors.Default}{option.Text}");
}
public override void Display()
if (HasPrevButton)
{
Player.PrintToChat(Menu.Title);
Player.PrintToChat("---");
var keyOffset = 1;
if (HasPrevButton)
{
Player.PrintToChat($" {ChatColors.Yellow}!1 {ChatColors.Default}-> Prev");
keyOffset++;
}
for (var i = CurrentOffset;
i < Math.Min(CurrentOffset + MenuItemsPerPage, Menu.MenuOptions.Count);
i++)
{
var option = Menu.MenuOptions[i];
Player.PrintToChat(
$" {(option.Disabled ? ChatColors.Grey : ChatColors.Green)} !{keyOffset++} {ChatColors.Default}{option.Text}");
}
if (HasNextButton)
{
Player.PrintToChat($" {ChatColors.Yellow}!8 {ChatColors.Default}-> Next");
}
Player.PrintToChat($" {ChatColors.Yellow}!7 {ChatColors.Default}-> Prev");
}
}
public static class ChatMenus
{
[Obsolete("Use MenuManager.OpenChatMenu instead")]
public static void OpenMenu(CCSPlayerController player, ChatMenu menu)
if (HasNextButton)
{
MenuManager.OpenChatMenu(player, menu);
}
[Obsolete("Use MenuManager.OnKeyPress instead")]
public static void OnKeyPress(CCSPlayerController player, int key)
{
MenuManager.OnKeyPress(player, key);
Player.PrintToChat($" {ChatColors.Yellow}!8 {ChatColors.Default}-> Next");
}
}
}
public static class ChatMenus
{
[Obsolete("Use MenuManager.OpenChatMenu instead")]
public static void OpenMenu(CCSPlayerController player, ChatMenu menu)
{
MenuManager.OpenChatMenu(player, menu);
}
[Obsolete("Use MenuManager.OnKeyPress instead")]
public static void OnKeyPress(CCSPlayerController player, int key)
{
MenuManager.OnKeyPress(player, key);
}
}

View File

@@ -14,48 +14,45 @@
* along with CounterStrikeSharp. If not, see <https://www.gnu.org/licenses/>. *
*/
namespace CounterStrikeSharp.API.Modules.Menu
namespace CounterStrikeSharp.API.Modules.Menu;
public class ConsoleMenu : BaseMenu
{
public class ConsoleMenu : BaseMenu
public ConsoleMenu(string title) : base(title)
{
public ConsoleMenu(string title) : base(title)
{
}
}
public class ConsoleMenuInstance : BaseMenuInstance
{
public ConsoleMenuInstance(CCSPlayerController player, IMenu menu) : base(player, menu)
{
}
public override void Display()
{
Player.PrintToConsole(Menu.Title);
Player.PrintToConsole("---");
var keyOffset = 1;
if (HasPrevButton)
{
Player.PrintToConsole($"!1 -> Prev");
keyOffset++;
}
for (var i = CurrentOffset;
i < Math.Min(CurrentOffset + MenuItemsPerPage, Menu.MenuOptions.Count);
i++)
{
var option = Menu.MenuOptions[i];
Player.PrintToConsole(
$" {(option.Disabled ? "[Enabled]" : "[Disabled] - ")} !{keyOffset++} {option.Text}");
}
if (HasNextButton)
{
Player.PrintToConsole($"!8 -> Next");
}
}
}
}
public class ConsoleMenuInstance : BaseMenuInstance
{
public ConsoleMenuInstance(CCSPlayerController player, IMenu menu) : base(player, menu)
{
}
public override void Display()
{
Player.PrintToConsole(Menu.Title);
Player.PrintToConsole("---");
var keyOffset = 1;
for (var i = CurrentOffset;
i < Math.Min(CurrentOffset + MenuItemsPerPage, Menu.MenuOptions.Count);
i++)
{
var option = Menu.MenuOptions[i];
Player.PrintToConsole($"{(option.Disabled ? "[Enabled]" : "[Disabled] - ")} css_{keyOffset++} {option.Text}");
}
if (HasPrevButton)
{
Player.PrintToConsole("css_7 -> Prev");
}
if (HasNextButton)
{
Player.PrintToConsole("css_8 -> Next");
}
}
}

View File

@@ -16,29 +16,41 @@
using System.Collections.Generic;
namespace CounterStrikeSharp.API.Modules.Menu
{
public interface IMenu
{
public string Title { get; set; }
public List<ChatMenuOption> MenuOptions { get; }
public ChatMenuOption AddMenuOption(string display, Action<CCSPlayerController, ChatMenuOption> onSelect, bool disabled = false);
}
namespace CounterStrikeSharp.API.Modules.Menu;
public interface IMenuInstance
public interface IMenu
{
public string Title { get; set; }
public List<ChatMenuOption> MenuOptions { get; }
public PostSelectAction PostSelectAction
{
protected IMenu Menu { get; }
protected CCSPlayerController? Player { get; }
protected int Page { get; }
protected int CurrentOffset { get; }
protected int NumPerPage { get; }
protected Stack<int> PrevPageOffsets { get; }
public void NextPage();
public void PrevPage();
public void Reset();
public void Display();
public void OnKeyPress(CCSPlayerController player, int key);
get { throw new NotImplementedException(); }
set { throw new NotImplementedException(); }
}
public ChatMenuOption AddMenuOption(string display, Action<CCSPlayerController, ChatMenuOption> onSelect, bool disabled = false);
}
public interface IMenuInstance
{
protected IMenu Menu { get; }
protected CCSPlayerController? Player { get; }
protected bool CloseOnSelect { get; }
protected int Page { get; }
protected int CurrentOffset { get; }
protected int NumPerPage { get; }
protected Stack<int> PrevPageOffsets { get; }
public void NextPage();
public void PrevPage();
public void Reset();
public void Close()
{
// Fallback for backwards compatibility
throw new NotImplementedException();
}
public void Display();
public void OnKeyPress(CCSPlayerController player, int key);
}

View File

@@ -32,7 +32,7 @@ public static class MenuManager
return !ActiveMenus.TryGetValue(player.Handle, out var value) ? null : value;
}
private static void ResetMenus(CCSPlayerController player)
public static void CloseActiveMenu(CCSPlayerController player)
{
if (ActiveMenus.TryGetValue(player.Handle, out var activeMenu))
{
@@ -41,26 +41,26 @@ public static class MenuManager
ActiveMenus.Remove(player.Handle);
}
public static void OpenChatMenu(CCSPlayerController player, ChatMenu menu)
{
ResetMenus(player);
CloseActiveMenu(player);
ActiveMenus[player.Handle] = new ChatMenuInstance(player, menu);
ActiveMenus[player.Handle].Display();
}
public static void OpenCenterHtmlMenu(BasePlugin plugin, CCSPlayerController player, CenterHtmlMenu menu)
{
ResetMenus(player);
CloseActiveMenu(player);
ActiveMenus[player.Handle] = new CenterHtmlMenuInstance(plugin, player, menu);
ActiveMenus[player.Handle].Display();
}
public static void OpenConsoleMenu(CCSPlayerController player, ConsoleMenu menu)
{
ResetMenus(player);
CloseActiveMenu(player);
ActiveMenus[player.Handle] = new ConsoleMenuInstance(player, menu);
ActiveMenus[player.Handle].Display();
@@ -68,9 +68,6 @@ public static class MenuManager
public static void OnKeyPress(CCSPlayerController player, int key)
{
if (ActiveMenus.TryGetValue(player.Handle, out var activeMenu))
{
activeMenu.OnKeyPress(player, key);
}
GetActiveMenu(player)?.OnKeyPress(player, key);
}
}