mud/fluffos-2.23-ds03/packages/mudlib_stats.c
2020-09-06 05:43:07 -07:00

602 lines
13 KiB
C

/*
* 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
*/
#ifdef LATTICE
#include "/lpc_incl.h"
#include "/backend.h"
#include "/md.h"
#include "/master.h"
#else
#include "../lpc_incl.h"
#include "../backend.h"
#include "../md.h"
#include "../master.h"
#include "../efun_protos.h"
#endif
#include "mudlib_stats.h"
#ifdef F_DOMAIN_STATS
void
f_domain_stats (void)
{
mapping_t *m;
if (st_num_arg) {
m = get_domain_stats(sp->u.string);
free_string_svalue(sp--);
} else {
m = get_domain_stats(0);
}
if (!m) {
push_number(0);
} else {
/* ref count is properly decremented by get_domain_stats */
push_mapping(m);
}
}
#endif
#ifdef F_SET_AUTHOR
void
f_set_author (void)
{
set_author(sp->u.string);
free_string_svalue(sp--);
}
#endif
#ifdef F_AUTHOR_STATS
void
f_author_stats (void)
{
mapping_t *m;
if (st_num_arg) {
m = get_author_stats(sp->u.string);
free_string_svalue(sp--);
} else {
m = get_author_stats(0);
}
if (!m) {
push_number(0);
} else {
/* ref count is properly decremented by get_author_stats */
push_mapping(m);
}
}
#endif
/* Support functions */
static mudlib_stats_t *domains = 0;
static mudlib_stats_t *backbone_domain = 0;
static mudlib_stats_t *authors = 0;
static mudlib_stats_t *master_author = 0;
static mudlib_stats_t *find_stat_entry (const char *, mudlib_stats_t *);
static mudlib_stats_t *add_stat_entry (const char *, mudlib_stats_t **);
static void init_author_for_ob (object_t *);
static const char *author_for_file (const char *);
static void init_domain_for_ob (object_t *);
static const char *domain_for_file (const char *);
static void save_stat_list (const char *, mudlib_stats_t *);
static void restore_stat_list (const char *, mudlib_stats_t **);
static mapping_t *get_info (mudlib_stats_t *);
static mapping_t *get_stats (const char *, mudlib_stats_t *);
static mudlib_stats_t *insert_stat_entry (mudlib_stats_t *, mudlib_stats_t **);
#ifdef DEBUGMALLOC_EXTENSIONS
/* debugging */
int check_valid_stat_entry (mudlib_stats_t * se) {
mudlib_stats_t *tmp;
tmp = domains;
while (tmp) {
if (tmp == se) return 1;
tmp = tmp->next;
}
tmp = authors;
while (tmp) {
if (tmp == se) return 1;
tmp = tmp->next;
}
return 0;
}
#endif
/**************************
* stat list manipulation
**************************/
static mudlib_stats_t *insert_stat_entry (mudlib_stats_t * entry, mudlib_stats_t ** 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
*/
static mudlib_stats_t *find_stat_entry (const 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.
*/
static mudlib_stats_t *add_stat_entry (const char * str, mudlib_stats_t ** list)
{
mudlib_stats_t *entry;
if ((entry = find_stat_entry(str, *list)))
return entry;
entry = ALLOCATE(mudlib_stats_t, TAG_MUDLIB_STATS, "add_stat_entry");
entry->name = make_shared_string(str);
entry->length = strlen(str);
entry->moves = 0;
entry->heart_beats = 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 (statgroup_t * st, object_t * ob)
{
st->domain = ob->stats.domain;
st->author = ob->stats.author;
}
void null_stats (statgroup_t * st)
{
if (st) {
st->domain = NULL;
st->author = NULL;
}
}
void init_stats_for_object (object_t * ob)
{
init_domain_for_ob(ob);
init_author_for_ob(ob);
}
/*
* Add moves to an existing domain.
*/
void add_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_heart_beats (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_array_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 (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 (const char * file, int errors)
{
mudlib_stats_t *entry;
const 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 (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%
* heart_beats -= 10%
*/
void mudlib_stats_decay()
{
mudlib_stats_t *dl;
static int next_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->heart_beats = dl->heart_beats * 9 / 10;
}
for (dl = authors; dl; dl = dl->next) {
dl->moves = dl->moves * 99 / 100;
dl->heart_beats = dl->heart_beats * 9 / 10;
}
}
#ifdef DEBUGMALLOC_EXTENSIONS
void mark_mudlib_stats() {
mudlib_stats_t *dl;
for (dl = domains; dl; dl = dl->next) {
DO_MARK(dl, TAG_MUDLIB_STATS);
EXTRA_REF(BLOCK(dl->name))++;
}
for (dl = authors; dl; dl = dl->next) {
DO_MARK(dl, TAG_MUDLIB_STATS);
EXTRA_REF(BLOCK(dl->name))++;
}
}
#endif
/*************************
Author specific functions
*************************/
static void init_author_for_ob (object_t * ob)
{
svalue_t *ret;
push_malloced_string(add_slash(ob->obname));
ret = apply_master_ob(APPLY_AUTHOR_FILE, 1);
if (ret == (svalue_t *)-1) {
ob->stats.author = master_author;
} else if (!ret || ret->type != T_STRING) {
ob->stats.author = NULL;
} else {
ob->stats.author = add_stat_entry(ret->u.string, &authors);
}
}
void set_author (const char *name)
{
object_t *ob;
if (!current_object)
return;
ob = current_object;
if (master_ob == (object_t *)-1) {
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_master_author (const char * str)
{
mudlib_stats_t *author;
author = add_stat_entry(str, &authors);
if (author)
master_author = author;
return author;
}
static const char *author_for_file (const char * file)
{
svalue_t *ret;
static char buff[50];
copy_and_push_string(file);
ret = apply_master_ob(APPLY_AUTHOR_FILE, 1);
if (ret == 0 || ret == (svalue_t*)-1 || ret->type != T_STRING)
return 0;
strcpy(buff, ret->u.string);
return buff;
}
/*************************
Domain specific functions
*************************/
static void init_domain_for_ob (object_t * ob)
{
svalue_t *ret;
const char *domain_name;
if (!current_object
#ifdef PACKAGE_UIDS
|| !current_object->uid
#endif
) {
/*
* 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 object who the creator of this object is.
*/
push_malloced_string(add_slash(ob->obname));
if (master_ob)
ret = apply_master_ob(APPLY_DOMAIN_FILE, 1);
else
ret = apply(applies_table[APPLY_DOMAIN_FILE], ob, 1, ORIGIN_DRIVER);
if (IS_ZERO(ret)) {
ob->stats.domain = current_object->stats.domain;
return;
}
if (ret->type != T_STRING)
error("'domain_file' in the master object must return a string!\n");
domain_name = ret->u.string;
if (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 (const 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.
*/
static const char *domain_for_file (const char * file)
{
svalue_t *ret;
static char buff[512];
share_and_push_string(file);
ret = apply_master_ob(APPLY_DOMAIN_FILE, 1);
if (ret == 0 || ret == (svalue_t*)-1 || ret->type != T_STRING)
return 0;
strcpy(buff, ret->u.string);
return buff;
}
/************************************
* save and restore stats to a file *
************************************/
static void save_stat_list (const char * file, mudlib_stats_t * list)
{
FILE *f;
char fname_buf[MAXPATHLEN];
char *fname = fname_buf;
if (file) {
if (strchr(file, '/')) {
if (file[0] == '/')
file++;
f = fopen(file, "w");
} else {
sprintf(fname, "%s/%s", LOG_DIR, file);
if (fname[0] == '/')
fname++;
f = fopen(fname, "w");
}
} else {
debug_message("*Warning: call to save_stat_list with null filename\n");
return;
}
if (!f) {
debug_message("*Error: unable to open stat file %s for writing.\n",
file);
return;
}
while (list) {
fprintf(f, "%s %d %d\n", list->name,
list->moves, list->heart_beats);
list = list->next;
}
fclose(f);
}
static void restore_stat_list (const char * file, mudlib_stats_t ** list)
{
FILE *f;
char fname_buf[MAXPATHLEN];
char *fname = fname_buf;
mudlib_stats_t *entry;
if (file) {
if (strchr(file, '/')) {
if (file[0] == '/')
file++;
f = fopen(file, "r");
} else {
sprintf(fname, "%s/%s", LOG_DIR, file);
if (fname[0] == '/')
fname++;
f = fopen(fname, "r");
}
} else {
debug_message("*Warning: call to save_stat_list with null filename\n");
return;
}
if (!f) {
debug_message("*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\n", &entry->moves, &entry->heart_beats);
}
fclose(f);
}
void save_stat_files()
{
save_stat_list(DOMAIN_STATS_FILE_NAME, domains);
save_stat_list(AUTHOR_STATS_FILE_NAME, authors);
}
void restore_stat_files()
{
restore_stat_list(DOMAIN_STATS_FILE_NAME, &domains);
restore_stat_list(AUTHOR_STATS_FILE_NAME, &authors);
}
/*************************************
* The following functions are the interface for efuns to get mappings
* that describe the statistics for authors and domains.
**************************************/
static mapping_t *
get_info (mudlib_stats_t * dl)
{
mapping_t *ret;
ret = allocate_mapping(8);
add_mapping_pair(ret, "moves", dl->moves);
add_mapping_pair(ret, "errors", dl->errors);
add_mapping_pair(ret, "heart_beats", dl->heart_beats);
add_mapping_pair(ret, "array_size", dl->size_array);
add_mapping_pair(ret, "objects", dl->objects);
return ret;
}
static mapping_t *
get_stats (const char * str, mudlib_stats_t * list)
{
mudlib_stats_t *dl;
mapping_t *m;
svalue_t lv, *s;
if (str) {
for (dl = list; dl; dl = dl->next) {
if (!strcmp(str, dl->name)) /* are these both shared strings? */
break;
}
if (dl) {
mapping_t *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 = dl->name; /* find_for_insert() adds a ref */
s = find_for_insert(m, &lv, 1);
s->type = T_MAPPING;
s->subtype = 0;
s->u.map = get_info(dl);
}
m->ref--;
return m;
}
mapping_t *get_domain_stats (const char * str){
return get_stats(str, domains);
}
mapping_t *get_author_stats (const char * str){
return get_stats(str, authors);
}