Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Mumble 3d positional audio #30

Draft
wants to merge 4 commits into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Code/client/Games/Skyrim/Camera/PlayerCamera.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ struct PlayerCamera : public TESCamera
void ForceFirstPerson() noexcept;
void ForceThirdPerson() noexcept;

float rotZ;
float rotX;
float yaw;
float pitch;
NiPoint3 pos;
float zoom;
NiNode* cameraNode;
Expand Down
159 changes: 159 additions & 0 deletions Code/client/Services/Generic/MumbleService.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
#include <TiltedOnlinePCH.h>

#include "Services/MumbleService.h"

#include "Events/UpdateEvent.h"

#include "Games/Skyrim/PlayerCharacter.h"
#include "Games/Skyrim/Camera/PlayerCamera.h"

#include "Services/TransportService.h"

struct LinkedMem
{
#ifdef _WIN32
UINT32 uiVersion;
DWORD uiTick;
#else
uint32_t uiVersion;
uint32_t uiTick;
#endif
float fAvatarPosition[3];
float fAvatarFront[3];
float fAvatarTop[3];
wchar_t name[256];
float fCameraPosition[3];
float fCameraFront[3];
float fCameraTop[3];
wchar_t identity[256];
#ifdef _WIN32
UINT32 context_len;
#else
uint32_t context_len;
#endif
unsigned char context[256];
wchar_t description[2048];
};

MumbleService::MumbleService(entt::dispatcher& aDispatcher, TransportService& aTransport)
: m_transport(aTransport)
{
aDispatcher.sink<UpdateEvent>().connect<&MumbleService::Update>(this);

m_uniqueIdentifier = Utils::RandomStringW(32);

#ifdef _WIN32
HANDLE hMapObject = OpenFileMappingW(FILE_MAP_ALL_ACCESS, FALSE, L"MumbleLink");
if (hMapObject == 0)
return;

m_pLinkedMem = static_cast<LinkedMem*>(MapViewOfFile(hMapObject, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(LinkedMem)));
if (m_pLinkedMem == nullptr)
{
CloseHandle(hMapObject);
hMapObject = nullptr;
return;
}
#else
char memname[256];
snprintf(memname, 256, "/MumbleLink.%d", getuid());

int shmfd = shm_open(memname, O_RDWR, S_IRUSR | S_IWUSR);

if (shmfd < 0)
{
return;
}

m_pLinkedMem = static_cast<LinkedMem*>(mmap(NULL, sizeof(struct LinkedMem), PROT_READ | PROT_WRITE, MAP_SHARED, shmfd, 0));

if (m_pLinkedMem == (void*)(-1))
{
m_pLinkedMem = nullptr;
return;
}
#endif
}

MumbleService::~MumbleService() = default;

void MumbleService::Update(const UpdateEvent& acEvent)
{
// TODO: Fallout4 doesn't have PlayerCamera?
#if TP_SKYRIM
if (!m_pLinkedMem || !m_transport.IsOnline())
return;

auto* pPlayer = PlayerCharacter::Get();
auto* pCamera = PlayerCamera::Get();
if (!pPlayer || !pCamera)
return;

if (m_pLinkedMem->uiVersion != 2)
{
#if TP_SKYRIM
wcsncpy(m_pLinkedMem->name, L"SkyrimTogether", 256);
#else
wcsncpy(m_pLinkedMem->name, L"FalloutTogether", 256);
#endif
wcsncpy(m_pLinkedMem->description, L"Together client link.", 2048);
m_pLinkedMem->uiVersion = 2;
}
m_pLinkedMem->uiTick++;

// Left handed coordinate system.
// X positive towards "right".
// Y positive towards "up".
// Z positive towards "front".
//
// 1 unit = 1 meter

auto playerRot = angleAxis(pPlayer->rotation.x, glm::vec3(1, 0, 0));
playerRot *= angleAxis(pPlayer->rotation.y, glm::vec3(0, 1, 0));
playerRot *= angleAxis(pPlayer->rotation.z, glm::vec3(0, 0, 1));

const auto playerUpVector = rotate(playerRot, glm::vec3(0, 1, 0));
const auto playerForwardVector = rotate(playerRot, glm::vec3(0, 0, -1));

// Unit vector pointing out of the avatar's eyes aka "At"-vector.
m_pLinkedMem->fAvatarFront[0] = playerForwardVector.x;
m_pLinkedMem->fAvatarFront[1] = playerForwardVector.z;
m_pLinkedMem->fAvatarFront[2] = playerForwardVector.y;

// Unit vector pointing out of the top of the avatar's head aka "Up"-vector (here Top points straight up).
m_pLinkedMem->fAvatarTop[0] = playerUpVector.x;
m_pLinkedMem->fAvatarTop[1] = playerUpVector.z;
m_pLinkedMem->fAvatarTop[2] = playerUpVector.y;

// Position of the avatar (here standing slightly off the origin)
m_pLinkedMem->fAvatarPosition[0] = pPlayer->position.x;
m_pLinkedMem->fAvatarPosition[1] = pPlayer->position.z;
m_pLinkedMem->fAvatarPosition[2] = pPlayer->position.y;

// Same as avatar but for the camera.
m_pLinkedMem->fCameraPosition[0] = pCamera->pos.x;
m_pLinkedMem->fCameraPosition[1] = pCamera->pos.z;
m_pLinkedMem->fCameraPosition[2] = pCamera->pos.y;

auto rot = angleAxis(pCamera->pitch, glm::vec3(1, 0, 0));
rot *= angleAxis(pCamera->yaw, glm::vec3(0, 1, 0));

const auto upVector = rotate(rot, glm::vec3(0, 1, 0));
const auto forwardVector = rotate(rot, glm::vec3(0, 0, -1));

m_pLinkedMem->fCameraFront[0] = forwardVector.x;
m_pLinkedMem->fCameraFront[1] = forwardVector.z;
m_pLinkedMem->fCameraFront[2] = forwardVector.y;

m_pLinkedMem->fCameraTop[0] = upVector.x;
m_pLinkedMem->fCameraTop[1] = upVector.z;
m_pLinkedMem->fCameraTop[2] = upVector.y;

// Identifier which uniquely identifies a certain player in a context (e.g. the ingame name).
wcsncpy(m_pLinkedMem->identity, m_uniqueIdentifier.c_str(), m_uniqueIdentifier.size());

// TODO: Proper context to make sure two people connected to different servers can't head one another
memcpy(m_pLinkedMem->context, "ContextBlob\x00\x01\x02\x03\x04", 16);
m_pLinkedMem->context_len = 16;
#endif
}
20 changes: 20 additions & 0 deletions Code/client/Services/MumbleService.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#pragma once

struct TransportService;
struct UpdateEvent;
struct LinkedMem;

class MumbleService final : public entt::registry
{
public:
MumbleService(entt::dispatcher&, TransportService& aTransport);
~MumbleService();

void Update(const UpdateEvent& acEvent);

private:

LinkedMem* m_pLinkedMem;
TiltedPhoques::WString m_uniqueIdentifier;
TransportService& m_transport;
};
37 changes: 37 additions & 0 deletions Code/client/Utils.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,47 @@
#include <World.h>
#include <Components.h>
#include <Actor.h>
#include <random>

namespace Utils
{

String RandomString(size_t aLength)
{
constexpr char kAllowedCharacters[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

std::random_device device;
std::mt19937 generator(device());
const std::uniform_int_distribution<> distribution(0, std::size(kAllowedCharacters) - 1);

String result;

for (std::size_t i = 0; i < aLength; ++i)
{
result += kAllowedCharacters[distribution(generator)];
}

return result;
}

TiltedPhoques::WString RandomStringW(size_t aLength)
{
constexpr wchar_t kAllowedCharacters[] = L"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

std::random_device device;
std::mt19937 generator(device());
const std::uniform_int_distribution<> distribution(0, std::size(kAllowedCharacters) - 1);

TiltedPhoques::WString result;

for (std::size_t i = 0; i < aLength; ++i)
{
result += kAllowedCharacters[distribution(generator)];
}

return result;
}

std::optional<uint32_t> GetServerId(entt::entity aEntity) noexcept
{
const auto* pLocalComponent = World::Get().try_get<LocalComponent>(aEntity);
Expand Down
3 changes: 3 additions & 0 deletions Code/client/Utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ static void Assert(const char* apExpression, const char* apMessage)
__debugbreak();
}

TiltedPhoques::String RandomString(size_t aLength);
TiltedPhoques::WString RandomStringW(size_t aLength);

std::optional<uint32_t> GetServerId(entt::entity aEntity) noexcept;

template<class T>
Expand Down
2 changes: 2 additions & 0 deletions Code/client/World.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <Services/InventoryService.h>
#include <Services/MagicService.h>
#include <Services/CommandService.h>
#include <Services/MumbleService.h>
#include <Services/CalendarService.h>
#include <Services/StringCacheService.h>
#include <Services/PlayerService.h>
Expand All @@ -36,6 +37,7 @@ World::World()
ctx().emplace<DebugService>(m_dispatcher, *this, m_transport, ctx().at<ImguiService>());
ctx().emplace<PapyrusService>(m_dispatcher);
ctx().emplace<DiscordService>(m_dispatcher);
ctx().emplace<MumbleService>(m_dispatcher, m_transport);
ctx().emplace<ObjectService>(*this, m_dispatcher, m_transport);
ctx().emplace<CalendarService>(*this, m_dispatcher, m_transport);
ctx().emplace<QuestService>(*this, m_dispatcher);
Expand Down