The Command Manager

So… I thought I could about write how the Command Manager of SourceMod works. You may ask, “What the hell? Who cares about some uninteresting command manager!?”. Well, I don’t know, and honestly, I don’t care. The devlog is so cool and I haven’t posted anything yet, and the first thing that came to my mind is the command manager, so that’s what I’m going to talk about now.

Ok, command manager…
First off, you can view its source here:
1) Public Interface
2) Class declaration
3) Implementation

Ok, so here is what you can do with it:
1) Register client, server, or mixed commands
2) Unregister registered commands
Hmm, that would be the most important things. You can also retreive information about registered commands, but I don’t want to cover that here.

1) Registering commands
The IVCommandManager interface has three RegisterCmd members: One of them is taking an ordinary function pointer, the second one takes an IScript pointer and a function name, and the third one takes a pointer to a class, which has to inherit from ICommandFunc.
1.1) Client commands
For client commands, the Server Plugin Interface provides the ClientCommand callback, so in theory, you should be able to catch all the client commands from there. However, if the client types a command that is already registered by the GameDLL, ClientCommand never gets called. That’s why the command manager registers a new command callback in such a case, which then calls the Command Manager’s generic command callback. But, as you might know, this completly kills the GameDLL’s command. So, to call the original gamedll function, such hooks first find it in the command list, and store a pointer to it. Then, when the command was not handled by SourceMod or its plugins / modules, the gamedll command gets executed.
1.2) Server commands
There is no generic ServerCommand function, so server commands use the second client command method.

But now, imagine that such a hook gets deleted. When the engine tries to access it, the game will crash. So, we need a method to remove any command from the command list. HL2 stores the commands in a linked list; it only stores the first one, and every command maintains its pointer to the next command. Note that there is no pointer to the previous command. New commands get added at the beginning of the list.

So, we have two cases:
1) The command we want to remove is NOT at the beginning of the list
In this case, we go through the list, until we find the command we want to remove. We set the previous command’s next pointer to our next pointer, and that’s all.
2) The command we want to remove IS at the beginning of the list
In this case, we have to modify the engine’s internal command list pointer. We don’t have direct access to it, so we need to use a workaround. What I have used is a command that is always registered (that’s why I gave it the name “eternal command”). It has a method, called BringToTop, that unregisters itself from the list and reregisters again. So, when you want to remove a command that is at the beginning, you call g_EternalCommand.BringToTop(), and then you do the same as in 1 (except more simple, as you only have to set g_EternalCommand’s next pointer to your next pointer).

Umm, yes, that’s all I wanted to post about; check out the source for more details.


Leave a Reply

You must be logged in to post a comment.