Compare commits

...

5 Commits
dev ... v170

Author SHA1 Message Date
Michael Wilson
501d51a668 feat: add CommandCallingContext to commands (#337) 2024-02-24 10:41:07 +10:00
Michael Wilson
87f48cb35c chore: update hl2sdk (#336) 2024-02-22 21:52:39 +10:00
Michael Wilson
39aa430528 [no ci] Create FUNDING.yml 🥰 2024-02-22 15:03:49 +10:00
roflmuffin
a404a4d9d5 [no ci] remove untriaged when author action label applied 2024-02-22 10:58:44 +10:00
roflmuffin
62ba29891d [no ci] add basic issue management workflows 2024-02-22 10:51:18 +10:00
24 changed files with 219 additions and 26 deletions

14
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,14 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
polar: # Replace with a single Polar username
custom: ['https://support.cssharp.dev']

View File

@@ -0,0 +1,29 @@
name: Add comment for needs-author-action
on:
issues:
types:
- labeled
jobs:
add-comment:
if: github.event.label.name == 'needs-author-action'
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- name: Add comment
run: gh issue comment "$NUMBER" --body "$BODY"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
NUMBER: ${{ github.event.issue.number }}
BODY: This issue has been marked `needs-author-action` and may be missing some important information.
- name: Remove label
run: gh issue edit "$NUMBER" --remove-label "$LABELS"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
NUMBER: ${{ github.event.issue.number }}
LABELS: untriaged

View File

@@ -0,0 +1,29 @@
name: Add comment for needs-author-action
on:
issue_comment:
types:
- created
jobs:
add-comment:
if: (github.event.sender.id == github.event.issue.user.id) && contains(github.event.issue.labels.*.name, 'needs-author-action')
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- name: Remove label
run: gh issue edit "$NUMBER" --remove-label "$LABELS"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
NUMBER: ${{ github.event.issue.number }}
LABELS: needs-author-action
- name: Add label
run: gh issue edit "$NUMBER" --add-label "$LABELS"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
NUMBER: ${{ github.event.issue.number }}
LABELS: needs-further-triage

24
.github/workflows/issues-stale.yml vendored Normal file
View File

@@ -0,0 +1,24 @@
name: Mark stale issues
on:
schedule:
- cron: '28 3 * * *'
jobs:
stale:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v5
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-issue-stale: 14
days-before-issue-close: 14
stale-issue-label: 'no-recent-activity'
only-labels: 'needs-author-action'
stale-issue-message: 'This issue has been automatically marked `no-recent-activity` because it has not had any activity for 14 days. It will be closed if no further activity occurs within 14 more days. Any new comment (by anyone, not necessarily the author) will remove no-recent-activity.'
close-issue-message: 'This issue will now be closed since it had been marked `no-recent-activity` but received no further activity in the past 14 days.'

20
.github/workflows/issues-triage-new.yml vendored Normal file
View File

@@ -0,0 +1,20 @@
name: Label new issues
on:
issues:
types:
- reopened
- opened
jobs:
label_issues:
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- run: gh issue edit "$NUMBER" --add-label "$LABELS"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
NUMBER: ${{ github.event.issue.number }}
LABELS: untriaged

View File

@@ -18,8 +18,10 @@ SET(SOURCE_FILES
src/mm_plugin.h
libraries/hl2sdk-cs2/tier1/convar.cpp
libraries/hl2sdk-cs2/tier1/generichash.cpp
libraries/hl2sdk-cs2/tier1/keyvalues3.cpp
libraries/hl2sdk-cs2/entity2/entityidentity.cpp
libraries/hl2sdk-cs2/entity2/entitysystem.cpp
libraries/hl2sdk-cs2/entity2/entitykeyvalues.cpp
libraries/dotnet/hostfxr.h
libraries/dotnet/coreclr_delegates.h
libraries/metamod-source/core/sourcehook/sourcehook.cpp

View File

@@ -1,6 +1,7 @@
using System;
using CounterStrikeSharp.API.Modules.Memory;
using CounterStrikeSharp.API.Modules.Commands;
namespace CounterStrikeSharp.API.Core
{
@@ -124,6 +125,17 @@ namespace CounterStrikeSharp.API.Core
}
}
public static CommandCallingContext CommandGetCallingContext(IntPtr command){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();
ScriptContext.GlobalScriptContext.Push(command);
ScriptContext.GlobalScriptContext.SetIdentifier(0x886D0EB6);
ScriptContext.GlobalScriptContext.Invoke();
ScriptContext.GlobalScriptContext.CheckErrors();
return (CommandCallingContext)ScriptContext.GlobalScriptContext.GetResult(typeof(CommandCallingContext));
}
}
public static void IssueClientCommand(int slot, string command){
lock (ScriptContext.GlobalScriptContext.Lock) {
ScriptContext.GlobalScriptContext.Reset();

View File

@@ -112,7 +112,7 @@ namespace CounterStrikeSharp.API.Core
" CounterStrikeSharp was created and is maintained by Michael \"roflmuffin\" Wilson.\n" +
" Counter-Strike Sharp uses code borrowed from SourceMod, Source.Python, FiveM, Saul Rennison, source2gen and CS2Fixes.\n" +
" See ACKNOWLEDGEMENTS.md for more information.\n" +
" Current API Version: " + currentVersion, true);
" Current API Version: " + currentVersion);
return;
}
@@ -124,8 +124,7 @@ namespace CounterStrikeSharp.API.Core
case "list":
{
info.ReplyToCommand(
$" List of all plugins currently loaded by CounterStrikeSharp: {_pluginManager.GetLoadedPlugins().Count()} plugins loaded.",
true);
$" List of all plugins currently loaded by CounterStrikeSharp: {_pluginManager.GetLoadedPlugins().Count()} plugins loaded.");
foreach (var plugin in _pluginManager.GetLoadedPlugins())
{
@@ -142,7 +141,7 @@ namespace CounterStrikeSharp.API.Core
sb.Append(plugin.Plugin.ModuleDescription);
}
info.ReplyToCommand(sb.ToString(), true);
info.ReplyToCommand(sb.ToString());
}
break;
@@ -153,8 +152,7 @@ namespace CounterStrikeSharp.API.Core
if (info.ArgCount < 3)
{
info.ReplyToCommand(
"Valid usage: css_plugins start/load [relative plugin path || absolute plugin path] (e.g \"TestPlugin\", \"plugins/TestPlugin/TestPlugin.dll\")\n",
true);
"Valid usage: css_plugins start/load [relative plugin path || absolute plugin path] (e.g \"TestPlugin\", \"plugins/TestPlugin/TestPlugin.dll\")\n");
break;
}
@@ -180,7 +178,7 @@ namespace CounterStrikeSharp.API.Core
}
catch (Exception e)
{
info.ReplyToCommand($"Could not load plugin \"{path}\"", true);
info.ReplyToCommand($"Could not load plugin \"{path}\"");
Logger.LogError(e, "Could not load plugin \"{Path}\"", path);
}
}
@@ -198,8 +196,7 @@ namespace CounterStrikeSharp.API.Core
if (info.ArgCount < 3)
{
info.ReplyToCommand(
"Valid usage: css_plugins stop/unload [plugin name || #plugin id] (e.g \"TestPlugin\", \"1\")\n",
true);
"Valid usage: css_plugins stop/unload [plugin name || #plugin id] (e.g \"TestPlugin\", \"1\")\n");
break;
}
@@ -207,7 +204,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}\"");
break;
}
@@ -221,8 +218,7 @@ namespace CounterStrikeSharp.API.Core
if (info.ArgCount < 3)
{
info.ReplyToCommand(
"Valid usage: css_plugins restart/reload [plugin name || #plugin id] (e.g \"TestPlugin\", \"#1\")\n",
true);
"Valid usage: css_plugins restart/reload [plugin name || #plugin id] (e.g \"TestPlugin\", \"#1\")\n");
break;
}
@@ -231,7 +227,7 @@ namespace CounterStrikeSharp.API.Core
if (plugin == null)
{
info.ReplyToCommand($"Could not reload plugin \"{pluginIdentifier}\"", true);
info.ReplyToCommand($"Could not reload plugin \"{pluginIdentifier}\"");
break;
}
@@ -245,8 +241,7 @@ namespace CounterStrikeSharp.API.Core
" list - List all plugins currently loaded.\n" +
" start / load - Loads a plugin not currently loaded.\n" +
" stop / unload - Unloads a plugin currently loaded.\n" +
" restart / reload - Reloads a plugin currently loaded."
, true);
" restart / reload - Reloads a plugin currently loaded.");
break;
}
}

View File

@@ -0,0 +1,7 @@
namespace CounterStrikeSharp.API.Modules.Commands;
public enum CommandCallingContext
{
Console = 0,
Chat = 1
}

View File

@@ -44,6 +44,12 @@ namespace CounterStrikeSharp.API.Modules.Commands
public string ArgByIndex(int index) => NativeAPI.CommandGetArgByIndex(Handle, index);
public string GetArg(int index) => NativeAPI.CommandGetArgByIndex(Handle, index);
/// <summary>
/// Whether or not the command was sent via Console or Chat.
/// </summary>
public CommandCallingContext CallingContext => NativeAPI.CommandGetCallingContext(Handle);
[Obsolete("Console parameter is now automatically set based on the context of the command.", true)]
public void ReplyToCommand(string message, bool console = false) {
if (CallingPlayer != null)
{
@@ -55,5 +61,26 @@ namespace CounterStrikeSharp.API.Modules.Commands
Server.PrintToConsole(message);
}
}
/// <summary>
/// Replies to the command with a message.
/// <remarks>
/// If the command was sent via Chat, <see cref="CCSPlayerController.PrintToChat"/> is used, otherwise <see cref="CCSPlayerController.PrintToConsole"/> is used.
/// If sent from the server console/RCON, <see cref="Server.PrintToConsole"/> is used.
/// </remarks>
/// </summary>
/// <param name="message">Message to send</param>
public void ReplyToCommand(string message)
{
if (CallingPlayer != null)
{
if (CallingContext == CommandCallingContext.Console) { CallingPlayer.PrintToConsole(message); }
else CallingPlayer.PrintToChat(message);
}
else
{
Server.PrintToConsole(message);
}
}
}
}

View File

@@ -530,7 +530,7 @@ namespace TestPlugin
[ConsoleCommand("cssharp_attribute", "This is a custom attribute event")]
public void OnCommand(CCSPlayerController? player, CommandInfo command)
{
command.ReplyToCommand("cssharp_attribute called", true);
command.ReplyToCommand("cssharp_attribute called");
}
[ConsoleCommand("css_changelevel", "Changes map")]

View File

@@ -84,6 +84,9 @@ bool gameLoopInitialized = false;
GetLegacyGameEventListener_t* GetLegacyGameEventListener = nullptr;
std::thread::id gameThreadId;
// Based on 64 fixed tick rate
const float engine_fixed_tick_interval = 0.015625f;
void Initialize()
{
modules::engine = new modules::CModule(ROOTBIN, "engine2");

View File

@@ -110,6 +110,8 @@ extern ISmmAPI *ismm;
extern CCoreConfig* coreConfig;
extern CGameConfig* gameConfig;
extern const float engine_fixed_tick_interval;
typedef IGameEventListener2 *GetLegacyGameEventListener_t(CPlayerSlot slot);
extern bool gameLoopInitialized;

View File

@@ -114,7 +114,7 @@ void ChatManager::InternalDispatch(CBaseEntity* pPlayerController, const char* s
if (pPlayerController == nullptr) {
globals::conCommandManager.ExecuteCommandCallbacks(
fullCommand.Arg(0), CCommandContext(CommandTarget_t::CT_NO_TARGET, CPlayerSlot(-1)),
fullCommand, HookMode::Pre);
fullCommand, HookMode::Pre, CommandCallingContext::Chat);
return;
}
@@ -123,6 +123,6 @@ void ChatManager::InternalDispatch(CBaseEntity* pPlayerController, const char* s
globals::conCommandManager.ExecuteCommandCallbacks(
fullCommand.Arg(0), CCommandContext(CommandTarget_t::CT_NO_TARGET, slot), fullCommand,
HookMode::Pre);
HookMode::Pre, CommandCallingContext::Chat);
}
} // namespace counterstrikesharp

View File

@@ -309,7 +309,7 @@ bool ConCommandManager::RemoveValveCommand(const char* name)
}
HookResult ConCommandManager::ExecuteCommandCallbacks(const char* name, const CCommandContext& ctx,
const CCommand& args, HookMode mode)
const CCommand& args, HookMode mode, CommandCallingContext callingContext)
{
CSSHARP_CORE_TRACE("[ConCommandManager::ExecuteCommandCallbacks][{}]: {}",
mode == Pre ? "Pre" : "Post", name);
@@ -319,6 +319,8 @@ HookResult ConCommandManager::ExecuteCommandCallbacks(const char* name, const CC
auto globalCallback = mode == HookMode::Pre ? m_global_cmd.callback_pre : m_global_cmd.callback_post;
m_cmd_contexts[&args] = callingContext;
if (globalCallback->GetFunctionCount() > 0) {
globalCallback->ScriptContext().Reset();
globalCallback->ScriptContext().Push(ctx.GetPlayerSlot().Get());
@@ -347,6 +349,7 @@ HookResult ConCommandManager::ExecuteCommandCallbacks(const char* name, const CC
}
if (!pInfo) {
m_cmd_contexts.erase(&args);
return result;
}
@@ -364,12 +367,15 @@ HookResult ConCommandManager::ExecuteCommandCallbacks(const char* name, const CC
auto thisResult = pCallback->ScriptContext().GetResult<HookResult>();
if (thisResult >= HookResult::Handled) {
m_cmd_contexts.erase(&args);
return thisResult;
} else if (thisResult > result) {
result = thisResult;
}
}
m_cmd_contexts.erase(&args);
return result;
}
@@ -380,7 +386,7 @@ void ConCommandManager::Hook_DispatchConCommand(ConCommandHandle cmd, const CCom
CSSHARP_CORE_TRACE("[ConCommandManager::Hook_DispatchConCommand]: {}", name);
auto result = ExecuteCommandCallbacks(name, ctx, args, HookMode::Pre);
auto result = ExecuteCommandCallbacks(name, ctx, args, HookMode::Pre, CommandCallingContext::Console);
if (result >= HookResult::Handled) {
RETURN_META(MRES_SUPERCEDE);
}
@@ -391,7 +397,7 @@ void ConCommandManager::Hook_DispatchConCommand_Post(ConCommandHandle cmd,
{
const char* name = args.Arg(0);
auto result = ExecuteCommandCallbacks(name, ctx, args, HookMode::Post);
auto result = ExecuteCommandCallbacks(name, ctx, args, HookMode::Post, CommandCallingContext::Console);
if (result >= HookResult::Handled) {
RETURN_META(MRES_SUPERCEDE);
}
@@ -401,4 +407,8 @@ bool ConCommandManager::IsValidValveCommand(const char* name) {
return pCmd.IsValid();
}
CommandCallingContext ConCommandManager::GetCommandCallingContext(CCommand* args) {
return m_cmd_contexts[args];
}
} // namespace counterstrikesharp

View File

@@ -53,6 +53,11 @@ struct CaseInsensitiveComparator {
namespace counterstrikesharp {
class ScriptCallback;
enum CommandCallingContext {
Console = 0,
Chat = 1,
};
class ConCommandInfo {
friend class ConCommandManager;
@@ -91,11 +96,13 @@ public:
void Hook_DispatchConCommand(ConCommandHandle cmd, const CCommandContext& ctx, const CCommand& args);
void Hook_DispatchConCommand_Post(ConCommandHandle cmd, const CCommandContext& ctx, const CCommand& args);
HookResult ExecuteCommandCallbacks(const char* name, const CCommandContext& ctx,
const CCommand& args, HookMode mode);
const CCommand& args, HookMode mode, CommandCallingContext callingContext);
CommandCallingContext GetCommandCallingContext(CCommand* args);
private:
std::vector<ConCommandInfo*> m_cmd_list;
std::map<std::string, ConCommandInfo*, CaseInsensitiveComparator> m_cmd_lookup;
std::map<const CCommand*, CommandCallingContext> m_cmd_contexts;
ConCommandInfo m_global_cmd = ConCommandInfo(true);
};

View File

@@ -314,7 +314,7 @@ void PlayerManager::OnClientCommand(CPlayerSlot slot, const CCommand& args) cons
globals::voiceManager.OnClientCommand(slot, args);
auto result = globals::conCommandManager.ExecuteCommandCallbacks(
cmd, CCommandContext(CommandTarget_t::CT_NO_TARGET, slot), args, HookMode::Pre);
cmd, CCommandContext(CommandTarget_t::CT_NO_TARGET, slot), args, HookMode::Pre, CommandCallingContext::Console);
if (result >= HookResult::Handled) {
RETURN_META(MRES_SUPERCEDE);

View File

@@ -107,7 +107,7 @@ void TimerSystem::OnGameFrame(bool simulating)
m_has_map_simulated = true;
}
} else {
timers::universal_time += globals::getGlobalVars()->interval_per_tick;
timers::universal_time += globals::engine_fixed_tick_interval;
}
m_last_ticked_time = globals::getGlobalVars()->curtime;

View File

@@ -118,6 +118,13 @@ static const char* CommandGetArgByIndex(ScriptContext& script_context)
return command->Arg(index);
}
static CommandCallingContext CommandGetCallingContext(ScriptContext& script_context)
{
auto* command = script_context.GetArgument<CCommand*>(0);
return globals::conCommandManager.GetCommandCallingContext(command);
}
static void IssueClientCommand(ScriptContext& script_context)
{
auto slot = script_context.GetArgument<int>(0);
@@ -194,6 +201,7 @@ REGISTER_NATIVES(commands, {
ScriptEngine::RegisterNativeHandler("COMMAND_GET_ARG_STRING", CommandGetArgString);
ScriptEngine::RegisterNativeHandler("COMMAND_GET_COMMAND_STRING", CommandGetCommandString);
ScriptEngine::RegisterNativeHandler("COMMAND_GET_ARG_BY_INDEX", CommandGetArgByIndex);
ScriptEngine::RegisterNativeHandler("COMMAND_GET_CALLING_CONTEXT", CommandGetCallingContext);
ScriptEngine::RegisterNativeHandler("FIND_CONVAR", FindConVar);
ScriptEngine::RegisterNativeHandler("SET_CONVAR_STRING_VALUE", SetConVarStringValue);

View File

@@ -6,6 +6,7 @@ COMMAND_GET_ARG_COUNT: command:pointer -> int
COMMAND_GET_ARG_STRING: command:pointer -> string
COMMAND_GET_COMMAND_STRING: command:pointer -> string
COMMAND_GET_ARG_BY_INDEX: command:pointer,index:int -> string
COMMAND_GET_CALLING_CONTEXT: command:pointer -> CommandCallingContext
ISSUE_CLIENT_COMMAND: slot:int,command:string -> void
ISSUE_CLIENT_COMMAND_FROM_SERVER: slot:int,command:string -> void
FIND_CONVAR: name:string -> pointer

View File

@@ -61,7 +61,7 @@ bool IsMapValid(ScriptContext& script_context)
float GetTickInterval(ScriptContext& script_context)
{
return globals::getGlobalVars()->interval_per_tick;
return globals::engine_fixed_tick_interval;
}
float GetCurrentTime(ScriptContext& script_context) { return globals::getGlobalVars()->curtime; }

View File

@@ -67,6 +67,8 @@ public class Mapping
return "ListenOverride";
case "DataType_t":
return "DataType";
case "CommandCallingContext":
return "CommandCallingContext";
case "any":
return "T";
}

View File

@@ -91,6 +91,7 @@ public partial class Generators
var result = $@"
using System;
using CounterStrikeSharp.API.Modules.Memory;
using CounterStrikeSharp.API.Modules.Commands;
namespace CounterStrikeSharp.API.Core
{{