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 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, 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. 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.
What you need for creating a plugin is
- A working C compiler. It does not matter which one
- The Mumble plugin framework header files which are the following (the exact version number in the filename may change depending on which API version you intend to use):
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. If you have never used cmake before, have a look at this short guide.
All in all the following file structure is assumed to be present on your device:
.
├── include
│ ├── MumbleAPI_v_1_0_x.h
│ ├── MumblePlugin_v_1_0_x.h
│ └── PluginComponents_v_1_0_x.h
├── CMakeLists.txt
└── plugin.c
The headers in include
are the ones listed above and the other files will be populated during this guide.
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_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.
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_v_1_0_x.h
. Furthermore we'll need a few more C headers that we'll include as well:
#include "MumblePlugin_v_1_0_x.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:
struct MumbleAPI_v_1_0_x 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 there are several functions that you must implement in your plugin. The first of these is
mumble_init
:
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.
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
:
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
:
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.
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:
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:
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) and therefore a dummy implementation is enough for our purposes:
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.