0
0
Fork 0
mirror of https://github.com/mumble-voip/mumble.git synced 2025-03-14 20:53:06 +00:00
mumble-voip_mumble/docs/dev/plugins/CreatePlugin.md
Robert Adam 2b35c0c28f REFAC(plugins): Unified Mumble plugin headers
Having different include files that are needed (and which are
inter-dependent) to create your own plugin, makes things harder than it
needs to be.

Therefore, all plugin header files (those for the "new" (1.4) plugin
framework anyway) have been combined into one header file. Thus,
developers now only have to download a single file and include that
instead of having to figure out what files to download and what to
include where.

Taking the chance, the version number has been removed from the header
file's name. This allows one to track changes made to the API via git
(which is not quite as easy if you create a new file every time you make
a change).
2023-07-27 19:39:30 +02:00

175 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).