/* * mudlib_stats.c * created by: Erik Kay * last modified: 11/1/92 * this file is a replacement for wiz_list.c and all of its purposes * the idea is that it will be more domain based, rather than user based * and will be a little more general purpose than wiz_list was */ #include <stdio.h> #include <string.h> #if !defined(NeXT) && !defined(LATTICE) #include <sys/param.h> #endif #ifdef LATTICE #include "amiga.h" #endif #include "config.h" #include "lint.h" #include "interpret.h" #include "object.h" #include "mapping.h" extern char *string_copy PROT((char *)), *xalloc PROT((int)); extern struct object *master_ob, *current_object; void init_domain_for_ob PROT((struct object *)); void init_author_for_ob PROT((struct object *)); mudlib_stats_t *domains = 0; mudlib_stats_t *backbone_domain = 0; mudlib_stats_t *authors = 0; mudlib_stats_t *root_author = 0; /************************** * stat list manipulation **************************/ static mudlib_stats_t *insert_stat_entry (entry, list) mudlib_stats_t *entry, **list; { entry->next = *list; *list = entry; return *list; } /* * Return the data for an individual domain, if it exists. * this uses a simple linear search. it's a good thing that most muds * will have a relatively small number of domains */ mudlib_stats_t *find_stat_entry(name, list) char *name; mudlib_stats_t *list; { int length; length = strlen(name); for (; list; list = list->next) if (list->length == length && strcmp(list->name, name) == 0) return list; return 0; } /* * add a new domain to the domain list. If it exists, do nothing. */ mudlib_stats_t *add_stat_entry (str, list) char *str; mudlib_stats_t **list; { mudlib_stats_t *entry; if ((entry = find_stat_entry(str, *list))) return entry; entry = (mudlib_stats_t *)DXALLOC(sizeof (mudlib_stats_t), 78, "add_stat_entry"); entry->name = make_shared_string(str); entry->length = strlen(str); entry->moves = 0; entry->cost = 0; entry->heart_beats = 0; entry->total_worth = 0; entry->errors = 0; entry->objects = 0; entry->next = NULL; entry->size_array = 0; insert_stat_entry (entry, list); return entry; } /************************************* * general stat modifying accessor functions **************************************/ void assign_stats(st, ob) statgroup_t *st; struct object *ob; { st->domain = ob->stats.domain; st->author = ob->stats.author; } void null_stats (st) statgroup_t *st; { if (st) { st->domain = NULL; st->author = NULL; } } void init_stats_for_object (ob) struct object *ob; { init_domain_for_ob(ob); init_author_for_ob(ob); } /* * Add moves to an existing domain. */ void add_moves(st, moves) statgroup_t *st; int moves; { if (st) { if (st->domain) st->domain->moves += moves; if (st->author) st->author->moves += moves; } } INLINE void add_cost (st, cost) statgroup_t *st; int cost; { if (st) { if (st->domain) st->domain->cost += cost; if (st->author) st->author->cost += cost; } } INLINE void add_heart_beats (st, hbs) statgroup_t *st; int hbs; { if (st) { if (st->domain) st->domain->heart_beats += hbs; if (st->author) st->author->heart_beats += hbs; } } void add_worth (st, worth) statgroup_t *st; int worth; { if (st) { if (st->domain) st->domain->total_worth += worth; if (st->author) st->author->total_worth += worth; } } void add_array_size (st, size) statgroup_t *st; int size; { if (st) { if (st->domain) st->domain->size_array += size; if (st->author) st->author->size_array += size; } } void add_errors (st, errors) statgroup_t *st; int errors; { if (st) { if (st->domain) st->domain->errors += errors; if (st->author) st->author->errors += errors; } } void add_errors_for_file (file, errors) char *file; int errors; { mudlib_stats_t *entry; char *name; name = domain_for_file (file); if (name && domains) { entry = find_stat_entry (name, domains); if (entry) entry->errors += errors; } name = author_for_file (file); if (name && authors) { entry = find_stat_entry (name, authors); if (entry) entry->errors += errors; } } void add_objects (st, objects) statgroup_t *st; int objects; { if (st) { if (st->domain) st->domain->objects += objects; if (st->author) st->author->objects += objects; } } /* * Basically the "scores" are averaged over time by having them decay * gradually at each reset. * Here's how the decay breaks down: * moves -= 1% * total_worth -= 1% * cost -= 10% * heart_beats -= 10% */ void mudlib_stats_decay() { mudlib_stats_t *dl; static int next_time; extern int current_time; /* Perform this once every hour. */ if (next_time > current_time) return; next_time = current_time + 60 * 60; for (dl = domains; dl; dl = dl->next) { dl->moves = dl->moves * 99 / 100; dl->total_worth = dl->total_worth * 99 / 100; dl->cost = dl->cost * 9 / 10; dl->heart_beats = dl->heart_beats * 9 / 10; } for (dl = authors; dl; dl = dl->next) { dl->moves = dl->moves * 99 / 100; dl->total_worth = dl->total_worth * 99 / 100; dl->cost = dl->cost * 9 / 10; dl->heart_beats = dl->heart_beats * 9 / 10; } } /* * free all of the mudlib statistics structures */ void free_mudlib_stats() { mudlib_stats_t *dl, *d; for (d = domains; d; d = dl) { free_string(d->name); dl = d->next; FREE((char *)d); } for (d = authors; d; d = dl) { free_string(d->name); dl = d->next; FREE((char *)d); } } /************************* Author specific functions *************************/ void init_author_for_ob (ob) struct object *ob; { struct svalue *ret; if (master_ob == NULL) { ob->stats.author = root_author; return; } push_string (ob->name, STRING_CONSTANT); ret = apply_master_ob ("author_file", 1); if (IS_ZERO(ret)) { ob->stats.author = NULL; } else { ob->stats.author = add_stat_entry (ret->u.string, &authors); } } void set_author (name) char *name; { struct object *ob; if (!current_object) return; ob = current_object; if (master_ob == NULL) { ob->stats.author = NULL; return; } if (ob->stats.author) { ob->stats.author->objects--; } ob->stats.author = add_stat_entry (name, &authors); if (ob->stats.author) { ob->stats.author->objects++; } } mudlib_stats_t *set_root_author (str) char *str; { mudlib_stats_t * author; author = add_stat_entry (str, &authors); if (author) root_author = author; return author; } char *author_for_file(file) char *file; { struct svalue *ret; static char buff[50]; push_string(file, STRING_CONSTANT); ret = apply_master_ob("author_file", 1); if (ret == 0 || ret->type != T_STRING) return 0; strcpy(buff, ret->u.string); return buff; } /************************* Domain specific functions *************************/ void init_domain_for_ob (ob) struct object *ob; { struct svalue *ret; char *domain_name; struct object *tmp_ob; static char *domain_file_fname = NULL; if (master_ob == 0) tmp_ob = ob; else { assert_master_ob_loaded(); tmp_ob = master_ob; } if (!master_ob || !current_object || !current_object->uid) { /* * Only for the master and void object. Note that * you can't ask for the backbone or root domain here since * we're in the process of loading the master object. */ ob->stats.domain = add_stat_entry ("NONAME", &domains); return; } /* * Ask master.c who the creator of this object is. */ push_string(ob->name, STRING_CONSTANT); if (!domain_file_fname) domain_file_fname = make_shared_string("domain_file"); ret = apply(domain_file_fname, tmp_ob, 1); if (!ret) error("No function 'domain_file' in master.c!\n"); domain_name = ret->u.string; if (IS_ZERO(ret) || strcmp(current_object->stats.domain->name, domain_name) == 0) { ob->stats.domain = current_object->stats.domain; return; } if (strcmp(backbone_domain->name, domain_name) == 0) { /* * The object is loaded from backbone. * We give domain ownership to the creator rather than backbone. */ ob->stats.domain = current_object->stats.domain; return; } /* * The object isn't loaded from backbone or from the same domain as * the creator, so we need to lookup the domain, and add it if it isnt * present. */ ob->stats.domain = add_stat_entry (domain_name, &domains); return; } mudlib_stats_t *set_backbone_domain (str) char *str; { mudlib_stats_t * dom; dom = add_stat_entry (str, &domains); if (dom) backbone_domain = dom; return dom; } /* * Argument is a file name, which we want to get the domain of. * Ask the master object. */ char *domain_for_file(file) char *file; { struct svalue *ret; static char buff[50]; if (!master_ob) return NULL; push_string(file, STRING_CONSTANT); ret = apply_master_ob("domain_file", 1); if (ret == 0 || ret->type != T_STRING) return 0; strcpy(buff, ret->u.string); return buff; } /************************************ * save and restore stats to a file * ************************************/ void save_stat_list (file, list) char * file; mudlib_stats_t *list; { FILE *f; char fname[MAXPATHLEN]; if (file) { if (strchr(file,'/')) { if (file[0] == '/') strcpy (file, file+1); f = fopen (file,"w"); } else { sprintf (fname,"%s/%s",LOG_DIR,file); if (fname[0] == '/') strcpy (fname, fname+1); f = fopen (fname, "w"); } } else { fprintf (stderr, "*Warning: call to save_stat_list with null filename\n"); return; } if (!f) { fprintf (stderr, "*Error: unable to open stat file %s for writing.\n", file); return; } while (list) { fprintf (f, "%s %d %d %d %d\n", list->name, list->moves, list->cost, list->heart_beats, list->total_worth); list = list->next; } fclose (f); } void restore_stat_list (file, list) char * file; mudlib_stats_t **list; { FILE *f; char fname[MAXPATHLEN]; mudlib_stats_t *entry; if (file) { if (strchr(file,'/')) { if (file[0] == '/') strcpy (file, file+1); f = fopen (file,"r"); } else { sprintf (fname,"%s/%s",LOG_DIR,file); if (fname[0] == '/') strcpy (fname, fname+1); f = fopen (fname, "r"); } } else { fprintf (stderr, "*Warning: call to save_stat_list with null filename\n"); return; } if (!f) { fprintf (stderr, "*Warning: unable to open stat file %s for reading.\n", file); return; } while (fscanf (f, "%s", fname) != EOF) { entry = add_stat_entry (fname, list); fscanf (f, "%d %d %d %d\n", &entry->moves, &entry->cost, &entry->heart_beats, &entry->total_worth); } fclose (f); } void save_stat_files () { save_stat_list ("domain_stats",domains); save_stat_list ("author_stats",authors); } void restore_stat_files () { restore_stat_list ("domain_stats", &domains); restore_stat_list ("author_stats", &authors); } /************************************* * The following functions are the interface for efuns to get mappings * that describe the statistics for authors and domains. **************************************/ struct mapping * get_info(dl) mudlib_stats_t *dl; { struct mapping *ret; ret = allocate_mapping(8); add_mapping_pair(ret, "moves", dl->moves); add_mapping_pair(ret, "cost", dl->cost); add_mapping_pair(ret, "errors", dl->errors); add_mapping_pair(ret, "heart_beats", dl->heart_beats); add_mapping_pair(ret, "worth", dl->total_worth); add_mapping_pair(ret, "array_size", dl->size_array); add_mapping_pair(ret, "objects", dl->objects); return ret; } struct mapping * get_stats(str, list) char *str; mudlib_stats_t *list; { mudlib_stats_t *dl; struct mapping *m; struct svalue lv, *s; if (str) { for (dl = list; dl; dl = dl->next) { if (!strcmp(str, dl->name)) /* are these both shared strings? */ break; } if (dl) { struct mapping *tmp; tmp = get_info(dl); tmp->ref--; return tmp; } else { return 0; } } m = allocate_mapping(8); for (dl = list; dl; dl = dl->next) { lv.type = T_STRING; lv.subtype = STRING_SHARED; lv.u.string = ref_string(dl->name); s = find_for_insert(m, &lv, 1); s->type = T_MAPPING; s->subtype = 0; s->u.map = get_info(dl); } m->ref--; return m; } struct mapping * get_domain_stats(str) char *str; { return get_stats(str, domains); } struct mapping * get_author_stats(str) char *str; { return get_stats(str, authors); }