This module aims to give more power to the amxmodx coder by giving facilities to use functions that are not possible to use without it (what includes functions from other metamod plugins). For windows and linux. From the version 2.1 uploaded 29/01/2010 it also supports memory direct access / patch.
The concept is the following:
The module handles the conversion between types in C++ and pawn and makes you able to use a function if you can provide it a way to have it's address in memory and tell him the types of data that are involved with it. It includes a generic way of working with C++ structs.
For example: let's say you want to use this function:
Code:
void PM_Move( struct playermove_s *ppmove, qboolean server)
It doesn't return a value, and it receives an argument of type "playermove_s *" and another one of type "qboolean".
As long as the types it needs are implemented in the module, you can use it. If one isn't implemented you can try to use it directly and still do operations with the function, particularly hooking it, since when you hook a function you don't necessarily care about all the arguments.
The missing thing then is a way of finding it in memory so that the module know where it is. There ways of doing that are:
- By knowing its name. In libraries compiled in linux, by default, the functions' symbolic names are attached to them making it easier to find them. Unfortunately the same doesn't happen when libraries are compiled for windows, making it useless for windows unless you have the source for the library and compile it yourself so that it attaches the symbolic names for the functions. I opted to do that for monstermod and i'm shipping a compiled library for windows and for linux that makes it easy and uniform to use it.
- By providing a set of bytes in memory unique to that function. This is a technique called "signature scanning". The technique is talked about the first time in these forums here and its expanded on here and here. This technique basically allows you to refer to any function in memory as long as you can find it. To understand it you have to read more about the subject in the links refered above.
- By providing their address upon having it in a plugin. If you can find the address of a function programatically you can use it. The module uses this to provide access to core functions of the engine. Those functions are provided by amxmodx without the module but by hooking them with this module you can intercept them being called in more situations (like when they are called by other metamod plugins)
Added in 2.1
- By providing its offset in the library. This is for fast testing and shouldn't be relied upon since it can easily break if it faces an update of the library.
- By providing its index in the virtual function table of the class. This let's you use many functions by providing only an index. It was copied from hamsandwich to work in the orpheu style. It brings the possibility of hooking virtual function of entities and objects like CGameRules. What it brings new is that it isnt limited to an hardcoded set of functions and let you use functions mod dependent that couldn't be hooked before.
To make the module aware of a function you have to create a file formatted with the notation
JSON and put it in the folder "configs/orpheu/functions". The files format is like the following:
For the function:
PHP Code:
CMBaseMonster* spawn_monster(int monster_type, Vector& origin, float angle, int respawn_index)
File would be:
Code:
{
"name" : "spawn_monster",
"library" : "monstermod",
"info" : "Spawns a monstermod monster in the world",
"arguments" :
[
{
"type" : "int",
"info" : "The type of the monster"
},
{
"type" : "Vector &",
"info" : "The position where the monster will be placed at"
},
{
"type" : "float",
"info" : "Angle of the monster"
},
{
"type" : "int",
"info" : "Respawn index. Related to the order of a monster respawning if failed to spawn at the first try"
}
],
"return" :
{
"type" : "CMBaseMonster *",
"info" : "The monster created"
},
"identifiers":
[
{
"os" : "windows",
"value" : "?spawn_monster@@YAPAVCMBaseMonster@@HAAVVector@@MH@Z"
},
{
"os" : "linux",
"value" : "_Z13spawn_monsteriR6Vectorfi"
}
]
}
- "Info" fields are optional.
- The field "name" must match the filename.
- The library name is "mod" for mods like cstrike, and "engine" for the engine dll. For metamod libraries you need to create a file like those you can find at "configs\orpheu\libraries" that contain a pair libraryname/libraryCvar, so the module recognize the module by a cvar of theirs.
- The identifiers are an array of groups "os"/"value" to identify the function. In the case that the library is "mod" you need also to provide an extra field called "mod". It should look like:
. The purpose of this field is documentation but it is mandatory.
In this case the method used to refer to the function address is by giving it's name. In the case of a signature, the field value would be an array of bytes or "*" or "?" like
Code:
"value" : [0x1,"*","?"]
"*" is meant to be used when you don't care about the value of that byte.
For example:
"value" : [0x1,"*"]
matches
[0x1,0x0] , [0x1,0x1] , ... [0x1,0xFF]
"?" is meant to be used when you don't care about the value of the byte and it can even not exist like:
"value" : [0x1,"?"]
matches the same as above plus [0x1]
JSON is an object notation widely used and to make sure your file is correctly formatted you can use a
validator.
For this project I slightly modified a library to parse JSON to read bytes. That means that when you want to validate a file, do not include a signature.
A special case of functions are those that belong to a class like:
PHP Code:
void CMController :: HandleAnimEvent( MonsterEvent_t *pEvent )
In this case you need to create a folder named "CMController" and create a file name "HandleAnimEvent" inside it. This is mandatory and has the purpose of a better organization. You will also have to provide in the file a field named "class". The file would look as:
Code:
{
"name" : "HandleAnimEvent",
"class" : "CMController",
"library" : "monstermod",
"arguments" :
[
{
"type" : "MonsterEvent_t *"
}
],
"identifiers":
[
{
"os" : "windows",
"value" : "?HandleAnimEvent@CMController@@UAEXPAUMonsterEvent_t@@@Z"
},
{
"os" : "linux",
"value" : "_ZN12CMController15HandleAnimEventEP14MonsterEvent_t"
}
]
}
The other method of using a function deals with having its address programatically. Take a look at
http://metamod.org/sdk/dox/eiface_8h-source.html - line 100.
You can find there the representation af a struct called "enginefuncs_s". That struct contains addresses of engine functions. The module provides a stock to use them.
Let's say you want to hook the function contained in the struct at
PHP Code:
void (*pfnServerPrint)( const char *szMsg );
You would create a file like
Code:
{
"name" : "ServerPrint",
"library" : "engine",
"arguments" :
[
{
"type" : "char *"
}
]
}
No need for identifiers since the address will be find at run time, programatically. In a plugin you would have:
PHP Code:
new OrpheuFunction:ServerPrint = OrpheuGetEngineFunction("pfnServerPrint","ServerPrint")
And could then call it or hook it. By hooking that function you can intercept console messages like those that occur when you issued the server command "
metamod".
This is a special (hardcode) case of the method of using a function when you have its address in a plugin. The other special case is
OrpheuGetDLLFunction to retrieve functions from the struct
DLL_FUNCTIONS
http://metamod.org/sdk/dox/eiface_8h-source.html - line 384.
For generic cases you would use the native:
PHP Code:
native OrpheuFunction:OrpheuCreateFunction(address,libFunctionName[],classname[]="")
The module let's you handle structs in a generic way. Let's say we want to hook the function
PM_Move that can be found in the struct
DLL_FUNCTIONS.
Its file:
Code:
{
"name" : "PM_Move",
"library" : "mod",
"arguments" :
[
{
"type" : "playermove_s *"
},
{
"type" : "qboolean"
}
]
}
Hooking it:
PHP Code:
public plugin_init()
{
new OrpheuFunction:PM_Move = OrpheuGetDLLFunction("pfnPM_Move","PM_Move")
OrpheuRegisterHook(PM_Move,"OnPM_Move")
}
public OnPM_Move(ppmove,server)
{
}
The first argument of the function is a struct. You can see it here
http://metamod.org/sdk/dox/pm__defs_8h-source.html - line 92
Dealing with the struct data:
PHP Code:
public OnPM_Move(ppmove,server)
{
new id = OrpheuGetParamStructMember(1,"player_index") + 1
new Float:friction = OrpheuGetParamStructMember(1,"friction")
new sztexturename[20]
OrpheuGetParamStructMember(1,"sztexturename",sztexturename,charsmax(sztexturename))
server_print("ID %d friction %f texturename %s",id,friction,sztexturename)
}
More information on dealing with structs in the includes.
The same struct contains addresses for other functions. Here is an example of how to hook one of them:
Code:
{
"name" : "PM_PlaySound",
"library" : "engine",
"arguments" :
[
{
"type" : "int"
},
{
"type" : "char *"
},
{
"type" : "float"
},
{
"type" : "float"
},
{
"type" : "int"
},
{
"type" : "int"
}
]
}
PHP Code:
new OrpheuHook:PM_PlaySoundHook
public plugin_init()
{
new OrpheuFunction:PM_Move = OrpheuGetDLLFunction("pfnPM_Move","PM_Move")
OrpheuRegisterHook(PM_Move,"OnPM_Move")
OrpheuRegisterHook(PM_Move,"OnPM_MovePost",OrpheuHookPost)
}
public OnPM_Move(ppmove,server)
{
// Retrieves the address of the function to hook
new PM_PlaySoundAddress = OrpheuGetParamStructMember(1,"PM_PlaySound")
// Creates the function in the module
new OrpheuFunction:PM_PlaySound = OrpheuCreateFunction(PM_PlaySoundAddress,"PM_PlaySound")
// Hooks it
PM_PlaySoundHook = OrpheuRegisterHook(PM_PlaySound,"OnPM_PlaySoundHook")
}
public OnPM_PlaySoundHook(channel,sample[],Float:volume,Float:attenuation,fFlags,pitch)
{
server_print("Sample %s",sample)
}
public OnPM_MovePost(ppmove,server)
{
OrpheuUnregisterHook(PM_PlaySoundHook)
}
More info on the includes.
List of supported data types:
Code:
"bool"
"byte"
"long"
"CBaseEntity *"
"char *"
"edict_s *"
"float"
"Vector *"
"CMBaseMonster *"
"char"
"short"
"entvars_s *"
Structures:
Code:
"movevars_s *"
"usercmd_s *"
"MonsterEvent_t *"
"DLL_FUNCTIONS *"
"playermove_s *"
"enginefuncs_t *"
"TraceResult *"
"physent_s *"
"pmplane_s *"
"pmtrace_s *"
"weapon_data_s *"
"AmmoInfo *"
"ItemInfo *"
"Task_t *"
"Schedule_t *"
I will provide more pratical examples later.
If the module doesn't run in your linux server, you must have libstdc++ installed.
Edit for Orpheu 2.1 - updated 29/01/2010
In the version 2.1 support for virtual functions (based on hamsandwich) and for memory retrieval / patching (based on mem hack) was added.
Virtual functions:
Virtual functions are functions that represent a concept shared among different classes (like Spawn) and implemented differently accordingly.
The way they are implemented by compiler permits to located them in memory by providing a simple numeric offset because references to them are kept in a table that each object of a class carries with it.
This is the table for the mod Counter Strike retrieved from it's linux binary of the class CHalfLifeMultiplay that extends CGameRules:
(Each line contains a symbolic name that represents a function)
PHP Code:
01 - Think__18CHalfLifeMultiplay
02 - IsAllowedToSpawn__18CHalfLifeMultiplayP11CBaseEntity
03 - FAllowFlashlight__18CHalfLifeMultiplay
04 - FShouldSwitchWeapon__18CHalfLifeMultiplayP11CBasePlayerP15CBasePlayerItem
05 - GetNextBestWeapon__18CHalfLifeMultiplayP11CBasePlayerP15CBasePlayerItem
06 - IsMultiplayer__18CHalfLifeMultiplay
07 - IsDeathmatch__18CHalfLifeMultiplay
08 - IsTeamplay__10CGameRules
09 - IsCoOp__18CHalfLifeMultiplay
10 - GetGameDescription__10CGameRules
11 - ClientConnected__18CHalfLifeMultiplayP7edict_sPCcT2Pc
12 - InitHUD__18CHalfLifeMultiplayP11CBasePlayer
13 - ClientDisconnected__18CHalfLifeMultiplayP7edict_s
14 - UpdateGameMode__18CHalfLifeMultiplayP11CBasePlayer
15 - FlPlayerFallDamage__18CHalfLifeMultiplayP11CBasePlayer
16 - FPlayerCanTakeDamage__18CHalfLifeMultiplayP11CBasePlayerP11CBaseEntity
17 - ShouldAutoAim__10CGameRulesP11CBasePlayerP7edict_s
18 - PlayerSpawn__18CHalfLifeMultiplayP11CBasePlayer
19 - PlayerThink__18CHalfLifeMultiplayP11CBasePlayer
20 - FPlayerCanRespawn__18CHalfLifeMultiplayP11CBasePlayer
21 - FlPlayerSpawnTime__18CHalfLifeMultiplayP11CBasePlayer
22 - GetPlayerSpawnSpot__18CHalfLifeMultiplayP11CBasePlayer
23 - AllowAutoTargetCrosshair__18CHalfLifeMultiplay
24 - ClientCommand_DeadOrAlive__18CHalfLifeMultiplayP11CBasePlayerPCc
25 - ClientCommand__18CHalfLifeMultiplayP11CBasePlayerPCc
26 - ClientUserInfoChanged__18CHalfLifeMultiplayP11CBasePlayerPc
27 - IPointsForKill__18CHalfLifeMultiplayP11CBasePlayerT1
28 - PlayerKilled__18CHalfLifeMultiplayP11CBasePlayerP9entvars_sT2
29 - DeathNotice__18CHalfLifeMultiplayP11CBasePlayerP9entvars_sT2
30 - CanHavePlayerItem__18CHalfLifeMultiplayP11CBasePlayerP15CBasePlayerItem
31 - PlayerGotWeapon__18CHalfLifeMultiplayP11CBasePlayerP15CBasePlayerItem
32 - WeaponShouldRespawn__18CHalfLifeMultiplayP15CBasePlayerItem
33 - FlWeaponRespawnTime__18CHalfLifeMultiplayP15CBasePlayerItem
34 - FlWeaponTryRespawn__18CHalfLifeMultiplayP15CBasePlayerItem
35 - VecWeaponRespawnSpot__18CHalfLifeMultiplayP15CBasePlayerItem
36 - CanHaveItem__18CHalfLifeMultiplayP11CBasePlayerP5CItem
37 - PlayerGotItem__18CHalfLifeMultiplayP11CBasePlayerP5CItem
38 - ItemShouldRespawn__18CHalfLifeMultiplayP5CItem
39 - FlItemRespawnTime__18CHalfLifeMultiplayP5CItem
40 - VecItemRespawnSpot__18CHalfLifeMultiplayP5CItem
41 - CanHaveAmmo__10CGameRulesP11CBasePlayerPCci
42 - PlayerGotAmmo__18CHalfLifeMultiplayP11CBasePlayerPci
43 - AmmoShouldRespawn__18CHalfLifeMultiplayP15CBasePlayerAmmo
44 - FlAmmoRespawnTime__18CHalfLifeMultiplayP15CBasePlayerAmmo
45 - VecAmmoRespawnSpot__18CHalfLifeMultiplayP15CBasePlayerAmmo
46 - FlHealthChargerRechargeTime__18CHalfLifeMultiplay
47 - FlHEVChargerRechargeTime__18CHalfLifeMultiplay
48 - DeadPlayerWeapons__18CHalfLifeMultiplayP11CBasePlayer
49 - DeadPlayerAmmo__18CHalfLifeMultiplayP11CBasePlayer
50 - GetTeamID__18CHalfLifeMultiplayP11CBaseEntity
51 - PlayerRelationship__18CHalfLifeMultiplayP11CBasePlayerP11CBaseEntity
52 - GetTeamIndex__10CGameRulesPCc
53 - GetIndexedTeamName__10CGameRulesi
54 - IsValidTeam__10CGameRulesPCc
55 - ChangePlayerTeam__10CGameRulesP11CBasePlayerPCcii
56 - SetDefaultPlayerTeam__10CGameRulesP11CBasePlayer
57 - PlayTextureSounds__18CHalfLifeMultiplay
58 - FAllowMonsters__18CHalfLifeMultiplay
59 - EndMultiplayerGame__18CHalfLifeMultiplay
60 - IsFreezePeriod__10CGameRules
61 - ServerDeactivate__18CHalfLifeMultiplay
62 - CheckMapConditions__18CHalfLifeMultiplay
63 - CleanUpMap__18CHalfLifeMultiplay
64 - RestartRound__18CHalfLifeMultiplay
65 - CheckWinConditions__18CHalfLifeMultiplay
66 - RemoveGuns__18CHalfLifeMultiplay
67 - GiveC4__18CHalfLifeMultiplay
68 - ChangeLevel__18CHalfLifeMultiplay
69 - GoToIntermission__18CHalfLifeMultiplay
To use one of them you need to create a file that should look like:
Code:
{
"name" : "GetNextBestWeapon",
"class" : "CGameRules",
"library" : "mod",
"arguments" :
[
{
"type" : "CBasePlayer *"
},
{
"type" : "CBasePlayerItem *"
}
],
"return" :
{
"type" : "bool"
},
"indexes" :
[
{
"os" : "windows",
"mod" : "cstrike",
"value" : 5
},
{
"os" : "linux",
"mod" : "cstrike",
"value" : 6
}
]
}
(In linux the virtual table includes an extra function at the beginning so the offsets should be +1 for it)
That function description should be placed at the folder "virtualFunctions" in a folder named "CGameRules" and in a file named "GetNextBestWeapon".
So that you can use those functions in a plugin some natives are provided like:
PHP Code:
OrpheuGetFunctionFromClass(entityClassName[],libFunctionName[],libClassName[])
OrpheuGetFunctionFromEntity(id,libFunctionName[],libClassName[])
OrpheuGetFunctionFromObject(object,libFunctionName[],libClassName[])
OrpheuGetFunctionFromMonster(id,libFunctionName[],libClassName[])
The first two work like the natives from hamsandwich:
- The first retrieves a function based on an entity class
- The second retrieves a function based on an entity
To use the functions from CHalfLifeMultiplay described above, we need OrpheuGetFunctionFromObject. This native retrieves a function based on an object of the class you want so we need an extra step to have the object.
For this particular case this is one way of doing it:
PHP Code:
{
"name" : "InstallGameRules",
"library" : "mod",
"return" :
{
"type" : "CHalfLifeMultiplay *"
},
"identifiers":
[
{
"os" : "windows",
"mod" : "cstrike",
"value" : [0x68,0x8c,0xea,"*",0xa,0xff,0x15,0xdc,0x23,"*",0xa,0x83,0xc4,0x4,0xff,0x15,0xe0,0x23,"*",0xa,0xa1,0xb8,0x25]
},
{
"os" : "linux",
"mod" : "cstrike",
"value" : "InstallGameRules__Fv"
}
]
}
PHP Code:
public plugin_precache()
{
new OrpheuFunction:InstallGameRules = OrpheuGetFunction("InstallGameRules")
OrpheuRegisterHook(InstallGameRules,"OnInstallGameRules",OrpheuHookPost)
}
public OnInstallGameRules()
{
new obj = OrpheuGetReturn()
new OrpheuFunction:GetNextBestWeapon = OrpheuGetFunctionFromObject(obj,"GetNextBestWeapon","CGameRules")
OrpheuRegisterHook(GetNextBestWeapon,"OnGetNextBestWeapon")
}
public OnGetNextBestWeapon(obj,id,weaponID)
{
//
}
Memory handling
The module has now support for retrieving or setting values directly in any portion of the memory where a library lies in. Examples of what can be done are: changing weapons cost, strings replacement that you can use for example to change text that appears in the console.
It can be done by using offsets to refer to where the memory you want to mess with is located or with signatures. As for the functions you need to create a file. This file can contain one or several descriptions of how/where is the memory you want to address.
Example for changing the awp cost:
PHP Code:
[
{
"name" : "awpCost",
"library" : "mod",
"type" : "long",
"memoryType" : "data",
"identifiers" :
[
{
"os" : "windows",
"mod" : "cstrike",
"value" : [0x8E,0x12,0x00,0x00,0x7D,0x00,0x00,0x00,0x0A,0x00,0x00,0x00,0x0A,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x07]
}
]
}
]
PHP Code:
OrpheuMemorySet("awpCost",1,1000)
This is just an example and for windows and cstrike but the module can be used for both windows and linux and for any mod.
More information in the includes.
As note: if you want to replace strings that are directly placed in memory (most are) you should use the type "string" instead of "char *".
Credits
Arkshine, jim_yang, xpaw, speed, the links of "signature scanning" provided above, hamsandwich
http://jsoncpp.sourceforge.net/
http://www.boost.org/
http://forums.alliedmods.net/showpos...47&postcount=2
Original version by Joaquimandrade (v2.3a): https://github.com/Arkshine/Orpheu/tree/master/legacy
- orpheu-source-2.3a.zip : project source code.
- orpheu-files-2.3a.zip : binaries and configuration file.
Latest version by Arkshine (v2.6.3): https://github.com/Arkshine/Orpheu/releases
- orpheu-files-2.6.x.zip : binaries and configuration file. Required if using HLDS version after february update.
Installation- Just unzip the content of orpheu-files-xx.zip in your /amxmodx directory. That's all.