/*---------------------------------------------------------------------- Callback to handle score reports from client. ----------------------------------------------------------------------*/ static int dp_PASCAL scores_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_result_t err; scorerep_buf_t sbuf; scorerep_t *srep; dp_uid_t uid; char sessid[dptab_KEY_MAXLEN]; int sessidlen; dp_species_t sessType; if (!dp || !buf || (status != dp_RES_CREATED)) return 0; DPRINT(("scores_cb: new score report; size %d\n", total)); /* Look up uid of handle reporting score */ uid = tserv_hdl2uid(dp->tserv, src); if (uid == dp_UID_NONE) { DPRINT(("scores_cb: can't map h:%x to uid\n", src)); return 0; } /* Get id and type of session that uid most recently tried to join */ err = dp_uid2sessid(dp, uid, sessid, &sessidlen, &sessType); if (err != dp_RES_OK) { DPRINT(("scores_cb: can't map uid:%d to session\n", uid)); return 0; } /* Write out incoming score record to web message queue, * preceded by header: * char sessid[8]; // inetadr + port + karma * dp_karma_t sessType; * unsigned short bloblen; */ { char wmqbuf[1024]; int wmqbuflen; memcpy(wmqbuf, sessid, sessidlen); wmqbuflen = sessidlen; wmqbuf[wmqbuflen++] = dpGETSHORT_FIRSTBYTE(sessType); wmqbuf[wmqbuflen++] = dpGETSHORT_SECONDBYTE(sessType); wmqbuf[wmqbuflen++] = dpGETSHORT_FIRSTBYTE(total); wmqbuf[wmqbuflen++] = dpGETSHORT_SECONDBYTE(total); assert(wmqbuflen + total < sizeof(wmqbuf)); memcpy(wmqbuf + wmqbuflen, buf, total); wmqbuflen += total; err = wmq_put(wmq, time(NULL), wmq_RECORDTAG_SCORE, wmqbuf, wmqbuflen); if (err != dp_RES_OK) DPRINT(("scores_cb: wmq_put returns err:%d\n", err)); assert(err == dp_RES_OK); } /* Delete the raw score report! Causes another callback here, ignored. */ dptab_delete(dptab, table, subkey, subkeylen); return 0; }
/*-------------------------------------------------------------------------- Call this (from the player table callback dp_players_cb()) whenever a player leaves the session. Sends a score record for the given player to the game server. If the player is ourselves, also sends scores for all other players. Return: dp_RES_BAD if score table doesn't exist or can't be created dp_RES_EMPTY if there are no score records found otherwise return status of dptab_addSubscriber(), dptab_set() --------------------------------------------------------------------------*/ dp_result_t dpscore_client_playerLeaving(dp_t *dp, dpid_t id) { dp_result_t err = dp_RES_OK; char subkey[dptab_KEY_MAXLEN]; int subkeylen = 0; int flags; dpid_t firstId; scorerep_buf_t sbuf; precondition(dp); DPRINT(("dpscore_client_playerLeaving: id:%d\n", id)); ASSERTMEM(); if (!dp->myscoretab) { DPRINT(("dpscore_client_playerLeaving: no myscorestab\n")); return dp_RES_BUG; } if (dp->scorerep) { /* If (local player), set SELFEXIT to show we're leaving */ /* We care because when local player leaves, we report on everyone */ /* Use same code as dpEnumPlayers to detect local player */ firstId = (dpid_t) (id & ~(dp_PLAYERS_PER_HOST-1)); flags = 0; if (firstId == dp->firstId) flags = scorerep_FLAGS_SELFEXIT; /* Grab the appropriate score data and put it into a buffer */ err = scorerep_toBuf(dp->scorerep, flags, id, &sbuf); ASSERTMEM(); if (err != dp_RES_OK) { DPRINT(("dpscore_client_playerLeaving: can't convert to buf\n")); return dp_RES_BUG; } /* The score report key is the session id plus this player's dpid plus * the id of the player who is leaving. */ assert(dp->sess_subkeylen + 4 <= dptab_KEY_MAXLEN); memcpy(subkey, dp->sess_subkey, dp->sess_subkeylen); ASSERTMEM(); subkeylen = dp->sess_subkeylen; subkey[subkeylen++] = dpGETSHORT_FIRSTBYTE(dp->firstId); /* KLUDGE */ subkey[subkeylen++] = dpGETSHORT_SECONDBYTE(dp->firstId); subkey[subkeylen++] = dpGETSHORT_FIRSTBYTE(id); subkey[subkeylen++] = dpGETSHORT_SECONDBYTE(id); assert(subkeylen <= dptab_KEY_MAXLEN); err = dptab_set(dp->dt, dp->myscoretab, subkey, subkeylen, sbuf.buf, sbuf.len, 1, PLAYER_ME); ASSERTMEM(); if (err != dp_RES_OK) { DPRINT(("dpscore_client_playerLeaving: dptab_set(MYSCORES.%s) returns err:%d\n", key2a(subkey, subkeylen), err)); return err; } } return dp_RES_OK; }
/*-------------------------------------------------------------------------- Call in dpDestroy *before dpFreeze* (and in dpCloseGameServer()) to clean up. Don't want to save old scores to disk... oughtta be a way to mark tables as 'volatile' in dptab_createTable(). --------------------------------------------------------------------------*/ dp_result_t dpscore_client_cleanup(dp_t *dp) { char key[dptab_KEY_MAXLEN]; DPRINT(("dpscore_client_cleanup: deleting myscores and scores tables\n")); /* Delete outgoing scores */ key[0] = dp_KEY_MYSCORES; dptab_deleteTable(dp->dt, key, 1); dp->myscoretab = NULL; /* clear our quick access pointer */ ASSERTMEM(); /* Delete incoming scores. KLUDGE; only does one session type... */ /* Watch out- the entry in the publishers table contains a pointer * to this table. Possible crash bug if a record comes in * from the game server after table is deleted. Not sure if deleteTable * wipes out the publishers table entry... */ key[0] = dp_KEY_SCORES; key[1] = dpGETSHORT_FIRSTBYTE(dp->defaultSessionType); key[2] = dpGETSHORT_SECONDBYTE(dp->defaultSessionType); dptab_deleteTable(dp->dt, key, 3); ASSERTMEM(); return dp_RES_OK; }
/*---------------------------------------------------------------------- Gets latest version info from server's table. ----------------------------------------------------------------------*/ static dp_result_t /* status */ getLatest( dp_t* dp, /* (input) source of table of latest */ dp_appParam_t *pApp) /* (output) latest version info */ { dptab_table_t *ptApps; char key[dptab_KEY_MAXLEN]; int keylen = 0; dp_result_t err; assert(dp != NULL); assert(pApp != NULL); /* Set up default values */ pApp->latest.major = dp_VERS_UNKNOWN; pApp->latest.minor = dp_VERS_UNKNOWN; /* Get table of applications */ keylen = 0; key[keylen++] = dp_KEY_APPLICATIONS; ptApps = dptab_getTable(dp->dt, key, keylen); /* If table present, find app info */ if(ptApps != NULL) { dp_version_t ver; dp_version_t* pVer = &ver; size_t len; keylen = 0; key[keylen++] = (char) dpGETSHORT_FIRSTBYTE(pApp->sessionType); key[keylen++] = (char) dpGETSHORT_SECONDBYTE(pApp->sessionType); key[keylen++] = (char) dpGETSHORT_FIRSTBYTE(pApp->platform); key[keylen++] = (char) dpGETSHORT_SECONDBYTE(pApp->platform); key[keylen++] = (char) pApp->language; err = dptab_get_bykey(ptApps, key, keylen, &pVer, &len); if (err != dp_RES_OK) { DPRINT(("enumapp.getLatest: game not in table\n")); return err; } pApp->latest = *pVer; } return dp_RES_OK; }
/*------------------------------------------------------------------------ Convert a player id (dpid_t) to a user id (dp_uid_t). Returns dp_UID_NONE on any error. ------------------------------------------------------------------------*/ DP_API dp_uid_t DP_APIX dpGetPlayerUid(dp_t *dp, dpid_t id) { dp_result_t err; dp_playerId_t player; char *playerbuf; size_t len_used; size_t len; char subkey[dptab_KEY_MAXLEN]; int subkeylen; precondition(dp != NULL); dp_assertValid(dp); if (!dp->players) return dp_UID_NONE; if (!dp->groups) return dp_UID_NONE; subkey[0] = (char) dpGETSHORT_FIRSTBYTE(id); subkey[1] = (char) dpGETSHORT_SECONDBYTE(id); subkeylen = 2; if (id >= dp->firstGId && id < dp->firstGId + dp_MAX_GROUPS) { DPRINT(("dpGetPlayerUid: player groups don't have UIDs\n")); return dp_UID_NONE; } err = dptab_get_bykey(dp->players, subkey, subkeylen, (void **)&playerbuf, &len); if (err != dp_RES_OK) { DPRINT(("dpGetPlayerUid: dptab_get_bykey(players, id:%d) returns err:%d\n", id, err)); dp_assertValid(dp); return dp_UID_NONE; } len_used = dp_unpack_playerId(id, playerbuf, &player); if (len != len_used) { DPRINT(("dpGetPlayerUid: can't unpack player %d.\n", id)); dp_assertValid(dp); return dp_UID_NONE; } if ((unsigned long)player.karma == dp_UID_NONE) return dp_UID_NONE; /* Better not let UID get to 131072! FIXME */ /* Kludge: only the lower 16 bits of uid available at moment */ return (dp_uid_t)(0x10000 + (unsigned long)(player.karma)); }
/*-------------------------------------------------------------------------- Request that the server send us score data for the given session type. Call from dpRequestObjectDeltas(). --------------------------------------------------------------------------*/ dp_result_t dpscore_client_subscribe(dp_t *dp, dp_species_t sessType) { dp_result_t err; dptab_table_t *tab; char key[dptab_KEY_MAXLEN]; int keylen; ASSERTMEM(); /* Create incoming scores table. */ key[0] = dp_KEY_SCORES; key[1] = dpGETSHORT_FIRSTBYTE(sessType); key[2] = dpGETSHORT_SECONDBYTE(sessType); keylen = 3; err = dptab_createTable(dp->dt, &tab, key, keylen, 0, NULL, NULL, dpscores_cb, dp); ASSERTMEM(); if (err != dp_RES_OK) { DPRINT(("dpscore_client_subscribe: can't create scores table, err:%d\n", err)); return err; } /* Allow the game server to send us data on this table. */ err = dptab_addPublisher(dp->dt, tab, key, keylen, dp->hGameServer); ASSERTMEM(); if (err != dp_RES_OK) { DPRINT(("dpscore_client_subscribe: can't add hGameServer h:%x as publisher?, err:%d\n", dp->hGameServer, err)); return err; } /* Request the game server to send us data on this table. * For now, ask for the whole table; later, we'll ask for just * part of it (otherwise we'll drown in data). */ err = dptab_requestSubscription(dp->dt, key, keylen, dp->hGameServer, NULL, NULL); ASSERTMEM(); if (err != dp_RES_OK) { DPRINT(("dpscore_client_subscribe: can't request scores from hGameServer h:%x, err:%d\n", dp->hGameServer, err)); return err; } ASSERTMEM(); return dp_RES_OK; }
/*---------------------------------------------------------------------- Callback for remote enum players. Only used if application requested object deltas for this session's players. ----------------------------------------------------------------------*/ void dp_PASCAL dp_rplayers_enumEx_cb(dpid_t id, char *name, long flags, void *context, dp_playerId_t *player) { dp_rplayers_enumEx_context_t *r = (dp_rplayers_enumEx_context_t *)context; dp_result_t err; char subkey[2]; int subkeylen; if (!r || !r->dp || !r->tab) return; if (player == NULL) return; /* Inform caller via local message, if desired */ subkeylen = 0; subkey[subkeylen++] = (char) dpGETSHORT_FIRSTBYTE(player->id); subkey[subkeylen++] = (char) dpGETSHORT_SECONDBYTE(player->id); err = dpSendObjectDelta(r->dp, dp_RES_CREATED, (dp_object_t *) player, r->tab, subkey, subkeylen); if (err != dp_RES_OK) { DPRINT(("dp_rplayers_enumEx_cb: can't send object delta, err:%d\n", err)); return; } return; }
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; }