Доступ к готовым решениям

Переход в группу "Пользователь"

300.00 ₽
Одноразовый платёж
Быстрый переход в группу "Пользователи", без надобности написания постов и ожидания.

Покупка дает возможность:
Быть полноправным участником форума
Нормальное копирование кода
Создавать темы
Скачивать файлы
Доступ к архиву Pawno-Info

Урок Как вызывать или перехватывать нативные функции

ne 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;
}
И да, как не сложно понять, в памяти будут зарегистрированы только SetPlayerPos и SetPlayerFacingAngle. И именно из-за этого, этот вариант нам не подходит, так как все те функции, с которыми вы хотите взаимодействовать, нужно будет определить в скрипте, и неважно, будут ли они использоваться во время его работы или нет.

Но нам на помощь приходит функция 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;
}
В итоге мы получим имена и адреса всех нативных функций из сампа (core) и подключённых плагинов.
Итог вывода в консоль на 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
На Linux будет та же самая ситуация, но адреса будут отличаться.


Поскольку мы имеем имена функций и их адреса, нам ничего не мешает их сохранить. Давайте попробуем сделать это на примере 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
Как вы, наверное, уже знаете, AMX_NATIVE - это
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;
}
То есть, в params[0] хранится кол-во переданных параметров, а во всех последующих ячейках значения этих параметров.

Давайте создадим свою версию 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;
}
И, как не странно, это работает, мы появляемся в небе по среди карты. Возможно вы задались вопросом: "А какой смысл писать свою реализацию SetPlayerPos?", в том то и дело, что его здесь нет. Это пример, относительно которого вы сможете из своего плагина вызывать SetPlayerPos в любой момент. Но самое классное, как по мне, в этом, это то, что функции из плагина вызываются не по имени, а по адресу, и вызов таких функций будет, в среднем, от 10 раз быстрее, чем из скрипта. Смысла в этом, конечно, немного, но основную задачу мы выполнили, а полный код будет выглядеть примерно вот так:

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
2. Перехват нативных функций

Перехват же нативных функций не сложнее, чем их вызов. Нужно перехватить тот же адрес, с помощью которого вызывается нативная функция. Пример, опять же, будет показан на 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
 
Последнее редактирование:
Сверху Снизу