Popular Post Tanga 131 Odesláno: 16. Srpen, 2017 Popular Post Share Odesláno: 16. Srpen, 2017 (upraveno) Hookovanie funkcií a callbackov Obsah Čo je to hookovanie Princíp hookovania a preprocesor _ALS_ - Advanced Library System Hookovanie funkcií Hookovanie callbackov Zhrnutie 1. Čo je to hookovanie Predstavte si gamemod v Pawn ako reťaz, kde každý krúžok predstavuje jednu funkciu (stock). Tieto krúžky sú pospájané, pretože jedna funkcia (stock) volá druhú a tá ďalšiu, atď.Dobrý, čitateľný kód je rozdelený do mnoho súborov, includov. Nanešťastie, každý callback môže byť definovaný iba raz, čo bráni efektívnemu rozdeleniu kódu.Tento problém rieši hookovanie, ktoré si možno predstaviť tak, že reťaz (gamemode) sa v niektorom krúžku rozdelí, pridá sa tam kód, ktorý chceme hooknuť a naspäť sa spojí, bez toho aby to akokoľvek ovplyvnilo zvyšok reťaze.To umožňuje použiť jeden callback aj viackrát v jednom súbore (prípadne v includoch). 2. Princíp hookovania a preprocesor Je treba povedať, že Pawn kompiler nefunguje (ani nemá prečo fungovať) tak ako Cčkový, alebo iný. Povedzme, že chceme upraviť (hooknuť) funkciu GivePlayerMoney() tak, že vždy keď je zavolaná, dá hráčovi dvojnásobok peňazí. Samozrejme bez toho, aby sme ovplyvnili nejaký už-existujúci kód. stock GivePlayerMoneyEx(playerid, money) { // definujeme vlastnu funkciu return GivePlayerMoney(playerid, money * 2); // v nej zavolame tu povodnu } #define GivePlayerMoney GivePlayerMoneyEx // predefinujeme tu povodnu Ďalšie použitie funkcie GivePlayerMoney() by už hráčovi dalo dvojnásobok peňazí.Štvrtý riadok spôsobí, že každé nasledujúce volanie GivePlayerMoney() preprocesor nahradí za GivePlayerMoneyEx(), teda za náš hook.Kompiler nehodí error/warning v prípade, že predefinujete už-existujúcu funkciu/callback. Na pochopenie princípu hookovania je treba vedieť ako funguje preprocesor Pawn.Stačí vedieť, že prebieha vo viacerých krokoch. V prvom kroku prebehne daný súbor, ktorý má spracovať. Skontroluje v ňom makrá a podmienky preprocesora a vyhodnotí ich.Súčasne si zapamätá ktoré funkcie (stocky, publicy) existujú, pridá si ich do medzipamäte a súbor prebehne znovu (opäť vyhodnotí makrá a podmienky preprocesora).Toto správanie podporí aj nasledujúci kód. Pre vaše dobro si každý z tých kódov skompilujte. #if defined A #error Funkcia je definovana #endif stock A() { } Kompiler hodí error kvôli tomu, že funkcia "A" je definovaná, hoc až za makrom.Zaujímavý je však tento kód: #if !defined A // vsimnite si zmenu, negaciu podmienky #error Funkcia nie je definovana #endif stock A() { } Hodí error rovnako, ako kód predtým. Ako je to možné? Predsa ide o znegovanú podmienku. Nečítajte ďalej, ak na to chcete prísť sami.Druhý kód prebieha tak, že preprocesor prejde ku "#if !defined". Tam samozrejme zastaví, pretože doteraz symbol "A" nikde nenašiel. Prečo teda prvý kód tiež narazil na error?V prvom kóde, preprocesor prebehne ku #if defined. "A" definované nie je a teda ide ďalej, ku koncu súboru, medzitým "A" nájde a uloží si ho do zoznamu známych symbolov. V ďalšej iterácii však preprocesor znova dôjde k #if defined, "A"čko už pozná a preto hodí error. Musím ešte povedať, že toto som zistil experimentami, popravde neviem ako funguje ten preprocesor. Ak máte nejaké objasnenie, môžete napísať. 3. _ALS_ - Advanced Library System Ak ste niekedy videli hookovací kód, všimli ste si, že oproti môjmu príkladu s GivePlayerMoney(), tam bolo niečo navyše.Správny kód by mal vyzerať takto: stock GivePlayerMoneyEx(playerid, money) { GivePlayerMoney(playerid, money * 2); } #if defined _ALS_GivePlayerMoney #undef GivePlayerMoney #else #define _ALS_GivePlayerMoney #endif #define GivePlayerMoney GivePlayerMoneyEx Toto slúži na detekciu, či už bola daná funkcia hooknutá. Ak áno, kód by nefungoval a tiež vyhodil warning 201: redefinition of constant/macro (symbol "GivePlayerMoney") Predefinovanie stocku/callbacku warning nehodí, predefinovanie makra/define áno.Kúzlo s "_ALS_" ošetruje práve to, aby bolo možné skombinovať viac hookov.Syntax "_ALS_" je akýsi neoficiálny štandard. Používajú to všetci a tým sa zaručí kompatibilita hookov od rôznych autorov. 4. Hookovanie funkcií Hookovanie funkcií už bolo ukázané, ale dám ešte jeden príklad. // ak je hrac vo vozidle, portne aj vozidlo stock SetPlayerPosEx(playerid, Float:x, Float:y, Float:z, bool:vehicleToo=false) { if (vehicleToo) { if (IsPlayerInAnyVehicle(playerid)) return PortWithVehicle(playerid, x, y, z); else return SetPlayerPos(playerid, x, y, z); } else return SetPlayerPos(playerid, x, y, z); } #if defined _ALS_SetPlayerPos #undef SetPlayerPos #else #define _ALS_SetPlayerPos #endif #define SetPlayerPos SetPlayerPosEx ... public OnPlayerCommandText(playerid, cmdtext[]) { if (!strcmp("/portme", cmdtext)) SetPlayerPos(playerid, 1, 2, 3, true); else if (!strcmp("/oldport", cmdtext)) SetPlayerPos(playerid, 234, 46, 324); } 5. Hookovanie callbackov Hookovanie callbackov prebieha trochu ináč než funkcií. Aj keď kód je skoro rovnaký, na pozadí ide v podstate o opačný proces než u funkcií. public OnPlayerGiveDamage(playerid, damagedid, Float:amount, weaponid, bodypart) { // pouzijeme povodny callback if (IsPlayerAdmin(damagedid)) { // pridame vlastnu funkcionalitu Kick(playerid); } // zavolame nasledujuci hook #if defined MyOnPlayerGiveDamage // moze sa stat, ze neexistuje viac hookov, preto podmienka return CallLocalFunction("MyOnPlayerGiveDamage", "iifii", playerid, damagedid, amount, weaponid, bodypart); #else return 0; // v callbackoch su dolezite spravne return hodnoty #endif } #if defined _ALS_OnPlayerGiveDamage #undef OnPlayerGiveDamage #else #define _ALS_OnPlayerGiveDamage #endif #define OnPlayerGiveDamage MyOnPlayerGiveDamage // premenovanie nasledujuceho callbacku #if defined MyOnPlayerGiveDamage // samozrejme forward forward MyOnPlayerGiveDamage(playerid, damagedid, Float:amount, weaponid, bodypart); #endif V skripte použijeme ten pôvodný callback a "predefinujeme" tie nasledujúce. Syntax "MyOnPlayerGiveDamage" už nie je "štandardizovaná", môžete použiť aj iný názov. 6. Zhrnutie Pri hookovaní v podstate nejde o ten kód, ako o to pochopiť čo robí preprocesor na pozadí. Ak ste si všetky kódy pozreli a porozmýšlali nad nimi, mali by ste tomu rozumieť.Či do toho "vidíte skrz na skrz" si môžete skúsiť odpovedaním na tieto dve bonusové otázky:1.) V prípade, že je 10 skriptov čo hookujú nejakú funkciu a môj skript ju hookol prvý, koľká v poradí sa vykoná funkcia z môjho skriptu?2.) V prípade, že je 10 skriptov čo hookujú nejaký callback a môj skript ho hookol prvý, koľký v poradí sa vykoná callback z môjho skriptu?Ak máte otázky/vylepšenia, pokojne sem s nimi. Edited 20. Srpen, 2017 by Tanga 9 Link to comment Share on other sites More sharing options...
XaNe 0 Odesláno: 16. Srpen, 2017 Share Odesláno: 16. Srpen, 2017 Dobrý tutoriál Na tu 4. obtížnost to sedí Link to comment Share on other sites More sharing options...
Hlavní moderátor vEnd 279 Odesláno: 16. Srpen, 2017 Hlavní moderátor Share Odesláno: 16. Srpen, 2017 Hezký návod, který kromě hookování zlehka objasňuje i preprocesor. Přidán mezi ověřené a brzy mu dám i nálepku. Mám jednu otázku. Je nutné používat CallRemoteFunction? Nešlo by zavolat funkci přímo? Takhle totiž nelze předávat pole jako argument (nemyslím řetězec). Link to comment Share on other sites More sharing options...
ATomas 286 Odesláno: 16. Srpen, 2017 Share Odesláno: 16. Srpen, 2017 (upraveno) Aha tak ono se to jmenuje hook to co pouzivam Mylne jsem to radil do skupiny (retezovani, i kdyz pretezovani v c++ funguje jinak vim Dobre napsane a strozumitelne takze odeme +. Jinak jen takove upozorneni. Hookovani je hezke elegantni a zjednodusi praci. Ovsem pozor, kdyz si takto nahookujete nejakou fci kterou casto volate a pak ji takto zmenite, tak s tim musi clovek nakladat opatrne, aby si pak neuvaril CPU kdyz spusti mod Napr si hodit logovani na kazde GivePlayerMoney tak to taky umi nejaky CPU sezrat .D Edited 16. Srpen, 2017 by ATomas Link to comment Share on other sites More sharing options...
Tanga 131 Odesláno: 17. Srpen, 2017 Author Share Odesláno: 17. Srpen, 2017 Mám jednu otázku. Je nutné používat CallRemoteFunction? Nešlo by zavolat funkci přímo? Takhle totiž nelze předávat pole jako argument (nemyslím řetězec). Dobrá otázka, dík. Po pár experimentoch som zistil, že áno. Resp. CallLocalFunction() j, to upravím. Povedzme, že includ z bodu 5 (callback hook) bude v include "testcode". Lepšie sa to predstavuje keď to volanie dám už mimo pôvodného súboru (nemusíš rozmýšlať či už to bolo definované alebo ktorý krok preprocesora prebieha... ale stalo by sa to isté). #include <a_samp> #include <testcode> main() { OnPlayerGiveDamage(1, 2, 3, 5, 6); // example of a normal call } a tiež teda ku koncu testcode.inc (riadok 19) je #define OnPlayerGiveDamage MyOnPlayerGiveDamage // premenovanie nasledujuceho callbacku z čoho vznikne main() { MyOnPlayerGiveDamage(1, 2, 3, 4, 5); } čo následne a) ak neexistuje ďalší hook, crashne pri forwarde z testinclude.inc "error 004: function "MyOnPlayerGiveDamage" is not implemented" ak existuje ďalší hook, zavolá rovno ten ďalší a preskočí ten môj (skompiluje, ale kód bude chybný) Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now