/* -*- LPC -*- */ /** * This is the Refresh Handler. When a player refreshes, this * handler is called so that all the miscellaneous bookkeeping * about that player can be reset. This should be used * for taking someone out of the playtesters' group, resetting the * criminal ledger in KLK, etc. Everything, in short, * which is not a property on the player object or otherwise * can be handled entirely by the player object without using any other * objects. * * Similarly, whenever a player gets deleted, this handler * gets called upon to call a number of deletion callbacks * to remove bookeeping about that player. * * If you have code that stores information about a player outside of * the player object, then you should call register_refresh() * and possibly register_delete() * on this handler. That will then remember the function you * give it. Then, whenever a player refreshes or deletes, the * function you specified will get called with the player object * and a flag to indicate if this is a total refresh or a partial * refresh (for refresh callbacks) or a string with the player * name (for player deletions). Do the right thing. * will get called with the player object and a flag to indicate * if this is a total refresh or a partial refresh. Do the * right thing. * * If your object moves to a different file, then you will * have to manually unregister the object from the refresh * handler. This is very important! To unregister, simply * call register_function() again, with the same object, but * give 0 for the function. * * Each object can have only one refresh function registered, * and clones can't have any! * * You can easily use the same function for both a deletion * and refresh callback. All you do is declare the function * void callback_func(mixed player, int flag) * and then test flag to see if it is PARTIAL_REFRESH, * TOTAL_REFRESH, or PLAYER_DELETED. In the last case, player * will be a string. Otherwise player is an object. * * @author Sin * * @change 17 Nov 1998, Sin * Handles deletions now, too. */ #include <refresh.h> #include <playerinfo.h> #define SAVE_FILE "/save/refresh" mapping refresh_callbacks; mapping delete_callbacks; nosave string loaded_by; nosave string loaded_time; /** * @ignore yes */ protected void load_me() { refresh_callbacks = 0; delete_callbacks = 0; unguarded( (: restore_object, SAVE_FILE, 0 :) ); if (!refresh_callbacks) { refresh_callbacks = ([ ]); } if (!delete_callbacks) { delete_callbacks = ([ ]); } } /** * @ignore yes */ protected void save_me() { unguarded( (: save_object, SAVE_FILE, 0 :) ); } /** * @ignore yes * This is used to make the player info handler happy. */ string query_name() { return "Refresh Handler"; } /* query_name() */ /** * @ignore yes */ void create() { seteuid("/secure/master"->creator_file(file_name(this_object()))); load_me(); if (!previous_object()) { loaded_by = "The Masked Man"; } else { if (previous_object()->query_creator()) { loaded_by = "Creator: " + previous_object()->query_name(); } else if (previous_object()->query_interactive()) { loaded_by = "Player: " + previous_object()->query_name(); } else { loaded_by = file_name(previous_object()); if (this_player()) loaded_by += sprintf(" (%s)", this_player()->query_name()); } } loaded_time = ctime(time()); } /** * @ignore yes */ void dest_me() { save_me(); destruct(this_object()); } /** * @ignore yes */ nomask mixed dwep() { efun::destruct(this_object()); return "Destructed With Extreme Prejudice"; } /** * Use this function to tell the refresh handler about functions that * you want to have called whenever any player refreshes or gets * deleted. * * Typically, you will call this by hand, with the 'call' command, * rather than coding a call to this function into your code. * * The first parameter is the object that contains the callback * to be called. This can be either a pointer to the object, or * the filename to the object. * * The second parameter is the name of the function that should be * called. This callback should take two parameters: an object for * the player who is affected, and an integer, which will be either * PARTIAL_REFRESH or TOTAL_REFRESH. * * This function will refuse to register a callback if either: * the object is a clone, the callback isn't defined in the object, or * the callback is a function pointer. */ string register_refresh(mixed ob, string func) { object real; if (objectp(ob)) { ob = base_name(ob); } if (!ob) { return "Please supply an object"; } real = load_object(ob); if (!real) { return "Couldn't find object"; } if (func && !function_exists(func, real)) { return "Couldn't find function"; } if (!func) { if (refresh_callbacks[ob]) { map_delete(refresh_callbacks, ob); } } else { if (refresh_callbacks[ob]) { refresh_callbacks[ob] = func; } else { refresh_callbacks += ([ ob : func ]); } } save_me(); } /** * This function does almost exactly what register_refresh() does, * but it is used for registering delete handlers. * * Delete callbacks are called with two parameters as well, but * the first parameter is a string containing the player's name, * while the second parameter is the integer PLAYER_DELETED. */ string register_delete(mixed ob, string func) { object real; if (objectp(ob)) { ob = base_name(ob); } if (!ob) { return "Please supply an object"; } real = load_object(ob); if (!real) { return "Couldn't find object"; } if (func && !function_exists(func, real)) { return "Couldn't find function"; } if (!func) { if (delete_callbacks[ob]) { map_delete(delete_callbacks, ob); } } else { if (delete_callbacks[ob]) { delete_callbacks[ob] = func; } else { delete_callbacks += ([ ob : func ]); } } save_me(); } /** * This function gets called by the player object whenever * a player refreshes, or by any of the various objects that delete * players when they get deleted. You should never call * this function directly. */ varargs void player_refreshed(object player, int totally) { string ob; if (base_name(file_name(previous_object())) != "/global/player") { return; } foreach (ob in keys(refresh_callbacks)) { object obj; obj = load_object(ob); if (obj) { mixed *vals; vals = ({ refresh_callbacks[ob], player, totally }); call_out( (: call_other, obj, vals :), 1); } } } /** * This, like player_refreshed(), goes through and calls all the * registered deletion callbacks. In this case, however, the * deletion callbacks get called with a _string_ rather than * an object. That string is the name of the player who is being * deleted. */ varargs void player_deleted(string player) { string ob; if (file_name(previous_object()) != "/secure/delete_clear" && file_name(previous_object()) != "/cmds/lord/rmp_layer" && !master()->high_programmer(previous_object(-1))) { unguarded( (: write_file, "/log/CHEAT", ctime( time() ) + ": illegal attempt to delete player files using "+ "refresh_handler\nTrace: "+ back_trace() :) ); return; } foreach (ob in keys(delete_callbacks)) { object obj; obj = load_object(ob); if (obj) { mixed *vals; vals = ({ delete_callbacks[ob], player, PLAYER_DELETED }); call_out( (: call_other, obj, vals :), 1); } } "/secure/related_files"->delete_related_files(player, 1, 0); } /** * Return the current list of callback functions. */ mapping query_funcs() { return ([ "refresh callbacks" : refresh_callbacks, "delete callbacks" : delete_callbacks, ]); } /** * This function supports the 'stat' command. If you stat the * refresh handler, you'll see up to 4 things: the number of * refresh callbacks registered, the number of delete callbacks registered, * the person or object who loaded the refresh handler, and the * time that the handler got loaded. */ mixed *stats() { return ({ ({ "refreshes", sizeof(refresh_callbacks) }), ({ "deletes", sizeof(delete_callbacks) }), ({ "loaded by", loaded_by }), ({ "loaded time", loaded_time }), }); }