242 lines
6.0 KiB
C++
242 lines
6.0 KiB
C++
// Copyright 2007-2023 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 <chrono>
|
|
#include <codecvt>
|
|
#include <cstdint>
|
|
#include <cstring>
|
|
#include <iostream>
|
|
#include <locale>
|
|
#include <string>
|
|
|
|
#include "LinkedMem.h"
|
|
#include "MumblePlugin.h"
|
|
#include "SharedMemory.h"
|
|
|
|
#define UNUSED(x) (void) x
|
|
|
|
|
|
constexpr const char *defaultName = "Link";
|
|
constexpr const char *defaultDescription = "Reads positional data from a linked game/software";
|
|
|
|
std::string pluginName(defaultName);
|
|
std::string applicationName;
|
|
std::string pluginDescription(defaultDescription);
|
|
std::string pluginContext;
|
|
std::string pluginIdentity;
|
|
|
|
SharedMemory sharedMem;
|
|
|
|
std::uint32_t last_tick = 0;
|
|
std::int64_t last_tick_time = 0;
|
|
|
|
/**
|
|
* @returns Time in ms since Epoch
|
|
*/
|
|
static std::uint64_t getTimeSinceEpoch() {
|
|
return std::chrono::system_clock::now().time_since_epoch() / std::chrono::milliseconds(1);
|
|
}
|
|
|
|
mumble_error_t mumble_init(mumble_plugin_id_t id) {
|
|
UNUSED(id);
|
|
|
|
if (!sharedMem.mapMemory(getLinkedMemoryName())) {
|
|
std::cerr << "Link plugin: Failed to setup shared memory: " << sharedMem.lastError() << std::endl;
|
|
|
|
return MUMBLE_EC_INTERNAL_ERROR;
|
|
}
|
|
|
|
return MUMBLE_STATUS_OK;
|
|
}
|
|
|
|
void mumble_shutdown() {
|
|
sharedMem.close();
|
|
}
|
|
|
|
MumbleStringWrapper mumble_getName() {
|
|
MumbleStringWrapper wrapper;
|
|
wrapper.data = pluginName.c_str();
|
|
wrapper.size = pluginName.size();
|
|
wrapper.needsReleasing = false;
|
|
|
|
return wrapper;
|
|
}
|
|
|
|
mumble_version_t mumble_getAPIVersion() {
|
|
return MUMBLE_PLUGIN_API_VERSION;
|
|
}
|
|
|
|
void mumble_registerAPIFunctions(void *apiStruct) {
|
|
UNUSED(apiStruct);
|
|
}
|
|
|
|
void mumble_releaseResource(const void *pointer) {
|
|
// This function should never be called
|
|
UNUSED(pointer);
|
|
|
|
std::terminate();
|
|
}
|
|
|
|
mumble_version_t mumble_getVersion() {
|
|
return { 1, 3, 0 };
|
|
}
|
|
|
|
MumbleStringWrapper mumble_getAuthor() {
|
|
static const char *author = "Mumble Developers";
|
|
|
|
MumbleStringWrapper wrapper;
|
|
wrapper.data = author;
|
|
wrapper.size = std::strlen(author);
|
|
wrapper.needsReleasing = false;
|
|
|
|
return wrapper;
|
|
}
|
|
|
|
MumbleStringWrapper mumble_getDescription() {
|
|
MumbleStringWrapper wrapper;
|
|
wrapper.data = pluginDescription.c_str();
|
|
wrapper.size = pluginDescription.size();
|
|
wrapper.needsReleasing = false;
|
|
|
|
return wrapper;
|
|
}
|
|
|
|
uint32_t mumble_getFeatures() {
|
|
return MUMBLE_FEATURE_POSITIONAL;
|
|
}
|
|
|
|
uint8_t mumble_initPositionalData(const char *const *programNames, const uint64_t *programPIDs, size_t programCount) {
|
|
UNUSED(programNames);
|
|
UNUSED(programPIDs);
|
|
UNUSED(programCount);
|
|
|
|
if (!sharedMem.isMemoryMapped()) {
|
|
return MUMBLE_PDEC_ERROR_TEMP;
|
|
}
|
|
|
|
LinkedMem lm = sharedMem.read();
|
|
|
|
if ((lm.uiVersion == 1) || (lm.uiVersion == 2)) {
|
|
if (lm.uiTick != last_tick) {
|
|
last_tick = lm.uiTick;
|
|
last_tick_time = getTimeSinceEpoch();
|
|
|
|
wchar_t buff[2048];
|
|
|
|
if (lm.name[0]) {
|
|
wcsncpy(buff, lm.name, 256);
|
|
buff[255] = 0;
|
|
applicationName = std::wstring_convert< std::codecvt_utf8< wchar_t > >().to_bytes(buff);
|
|
|
|
// Call the plugin itself "Link (<whatever>)"
|
|
pluginName += " (" + applicationName + ")";
|
|
}
|
|
|
|
if (lm.description[0]) {
|
|
wcsncpy(buff, lm.description, 2048);
|
|
buff[2047] = 0;
|
|
pluginDescription = std::wstring_convert< std::codecvt_utf8< wchar_t > >().to_bytes(buff);
|
|
}
|
|
|
|
return MUMBLE_PDEC_OK;
|
|
}
|
|
}
|
|
|
|
return MUMBLE_PDEC_ERROR_TEMP;
|
|
}
|
|
|
|
#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) {
|
|
SET_TO_ZERO(avatarPos);
|
|
SET_TO_ZERO(avatarDir);
|
|
SET_TO_ZERO(avatarAxis);
|
|
SET_TO_ZERO(cameraPos);
|
|
SET_TO_ZERO(cameraDir);
|
|
SET_TO_ZERO(cameraAxis);
|
|
|
|
LinkedMem lm = sharedMem.read();
|
|
|
|
if (lm.uiTick != last_tick) {
|
|
last_tick = lm.uiTick;
|
|
last_tick_time = getTimeSinceEpoch();
|
|
} else if ((getTimeSinceEpoch() - last_tick_time) > 5000) {
|
|
return false;
|
|
}
|
|
|
|
if ((lm.uiVersion != 1) && (lm.uiVersion != 2)) {
|
|
return false;
|
|
}
|
|
|
|
for (int i = 0; i < 3; ++i) {
|
|
avatarPos[i] = lm.fAvatarPosition[i];
|
|
avatarDir[i] = lm.fAvatarFront[i];
|
|
avatarAxis[i] = lm.fAvatarTop[i];
|
|
}
|
|
|
|
if (lm.uiVersion == 2) {
|
|
for (int i = 0; i < 3; ++i) {
|
|
cameraPos[i] = lm.fCameraPosition[i];
|
|
cameraDir[i] = lm.fCameraFront[i];
|
|
cameraAxis[i] = lm.fCameraTop[i];
|
|
}
|
|
|
|
if (lm.context_len > 255) {
|
|
lm.context_len = 255;
|
|
}
|
|
lm.identity[255] = 0;
|
|
|
|
pluginContext.assign(reinterpret_cast< const char * >(lm.context), lm.context_len);
|
|
pluginIdentity = std::wstring_convert< std::codecvt_utf8< wchar_t > >().to_bytes(lm.identity);
|
|
} else {
|
|
for (int i = 0; i < 3; ++i) {
|
|
cameraPos[i] = lm.fAvatarPosition[i];
|
|
cameraDir[i] = lm.fAvatarFront[i];
|
|
cameraAxis[i] = lm.fAvatarTop[i];
|
|
}
|
|
|
|
pluginContext.clear();
|
|
pluginIdentity.clear();
|
|
}
|
|
|
|
*context = pluginContext.c_str();
|
|
*identity = pluginIdentity.c_str();
|
|
|
|
return true;
|
|
}
|
|
|
|
#undef SET_TO_ZERO
|
|
|
|
void mumble_shutdownPositionalData() {
|
|
if (!applicationName.empty()) {
|
|
// We know that pluginName is in the format "Link (<whatever>)" where <whatever> is the applicationName
|
|
pluginName.erase(pluginName.size() - applicationName.size() - 3, std::string::npos);
|
|
} else if (applicationName.size() != std::strlen(defaultName)) {
|
|
// This code part should actually never run, since we expect the pluginName to be modified in the described way
|
|
// as soon as applicationName is defined.
|
|
pluginName.clear();
|
|
pluginName.append(defaultName);
|
|
}
|
|
|
|
applicationName.clear();
|
|
pluginDescription = std::string(defaultDescription);
|
|
pluginContext.clear();
|
|
pluginIdentity.clear();
|
|
|
|
sharedMem.reset();
|
|
}
|
|
|
|
MumbleStringWrapper mumble_getPositionalDataContextPrefix() {
|
|
MumbleStringWrapper wrapper;
|
|
wrapper.data = applicationName.c_str();
|
|
wrapper.size = applicationName.size();
|
|
wrapper.needsReleasing = false;
|
|
|
|
return wrapper;
|
|
}
|