Wiki

Clone wiki

asss / 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