Skip to content

Commit

Permalink
FEAT(positional-audio): Add plugin for Among Us
Browse files Browse the repository at this point in the history
Tested with v2020.10.22s and v2020.09.22s.

Unless the pattern we're searching for becomes invalid or the structures we're using change, the plugin should keep working.
  • Loading branch information
davidebeatrici committed Nov 11, 2020
1 parent 4f8540e commit 0155a24
Show file tree
Hide file tree
Showing 5 changed files with 520 additions and 0 deletions.
19 changes: 19 additions & 0 deletions plugins/amongus/CMakeLists.txt
@@ -0,0 +1,19 @@
# Copyright 2020 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>.

add_library(amongus SHARED
"amongus.cpp"
"Game.cpp"

"../Module.cpp"
"../Process.cpp"
"../ProcessWindows.cpp"
)

if(WIN32)
target_sources(amongus PRIVATE "../HostWindows.cpp")
else()
target_sources(amongus PRIVATE "../HostLinux.cpp")
endif()
84 changes: 84 additions & 0 deletions plugins/amongus/Game.cpp
@@ -0,0 +1,84 @@
// Copyright 2020 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 "Game.h"

#include "mumble_plugin_utils.h"

Game::Game(const procid_t id, const std::string name) : m_ok(false), m_proc(id, name) {
if (!m_proc.isOk()) {
return;
}

const auto &modules = m_proc.modules();
const auto iter = modules.find("GameAssembly.dll");
if (iter == modules.cend()) {
return;
}

// 74 89 jz short loc_????????
// A1 ?? ?? ?? ?? mov eax, AmongUsClient_c **
// 8B 40 5C mov eax, [eax+5Ch]
const std::vector< uint8_t > clientPattern = { 0x74, 0x39, 0xA1, '?', '?', '?', '?', 0x8B, 0x40, 0x5C };
m_client = m_proc.findPattern(clientPattern, iter->second);
if (!m_client) {
return;
}

// +3 in order to skip to the memory address we actually care about
m_client = m_proc.peekPtr(m_proc.peekPtr(m_client + 3));
if (!m_client) {
return;
}

const auto clientC = m_proc.peek< AmongUsClient_c >(m_client);
if (!clientC.staticFields) {
return;
}

const auto clientStaticFields = m_proc.peek< AmongUsClient_StaticFields >(clientC.staticFields);
if (!clientStaticFields.instance) {
return;
}

m_client = clientStaticFields.instance;

const auto fields = clientFields();

const auto playerControlO = m_proc.peek< PlayerControl_o >(fields.playerPrefab);
if (!playerControlO.klass) {
return;
}

const auto playerControlC = m_proc.peek< PlayerControl_c >(playerControlO.klass);
if (!playerControlC.staticFields) {
return;
}

m_playerControlStaticFields = playerControlC.staticFields;

m_ok = true;
}

PlayerControl_Fields Game::playerControlFields() {
const auto playerControlStaticFields = m_proc.peek< PlayerControl_StaticFields >(m_playerControlStaticFields);
if (playerControlStaticFields.localPlayer) {
return m_proc.peek< PlayerControl_o >(playerControlStaticFields.localPlayer).fields;
}

return {};
}

std::string Game::string(const procptr_t address) {
const auto object = m_proc.peek< String_o >(address);

std::u16string string;
string.resize(object.fields.length);
if (m_proc.peek(address + sizeof(object), &string[0], sizeof(char16_t) * string.size())) {
return utf16ToUtf8(string.data());
}

return {};
}
41 changes: 41 additions & 0 deletions plugins/amongus/Game.h
@@ -0,0 +1,41 @@
// Copyright 2020 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>.

#ifndef AMONGUS_GAME
#define AMONGUS_GAME

#include "structs.h"

#include "ProcessWindows.h"

class Game {
protected:
bool m_ok;
ptr_t m_client;
ptr_t m_playerControlStaticFields;
ProcessWindows m_proc;

public:
inline bool isOk() const { return m_ok; }

inline AmongUsClient_Fields clientFields() const { return m_proc.peek< AmongUsClient_o >(m_client).fields; }

inline GameData_PlayerInfo_Fields playerInfoFields(const ptr_t cachedData) const {
return m_proc.peek< GameData_PlayerInfo_o >(cachedData).fields;
}

inline UnityEngine_Vector2_Fields playerPosition(const ptr_t netTransform) const {
const auto networkTransform = m_proc.peek< CustomNetworkTransform_o >(netTransform);
return networkTransform.fields.prevPosSent.fields;
}

PlayerControl_Fields playerControlFields();

std::string string(const procptr_t address);

Game(const procid_t id, const std::string name);
};

#endif
123 changes: 123 additions & 0 deletions plugins/amongus/amongus.cpp
@@ -0,0 +1,123 @@
// Copyright 2020 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 "Game.h"

#include "mumble_plugin.h"
#include "mumble_plugin_utils.h"

#include <sstream>

std::unique_ptr< Game > game;

static inline bool inGame(const AmongUsClient_Fields &fields) {
if (fields.gameMode != GameMode::FreePlay && fields.gameState != GameState::NotJoined) {
return true;
}

return false;
}

static int fetch(float *avatarPos, float *avatarFront, float *avatarTop, float *cameraPos, float *cameraFront,
float *cameraTop, std::string &context, std::wstring &identity) {
for (uint8_t i = 0; i < 3; ++i) {
avatarPos[i] = avatarFront[i] = avatarTop[i] = cameraPos[i] = cameraFront[i] = cameraTop[i] = 0.f;
}

const auto fields = game->clientFields();

if (!inGame(fields)) {
return false;
}

if (fields.gameState == GameState::Ended) {
return true;
}

const auto playerControlFields = game->playerControlFields();

// Mumble | Game
// X | X
// Y | -
// Z | Y
const auto position = game->playerPosition(playerControlFields.netTransform);
avatarPos[0] = cameraPos[0] = position.x;
avatarPos[2] = cameraPos[2] = position.y;

// Context
std::ostringstream stream;
stream << " {\"Game ID\": " << fields.gameId << "}";
context = stream.str();

stream.str(std::string());
stream.clear();

// Identity
const auto playerFields = game->playerInfoFields(playerControlFields.cachedData);

stream << "Name: " << game->string(playerFields.playerName) << '\n';
stream << "Client ID: " << std::to_string(fields.clientId) << '\n';
stream << "Player ID: " << std::to_string(playerFields.playerId) << '\n';
stream << "Color ID: " << std::to_string(playerFields.colorId) << '\n';
stream << "Skin ID: " << std::to_string(playerFields.skinId) << '\n';
stream << "Hat ID: " << std::to_string(playerFields.hatId) << '\n';
stream << "Pet ID: " << std::to_string(playerFields.petId) << '\n';
stream << "Dead: " << (playerFields.isDead ? "true" : "false") << '\n';
stream << "Host ID: " << std::to_string(fields.hostId) << '\n';
stream << "Public game: " << (fields.isGamePublic ? "true" : "false");

identity = utf8ToUtf16(stream.str());

return true;
}

static int tryLock(const std::multimap< std::wstring, unsigned long long int > &pids) {
const std::string name = "Among Us.exe";
const auto id = Process::find(name, pids);
if (!id) {
return false;
}

game = std::make_unique<Game>(id, name);
if (!game->isOk()) {
game.reset();
return false;
}

const auto fields = game->clientFields();
if (!inGame(fields)) {
game.reset();
return false;
}

return true;
}

static const std::wstring longDesc() {
return std::wstring(L"Supports Among Us with context and identity support.");
}

static std::wstring description(L"Among Us");
static std::wstring shortName(L"Among Us");

static int tryLock1() {
return tryLock(std::multimap< std::wstring, unsigned long long int >());
}

static void nullUnlock() {
}

static MumblePlugin amongusPlug = { MUMBLE_PLUGIN_MAGIC, description, shortName, nullptr, nullptr, tryLock1,
nullUnlock, longDesc, fetch };

static MumblePlugin2 amongusPlug2 = { MUMBLE_PLUGIN_MAGIC_2, MUMBLE_PLUGIN_VERSION, tryLock };

extern "C" MUMBLE_PLUGIN_EXPORT MumblePlugin *getMumblePlugin() {
return &amongusPlug;
}

extern "C" MUMBLE_PLUGIN_EXPORT MumblePlugin2 *getMumblePlugin2() {
return &amongusPlug2;
}

0 comments on commit 0155a24

Please sign in to comment.