mumble-voip_mumble/docs/dev/plugins/CreatePlugin.md

176 lines
6.9 KiB
Markdown

# Creating Mumble plugins
In the following the necessary steps to create a working Mumble plugin are outlined. These instructions cover the plain C API. If you are using a
[language binding](LanguageBindings.md) for a different programming language, different steps are usually required. Please refer to the binding's
documentation for that.
In the spirit of the classical [Hello world program](https://en.wikipedia.org/wiki/%22Hello,_World!%22_program), this guide will step you through the
creation of a "Hello Mumble" plugin.
The source code of the example plugin described here can be found in [this repository](https://github.com/mumble-voip/mumble-plugin-template). This
also intended to be used as the basis for everyone that wants to start writing a plugin without having to worry about the boilerplate themselves.
## Preparations
What you need for creating a plugin is
- A working C compiler. It does not matter which one
- The Mumble plugin header file: [MumblePlugin.h](https://github.com/mumble-voip/mumble/blob/master/plugins/MumblePlugin.h)
Although not strictly required, it usually is handy to use a build system for managing your plugin project. In this guide we'll use
[cmake](https://cmake.org/). If you have never used cmake before, have a look at [this short guide](https://stackoverflow.com/a/26007567).
All in all the following file structure is assumed to be present on your device:
```
.
├── include
│   └── MumblePlugin.h
├── CMakeLists.txt
└── plugin.c
```
The headers in `include` are the ones listed above and the other files will be populated during this guide.
## CMakeLists.txt
The `CMakeLists.txt` file is our cmake project file that tells cmake what we expect it to do.
In it, you have to put the following:
```cmake
cmake_minimum_required(VERSION 3.15)
project(MumblePlugin
VERSION "1.0.0"
DESCRIPTION "Minimal Mumble plugin"
LANGUAGES "C"
)
add_library(plugin
SHARED
plugin.c
)
target_include_directories(plugin
PUBLIC "${CMAKE_SOURCE_DIR}/include/"
)
```
If you want to understand the details it would be best if you searched for a proper cmake tutorial. The gist of it is that we tell cmake that we want
to build a shared library from the source file `plugin.c` and that everything in the `include` directory should be includable from it.
## Writing the plugin
Now that the boilerplate is out of the way, we can start writing the actual plugin. This will be done in the `plugin.c` source file.
The first thing you should do is to include `MumblePlugin.h`. Furthermore we'll need a few more C headers that we'll include as well:
```c
#include "MumblePlugin.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
```
Furthermore every plugin needs a way to store at least the Mumble-API and its own ID. In C this can be done using global variables. Therefore go ahead
and create the respective variables in the global namespace:
```c
MumbleAPI mumbleAPI;
mumble_plugin_id_t ownID;
```
Both data types are defined by the API via the included headers.
As stated in the docs of the [plugin-API](PluginAPI.md) there are several functions that you must implement in your plugin. The first of these is
`mumble_init`:
```c
mumble_error_t mumble_init(mumble_plugin_id_t pluginID) {
ownID = pluginID;
if (mumbleAPI.log(ownID, "Hello Mumble") != MUMBLE_STATUS_OK) {
// Logging failed -> usually you'd probably want to log things like this in your plugin's
// logging system (if there is any)
}
return MUMBLE_STATUS_OK;
}
```
As you can see the function takes the plugin's ID as a parameter, so make sure you store that in our respective variable. As you can see our Hello
Mumble plugin will use the Mumble-API to log something in Mumble's console. Note that it is safe to access the API here already due to the rules for a
[plugin's initialization processs](PluginLifecycle.md#initialization).
The final step is to return `MUMBLE_STATUS_OK` in order to let Mumble know that the plugin's initialization was successfull.
The next function to be implement is `mumble_shutdown` which is structured very similarly to `mumble_init`:
```c
void mumble_shutdown() {
if (mumbleAPI.log(ownID, "Goodbye Mumble") != MUMBLE_STATUS_OK) {
// Logging failed -> usually you'd probably want to log things like this in your plugin's
// logging system (if there is any)
}
}
```
Next up is `mumble_getName`:
```c
struct MumbleStringWrapper mumble_getName() {
static const char *name = "HelloMumble";
struct MumbleStringWrapper wrapper;
wrapper.data = name;
wrapper.size = strlen(name);
wrapper.needsReleasing = false;
return wrapper;
}
```
If you want to read details about why a `MumbleStringWrapper` is required, have a look at the [resource management docs](ResourceManagement.md).
The implementation of `mumble_getAPIVersion` is almost trivial as long as you are sticking to the API version the headers you are using belong to
(which is strongly recommended). In that case the constant `MUMBLE_PLUGIN_API_VERSION` will hold the correct version and all you have to do is to
return it from this function:
```c
mumble_version_t mumble_getAPIVersion() {
// This constant will always hold the API version that fits the included header files
return MUMBLE_PLUGIN_API_VERSION;
}
```
The function for receiving the Mumble-API function is implemented as follows:
```c
void mumble_registerAPIFunctions(void *apiStruct) {
// Provided mumble_getAPIVersion returns MUMBLE_PLUGIN_API_VERSION, this cast will make sure
// that the passed pointer will be cast to the proper type
mumbleAPI = MUMBLE_API_CAST(apiStruct);
}
```
Note that the function takes a `void *` and thus has to cast this pointer to the correct type itself. In the case that you are using the API version
corresponding to the included headers (again: as you should), this is easy thanks to the pre-defined macro `MUMBLE_API_CAST`. It will automatically
cast the pointer to the correct API type.
The final function that needs to be implemented is `mumble_releaseResource`. Note that because our `MumbleStringWrapper` used above specifies
`needsReleasing = false`, this function will never actually be called (unless you implement other functions that do return resources that need
releasing - see [Resource management](ResourceManagement.md)) and therefore a dummy implementation is enough for our purposes:
```c
void mumble_releaseResource(const void *pointer) {
// As we never pass a resource to Mumble that needs releasing, this function should never
// get called
printf("Called mumble_releaseResource but expected that this never gets called -> Aborting");
abort();
}
```
And that's it. This is all that is strictly required in order to get a working plugin.
Note however that you will probably also want to implement the following functions (though from a functional point of view that is completely
optional):
- `mumble_getVersion`
- `mumble_getAuthor`
- `mumble_getDescription`
All available functions are listed and documented in the [plugin-API headers](PluginAPI.md#header-files).