Hallo Community,
aufgrund diverser Includes, die mich schon fast kotzen lassen,
zeige ich euch mal, wie ihr jetzt richtig Includes schreibt.
Dafür benötigen wir das sogenannte "hooking".
Was ist Hooking?
Hook ist englisch und bedeutet Haken (siehe z.B. Kapitän Hook).
Im Endeffekt heißt es also nichts anderes als sich irgendwo einzuhaken bzw sich irgendwo dazwischen zu klemmen.
Wozu wird Hooking benötigt?
Hooking wird z.B. in Includes benötigt und zwar aus einem einfachen Grund.
Includes sind theoretisch nichts weiter als Implementierung in die Scripte.
Beispiel:
Script:
#include <BeispielInclude>
forward Tutorial(playerid);
forward SekundenTimer();
main() { }
public Tutorial(playerid)
{
return 1;
}
public SekundenTimer()
{
return 1;
}
Include:
new Variable;
new Float:FloatVariable;
new Array[5];
So würde der Code zusammengefasst aussehen
new Variable;
new Float:FloatVariable;
new Array[5];
forward Tutorial(playerid);
forward SekundenTimer();
main() { }
public Tutorial(playerid)
{
return 1;
}
public SekundenTimer()
{
return 1;
}
Wie man nun sehr gut erkennen kann wurde #include <BeispielInclude>
mit dem Inhalt der BeispielInclude ersetzt.
Wozu ich euch das zeige?
Einfach
Wenn wir nun 2x eine Funktion im Script haben, spuckt der Compiler logischerweise einen Fehler aus,
da er nicht genau weiß, was nun mit der 2. Funktion geschehen soll.
Wie verhindert man dies?
Theoretisch gar nicht.
Oder doch?
Prinzip ist einfach.
Man hakt sich zwischen die Funktionen.
Bitte bedenkt, das sich das zwar auf alle Funktionen ausweiten lässt,
aber nicht immer Sinn macht.
Ab wann was Sinn macht, lest ihr weiter unten.
Hooking von Callbacks/Funktionen
Dazu benutzen wir das ALS-Hooking verfahren.
Das normale Hooking-Verfahren würde bei mehreren Hooks nur zu Problemen führen.
Die ALS Methode ist bisher am verbreitesten.
Es wird immer einen Include und Scriptteil und geben,
das dient nur zu Übersicht
Wir nehmen mal als Beispiel das Callback OnPlayerConnect
Script:
#include <a_samp>
#include <hooking>
public OnPlayerConnect(playerid)
{
return true;
}
Hier ist das noch ziemlich Standardmäßig
Aber nun zum eigentlichen.
Wir wollen uns ja dazwischen klemmen, deshalb erstellen wir einen weitern
Public, mit dem gleichen Namen.
Aber, dann haben wir doch das gleiche Problem wie vorher...
Ja, aber nur momentan, da kommt noch was dazu, also Geduld
Kommen wir zum eigentlichen.
Wir müssen dem Compiler nun sagen, das es 2 verschiedene Funkionen/Callbacks sind.
Das machen wir mit einem Macro/Define.
#define OnPlayerConnect _OnPlayerConnect
Vorsicht:
Ihr dürft aber nicht den Fehler machen und es ganz nach oben setzen,
sonst habt ihr eure Problem nicht umgangen.
Es muss direkt unter den public, den ihr hooken wollt.
Include:
public OnPlayerConnect(playerid) // Die Parameter kommen natürlich immer auf die Funktion an ;)
{
return true;
}
#define OnPlayerConnect _OnPlayerConnect
Aber moment.
Nun bekommt ihr den Fehler, das _OnPlayerConnect noch nicht deklariert wurde,
falls ihr versucht es zu compilen.
Das liegt daran, das der Macro/Define erst dann anfängt zu wirken, wenn er erstellt wird.
D.h. würde ich ihn nach oben setzen, würde er den neu erstellten public mit "umbennen".
Um das nicht definiert Problem zu lösen erstellen wir einen forward mit dem Namen der hinter der Funktion steht, die gehookt werden soll.
forward _OnPlayerConnect(playerid); // Die Parameter kommen natürlich immer auf die Funktion an ;)
Aber HALT.
Wenn wir 2 Includes haben die nach diesem Prinzip arbeiten, bekommen wir Fehler.
Dafür ist das oben erwähnte ALS-Hooking erforderlich.
Sofern die 2. Include dies auch verwendet.
Wie gesagt es ist verbreitet, aber nicht jede Include benutzt dies auch
Dafür müssen wir Macro/Define etwas erweitern.
Folgendes kommt dazu
#if defined _OnPlayerConnect
forward _OnPlayerConnect(playerid);
#endif#
if defined _ALS_OnPlayerConnect
#undef OnPlayerConnect
#else
#define _ALS_OnPlayerConnect
#endif
//hier kommt der Macro hin (#define OnPlayerConnect _OnPlayerConnect)
Kurze Erklärung.
Als erstes wird geprüft, ob die Funktion die wir hooken wollen überhaupt verwendet wird,
wenn ja wird, sie geforwardet um keine Fehler auszulösen.
Danach wird geprüft, ob das ALS Hooking schoneinmal verwendet wurde,
falls Ja, wird der Macro ab der Stelle ungültig gemacht.
Falls Nein, wird ein Macro/Define gesetzt, zu Markierung für weitere Includes,
damit keine Fehler auftreten.
Direkt darunter, wird ein neuer(unser) Macro/Define erstellt.
Fast Fertig.
Bisher sollte es dann so aussehen.
Include:
public OnPlayerConnect(playerid) // Die Parameter kommen natürlich immer auf die Funktion an ;)
{
return 1;
}
#if defined _OnPlayerConnect
forward _OnPlayerConnect(playerid); // Die Parameter kommen natürlich immer auf die Funktion an ;)
#endif
#if defined _ALS_OnPlayerConnect
#undef OnPlayerConnect
#else
#define _ALS_OnPlayerConnect
#endif
#define OnPlayerConnect _OnPlayerConnect
Momentmal, wenn ich es nun teste, wird OnPlayerConnect in meinem Gamemode bzw Script nicht aufgerufen.
Ich sagte doch, wir sind erst fast fertig
Wir haben uns nicht ganz dazwischen gehakt, sondern es eher abgefangen.
Aber wir wollen dazwischen und nicht es abfangen.
Das machen wir wie folgt.
Wie wir es bereits weiter oben gemacht haben nutzen wir wieder die "#if defined" Überprüfung.
Ist es vorhanden rufen wir das Callback auf, wenn nicht, geben wir einfach "true" zurück.
Das verhindert, das wir unnötig Zeit verlieren und das der Aufruf nicht einfach ins Leere läuft.
Am Ende sieht es dann für unser Beispiel wie folgt aus:
public OnPlayerConnect(playerid)
{
#if defined _OnPlayerConnect
return _OnPlayerConnect(playerid);
#else return true;
#endif
}
Dafür hat SA:MP die Funktion CallLocalFunction.
Diese Funktion lässt uns Funktionen direkt aus dem Script, in dem es verwendet wird, laden.
Es gibt aber noch die Möglichkeit des direkten Funktionsaufrufs.
Da gibt es nicht viel zu beachten,
einfach die Funktion mit dem neuen Namen aufrufen,
hier wäre das:
_OnPlayerConnect(playerid);
CallLocalFunktion, funktioniert wie SetTimerEx.
Das brauch ich wohl nicht zu erklären
Nun sind wir fertig