From 87277ffcb17d0e2c3c4477f5b94cde8e0e1522af Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Sat, 21 Sep 2019 00:06:26 +0200 Subject: [PATCH] Plugin API --- .gitignore | 1 + src/mumble/pluginAPI/MumblePlugin.h | 262 ++++++++++++++++++++++++ src/mumble/pluginAPI/PluginComponents.h | 234 +++++++++++++++++++++ 3 files changed, 497 insertions(+) create mode 100644 src/mumble/pluginAPI/MumblePlugin.h create mode 100644 src/mumble/pluginAPI/PluginComponents.h diff --git a/.gitignore b/.gitignore index d010e351842..6144c332145 100644 --- a/.gitignore +++ b/.gitignore @@ -44,3 +44,4 @@ doxygen/ .DS_Store .directory tags +compile_commands.json diff --git a/src/mumble/pluginAPI/MumblePlugin.h b/src/mumble/pluginAPI/MumblePlugin.h new file mode 100644 index 00000000000..14093988238 --- /dev/null +++ b/src/mumble/pluginAPI/MumblePlugin.h @@ -0,0 +1,262 @@ +/// @file pluginAPI/Plugin.h + + +#ifndef EXTERNAL_MUMBLE_PLUGIN_H_ +#define EXTERNAL_MUMBLE_PLUGIN_H_ + +#include "PluginComponents.h" +#include +#include +#include + +#ifdef WIN32 +#define PLUGIN_EXPORT __declspec(dllexport) +#else +#define PLUGIN_EXPORT __attribute__ ((visibility("default"))) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + // functions for init and de-init + + /// Gets called right after loading the plugin in order to let the plugin initialize. + /// + /// @returns The status of the initialization. If everything went fine, return STATUS_OK + PLUGIN_EXPORT MumbleError_t init(); + + /// Gets called when unloading the plugin in order to allow it to clean up after itself. + PLUGIN_EXPORT void shutdown(); + + /// Tells the plugin some basic information about the Mumble client loading it. + /// This function will be the first one that is being called on this plugin - even before it is decided whether to load + /// the plugin at all. + /// + /// @param mumbleVersion The Version of the Mumble client + /// @param mumbleAPIVersion The Version of the plugin-API the Mumble client runs with + /// @param minimalExpectedAPIVersion The minimal Version the Mumble clients expects this plugin to meet in order to load it + PLUGIN_EXPORT void setMumbleInfo(Version_t mumbleVersion, Version_t mumbleAPIVersion, Version_t minimalExpectedAPIVersion); + + // functions for general plugin info + + /// Gets the name of the plugin. The plugin has to guarantee that the returned pointer will still be valid. The string will be copied + /// for further usage though. + /// + /// @returns A pointer to the plugin name (encoded as a C-String) + PLUGIN_EXPORT const char* getName(); + + /// Gets the Version of this plugin + /// + /// @returns The plugin's version + PLUGIN_EXPORT Version_t getVersion(); + + /// Gets the Version of the plugin-API this plugin intends to use. + /// Mumble will decide whether this plugin is loadable or not based on the return value of this function. + /// + /// @return The respective API Version + PLUGIN_EXPORT Version_t getAPIVersion(); + + /// Gets the name of the plugin author(s). The plugin has to guarantee that the returned pointer will still be valid. The string will + /// be copied for further usage though. + /// + /// @returns A pointer to the author(s) name(s) (encoded as a C-String) + PLUGIN_EXPORT const char* getAuthor(); + + /// Gets the description of the plugin. The plugin has to guarantee that the returned pointer will still be valid. The string will + /// be copied for further usage though. + /// + /// @returns A pointer to the description (encoded as a C-String) + PLUGIN_EXPORT const char* getDescription(); + + /// Provides the MumbleAPI struct to the plugin. This struct contains function pointers that can be used + /// to interact with the Mumble client. It is up to the plugin to store this struct somewhere if it wants to make use + /// of it at some point. + /// + /// @param api The MumbleAPI struct + PLUGIN_EXPORT void registerAPIFunctions(const struct MumbleAPI *api); + + /// Registers the ID of this plugin. This is the ID Mumble will reference this plugin with and by which this plugin + /// can identify itself when communicating with Mumble. + /// + /// @param id The ID for this plugin + PLUGIN_EXPORT void registerPluginID(uint32_t id); + + /// Gets the feature set of this plugin. The feature set is described by bitwise or'ing the elements of the PluginFeature enum + /// together. + /// + /// @returns The feature set of this plugin + PLUGIN_EXPORT uint32_t getPluginFeatures(); + + /// Requests this plugin to deactivate the given (sub)set of provided features. + /// If this is not possible, the features that can't be deactivated shall be returned by this function. + /// + /// Example (check if FEATURE_POSITIONAL shall be deactivated): + /// @code + /// if (features & FEATURE_POSITIONAL) { + /// // positional shall be deactivated + /// }; + /// @endcode + /// + /// @param features The feature set that shall be deactivated + /// @returns The feature set that can't be disabled (bitwise or'ed). If all requested features can be disabled, return + /// FEATURE_NONE. If none of the requested features can be disabled return the unmodified features parameter. + PLUGIN_EXPORT uint32_t deactivateFeatures(uint32_t features); + + + // -------- Positional Audio -------- + // If this plugin wants to provide positional audio, all functiosn of this category have to be implemented + + /// Indicates that Mumble wants to use this plugin to request positional data. Therefore it should check whether it is currently + /// able to do so and allocate memory that is needed for that process. + /// As a parameter this function gets an array of names and an array of PIDs. They are of same length and the PID at index i + /// belongs to a program whose name is listed at index i in the "name-array". + /// + /// @param programNames An array of pointers to the program names + /// @param programPIDs An array of pointers to the corresponding program PIDs + /// @param programCount The length of programNames and programPIDs + /// @returns The error code. If everything went fine PDEC_OK shall be returned. In that case Mumble will start frequently + /// calling fetchPositionalData. If this returns anything but PDEC_OK, Mumble will assume that the plugin is (currently) + /// uncapable of providing positional data. In this case this function must not have allocated any memory that needs to be + /// cleaned up later on. Depending on the returned error code, Mumble might try to call this function again later on. + PLUGIN_EXPORT uint8_t initPositionalData(const char **programNames, const uint64_t **programPIDs, size_t programCount); + + /// Retrieves the positional audio data. If no data can be fetched, set all float-vectors to 0 and return false. + /// + /// @param[out] avatar_pos A float-array of size 3 representing the cartesian position of the player/avatar in the ingame world. + /// One unit represents one meter of distance. + /// @param[out] avatar_front A float-array of size 3 representing the cartesian direction-vector of the player/avatar ingame (where it + /// is facing). + /// @param[out] avatar_axis A float-array of size 3 representing the vector pointing from the toes of the character to its head. One + /// unit represents one meter of distance. + /// @param[out] camera_pos A float-array of size 3 representing the cartesian position of the camera in the ingame world. + /// One unit represents one meter of distance. + /// @param[out] camera_front A float-array of size 3 representing the cartesian direction-vector of the camera ingame (where it + /// is facing). + /// @param[out] camera_axis A float-array of size 3 representing a vector from the bottom of the camera to its top. One unit + /// represents one meter of distance. + /// @param[out] context A pointer to where the pointer to a C-encoded string storing the context of the provided positional data + /// shall be written. This context should include information about the server (and team) the player is on. Only players with identical + /// context will be able to hear each other's audio. The returned pointer has to remain valid until the next invokation of this function + /// or until shutdownPositionalData is called. + /// @param[out] identity A pointer to where the pointer to a C-encoded string storing the identity of the player shall be written. It can + /// be polled by external scripts from the server and should uniquely identify the player in the game. The pointer has to remain valid + /// until the next invokation of this function or until shutdownPositionalData is called. + /// @returns Whether this plugin can continue delivering positional data. If this function returns false, shutdownPositionalData will + /// be called. + PLUGIN_EXPORT bool fetchPositionalData(float *avatar_pos, float *avatar_front, float *avatar_axis, float *camera_pos, float *camera_front, + float *camera_axis, const char **context, const char **identity); + + /// Indicates that this plugin will not be asked for positional data any longer. Thus any memory allocated for this purpose should + /// be freed at this point. + PLUGIN_EXPORT void shutdownPositionalData(); + + + + // -------- EventHandlers / Callback functions ----------- + + /// Called when connecting to a server. + /// + /// @param connection The ID of the newly established server-connection + PLUGIN_EXPORT void onServerConnected(MumbleConnection_t connection); + + /// Called when disconnecting from a server. + /// + /// @param connection The ID of the server-connection that has been terminated + PLUGIN_EXPORT void onServerDisconnected(MumbleConnection_t connection); + + /// Called whenever any user on the server enters a channel + /// This function will also be called when freshly connecting to a server as each user on that + /// server needs to be "added" to the respective channel as far as the local client is concerned. + /// + /// @param connection The ID of the server-connection this event is connected to + /// @param userID The ID of the user this event has been triggered for + /// @param previousChannelID The ID of the chanel the user is coming from. Negative IDs indicate that there is no previous channel (e.g. the user + /// freshly connected to the server) or the channel isn't available because of any other reason. + /// @param newChannelID The ID of the channel the user has entered. If the ID is negative, the new channel could not be retrieved. This means + /// that the ID is invalid. + PLUGIN_EXPORT void onChannelEntered(MumbleConnection_t connection, MumbleUserID_t userID, MumbleChannelID_t previousChannelID, + MumbleChannelID_t newChannelID); + + /// Called whenever a user leaves a channel. + /// This includes a client disconnecting from the server as this will also lead to the user not being in that channel anymore. + /// + /// @param connection The ID of the server-connection this event is connected to + /// @param userID The ID of the user that left the channel + /// @param channelID The ID of the channel the user left. If the ID is negative, the channel could not be retrieved. This means that the ID is + /// invalid. + PLUGIN_EXPORT void onChannelExited(MumbleConnection_t connection, MumbleUserID_t userID, MumbleChannelID_t channelID); + + /// Called when any user changes his/her talking state. + /// + /// @param connection The ID of the server-connection this event is connected to + /// @param userID The ID of the user whose talking state has been changed + /// @param talkingState The new TalkingState the user has switched to. + PLUGIN_EXPORT void onUserTalkingStateChanged(MumbleConnection_t connection, MumbleUserID_t userID, TalkingState_t talkingState); + + /// Called whenever there is audio input. + /// + /// @param inputPCM A pointer to a short-array holding the pulse-code-modulation (PCM) representing the audio input. Its length + /// is sampleCount * channelCount. + /// @param sampleCount The amount of sample points per channel + /// @param channelCount The amount of channels in the audio + /// @param isSpeech A boolean flag indicating whether Mumble considers the input as part of speech (instead of background noise) + /// @returns Whether this callback has modified the audio input-array + PLUGIN_EXPORT bool onAudioInput(short *inputPCM, uint32_t sampleCount, uint16_t channelCount, bool isSpeech); + + /// Called whenever Mumble fetches data from an active audio source (could be a voice packet or a playing sample). + /// The provided audio buffer is the raw buffer without any processing applied to it yet. + /// + /// @param outputPCM A pointer to a float-array holding the pulse-code-modulation (PCM) representing the audio output. Its length + /// is sampleCount * channelCount. + /// @param sampleCount The amount of sample points per channel + /// @param channelCount The amount of channels in the audio + /// @param isSpeech Whether this audio belongs to a received voice packet (and will thus (most likely) contain speech) + /// @param userID If isSpeech is true, this contains the ID of the user this voice packet belongs to. If isSpeech is false, + /// the content of this parameter is unspecified and should not be accessed + /// @returns Whether this callback has modified the audio output-array + PLUGIN_EXPORT bool onAudioSourceFetched(float *outputPCM, uint32_t sampleCount, uint16_t channelCount, bool isSpeech, MumbleUserID_t userID); + + /// Called whenever Mumble has processed the data from an active audio source (could be a voice packet or a playing sample). + /// Processing contains things like applying positional audio effects (if enabled) and/or volume adjustments. + /// + /// @param outputPCM A pointer to a float-array holding the pulse-code-modulation (PCM) representing the audio output. Its length + /// is sampleCount * channelCount. + /// @param sampleCount The amount of sample points per channel + /// @param channelCount The amount of channels in the audio + /// @param isSpeech Whether this audio belongs to a received voice packet (and will thus (most likely) contain speech) + /// @param userID If isSpeech is true, this contains the ID of the user this voice packet belongs to. If isSpeech is false, + /// the content of this parameter is unspecified and should not be accessed + /// @returns Whether this callback has modified the audio output-array + PLUGIN_EXPORT bool onAudioSourceProcessed(float *outputPCM, uint32_t sampleCount, uint16_t channelCount, bool isSpeech, MumbleUserID_t userID); + + /// Called whenever the fully mixed and processed audio is about to be handed to the audio backend (about to be played). + /// Note that this happens immediately before Mumble clips the audio buffer. + /// + /// @param outputPCM A pointer to a float-array holding the pulse-code-modulation (PCM) representing the audio output. Its length + /// is sampleCount * channelCount. + /// @param sampleCount The amount of sample points per channel + /// @param channelCount The amount of channels in the audio + /// @returns Whether this callback has modified the audio output-array + PLUGIN_EXPORT bool onAudioOutputAboutToPlay(float *outputPCM, uint32_t sampleCount, uint16_t channelCount); + + /// Called whenever data has been received that has been sent by a plugin. This data should only be processed by the + /// intended plugin. For this reason a dataID is provided that should be used to determine whether the data is intended + /// for this plugin or not. As soon as the data has been processed, no further plugins will be notified about it. + /// + /// @param connection The ID of the server-connection the data is coming from + /// @param sender The ID of the user whose client's plugin has sent the data + /// @param data The sent data represented as a string + /// @param dataLength The length of data + /// @param dataID The ID of this data + /// @return Whether the given data has been processed by this plugin + PLUGIN_EXPORT bool onReceiveData(MumbleConnection_t connection, MumbleUserID_t sender, const char *data, size_t dataLenght, + const char *dataID); + + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/src/mumble/pluginAPI/PluginComponents.h b/src/mumble/pluginAPI/PluginComponents.h new file mode 100644 index 00000000000..69c77216c34 --- /dev/null +++ b/src/mumble/pluginAPI/PluginComponents.h @@ -0,0 +1,234 @@ +/** + * This header file contains definitions of types and other components used in Mumble's plugin system + */ + +#ifndef MUMBLE_PLUGINCOMPONENT_H_ +#define MUMBLE_PLUGINCOMPONENT_H_ + +#include +#include + +#define STATUS_OK EC_OK + +enum PluginFeature { + /// None of the below + FEATURE_NONE = 0, + /// The plugin provides positional data from a game + FEATURE_POSITIONAL = 1 << 0, + /// The plugin modifies the input/output audio itself + FEATURE_AUDIO = 1 << 1 +}; + +enum TalkingState { + INVALID=-1, + PASSIVE=0, + TALKING, + WHISPERING, + SHOUTING +}; + +enum TransmissionMode { + CONTINOUS, + VOICE_ACTIVATION, + PUSH_TO_TALK +}; + +enum ErrorCode { + EC_OK = 0, + EC_GENERIC_ERROR +}; + +enum PositionalDataErrorCode { + /// Positional data has been initialized properly + PDEC_OK = 0, + /// Positional data is temporarily unavailable (e.g. because the corresponding process isn't running) but might be + /// at another point in time. + PDEC_ERROR_TEMP, + /// Positional data is permanently unavailable (e.g. because the respective memory offsets are outdated). + PDEC_ERROR_PERM +}; + +struct Version { + int32_t major; + int32_t minor; + int32_t patch; +}; + + +typedef enum TalkingState TalkingState_t; +typedef enum TransmissionMode TransmissionMode_t; +typedef struct Version Version_t; +typedef int32_t MumbleConnection_t; +typedef uint32_t MumbleUserID_t; +typedef int32_t MumbleChannelID_t; +typedef enum ErrorCode MumbleError_t; + + +struct MumbleAPI { + // -------- Memory management -------- + + /// Frees the given pointer. + /// + /// @param pointer The pointer to free + /// @returns The error code. If everything went well, STATUS_OK will be returned. + MumbleError_t (*freeMemory)(void *pointer); + + + + // -------- Getter functions -------- + + /// Gets the connection ID of the server the user is currently active on (the user's audio output is directed at). + /// + /// @param[out] connection A pointer to the memory location the ID should be written to + /// @returns The error code. If everything went well, STATUS_OK will be returned. Only then it is valid to access the + /// value of the provided pointer + MumbleError_t (*getActiveServerConnection)(MumbleConnection_t *connection); + + /// Fills in the information about the local user. + /// + /// @param connection The ID of the server-connection to use as a context + /// @param[out] userID A pointer to the memory the user's ID shall be written to + /// @returns The error code. If everything went well, STATUS_OK will be returned. Only then the passed pointer + /// may be accessed + MumbleError_t (*getLocalUserID)(MumbleConnection_t connection, MumbleUserID_t *userID); + + /// Fills in the information about the given user's name. + /// + /// @param connection The ID of the server-connection to use as a context + /// @param userID The user's ID whose name should be obtained + /// @param[out] userName A pointer to where the pointer to the allocated string (C-encoded) should be written to. The + /// allocated memory has to be freed by a call to freeMemory by the plugin eventually. The memory will only be + /// allocated if this function returns STATUS_OK. + /// @returns The error code. If everything went well, STATUS_OK will be returned. Only then the passed pointer + /// may be accessed + MumbleError_t (*getUserName)(MumbleConnection_t connection, MumbleUserID_t userID, const char **userName); + + /// Fills in the information about the given channel's name. + /// + /// @param connection The ID of the server-connection to use as a context + /// @param channelID The channel's ID whose name should be obtained + /// @param[out] channelName A pointer to where the pointer to the allocated string (C-ecoded) should be written to. The + /// allocated memory has to be freed by a call to freeMemory by the plugin eventually. The memory will only be + /// allocated if this function returns STATUS_OK. + /// @returns The error code. If everything went well, STATUS_OK will be returned. Only then the passed pointer + /// may be accessed + MumbleError_t (*getChannelName)(MumbleConnection_t connection, MumbleChannelID_t channelID, const char **channelName); + + /// Gets an array of all users that are currently connected to the provided server. Passing a nullptr as any of the out-parameter + /// will prevent that property to be set/allocated. If you are only interested in the user count you can thus pass nullptr as the + /// users parameter and save time on allocating + freeing the channels-array while still getting the size out. + /// + /// @param connection The ID of the server-connection to use as a context + /// @param[out] users A pointer to where the pointer of the allocated array shall be written. The + /// allocated memory has to be freed by a call to freeMemory by the plugin eventually. The memory will only be + /// allocated if this function returns STATUS_OK. + /// @param[out] userCount A pointer to where the size of the allocated user-array shall be written to + /// @returns The error code. If everything went well, STATUS_OK will be returned. Only then the passed pointer + /// may be accessed + MumbleError_t (*getAllUsers)(MumbleConnection_t connection, MumbleUserID_t **users, size_t *userCount); + + /// Gets an array of all channels on the provided server. Passing a nullptr as any of the out-parameter will prevent + /// that property to be set/allocated. If you are only interested in the channel count you can thus pass nullptr as the + /// channels parameter and save time on allocating + freeing the channels-array while still getting the size out. + /// + /// @param connection The ID of the server-connection to use as a context + /// @param[out] channels A pointer to where the pointer of the allocated array shall be written. The + /// allocated memory has to be freed by a call to freeMemory by the plugin eventually. The memory will only be + /// allocated if this function returns STATUS_OK. + /// @param[out] channelCount A pointer to where the size of the allocated channel-array shall be written to + /// @returns The error code. If everything went well, STATUS_OK will be returned. Only then the passed pointer + /// may be accessed + MumbleError_t (*getAllChannels)(MumbleConnection_t connection, MumbleChannelID_t **channels, size_t *channelCount); + + /// Gets the ID of the channel the given user is currently connected to. + /// + /// @param connection The ID of the server-connection to use as a context + /// @param userID The ID of the user to search for + /// @param[out] A pointer to where the ID of the channel shall be written + /// @returns The error code. If everything went well, STATUS_OK will be returned. Only then the passed pointer + /// may be accessed + MumbleError_t (*getChannelOfUser)(MumbleConnection_t connection, MumbleUserID_t userID, MumbleChannelID_t *channel); + + /// Gets an array of all users in the specified channel. + /// + /// @param connection The ID of the server-connection to use as a context + /// @param channelID The ID of the channel whose users shall be retrieved + /// @param[out] userList A pointer to where the pointer of the allocated array shall be written. The allocated memory has + /// to be freed by a call to freeMemory by the plugin eventually. The memory will only be allocated if this function + /// returns STATUS_OK. + /// @param[out] userCount A pointer to where the size of the allocated user-array shall be written to + /// @returns The error code. If everything went well, STATUS_OK will be returned. Only then the passed pointer + /// may be accessed + MumbleError_t (*getUsersInChannel)(MumbleConnection_t connection, MumbleChannelID_t channelID, MumbleUserID_t **userList, size_t *userCount); + + /// Gets the current transmission mode of the local user. + /// + /// @param[out] transmissionMode A pointer to where the transmission mode shall be written. + /// @returns The error code. If everything went well, STATUS_OK will be returned. Only then the passed pointer + /// may be accessed + MumbleError_t (*getLocalUserTransmissionMode)(TransmissionMode_t *transmissionMode); + + + + // -------- Request functions -------- + + /// Requests Mumble to set the local user's transmission mode to the specified one. + /// + /// @param transmissionMode The requested transmission mode + /// @returns The error code. If everything went well, STATUS_OK will be returned. + MumbleError_t (*requestLocalUserTransmissionMode)(TransmissionMode_t transmissionMode); + + /// Requests Mumble to move the given user into the given channel + /// + /// @param connection The ID of the server-connection to use as a context + /// @param userID The ID of the user that shall be moved + /// @param channelID The ID of the channel to move the user to + /// @param password The password of the target channel (encoded as a C-string). Pass NULL if the target channel does not require a + /// password for entering + /// @returns The error code. If everything went well, STATUS_OK will be returned. + MumbleError_t (*requestUserMove)(MumbleConnection_t connection, MumbleUserID_t userID, MumbleChannelID_t channelID, const char *password); + + + + // -------- Find functions -------- + + /// Fills in the information about a user with the specified name, if such a user exists. The search is case-sensitive. + /// + /// @param connection The ID of the server-connection to use as a context + /// @param userName The respective user's name + /// @param[out] userID A pointer to the memory the user's ID shall be written to + /// @returns The error code. If everything went well, STATUS_OK will be returned. Only then the passed pointer may + /// be accessed. + MumbleError_t (*findUserByName)(MumbleConnection_t connection, const char *userName, MumbleUserID_t *userID); + + /// Fills in the information about a channel with the specified name, if such a channel exists. The search is case-sensitive. + /// + /// @param connection The ID of the server-connection to use as a context + /// @param channelName The respective channel's name + /// @param[out] channelID A pointer to the memory the channel's ID shall be written to + /// @returns The error code. If everything went well, STATUS_OK will be returned. Only then the passed pointer may + /// be accessed. + MumbleError_t (*findChannelByName)(MumbleConnection_t connection, const char *channelName, MumbleChannelID_t *channelID); + + + + // -------- Miscellaneous -------- + + /// Sends the provided data to the provided client(s). This kind of data can only be received by another plugin active + /// on that client. The sent data can be seen by any active plugin on the receiving client. Therefore the sent data + /// must not contain sensitive information or anything else that shouldn't be known by others. + /// + /// @param connection The ID of the server-connection to send the data through (the server the given users are on) + /// @param users An array of user IDs to send the data to + /// @param userCount The size of the provided user-array + /// @param data The data that shall be sent as a String + /// @param dataLength The length of the data-string + /// @param dataID The ID of the sent data. This has to be used by the receiving plugin(s) to figure out what to do with + /// the data + /// @returns The error code. If everything went well, STATUS_OK will be returned. + MumbleError_t sendData(MumbleConnection_t connection, MumbleUserID_t *users, size_t userCount, const char *data, size_t dataLength, + const char *dataID); +}; + + +#endif