Difference between revisions of "Pluginguide"

From Mumble Wiki
Jump to: navigation, search
m
Line 23: Line 23:
 
==How a Plugin Works==
 
==How a Plugin Works==
  
Below is a standard template that you can use for your plugin making.  
+
Below is a standard template that you can use for your plugin making. The code itself will be explained in the comments that follow.
  
<code>
+
<pre style="C++">
 
/* <your copyright here>
 
/* <your copyright here>
   Copyright (C) 2005-2010, Thorvald Natvig <thorvald@natvig.com>
+
   Copyright (C) 2005-2010, Thorvald Natvig <thorvald@natvig.com>  
  
 
   All rights reserved.
 
   All rights reserved.
 
+
 
   Redistribution and use in source and binary forms, with or without
 
   Redistribution and use in source and binary forms, with or without
 
   modification, are permitted provided that the following conditions
 
   modification, are permitted provided that the following conditions
   are met:
+
   are met:  
  
 
   - Redistributions of source code must retain the above copyright notice,
 
   - Redistributions of source code must retain the above copyright notice,
Line 55: Line 55:
 
   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 
   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 
   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+
*/  
 
+
 
#include <stdio.h>
 
#include <stdio.h>
 
#include <stdlib.h>
 
#include <stdlib.h>
 
#include <windows.h>
 
#include <windows.h>
 
#include <tlhelp32.h>
 
#include <tlhelp32.h>
#include <math.h>
+
#include <math.h>  
 
 
#include "../mumble_plugin.h"
 
  
HANDLE h;
+
#include "../mumble_plugin.h
  
 +
HANDLE h;
 +
 
BYTE *posptr;
 
BYTE *posptr;
 
BYTE *faceptr;
 
BYTE *faceptr;
Line 229: Line 229:
 
return &<game acronym, lowercase>plug;
 
return &<game acronym, lowercase>plug;
 
}
 
}
</code>
+
</pre>

Revision as of 21:30, 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. The code itself will be explained in the comments that follow.

/* <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;
}