Skip to content

Safer entity iteration, real success reporting, and better feedback for ForceInput - #24

Open
Rushaway wants to merge 1 commit into
masterfrom
fix/forceinput-safety-and-feedback
Open

Safer entity iteration, real success reporting, and better feedback for ForceInput#24
Rushaway wants to merge 1 commit into
masterfrom
fix/forceinput-safety-and-feedback

Conversation

@Rushaway

Copy link
Copy Markdown
Member

Summary

This PR fixes a few latent bugs in ForceInputs.sp and improves user feedback. It compiles cleanly with zero warnings on SourcePawn 1.12.0.7210.

Bugs fixed

1. Unsafe entity iteration (potential crash / wrong targets)

Command_ForceInput fired inputs while iterating FindEntityByClassname() for the #<HammerID>, classname and wildcard selectors. An input can create or destroy entities synchronously (Kill, entity-spawning inputs, templates, …), which invalidates the running enumeration — the exact class of problem this plugin's history is about ("Modified … to fix crash issues"). It could also apply the input to entities that were spawned by the same command.

Now the matching entities are collected into an ArrayList (as entity references) first, and the input is fired on that snapshot.

2. AcceptEntityInput() return value ignored

Every path printed "[SM] Input successful." unconditionally, even when the input name was invalid or the entity rejected it. The return value is now checked. Multi-entity / multi-player commands report a summary instead of spamming one line per entity:

  • [SM] Input "Foo" applied to 12 entities.
  • [SM] Input "Foo" applied to 9 of 12 entities, 3 failed.
  • [SM] No entities matched "func_nope".

3. Silent no-ops

sm_forceinput nonexistent Kill, an unknown HammerID, and !target aimed at the sky / world all did nothing with no reply. They now tell the admin what happened.

4. !target could not target player slot 1

The check was entity <= 1, which also excludes the first player slot. Changed to entity < 1 (worldspawn / no-hit still rejected).

5. sm_forceinputplayer nits

  • aTargetList was sized MAXPLAYERS and passed MAXPLAYERS as the max — now MAXPLAYERS + 1 / sizeof(...).
  • Guarded on IsClientInGame() (was IsValidEntity()), since forcing an input on a not-in-game client entity is meaningless.
  • LogAction() now receives the real target client instead of -1.

Improvements

  • RegAdminCmd() calls now include descriptions, so both commands show useful text in sm help.
  • Shared FireInput() / LogInput() helpers remove the copy-pasted SetVariantString + AcceptEntityInput + log blocks (4 near-identical copies).
  • Use the args parameter instead of GetCmdArgs().
  • Version bumped to 2.2.0.

Behaviour changes worth a look during review

  • Per-entity "Input successful." replies are replaced by a single summary line.
  • Invalid inputs now report failure instead of a false success.
  • !target on player slot 1 now works.

🤖 Generated with Claude Code

…er feedback

- Snapshot matching entities into an ArrayList before firing inputs. Firing
  an input while iterating FindEntityByClassname() is unsafe because an input
  can create or remove entities mid-iteration; this could crash the server or
  make the command act on entities it just spawned.
- Check the AcceptEntityInput() return value instead of always printing
  "Input successful.". Commands now report how many entities/players the input
  actually applied to, and how many failed.
- Reply when nothing matched the selector / crosshair instead of silently
  doing nothing.
- sm_forceinputplayer: size aTargetList as MAXPLAYERS + 1, gate on
  IsClientInGame(), and pass the real target to LogAction().
- !target: allow player slot 1 (was excluded by `entity <= 1`).
- Add command descriptions to RegAdminCmd() so they show up in `sm help`.
- Use the `args` parameter instead of GetCmdArgs(); minor cleanup.

Bump version to 2.2.0. Compiles clean on SourcePawn 1.12.0.7210.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings September 10, 2026 20:47

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

A few edge cases still produce misleading behavior (stale variant parameters, invalid HammerID parsing safety, and inaccurate success/failure counts when targets become invalid or aren’t in-game).

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR updates the ForceInputs SourceMod plugin to make entity targeting safer (by snapshotting matches before firing inputs), improve correctness of success/failure reporting, and provide clearer admin feedback for no-op situations.

Changes:

  • Snapshot matching entities into an ArrayList before firing inputs to avoid unsafe iteration while inputs can create/remove entities.
  • Add FireInput()/LogInput() helpers and switch to checking AcceptEntityInput() return values with aggregated success/failure summaries.
  • Improve UX via command descriptions in sm help, clearer no-match/no-target replies, and bump version to 2.2.0.
File summaries
File Description
addons/sourcemod/scripting/ForceInputs.sp Refactors input firing/logging and implements safer entity iteration plus better success/failure and no-op feedback.
.github/copilot-instructions.md Updates documented current plugin version to match the bump.
Review details
  • Files reviewed: 2/2 changed files
  • Comments generated: 5
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +40 to +46
bool FireInput(int entity, const char[] input, const char[] parameter, int activator, int caller)
{
if(parameter[0])
SetVariantString(parameter);

return AcceptEntityInput(entity, input, activator, caller);
}
Comment on lines 99 to 103
for(int i = 0; i < TargetCount; i++)
{
if(!IsValidEntity(aTargetList[i]))
if(!IsClientInGame(aTargetList[i]))
continue;

Comment on lines +204 to 207
if(sArguments[0][0] == '#') // HammerID
{
int HammerID = StringToInt(sArguments[0][1]);
int iHammerID = StringToInt(sArguments[0][1]);

Comment on lines +238 to +243
for(int i = 0; i < hEntities.Length; i++)
{
int entity = EntRefToEntIndex(hEntities.Get(i));

if(entity == INVALID_ENT_REFERENCE || !IsValidEntity(entity))
continue;
Comment on lines +33 to +34
RegAdminCmd("sm_forceinput", Command_ForceInput, ADMFLAG_ROOT, "Force an input on entities by classname/targetname/HammerID (supports !self, !target, #<HammerID> and * wildcards)");
RegAdminCmd("sm_forceinputplayer", Command_ForceInputPlayer, ADMFLAG_ROOT, "Force an input on one or more players");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants