Archive for the ‘Wiki Articles’ Category

KeyValues (SourceMod Scripting)

Monday, May 7th, 2007

Today’s article is a wiki article about KeyValues (SourceMod Scripting).

While we don’t encourage using KeyValues for C++, they work quite nicely in scripts, where event based parsers are more complicated to set up, especially since dynamic structures do not otherwise exist yet.

The original API for KeyValues was very confusing, which was a direct result of Valve’s header being poorly documented. However, the current version of the API is much more useful and covers most of the use cases as easily as possible. The wiki article goes into detail about iteration, traversal, and deletion.

The only noteworthy (and confusing) thing about SourceMod’s KeyValues API is the traversal stack. Why does it maintain an internal stack of how deep you’ve nested? Because the only other solution would be to return a new Handle. Observe the difference:

native bool:KvFindKey(Handle:kv, const String:name[]);
native Handle:KvFindKey(Handle:kv, const String:name[]);

In the second example, the user must explicitly call CloseHandle simply for finding a sub-section. Not only is this extremely annoying, but also inefficient, and would lead to a myriad of memory leaks due to people forgetting to close simple iterations everywhere.

C++ users don’t have to worry about this, of course. This is a consequence of pointers needing to be stuffed through Handles combined with the lack of a garbage collector.

Events in SourceMod

Friday, May 4th, 2007

Today we’re introducing another wiki article: Events (SourceMod Scripting).

Events are one of the nicest additions to HL2, providing a message passing system similar to network messages, but more user friendly and much easier to debug. You’ll notice one major piece missing from the API: enumeration. Event properties are not enumerable.

Unfortunately, this was only a feature in Valve’s original API, IGameEventManager. When Valve added IGameEventManager2 as a more optimized API, they never bothered to expose the KeyValues functionality implicit in IGameEvent (implemented as CGameEvent). Since the original API is deprecated, SourceMod uses the newer version.

Of course, SourceMod’s event API is completely unrelated to AMX Mod X’s register_event, which was based on user messages. The Half-Life 2 game event manager only touches netcode when it transmits events (for which there is a special svc code). (AMX Mod X’s function should have been named register_message from the start.)

ConVars (cvars) in SourceMod

Wednesday, May 2nd, 2007

Today’s article is mostly a wiki addition: ConVars (SourceMod Scripting).

The major difference between SourceMod’s cvar API and AMX Mod X’s is how cvars are addressed. AMX Mod X references cvars by name, a string. SourceMod’s cvar API references by a Handle, which contains the ConVar pointer.

AMX Mod X’s failure to use the HL1 cvar_t pointer is nothing short of horrendous. The only benefit is that it makes coding much easier for newcomers. Unfortunately, it’s a huge performance issue. There are hundreds of cvars, and doing named lookups for every cvar operation is extremely painful, especially if the same cvar is used over and over again.

Even more concerning, it took the AMX Mod X developers (me included) a long time to notice this. Only later in the 1.x branch were “pcvar” natives introduced. These natives dealt with direct cvar_t pointers, and used the engine call SET_CVAR_DIRECT to make changes. One must wonder why HL1 even had SET_CVAR_FLOAT and SET_CVAR_STRING, as it basically encourages using much slower code.

While many people still use AMX Mod X’s name based lookup, “pcvar” usage is encouraged. Of course with SourceMod, it is the only option.

SourceMod maintains similarity with AMX Mod X’s “pcvar” API by using one Handle for every named cvar. If two plugins create or find the same cvar, they will both receive the same Handle. Thus, no plugin owns any cvars, and cvars are not removed until SourceMod unloads. This has a few benefits:

  • You can directly compare cvar Handles. For example, instead of doing a strcmp() call, you can do if (hndl1 == hndl2)
  • If a plugin unloads, the cvar will stick around, so any settings will still be set if the plugin reloads or refreshes.
  • Plugins don’t have to call CloseHandle() simply for finding a ConVar

The other major difference is that HL2 engine passes ConVar changes through a callback. Thus, you can detect changes without making a secondary console command. This was not possible in HL1. Although you could hook SET_CVAR_FLOAT, it was not virtual, and thus internal or plugin usage of it would not be caught. Also, the change callbacks are per-ConVar; HL1′s is global, and thus a bit less optimized.

On Timer Design, Part 3 of 3

Monday, April 30th, 2007

Today let’s finish up two previous articles on timer design.

As we learned, SourceMod drops features of previous timer designs in order to make it as streamlined as possible. Timers have one unchangeable interval. If you want them to repeat, they repeat until you explicitly tell them to stop. You can slide an optional integer through the callback, but no arrays with strange copyback expectations or parameter re-ordering messes. This design doesn’t restrict you. It simply means that you have to use the other primitives of SourceMod as building blocks for your scripts.

To read on, see today’s wiki article, Timers (SourceMod Scripting), where various common examples are addressed:

  • One-time timers
  • Killing timers without using a crutch like AMX Mod X’s task_exists()
  • Counting repeatable timers
  • Passing basic data to a timer
  • Passing advanced data to a timer

Function Calling API Wiki Article

Wednesday, April 18th, 2007

Today’s article is a wiki article on Function Calling API (SourceMod Scripting).

This has been covered here before; this article briefly explains the difference between forwards and single calls, and the difference between global and private forwards. Most importantly, it demonstrates the plugin-level API for managing both.

Creating Natives (Scripting)

Monday, April 16th, 2007

Today’s addition is another update to the SDK and wiki: Creating Natives (SourceMod Scripting).

This article goes in depth about creating natives (“dynamic” or “fake” natives) from plugins, for other plugins to use. This was discussed previously here.

Half-Life Entity Properties

Monday, March 26th, 2007

Today’s Dev Log article is on the wiki: Entity Properties.

This article discusses about how the two major entity property types work in Source: network variables and save/restore fields. It also shows how they can be enumerated from server-side plugins.

Command API, Part 3 of 3

Monday, March 5th, 2007

Today’s wiki update is a short addition about client-only commands. What are client-only commands? They are commands that are ONLY meant to be processed from clients, and never from the server.

In Half-Life 1, the distinction was simple. If a command was typed through the client’s console, it would be sent through the DLL_FUNCTIONS::ClientCommand function. Server commands had to be explicitly registered, whereas client command strings were always forwarded through one function. AMX Mod X derived four hooks from this:

  • register_srvcmd: Explicitly registers one server command through the HL1 engine.
  • register_clcmd: Adds a filter rule for catching strings passed through ClientCommand.
  • register_concmd: Adds both a filter rule for ClientCommand AND registers an explicit server command.
  • client_command(id): Forward called whenever a string passes through ClientCommand.

How does Half-Life 2 complicate this? When explicit server commands are registered, they can optionally be flagged as FCVAR_GAMEDLL (and in fact any command the game mod registers should have this flag). When a client sends a command string from their console, the Engine does the following:

  1. Are there any server commands matching what the client typed?
  2. If not, pass the string normally through IServerGameClients::ClientCommand.
  3. If so, does the server command have the FCVAR_GAMEDLL flag?
  4. If not, pass the string normally through IServerGameDLL::ClientCommand.
  5. If so, instead, act as if the server command itself was called. The server command must then listen to IServerGameClients::SetCommandClient to see whether the server or a client sent the command.

This is actually much closer to the AMX Mod X model, because it allows for dual-purpose commands with one implementation. Let’s take a look at how the SourceMod functions work with this new system:

  1. RegServerCmd: Explicitly registers a server command, or hooks an already existing one. The callback is only used if the source was the server, and not a client.
  2. RegConsoleCmd/RegAdminCmd: Hooks an existing console command; if it doesn’t exist, a new one is created without the FCVAR_GAMEDLL flag. The callback is called whether or not it’s from a client. It is hooked from both the explicit server command’s function and the IServerGameClients::ClientCommand function, because when a client uses it, it may go to either one.
  3. OnClientCommand(id, args): Hooks any command string passing through IServerGameClients::ClientCommand.

It should now be apparent why we really couldn’t implement a register_clcmd filter — not all client commands pass through IServerGameClients::ClientCommand! Likewise, this means the OnClientCommand forward will not recieve some client commands. Most importantly is that “say” is one of these dual commands, as it can be used by both the server and the client. The most useful command to filter can’t be filtered!

This is actually a somewhat clever idea on Valve’s part, so it’s not a design flaw. It just means that in order to hook say, you must register it as a console command, and that it will never pass through OnClientCommand (at least in Counter-Strike:Source).