Difference between revisions of "Pluginguide"

From Mumble Wiki
Jump to: navigation, search
m
Line 1: Line 1:
 
=Introduction=
 
=Introduction=
  
==test==
+
Game positional audio is a feature of Mumble that many users consider very useful. However, creating the game plugin can sometimes be complicated, and for the average person, daunting. This guide will help you understand how a game plugin works, what it does, and how you can make one.
  
 
=First Steps=
 
=First Steps=
  
==test==
+
==Tools Needed==
 +
 
 +
[http://www.heijnen1.demon.nl/CheatEngine55.exe Cheat Engine]. Just use the default options when installing. Note that Mumble does NOT support cheating of any kind. We use Cheat Engine because the interface is easy to use, and the program is fits our purposes; Cheat Engine is simply a memory searching tool, which is required to find the positional addresses in the game.
 +
 
 +
[http://www.microsoft.com/express/Downloads/#2008-Visual-CPP Visual C++ 2008 Express edition]. Again, default options, except for the SQL server, which you can uncheck.
 +
 
 +
[https://sourceforge.net/projects/notepad-plus/files/ Notepad++]. After you install Notepad++, start it, go to Preferences -> New Document/Default Directory, and check "Unix" in the Format box.
 +
 
 +
==Learn a Little C++==
 +
 
 +
Although you do not need to be an expert programmer in order to write a plugin, you do need to understand fundamental data types. Here are a few of the most important:
 +
 
 +
float: This is the data type that almost all positional audio game addresses use. They are 32 bit, decimal numbers stored in the memory. A float data type is 4 bytes * 8 = 32 bits. An example of a floating point value would be "1234.0123456".
 +
 
 +
byte: This is the smallest data type in Intel x86-based computing. This type of memory address holds 1 byte of information (1 byte * 8 = 8 bits). From this type of memory address, you can get 0-255 base^10 values, or -127 to 128, depending on whether or not you use a signed byte (it has a + or - on the front of the value), or an unsigned byte (no + or -). An example of a byte value would be "12".
 +
 
 +
==How a Plugin Works==
 +
 
 +
Below is a standard template that you can use for your plugin making.
 +
 
 +
<code>
 +
/* <your copyright here>
 +
  Copyright (C) 2005-2010, Thorvald Natvig <thorvald@natvig.com>
 +
 
 +
  All rights reserved.
 +
 
 +
  Redistribution and use in source and binary forms, with or without
 +
  modification, are permitted provided that the following conditions
 +
  are met:
 +
 
 +
  - Redistributions of source code must retain the above copyright notice,
 +
    this list of conditions and the following disclaimer.
 +
  - Redistributions in binary form must reproduce the above copyright notice,
 +
    this list of conditions and the following disclaimer in the documentation
 +
    and/or other materials provided with the distribution.
 +
  - Neither the name of the Mumble Developers nor the names of its
 +
    contributors may be used to endorse or promote products derived from this
 +
    software without specific prior written permission.
 +
 
 +
  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 +
  ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 +
  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 +
  A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
 +
  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 +
  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 +
  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 +
  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 +
  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 +
  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 +
  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 +
*/
 +
 
 +
#include <stdio.h>
 +
#include <stdlib.h>
 +
#include <windows.h>
 +
#include <tlhelp32.h>
 +
#include <math.h>
 +
 
 +
#include "../mumble_plugin.h"
 +
 
 +
HANDLE h;
 +
 
 +
BYTE *posptr;
 +
BYTE *faceptr;
 +
BYTE *topptr;
 +
 
 +
static DWORD getProcess(const wchar_t *exename) {
 +
PROCESSENTRY32 pe;
 +
DWORD pid = 0;
 +
 
 +
pe.dwSize = sizeof(pe);
 +
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
 +
if (hSnap != INVALID_HANDLE_VALUE) {
 +
BOOL ok = Process32First(hSnap, &pe);
 +
 
 +
while (ok) {
 +
if (wcscmp(pe.szExeFile, exename)==0) {
 +
pid = pe.th32ProcessID;
 +
break;
 +
}
 +
ok = Process32Next(hSnap, &pe);
 +
}
 +
CloseHandle(hSnap);
 +
}
 +
return pid;
 +
}
 +
 
 +
static BYTE *getModuleAddr(DWORD pid, const wchar_t *modname) {
 +
MODULEENTRY32 me;
 +
BYTE *addr = NULL;
 +
me.dwSize = sizeof(me);
 +
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
 +
if (hSnap != INVALID_HANDLE_VALUE) {
 +
BOOL ok = Module32First(hSnap, &me);
 +
 
 +
while (ok) {
 +
if (wcscmp(me.szModule, modname)==0) {
 +
addr = me.modBaseAddr;
 +
break;
 +
}
 +
ok = Module32Next(hSnap, &me);
 +
}
 +
CloseHandle(hSnap);
 +
}
 +
return addr;
 +
}
 +
 
 +
 
 +
static bool peekProc(VOID *base, VOID *dest, SIZE_T len) {
 +
SIZE_T r;
 +
BOOL ok=ReadProcessMemory(h, base, dest, len, &r);
 +
return (ok && (r == len));
 +
}
 +
 
 +
static DWORD peekProc(VOID *base) {
 +
DWORD v = 0;
 +
peekProc(base, reinterpret_cast<BYTE *>(&v), sizeof(DWORD));
 +
return v;
 +
}
 +
 
 +
static BYTE *peekProcPtr(VOID *base) {
 +
DWORD v = peekProc(base);
 +
return reinterpret_cast<BYTE *>(v);
 +
}
 +
 
 +
static void about(HWND h) {
 +
::MessageBox(h, L"Reads audio position information from <game>", L"Mumble <game acronym> Plugin", MB_OK);
 +
}
 +
 
 +
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]=0.0f;
 +
 
 +
char state;
 +
bool ok;
 +
 
 +
 
 +
/*
 +
description of your state value
 +
*/
 +
ok = peekProc((BYTE *) 0x00A1D0A8, &state, 1); // Magical state value
 +
if (! ok)
 +
return false;
 +
 
 +
if (state == 0)
 +
return true; // This results in all vectors beeing zero which tells Mumble to ignore them.
 +
 
 +
ok = peekProc(posptr, avatar_pos, 12) &&
 +
    peekProc(faceptr, avatar_front, 12) &&
 +
    peekProc(topptr, avatar_top, 12);
 +
 
 +
if (! ok)
 +
return false;
 +
 
 +
for (int i=0;i<3;i++) {
 +
camera_pos[i] = avatar_pos[i];
 +
camera_front[i] = avatar_front[i];
 +
camera_top[i] = avatar_top[i];
 +
}
 +
 
 +
return ok;
 +
}
 +
 
 +
static int trylock() {
 +
h = NULL;
 +
posptr = faceptr = topptr = NULL;
 +
 
 +
DWORD pid=getProcess(L"<game executable name>.exe");
 +
if (!pid)
 +
return false;
 +
BYTE *mod=getModuleAddr(pid, L"<module name, if you need it>.dll");
 +
if (!mod)
 +
return false;
 +
 
 +
h=OpenProcess(PROCESS_VM_READ, false, pid);
 +
if (!h)
 +
return false;
 +
 
 +
posptr = mod + 0x<offset>);
 +
faceptr = mod + 0x<offset>);
 +
topptr = mod + 0x<offset>);
 +
 
 +
float apos[3], afront[3], atop[3], cpos[3], cfront[3], ctop[3];
 +
std::string context;
 +
std::wstring identity;
 +
 
 +
if (fetch(apos, afront, atop, cpos, cfront, ctop, context, identity))
 +
return true;
 +
 
 +
CloseHandle(h);
 +
h = NULL;
 +
return false;
 +
}
 +
 
 +
static void unlock() {
 +
if (h) {
 +
CloseHandle(h);
 +
h = NULL;
 +
}
 +
return;
 +
}
 +
 
 +
static const std::wstring longdesc() {
 +
return std::wstring(L"Supports <game name> <version>. No identity support yet.");
 +
}
 +
 
 +
static std::wstring description(L"<game name> <version>");
 +
static std::wstring shortname(L"<game name>");
 +
 
 +
static MumblePlugin <game acronym, lowercase>plug = {
 +
MUMBLE_PLUGIN_MAGIC,
 +
description,
 +
shortname,
 +
about,
 +
NULL,
 +
trylock,
 +
unlock,
 +
longdesc,
 +
fetch
 +
};
 +
 
 +
extern "C" __declspec(dllexport) MumblePlugin *getMumblePlugin() {
 +
return &<game acronym, lowercase>plug;
 +
}
 +
</code>

Revision as of 21:13, 21 January 2010

Introduction

Game positional audio is a feature of Mumble that many users consider very useful. However, creating the game plugin can sometimes be complicated, and for the average person, daunting. This guide will help you understand how a game plugin works, what it does, and how you can make one.

First Steps

Tools Needed

Cheat Engine. Just use the default options when installing. Note that Mumble does NOT support cheating of any kind. We use Cheat Engine because the interface is easy to use, and the program is fits our purposes; Cheat Engine is simply a memory searching tool, which is required to find the positional addresses in the game.

Visual C++ 2008 Express edition. Again, default options, except for the SQL server, which you can uncheck.

Notepad++. After you install Notepad++, start it, go to Preferences -> New Document/Default Directory, and check "Unix" in the Format box.

Learn a Little C++

Although you do not need to be an expert programmer in order to write a plugin, you do need to understand fundamental data types. Here are a few of the most important:

float: This is the data type that almost all positional audio game addresses use. They are 32 bit, decimal numbers stored in the memory. A float data type is 4 bytes * 8 = 32 bits. An example of a floating point value would be "1234.0123456".

byte: This is the smallest data type in Intel x86-based computing. This type of memory address holds 1 byte of information (1 byte * 8 = 8 bits). From this type of memory address, you can get 0-255 base^10 values, or -127 to 128, depending on whether or not you use a signed byte (it has a + or - on the front of the value), or an unsigned byte (no + or -). An example of a byte value would be "12".

How a Plugin Works

Below is a standard template that you can use for your plugin making.

/* <your copyright here>

  Copyright (C) 2005-2010, Thorvald Natvig <thorvald@natvig.com>
  All rights reserved.
  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions
  are met:
  - Redistributions of source code must retain the above copyright notice,
    this list of conditions and the following disclaimer.
  - Redistributions in binary form must reproduce the above copyright notice,
    this list of conditions and the following disclaimer in the documentation
    and/or other materials provided with the distribution.
  - Neither the name of the Mumble Developers nor the names of its
    contributors may be used to endorse or promote products derived from this
    software without specific prior written permission.
  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  ``AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  • /
  1. include <stdio.h>
  2. include <stdlib.h>
  3. include <windows.h>
  4. include <tlhelp32.h>
  5. include <math.h>
  1. include "../mumble_plugin.h"

HANDLE h;

BYTE *posptr; BYTE *faceptr; BYTE *topptr;

static DWORD getProcess(const wchar_t *exename) { PROCESSENTRY32 pe; DWORD pid = 0;

pe.dwSize = sizeof(pe); HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hSnap != INVALID_HANDLE_VALUE) { BOOL ok = Process32First(hSnap, &pe);

while (ok) { if (wcscmp(pe.szExeFile, exename)==0) { pid = pe.th32ProcessID; break; } ok = Process32Next(hSnap, &pe); } CloseHandle(hSnap); } return pid; }

static BYTE *getModuleAddr(DWORD pid, const wchar_t *modname) { MODULEENTRY32 me; BYTE *addr = NULL; me.dwSize = sizeof(me); HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid); if (hSnap != INVALID_HANDLE_VALUE) { BOOL ok = Module32First(hSnap, &me);

while (ok) { if (wcscmp(me.szModule, modname)==0) { addr = me.modBaseAddr; break; } ok = Module32Next(hSnap, &me); } CloseHandle(hSnap); } return addr; }


static bool peekProc(VOID *base, VOID *dest, SIZE_T len) { SIZE_T r; BOOL ok=ReadProcessMemory(h, base, dest, len, &r); return (ok && (r == len)); }

static DWORD peekProc(VOID *base) { DWORD v = 0; peekProc(base, reinterpret_cast<BYTE *>(&v), sizeof(DWORD)); return v; }

static BYTE *peekProcPtr(VOID *base) { DWORD v = peekProc(base); return reinterpret_cast<BYTE *>(v); }

static void about(HWND h) { ::MessageBox(h, L"Reads audio position information from <game>", L"Mumble <game acronym> Plugin", MB_OK); }

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]=0.0f;

char state; bool ok;


/* description of your state value */ ok = peekProc((BYTE *) 0x00A1D0A8, &state, 1); // Magical state value if (! ok) return false;

if (state == 0) return true; // This results in all vectors beeing zero which tells Mumble to ignore them.

ok = peekProc(posptr, avatar_pos, 12) && peekProc(faceptr, avatar_front, 12) && peekProc(topptr, avatar_top, 12);

if (! ok) return false;

for (int i=0;i<3;i++) { camera_pos[i] = avatar_pos[i]; camera_front[i] = avatar_front[i]; camera_top[i] = avatar_top[i]; }

return ok; }

static int trylock() { h = NULL; posptr = faceptr = topptr = NULL;

DWORD pid=getProcess(L"<game executable name>.exe"); if (!pid) return false; BYTE *mod=getModuleAddr(pid, L"<module name, if you need it>.dll"); if (!mod) return false;

h=OpenProcess(PROCESS_VM_READ, false, pid); if (!h) return false;

posptr = mod + 0x<offset>); faceptr = mod + 0x<offset>); topptr = mod + 0x<offset>);

float apos[3], afront[3], atop[3], cpos[3], cfront[3], ctop[3]; std::string context; std::wstring identity;

if (fetch(apos, afront, atop, cpos, cfront, ctop, context, identity)) return true;

CloseHandle(h); h = NULL; return false; }

static void unlock() { if (h) { CloseHandle(h); h = NULL; } return; }

static const std::wstring longdesc() { return std::wstring(L"Supports <game name> <version>. No identity support yet."); }

static std::wstring description(L"<game name> <version>"); static std::wstring shortname(L"<game name>");

static MumblePlugin <game acronym, lowercase>plug = { MUMBLE_PLUGIN_MAGIC, description, shortname, about, NULL, trylock, unlock, longdesc, fetch };

extern "C" __declspec(dllexport) MumblePlugin *getMumblePlugin() { return &<game acronym, lowercase>plug; }