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
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/testPlugin/testPlugin.cpp')
-rw-r--r--plugins/testPlugin/testPlugin.cpp480
1 files changed, 480 insertions, 0 deletions
diff --git a/plugins/testPlugin/testPlugin.cpp b/plugins/testPlugin/testPlugin.cpp
new file mode 100644
index 000000000..e18822fed
--- /dev/null
+++ b/plugins/testPlugin/testPlugin.cpp
@@ -0,0 +1,480 @@
+// Copyright 2021 The Mumble Developers. All rights reserved.
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file at the root of the
+// Mumble source tree or at <https://www.mumble.info/LICENSE>.
+
+// Include the definitions of the plugin functions
+// Not that this will also include ../PluginComponents.h
+#include "../MumblePlugin_v_1_0_x.h"
+#include "../MumbleAPI_v_1_0_x.h"
+
+#include <iostream>
+#include <cstring>
+
+// These are just some utility functions facilitating writing logs and the like
+// The actual implementation of the plugin is further down
+std::ostream& pLog() {
+ std::cout << "TestPlugin: ";
+ return std::cout;
+}
+
+template<typename T>
+void pluginLog(T log) {
+ pLog() << log << std::endl;
+}
+
+std::ostream& operator<<(std::ostream& stream, const mumble_version_t version) {
+ stream << "v" << version.major << "." << version.minor << "." << version.patch;
+ return stream;
+}
+
+
+//////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////
+//////////////////// PLUGIN IMPLEMENTATION ///////////////////
+//////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////
+
+MumbleAPI_v_1_0_x mumAPI;
+mumble_connection_t activeConnection;
+mumble_plugin_id_t ownID;
+
+//////////////////////////////////////////////////////////////
+//////////////////// OBLIGATORY FUNCTIONS ////////////////////
+//////////////////////////////////////////////////////////////
+// All of the following function must be implemented in order for Mumble to load the plugin
+
+mumble_error_t mumble_init(uint32_t id) {
+ pluginLog("Initialized plugin");
+
+ ownID = id;
+
+ // Print the connection ID at initialization. If not connected to a server it should be -1.
+ pLog() << "Plugin ID is " << id << std::endl;
+
+ mumAPI.log(ownID, "Intitialized");
+
+ // Little showcase for how to retrieve a setting from Mumble
+ int64_t voiceHold;
+ mumble_error_t error = mumAPI.getMumbleSetting_int(ownID, MSK_AUDIO_INPUT_VOICE_HOLD, &voiceHold);
+ if (error == STATUS_OK) {
+ pLog() << "The voice hold is set to " << voiceHold << std::endl;
+ } else {
+ pluginLog("Failed to retrieve voice hold");
+ pLog() << errorMessage(error) << std::endl;
+ }
+
+ // STATUS_OK is a macro set to the appropriate status flag (ErrorCode)
+ // If you need to return any other status have a look at the ErrorCode enum
+ // inside PluginComponents.h and use one of its values
+ return STATUS_OK;
+}
+
+void mumble_shutdown() {
+ pluginLog("Shutdown plugin");
+
+ mumAPI.log(ownID, "Shutdown");
+}
+
+MumbleStringWrapper mumble_getName() {
+ static const char *name = "TestPlugin";
+
+ MumbleStringWrapper wrapper;
+ wrapper.data = name;
+ wrapper.size = strlen(name);
+ // It's a static String and therefore doesn't need releasing
+ wrapper.needsReleasing = false;
+
+ return wrapper;
+}
+
+mumble_version_t mumble_getAPIVersion() {
+ // MUMBLE_PLUGIN_API_VERSION will always contain the API version of the used header file (the one used to build
+ // this plugin against). Thus you should always return that here in order to no have to worry about it.
+ return MUMBLE_PLUGIN_API_VERSION;
+}
+
+void mumble_registerAPIFunctions(void *api) {
+ // In this function the plugin is presented with a struct of function pointers that can be used
+ // to interact with Mumble. Thus you should store it somewhere safe for later usage.
+
+ // The pointer has to be cast to the respective API struct. You always have to cast to the same API version
+ // as this plugin itself is using. Thus if this plugin is compiled using the API version 1.0.x (where x is an arbitrary version)
+ // the pointer has to be cast to MumbleAPI_v_1_0_x (where x is a literal "x").
+ // Furthermore the struct HAS TO BE COPIED!!! Storing the pointer is not an option as it will become invalid quickly!
+
+ // **If** you are using the same API version that is specified in the included header file (as you should), you
+ // can simply use the MUMBLE_API_CAST to cast the pointer to the correct type and automatically dereferencing it.
+ mumAPI = MUMBLE_API_CAST(api);
+
+ pluginLog("Registered Mumble's API functions");
+}
+
+void mumble_releaseResource(const void *pointer) {
+ std::cerr << "[ERROR]: Unexpected call to mumble_releaseResources" << std::endl;
+ std::terminate();
+ // This plugin doesn't use resources that are explicitly allocated (only static Strings are used). Therefore
+ // we don't have to implement this function.
+ //
+ // If you allocated resources using malloc(), you're implementation for releasing that would be
+ // free(const_cast<void *>(pointer));
+ //
+ // If however you allocated a resource using the new operator (C++ only), you have figure out the pointer's
+ // original type and then invoke
+ // delete static_cast<ActualType *>(pointer);
+
+ // Mark as unused
+ (void) pointer;
+}
+
+
+//////////////////////////////////////////////////////////////
+///////////////////// OPTIONAL FUNCTIONS /////////////////////
+//////////////////////////////////////////////////////////////
+// The implementation of below functions is optional. If you don't need them, don't include them in your
+// plugin
+
+void mumble_setMumbleInfo(mumble_version_t mumbleVersion, mumble_version_t mumbleAPIVersion, mumble_version_t minimumExpectedAPIVersion) {
+ // this function will always be the first one to be called. Even before init()
+ // In here you can get info about the Mumble version this plugin is about to run in.
+ pLog() << "Mumble version: " << mumbleVersion << "; Mumble API-Version: " << mumbleAPIVersion << "; Minimal expected API-Version: "
+ << minimumExpectedAPIVersion << std::endl;
+}
+
+mumble_version_t mumble_getVersion() {
+ // Mumble uses semantic versioning (see https://semver.org/)
+ // { major, minor, patch }
+ return { 1, 0, 0 };
+}
+
+MumbleStringWrapper mumble_getAuthor() {
+ static const char *author = "MumbleDevelopers";
+
+ MumbleStringWrapper wrapper;
+ wrapper.data = author;
+ wrapper.size = strlen(author);
+ // It's a static String and therefore doesn't need releasing
+ wrapper.needsReleasing = false;
+
+ return wrapper;
+}
+
+MumbleStringWrapper mumble_getDescription() {
+ static const char *description =
+ "This plugin is merely a reference implementation without any real functionality. It shouldn't be included in the release build of Mumble.";
+
+ MumbleStringWrapper wrapper;
+ wrapper.data = description;
+ wrapper.size = strlen(description);
+ // It's a static String and therefore doesn't need releasing
+ wrapper.needsReleasing = false;
+
+ return wrapper;
+}
+
+uint32_t mumble_getFeatures() {
+ // Tells Mumble whether this plugin delivers some known common functionality. See the PluginFeature enum in
+ // PluginComponents.h for what is available.
+ // If you want your plugin to deliver positional data, you'll want to return FEATURE_POSITIONAL
+ return FEATURE_NONE;
+}
+
+uint32_t mumble_deactivateFeatures(uint32_t features) {
+ pLog() << "Asked to deactivate feature set " << features << std::endl;
+
+ // All features that can't be deactivated should be returned
+ return features;
+}
+
+uint8_t mumble_initPositionalData(const char *const*programNames, const uint64_t *programPIDs, size_t programCount) {
+ std::ostream& stream = pLog() << "Got " << programCount << " programs to init positional data.";
+
+ if (programCount > 0) {
+ stream << " The first name is " << programNames[0] << " and has PID " << programPIDs[0];
+ }
+
+ stream << std::endl;
+
+ // As this plugin doesn't provide PD, we return PDEC_ERROR_PERM to indicate that even in the future we won't do so
+ // If your plugin is indeed delivering positional data but is only temporarily unable to do so, return PDEC_ERROR_TEMP.
+ // and if you deliver PD and succeeded initializing return PDEC_OK.
+ return PDEC_ERROR_PERM;
+}
+
+#define SET_TO_ZERO(name) name[0] = 0.0f; name[1] = 0.0f; name[2] = 0.0f
+bool mumble_fetchPositionalData(float *avatarPos, float *avatarDir, float *avatarAxis, float *cameraPos, float *cameraDir,
+ float *cameraAxis, const char **context, const char **identity) {
+ pluginLog("Has been asked to deliver positional data");
+
+ // If unable to provide positional data, this function should return false and reset all given values to 0 / empty Strings
+ SET_TO_ZERO(avatarPos);
+ SET_TO_ZERO(avatarDir);
+ SET_TO_ZERO(avatarAxis);
+ SET_TO_ZERO(cameraPos);
+ SET_TO_ZERO(cameraDir);
+ SET_TO_ZERO(cameraAxis);
+ *context = "";
+ *identity = "";
+
+ // This function returns whether it can continue to deliver positional data
+ return false;
+}
+#undef SET_TO_ZERO
+
+void mumble_shutdownPositionalData() {
+ pluginLog("Shutting down positional data");
+}
+
+void mumble_onServerConnected(mumble_connection_t connection) {
+ activeConnection = connection;
+
+ pLog() << "Established server-connection with ID " << connection << std::endl;
+
+ // Use API function that'll block
+ mumAPI.log(ownID, "Connected to a server");
+}
+
+void mumble_onServerDisconnected(mumble_connection_t connection) {
+ activeConnection = -1;
+
+ const char *serverHash;
+ if (mumAPI.getServerHash(ownID, connection, &serverHash) == STATUS_OK) {
+ pLog() << "Disconnected from server-connection with ID " << connection << "(hash: " << serverHash << ")" << std::endl;
+
+ mumAPI.freeMemory(ownID, serverHash);
+ } else {
+ pluginLog("[ERROR]: mumble_onServerDisconnected - Unable to fetch server-hash");
+ }
+}
+
+void mumble_onServerSynchronized(mumble_connection_t connection) {
+ // The client has finished synchronizing with the server. Thus we can now obtain a list of all users on this server
+ const char *serverHash;
+ if (mumAPI.getServerHash(ownID, connection, &serverHash) == STATUS_OK) {
+ pLog() << "Server has finished synchronizing (ServerConnection: " << connection << "; hash: " << serverHash << ")" << std::endl ;
+
+ mumAPI.freeMemory(ownID, serverHash);
+ } else {
+ pluginLog("[ERROR]: mumble_onServerSynchronized - Unable to fetch server-hash");
+ }
+
+ size_t userCount;
+ mumble_userid_t *userIDs;
+
+ if (mumAPI.getAllUsers(ownID, activeConnection, &userIDs, &userCount) != STATUS_OK) {
+ pluginLog("[ERROR]: Can't obtain user list");
+ return;
+ }
+
+ mumble_userid_t localUserID;
+ if (mumAPI.getLocalUserID(ownID, connection, &localUserID) != STATUS_OK) {
+ pluginLog("[ERROR]: Can't obtain ID of local user");
+ return;
+ }
+
+ pLog() << "There are " << userCount << " users on this server. Their names are:" << std::endl;
+
+ for(size_t i=0; i<userCount; i++) {
+ const char *userName;
+ if (mumAPI.getUserName(ownID, connection, userIDs[i], &userName) != STATUS_OK) {
+ pLog() << "<Unable to fetch user name>" << std::endl;
+ continue;
+ }
+
+ const char *userHash;
+ if (mumAPI.getUserHash(ownID, connection, userIDs[i], &userHash) != STATUS_OK) {
+ pluginLog("<Unable to get user-hash>");
+ }
+
+ pLog() << "\t" << userName << " (" << userHash << ")" << std::endl;
+
+ // Mute the user "MuteMe" if this is not the name of the local user (in which case it'd fail)
+ if (userIDs[i] != localUserID && std::strcmp(userName, "MuteMe") == 0) {
+ if (mumAPI.requestLocalMute(ownID, connection, userIDs[i], true) != STATUS_OK) {
+ pluginLog("[ERROR]: Failed at muting user \"MuteMe\"!");
+ }
+ }
+
+ mumAPI.freeMemory(ownID, userName);
+ mumAPI.freeMemory(ownID, userHash);
+ }
+
+ mumAPI.freeMemory(ownID, userIDs);
+
+ size_t channelCount;
+ mumble_channelid_t *channelIDs;
+
+ if (mumAPI.getAllChannels(ownID, activeConnection, &channelIDs, &channelCount) != STATUS_OK) {
+ pluginLog("[ERROR]: Failed to fetch channel list!");
+ return;
+ }
+
+ pLog() << "There are " << channelCount << " channels on this server" << std::endl;
+
+ mumAPI.freeMemory(ownID, channelIDs);
+
+ mumble_userid_t localUser;
+ if (mumAPI.getLocalUserID(ownID, activeConnection, &localUser) != STATUS_OK) {
+ pluginLog("Failed to retrieve local user ID");
+ return;
+ }
+
+ if (mumAPI.sendData(ownID, activeConnection, &localUser, 1, reinterpret_cast<const uint8_t *>("Just a test"), 12, "testMsg") == STATUS_OK) {
+ pluginLog("Successfully sent plugin message");
+
+ // Try break the rate-limiter for plugin messages
+ for (int i = 0; i < 40; i++) {
+ std::string data = "Rate-limit message #" + std::to_string(i);
+
+ mumAPI.sendData(ownID, activeConnection, &localUser, 1, reinterpret_cast<const uint8_t * >(data.c_str()), data.size(), "testMsg");
+ }
+ } else {
+ pluginLog("Failed at sending message");
+ }
+
+ if (mumAPI.requestSetLocalUserComment(ownID, connection, "This user has the TestPlugin enabled - <b>hand over a cookie!</b>") != STATUS_OK) {
+ pluginLog("Failed at setting the local user's comment");
+ }
+}
+
+void mumble_onChannelEntered(mumble_connection_t connection, mumble_userid_t userID, mumble_channelid_t previousChannelID, mumble_channelid_t newChannelID) {
+ std::ostream& stream = pLog() << "User with ID " << userID << " entered channel with ID " << newChannelID << ".";
+
+ // negative ID means that there was no previous channel (e.g. because the user just connected)
+ if (previousChannelID >= 0) {
+ stream << " Came from channel with ID " << previousChannelID << ".";
+ }
+
+ stream << " (ServerConnection: " << connection << ")" << std::endl;
+}
+
+void mumble_onChannelExited(mumble_connection_t connection, mumble_userid_t userID, mumble_channelid_t channelID) {
+ pLog() << "User with ID " << userID << " has left channel with ID " << channelID << ". (ServerConnection: " << connection << ")" << std::endl;
+}
+
+void mumble_onUserTalkingStateChanged(mumble_connection_t connection, mumble_userid_t userID, mumble_talking_state_t talkingState) {
+ std::ostream& stream = pLog() << "User with ID " << userID << " changed his talking state to ";
+
+ // The possible values are contained in the TalkingState enum inside PluginComponent.h
+ switch(talkingState) {
+ case INVALID:
+ stream << "Invalid";
+ break;
+ case PASSIVE:
+ stream << "Passive";
+ break;
+ case TALKING:
+ stream << "Talking";
+ break;
+ case WHISPERING:
+ stream << "Whispering";
+ break;
+ case SHOUTING:
+ stream << "Shouting";
+ break;
+ default:
+ stream << "Unknown (" << talkingState << ")";
+ }
+
+ stream << ". (ServerConnection: " << connection << ")" << std::endl;
+}
+
+bool mumble_onAudioInput(short *inputPCM, uint32_t sampleCount, uint16_t channelCount, uint32_t sampleRate, bool isSpeech) {
+ // pLog() << "Audio input with " << channelCount << " channels and " << sampleCount << " samples per channel encountered. IsSpeech: "
+ // << isSpeech << " Sample rate is " << sampleRate << "Hz" << std::endl;
+
+ // mark variables as unused
+ (void) inputPCM;
+ (void) sampleCount;
+ (void) channelCount;
+ (void) sampleRate;
+ (void) isSpeech;
+
+ // This function returns whether it has modified the audio stream
+ return false;
+}
+
+bool mumble_onAudioSourceFetched(float *outputPCM, uint32_t sampleCount, uint16_t channelCount, uint32_t sampleRate, bool isSpeech, mumble_userid_t userID) {
+ std::ostream& stream = pLog() << "Audio output source with " << channelCount << " channels and " << sampleCount << " samples per channel "
+ << "(" << sampleRate << " Hz) fetched.";
+
+ if (isSpeech) {
+ stream << " The output is speech from user with ID " << userID << ".";
+ }
+
+ stream << std::endl;
+
+ // Mark ouputPCM as unused
+ (void) outputPCM;
+
+ // This function returns whether it has modified the audio stream
+ return false;
+}
+
+bool mumble_onAudioOutputAboutToPlay(float *outputPCM, uint32_t sampleCount, uint16_t channelCount, uint32_t sampleRate) {
+ // pLog() << "The resulting audio output has " << channelCount << " channels with " << sampleCount << " samples per channel ("
+ // sampleRate << " Hz)" << std::endl;
+
+ // mark variables as unused
+ (void) outputPCM;
+ (void) sampleCount;
+ (void) channelCount;
+ (void) sampleRate;
+
+ // This function returns whether it has modified the audio stream
+ return false;
+}
+
+bool mumble_onReceiveData(mumble_connection_t connection, mumble_userid_t sender, const uint8_t *data, size_t dataLength, const char *dataID) {
+ pLog() << "Received data with ID \"" << dataID << "\" from user with ID " << sender << ". Its length is " << dataLength
+ << ". (ServerConnection:" << connection << ")" << std::endl;
+
+ if (std::strcmp(dataID, "testMsg") == 0) {
+ // We know that data is only a normal C-encoded String, so the reinterpret_cast is safe
+ pLog() << "The received data: " << reinterpret_cast<const char *>(data) << std::endl;
+ }
+
+ // This function returns whether it has processed the data (preventing further plugins from seeing it)
+ return false;
+}
+
+void mumble_onUserAdded(mumble_connection_t connection, mumble_userid_t userID) {
+ pLog() << "Added user with ID " << userID << " (ServerConnection: " << connection << ")" << std::endl;
+}
+
+void mumble_onUserRemoved(mumble_connection_t connection, mumble_userid_t userID) {
+ pLog() << "Removed user with ID " << userID << " (ServerConnection: " << connection << ")" << std::endl;
+}
+
+void mumble_onChannelAdded(mumble_connection_t connection, mumble_channelid_t channelID) {
+ pLog() << "Added channel with ID " << channelID << " (ServerConnection: " << connection << ")" << std::endl;
+}
+
+void mumble_onChannelRemoved(mumble_connection_t connection, mumble_channelid_t channelID) {
+ pLog() << "Removed channel with ID " << channelID << " (ServerConnection: " << connection << ")" << std::endl;
+}
+
+void mumble_onChannelRenamed(mumble_connection_t connection, mumble_channelid_t channelID) {
+ pLog() << "Renamed channel with ID " << channelID << " (ServerConnection: " << connection << ")" << std::endl;
+}
+
+void mumble_onKeyEvent(uint32_t keyCode, bool wasPress) {
+ pLog() << "Encountered key " << (wasPress ? "press" : "release") << " of key with code " << keyCode << std::endl;
+}
+
+bool mumble_hasUpdate() {
+ // This plugin never has an update
+ return false;
+}
+
+MumbleStringWrapper mumble_getUpdateDownloadURL() {
+ static const char *url = "https://i.dont.exist/testplugin.zip";
+
+ MumbleStringWrapper wrapper;
+ wrapper.data = url;
+ wrapper.size = strlen(url);
+ // It's a static String and therefore doesn't need releasing
+ wrapper.needsReleasing = false;
+
+ return wrapper;
+}