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:
authorDavide Beatrici <git@davidebeatrici.dev>2020-09-27 21:12:17 +0300
committerDavide Beatrici <git@davidebeatrici.dev>2020-09-27 21:12:17 +0300
commit501072ef50ae7d8bcd5168f1aea23013d04ce8c0 (patch)
tree29b66e1944882c6cb069259578ca737bccb939d6 /plugins
parent5df2bb2c0bdf91ed078e3cd3fafb04bdec5333c8 (diff)
FEAT(client): add Source Engine plugin, retract Left 4 Dead 1 & 2 plugins
5df2bb2c0bdf91ed078e3cd3fafb04bdec5333c8 explains how we gain access to the game's interfaces. Once we have access to the interfaces, we retrieve the address of the variables' we're interested in by reading the assembly of one or more functions accessing them. The process for each function is documented in the code.
Diffstat (limited to 'plugins')
-rw-r--r--plugins/l4d/CMakeLists.txt7
-rw-r--r--plugins/l4d/l4d.cpp198
-rw-r--r--plugins/l4d2/CMakeLists.txt7
-rw-r--r--plugins/l4d2/l4d2.cpp257
-rw-r--r--plugins/se/CMakeLists.txt6
-rw-r--r--plugins/se/client.h177
-rw-r--r--plugins/se/common.h86
-rw-r--r--plugins/se/engine.h183
-rw-r--r--plugins/se/se.cpp311
9 files changed, 775 insertions, 457 deletions
diff --git a/plugins/l4d/CMakeLists.txt b/plugins/l4d/CMakeLists.txt
index 7a429da99..9413ad4b5 100644
--- a/plugins/l4d/CMakeLists.txt
+++ b/plugins/l4d/CMakeLists.txt
@@ -3,4 +3,9 @@
# 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(l4d SHARED "l4d.cpp")
+set(PLUGIN_RETRACTED ON PARENT_SCOPE)
+if(retracted-plugins)
+ add_library(l4d SHARED "../null_plugin.cpp")
+
+ target_compile_definitions(l4d PRIVATE "NULL_DESC=L\"Left 4 Dead (retracted, now using Source Engine plugin)\"")
+endif()
diff --git a/plugins/l4d/l4d.cpp b/plugins/l4d/l4d.cpp
deleted file mode 100644
index 81ec76103..000000000
--- a/plugins/l4d/l4d.cpp
+++ /dev/null
@@ -1,198 +0,0 @@
-// Copyright 2005-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 "../mumble_plugin_main.h" // Include standard plugin header.
-#include "../mumble_plugin_utils.h" // Include plugin header for special functions, like "escape".
-
-static procptr_t steamclient, engine; // Variables to contain modules addresses
-
-static int fetch(float *avatar_pos, float *avatar_front, float *avatar_top, float *camera_pos, float *camera_front,
- float *camera_top, std::string &context, std::wstring &identity) {
- for (int i = 0; i < 3; i++) {
- avatar_pos[i] = avatar_front[i] = avatar_top[i] = camera_pos[i] = camera_front[i] = camera_top[i] = 0.0f;
- }
-
- // Boolean value to check if game addresses retrieval is successful
- bool ok;
- // Create containers to stuff our raw data into, so we can convert it to Mumble's coordinate system
- float avatar_pos_corrector[3], camera_pos_corrector[3], camera_front_corrector[3], camera_top_corrector[3];
- // Char values for extra features
- char serverid[22], host[22], map[30], playerid[22];
- // State
- bool state;
-
- // State pointers
- procptr_t state_base = peekProcPtr(pModule + 0x5A7354);
- if (state_base == 0)
- return false;
- procptr_t state_offset = peekProcPtr(state_base + 0x5C);
- if (state_offset == 0)
- return false;
-
- // Peekproc and assign game addresses to our containers, so we can retrieve positional data
- ok = peekProc(state_offset + 0x8, state) && // Magical state value: 0 when not playing and 1 when in-game.
- peekProc(pModule + 0x512264, avatar_pos_corrector) && // Avatar position values (X, Z and Y).
- peekProc(pModule + 0x5943B0, camera_pos_corrector) && // Camera position values (X, Z and Y).
- peekProc(pModule + 0x594410, camera_front_corrector) && // Front vector values (X, Z and Y).
- peekProc(pModule + 0x594440, camera_top_corrector) && // Top vector values (X, Z and Y).
- peekProc(steamclient + 0x95E56D, serverid) && // Unique server Steam ID.
- peekProc(engine + 0x61F5D8, host) && // Server value: "IP:Port" (xxx.xxx.xxx.xxx:yyyyy) when in a remote server
- // and "loopback" when on a local server.
- peekProc(pModule + 0x5A7A29, map) && // Map name.
- peekProc(engine + 0x40CF00, playerid); // Unique player Steam ID.
-
- // This prevents the plugin from linking to the game in case something goes wrong during values retrieval from
- // memory addresses.
- if (!ok)
- return false;
-
- // State
- if (!state) { // If not in-game
- context.clear(); // Clear context
- identity.clear(); // Clear identity
-
- // Set vectors values to 0.
- for (int i = 0; i < 3; i++) {
- avatar_pos[i] = avatar_front[i] = avatar_top[i] = camera_pos[i] = camera_front[i] = camera_top[i] = 0.0f;
- }
-
- return true; // This tells Mumble to ignore all vectors.
- }
-
- // Begin context
- escape(serverid, sizeof(serverid));
- escape(host, sizeof(host));
- std::ostringstream ocontext;
- if (strcmp(serverid, "") != 0) {
- ocontext << " {\"Server ID\": \"" << serverid << "\"}"; // Set context with server ID
- } else {
- ocontext << " {\"Host\": \"" << host << "\"}"; // Set context with IP address and port
- }
-
- context = ocontext.str();
- // End context
-
- // Begin identity
- std::wostringstream oidentity;
- oidentity << "{";
-
- // Host
- if (strcmp(host, "") != 0
- && !strstr(host, "loopback")) { // Only include host (IP:Port) if it is not empty and does not include the
- // string "loopback" (which means it's a local server).
- oidentity << std::endl << "\"Host\": \"" << host << "\","; // Set host address in identity.
- } else {
- oidentity << std::endl << "\"Host\": null,";
- }
-
- // Map
- escape(map, sizeof(map));
- if (strcmp(map, "") != 0) {
- oidentity << std::endl << "\"Map\": \"" << map << "\","; // Set map name in identity.
- } else {
- oidentity << std::endl << "\"Map\": null,";
- }
-
- // Player ID
- escape(playerid, sizeof(playerid));
- if (strcmp(playerid, "") != 0) {
- oidentity << std::endl << "\"Player ID\": \"" << playerid << "\""; // Set player ID in identity.
- } else {
- oidentity << std::endl << "\"Player ID\": null";
- }
-
- oidentity << std::endl << "}";
- identity = oidentity.str();
- // End identity
-
- /*
- Mumble | Game
- X | X
- Y | Z
- Z | Y
- */
- avatar_pos[0] = avatar_pos_corrector[0];
- avatar_pos[1] = avatar_pos_corrector[2];
- avatar_pos[2] = avatar_pos_corrector[1];
-
- camera_pos[0] = camera_pos_corrector[0];
- camera_pos[1] = camera_pos_corrector[2];
- camera_pos[2] = camera_pos_corrector[1];
-
- camera_front[0] = camera_front_corrector[0];
- camera_front[1] = camera_front_corrector[2];
- camera_front[2] = camera_front_corrector[1];
-
- camera_top[0] = camera_top_corrector[0];
- camera_top[1] = camera_top_corrector[2];
- camera_top[2] = camera_top_corrector[1];
-
- // Convert from inches to meters and sync avatar vectors with camera ones
- for (int i = 0; i < 3; i++) {
- avatar_pos[i] /= 39.37f;
- camera_pos[i] /= 39.37f;
- avatar_front[i] = camera_front[i];
- avatar_top[i] = camera_top[i];
- }
-
- return true;
-}
-
-static int trylock(const std::multimap< std::wstring, unsigned long long int > &pids) {
- if (!initialize(pids, L"left4dead.exe", L"client.dll")) { // Retrieve "client.dll" module's memory address
- return false;
- }
-
- // Server ID
- steamclient = getModuleAddr(L"steamclient.dll"); // Retrieve "steamclient.dll" module's memory address
- // This prevents the plugin from linking to the game in case something goes wrong during module's memory address
- // retrieval.
- if (steamclient == 0)
- return false;
-
- // Host & Player ID
- engine = getModuleAddr(L"engine.dll"); // Retrieve "engine.dll" module's memory address
- // This prevents the plugin from linking to the game in case something goes wrong during module's memory address
- // retrieval.
- if (engine == 0)
- return false;
-
- // Check if we can get meaningful data from it
- float apos[3], afront[3], atop[3], cpos[3], cfront[3], ctop[3];
- std::wstring sidentity;
- std::string scontext;
-
- if (fetch(apos, afront, atop, cpos, cfront, ctop, scontext, sidentity)) {
- return true;
- } else {
- generic_unlock();
- return false;
- }
-}
-
-static const std::wstring longdesc() {
- return std::wstring(
- L"Supports Left 4 Dead version 1.0.3.1 with context and identity support."); // Plugin long description
-}
-
-static std::wstring description(L"Left 4 Dead (v1.0.3.1)"); // Plugin short description
-static std::wstring shortname(L"Left 4 Dead"); // Plugin short name
-
-static int trylock1() {
- return trylock(std::multimap< std::wstring, unsigned long long int >());
-}
-
-static MumblePlugin l4dplug = { MUMBLE_PLUGIN_MAGIC, description, shortname, nullptr, nullptr, trylock1,
- generic_unlock, longdesc, fetch };
-
-static MumblePlugin2 l4dplug2 = { MUMBLE_PLUGIN_MAGIC_2, MUMBLE_PLUGIN_VERSION, trylock };
-
-extern "C" MUMBLE_PLUGIN_EXPORT MumblePlugin *getMumblePlugin() {
- return &l4dplug;
-}
-
-extern "C" MUMBLE_PLUGIN_EXPORT MumblePlugin2 *getMumblePlugin2() {
- return &l4dplug2;
-}
diff --git a/plugins/l4d2/CMakeLists.txt b/plugins/l4d2/CMakeLists.txt
index 45005b94a..40213dae1 100644
--- a/plugins/l4d2/CMakeLists.txt
+++ b/plugins/l4d2/CMakeLists.txt
@@ -3,4 +3,9 @@
# 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(l4d2 SHARED "l4d2.cpp")
+set(PLUGIN_RETRACTED ON PARENT_SCOPE)
+if(retracted-plugins)
+ add_library(l4d2 SHARED "../null_plugin.cpp")
+
+ target_compile_definitions(l4d2 PRIVATE "NULL_DESC=L\"Left 4 Dead 2 (retracted, now using Source Engine plugin)\"")
+endif()
diff --git a/plugins/l4d2/l4d2.cpp b/plugins/l4d2/l4d2.cpp
deleted file mode 100644
index cb21fba2f..000000000
--- a/plugins/l4d2/l4d2.cpp
+++ /dev/null
@@ -1,257 +0,0 @@
-// Copyright 2005-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 "../mumble_plugin_main.h" // Include standard plugin header.
-#include "../mumble_plugin_utils.h" // Include plugin header for special functions, like "escape".
-
-// Variables to contain modules addresses
-procptr_t steamclient = 0;
-procptr_t server = 0;
-procptr_t engine = 0;
-
-#ifdef WIN32
-// Memory offsets
-const procptr_t state_offset = 0x6ACBD5;
-const procptr_t avatar_pos_offset = 0x6B9E1C;
-const procptr_t camera_pos_offset = 0x774B98;
-const procptr_t avatar_front_offset = 0x774BF8;
-const procptr_t avatar_top_offset = 0x774C28;
-const procptr_t host_offset = 0x772B24;
-const procptr_t servername_offset = 0x772D2C;
-const procptr_t map_offset = 0x772C28;
-const procptr_t serverid_steamclient_offset = 0x95E56D;
-const procptr_t player_server_offset = 0x7F87BC;
-const procptr_t playerid_engine_offset = 0x4EBF88;
-// Module names
-const wchar_t *exe_name = L"left4dead2.exe";
-const wchar_t *client_name = L"client.dll";
-const wchar_t *steamclient_name = L"steamclient.dll";
-const wchar_t *server_name = L"server.dll";
-const wchar_t *engine_name = L"engine.dll";
-#else
-// Memory offsets
-const procptr_t state_offset = 0xE0A24C;
-const procptr_t avatar_pos_offset = 0xE773FC;
-const procptr_t camera_pos_offset = 0xED8700;
-const procptr_t avatar_front_offset = 0xE3C138;
-const procptr_t avatar_top_offset = 0xE3C150;
-const procptr_t host_offset = 0xE356D0;
-const procptr_t servername_offset = 0xE358D8;
-const procptr_t map_offset = 0xE09E9D;
-const procptr_t serverid_steamclient_offset = 0x1216CA5;
-const procptr_t player_server_offset = 0xF340E4;
-const procptr_t playerid_engine_offset = 0xA62C60;
-// Module names
-const wchar_t *exe_name = L"hl2_linux";
-const wchar_t *client_name = L"client.so";
-const wchar_t *steamclient_name = L"steamclient.so";
-const wchar_t *server_name = L"server.so";
-const wchar_t *engine_name = L"engine.so";
-#endif
-
-static int fetch(float *avatar_pos, float *avatar_front, float *avatar_top, float *camera_pos, float *camera_front,
- float *camera_top, std::string &context, std::wstring &identity) {
- for (int i = 0; i < 3; i++) {
- avatar_pos[i] = avatar_front[i] = avatar_top[i] = camera_pos[i] = camera_front[i] = camera_top[i] = 0.0f;
- }
-
- // Boolean value to check if game addresses retrieval is successful
- bool ok;
- // Create containers to stuff our raw data into, so we can convert it to Mumble's coordinate system
- float avatar_pos_corrector[3], camera_pos_corrector[3], avatar_front_corrector[3], avatar_top_corrector[3];
- // Char values for extra features
- char serverid[22], host[22], servername[50], map[30], player[33], playerid[22];
- // State
- unsigned char state;
-
- // Peekproc and assign game addresses to our containers, so we can retrieve positional data
- ok = peekProc(pModule + state_offset, &state, 1)
- && // Magical state value: 0 or 255 when in main menu and 1 when in-game.
- peekProc(pModule + avatar_pos_offset, avatar_pos_corrector, 12) && // Avatar Position values (X, Z and Y).
- peekProc(pModule + camera_pos_offset, camera_pos_corrector, 12) && // Camera Position values (X, Z and Y).
- peekProc(pModule + avatar_front_offset, avatar_front_corrector, 12) && // Front vector values (X, Z and Y).
- peekProc(pModule + avatar_top_offset, avatar_top_corrector, 12) && // Top vector values (Z, X and Y).
- peekProc(steamclient + serverid_steamclient_offset, serverid) && // Unique server Steam ID.
- peekProc(pModule + host_offset, host)
- && // Server value: "IP:Port" (xxx.xxx.xxx.xxx:yyyyy) when in a remote server, "loopback:0" when on a local
- // server and empty when not playing.
- peekProc(pModule + servername_offset, servername) && // Server name.
- peekProc(pModule + map_offset, map) && // Map name.
- peekProc(server + player_server_offset, player) && // Player nickname.
- peekProc(engine + playerid_engine_offset, playerid); // Unique player Steam ID.
-
- // This prevents the plugin from linking to the game in case something goes wrong during values retrieval from
- // memory addresses.
- if (!ok)
- return false;
-
- // State
- if (state != 1) { // If not in-game
- context.clear(); // Clear context
- identity.clear(); // Clear identity
- // Set vectors values to 0.
- for (int i = 0; i < 3; i++) {
- avatar_pos[i] = avatar_front[i] = avatar_top[i] = camera_pos[i] = camera_front[i] = camera_top[i] = 0.0f;
- }
-
- return true; // This tells Mumble to ignore all vectors.
- }
-
- // Begin context
- escape(serverid, sizeof(serverid));
- std::ostringstream ocontext;
- if (strcmp(serverid, "") != 0) {
- ocontext << " {\"Server ID\": \"" << serverid << "\"}"; // Set context with IP address and port
- }
-
- context = ocontext.str();
- // End context
-
- // Begin identity
- std::wostringstream oidentity;
- oidentity << "{";
-
- // Host
- escape(host, sizeof(host));
- if (strcmp(host, "") != 0
- && !strstr(host, "loopback")) { // Only include host (IP:Port) if it is not empty and does not include the
- // string "loopback" (which means it's a local server).
- oidentity << std::endl << "\"Host\": \"" << host << "\","; // Set host address in identity.
- } else {
- oidentity << std::endl << "\"Host\": null,";
- }
-
- // Server name
- escape(servername, sizeof(servername));
- if (strcmp(servername, "") != 0) {
- oidentity << std::endl << "\"Server name\": \"" << servername << "\","; // Set server name in identity.
- } else {
- oidentity << std::endl << "\"Server name\": null,";
- }
-
- // Map
- escape(map, sizeof(map));
- if (strcmp(map, "") != 0) {
- oidentity << std::endl << "\"Map\": \"" << map << "\","; // Set map name in identity.
- } else {
- oidentity << std::endl << "\"Map\": null,";
- }
-
- // Player nickname
- escape(player, sizeof(player));
- if (strcmp(player, "") != 0) {
- oidentity << std::endl << "\"Player\": \"" << player << "\","; // Set player nickname in identity.
- } else {
- oidentity << std::endl << "\"Player\": null,";
- }
-
- // Player ID
- escape(playerid, sizeof(playerid));
- if (strcmp(playerid, "") != 0) {
- oidentity << std::endl << "\"Player ID\": \"" << playerid << "\""; // Set player ID in identity.
- } else {
- oidentity << std::endl << "\"Player ID\": null";
- }
-
- oidentity << std::endl << "}";
- identity = oidentity.str();
- // End identity
-
- /*
- Mumble | Game
- X | X
- Y | Z
- Z | Y
- */
- avatar_pos[0] = avatar_pos_corrector[0];
- avatar_pos[1] = avatar_pos_corrector[2];
- avatar_pos[2] = avatar_pos_corrector[1];
-
- camera_pos[0] = camera_pos_corrector[0];
- camera_pos[1] = camera_pos_corrector[2];
- camera_pos[2] = camera_pos_corrector[1];
-
- avatar_front[0] = avatar_front_corrector[0];
- avatar_front[1] = avatar_front_corrector[2];
- avatar_front[2] = avatar_front_corrector[1];
-
- avatar_top[0] = avatar_top_corrector[0];
- avatar_top[1] = avatar_top_corrector[2];
- avatar_top[2] = avatar_top_corrector[1];
-
- // Convert from inches to meters and sync camera vectors with avatar ones
- for (int i = 0; i < 3; i++) {
- avatar_pos[i] /= 39.37f;
- camera_pos[i] /= 39.37f;
- camera_front[i] = avatar_front[i];
- camera_top[i] = avatar_top[i];
- }
-
- return true;
-}
-
-static int trylock(const std::multimap< std::wstring, unsigned long long int > &pids) {
- if (!initialize(pids, exe_name, client_name)) { // Retrieve "client_name" module's memory address
- return false;
- }
-
- // Server ID
- steamclient = getModuleAddr(steamclient_name); // Retrieve "steamclient_name" module's memory address
- // This prevents the plugin from linking to the game in case something goes wrong during module's memory address
- // retrieval.
- if (!steamclient)
- return false;
-
- // Player name
- server = getModuleAddr(server_name); // Retrieve "server_name" module's memory address
- // This prevents the plugin from linking to the game in case something goes wrong during module's memory address
- // retrieval.
- if (!server)
- return false;
-
- // Player ID
- engine = getModuleAddr(engine_name); // Retrieve "engine_name" module's memory address
- // This prevents the plugin from linking to the game in case something goes wrong during module's memory address
- // retrieval.
- if (!engine)
- return false;
-
- // Check if we can get meaningful data from it
- float apos[3], afront[3], atop[3], cpos[3], cfront[3], ctop[3];
- std::wstring sidentity;
- std::string scontext;
-
- if (fetch(apos, afront, atop, cpos, cfront, ctop, scontext, sidentity)) {
- return true;
- } else {
- generic_unlock();
- return false;
- }
-}
-
-static const std::wstring longdesc() {
- return std::wstring(
- L"Supports Left 4 Dead 2 version 2.1.4.6 with context and identity support."); // Plugin long description
-}
-
-static std::wstring description(L"Left 4 Dead 2 (v2.1.4.6)"); // Plugin short description
-static std::wstring shortname(L"Left 4 Dead 2"); // Plugin short name
-
-static int trylock1() {
- return trylock(std::multimap< std::wstring, unsigned long long int >());
-}
-
-static MumblePlugin l4d2plug = { MUMBLE_PLUGIN_MAGIC, description, shortname, nullptr, nullptr, trylock1,
- generic_unlock, longdesc, fetch };
-
-static MumblePlugin2 l4d2plug2 = { MUMBLE_PLUGIN_MAGIC_2, MUMBLE_PLUGIN_VERSION, trylock };
-
-extern "C" MUMBLE_PLUGIN_EXPORT MumblePlugin *getMumblePlugin() {
- return &l4d2plug;
-}
-
-extern "C" MUMBLE_PLUGIN_EXPORT MumblePlugin2 *getMumblePlugin2() {
- return &l4d2plug2;
-}
diff --git a/plugins/se/CMakeLists.txt b/plugins/se/CMakeLists.txt
new file mode 100644
index 000000000..348abcaa7
--- /dev/null
+++ b/plugins/se/CMakeLists.txt
@@ -0,0 +1,6 @@
+# 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(se SHARED "se.cpp")
diff --git a/plugins/se/client.h b/plugins/se/client.h
new file mode 100644
index 000000000..583e971f7
--- /dev/null
+++ b/plugins/se/client.h
@@ -0,0 +1,177 @@
+// Copyright 2019-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 MUMBLE_MUMBLE_PLUGIN_SE_CLIENT_
+#define MUMBLE_MUMBLE_PLUGIN_SE_CLIENT_
+
+struct TypeDescription {
+ uint32_t fieldType;
+ uint32_t fieldName;
+ int32_t fieldOffset;
+ uint16_t fieldSize;
+ int16_t flags;
+ uint32_t externalName;
+ uint32_t saveRestoreOps;
+ uint32_t inputFunc;
+ uint32_t td;
+ int32_t fieldSizeInBytes;
+ uint32_t overrideField;
+ int32_t overrideCount;
+ float fieldTolerance;
+ int32_t flatOffset[2];
+ uint16_t flatGroup;
+};
+
+struct DataMap {
+ uint32_t dataDesc;
+ int32_t dataNumFields;
+ uint32_t dataClassName;
+ uint32_t baseMap;
+ int32_t nPackedSize;
+ uint32_t optimizedDataMap;
+};
+
+struct EntityCacheInfo {
+ uint32_t networkable;
+ uint16_t baseEntitiesIndex;
+ uint16_t isDormant;
+};
+
+static int32_t getDataVar(const std::string &name, procptr_t predMap) {
+ while (predMap) {
+ DataMap dataMap;
+ if (!peekProc(predMap, dataMap)) {
+ return 0;
+ }
+
+ // The structure is 4 bytes bigger on Linux.
+ const size_t realStructSize = isWin32 ? sizeof(TypeDescription) : sizeof(TypeDescription) + 4;
+
+ const auto descs = peekProcVector< TypeDescription >(dataMap.dataDesc, dataMap.dataNumFields, realStructSize);
+ for (const auto &desc : descs) {
+ if (!desc.fieldName) {
+ continue;
+ }
+
+ const auto fieldName = peekProcString(desc.fieldName);
+
+ if (fieldName == name) {
+ return desc.fieldOffset;
+ }
+
+ if (desc.td) {
+ const auto offset = getDataVar(name, desc.td);
+ if (offset) {
+ return offset;
+ }
+ }
+ }
+
+ predMap = dataMap.baseMap;
+ }
+
+ return 0;
+}
+
+static int32_t getDataVarFromEntity(const std::string &name, const procptr_t entity) {
+ procptr_t GetPredDescMap;
+
+ // Brute-force virtual function index.
+ for (uint8_t i = 20; i > 16; --i) {
+ GetPredDescMap = getVirtualFunction(entity, i);
+
+ if (peekProc< uint8_t >(GetPredDescMap + (isWin32 ? 0 : 1)) == 0xB8) {
+ break;
+ }
+ }
+
+ // Windows:
+ // B8 ?? ?? ?? ?? mov eax, offset dword_????????
+ // C3 retn
+ //
+ // Linux:
+ // 55 push ebp
+ // B8 ?? ?? ?? ?? mov eax, offset dword_????????
+ // 89 E5 mov ebp, esp
+ // 5D pop ebp
+ // C3 retn
+ return getDataVar(name, peekProc< uint32_t >(GetPredDescMap + (isWin32 ? 1 : 2)));
+}
+
+static procptr_t getLocalPlayer(const procptr_t localClient, procptr_t clientEntityList, const procptr_t engineClient) {
+ const auto GetLocalPlayer = getVirtualFunction(engineClient, 12);
+
+ // Windows:
+ // 6A FF push 0FFFFFFFFh
+ // E8 ?? ?? ?? ?? call GetLocalClient
+ // 8B 80 ?? ?? ?? ?? mov eax, [eax+?]
+ // 83 C4 04 add esp, 4
+ // 40 inc eax
+ // C3 retn
+ //
+ // Linux:
+ // 55 push ebp
+ // 89 E5 mov ebp, esp
+ // 83 EC 18 sub esp, 18h
+ // C7 04 24 FF FF FF FF mov dword ptr [esp], 0FFFFFFFFh
+ // E8 ?? ?? ?? ?? call GetLocalClient
+ // 8B 80 ?? ?? ?? ?? mov eax, [eax+?]
+ // C9 leave
+ // 83 C0 01 add eax, 1
+ // C3 retn
+ const auto localPlayerIndexOffset = peekProc< int32_t >(GetLocalPlayer + (isWin32 ? 9 : 14));
+ const auto localPlayerIndex = peekProc< uint32_t >(localClient + localPlayerIndexOffset) + 1;
+
+ auto GetClientNetworkable = getVirtualFunction(clientEntityList, 0);
+
+ // Left 4 Dead:
+ // 8B 44 24 04 mov eax, [esp+arg_0]
+ // 8B 44 C1 ?? mov eax, [ecx+eax*8+?]
+ // C2 04 00 retn 4
+
+ // Windows:
+ // 55 push ebp
+ // 8B EC mov ebp, esp
+ // 8B 45 08 mov eax, [ebp+arg_0]
+ // 8B 44 C1 ?? mov eax, [ecx+eax*8+?]
+ // 5D pop ebp
+ // C2 04 00 retn 4
+ //
+ // Linux:
+ // 81 6C 24 04 ?? ?? ?? ?? sub [esp+arg_0], ????????
+ // EB ?? jmp short GetClientNetworkable
+ if (!isWin32) {
+ clientEntityList -= peekProc< int32_t >(GetClientNetworkable + 4);
+ GetClientNetworkable = GetClientNetworkable + 10 + peekProc< int8_t >(GetClientNetworkable + 9);
+
+ // 55 push ebp
+ // 89 E5 mov ebp, esp
+ // 8B 55 0C mov edx, [ebp+arg_4]
+ // 8B 45 08 mov eax, [ebp+arg_0]
+ // 5D pop ebp
+ // 8B 84 D0 ?? ?? ?? ?? mov eax, [eax+edx*8+?]
+ // C3 retn
+ }
+
+ procptr_t entityCacheInfo;
+
+ if (isWin32) {
+ if (peekProc< uint8_t >(GetClientNetworkable + 6) == 0xC1) {
+ // Left 4 Dead
+ entityCacheInfo = clientEntityList + peekProc< int8_t >(GetClientNetworkable + 7);
+ } else {
+ entityCacheInfo = clientEntityList + peekProc< int8_t >(GetClientNetworkable + 9);
+ }
+ } else {
+ entityCacheInfo = clientEntityList + peekProc< int32_t >(GetClientNetworkable + 13);
+ }
+
+ const auto entity = peekProc< EntityCacheInfo >(entityCacheInfo + sizeof(EntityCacheInfo) * localPlayerIndex);
+
+ // We subtract 8 bytes in order to cast from IClientNetworkable to IClientEntity.
+ return entity.networkable ? (entity.networkable - 8) : 0;
+}
+
+#endif
diff --git a/plugins/se/common.h b/plugins/se/common.h
new file mode 100644
index 000000000..d0e020938
--- /dev/null
+++ b/plugins/se/common.h
@@ -0,0 +1,86 @@
+// Copyright 2019-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 MUMBLE_MUMBLE_PLUGIN_SE_COMMON_
+#define MUMBLE_MUMBLE_PLUGIN_SE_COMMON_
+
+typedef std::map< std::string, procptr_t > Interfaces;
+
+struct InterfaceReg {
+ uint32_t createFunction;
+ uint32_t name;
+ uint32_t next;
+};
+
+static Interfaces getInterfaces(const procptr_t module) {
+ Interfaces interfaces;
+
+ // s_pInterfaceRegs is exported on Linux
+ auto s_pInterfaceRegs = getExportedSymbol("s_pInterfaceRegs", module);
+ if (!s_pInterfaceRegs) {
+ const auto CreateInterface = getExportedSymbol("CreateInterface", module);
+ if (CreateInterface == 0) {
+ return interfaces;
+ }
+
+ bool jmpOnly;
+
+ if (peekProc< uint8_t >(CreateInterface) == 0xE9) {
+ // Left 4 Dead:
+ // E9 ?? ?? ?? ?? jmp CreateInterface_0
+ jmpOnly = true;
+ } else {
+ // Other games:
+ // 55 push ebp
+ // 8B EC mov ebp, esp
+ // 5D pop ebp
+ // E9 ?? ?? ?? ?? jmp CreateInterfaceInternal
+ jmpOnly = false;
+ }
+
+ const auto jmpTarget = peekProc< int32_t >(CreateInterface + (jmpOnly ? 1 : 5));
+ const auto jmpInstructionEnd = CreateInterface + (jmpOnly ? 5 : 9);
+ const auto CreateInterfaceInternal = jmpInstructionEnd + jmpTarget;
+
+ // Left 4 Dead:
+ // 56 push esi
+ // 8B 35 ?? ?? ?? ?? mov esi, s_pInterfaceRegs
+ // 85 F6 test esi, esi
+ // 57 push edi
+ //
+ // Other games:
+ // 55 push ebp
+ // 8B EC mov ebp, esp
+ // 56 push esi
+ // 8B 35 ?? ?? ?? ?? mov esi, s_pInterfaceRegs
+ if (peekProc< uint16_t >(CreateInterfaceInternal + (jmpOnly ? 1 : 4)) != 0x358B) {
+ return interfaces;
+ }
+
+ s_pInterfaceRegs = peekProc< uint32_t >(CreateInterfaceInternal + (jmpOnly ? 3 : 6));
+ }
+
+ auto iface = peekProc< InterfaceReg >(peekProcPtr(s_pInterfaceRegs));
+
+ do {
+ const auto name = peekProcString(iface.name);
+ const auto address = peekProc< uint32_t >(iface.createFunction + (isWin32 ? 1 : 2));
+
+ interfaces.insert(Interfaces::value_type(name, address));
+ } while (iface.next && peekProc(iface.next, iface));
+
+ return interfaces;
+}
+
+static procptr_t getInterfaceAddress(const std::string &name, const Interfaces &interfaces) {
+ const auto iface = interfaces.find(name);
+ if (iface == interfaces.end()) {
+ return 0;
+ }
+
+ return iface->second;
+}
+
+#endif
diff --git a/plugins/se/engine.h b/plugins/se/engine.h
new file mode 100644
index 000000000..77f9a90fd
--- /dev/null
+++ b/plugins/se/engine.h
@@ -0,0 +1,183 @@
+// Copyright 2019-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 MUMBLE_MUMBLE_PLUGIN_SE_ENGINE_
+#define MUMBLE_MUMBLE_PLUGIN_SE_ENGINE_
+
+struct NetInfo {
+ uint32_t type;
+ uint8_t ip[4];
+ uint16_t port;
+};
+
+static procptr_t getLocalClient(const procptr_t engineClient) {
+ // We use GetBaseLocalClient() instead of GetLocalClient() because we just need the main client.
+ // GetLocalClient() gets the client from an array at the index passed to the function.
+ // There are multiple clients because of the split screen feature.
+
+ const auto GetNetChannelInfo = getVirtualFunction(engineClient, 74);
+
+ // Windows:
+ // E8 ?? ?? ?? ?? call GetBaseLocalClient
+ // 8B 40 ?? mov eax, [eax+?]
+ // C3 retn
+ //
+ // Linux:
+ // 55 push ebp
+ // 89 E5 mov ebp, esp
+ // 83 EC 08 sub esp, 8
+ // E8 ?? ?? ?? ?? call GetBaseLocalClient
+ // 8B 40 ?? mov eax, [eax+?]
+ // C9 leave
+ // C3 retn
+ const auto callTarget = peekProc< int32_t >(GetNetChannelInfo + (isWin32 ? 1 : 7));
+ const auto callInstructionEnd = GetNetChannelInfo + (isWin32 ? 5 : 11);
+ const auto GetBaseLocalClient = callInstructionEnd + callTarget;
+
+ // Windows:
+ // A1 ?? ?? ?? ?? mov eax, dword_????????
+ // 83 C0 ?? add eax, ?
+ // C3 retn
+ if (isWin32) {
+ return peekProcPtr(peekProc< uint32_t >(GetBaseLocalClient + 1)) + peekProc< int8_t >(GetBaseLocalClient + 7);
+ }
+
+ // Linux:
+ // 55 push ebp
+ // 89 E5 mov ebp, esp
+ // 83 EC 18 sub esp, 18h
+ // C7 44 24 04 00 00 00 00 mov dword ptr [esp+4], 0
+ // C7 04 24 ?? ?? ?? ?? mov dword ptr [esp], offset dword_????????
+ // E8 ?? ?? ?? ?? call sub_????????
+ // C9 leave
+ // C3 retn
+ //
+ // The function is quite different on Linux. It returns the result of an unknown function:
+ //
+ // 55 push ebp
+ // 89 E5 mov ebp, esp
+ // 53 push ebx
+ // 83 EC 14 sub esp, 14h
+ // 8B 45 0C mov eax, [ebp+arg_4]
+ // 8B 5D 08 mov ebx, [ebp+arg_0]
+ // 83 F8 FF cmp eax, 0FFFFFFFFh
+ // 74 0E jz short loc_1
+ // 8B 44 83 04 mov eax, [ebx+eax*4+4]
+ // 83 C0 04 add eax, 4
+ //
+ // loc_0:
+ // 83 C4 14 add esp, 14h
+ // 5B pop ebx
+ // 5D pop ebp
+ // C3 retn
+ //
+ // 90 align 10h
+ //
+ // loc_1:
+ // 8B 03 mov eax, [ebx]
+ // 89 1C 24 mov [esp], ebx
+ // FF 50 14 call dword ptr [eax+14h]
+ // 8B 44 83 04 mov eax, [ebx+eax*4+4]
+ // 83 C0 04 add eax, 4
+ // EB E8 jmp short loc_0
+ //
+ // Its purpose seem to be to iterate over the clients array, which is done directly by GetBaseLocalClient() and
+ // GetLocalClient() on Windows.
+ return peekProcPtr(peekProc< uint32_t >(GetBaseLocalClient + 17) + 4) + 4;
+}
+
+static int8_t getSignOnStateOffset(const procptr_t engineClient) {
+ const auto IsInGame = getVirtualFunction(engineClient, 26);
+
+ // Windows:
+ // E8 ?? ?? ?? ?? call GetBaseLocalClient
+ // 33 C9 xor ecx, ecx
+ // 83 78 ?? 06 cmp dword ptr [eax+?], 6
+ // 0F 94 C0 setz al
+ // C3 retn
+ //
+ // Linux:
+ // 55 push ebp
+ // 89 E5 mov ebp, esp
+ // 83 EC 08 sub esp, 8
+ // E8 ?? ?? ?? ?? call GetBaseLocalClient
+ // 83 78 ?? 06 cmp dword ptr [eax+?], 6
+ // C9 leave
+ // 0F 94 C0 setz al
+ // C3 retn
+ return peekProc< int8_t >(IsInGame + (isWin32 ? 9 : 13));
+}
+
+static int32_t getLevelNameOffset(const procptr_t engineClient) {
+ const auto GetLevelNameShort = getVirtualFunction(engineClient, 53);
+
+ // Windows:
+ // ...
+ //
+ // E8 ?? ?? ?? ?? call GetBaseLocalClient
+ // 05 ?? ?? ?? ?? add eax, ?
+ // C3 retn
+ //
+ // Linux:
+ // ...
+ //
+ // E8 ?? ?? ?? ?? call GetBaseLocalClient
+ // C9 leave
+ // 05 ?? ?? ?? ?? add eax, ?
+ // C3 retn
+ if (isWin32) {
+ if (peekProc< uint8_t >(GetLevelNameShort + 37) == 0x05) {
+ // Left 4 Dead
+ return peekProc< int32_t >(GetLevelNameShort + 38);
+ } else {
+ return peekProc< int32_t >(GetLevelNameShort + 40);
+ }
+ }
+
+ return peekProc< int32_t >(GetLevelNameShort + 57);
+}
+
+static int32_t getNetInfoOffset(const procptr_t localClient, const procptr_t engineClient) {
+ const auto GetNetChannelInfo = getVirtualFunction(engineClient, 74);
+
+ // Windows:
+ // E8 ?? ?? ?? ?? call GetBaseLocalClient
+ // 8B 40 ?? mov eax, [eax+?]
+ // C3 retn
+ //
+ // Linux:
+ // 55 push ebp
+ // 89 E5 mov ebp, esp
+ // 83 EC 08 sub esp, 8
+ // E8 ?? ?? ?? ?? call GetBaseLocalClient
+ // 8B 40 ?? mov eax, [eax+?]
+ // C9 leave
+ // C3 retn
+ const auto NetChannelInfo = peekProcPtr(localClient + peekProc< int8_t >(GetNetChannelInfo + (isWin32 ? 7 : 13)));
+ const auto GetAddress = getVirtualFunction(NetChannelInfo, 1);
+
+ // Windows:
+ // 6A 00 push 0
+ // 81 C1 ?? ?? ?? ?? add ecx, ?
+ // E8 C3 9D 1D 00 call ToString
+ // C3 retn
+ //
+ // Linux:
+ // 55 push ebp
+ // 89 E5 mov ebp, esp
+ // 83 EC ?? sub esp, ?
+ // 8B 45 08 mov eax, [ebp+arg_0]
+ // C7 44 24 04 00 00 00 00 mov dword ptr [esp+4], 0
+ // 05 ?? ?? ?? ?? add eax, ?
+ // 89 04 24 mov [esp], eax
+ // E8 ?? ?? ?? ?? call ToString
+ // C9 leave
+ // C3 retn
+ const auto netInfo = NetChannelInfo + peekProc< int32_t >(GetAddress + (isWin32 ? 4 : 18));
+
+ return static_cast< int32_t >(netInfo - localClient);
+}
+
+#endif
diff --git a/plugins/se/se.cpp b/plugins/se/se.cpp
new file mode 100644
index 000000000..b84363e3a
--- /dev/null
+++ b/plugins/se/se.cpp
@@ -0,0 +1,311 @@
+// Copyright 2019-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 "../mumble_plugin_main.h"
+#include "../mumble_plugin_utils.h"
+
+#include "client.h"
+#include "common.h"
+#include "engine.h"
+
+static inline void anglesToVectors(const float (&angles)[3], float *front, float *top) {
+ // Calculate pitch
+ float pitchSin, pitchCos;
+ sinCos(degreesToRadians(angles[0]), pitchSin, pitchCos);
+
+ // Calculate yaw
+ float yawSin, yawCos;
+ sinCos(degreesToRadians(angles[1]), yawSin, yawCos);
+
+ // Calculate roll
+ float rollSin, rollCos;
+ sinCos(degreesToRadians(angles[2]), rollSin, rollCos);
+
+ // Calculate front vector
+ front[0] = pitchCos * yawCos;
+ front[1] = -pitchSin;
+ front[2] = pitchCos * yawSin;
+
+ // Calculate top vector
+ top[0] = (rollCos * pitchSin * yawCos + -rollSin * -yawSin);
+ top[1] = rollCos * pitchCos;
+ top[2] = (rollCos * pitchSin * yawSin + -rollSin * yawCos);
+}
+
+procptr_t localClient, localPlayer;
+int32_t signOnStateOffset, deadFlagOffset, rotationOffset, originPositionOffset, eyesPositionOffsetOffset,
+ netInfoOffset, levelNameOffset;
+
+static int fetch(float *avatarPos, float *avatarFront, float *avatarTop, float *cameraPos, float *cameraFront,
+ float *cameraTop, std::string &context, std::wstring &identity) {
+ const auto signOnState = peekProc< uint32_t >(localClient + signOnStateOffset);
+
+ // 0: SIGNONSTATE_NONE
+ // 1: SIGNONSTATE_CHALLENGE
+ // 2: SIGNONSTATE_CONNECTED
+ // 3: SIGNONSTATE_NEW
+ // 4: SIGNONSTATE_PRESPAWN
+ // 5: SIGNONSTATE_SPAWN
+ // 6: SIGNONSTATE_FULL
+ // 7: SIGNONSTATE_CHANGELEVEL
+ if (signOnState != 6) {
+ return false;
+ }
+
+ const auto isPlayerDead = peekProc< bool >(localPlayer + deadFlagOffset);
+ if (isPlayerDead) {
+ context.clear();
+ identity.clear();
+
+ for (uint8_t i = 0; i < 3; ++i) {
+ avatarPos[i] = avatarFront[i] = avatarTop[i] = cameraPos[i] = cameraFront[i] = cameraTop[i] = 0.0f;
+ }
+
+ return true;
+ }
+
+ float rotation[3];
+ if (!peekProc(localPlayer + rotationOffset, rotation, sizeof(rotation))) {
+ return false;
+ }
+
+ float originPosition[3];
+ if (!peekProc(localPlayer + originPositionOffset, originPosition, sizeof(originPosition))) {
+ return false;
+ }
+
+ float eyesPositionOffset[3];
+ if (!peekProc(localPlayer + eyesPositionOffsetOffset, eyesPositionOffset, sizeof(eyesPositionOffset))) {
+ return false;
+ }
+
+ NetInfo ni;
+ if (!peekProc(localClient + netInfoOffset, ni)) {
+ return false;
+ }
+
+ std::string host;
+
+ // 0: NA_NULL
+ // 1: NA_LOOPBACK
+ // 2: NA_BROADCAST
+ // 3: NA_IP
+ switch (ni.type) {
+ case 3: {
+ std::ostringstream ss;
+ for (uint8_t i = 0; i < 4; ++i) {
+ ss << std::to_string(ni.ip[i]);
+ if (i < 3) {
+ ss << ".";
+ }
+ }
+
+ ss << ":" << networkToHost(ni.port);
+ host = ss.str();
+ break;
+ }
+ default:
+ return true;
+ }
+
+ // 40 is the size of the char array in the engine's code
+ char map[40];
+ if (!peekProc(localClient + levelNameOffset, map, sizeof(map))) {
+ map[0] = '\0';
+ }
+
+ // Begin context
+ std::ostringstream ocontext;
+ ocontext << " {\"Host\": \"" << host << "\"}"; // Set context with server's IP address and port.
+ context = ocontext.str();
+ // End context
+
+ // Begin identity
+ std::wostringstream oidentity;
+ oidentity << "{";
+
+ // Map
+ escape(map, sizeof(map));
+ if (strcmp(map, "") != 0) {
+ oidentity << std::endl << "\"Map\": \"" << map << "\""; // Set map name in identity.
+ } else {
+ oidentity << std::endl << "\"Map\": null";
+ }
+
+ oidentity << std::endl << "}";
+ identity = oidentity.str();
+ // End identity
+
+ // Mumble | Game
+ // X | X
+ // Y | Z
+ // Z | Y
+ //
+ // originPosition corresponds to the origin of the player model,
+ // usually the point (i.e. feet) where it touches the ground.
+ // eyesPositionOffset is the difference between the eyes and the origin position.
+ // We use the eyes' position because they are closer to the mouth.
+ avatarPos[0] = originPosition[0] + eyesPositionOffset[0];
+ avatarPos[1] = originPosition[2] + eyesPositionOffset[2];
+ avatarPos[2] = originPosition[1] + eyesPositionOffset[1];
+
+ anglesToVectors(rotation, avatarFront, avatarTop);
+
+ // Sync camera vectors with avatar ones.
+ //
+ // This is not ideal, but it's not an issue unless 3rd person mode is enabled.
+ // The command to set it is "thirdperson" and requires "sv_cheats" to be enabled.
+ // For reference, the functions that return what we need are MainViewOrigin() and MainViewAngles().
+ for (uint8_t i = 0; i < 3; ++i) {
+ cameraPos[i] = avatarPos[i] /= 39.37f; // Convert from inches to meters.
+ cameraFront[i] = avatarFront[i];
+ cameraTop[i] = avatarTop[i];
+ }
+
+ return true;
+}
+
+static bool tryInit(const std::multimap< std::wstring, unsigned long long int > &pids) {
+#ifdef OS_LINUX
+ if (initialize(pids, L"hl2_linux")) {
+ return true;
+ }
+#endif
+
+ if (initialize(pids, L"left4dead2.exe")) {
+ return true;
+ }
+
+ if (initialize(pids, L"left4dead.exe")) {
+ return true;
+ }
+
+ return false;
+}
+
+static int tryLock(const std::multimap< std::wstring, unsigned long long int > &pids) {
+ if (!tryInit(pids)) {
+ return false;
+ }
+
+ const auto engine = getModuleAddr(isWin32 ? L"engine.dll" : L"engine.so");
+ if (!engine) {
+ return false;
+ }
+
+ const auto client = getModuleAddr(isWin32 ? L"client.dll" : L"client.so");
+ if (!client) {
+ return false;
+ }
+
+ const auto engineInterfaces = getInterfaces(engine);
+
+ const auto engineClient = getInterfaceAddress("VEngineClient013", engineInterfaces);
+ if (!engineClient) {
+ return false;
+ }
+
+ localClient = getLocalClient(engineClient);
+ if (!localClient) {
+ return false;
+ }
+
+ signOnStateOffset = getSignOnStateOffset(engineClient);
+ if (!signOnStateOffset) {
+ return false;
+ }
+
+ const auto signOnState = peekProc< uint32_t >(localClient + signOnStateOffset);
+ if (signOnState != 6) {
+ return false;
+ }
+
+ const auto clientInterfaces = getInterfaces(client);
+
+ const auto clientEntityList = getInterfaceAddress("VClientEntityList003", clientInterfaces);
+ if (!clientEntityList) {
+ return false;
+ }
+
+ localPlayer = getLocalPlayer(localClient, clientEntityList, engineClient);
+ if (!localPlayer) {
+ return false;
+ }
+
+ // "pl" is the offset to the internal player class.
+ const auto pl = getDataVarFromEntity("pl", localPlayer);
+ if (!pl) {
+ return false;
+ }
+
+ // "deadflag" is the offset relative to "pl".
+ //
+ // For some reason it's missing from Left 4 Dead 2 on Linux, along with many other datavars we don't need.
+ // The offset (0x4) has never changed across the various engine versions released during the years (10+), thus we
+ // force it.
+ const auto deadflag = getDataVarFromEntity("deadflag", localPlayer);
+ deadFlagOffset = pl + (deadflag ? deadflag : 0x4);
+
+ originPositionOffset = getDataVarFromEntity("m_vecAbsOrigin", localPlayer);
+ if (!originPositionOffset) {
+ return false;
+ }
+
+ eyesPositionOffsetOffset = getDataVarFromEntity("m_vecViewOffset", localPlayer);
+ if (!eyesPositionOffsetOffset) {
+ return false;
+ }
+
+ rotationOffset = getDataVarFromEntity("m_angAbsRotation", localPlayer);
+ if (!rotationOffset) {
+ return false;
+ }
+
+ netInfoOffset = getNetInfoOffset(localClient, engineClient);
+ if (!netInfoOffset) {
+ return false;
+ }
+
+ levelNameOffset = getLevelNameOffset(engineClient);
+ if (!levelNameOffset) {
+ return false;
+ }
+
+ float avatarPos[3], avatarFront[3], avatarTop[3];
+ float cameraPos[3], cameraFront[3], cameraTop[3];
+ std::string context;
+ std::wstring identity;
+
+ if (fetch(avatarPos, avatarFront, avatarTop, cameraPos, cameraFront, cameraTop, context, identity)) {
+ return true;
+ } else {
+ generic_unlock();
+ return false;
+ }
+}
+
+static const std::wstring longDesc() {
+ return std::wstring(L"Supports Source Engine games with context and identity support.");
+}
+
+static std::wstring description(L"Source Engine");
+static std::wstring shortName(L"Source Engine");
+
+static int tryLock1() {
+ return tryLock(std::multimap< std::wstring, unsigned long long int >());
+}
+
+static MumblePlugin sePlug = { MUMBLE_PLUGIN_MAGIC, description, shortName, NULL, NULL, tryLock1,
+ generic_unlock, longDesc, fetch };
+
+static MumblePlugin2 sePlug2 = { MUMBLE_PLUGIN_MAGIC_2, MUMBLE_PLUGIN_VERSION, tryLock };
+
+extern "C" MUMBLE_PLUGIN_EXPORT MumblePlugin *getMumblePlugin() {
+ return &sePlug;
+}
+
+extern "C" MUMBLE_PLUGIN_EXPORT MumblePlugin2 *getMumblePlugin2() {
+ return &sePlug2;
+}