Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mumble-voip/mumble.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
authorRobert Adam <dev@robert-adam.de>2021-06-03 12:28:24 +0300
committerRobert Adam <dev@robert-adam.de>2021-06-06 18:49:41 +0300
commit54012c5a07cde75b86cef0c814144cbb1e845393 (patch)
treea87e827478379a86ea293a00c650ee1718c75c8f /docs
parent9d5feb54e650148b2da65e1d3db282bf6872ed6f (diff)
DOCS: Document new plugin system
Diffstat (limited to 'docs')
-rw-r--r--docs/dev/plugins/Bundling.md49
-rw-r--r--docs/dev/plugins/CreatePlugin.md181
-rw-r--r--docs/dev/plugins/Debugging.md17
-rw-r--r--docs/dev/plugins/LanguageBindings.md15
-rw-r--r--docs/dev/plugins/MumbleAPI.md103
-rw-r--r--docs/dev/plugins/PluginAPI.md43
-rw-r--r--docs/dev/plugins/PluginLifecycle.md20
-rw-r--r--docs/dev/plugins/PositionalAudioPlugin.md51
-rw-r--r--docs/dev/plugins/README.md60
-rw-r--r--docs/dev/plugins/ResourceManagement.md43
-rw-r--r--docs/dev/plugins/Versioning.md16
11 files changed, 598 insertions, 0 deletions
diff --git a/docs/dev/plugins/Bundling.md b/docs/dev/plugins/Bundling.md
new file mode 100644
index 000000000..8f559345e
--- /dev/null
+++ b/docs/dev/plugins/Bundling.md
@@ -0,0 +1,49 @@
+# Bundling a Mumble plugin
+
+After you have built your plugin you will have a shared library for the respective target OS. While it is possible to just give users this shared
+library and tell them to install them, it might get inconvenient at times. Especially if you want to support your plugin for multiple OS.
+
+Therefore Mumble provides a way to package your plugin in a user-friendly and cross-platform way.
+
+It works by putting the shared libraries for the different platforms into a single zip-file and then changing the archive's file extension from `.zip`
+to `.mumble_plugin`.
+
+The archive must not contain anything besides the plugin libraries and these should follow the following naming conventions:
+- The shared library should contain the name of the plugin
+- followed by the OS specifier
+- followed by the architecture specifier
+
+The OS specifier may be one of the following
+| **OS** | **Specifier** |
+| ------ | ------------- |
+| Windows | `_win` |
+| Linux | `_linux` |
+| macOS | `_macos` |
+
+and the architecture specifier is one of
+| **Architecture** | **Specifier** |
+| x86 (64-bit) | `_x86_64` |
+| x86 (32-bit) | `_i386` |
+| ARM (v5) | `_armv5` |
+| ARM (v6) | `_armv6` |
+| ARM (v7) | `_armv7` |
+
+Therefore a plugin library may be named for instance
+```
+myPlugin_linux_x86_64.so
+```
+or
+```
+myPlugin_win_i386.dll
+```
+
+Platform-specific prefixes such as `lib` on Unix-systems are allowed as well. Therefore
+```
+libmyPlugin_linux_x86_64.so
+```
+is completely equivalent to the example given above.
+
+Note that plugin names **must not** contain version numbers. At least not when they are distributed to end users. This is because Mumble assumes that
+the new version of a plugin will use a shared library with the exact same name as the old one. If this assumption is violated, the update mechanism
+will not work properly causing the new and the old version of the plugin to be installed in parallel.
+
diff --git a/docs/dev/plugins/CreatePlugin.md b/docs/dev/plugins/CreatePlugin.md
new file mode 100644
index 000000000..2fc074d2c
--- /dev/null
+++ b/docs/dev/plugins/CreatePlugin.md
@@ -0,0 +1,181 @@
+# 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 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):
+ * [MumbleAPI_v_1_0_x.h](https://github.com/mumble-voip/mumble/blob/master/plugins/MumbleAPI_v_1_0_x.h)
+ * [MumblePlugin_v_1_0_x.h](https://github.com/mumble-voip/mumble/blob/master/plugins/MumblePlugin_v_1_0_x.h)
+ * [PluginComponents_v_1_0_x.h](https://github.com/mumble-voip/mumble/blob/master/plugins/PluginComponents_v_1_0_x.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
+│   ├── 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.
+
+
+## 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_v_1_0_x.h`. Furthermore we'll need a few more C headers that we'll include as well:
+```c
+#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:
+```c
+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](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).
diff --git a/docs/dev/plugins/Debugging.md b/docs/dev/plugins/Debugging.md
new file mode 100644
index 000000000..75bff2ad1
--- /dev/null
+++ b/docs/dev/plugins/Debugging.md
@@ -0,0 +1,17 @@
+# Debugging a Mumble plugin
+
+Generally you have to resort to the standard debugging techniques when it comes to debugging the functionality inside your plugin. If however you want
+to find out why Mumble doesn't recognize any given function in your plugin or you think that the issue may lie within Mumble somewhere, you have to
+[build Mumble](../build-instructions/README.md) yourself.
+
+When doing so, you activate the `plugin-debug` and/or `plugin-callback-debug` options when invoking cmake:
+```bash
+cmake -Dplugin-debug=ON -Dplugin-callback-debug=ON ..
+```
+
+The first option (`plugin-debug`) will cause Mumble to be verbose about the function resolution process in plugins. That means it will report which
+functions of the plugin interface Mumble recognizes as implemented in any given plugin.
+
+The second option will cause Mumble to print a log message to the console every time it encounters an event that is passed to interested plugins via
+the respective callback function. Note that this output can be excessively verbose due to the audio-related events.
+
diff --git a/docs/dev/plugins/LanguageBindings.md b/docs/dev/plugins/LanguageBindings.md
new file mode 100644
index 000000000..8cecfd4b8
--- /dev/null
+++ b/docs/dev/plugins/LanguageBindings.md
@@ -0,0 +1,15 @@
+# Mumble plugin - Language bindings
+
+## C
+
+`C` is the native language of the plugin API. If you intend to use it, you don't need any special bindings.
+
+
+## C++
+
+There exists an official `C++` wrapper around the C API. It can be found at https://github.com/mumble-voip/mumble-plugin-cpp.
+
+## Rust
+
+`Rust` bindings are maintained independently from the main Mumble project. They can be found at https://crates.io/crates/mumble-sys.
+
diff --git a/docs/dev/plugins/MumbleAPI.md b/docs/dev/plugins/MumbleAPI.md
new file mode 100644
index 000000000..7acc7fa71
--- /dev/null
+++ b/docs/dev/plugins/MumbleAPI.md
@@ -0,0 +1,103 @@
+# The Mumble-API
+
+The Mumble-API is a set of function pointers that is given to the plugin during the [initialization process](PluginLifecycle.md#initialization) via
+the `mumble_registerAPIFunctions` function in the [plugin-API](PluginAPI.md).
+
+
+## How it works
+
+As a plugin developer you don't have to worry about the implementation of these functions. They are implemented on Mumble's side. All you have to care
+about is that certain functions exist and that you are free to call any of them.
+
+In order to do so, you need to know the function signature which is defined in the Mumble-API header file. Functions in there are defined as e.g.
+```cpp
+mumble_error_t(PLUGIN_CALLING_CONVENTION *isConnectionSynchronized)(mumble_plugin_id_t callerID, mumble_connection_t connection, bool *synchronized);
+```
+The definition may seem a bit odd and intimidating at first glance but that is only because these functions need to be specified as [function
+pointers](https://www.geeksforgeeks.org/function-pointer-in-c/). Here are a set of easy steps to (mentally) convert this notation into a (probably)
+more familiar format:
+1. Ignore `PLUGIN_CALLING_CONVENTION`. The [calling convention](https://en.wikipedia.org/wiki/Calling_convention) is nothing you have to worry about.
+ This is handled for you by your compiler
+2. Remove the first set of parenthesis and the star within them. What remains is the function name
+
+Applying these steps to the above function yields the following definition:
+```cpp
+mumble_error_t isConnectionSynchronized(mumble_plugin_id_t callerID, mumble_connection_t connection, bool *synchronized);
+```
+
+Thus we can see that this is a function called `isConnectionSynchronized` that takes 3 parameters and returns a `mumble_error_t`. Therefore if we
+assume that you have stored the Mumble-API in a variable named `mumbleAPI` in your plugin, you could call this function as
+```cpp
+mumble_error_t returnedError = mumbleAPI.isConnectionSynchronized(...);
+```
+
+## General function structure
+
+Each function follows the general signature
+```cpp
+mumble_error_t myFunction(mumble_plugin_id_t callerID /* potentially more arguments */);
+```
+
+That means that each function _always_ returns an error code that indicates whether the API call was successful. How to handle this returned error
+code is described in the [error handling section](#error-handling).
+
+Furthermore the first parameter is _always_ the ID of the plugin that makes the API call. Your plugin's ID is given to you during the [initialization
+process](PluginLifecycle.md#initialization) via the `mumble_init` function. You have to store this ID and use it every time you make a call to the
+Mumble-API as otherwise your calls will be ignored by Mumble.
+
+Given that the return value of Mumble-API functions are always occupied with the error code, all functions that query some sort of information use an
+_out parameter_. That means that you pre-allocate a variable of the respective type and then pass a pointer to that variable to the API function. On
+successful execution this function will then set the value of your variable through the given pointer.
+
+A good example is obtaining the currently active server connection:
+```cpp
+mumble_connection_t activeConnection;
+if (mumbleAPI.getActiveServerConnection(pluginID, &activeConnection) == MUMBLE_STATUS_OK) {
+ // Do something with activeConnection
+}
+```
+
+
+## Error handling
+
+The error code returned by Mumble-API functions can (and often must) be used to determine whether the given function call was successful. If the error
+code compares equal to `MUMBLE_STATUS_OK` (a macro defined in the `PluginComponents` header that is automatically included in the Mumble-API header),
+then the API call was successful. **Every other value indicates failure**.
+
+In case of an error, the error code will give information on the exact problem that was found. The possible error codes are defined in the
+`Mumble_ErrorCode` enum (also defined in `PluginComponents`). These values can be used to check the error code against certain expected errors in the
+error-handling branch of your code.
+
+If you want to log the error in some way, it is recommended to use the `errorMessage` function (yet again defined in the `PluginComponents` header).
+This function will return a String-representation of the respective error.
+
+Given that errors are only reported in form of error codes, it is essential to always explicitly check for them. This is especially important when
+these API function are used to query information. Accessing the variable passed as an out-parameter to a Mumble-API function may lead to undefined
+behavior unless you have initialized that variable to a defined value before passing it to the function.
+
+The only guarantee about variables given as out-parameters to API functions in case of an error is that they remain unchanged by the function.
+
+
+## Multithreading
+
+All API functions are synchronized. That means they can be called from an arbitrary thread. In order to achieve this synchronization, the functions
+are executed in the main thread on Mumble's side. As the functions return a value, the caller has to wait for the execution to have finished in order
+to continue. In other words: API function calls are **blocking**.
+
+Therefore special care is to be taken as this can easily lead to deadlocks as soon as the plugin spawns a custom thread and uses the API functions
+from it. An example of an easy way to run into such a deadlock is as follows:
+
+Assume the plugin has spawned a worker thread that performs some kind of work asynchronously. Let's call that thread "W". Now an event occurs that
+causes a event callback to be called in the plugin-API which usually happens in the already mentioned main thread (see also
+[Threading](PluginAPI.md#threading)). Inside this event callback, the plugin notifies W to perform some sort of work and waits for that to finish. For
+performing that work W needs to call an API function which blocks until the function can be executed in the main thread. Given that the main thread is
+currently blocked by the plugin callback, a deadlock occurs.
+
+That is to say: You must not wait for a job to finish asynchronously that might call an API function from the plugin-API.
+
+
+## Header files
+
+The following header files describe the Mumble-API struct that contains the function pointers as well as descriptions of the respective functions:
+- [Mumble-API v1.0.x](https://github.com/mumble-voip/mumble/blob/master/plugins/MumbleAPI_v_1_0_x.h)
+
diff --git a/docs/dev/plugins/PluginAPI.md b/docs/dev/plugins/PluginAPI.md
new file mode 100644
index 000000000..4764ca93b
--- /dev/null
+++ b/docs/dev/plugins/PluginAPI.md
@@ -0,0 +1,43 @@
+# The plugin-API
+
+The plugin-API consist of a collection of C functions that can be implemented by a plugin. A hand full of these must be implemented by every plugin in
+order for it to get recognized as such by Mumble.
+
+Generally all plugin-API functions are only called if the plugin is currently active. That means that it is save to assume that these function calls
+happen between the call to `mumble_init` and `mumble_shutdown`. This is important if the plugin allocates some resources to use during its lifetime
+(allocated in the init function and deallocated again in the shutdown function) as these can then always be safely accessed in these functions.
+
+There are a few exceptions to this rule though. Certain functions are supposed to (and will be) called even if the plugin is not active. In these
+functions you must not make any assumptions about certain resources in your plugin being initialized. Furthermore you must not allocate resources in
+these functions as the plugin might never get the chance to clean them up again (thereby causing a memory leak).
+
+An example of such a function is `mumble_getName`.
+
+All functions that fall in this category are documented with a special note pointing this behavior out in the function's documentation.
+
+
+## Mandatory functions
+
+These functions must be implemented by every plugin. All other functions in the plugin-API are optional and whether or not to implement them is up to
+the plugin developer.
+
+| **Name** | **Description** |
+| -------- | --------------- |
+| `mumble_init` | Used to initialize the plugin upon starting it |
+| `mumble_shutdown` | Called when the plugin gets shut down. This is required for plugins to perform any sort of clean-up |
+| `mumble_getName` | Gets the name of the plugin |
+| `mumble_getAPIVersion` | The version of the API this plugin is using. This simultaneously determines the version of the plugin-API as well as the Mumble-API |
+| `mumble_registerAPIFunctions` | Used to provide the plugin with the [Mumble-API](MumbleAPI.md) functions |
+| `mumble_releaseResource` | Used to ask the plugin to free any resource it has allocated for passing them to Mumble |
+
+
+## Threading
+
+Unless otherwise noted in the function's description, all functions all called from the same thread.
+
+
+## Header files
+
+The following header files describe the plugin-API interface. They also contain a description of what each function is for.
+- [Plugin-API v1.0.x](https://github.com/mumble-voip/mumble/blob/master/plugins/MumblePlugin_v_1_0_x.h)
+
diff --git a/docs/dev/plugins/PluginLifecycle.md b/docs/dev/plugins/PluginLifecycle.md
new file mode 100644
index 000000000..5e435f8d2
--- /dev/null
+++ b/docs/dev/plugins/PluginLifecycle.md
@@ -0,0 +1,20 @@
+# Mumble plugin lifecycle
+
+## Initialization
+
+When the user chooses to activate an installed plugin, the following functions inside that plugin will be called (if implemented):
+1. `mumble_setMumbleInfo` - Tells the plugin about which version of Mumble is about to load it
+2. `mumble_getAPIVersion` - Ask the plugin which API version it is using
+3. `mumble_registerAPIFunctions` - Provides the [Mumble-API](MumbleAPI.md) functions to the plugin
+4. `mumble_init` - Actually initialize the plugin
+
+Most notably: The plugin will _always_ receive the Mumble-API function _before_ `mumble_init` is called. Therefore the API can already be used in
+`mumble_init`, if required.
+
+
+## Shutdown
+
+When deactivating a plugin, `mumble_shutdown` will be called. Note that during this shutdown method it is still safe to use the Mumble-API. As soon as
+this function returns though, the Mumble-API that was given to the plugin must be considered invalid and must no longer be used. This is important if
+your plugin has spawned a separate thread that might end asynchronously.
+
diff --git a/docs/dev/plugins/PositionalAudioPlugin.md b/docs/dev/plugins/PositionalAudioPlugin.md
new file mode 100644
index 000000000..f6a1f5a81
--- /dev/null
+++ b/docs/dev/plugins/PositionalAudioPlugin.md
@@ -0,0 +1,51 @@
+# Creating a positional audio plugin
+
+This is a description that covers how a plugin that wants to provide positional audio support in Mumble for a given game can be created using the
+plugin framework that was introduced with Mumble 1.4.0. This is **not** a description of how one can try to interface or peek into games in order to
+figure out the player's position. For this see our [guide on positional audio
+fetching](https://www.mumble.info/documentation/developer/positional-audio/create-plugin/).
+
+A positional audio plugin needs to fulfill the same requirements that any other Mumble plugin has to match. Therefore the [guide on writing a
+plugin](CreatePlugin.md) is a must-read before continuing further.
+
+Starting from the plugin that was written in the linked guide (minus the calls to th `log` API function), the following functions have to be
+implemented in addition:
+```c
+uint32_t mumble_getFeatures() {
+ return MUMBLE_FEATURE_POSITIONAL;
+}
+
+uint8_t mumble_initPositionalData(const char *const programNames, const uint64_t programPIDs, size_t programCount) {
+ // Check if the supported game is in the list of programs and if yes
+ // check if the position can be obtained from the program
+
+ // If everything went well
+ return MUMBLE_PDEC_OK;
+ // Other potential return values are:
+ // MUMBLE_PDEC_ERROR_TEMP -> The plugin can temporarily not deliver positional data
+ // MUMBLE_PDEC_PERM -> Permanenet error. The plugin will never be able to deliver positional data
+}
+
+bool mumble_fetchPositionalData(float *avatarPos, float *avatarDir, float *avatarAxis, float *cameraPos, float *cameraDir,
+ float *cameraAxis, const char **context, const char **identity) {
+ // fetch positional data and store it in the respective variables. All fields that can't be filled properly
+ // have to be set to 0 or the empty String ""
+
+ // If positional data could be fetched successfully
+ return true;
+ // otherwise return false
+}
+
+void mumble_shutdownPositionalData() {
+ // Unlink the connection to the supported game
+ // Perform potential clean-up code
+}
+```
+
+The first function is necessary to let Mumble know that this plugin is generally capable of gathering positional data from games. Only then will the
+init function be called every now and then, checking whether the plugin can currently link to the game's data. If so, the fetch function will be
+called repeatedly in order to pass the gathered positional data to Mumble for use in positional audio.
+
+After the plugin has lost link or Mumble (the user) decides that positional data from this plugin is no longer needed, the shutdown function is called
+in which the plugin should terminate its connection to the game and clean up after itself.
+
diff --git a/docs/dev/plugins/README.md b/docs/dev/plugins/README.md
new file mode 100644
index 000000000..fad263f76
--- /dev/null
+++ b/docs/dev/plugins/README.md
@@ -0,0 +1,60 @@
+# The Mumble plugin framework
+
+Mumble supports loading plugins that provide extra functionality if enabled. Users can choose which plugins to install and which to enable at which
+point in time.
+
+One example use of plugins is the connection to a game that is running. In this case the plugin is responsible for fetching the current player
+position from the game (usually by inspecting the game's in-memory representation) and providing this information to Mumble in order to make the
+positional audio feature work.
+
+This kind of plugin has existed for quite a while but only from 1.4.0 onwards does Mumble support general purpose plugins. That means that plugins can
+do basically everything (provided the API supports it). For instance you could program a plugin that always sets your comment to "Bananaaaa" when you
+connect to a server or one that automatically moves you into a channel named "AFK" once you deafen yourself.
+
+## Contents
+
+- [The very basics](#the-basics)
+- [How to write your own plugin](CreatePlugin.md)
+- [How to write a positional audio plugin](PositionalAudioPlugin.md)
+- [The Mumble-API](MumbleAPI.md)
+- [The plugin-API](PluginAPI.md)
+- [Resource management in plugins](ResourceManagement.md)
+- [Lifecycle of a plugin](PluginLifecycle.md)
+- [Bindings for languages other than C](LanguageBindings.md)
+- [Debugging your plugin](Debugging.md)
+- [Plugin (framework) versioning](Versioning.md)
+- [Bundling your plugin for the end user](Bundling.md)
+
+
+## The basics
+
+At its core a plugin is nothing more than a shared library that implements a set of pre-defined functions. This set of pre-defined functions is the
+so-called [plugin-API](PluginAPI.md). It consists of a few functions that must be implemented by every plugin and even more functions that can be
+implemented as needed.
+
+The plugin-API is written in plain [C](https://en.wikipedia.org/wiki/C_(programming_language)). Thus any plugin must be written in C. There are also
+[bindings for other languages](LanguageBindings.md).
+
+Using C as the base language for the API has several advantages:
+- Binary compatibility: It does not matter which compiler was used to create the plugin. This lifts a restriction that plugins prior to 1.4.0 had: You
+ could only use those plugins that were compiled with the _exact_ same compiler that was used to compile Mumble.
+- C is widely used as the smallest denominator of a variety of languages. This allows for relatively easy inter-operability between other languages
+ which is the pre-condition for being able to create language-bindings for other programming languages
+
+Of course using C also has a few drawbacks (lack of high-level programming features) but these can mostly be overcome by resorting to one of the
+available [language bindings](LanguageBindings.md).
+
+The plugin-API is the part that allows communication from Mumble to the plugin. Every time an event occurs, Mumble will call the corresponding
+function in the plugin which can then act on it. However with only a one-way-communication there wouldn't actually be all that many interesting things
+that could be done.
+
+That's why there is also a second part to the communication scheme that allows the plugin to talk back to Mumble. This is done via the
+[Mumble-API](MumbleAPI.md). The Mumble-API is a collection of functions that Mumble provides to every plugin during the initialization process.
+
+These functions can be called by the plugin to query certain properties (e.g. a list of users on a given server) and to ask Mumble to perform certain
+actions (e.g. move user X to channel Y).
+
+Therefore the typical "working formula" for plugins is this: Mumble lets the plugin know about certain events (e.g. connected to a server) by calling
+the respective function in the plugin-API. The plugin then has the possibility to act on these events by causing something to happen inside Mumble
+(e.g. move to channel X) by using the Mumble-API.
+
diff --git a/docs/dev/plugins/ResourceManagement.md b/docs/dev/plugins/ResourceManagement.md
new file mode 100644
index 000000000..0f616822d
--- /dev/null
+++ b/docs/dev/plugins/ResourceManagement.md
@@ -0,0 +1,43 @@
+# Resource management
+
+During the lifetime of a plugin it usually becomes necessary to allocate some resources and pass them across the boundary between Mumble and the
+plugin. Every time this happens, the ownership of that resource is transferred with it. That means that the side that is passed the resources is then
+also responsible for cleaning them once they are no longer needed.
+
+Note however that memory allocations and deallocations must not happen in different modules. In this case that means that the side that _allocated_
+the resource, must also be the one that _deallocates_ it. The problem is only that this is then not the side that actually _uses_ and _owns_ the
+resource anymore.
+
+Therefore systems exist to pass a resource back to the original creator in order for it to deallocate it again. By this the ownership is then always
+transferred back to that side as well.
+
+
+## Mumble → Plugin
+
+Certain operations require that Mumble allocates some resources that are then passed to the plugin as pointers. Most notably Strings and Arrays. These
+resources must be cleaned up again after the plugin is done using them.
+
+In order for that to happen, the `freeMemory` function in the [Mumble-API](MumbleAPI.md) must be used.
+
+Which resources require freeing is documented in the description of the respective Mumble-API functions. As a rule of thumb though all Strings and all
+Arrays require this procedure.
+
+
+## Plugin → Mumble
+
+There are also cases in which the plugin has to allocate resources to be passed to Mumble. One prominent example of this is the String that is
+returned from `mumble_getName`.
+
+Note that because certain languages (e.g. C++) provide a way to declare static Strings that don't require freeing, the Plugin must tell Mumble what
+kind of resource this is. This is what the `MumbleStringWrapper` struct is for (defined in the `PluginComponents` header). It contains a `data` field
+for holding the `const char *`, a `size` field to hold the String's size and it also holds a `needsReleasing` flag that indicates whether or not the
+given String requires freeing.
+
+If `needsReleasing = false` then nothing else needs to be done on the plugin side.
+
+If `needsReleasing = true` Mumble will call `mumble_releaseResource` with the pointer to the given resource as the argument. It is then the job of the
+plugin to deallocate the resource. When doing this it must be made sure that the way of allocation matches the way of deallocation. That means that
+resources allocated using `new` must be released using `delete`, `new[]` with `delete[]` and resources allocated using `malloc` are to be released
+using `free`. If your plugin uses different ways of allocating resources, it is your responsibility to keep track of which resource was allocated in
+which way so it can be deallocated properly again.
+
diff --git a/docs/dev/plugins/Versioning.md b/docs/dev/plugins/Versioning.md
new file mode 100644
index 000000000..484d862a9
--- /dev/null
+++ b/docs/dev/plugins/Versioning.md
@@ -0,0 +1,16 @@
+# Versioning
+
+Mumble's plugin framework uses _semantic versioning_ also known as [SemVer](https://semver.org/). That means that a version always has the format
+MAJOR.MINOR.PATCH.
+
+Versions that differ in their major version number mean that these versions are _incompatible_ with one another. There was at least one breaking
+change between the two versions.
+
+If however the minor version number changes, the versions are still compatible but the higher minor version number indicates new features that were
+not available in the old version. However the versions are still compatible on the set of old features.
+
+The patch version number is increased if something in the existing feature sets is changed without affecting the API. Therefore plugins usually don't
+have to care about the patch version.
+
+Note that plugins are **expected to follow this scheme as well**.
+