예제 #1
0
파일: deltas.c 프로젝트: BygoneWorlds/anet
/*----------------------------------------------------------------------
 Callback for remote player tables.  Only used if application requested
 object deltas for this session's players.
----------------------------------------------------------------------*/
int dp_PASCAL dp_rplayers_cb(dptab_t *dptab, dptab_table_t *table, playerHdl_t src, playerHdl_t dest, char *subkey, int subkeylen, void *buf, size_t sent, size_t total, int seconds_left, void *context, dp_result_t status)
{
	dp_t *dp = (dp_t *)context;
	dp_playerId_t player;
	dp_result_t err;
	dpid_t id;
	size_t len_used;

	/* current table handled specially in dp_player_notify */
	if (table == dp->players)
		return 0;

	memset(&player, 0, sizeof(player));
	id = (dpid_t) dpMAKESHORT(subkey[0], subkey[1]);
	len_used = dp_unpack_playerId(id, buf, &player);
	if (total != len_used) {
		DPRINT(("dp_rplayers_cb: err: can't unpack player %d (%d != %d).\n", id, total, len_used));
		dp_assertValid(dp);
		return dp_RES_BUG;
	}
	DPRINT(("dp_rplayers_cb: status %d, name %s\n", status, player.name));

	/* Inform caller via local message, if desired */
	err = dpSendObjectDelta(dp, status, (dp_object_t *) &player, table, subkey, subkeylen);
	if (err != dp_RES_OK) {
		DPRINT(("dp_rplayers_cb: can't send object delta, err:%d\n", err));
		return err;
	}

	return 0;
}
예제 #2
0
파일: dpscore.c 프로젝트: BygoneWorlds/anet
/*--------------------------------------------------------------------------
 Retrieve a player's dpid.
--------------------------------------------------------------------------*/
dpid_t dpGetMyId(dp_t *dp)
{
	dp_result_t err;
	void *buf;
	size_t len;
	char subkey[dptab_KEY_MAXLEN];
	int subkeylen;
	
	err = dptab_get_byindex(dp->myplayers, 0, &buf, &len, subkey, &subkeylen);
	if (err != dp_RES_OK) {
		DPRINT(("dpGetMyId: no player created yet?\n"));
		return dp_ID_NONE;
	}
	return (dpid_t)dpMAKESHORT(subkey[0], subkey[1]);
}
예제 #3
0
int main(int argc, char **argv)
{
	sbdclnt_t *sbdclnt;
	int port;
	int clicked;
	clock_t timeout;
	aeh_t aeh;
	int ninst;
	char comments[sendcrsh_COMMENT_MAXLEN];
	int len, lensent;
	char buf[sbd_MAXLEN];
	int pos;
	unsigned long crc;
	char signature[10];
	int i = 0;

	logprint_setFile("sendcrsh.log");

	if (!checkEnvironment()) {
		DPRINT(("anet2.dll did not set our environment!\n"));
		return 1;
	}
	if (!checkUnique()) {
		DPRINT(("Another instance of sendcrsh was running!\n"));
		return 1;
	}

	if (argc <= 3) { /* Usage: sendcrsh <host> <port> <hexdata> */
		DPRINT(("sendcrsh invoked with fewer than 3 arguments\n"));
		return 1;
	}
	DPRINT(("%s %s %s %s\n", argv[0], argv[1], argv[2], argv[3]));

	port = atoi(argv[2]);
	if ((port < 1024) || (port > 65536)) {
		DPRINT(("sendcrsh invoked with invalid port %d\n", port));
		return 1;
	}

	if (NULL == hex2buf(argv[3], buf, 2)) {
		DPRINT(("hex2buf(datalen) failed\n"));
		return 1;
	}
	len = dpMAKESHORT(buf[0], buf[1]);
	if (NULL == hex2buf(argv[3] + 4, buf, len)) {
		DPRINT(("hex2buf(data) failed\n"));
		return 1;
	}
	if (argv[3][4 + 2*len] != '\0') {
		DPRINT(("data does not end at datalen:%d\n", len));
		return 1;
	}
	if (len > sbd_MAXLEN) {
		DPRINT(("datalen:%d is greater than max:%d\n", len, sbd_MAXLEN));
		return 1;
	}
	pos = getLastRecord(buf, len, &aeh, &ninst);
	if (pos == -1) {
		DPRINT(("getLastRecord failed\n"));
		return 1;
	}
	if (aeh.nstk == 0) {
		DPRINT(("exception has 0 length stack trace\n"));
	}
	crc = aeh_getSignature(&aeh);
	if (crc == 0) {
		DPRINT(("aeh_getSignature returns 0 crc, error?\n"));
	}
	aeh_signature_toString(crc, signature);

	DPRINT(("%s to %s:%d len:%d data:%s\n", argv[0], argv[1], port, len,
		argv[3] + 4));
	sendbox_create(winCmdShow, signature, sendcrsh_COMMENT_MAXLEN, 120);
	while (1) {
		clock_t now = eclock();

		clicked = sendbox_poll(comments);
		if (clicked != sendbox_CLICKED_NONE) {
			DPRINT(("t:%d, clicked:%d, breaking\n", now, clicked));
			break;
		}
		Sleep(100);
		if (!((++i)%10)) {
			DPRINT(("%dth call to sendbox_poll at t:%d\n", i, now));
		}
	}
	sendbox_destroy();

	if (clicked != sendbox_CLICKED_OK) {
		DPRINT(("User cancelled send, clicked:%d\n", clicked));
		return 0;
	}

	timeout = eclock() + 30 * ECLOCKS_PER_SEC;
	/* add the comments to the buffer */
	if (comments[0]) {
		aeh_buf_t aehbuf;
		int commentlen = strlen(comments);
		int nwritten;

		if ((len + sizeof(unsigned int /* aeh_info_t.id */) + sizeof(commentlen) + commentlen) > sbd_MAXLEN) {
			DPRINT(("no room in buf for comment\n"));
			return 1;
		}
		/* stuff the comment into the info list */
		if (aeh_RES_OK != aeh_addComment(&aeh, comments)) {
			DPRINT(("aeh_addComment failed\n"));
			return 1;
		}
		/* convert it back to a buf */
		if (aeh_RES_OK != aeh_writeOutputStream(&aeh, &aehbuf)) {
			DPRINT(("can't convert aeh back to aehbuf\n"));
			return 1;
		}
		DPRINT(("writing new record at pos:%d buf:\n", pos));
		dumpbuf(aehbuf.buf, aehbuf.buflen);
		nwritten = aehlog_writetobuf(&aehbuf, ninst, buf+pos, sbd_MAXLEN-pos);
		if (-1 == nwritten) {
			DPRINT(("can't convert aehbuf back to buf\n"));
			return 1;
		}
		len = pos + nwritten;
	}
	aeh_Destroy(&aeh);

	/* send the buffer */
	sbdclnt = sbdclnt_create(buf, len, argv[1], (unsigned short)port);
	if (sbdclnt == NULL) {
		DPRINT(("sbdclnt_create failed\n"));
		return 1;
	}
	while ((long)(eclock() - timeout) < 0) {
		lensent = sbdclnt_poll(sbdclnt);
		if (lensent != 0)
			break;
		Sleep(100);
	}
	sbdclnt_destroy(sbdclnt);
	if (lensent != len) {
		DPRINT(("send only %d of %d bytes!\n", lensent, len));
		return 1;
	}
	DPRINT(("send completed successfully\n"));
	return 0;
}
예제 #4
0
int main()
{
	dp_result_t err;
	time_t now, t_last, t_start = time(NULL);
	wmq_t *wmq;
	wmq_record_t record;
	char buf[1024];
	char *pbuf;
	unsigned short len;
	time_t data;
	long offset_old = 0;
	time_t t_old = t_start;

	record.buf = buf;
	record.buflen = 1024;

	wmq = wmq_create("wmq", 0);
	assert(wmq);

	now = time(0);
	err = wmq_seek(wmq, now, 0);  /* seek to beginning of today */
	assert(!err);

	while (1) {
		char *pbuf = buf;
		char sessid[8];  /* inet only: inetadr(4) port(2) karma(2) */
		dp_species_t sessType;
		dp_uid_t uid;
		dpid_t id;
		scorerep_t *rep;
		scorerep_buf_t repbuf;
		scorerep_player_t *player;
		int score;
		int won;

		err = wmq_get(wmq, &record);
		assert(err == dp_RES_OK || err == dp_RES_EMPTY);

		if (err != dp_RES_OK)
			continue;
		DPRINT(("wmq2mysql: read %d bytes, time:%08x: %s\n", record.datalen, wmq->t, hexstring(record.buf, record.datalen)));
		if (record.datalen < 12) {
			DPRINT(("wmq2mysql: len %d is not big enough for a header!\n", record.datalen));
			exit(1);
		}
		memcpy(sessid, pbuf, 8);
		pbuf += 8;
		sessType = dpMAKESHORT(pbuf[0], pbuf[1]);
		pbuf += 2;
		repbuf.len = dpMAKESHORT(pbuf[0], pbuf[1]);
		pbuf += 2;
		assert(repbuf.len > 0 && repbuf.len < scorerep_MAX_BUFLEN);
		memcpy(repbuf.buf, pbuf, repbuf.len);

		rep = scorerep_create();
		if (rep == NULL) {
			DPRINT(("wmq2mysql: can't create scorerep\n"));
			exit(1);
		}
		err = scorerep_fromBuf(rep, &repbuf);
		if (err != dp_RES_OK) {
			DPRINT(("wmq2mysql: scorerep_fromBuf returns err:%d\n", err));
			exit(1);
		}
		if (!(rep->flags & scorerep_FLAGS_SELFEXIT)) {
			DPRINT(("wmq2mysql: ignoring non-SELFEXIT score\n"));
			continue;
		}
		uid = rep->uid;
		id = rep->id;
		player = (scorerep_player_t *)assoctab_subscript(rep->players, id);
		if (!player) {
			DPRINT(("wmq2mysql: no entry for self (id:%d) in SELFEXIT!\n", id));
			exit(1);
		}
		assert(uid == player->uid);
		DPRINT(("wmq2mysql: read sessType:%d uid:%d (id:%d)\n", sessType, uid, id));
		if (player->bloblen < 3) {
			DPRINT(("wmq2mysql: report has bloblen:%d, no room for standard score + win byte\n", player->bloblen));
			continue;
		}
		score = dpMAKESHORT(player->blob[0], player->blob[1]);
		won = (int)player->blob[2];
		DPRINT(("wmq2mysql: read score:%d won:%d\n", score, won));

	}
	wmq_destroy(wmq);
	return 0;
}
예제 #5
0
int scorerep_test(void)
{
	dp_result_t err;
	dp_uid_t myUID;
	dpid_t myId;
	dp_uid_t hisUID;
	dpid_t hisId;
	scorerep_t *rep;
	scorerep_buf_t repbuf;
	scorerep_player_t *player;
	unsigned short bloblen;
	char blob[scorerep_MAX_BLOBLEN];

	/* create a rep */
	rep = scorerep_create();
	assert(rep);

	/* set my id and uid */
	myId = 1;
	myUID = (long) myId + 10000;
	err = scorerep_setSelf(rep, myId, myUID);
	assert(!err);
	assert(rep->id == myId);
	assert(rep->uid == myUID);
	
	/* fill my score report with a few blobs */
	for (hisId = myId; hisId < myId + 10; hisId++) {
		hisUID = (long) hisId + 10000;
		blob[0] = dpGETSHORT_FIRSTBYTE(hisId);
		blob[1] = dpGETSHORT_SECONDBYTE(hisId);
		blob[2] = dpGETLONG_FIRSTBYTE(hisUID);
		blob[3] = dpGETLONG_SECONDBYTE(hisUID);
		blob[4] = dpGETLONG_THIRDBYTE(hisUID);
		blob[5] = dpGETLONG_FOURTHBYTE(hisUID);
		bloblen = 6;
		err = scorerep_set(rep, hisId, hisUID, 0, blob, bloblen);
		assert(!err);
	}
	
	/* convert it to a buffer with everyone's scores */
	err = scorerep_toBuf(rep, scorerep_FLAGS_SELFEXIT, myId, &repbuf);
	assert(!err);
	
	/* destroy the rep */
	scorerep_destroy(rep);
	rep = NULL;

	/* create a new rep */
	rep = scorerep_create();
	assert(rep);

	/* set my id and uid */
	myId = 1;
	myUID = (long) myId + 10000;
	err = scorerep_setSelf(rep, myId, myUID);
	assert(!err);
	assert(rep->id == myId);
	assert(rep->uid == myUID);
	
	/* read the buffer into my report table */
	err = scorerep_fromBuf(rep, &repbuf);
	assert(!err);
	assert(rep->players);
	assert(rep->flags == scorerep_FLAGS_SELFEXIT);
	
	/* check that it contains what I put there and only that */
	assert(rep->players->n_used = 10);
	for (hisId = myId; hisId < myId + 10; hisId++) {
		player = (scorerep_player_t *)assoctab_subscript(rep->players, hisId);
		assert(player);
		hisUID = (long) hisId + 10000;
		assert(player->uid = hisUID);
		assert(player->bloblen == 6);
		assert(hisId == dpMAKESHORT(player->blob[0], player->blob[1]));
		assert(hisUID == dpMAKELONG(player->blob[2], player->blob[3], player->blob[4], player->blob[5]));
	}
	
	/* convert it to a buffer with only my and his scores */
	hisId = myId + 1;
	hisUID = (long) hisId + 10000;
	memset(&repbuf, 0, sizeof(repbuf));
	err = scorerep_toBuf(rep, 0, hisId, &repbuf);
	assert(!err);
	
	/* destroy the rep */
	scorerep_destroy(rep);
	rep = NULL;

	/* create a new rep */
	rep = scorerep_create();
	assert(rep);

	/* set my id and uid */
	myId = 1;
	myUID = (long) myId + 10000;
	err = scorerep_setSelf(rep, myId, myUID);
	assert(!err);
	assert(rep->id == myId);
	assert(rep->uid == myUID);

	/* read the buffer into my report table */
	err = scorerep_fromBuf(rep, &repbuf);
	assert(!err);
	assert(rep->players);
	assert(rep->flags == 0);

	/* check that it contains what I put there and only that */
	assert(rep->players->n_used = 2);
	player = (scorerep_player_t *)assoctab_subscript(rep->players, myId);
	assert(player);
	assert(player->uid = myUID);
	assert(player->bloblen == 6);
	assert(myId == dpMAKESHORT(player->blob[0], player->blob[1]));
	assert(myUID == dpMAKELONG(player->blob[2], player->blob[3], player->blob[4], player->blob[5]));

	hisId = myId + 1;
	hisUID = (long) hisId + 10000;
	player = (scorerep_player_t *)assoctab_subscript(rep->players, hisId);
	assert(player);
	assert(player->uid = hisUID);
	assert(player->bloblen == 6);
	assert(hisId == dpMAKESHORT(player->blob[0], player->blob[1]));
	assert(hisUID == dpMAKELONG(player->blob[2], player->blob[3], player->blob[4], player->blob[5]));

	/* destroy the rep */
	scorerep_destroy(rep);
	rep = NULL;
	
	return 0;
}
예제 #6
0
파일: sbdserv.c 프로젝트: BygoneWorlds/anet
/*------------------------------------------------------------------------
 Read data from handle h.
 Handle must be set non-blocking and have data waiting.
 Returns 0 if data read successfully, but more is expected,
 		 -1 on error caller should close the handle and call handleClosed,
 On completion, the length read is returned and *bufp is set to the
 address of the buffer containing the data received from that handle.
------------------------------------------------------------------------*/
static int sbdserv_readData(sbdserv_t *sbdserv, int h, char **bufp)
{
	int nRecvd;
	int nToRecv;
	sbdserv_conn_t *pc;

	*bufp = NULL;
	if (!sbdserv || (h == -1))
		return -1;
	pc = (sbdserv_conn_t *)assoctab_subscript(sbdserv->conns, h);
	if (!pc) {
		DPRINT(("sbdserv_readData: no such handle h:%d\n", h));
		return -1;
	}
	nToRecv = sbd_HEADERLEN + sbd_MAXLEN - pc->pos;
	if (nToRecv <= 0) {
		DPRINT(("sbdserv_readData: bug: h:%d tried to read %dB (pos:%d)\n", h, nToRecv, pc->pos));
		assert(nToRecv > 0);
	}
	errno = 0;
	nRecvd = recv(h, pc->buf + pc->pos, nToRecv, 0);
#ifdef VERBOSE
	DPRINT(("sbdserv_readData: recv(h:%d, pos:%d, n:%d) returns %d (errorr:%d)\n", h, pc->pos, nToRecv, nRecvd, errno));
#endif
	if (nRecvd <= 0) {
		if ((nRecvd == 0) || (errno == ENOTCONN)) {
			DPRINT(("sbdserv_readData: h:%d closed connection\n", h));
			return -1;	/* error - connection closed from other end */
		} else if (errno == EWOULDBLOCK) {
			DPRINT(("sbdserv_readData: no data for h:%d\n", h));
			return 0;	/* error - selected, but no data. */
		} else if (errno == EINTR) {
			DPRINT(("sbdserv_readData: h:%d recv interupted by signal\n", h));
			return -1;
		}
		DPRINT(("sbdserv_readData: read error:%d on h:%d\n", errno, h));
		return -1;	/* error - read error */
	}
#ifdef VERBOSE
	DPRINT(("sbdserv_readData: recv'd %dB from h:%d:%s\n", nRecvd, h, hexstring(pc->buf + pc->pos, nRecvd)));
#endif
	pc->pos += nRecvd;
	if (!pc->len) {
		if (pc->pos < sbd_HEADERLEN)
			return 0;	/* still waiting for header bytes */
		if (memcmp(pc->buf, sbd_MAGIC, 2)) {
			DPRINT(("sbdserv_readData: magic doesn't match for h:%d\n", h));
#ifdef VERBOSE
			DPRINT(("sbdserv_readData: %s\n", hexstring(pc->buf, pc->pos)));
#endif
			return -1;	/* error - magic doesn't match */
		}
		pc->len = dpMAKESHORT(pc->buf[2], pc->buf[3]);
		if ((pc->len == 0) || (pc->len > sbd_MAXLEN)) {
			DPRINT(("sbdserv_readData: len:%d out of range for h:%d\n", pc->len, h));
			pc->len = 0;
			return -1;	/* error - len out of range */
		}
	}
	if (pc->pos > sbd_HEADERLEN + pc->len) {
		DPRINT(("sbdserv_readData: recd more than len:%d data from h:%d\n", pc->len, h));
		return -1;	/* error - received more than len data */
	} else if (pc->pos == sbd_HEADERLEN + pc->len) {
		*bufp = pc->buf + sbd_HEADERLEN;
		return pc->len;
	}
	return 0;	/* still waiting for data */
}
예제 #7
0
파일: deltas.c 프로젝트: BygoneWorlds/anet
/*----------------------------------------------------------------------
 A change happened in one of the tables the user was monitoring.
 Inform them.
----------------------------------------------------------------------*/
dp_result_t dpSendObjectDelta(
	dp_t *dp,
	dp_result_t status,
	dp_object_t *data,
	dptab_table_t *tab,
	char *subkey,
	int subkeylen)
{
	playerHdl_t dest;
	size_t pktlen;
	size_t datalen;
	dp_result_t err;
	struct {
		dp_packetType_t   tag PACK;
 		dp_objectDelta_packet_t body PACK;
	} pkt;

	precondition(dp);
	precondition(data);
	precondition(tab);
	precondition(subkeylen);

	dp_assertValid(dp);

	memset(&pkt, 0, sizeof(pkt));

	/* DPRINT(("dpSendObjectDeltaPacket: status e:%d, key %s, subkey %s, name ",status, key2a(tab->key, tab->keylen), key2a2(subkey, subkeylen))); */
	DPRINT(("dpSendObjectDeltaPacket: status e:%d, ",status));
	switch(tab->key[0]) {
	case dp_KEY_PLAYERS: {
		if (tab == dp->players) {
			dpid_t id = (dpid_t) dpMAKESHORT(subkey[0], subkey[1]);
			if (dp->monitor_player_latencies) {
				playerHdl_t h = dpid2commHdl(dp, id);

				if (h != PLAYER_NONE) {
					/* Stuff latency into delta for players in our session */
					int		loss;
					int		latency;

					latency = dpio_get_player_latency(dp->dpio, h, 0, &loss);
					if (latency > 32767)
						latency = 32767;

					pkt.body.latency = latency;
					pkt.body.pktloss = loss;
				}
			}

			pkt.body.flags |= dp_OBJECTDELTA_FLAG_INOPENSESS;
			if (id >= dp->firstId && id < dp->firstId + dp_PLAYERS_PER_HOST)
				pkt.body.flags |= dp_OBJECTDELTA_FLAG_LOCAL;
			if (dp_commHdl2dpid(dp, dp->hMaster) == id) 
				pkt.body.flags |= dp_OBJECTDELTA_FLAG_ISHOST;
		}
		datalen = sizeof(dp_playerId_t);
		DPRINT(("playername:%s ping:%d loss:%d", data->p.name, pkt.body.latency, pkt.body.pktloss));
		break; 
	}
	case dp_KEY_SESSIONS:
		if (dp->players && !memcmp(data->sess.reserved2, dp->sess_subkey, dp->sess_subkeylen))
			pkt.body.flags |= dp_OBJECTDELTA_FLAG_INOPENSESS;
		if (!memcmp(data->sess.adrMaster, dp->dpio->myAdr, dp->dpio->myAdrLen))
			pkt.body.flags |= dp_OBJECTDELTA_FLAG_LOCAL;
		datalen = sizeof(dp_session_t); 
		DPRINT(("sessname %s, flags %x", data->sess.sessionName, data->sess.flags));
		break; 
	case dp_KEY_SERVERPINGS:
		datalen = sizeof(dp_serverInfo_t);
		DPRINT(("hostname %s", data->serv.hostname));
		break; 
	case dp_KEY_SCORES:
		datalen = sizeof(data->score.nScoreTypes) 
			+ data->score.nScoreTypes * ( sizeof(data->score.scoreIds[0]) + 
										  sizeof(data->score.scores[0]) );
		DPRINT(("uid %d", dpMAKELONG(subkey[0], subkey[1], subkey[2], subkey[3])));
		break;
	default:
		return dp_RES_BAD;
	}
	DPRINT(("\n"));

	pkt.tag = dp_OBJECTDELTA_PACKET_ID;
	pkt.body.status = status;
	pkt.body.keylen = tab->keylen;
	memcpy(pkt.body.key, tab->key, tab->keylen);
	pkt.body.subkeylen = subkeylen;
	memcpy(pkt.body.subkey, subkey, subkeylen);
	memcpy(&pkt.body.data, data, datalen);

	dest = PLAYER_ME;
	pktlen = sizeof(pkt) - sizeof(pkt.body.data) + datalen;

	err = dpio_put_reliable(dp->dpio, &dest, 1, &pkt, pktlen, NULL);
	dp_assertValid(dp);
	return err;
}
예제 #8
0
파일: deltas.c 프로젝트: BygoneWorlds/anet
/*------------------------------------------------------------------------
 Start or stop monitoring the given object table for changes.
 Initially, and on any change to the given table, a dp_objectDelta_packet_t
 is generated and placed in a queue for retrieval with dpReceive.
 See anet.h for the definition of dp_objectDelta_packet_t.

 When called with monitor=TRUE, a burst of messages are generated giving 
 the initial contents of the table.
 When called with monitor=FALSE, no more messages of that sort will
 be generated, although there may still be some in the queue; you can
 call dpReceive until it returns empty to flush these out if desired.

 The key argument is a variable-length binary string that indicates
 what objects to start or stop monitoring.

 To start or stop monitoring sessions, use 
	keylen=1, key[0] = dp_KEY_SESSIONS
 To stop monitoring servers, use 
	keylen=1, key[0] = dp_KEY_SERVERPINGS,
 To start monitoring servers, use 
 	keylen=3;
	key[0] = dp_KEY_SERVERPINGS,
	key[1] = (char) dpGETSHORT_FIRSTBYTE(sessiontype);
	key[2] = (char) dpGETSHORT_SECONDBYTE(sessiontype);
 To monitor players, use
    char key[dp_MAX_KEYLEN+1];
	key[0] = dp_KEY_PLAYERS;
	dpGetSessionId(dp, &sess, &key[1], &keylen);
	keylen++;
 To request that latencies be included in player deltas for the current 
	session, use
	keylen = 1, key[0] = dp_KEY_PLAYER_LATENCIES;
	The latency in milliseconds will be placed in the latency field of
	the object delta packet.
	The frequency of latency measurements is influenced by the intervals
	set with dpSetPingIntervals.
	Deltas are synthesized locally and do not load the network.

 Note: keylen will be dp_MAX_KEYLEN+1 when requesting player deltas
 in IPX sessions!
------------------------------------------------------------------------*/
DP_API dp_result_t DP_APIX dpRequestObjectDeltas(
	dp_t *dp,
	int monitor,		/* TRUE to start, FALSE to stop */
	const char *key,
	int keylen)
{
	dp_result_t err;
	precondition(dp);
	precondition(key);
	precondition((keylen > 0) && (keylen <= dp_KEY_MAXLEN+1));

	DPRINT(("dpRequestObjectDeltas(,%d,%s)\n",
			monitor, key2a(key, keylen)));

	switch (key[0]) {
	case dp_KEY_SESSIONS:
		if (keylen != 1) {
			DPRINT(("dpRequestObjectDeltas: sessions: can't specify session type yet\n"));
			return dp_RES_BAD;
		}
		if (dp->monitor_object_sessions && monitor) 
			return dp_RES_ALREADY;
		/* Cause dp_sessions_cb to generate new messages */
		dp->monitor_object_sessions = monitor;
		if (monitor) {
			dptab_table_t *sesstab;
			int i;

			/* enumerate the existing sessions */
			/* If we're a standalone master, enum the mysessions table */
			if (bIAmStandaloneMaster(dp))
				sesstab = dp->mysessions;
			else
				sesstab = dp->sessions;
			DPRINT(("dpRequestObjectDeltas: table size %d\n", dptab_tableSize(sesstab)));
			for (i=0; i<dptab_tableSize(sesstab); i++) {
				char *s;
				dp_session_t sess;
				size_t len;
				char subkey[dptab_KEY_MAXLEN];
				int subkeylen;

				err = dptab_get_byindex(sesstab, i, (void **)&s, &len, subkey, &subkeylen);
				if (err != dp_RES_OK) {
					DPRINT(("dpRequestObjectDeltas: bug?\n"));
					dp_assertValid(dp);
					break;
				}
				/* Must unpack when retrieving records from dptab */
				dp_unpack_session(dp, subkey, subkeylen, s, len, &sess);
				err = dpSendObjectDelta(dp, dp_RES_CREATED, (dp_object_t *) &sess, sesstab, subkey, subkeylen);
				if (err != dp_RES_OK) {
					DPRINT(("dpRequestObjectDeltas: can't send object delta, err:%d\n", err));
				}
			}
		}
		return dp_RES_OK;
		break;

	case dp_KEY_SERVERPINGS:
		if (dp->monitor_object_servers && monitor) return dp_RES_ALREADY;
		/* Cause dpHandleServerPingResponsePacket to generate new messages */
		dp->monitor_object_servers = monitor;
		dp->serverping_interval = dp->dpio->clocksPerSec;
		dp->next_serverping = dp->now;
		dp->serverping_rx_count = 0;
		dp->serverping_rx_count_old = 0;
		if (monitor) {
			int i;

			if (keylen != 3) {
				DPRINT(("dpRequestObjectDeltas: must specify session type.\n"));
				return dp_RES_BAD;
			}
			dp->monitor_object_servers_sessType = dpMAKESHORT(key[1], key[2]);
			DPRINT(("dpRequestObjectDeltas: servers_sessType %d\n",dp->monitor_object_servers_sessType));
			
			/* enumerate the existing servers */
			for (i=0; i<dptab_tableSize(dp->serverpings); i++) {
				dp_serverInfo_t *server;
				size_t len;
				char subkey[dptab_KEY_MAXLEN];
				int subkeylen;

				err = dptab_get_byindex(dp->serverpings, i, (void **)&server, &len, subkey, &subkeylen);
				if (err != dp_RES_OK) {
					DPRINT(("dpRequestObjectDeltas: bug?\n"));
					dp_assertValid(dp);
					break;
				}
				err = dpSendObjectDelta(dp, dp_RES_CREATED, (dp_object_t *) server, dp->serverpings, subkey, subkeylen);
				if (err != dp_RES_OK) {
					DPRINT(("dpRequestObjectDeltas: can't send object delta, err:%d\n", err));
				}
			}
		}
		return dp_RES_OK;
		break;

	case dp_KEY_PLAYERS: 
		{
		dp_session_t sDesc;
		void *rplayers_context;
		dp_rplayers_enumEx_context_t rpecontext;
		size_t slen;
		dptab_table_t *rplayers;
		char pkey[3];
		int pkeylen;

		precondition(keylen == dp->dpio->myAdrLen + 3);
		pkeylen = 0;
		pkey[pkeylen++] = dp_KEY_PLAYERS;
		pkey[pkeylen++] = key[dp->dpio->myAdrLen+1];
		pkey[pkeylen++] = key[dp->dpio->myAdrLen+2];
		rplayers_context = NULL;
		if ((rplayers = dptab_getTable(dp->dt, pkey, pkeylen)))
			rplayers_context = dptab_getTableContext(rplayers, dp_rplayers_cb);
		if (!monitor) {
			/* caller no longer wants object delta messages for this table */
			if (!rplayers_context)
				return dp_RES_ALREADY;
			dptab_clearTableCallback(rplayers, dp_rplayers_cb);
			/* bug: should cancel subscription if remote */
			return dp_RES_OK;
		}
		/* caller wants to start getting object delta messages for this table */
		if (rplayers_context)
			return dp_RES_ALREADY;
		slen = sizeof(sDesc);
		err = dpGetSessionDescById(dp, key+1, &sDesc, &slen);
		if (err != dp_RES_OK) {
			DPRINT(("dpRequestObjectDeltas: dpGetSessionById returns %d\n", err));
			return err;
		}
		rpecontext.dp = dp;
		rpecontext.tab = rplayers;
		err = dpEnumPlayersEx(dp, &sDesc, dp_rplayers_enumEx_cb, &rpecontext, 0);
		if (err != dp_RES_OK) {
			DPRINT(("dpRequestObjectDeltas: dpEnumPlayers returns %d\n", err));
			return err;
		}
		rplayers = dptab_getTable(dp->dt, pkey, pkeylen);
		assert(rplayers);
		err = dptab_setTableCallback(rplayers, dp_rplayers_cb, dp);
		if (err != dp_RES_OK) {
			DPRINT(("dpRequestObjectDeltas: dptab_setTableCallback returns %d\n", err));
			return err;
		}
		return dp_RES_OK;
		}
		break;

	case dp_KEY_PLAYER_LATENCIES:
		if (keylen != 1) {
			DPRINT(("dpRequestObjectDeltas: player_latencies: bad keylen:%d != 1\n", keylen));
			return dp_RES_BAD;
		}
		dp->monitor_player_latencies = (monitor) ? TRUE : FALSE;
		return dp_RES_OK;
		break;
		
	case dp_KEY_SCORES:
		/* For the moment, only allow the default session type */
		if (keylen != 1) {
			DPRINT(("dpRequestObjectDeltas: scores: can't specify session type yet\n"));
			return dp_RES_BAD;
		}
		err = dpscore_client_subscribe(dp, dp->defaultSessionType);
		if (err != dp_RES_OK) {
			DPRINT(("dpRequestObjectDeltas: dpscore_client_subscribe returns err:%d\n", err));
			return dp_RES_BUG;
		}
		return dp_RES_OK;
		break; 
	default:
		;
	}

	DPRINT(("dpRequestObjectDeltas: unknown request\n"));
	return dp_RES_BAD;
}