Wiki
Clone wikiasss / Adviser
Advisers
Advisers are a form of hooking. They are a formal means for modules to extend or modify the behavior of the module that defines the adviser. A module should define an adviser when it implements a feature or behavior that other modules may need to modify in a controlled way.
Properties of advisers
- They are a collection of functions to be implemented or values to be defined, associated with a type identifier.
- Typically adviser functions should return data to the caller, either through the function return value, or through a pointer argument.
- Advisers are not used for event notification, as there is no guarantee that the defining module will consult every adviser. Callbacks should be used instead for event notification.
- The defining module calls functions in the adviser, like an interface, and they will often call multiple advisers to work on a single request. Unlike interfaces, the goal of advisers is to let the instantiating module influence functionality.
- Can be implemented several times by many modules on the same scope, there is no real limit to the number of advisers per type.
- Not all fields necessarily have to be filled in the instance. The defining module must document how its fields are used and verify them.
Naming convention:
- struct name: A<advisertype>
- adviser identifier: A_<ADVISERTYPE>
- adviser version: "advisertype-version"
Adviser types must start with ADVISER_HEAD_DECL
Advisers must start with ADVISER_HEAD_INIT(A_<ADVISERTYPE>) to store the adviser type identifier.
Example
- game.h defines an Appk adviser.
- game.c calls a list of Appk advisers to modify its behavior.
- (not shown: game.c handling EditIndividualPPK)
- pacifier.c includes game.h and implements and registers an Appk adviser.
Defining an adviser
game.h
#define A_PPK "ppk-1"
/** the position packet adviser struct */
typedef struct Appk
{
ADVISER_HEAD_DECL
/** Modifies a player's position packet before it is sent out.
* The adviser may edit the packet, but should not rely on
* this function for notification, as other advisers may be
* consulted. Instead the CB_PPK callback should be used for
* notification.
* @param p the player that the position packet belongs to
* @param pos a pointer to the position packet
*/
void (*EditPPK)(Player *p, struct C2SPosition *pos);
/** Modifies a player's position packet before it is sent to a specific player.
* @param p the player that the position packet belongs to
* @param t the player that the position packet will be sent to
* @param pos a pointer to the position packet
* @param extralen the extra length of the position packet (0 = none, 2 = energy, 10 = epd)
* @return TRUE if this function modified the packet, FALSE otherwise
*/
int (*EditIndividualPPK)(Player *p, Player *t, struct C2SPosition *pos, int *extralen);
} Appk;
Calling advisers
game.c
LinkedList advisers = LL_INITIALIZER;
Link *link;
Appk *ppkadviser;
/* consult the PPK advisers allow modules to modify this position packet before we send it to other players */
mm->GetAdviserList(A_PPK, arena, &advisers);
/* FOR EACH ppkadviser in advisers (iterate using link) */
FOR_EACH(&advisers, ppkadviser, link)
{
if (ppkadviser->EditPPK)
{
ppkadviser->EditPPK(p, pos);
}
}
mm->ReleaseAdviserList(&advisers);
/* at this point, the position packet has been modified by all modules that wanted to,
* and we will work with it in this form from now on */
Implementing an adviser
pacifier.c
/* not actually necessary for core modules, they are all included with asss.h */
#include "game.h"
/* function we will put in the adviser */
void EditPPK(Player *p, struct C2SPosition *pos)
{
if (pos->weapon.type != W_NULL)
{
/* remove all weapons coming from the player. they will not show up on other player's screens. */
pos->weapon.type = W_NULL;
}
}
/* define the adviser, notice we are not implementing the second function */
local Appk ppkadviser =
{
ADVISER_HEAD_INIT(A_PPK)
EditPPK, NULL
};
Registering an adviser
pacifier.c
/* entry point for the 'pacifier' module */
EXPORT int MM_pacifier(int action, Imodman *mm, Arena *a)
{
if (action == MM_LOAD)
{
/* register our adviser */
mm->RegAdviser(&ppkadviser, ALLARENAS);
/* return success */
return MM_OK;
}
else if (action == MM_UNLOAD)
{
mm->UnregAdviser(&ppkadviser, ALLARENAS);
return MM_OK;
}
return MM_FAIL;
}
Updated