fbmuck-6.05/auto/
fbmuck-6.05/contrib/jresolver/
fbmuck-6.05/contrib/jresolver/org/
fbmuck-6.05/contrib/jresolver/org/fuzzball/
fbmuck-6.05/docs/devel/
fbmuck-6.05/game/
fbmuck-6.05/game/logs/
fbmuck-6.05/game/muf/
fbmuck-6.05/scripts/
fbmuck-6.05/src_docs/
#include "copyright.h"
#include "config.h"
#include "params.h"

#include "db.h"
#include "tune.h"
#include "props.h"
#include "externs.h"
#include "interface.h"

#ifdef DISKBASE

extern FILE *input_file;
extern FILE *delta_infile;
extern FILE *delta_outfile;
extern void getproperties(FILE * f, dbref obj, const char *pdir);


long propcache_hits = 0L;
long propcache_misses = 0L;

struct pload_Q {
	dbref obj;
	long count;
	int Qtype;
};

struct pload_Q propchanged_Q = { NOTHING, 0, PROPS_CHANGED };
struct pload_Q proploaded_Q = { NOTHING, 0, PROPS_LOADED };
struct pload_Q proppri_Q = { NOTHING, 0, PROPS_PRIORITY };


void
removeobj_ringqueue(dbref obj)
{
	struct pload_Q *ref = NULL;

	switch (DBFETCH(obj)->propsmode) {
	case PROPS_UNLOADED:
		return;
		break;
	case PROPS_LOADED:
		ref = &proploaded_Q;
		break;
	case PROPS_PRIORITY:
		ref = &proppri_Q;
		break;
	case PROPS_CHANGED:
		ref = &propchanged_Q;
		break;
	}

	if (DBFETCH(obj)->nextold == NOTHING || DBFETCH(obj)->prevold == NOTHING)
		return;
	if (!ref || ref->obj == NOTHING)
		return;

	if (DBFETCH(obj)->nextold == obj || DBFETCH(obj)->prevold == obj) {
		if (ref->obj == obj)
			ref->obj = NOTHING;
	} else {
		DBFETCH(DBFETCH(obj)->prevold)->nextold = DBFETCH(obj)->nextold;
		DBFETCH(DBFETCH(obj)->nextold)->prevold = DBFETCH(obj)->prevold;
		if (ref->obj == obj)
			ref->obj = DBFETCH(obj)->nextold;
	}
	DBFETCH(obj)->prevold = NOTHING;
	DBFETCH(obj)->nextold = NOTHING;
	ref->count--;
}

void
addobject_ringqueue(dbref obj, int mode)
{
	struct pload_Q *ref = NULL;

	removeobj_ringqueue(obj);

	DBFETCH(obj)->propsmode = mode;
	switch (mode) {
	case PROPS_UNLOADED:
		DBFETCH(obj)->nextold = NOTHING;
		DBFETCH(obj)->prevold = NOTHING;
		return;
		break;
	case PROPS_LOADED:
		ref = &proploaded_Q;
		break;
	case PROPS_PRIORITY:
		ref = &proppri_Q;
		break;
	case PROPS_CHANGED:
		ref = &propchanged_Q;
		break;
	}

	if (ref->obj == NOTHING) {
		DBFETCH(obj)->nextold = obj;
		DBFETCH(obj)->prevold = obj;
		ref->obj = obj;
	} else {
		DBFETCH(obj)->nextold = ref->obj;
		DBFETCH(DBFETCH(ref->obj)->prevold)->nextold = obj;
		DBFETCH(obj)->prevold = DBFETCH(ref->obj)->prevold;
		DBFETCH(ref->obj)->prevold = obj;
	}
	ref->count++;
}


dbref
first_ringqueue_obj(struct pload_Q *ref)
{
	if (!ref)
		return NOTHING;
	return (ref->obj);
}


dbref
next_ringqueue_obj(struct pload_Q * ref, dbref obj)
{
	if (DBFETCH(obj)->nextold == ref->obj)
		return NOTHING;
	return (DBFETCH(obj)->nextold);
}


#define FETCHSTATS_INTERVAL1 120
#define FETCHSTATS_INTERVAL2 600
#define FETCHSTATS_INTERVAL3 3600

#define FETCHSTATS_SLOT_TIME 30
#define FETCHSTATS_SLOTS ((FETCHSTATS_INTERVAL3 / FETCHSTATS_SLOT_TIME) + 1)

static int fetchstats[FETCHSTATS_SLOTS];
static int lastfetchslot = -1;

void
update_fetchstats(void)
{
	time_t now;
	int slot, i;

	now = time(NULL);
	slot = ((now / FETCHSTATS_SLOT_TIME) % FETCHSTATS_SLOTS);
	if (slot != lastfetchslot) {
		if (lastfetchslot == -1) {
			for (i = 0; i < FETCHSTATS_SLOTS; i++) {
				fetchstats[i] = -1;
			}
		}
		while (lastfetchslot != slot) {
			lastfetchslot = ((lastfetchslot + 1) % FETCHSTATS_SLOTS);
			fetchstats[lastfetchslot] = 0;
		}
	}
	fetchstats[slot] += 1;
}


void
report_fetchstats(dbref player)
{
	int i, count, slot;
	double sum, minv, maxv;
	char buf[BUFFER_LEN];
	time_t now;

	now = time(NULL);
	i = slot = ((now / FETCHSTATS_SLOT_TIME) % FETCHSTATS_SLOTS);

	while (lastfetchslot != slot) {
		lastfetchslot = ((lastfetchslot + 1) % FETCHSTATS_SLOTS);
		fetchstats[lastfetchslot] = 0;
	}

	sum = maxv = 0.0;
	minv = fetchstats[i];
	count = 0;

	for (; count < (FETCHSTATS_INTERVAL1 / FETCHSTATS_SLOT_TIME); count++) {
		if (fetchstats[i] == -1)
			break;
		sum += fetchstats[i];
		if (fetchstats[i] < minv)
			minv = fetchstats[i];
		if (fetchstats[i] > maxv)
			maxv = fetchstats[i];
		i = ((i + FETCHSTATS_SLOTS - 1) % FETCHSTATS_SLOTS);
	}
	snprintf(buf, sizeof(buf), "Disk Fetches %2g minute min/ave/max: %.2f/%.2f/%.2f",
			(FETCHSTATS_INTERVAL1 / 60.0),
			(minv * 60.0 / FETCHSTATS_SLOT_TIME),
			(sum * 60.0 / (FETCHSTATS_SLOT_TIME * count)),
			(maxv * 60.0 / FETCHSTATS_SLOT_TIME));
	notify(player, buf);


	for (; count < (FETCHSTATS_INTERVAL2 / FETCHSTATS_SLOT_TIME); count++) {
		if (fetchstats[i] == -1)
			break;
		sum += fetchstats[i];
		if (fetchstats[i] < minv)
			minv = fetchstats[i];
		if (fetchstats[i] > maxv)
			maxv = fetchstats[i];
		i = ((i + FETCHSTATS_SLOTS - 1) % FETCHSTATS_SLOTS);
	}
	snprintf(buf, sizeof(buf), "Disk Fetches %2g minute min/ave/max: %.2f/%.2f/%.2f",
			(FETCHSTATS_INTERVAL2 / 60.0),
			(minv * 60.0 / FETCHSTATS_SLOT_TIME),
			(sum * 60.0 / (FETCHSTATS_SLOT_TIME * count)),
			(maxv * 60.0 / FETCHSTATS_SLOT_TIME));
	notify(player, buf);


	for (; count < (FETCHSTATS_INTERVAL3 / FETCHSTATS_SLOT_TIME); count++) {
		if (fetchstats[i] == -1)
			break;
		sum += fetchstats[i];
		if (fetchstats[i] < minv)
			minv = fetchstats[i];
		if (fetchstats[i] > maxv)
			maxv = fetchstats[i];
		i = ((i + FETCHSTATS_SLOTS - 1) % FETCHSTATS_SLOTS);
	}
	snprintf(buf, sizeof(buf), "Disk Fetches %2g minute min/ave/max: %.2f/%.2f/%.2f",
			(FETCHSTATS_INTERVAL3 / 60.0),
			(minv * 60.0 / FETCHSTATS_SLOT_TIME),
			(sum * 60.0 / (FETCHSTATS_SLOT_TIME * count)),
			(maxv * 60.0 / FETCHSTATS_SLOT_TIME));
	notify(player, buf);
}


void
report_cachestats(dbref player)
{
	dbref obj;
	int count, total, checked, gap, ipct;
	time_t when, now;
	double pct;
	char buf[BUFFER_LEN];

	notify(player, "LRU proploaded cache time distribution graph.");

	total = proploaded_Q.count;
	checked = 0;
	gap = 0;
	when = now = time(NULL);
	notify(player, "Mins  Objs (%of db) Graph of #objs vs. age.");
	for (; checked < total; when -= 60) {
		count = 0;
		obj = first_ringqueue_obj(&proploaded_Q);
		while (obj != NOTHING) {
			if (DBFETCH(obj)->propstime > (when - 60) && DBFETCH(obj)->propstime <= when)
				count++;
			obj = next_ringqueue_obj(&proploaded_Q, obj);
		}
		checked += count;
		pct = count * 100.0 / total;
		ipct = count * 100 / total;
		if (ipct > 50)
			ipct = 50;
		if (count) {
			if (gap)
				notify(player, "[gap]");
			snprintf(buf, sizeof(buf), "%3ld:%6d (%5.2f%%) %*s", ((now - when) / 60), count, pct, ipct, "*");
			notify(player, buf);
			gap = 0;
		} else {
			gap = 1;
		}
	}
}


void
diskbase_debug(dbref player)
{
	char buf[BUFFER_LEN];
	double ph, pm;

	ph = propcache_hits;
	pm = propcache_misses;
	snprintf(buf, sizeof(buf),
			"Propcache hit ratio: %.3f%% (%ld hits / %ld fetches)",
			(100.0 * ph / (ph + pm)), propcache_hits, propcache_misses);
	notify(player, buf);
	report_fetchstats(player);

	notify_fmt(player, "PropLoaded count: %d", proploaded_Q.count);
	notify_fmt(player, "PropPriority count: %d", proppri_Q.count);
	notify_fmt(player, "PropChanged count: %d", propchanged_Q.count);
	report_cachestats(player);
}


void
unloadprops_with_prejudice(dbref obj)
{
	PropPtr l;

	if ((l = DBFETCH(obj)->properties)) {
		/* if it has props, then dispose */
		delete_proplist(l);
		DBFETCH(obj)->properties = NULL;
	}
	removeobj_ringqueue(obj);
	DBFETCH(obj)->propsmode = PROPS_UNLOADED;
	DBFETCH(obj)->propstime = 0;
}


int
disposeprops_notime(dbref obj)
{
	if (DBFETCH(obj)->propsmode == PROPS_UNLOADED)
		return 0;
	if (DBFETCH(obj)->propsmode == PROPS_CHANGED)
		return 0;

	unloadprops_with_prejudice(obj);
	return 1;
}


int
disposeprops(dbref obj)
{
	if ((time(NULL) - DBFETCH(obj)->propstime) < tp_clean_interval)
		return 0;				/* don't dispose if less than X minutes old */
	return disposeprops_notime(obj);
}


void
dispose_all_oldprops(void)
{
	dbref i;
	time_t now = time(NULL);

	for (i = 0; i < db_top; i++) {
		if ((now - DBFETCH(i)->propstime) >= tp_clean_interval)
			disposeprops_notime(i);
	}
}

void
housecleanprops(void)
{
	int limit, max;
	dbref i, j;

	if ((proploaded_Q.count < 100) ||
		(proploaded_Q.count < (tp_max_loaded_objs * db_top / 100))) return;

	limit = 40;
	max = db_top;
	i = first_ringqueue_obj(&proploaded_Q);
	while (limit > 0 && max-- > 0 && i != NOTHING) {
		j = next_ringqueue_obj(&proploaded_Q, i);
		if (disposeprops_notime(i))
			limit--;
		i = j;
	}
}


int
fetchprops_priority(dbref obj, int mode, const char *pdir)
{
	const char *s;
	int hitflag = 0;

	/* update fetched timestamp */
	DBFETCH(obj)->propstime = time(NULL);

	/* if in memory, don't try to reload. */
	if (DBFETCH(obj)->propsmode != PROPS_UNLOADED) {

		/* but do update the queue position */
		addobject_ringqueue(obj, DBFETCH(obj)->propsmode);
		if (!pdir)
			pdir = "/";
		while ((s = propdir_unloaded(DBFETCH(obj)->properties, pdir))) {
			propcache_misses++;
			hitflag++;
			if (!mode)
				update_fetchstats();
			if (FLAGS(obj) & SAVED_DELTA) {
				getproperties(delta_infile, obj, s);
			} else {
				getproperties(input_file, obj, s);
			}
		}
		if (hitflag) {
			return 1;
		} else {
			propcache_hits++;
			return 0;
		}
	}

	propcache_misses++;

	housecleanprops();

	/* actually load in root properties from the appropriate file */
	if (FLAGS(obj) & SAVED_DELTA) {
		getproperties(delta_infile, obj, "/");
	} else {
		getproperties(input_file, obj, "/");
	}

	/* update fetch statistics */
	if (!mode)
		update_fetchstats();

	/* add object to appropriate queue */
	addobject_ringqueue(obj, ((mode) ? PROPS_PRIORITY : PROPS_LOADED));

	return 1;
}


void
fetchprops(dbref obj, const char *pdir)
{
	fetchprops_priority(obj, 0, pdir);
}


void
dirtyprops(dbref obj)
{
	if (DBFETCH(obj)->propsmode == PROPS_CHANGED)
		return;

	addobject_ringqueue(obj, PROPS_CHANGED);
}

void
undirtyprops(dbref obj)
{
	if (DBFETCH(obj)->propsmode == PROPS_UNLOADED)
		return;

	if (DBFETCH(obj)->propsmode != PROPS_CHANGED) {
		disposeprops(obj);
		return;
	}

	addobject_ringqueue(obj, PROPS_LOADED);
	disposeprops(obj);
}


int
propfetch(dbref obj, PropPtr p)
{
	FILE *f;

	if (!p)
		return 0;
	SetPFlags(p, (PropFlags(p) | PROP_TOUCHED));
	if (PropFlags(p) & PROP_ISUNLOADED) {
		f = (FLAGS(obj) & SAVED_DELTA) ? delta_infile : input_file;
		db_get_single_prop(f, obj, (long) PropDataVal(p), p, NULL);
		return 1;
	}
	return 0;
}

#endif							/* DISKBASE */