ne oukibt
Новичок
Приветствую всех.
Те, кто имел опыт в разработке плагинов, возможно задавался вопросом: "Как вызывать или перехватывать нативные функции".
Как раз в этом уроке это и будет рассмотрено. И не имеет значения, является ли нативная функция стандартной функцией сампа или другого плагина.
Прежде всего нужно уточнить, что в плагинах, таких, как например, streamer, используется метод из sampgdk, а именно InvokeNative, который вызывает функцию по имени. Так почему же не использовать его?
В том то и проблема, что вызов происходит по имени, а это ещё медленнее, чем вызывать нативную функцию из AMX, хотя там тоже используется похожая система.
1. Вызов нативных функций:
Наверное, не для кого не секрет, что место для названий функций и public-функций (public / callback), а также их адресов, выделяется динамически, и туда попадают только те функции, которые зарегистрированы в AMX, то есть те, которые используются в скрипте. Предположим, что мы имеем следующий скрипт:
И да, как не сложно понять, в памяти будут зарегистрированы только SetPlayerPos и SetPlayerFacingAngle. И именно из-за этого, этот вариант нам не подходит, так как все те функции, с которыми вы хотите взаимодействовать, нужно будет определить в скрипте, и неважно, будут ли они использоваться во время его работы или нет.
Но нам на помощь приходит функция amx_Register, с помощью которой и регистрируются эти самые нативные функции. И как она нам поможет?
С помощью неё объявляются все нативные функции, и не имеет значения, используются ли они в AMX или нет.
Для того чтобы нам узнать адрес нужной функции, следует перехватить amx_Register и получить из него данные.
Это можно сделать примерно следующим способом:
В итоге мы получим имена и адреса всех нативных функций из сампа (core) и подключённых плагинов.
Итог вывода в консоль на windows без подключённых плагинов:
На Linux будет та же самая ситуация, но адреса будут отличаться.
Поскольку мы имеем имена функций и их адреса, нам ничего не мешает их сохранить. Давайте попробуем сделать это на примере SetPlayerPos.
Мой код выглядит примерно следующим образом:
Как вы, наверное, уже знаете, AMX_NATIVE - это
Следовательно, функция вызывается примерно вот так:
Данные в params заполняются таким же образом, как и при срабатывании вашей функции, которая была вызвана из скрипта, аля:
То есть, в params[0] хранится кол-во переданных параметров, а во всех последующих ячейках значения этих параметров.
Давайте создадим свою версию SetPlayerPos, но при этом, немного изменим её, а именно, координату Z, добавим к ней 100
И, как не странно, это работает, мы появляемся в небе по среди карты. Возможно вы задались вопросом: "А какой смысл писать свою реализацию SetPlayerPos?", в том то и дело, что его здесь нет. Это пример, относительно которого вы сможете из своего плагина вызывать SetPlayerPos в любой момент. Но самое классное, как по мне, в этом, это то, что функции из плагина вызываются не по имени, а по адресу, и вызов таких функций будет, в среднем, от 10 раз быстрее, чем из скрипта. Смысла в этом, конечно, немного, но основную задачу мы выполнили, а полный код будет выглядеть примерно вот так:
2. Перехват нативных функций
Перехват же нативных функций не сложнее, чем их вызов. Нужно перехватить тот же адрес, с помощью которого вызывается нативная функция. Пример, опять же, будет показан на SetPlayerPos:
Когда-то в скором времени статья будет дополнена.
Автор: oukibt
Те, кто имел опыт в разработке плагинов, возможно задавался вопросом: "Как вызывать или перехватывать нативные функции".
Как раз в этом уроке это и будет рассмотрено. И не имеет значения, является ли нативная функция стандартной функцией сампа или другого плагина.
Прежде всего нужно уточнить, что в плагинах, таких, как например, streamer, используется метод из sampgdk, а именно InvokeNative, который вызывает функцию по имени. Так почему же не использовать его?
В том то и проблема, что вызов происходит по имени, а это ещё медленнее, чем вызывать нативную функцию из AMX, хотя там тоже используется похожая система.
1. Вызов нативных функций:
Наверное, не для кого не секрет, что место для названий функций и public-функций (public / callback), а также их адресов, выделяется динамически, и туда попадают только те функции, которые зарегистрированы в AMX, то есть те, которые используются в скрипте. Предположим, что мы имеем следующий скрипт:
PHP:
#include <a_samp>
main() {}
public OnPlayerSpawn(playerid)
{
SetPlayerPos(playerid, 0.0, 0.0, 4.0);
SetPlayerFacingAngle(playerid, 35.0);
return 1;
}
Но нам на помощь приходит функция amx_Register, с помощью которой и регистрируются эти самые нативные функции. И как она нам поможет?
С помощью неё объявляются все нативные функции, и не имеет значения, используются ли они в AMX или нет.
Для того чтобы нам узнать адрес нужной функции, следует перехватить amx_Register и получить из него данные.
Я настоятельно для этого рекомендую использовать библиотеку urmem, которая, в отличии от того же subhook, корректно работает на разных ОС, а также написана в одном файле, что обеспечивает больше удобства при работе с ней.
C++:
#include "SDK/amx/amx.h"
#include "SDK/plugincommon.h"
#ifdef WIN32
#define CDECL __cdecl
#else
#define CDECL
#endif
#include "urmem.hpp"
urmem::hook Hook_amxRegister;
using logprintf_t = void* (*)(const char* fmt, ...);
logprintf_t logprintf = nullptr;
extern void* pAMXFunctions;
int CDECL amx_RegisterHook(AMX* amx, AMX_NATIVE_INFO* nativelist, int number)
{
Hook_amxRegister.disable();
int i = 0;
if (number == -1)
{
while (nativelist->func)
{
if (nativelist[i].func == nullptr) break;
logprintf("%s = 0x%06x", nativelist[i].name, (AMX_NATIVE*)nativelist[i].func);
i++;
}
}
else
{
for (i = 0; i < number; i++)
{
logprintf("%s = 0x%06x", nativelist[i].name, (AMX_NATIVE*)nativelist[i].func);
}
}
int retval = reinterpret_cast<int(*)(AMX*, AMX_NATIVE_INFO*, int)>(reinterpret_cast<void**>(pAMXFunctions)[PLUGIN_AMX_EXPORT_Register])(amx, nativelist, number);
Hook_amxRegister.enable();
return retval;
}
PLUGIN_EXPORT bool PLUGIN_CALL Load(void** ppData)
{
pAMXFunctions = ppData[PLUGIN_DATA_AMX_EXPORTS];
logprintf = reinterpret_cast<logprintf_t>(ppData[PLUGIN_DATA_LOGPRINTF]);
if (pAMXFunctions == NULL || logprintf == NULL) return false;
Hook_amxRegister.install((urmem::address_t)reinterpret_cast<void**>(pAMXFunctions)[PLUGIN_AMX_EXPORT_Register], urmem::get_func_addr(&amx_RegisterHook));
return true;
}
Итог вывода в консоль на windows без подключённых плагинов:
Код:
----------
Loaded log file: "server_log.txt".
----------
SA-MP Dedicated Server
----------------------
v0.3.7-R2, (C)2005-2015 SA-MP Team
Server Plugins
--------------
Loading plugin: test
Loaded.
Loaded 1 plugins.
Started server on port: 7777, with maxplayers: 50 lanmode is OFF.
Filterscripts
---------------
Loaded 0 filterscripts.
numargs = 0x4045b0
getarg = 0x4045d0
setarg = 0x404600
heapspace = 0x404650
funcidx = 0x404660
swapchars = 0x4046f0
tolower = 0x404730
toupper = 0x404740
min = 0x404750
max = 0x404770
clamp = 0x404790
random = 0x404ac0
getproperty = 0x404810
setproperty = 0x404910
deleteproperty = 0x404a00
existproperty = 0x404a70
float = 0x405be0
floatstr = 0x405bf0
floatmul = 0x405c60
floatdiv = 0x405c80
floatadd = 0x405ca0
floatsub = 0x405cc0
floatfract = 0x405ce0
floatround = 0x405d10
floatcmp = 0x405d70
floatsqroot = 0x405db0
floatpower = 0x405de0
floatlog = 0x405e00
floatsin = 0x405eb0
floatcos = 0x405ed0
floattan = 0x405ef0
floatabs = 0x405f10
ispacked = 0x406c60
memcpy = 0x406f70
strcat = 0x4063a0
strcmp = 0x406540
strdel = 0x406850
strfind = 0x4065e0
strins = 0x406930
strlen = 0x4061f0
strmid = 0x4066c0
strpack = 0x406230
strunpack = 0x4062f0
strval = 0x406b00
uudecode = 0x406e10
uuencode = 0x406ea0
valstr = 0x406bc0
fopen = 0x4055a0
fclose = 0x4056c0
fwrite = 0x4056e0
fread = 0x405770
fputchar = 0x405830
fgetchar = 0x405880
fblockwrite = 0x4058d0
fblockread = 0x405950
ftemp = 0x4059d0
fseek = 0x4059e0
fexist = 0x405a40
flength = 0x405b80
fremove = 0x405ae0
gettime = 0x407060
settime = 0x407010
getdate = 0x407150
setdate = 0x407100
tickcount = 0x4071f0
settimer = 0x407240
gpci = 0x470fe0
GetPlayerPoolSize = 0x471030
GetVehiclePoolSize = 0x471050
GetActorPoolSize = 0x471070
print = 0x472c70
printf = 0x472cf0
format = 0x472d20
SetTimer = 0x472d70
KillTimer = 0x472df0
GetTickCount = 0x472e10
GetMaxPlayers = 0x472e20
SetTimerEx = 0x473860
LimitGlobalChatRadius = 0x472e40
LimitPlayerMarkerRadius = 0x472e60
SetWeather = 0x473b00
SetPlayerWeather = 0x47c9f0
CallRemoteFunction = 0x474000
CallLocalFunction = 0x4743b0
VectorSize = 0x473b20
asin = 0x473b50
acos = 0x473b70
atan2 = 0x473bb0
atan = 0x473b90
SHA256_PassHash = 0x473bd0
SetSVarInt = 0x478760
SetSVarString = 0x4787f0
SetSVarFloat = 0x4788d0
GetSVarInt = 0x478960
GetSVarString = 0x478a60
GetSVarFloat = 0x4789e0
DeleteSVar = 0x478b00
GetSVarType = 0x478b80
GetSVarNameAtIndex = 0x478c00
GetSVarsUpperIndex = 0x478c50
GameModeExit = 0x46fae0
SetGameModeText = 0x46fb20
SetTeamCount = 0x407fb0
AddPlayerClass = 0x46fba0
AddPlayerClassEx = 0x46fc10
AddStaticVehicle = 0x46fc80
AddStaticVehicleEx = 0x46fdd0
AddStaticPickup = 0x4703f0
CreatePickup = 0x470450
DestroyPickup = 0x4704b0
SetPlayerWorldBounds = 0x479900
ShowNameTags = 0x473040
ShowPlayerMarkers = 0x473060
SetWorldTime = 0x472b30
GetWeaponName = 0x471380
EnableTirePopping = 0x473ad0
EnableVehicleFriendlyFire = 0x473af0
AllowInteriorWeapons = 0x473080
SetGravity = 0x473cd0
GetGravity = 0x474710
AllowAdminTeleport = 0x473f20
SetDeathDropAmount = 0x4746f0
CreateExplosion = 0x47ec20
CreateExplosionForPlayer = 0x47ed20
SetDisabledWeapons = 0x475a70
UsePlayerPedAnims = 0x4730a0
DisableInteriorEnterExits = 0x4773d0
SetNameTagDrawDistance = 0x4773e0
DisableNameTagLOS = 0x477400
ConnectNPC = 0x477ab0
IsPlayerNPC = 0x477d00
Create3DTextLabel = 0x4775e0
Delete3DTextLabel = 0x4777b0
Attach3DTextLabelToPlayer = 0x477860
Attach3DTextLabelToVehicle = 0x4778b0
Update3DTextLabelText = 0x477900
CreatePlayer3DTextLabel = 0x4776a0
DeletePlayer3DTextLabel = 0x4777f0
UpdatePlayer3DTextLabelText = 0x4779b0
ShowPlayerDialog = 0x480040
EnableZoneNames = 0x475a90
GangZoneCreate = 0x476d80
GangZoneDestroy = 0x476dc0
GangZoneShowForPlayer = 0x476e00
GangZoneShowForAll = 0x476e70
GangZoneHideForPlayer = 0x476eb0
GangZoneHideForAll = 0x476f10
GangZoneFlashForPlayer = 0x476f50
GangZoneFlashForAll = 0x476fc0
GangZoneStopFlashForPlayer = 0x477000
GangZoneStopFlashForAll = 0x477060
IsPlayerAdmin = 0x4707d0
Kick = 0x4704d0
Ban = 0x4704f0
BanEx = 0x4705a0
SendRconCommand = 0x473930
GetServerVarAsString = 0x4770a0
GetServerVarAsInt = 0x477130
GetServerVarAsBool = 0x4771a0
GetConsoleVarAsString = 0x4770a0
GetConsoleVarAsInt = 0x477130
GetConsoleVarAsBool = 0x4771a0
GetPlayerNetworkStats = 0x473190
GetNetworkStats = 0x473750
GetPlayerVersion = 0x470f90
BlockIpAddress = 0x4706b0
UnBlockIpAddress = 0x470740
GetServerTickRate = 0x4732b0
NetStats_GetConnectedTime = 0x4732c0
NetStats_MessagesReceived = 0x473330
NetStats_BytesReceived = 0x4733b0
NetStats_MessagesSent = 0x473440
NetStats_BytesSent = 0x4734b0
NetStats_MessagesRecvPerSecond = 0x473530
NetStats_PacketLossPercent = 0x473590
NetStats_ConnectionStatus = 0x473670
NetStats_GetIpPort = 0x4736d0
SetSpawnInfo = 0x470810
SpawnPlayer = 0x479a50
SetPlayerTeam = 0x47a340
GetPlayerTeam = 0x470a30
SetPlayerName = 0x479b60
SetPlayerSkin = 0x479d80
GetPlayerSkin = 0x470a80
GetPlayerPos = 0x470bc0
SetPlayerPos = 0x47a470
SetPlayerPosFindZ = 0x47a5a0
GetPlayerLastShotVectors = 0x470c70
GetPlayerHealth = 0x470d80
SetPlayerHealth = 0x47a6d0
SetPlayerColor = 0x4718b0
GetPlayerColor = 0x4718f0
GetPlayerVehicleID = 0x471920
GetPlayerVehicleSeat = 0x471a10
GetPlayerSurfingVehicleID = 0x471960
GetPlayerSurfingObjectID = 0x4719b0
PutPlayerInVehicle = 0x47a7c0
RemovePlayerFromVehicle = 0x470de0
IsPlayerInVehicle = 0x470eb0
IsPlayerInAnyVehicle = 0x470f00
GetPlayerName = 0x470f40
SetPlayerCheckpoint = 0x471a60
DisablePlayerCheckpoint = 0x471ab0
IsPlayerInCheckpoint = 0x471af0
SetPlayerRaceCheckpoint = 0x471b20
DisablePlayerRaceCheckpoint = 0x471b80
IsPlayerInRaceCheckpoint = 0x471bc0
SetPlayerInterior = 0x47b140
GetPlayerInterior = 0x471bf0
SetPlayerAttachedObject = 0x478e10
RemovePlayerAttachedObject = 0x478f10
SetPlayerAttachedObject = 0x478e10
IsPlayerAttachedObjectSlotUsed = 0x478f70
EditAttachedObject = 0x480520
SendClientCheck = 0x479150
SetPlayerCameraLookAt = 0x47b420
SetPlayerCameraPos = 0x47b300
SetCameraBehindPlayer = 0x471fc0
GetPlayerCameraMode = 0x472080
GetPlayerCameraAspectRatio = 0x4720d0
GetPlayerCameraZoom = 0x472140
GetPlayerCameraPos = 0x471c70
GetPlayerCameraFrontVector = 0x471d40
EnablePlayerCameraTarget = 0x471e10
GetPlayerCameraTargetObject = 0x471e80
GetPlayerCameraTargetVehicle = 0x471ed0
GetPlayerCameraTargetPlayer = 0x471f20
GetPlayerCameraTargetActor = 0x471f70
AttachCameraToObject = 0x47b560
AttachCameraToPlayerObject = 0x47b660
InterpolateCameraPos = 0x47b770
InterpolateCameraLookAt = 0x47b940
TogglePlayerControllable = 0x47bb10
PlayerPlaySound = 0x47c330
SetPlayerScore = 0x4726b0
GetPlayerScore = 0x4726f0
SetPlayerFacingAngle = 0x47bf60
GetPlayerFacingAngle = 0x472a10
GivePlayerMoney = 0x47bd50
GetPlayerMoney = 0x472720
ResetPlayerMoney = 0x472750
IsPlayerConnected = 0x4729b0
GetPlayerState = 0x4729e0
ResetPlayerWeapons = 0x472a70
GivePlayerWeapon = 0x47c040
SetPlayerArmedWeapon = 0x47c130
GetPlayerIp = 0x4730b0
GetPlayerPing = 0x473120
GetPlayerWeapon = 0x473820
SetPlayerArmour = 0x47c570
GetPlayerArmour = 0x4739b0
SetPlayerMapIcon = 0x47c770
RemovePlayerMapIcon = 0x47c900
GetPlayerKeys = 0x473a10
SetPlayerMarkerForPlayer = 0x47c650
GetPlayerAmmo = 0x472830
SetPlayerAmmo = 0x47be60
GetPlayerTargetPlayer = 0x472890
GetPlayerTargetActor = 0x4728d0
GetPlayerWeaponData = 0x472910
GetPlayerWeaponState = 0x478c60
AllowPlayerTeleport = 0x473f00
ForceClassSelection = 0x470970
SetPlayerWantedLevel = 0x47f020
GetPlayerWantedLevel = 0x475ac0
GetPlayerFightingStyle = 0x475af0
SetPlayerFightingStyle = 0x47f110
GetPlayerVelocity = 0x477410
SetPlayerVelocity = 0x47f480
IsPlayerInRangeOfPoint = 0x471090
GetPlayerDistanceFromPoint = 0x471130
IsPlayerStreamedIn = 0x471230
IsVehicleStreamedIn = 0x4712a0
IsActorStreamedIn = 0x471310
SetPlayerVirtualWorld = 0x470ac0
GetPlayerVirtualWorld = 0x470b00
ShowPlayerNameTagForPlayer = 0x47c450
EnableStuntBonusForAll = 0x477220
EnableStuntBonusForPlayer = 0x4772e0
TogglePlayerSpectating = 0x479ee0
GetPlayerDrunkLevel = 0x4775a0
SetPlayerDrunkLevel = 0x47f830
PlayerSpectateVehicle = 0x479ff0
PlayerSpectatePlayer = 0x47a190
ApplyAnimation = 0x47cad0
ClearAnimations = 0x47cde0
GetPlayerAnimationIndex = 0x473dc0
GetAnimationName = 0x473e20
SetPlayerSpecialAction = 0x47b220
GetPlayerSpecialAction = 0x471c20
StartRecordingPlayerData = 0x471660
StopRecordingPlayerData = 0x471710
PlayCrimeReportForPlayer = 0x47fb20
PlayAudioStreamForPlayer = 0x47fe40
StopAudioStreamForPlayer = 0x477d50
RemoveBuildingForPlayer = 0x4803c0
SetPlayerShopName = 0x477e20
SetPlayerSkillLevel = 0x477f70
SetPlayerChatBubble = 0x47f920
SelectTextDraw = 0x47f260
CancelSelectTextDraw = 0x47f370
DisableRemoteVehicleCollisions = 0x4791c0
SetPVarInt = 0x477fc0
SetPVarString = 0x478090
SetPVarFloat = 0x4781c0
GetPVarInt = 0x478290
GetPVarString = 0x478420
GetPVarFloat = 0x478350
DeletePVar = 0x478500
GetPVarType = 0x4785c0
GetPVarNameAtIndex = 0x478680
GetPVarsUpperIndex = 0x478700
CreatePlayerTextDraw = 0x476260
PlayerTextDrawSetString = 0x476350
PlayerTextDrawLetterSize = 0x476450
PlayerTextDrawTextSize = 0x4764d0
PlayerTextDrawAlignment = 0x476550
PlayerTextDrawColor = 0x4765d0
PlayerTextDrawUseBox = 0x476650
PlayerTextDrawBoxColor = 0x4766e0
PlayerTextDrawSetShadow = 0x476760
PlayerTextDrawSetOutline = 0x4767e0
PlayerTextDrawBackgroundColor = 0x476860
PlayerTextDrawFont = 0x4768e0
PlayerTextDrawSetProportional = 0x476960
PlayerTextDrawSetSelectable = 0x4769f0
PlayerTextDrawSetPreviewModel = 0x476a70
PlayerTextDrawSetPreviewRot = 0x476af0
PlayerTextDrawSetPreviewVehCol = 0x476ba0
PlayerTextDrawShow = 0x476c30
PlayerTextDrawHide = 0x476ca0
PlayerTextDrawDestroy = 0x476d10
IsValidVehicle = 0x473d30
CreateVehicle = 0x4713c0
DestroyVehicle = 0x471490
GetVehiclePos = 0x4714d0
SetVehiclePos = 0x471560
GetVehicleZAngle = 0x472e80
GetVehicleRotationQuat = 0x472f70
GetVehicleDistanceFromPoint = 0x4711b0
SetVehicleZAngle = 0x47c210
SetVehicleParamsForPlayer = 0x47bc10
ManualVehicleEngineAndLights = 0x4721b0
SetVehicleParamsEx = 0x4721d0
GetVehicleParamsEx = 0x472290
SetVehicleParamsCarDoors = 0x4723b0
GetVehicleParamsCarDoors = 0x472460
SetVehicleParamsCarWindows = 0x472530
GetVehicleParamsCarWindows = 0x4725e0
SetVehicleToRespawn = 0x46ff60
GetVehicleModelInfo = 0x46ffa0
RepairVehicle = 0x470030
AddVehicleComponent = 0x470070
RemoveVehicleComponent = 0x4700b0
ChangeVehicleColor = 0x4700f0
ChangeVehiclePaintjob = 0x470140
LinkVehicleToInterior = 0x4797f0
SetVehicleHealth = 0x473cf0
GetVehicleHealth = 0x473d60
AttachTrailerToVehicle = 0x47cee0
DetachTrailerFromVehicle = 0x47d010
IsTrailerAttachedToVehicle = 0x473f40
GetVehicleTrailer = 0x473fa0
SetVehicleNumberPlate = 0x470180
GetVehicleModel = 0x4702a0
GetVehicleParamsSirenState = 0x4702f0
GetVehicleComponentInSlot = 0x470350
GetVehicleComponentType = 0x4703c0
GetVehicleVelocity = 0x477510
SetVehicleVelocity = 0x47f590
SetVehicleAngularVelocity = 0x47f6e0
GetVehicleDamageStatus = 0x478cc0
UpdateVehicleDamageStatus = 0x478da0
SetVehicleVirtualWorld = 0x470b40
GetVehicleVirtualWorld = 0x470b80
SendClientMessage = 0x471750
SendClientMessageToAll = 0x471820
SendDeathMessage = 0x47ac90
SendDeathMessageToPlayer = 0x47ad70
GameTextForAll = 0x47ae50
GameTextForPlayer = 0x47afb0
SendPlayerMessageToPlayer = 0x47a9b0
SendPlayerMessageToAll = 0x47ab30
TextDrawCreate = 0x475b50
TextDrawSetString = 0x475bf0
TextDrawLetterSize = 0x475cb0
TextDrawTextSize = 0x475cf0
TextDrawAlignment = 0x475d30
TextDrawColor = 0x475d70
TextDrawUseBox = 0x475db0
TextDrawBoxColor = 0x475e00
TextDrawSetShadow = 0x475e40
TextDrawSetOutline = 0x475e80
TextDrawBackgroundColor = 0x475ec0
TextDrawFont = 0x475f00
TextDrawSetProportional = 0x475f40
TextDrawSetSelectable = 0x475f90
TextDrawSetPreviewModel = 0x475fd0
TextDrawSetPreviewRot = 0x476010
TextDrawSetPreviewVehCol = 0x476090
TextDrawShowForPlayer = 0x4760e0
TextDrawShowForAll = 0x4761a0
TextDrawHideForPlayer = 0x476140
TextDrawHideForAll = 0x4761e0
TextDrawDestroy = 0x476220
CreateObject = 0x474720
AttachObjectToVehicle = 0x474800
AttachPlayerObjectToVehicle = 0x4748d0
AttachObjectToObject = 0x474980
SetObjectPos = 0x47d0f0
SetObjectRot = 0x47d250
GetObjectPos = 0x474e40
GetObjectRot = 0x474ed0
GetObjectModel = 0x474f70
SetObjectNoCameraCol = 0x474fb0
IsValidObject = 0x475040
DestroyObject = 0x47d3a0
IsObjectMoving = 0x475080
MoveObject = 0x47d4b0
StopObject = 0x47d780
EditObject = 0x47d890
EditPlayerObject = 0x47d9d0
SelectObject = 0x4750c0
CancelEdit = 0x4751b0
SetObjectMaterial = 0x474a60
SetObjectMaterialText = 0x474c60
CreatePlayerObject = 0x475290
SetPlayerObjectPos = 0x47db10
SetPlayerObjectRot = 0x47dcd0
GetPlayerObjectPos = 0x475360
GetPlayerObjectRot = 0x475410
GetPlayerObjectModel = 0x4754e0
SetPlayerObjectNoCameraCol = 0x47dea0
IsValidPlayerObject = 0x475540
DestroyPlayerObject = 0x47dfc0
IsPlayerObjectMoving = 0x4755b0
MovePlayerObject = 0x47e100
StopPlayerObject = 0x47e440
SetPlayerObjectMaterial = 0x47e570
SetPlayerObjectMaterialText = 0x47e8c0
AttachObjectToPlayer = 0x47ee30
AttachPlayerObjectToPlayer = 0x475ab0
SetObjectsDefaultCameraCol = 0x475620
CreateActor = 0x4792a0
DestroyActor = 0x4792e0
SetActorPos = 0x480640
GetActorPos = 0x479320
SetActorVirtualWorld = 0x479430
GetActorVirtualWorld = 0x479470
ApplyActorAnimation = 0x4794b0
ClearActorAnimations = 0x479610
SetActorFacingAngle = 0x480830
GetActorFacingAngle = 0x4793d0
GetActorHealth = 0x479670
SetActorHealth = 0x4796d0
IsValidActor = 0x479710
SetActorInvulnerable = 0x479750
IsActorInvulnerable = 0x479790
CreateMenu = 0x475650
DestroyMenu = 0x475700
AddMenuItem = 0x475730
SetMenuColumnHeader = 0x4757f0
ShowMenuForPlayer = 0x4758a0
HideMenuForPlayer = 0x475910
IsValidMenu = 0x475980
DisableMenu = 0x4759b0
DisableMenuRow = 0x4759f0
GetPlayerMenu = 0x475a30
SetPlayerTime = 0x472b50
TogglePlayerClock = 0x472c30
GetPlayerTime = 0x472b90
HTTP = 0x478fd0
db_open = 0x4817e0
db_close = 0x481920
db_query = 0x481980
db_free_result = 0x481b10
db_num_rows = 0x480fa0
db_next_row = 0x480fe0
db_num_fields = 0x481030
db_field_name = 0x481070
db_get_field = 0x481110
db_get_field_int = 0x4811c0
db_get_field_float = 0x481230
db_get_field_assoc = 0x4812b0
db_get_field_assoc_int = 0x481440
db_get_field_assoc_float = 0x481590
db_debug_openfiles = 0x4816f0
db_debug_openresults = 0x481700
db_get_mem_handle = 0x481710
db_get_result_mem_handle = 0x481730
Поскольку мы имеем имена функций и их адреса, нам ничего не мешает их сохранить. Давайте попробуем сделать это на примере SetPlayerPos.
Мой код выглядит примерно следующим образом:
C++:
#ifndef TEST_H
#define TEST_H
#include "SDK/amx/amx.h"
#include "SDK/plugincommon.h"
#define PLUGIN_NAME "test"
#ifdef WIN32
#define CDECL __cdecl
#else
#define CDECL
#endif
#include "urmem.hpp"
urmem::hook Hook_amxRegister;
using logprintf_t = void* (*)(const char* fmt, ...);
logprintf_t logprintf = nullptr;
extern void* pAMXFunctions;
AMX_NATIVE amxf_SetPlayerPos = nullptr;
int CDECL amx_RegisterHook(AMX* amx, AMX_NATIVE_INFO* nativelist, int number)
{
Hook_amxRegister.disable();
int i = 0;
if (number == -1)
{
while (nativelist->func)
{
if (nativelist[i].func == nullptr) break;
if (!std::strcmp(nativelist[i].name, "SetPlayerPos"))
{
amxf_SetPlayerPos = (AMX_NATIVE)nativelist[i].func;
logprintf("SetPlayerPos hooked");
}
i++;
}
}
else
{
for (i = 0; i < number; i++)
{
if (!std::strcmp(nativelist[i].name, "SetPlayerPos"))
{
amxf_SetPlayerPos = (AMX_NATIVE)nativelist[i].func;
logprintf("SetPlayerPos hooked");
}
}
}
int retval = reinterpret_cast<int(*)(AMX*, AMX_NATIVE_INFO*, int)>(reinterpret_cast<void**>(pAMXFunctions)[PLUGIN_AMX_EXPORT_Register])(amx, nativelist, number);
Hook_amxRegister.enable();
return retval;
}
PLUGIN_EXPORT bool PLUGIN_CALL Load(void** ppData)
{
pAMXFunctions = ppData[PLUGIN_DATA_AMX_EXPORTS];
logprintf = reinterpret_cast<logprintf_t>(ppData[PLUGIN_DATA_LOGPRINTF]);
if (pAMXFunctions == NULL || logprintf == NULL) return false;
Hook_amxRegister.install((urmem::address_t)reinterpret_cast<void**>(pAMXFunctions)[PLUGIN_AMX_EXPORT_Register], urmem::get_func_addr(&amx_RegisterHook));
return true;
}
PLUGIN_EXPORT unsigned int PLUGIN_CALL Supports()
{
return SUPPORTS_AMX_NATIVES | SUPPORTS_VERSION;
}
PLUGIN_EXPORT void PLUGIN_CALL Unload()
{
logprintf(" %s plugin was unloaded", PLUGIN_NAME);
}
PLUGIN_EXPORT int PLUGIN_CALL AmxLoad(AMX* amx)
{
return 1;
}
PLUGIN_EXPORT int PLUGIN_CALL AmxUnload(AMX* amx)
{
return AMX_ERR_NONE;
}
#endif
typedef cell (AMX_NATIVE_CALL *AMX_NATIVE)(struct tagAMX *amx, cell *params);Следовательно, функция вызывается примерно вот так:
amxf_SetPlayerPos(amx, params);Данные в params заполняются таким же образом, как и при срабатывании вашей функции, которая была вызвана из скрипта, аля:
C++:
cell AMX_NATIVE_CALL MyFunc(AMX* amx, cell* params)
{
return 1;
}
Давайте создадим свою версию SetPlayerPos, но при этом, немного изменим её, а именно, координату Z, добавим к ней 100
PHP:
#include <a_samp>
main() {}
native MySetPlayerPos(playerid, Float:X, Float:Y, Float:Z);
public OnPlayerSpawn(playerid)
{
MySetPlayerPos(playerid, 0.0, 0.0, 4.0);
return 1;
}
C++:
inline cell SetPlayerPos(AMX* amx, int playerid, float X, float Y, float Z)
{
cell params[5];
params[0] = 4 * sizeof(cell);
params[1] = playerid;
params[2] = amx_ftoc(X);
params[3] = amx_ftoc(Y);
params[4] = amx_ftoc(Z);
return amxf_SetPlayerPos(amx, params);
}
cell AMX_NATIVE_CALL MySetPlayerPos(AMX* amx, cell* params)
{
if (params[0] != 4 * sizeof(cell)) logprintf("Error [MySetPlayerPos]: invalid params count");
int playerid = params[1];
float X = amx_ctof(params[2]),
Y = amx_ctof(params[3]),
Z = amx_ctof(params[4]);
SetPlayerPos(amx, playerid, X, Y, Z + 100.0f);
return 1;
}
PLUGIN_EXPORT int PLUGIN_CALL AmxLoad(AMX* amx)
{
AMX_NATIVE_INFO natives[] = {
{ "MySetPlayerPos", MySetPlayerPos },
{ NULL, NULL }
};
amx_Register(amx, natives, -1);
return 1;
}
C++:
#ifndef TEST_H
#define TEST_H
#include "SDK/amx/amx.h"
#include "SDK/plugincommon.h"
#define PLUGIN_NAME "test"
#ifdef WIN32
#define CDECL __cdecl
#else
#define CDECL
#endif
#include "urmem.hpp"
urmem::hook Hook_amxRegister;
using logprintf_t = void* (*)(const char* fmt, ...);
logprintf_t logprintf = nullptr;
extern void* pAMXFunctions;
AMX_NATIVE amxf_SetPlayerPos = nullptr;
int CDECL amx_RegisterHook(AMX* amx, AMX_NATIVE_INFO* nativelist, int number)
{
Hook_amxRegister.disable();
int i = 0;
if (number == -1)
{
while (nativelist->func)
{
if (nativelist[i].func == nullptr) break;
if (!std::strcmp(nativelist[i].name, "SetPlayerPos"))
{
amxf_SetPlayerPos = (AMX_NATIVE)nativelist[i].func;
logprintf("SetPlayerPos hooked");
}
i++;
}
}
else
{
for (i = 0; i < number; i++)
{
if (!std::strcmp(nativelist[i].name, "SetPlayerPos"))
{
amxf_SetPlayerPos = (AMX_NATIVE)nativelist[i].func;
logprintf("SetPlayerPos hooked");
}
}
}
int retval = reinterpret_cast<int(*)(AMX*, AMX_NATIVE_INFO*, int)>(reinterpret_cast<void**>(pAMXFunctions)[PLUGIN_AMX_EXPORT_Register])(amx, nativelist, number);
Hook_amxRegister.enable();
return retval;
}
inline cell SetPlayerPos(AMX* amx, int playerid, float X, float Y, float Z)
{
cell params[5];
params[0] = 4 * sizeof(cell);
params[1] = playerid;
params[2] = amx_ftoc(X);
params[3] = amx_ftoc(Y);
params[4] = amx_ftoc(Z);
return amxf_SetPlayerPos(amx, params);
}
cell AMX_NATIVE_CALL MySetPlayerPos(AMX* amx, cell* params)
{
if (params[0] != 4 * sizeof(cell)) logprintf("Error [MySetPlayerPos]: invalid params count");
int playerid = params[1];
float X = amx_ctof(params[2]),
Y = amx_ctof(params[3]),
Z = amx_ctof(params[4]);
SetPlayerPos(amx, playerid, X, Y, Z + 100.0f);
return 1;
}
PLUGIN_EXPORT int PLUGIN_CALL AmxLoad(AMX* amx)
{
AMX_NATIVE_INFO natives[] = {
{ "MySetPlayerPos", MySetPlayerPos },
{ NULL, NULL }
};
amx_Register(amx, natives, -1);
return 1;
}
PLUGIN_EXPORT bool PLUGIN_CALL Load(void** ppData)
{
pAMXFunctions = ppData[PLUGIN_DATA_AMX_EXPORTS];
logprintf = reinterpret_cast<logprintf_t>(ppData[PLUGIN_DATA_LOGPRINTF]);
if (pAMXFunctions == NULL || logprintf == NULL) return false;
Hook_amxRegister.install((urmem::address_t)reinterpret_cast<void**>(pAMXFunctions)[PLUGIN_AMX_EXPORT_Register], urmem::get_func_addr(&amx_RegisterHook));
return true;
}
PLUGIN_EXPORT unsigned int PLUGIN_CALL Supports()
{
return SUPPORTS_AMX_NATIVES | SUPPORTS_VERSION;
}
PLUGIN_EXPORT void PLUGIN_CALL Unload()
{
logprintf(" %s plugin was unloaded", PLUGIN_NAME);
}
PLUGIN_EXPORT int PLUGIN_CALL AmxUnload(AMX* amx)
{
return AMX_ERR_NONE;
}
#endif
Перехват же нативных функций не сложнее, чем их вызов. Нужно перехватить тот же адрес, с помощью которого вызывается нативная функция. Пример, опять же, будет показан на SetPlayerPos:
C++:
#ifndef TEST_H
#define TEST_H
#include "SDK/amx/amx.h"
#include "SDK/plugincommon.h"
#define PLUGIN_NAME "test"
#ifdef WIN32
#define CDECL __cdecl
#else
#define CDECL
#endif
#include "urmem.hpp"
urmem::hook Hook_amxRegister;
urmem::hook Hook_amxfSetPlayerPos;
using logprintf_t = void* (*)(const char* fmt, ...);
logprintf_t logprintf = nullptr;
extern void* pAMXFunctions;
AMX_NATIVE amxf_SetPlayerPos = nullptr;
cell AMX_NATIVE_CALL SetPlayerPos_Hook(AMX* amx, cell* params)
{
Hook_amxfSetPlayerPos.disable();
logprintf("Hello from SetPlayerPos");
cell retval = amxf_SetPlayerPos(amx, params);
Hook_amxfSetPlayerPos.enable();
return retval;
}
int CDECL amx_RegisterHook(AMX* amx, AMX_NATIVE_INFO* nativelist, int number)
{
Hook_amxRegister.disable();
int i = 0;
if (number == -1)
{
while (nativelist->func)
{
if (nativelist[i].func == nullptr) break;
if (!std::strcmp(nativelist[i].name, "SetPlayerPos"))
{
amxf_SetPlayerPos = (AMX_NATIVE)nativelist[i].func;
Hook_amxfSetPlayerPos.install((urmem::address_t)amxf_SetPlayerPos, (urmem::address_t)SetPlayerPos_Hook);
}
i++;
}
}
else
{
for (i = 0; i < number; i++)
{
if (!std::strcmp(nativelist[i].name, "SetPlayerPos"))
{
amxf_SetPlayerPos = (AMX_NATIVE)nativelist[i].func;
Hook_amxfSetPlayerPos.install((urmem::address_t)amxf_SetPlayerPos, (urmem::address_t)SetPlayerPos_Hook);
}
}
}
int retval = reinterpret_cast<int(*)(AMX*, AMX_NATIVE_INFO*, int)>(reinterpret_cast<void**>(pAMXFunctions)[PLUGIN_AMX_EXPORT_Register])(amx, nativelist, number);
Hook_amxRegister.enable();
return retval;
}
PLUGIN_EXPORT bool PLUGIN_CALL Load(void** ppData)
{
pAMXFunctions = ppData[PLUGIN_DATA_AMX_EXPORTS];
logprintf = reinterpret_cast<logprintf_t>(ppData[PLUGIN_DATA_LOGPRINTF]);
if (pAMXFunctions == NULL || logprintf == NULL) return false;
Hook_amxRegister.install((urmem::address_t)reinterpret_cast<void**>(pAMXFunctions)[PLUGIN_AMX_EXPORT_Register], urmem::get_func_addr(&amx_RegisterHook));
return true;
}
PLUGIN_EXPORT int PLUGIN_CALL AmxLoad(AMX* amx)
{
return 1;
}
PLUGIN_EXPORT unsigned int PLUGIN_CALL Supports()
{
return SUPPORTS_AMX_NATIVES | SUPPORTS_VERSION;
}
PLUGIN_EXPORT void PLUGIN_CALL Unload()
{
logprintf(" %s plugin was unloaded", PLUGIN_NAME);
}
PLUGIN_EXPORT int PLUGIN_CALL AmxUnload(AMX* amx)
{
return AMX_ERR_NONE;
}
#endif
Автор: oukibt
Последнее редактирование:
