Écrit par David Henry, le 2 septembre 2002
Attention : cet article a été écrit pour le SDK 2.2 d'Half-Life 1 ! Il se peut qu'il ne soit pas entièrement compatible avec des versions antérieures ou supérieures du Kit.
Aujourd'hui, comment demander des petits services à la client.dll par le biais de la mp.dll !!! Mé ? comment qu'on va faire !? Et bien avec le SDK 2.0, est arrivé un nouveau machin bizarre qui comment par « Ev ».. et finit par « nt »... Allez je vous laisse chercher un peu... bon je vous aide, il manque une lettre... Meuh non pas Evant ! tss.. bon aller je vous le dis sinon on va encore y passer la nuit : il s'agit des Events ! Quoi c'était marqué dans le titre ? Ben vi que c'est dans le titre !! Quoi ? Vous êtes dégoûté ? arff... C'est trop tard maintenant.
Les events ont été créés dans le but de diminuer la taille du flux de données network, et ainsi diminuer le lag, en intégrant des petits bouts de code dans la client.dll, appelés par la mp.dll, tel des animations, des effets sonores, application de décals d'impacts pour les armes, etc...
En fait, C'est en quelque sorte la mp.dll qui appelle une fonction de la client.dll. Évidemment, ces fonctions sont limitées. Vous ne pouvez pas par exemple décrémenter le nombre de vie du joueur, ou les munitions de son arme, et ces fonctions ne se coderont pas de la même manière que dans la mp.dll puisqu'on n'y retrouve pas les mêmes fonctions/classes, cependant certaines ont été copiées de la mp.dll et ressemble assez...
La question que vous devez vous poser maintenant, c'est comment on fait pour passer d'une
dll à l'autre ? La réponse est simple, avec un fichier qui servira "d'intermédiaire". Ces
fichiers, ce sont les .sc situés dans le répertoire « events » de votre mod
(si vous ne l'avez pas, il est bien temps de le créer). Si vous prenez par exemple un des
fichiers de valve, vous verrez par exemple à l'intérieur tout un script biscornu avec une
sorte de fonction Fire_Glock()
présente dans chaque fichier, et tout plein d'autres
trucs qu'on voit pas trop d'où ça sort...
Le plus con dans tout ça c'est ce script ne sert strictement à rien (peut-être pour un prochain SDK ?), et donc vos .sc perso pour vos events perso, mettez simplement: « Salut client.dll ! ça va ? » pour créer un environnement de sympathie et une bonne ambiance entre les 2 dlls. Non je déconne :-) ne faites pas ça, vous auriez l'air trop con à la sortie de votre mod. Vous pourrez les laisser vide en fait, leur contenu n'a aucune importance.
Encore une remarque, chaque fichier .sc équivaut à UNE fonction event (UNE fonction de la dll client).
On va commencer dans le mauvais sens, je sais, mais je pense que ce sera plus pratique comme ça. Nous sommes dans la mp.dll (ou hl.dll, ou votremod.dll, enfin c'est celle qu'est pas la dll client quoi)
En fait, c'est aussi facile que de créer une variable de type unsigned short
, de
lui assigner une valeur et de le faire passer comme paramètre à une fonction. Commençons par nous
rendre dans la classe de notre objet, et d'y ajouter la variable membre :
private: unsigned short m_usMyEvent;
Puis maintenant, nous allons précacher le fichier qui nous servira de lien, dans votre
fonction Precache()
:
m_usMyEvent = PRECACHE_EVENT (1, "events/myevent.sc");
Tiens, PRECACHE_EVENT
possède deux paramètres ! En fait, le premier paramètre doit
toujours être 1, donc pas de soucis (seulement ne l'oubliez pas). N'oubliez pas non plus de créer
myevent.sc.
Tous les events précachés sont envoyés à la client.dll, lors de la connexion. Si vous ne possédez pas le .sc que le serveur a besoin, il sera automatiquement téléchargé depuis le serveur (si le téléchargement est autorisé bien sur).
Maintenant, il nous reste une dernière chose à faire pour appeler l'event. Nous allons utiliser
pour ça une macro définie dans enginecallback.h : PLAYBACK_EVENT_FULL
qui
vaut la même chose que (*g_engfuncs.pfnPlaybackEvent
) à 12 paramètres, mais qui est
plus compréhensible. La déclaration de fonction pfnPlaybackEvent
se présente ainsi :
void pfnPlaybackEvent (int flags, const edict_t *pInvoker, unsigned short eventindex, float delay, float *origin, float *angles, float fparam1, float fparam2, int iparam1, int iparam2, int bparam1, int bparam2);
FEV
).m_pPlayer->edict()
ou edict()
tout court qui sont utilisés ici.m_usMyEvent
.(float *)&vec_Zero
pour les 2.Voilà pour la description des paramètres. Maintenant nous allons pouvoir poursuivre notre exemple, placez vous à l'endroit où y'aura besoin d'appeler l'event, et mettez par exemple pour une arme :
PLAYBACK_EVENT_FULL (0, m_pPlayer->edict(), m_usMyEvent, 0, (float *)&vec_Zero, (float *)&vec_Zero, 0.0, 0.0, 0, 0, 0, 0);
Vous allez me dire, « mais put1 ça fait des kms d'inutile !!! » et moi je vais vous répondre « ben ouai » et la vous allez me dire « et ya rien pour faire plus simple? » alors moi je vais vous répondre « si » et là, vous allez me demander c'est quoi et moi je vais vous expliquer ce que c'est :
Il existe des versions simplifiées de PLAYBACK_EVENT_FULL
, qui sont
PLAYBACK_EVENT
à 3 paramètres et PLAYBACK_EVENT_DELAY
à 4 paramètres
définies dans util.h. Dans les 2 cas, on utilise les 3 premiers paramètres de
PLAYBACK_EVENT_FULL
, c'est à dire les flags, « l'appelant » et l'index
(qu'on récupère en précachant) du fichier qui sert de transition entre les deux dlls. Pour
PLAYBACK_EVENT_DELAY
, le 4ème paramètre, je vous laisse deviner... c'est pas dur,
c'est même les 5 derniers caractères du nom de la macro...
On peut donc dans notre cas, mettre simplement :
PLAYBACK_EVENT (0, m_pPlayer->edict (), m_usMyEvent);
Notez que je ne fais pas passer de flags. Vous pouvez rajouter le delay en 4ème paramètre et
changer PLAYBACK_EVENT
en PLAYBACK_EVENT_DELAY
. Si vous voulez mettre
votre code en open source, et que vous êtes un salop, vous pouvez mettre
(*g_engfuncs.pfnPlaybackEvent)( /* blablabla... avec tous les paramètres même les inutiles */ )
mais rien que pour vous, je vous recommande d'utiliser plutôt les macros.
Voilà, reste plus qu'à voir ce qui se passe côté client.
Nous sommes maintenant dans la client.dll, et nous allons créer la fonction qui devra être exécutée à l'appelle de l'event.
Vous avez un petit sous dossier nommé « hl » dans votre workspace, ouvrez-le et
allez dans hl_events.cpp. Dans la partie extern "C"
, rajoutez votre
nouvelle fonction à la suite des autres :
extern "C" { // HLDM void EV_FireGlock1 (struct event_args_s *args); void EV_FireGlock2 (struct event_args_s *args); void EV_FireShotGunSingle (struct event_args_s *args); void EV_FireShotGunDouble (struct event_args_s *args); void EV_FireMP5 (struct event_args_s *args); void EV_FireMP52 (struct event_args_s *args); void EV_FirePython (struct event_args_s *args); void EV_FireGauss (struct event_args_s *args); void EV_SpinGauss (struct event_args_s *args); void EV_Crowbar (struct event_args_s *args); void EV_FireCrossbow (struct event_args_s *args); void EV_FireCrossbow2 (struct event_args_s *args); void EV_FireRpg (struct event_args_s *args); void EV_EgonFire (struct event_args_s *args); void EV_EgonStop (struct event_args_s *args); void EV_HornetGunFire (struct event_args_s *args); void EV_TripmineFire (struct event_args_s *args); void EV_SnarkFire (struct event_args_s *args); void EV_TrainPitchAdjust (struct event_args_s *args); void EV_MyEvent (struct event_args_s *args); }
Notre fonction s'appellera donc EV_MyEvent et elle aura un paramètre, c'est pour ensuite
pouvoir récupérer les variables que l'on a fait passer en paramètre à pfnPlaybackEvent()
dans la mp.dll. Ensuite, descendez un petit peu dans ce fichier, et dans la fonction
Game_HookEvents()
, rajoutez le vôtre sur le modèle des autres :
void Game_HookEvents () { gEngfuncs.pfnHookEvent ("events/glock1.sc", EV_FireGlock1); gEngfuncs.pfnHookEvent ("events/glock2.sc", EV_FireGlock2); gEngfuncs.pfnHookEvent ("events/shotgun1.sc", EV_FireShotGunSingle); gEngfuncs.pfnHookEvent ("events/shotgun2.sc", EV_FireShotGunDouble); gEngfuncs.pfnHookEvent ("events/mp5.sc", EV_FireMP5); gEngfuncs.pfnHookEvent ("events/mp52.sc", EV_FireMP52); gEngfuncs.pfnHookEvent ("events/python.sc", EV_FirePython); gEngfuncs.pfnHookEvent ("events/gauss.sc", EV_FireGauss); gEngfuncs.pfnHookEvent ("events/gaussspin.sc", EV_SpinGauss); gEngfuncs.pfnHookEvent ("events/train.sc", EV_TrainPitchAdjust); gEngfuncs.pfnHookEvent ("events/crowbar.sc", EV_Crowbar); gEngfuncs.pfnHookEvent ("events/crossbow1.sc", EV_FireCrossbow); gEngfuncs.pfnHookEvent ("events/crossbow2.sc", EV_FireCrossbow2); gEngfuncs.pfnHookEvent ("events/rpg.sc", EV_FireRpg); gEngfuncs.pfnHookEvent ("events/egon_fire.sc", EV_EgonFire); gEngfuncs.pfnHookEvent ("events/egon_stop.sc", EV_EgonStop); gEngfuncs.pfnHookEvent ("events/firehornet.sc", EV_HornetGunFire); gEngfuncs.pfnHookEvent ("events/tripfire.sc", EV_TripmineFire); gEngfuncs.pfnHookEvent ("events/snarkfire.sc", EV_SnarkFire); gEngFuncs.pfnHookEvent ("events/myevent.sc", EV_MyEvent); }
C'est ici qu'on dit à la dll client que le fichier « intermédiaire » entre la
mp.dll et notre fonction event EV_MyEvent
, c'est myevent.sc.
Allez maintenant dans ev_hldm.cpp et là, rebelotte : on va devoir
refaire la même chose avec le extern "C"
qu'il y a au début de ce
fichier :/. Ça fait un peu lourd en effet, de devoir recopier la même chose deux
fois... Perso moi ce que j'ai fait, c'est que j'ai déplacer cet extern "C"
dans
event.h et d'ajouter un include dans hl_events.cpp pour n'avoir qu'un seul
extern "C"
sur ces fonctions (attention par contre lors de ce genre de manip', je
ne vous dis pas de le faire ici, et je ne prends aucune responsabilité en cas de pb...).
Puis rendez-vous tout à la fin de ev_hldm.cpp pour y ajouter notre fonction :
void EV_MyEvent (event_args_t *args) { // PLACEZ VOTRE CODE --> ICI <-- }
Voilà, vous avez votre fonction event ! Maintenant, quelques petits trucs quand même avant la fin de l'émission :
enum
de vos anims comme
dans la mp.dll !EV_HLDM_FireBullets()
, EV_EjectBrass()
, EV_MuzzleFlash()
,
...), principalement dans ev_hldm.cpp.args
de type event_args_s
qui vous sera utile.
event_args_s
est une structure définie dans event_args.h ; les noms des
6 paramètres sont les mêmes que dans la mp.dll donc pour les réutiliser :
args->fparam1
, args->iparams2
, etc...Cet article est mis à disposition sous un contrat Creative Commons (licence CC-BY-ND).