What steps are involved in making a new DFHack plugin? I was thinking of making a flood-fill traffic designation utility, but I couldn't figure out where to start.
Well. Let's see. The only plugin that uses flood fills is the vein digger. You can
see it here.This file is compiled into a library. DFHack then loads the library and requires some functions to be present:
plugin_name should always return the same name as the resulting plugin library file name. (see the bit about adding the plugin to the build system later)
plugin_init allows your plugin to declare which commands it exports to the dfhack console. There's an extra 'bool' parameter that declares if the command is interactive - accepts input from console. Such commands can't be used from a hotkey.
plugin_shutdown is called right before the plugin is unloaded by dfhack. You *must* clean up here. Any threads you might have started and are executing code from your plugin must be finished before this function returns (this applies to things like stonesense).
A command - in this case it's the vein digger command. It uses flood fills and shows how to use the
map cache, which is a higher-level abstraction built on top of the
Maps module. Using the Maps module directly would be much harder than using the cache, because the cache basically treats the DF map as one continuous space, hiding the underlying structures to an extent. This is very good for doing flood fills
A plugin can optionally include a
plugin_onupdate function. In this case, it's the one from the reveal plugin, making it impossible to unpause if hell is revealed (to my great relief, not having to handle all the problems that caused for people).
Now a bit about synchonizing with the game
What happens is that the game has its ows thread where it executes everything interesting. This thread calls a function in DFHack, which in turn calls all the plugin_onupdate functions. That means that you don't have to synchronize with the game in this function.
The command functions are a bit different - they are tied to the console thread. When you type a command into the console, your command function will be called. There, you must call Core::Suspend to wait for the next time DF is idle and not doing stuff. After you are done with whatever you need to do, you have to call Core::Resume. This hands off the DF thread to the next command waiting, or DF itself. A lot of thread synchronization magic is hidden behind the DFhack API - Ideally, you won't ever have to deal with it directly.
You can use the
Console object to mess with the console. A command can print to the console any time, but if you need interactivity, it has to be
declared like this to prevent some breakage. The console implements a line editor and is thread safe. Only one thing can read from it at a time though, and only safe way to do it is with the lineedit method. The liquids tool has some examples of this.
To make it compile, you put your .cpp file in plugins/ and
add it to the build system like this. Then rerun cmake to generate new project files.
Where you go from this point is up to you. Ideally, it will become a part of DFHack that can be maintained. If you can, fetch the dfhack source tree with git and work on top of that, even better, fork the project on github and publish your changes there.
And stop by in #dfhack on freenode. There's plenty of friendly people there