Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
FEAT(positional-audio): Add plugin for Among Us
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
1 parent
4f8540e
commit 0155a24
Showing
5 changed files
with
520 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 {}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} |
Oops, something went wrong.