/*-------------------------------------------------------------------------- Get a variable for a player. Caller must set *len before calling to the size of buf. If the player or variable does not exist, dp_RES_EMPTY is returned. If *len is not big enough, *len is set to the current size, and dp_res_FULL is returned. buf may be NULL if you only want to find the variable's size. --------------------------------------------------------------------------*/ dp_result_t pv_get(pv_t *pv, int player, int key, size_t *len, void *buf, int flags) { pv_peer_t *peer; pv_var_t *pvar; size_t buflen; if (!pv) return dp_RES_BUG; if (!len) return dp_RES_BAD; /* Get this player's table. */ peer = (pv_peer_t *)assoctab_subscript(pv->peers, player); if (!peer) return dp_RES_EMPTY; /* Get the desired key/value pair */ pvar = (pv_var_t *)assoctab_subscript(peer->vars, key); if (!pvar) return dp_RES_EMPTY; /* Hmm, it'd be nice to have another code */ buflen = *len; *len = pvar->len; if (buflen < pvar->len) return dp_RES_FULL; if (buf) memcpy(buf, pvar->buf, pvar->len); (void) flags; return dp_RES_OK; }
/*------------------------------------------------------------------------ Look at the request from the given handle, and formulate an appropriate http response. Returns 0 on success. Returns -1 on failure. ------------------------------------------------------------------------*/ static int bhttp_formatOutputBuffer(bhttp_t *bhttp, int h, int status, char *type, char *content) { bhttp_conn_t *pc; time_t now; time_t mod = 0; char nowbuf[100]; char modbuf[100]; static const char* rfc1123fmt = "%a, %d %b %Y %H:%M:%S GMT"; if (!bhttp || (h == -1)) return -1; pc = (bhttp_conn_t *)assoctab_subscript(bhttp->conns, h); if (!pc) { DPRINT(("bhttp_formatOutputBuffer: no such handle h:%d\n", h)); return -1; } now = time( (time_t*) 0 ); if ( mod == (time_t) 0 ) mod = now; (void) strftime( nowbuf, sizeof(nowbuf), rfc1123fmt, gmtime( &now ) ); (void) strftime( modbuf, sizeof(modbuf), rfc1123fmt, gmtime( &mod ) ); pc->olen = sprintf( pc->obuf, "%.20s %d %s\r\nServer: %s\r\nContent-type: %s\r\nDate: %s\r\nLast-modified: %s\r\nConnection: close\r\n", "HTTP/1.0", status, httpResult2a(status), "bhttp", type, nowbuf, modbuf ); if (content) { /* Terminate the http header with a blank line, follow it with the content for this URL */ pc->olen += sprintf(pc->obuf+pc->olen, "Content-length: %d\r\n\r\n%s", strlen(content), content); } else { /* Terminate the http header with a blank line */ pc->olen += sprintf(pc->obuf+pc->olen, "\r\n"); } pc->opos = 0; return 0; }
/*------------------------------------------------------------------------ Add a handle to the bhttp. Returns 0 on success, -1 on failure. ------------------------------------------------------------------------*/ static int bhttp_handleOpened(bhttp_t *bhttp, int h) { clock_t now = eclock(); bhttp_conn_t *pc; if (!bhttp || (h == -1)) return -1; pc = (bhttp_conn_t *)assoctab_subscript(bhttp->conns, h); if (pc) { DPRINT(("bhttp_handleOpened: h:%d already open\n", h)); return -1; } pc = (bhttp_conn_t *)assoctab_subscript_grow(bhttp->conns, h); if (!pc) { DPRINT(("bhttp_handleOpened: can't grow table for h:%d\n", h)); return -1; } memset(pc, 0, sizeof(bhttp_conn_t)); pc->ilen = 0; pc->ipos = 0; FD_SET(h, &bhttp->rfds); pc->t_connect = now; if (h > bhttp->sockmax) bhttp->sockmax = h; bhttp_assertValid(bhttp); DPRINT(("bhttp_handleOpened(h:%d)\n", h)); return 0; }
/*------------------------------------------------------------------------ Add a handle to the sbdserv. Returns 0 on success, -1 on failure. ------------------------------------------------------------------------*/ static int sbdserv_handleOpened(sbdserv_t *sbdserv, int h) { clock_t now = eclock(); sbdserv_conn_t *pc; if (!sbdserv || (h == -1)) return -1; pc = (sbdserv_conn_t *)assoctab_subscript(sbdserv->conns, h); if (pc) { DPRINT(("sbdserv_handleOpened: h:%d already open\n", h)); return -1; } pc = (sbdserv_conn_t *)assoctab_subscript_grow(sbdserv->conns, h); if (!pc) { DPRINT(("sbdserv_handleOpened: can't grow table for h:%d\n", h)); return -1; } memset(pc, 0, sizeof(sbdserv_conn_t)); pc->pos = 0; pc->len = 0; FD_SET(h, &sbdserv->fds); pc->t_connect = now; if (h > sbdserv->sockmax) sbdserv->sockmax = h; sbdserv_assertValid(sbdserv); DPRINT(("sbdserv_handleOpened(h:%d)\n", h)); return 0; }
main() { int i; assoctab_t *pt[NTABS]; for (i=0;i<NTABS;i++) { pt[i] = assoctab_create(sizeof(int)); if (!pt[i]) { printf("create: test failed\n"); exit(1); } if (assoctab_subscript(pt[i], 1) != NULL) { printf("subcript1: test failed\n"); exit(1); } } for (i=0;i<NTABS;i++) { int h; h = _heapchk(); if (h != _HEAPOK) { printf("heapchk: test failed\n"); exit(1); } test1(pt[i], 1); test1(pt[i], 2); test1(pt[i], 3); } for (i=0;i<NTABS;i++) assoctab_destroy(pt[i]); printf("test okay\n"); return 0; }
/*-------------------------------------------------------------------------- Delete a particular player from the table. --------------------------------------------------------------------------*/ dp_result_t pv_deletePlayer(pv_t *pv, int player) { int i; pv_peer_t *peer; if (!pv) return dp_RES_BUG; DPRINT(("pv_deletePlayer(dp, %d)\n", player)); /* Get this player's table. */ peer = (pv_peer_t *)assoctab_subscript(pv->peers, player); if (!peer) { DPRINT(("pv_deletePlayer: no table for player %d\n", player)); return dp_RES_EMPTY; } /* Free all memory pointed to by the variables in peer->vars */ for (i=0; i < peer->vars->n_used; i++) { pv_var_t *pvar; assoctab_item_t *pe; pe = assoctab_getkey(peer->vars, i); if (!pe) break; /* horrible error */ pvar = (pv_var_t *) &pe->value; /* Destroy their values */ if (pvar->buf) dp_FREE(pvar->buf); } if (peer->incoming.buf) dp_FREE(peer->incoming.buf); /* Destroy it, and remove it from the table. */ assoctab_destroy(peer->vars); if (assoctab_subscript_delete(pv->peers, player)) return dp_RES_BUG; return dp_RES_OK; }
/*-------------------------------------------------------------------------- Set variable 'key' for a player. Value is copied onto the heap, and must be freed later with pv_set (overwriting frees), pv_deletePlayer or pv_destroy. --------------------------------------------------------------------------*/ dp_result_t pv_set(pv_t *pv, int player, int key, size_t len, void *buf, int flags) { pv_peer_t *peer; pv_var_t *pvar; if (!pv) return dp_RES_BUG; DPRINT(("pv_set(pv, player %d, key %d, len %d, buf %p, flags %x):\n", player, key, len, buf, flags)); #ifdef DPRINTBUFS DPRINT(("pv_set: buf = ")); dprint_buf(buf, len); #endif if (len > dp_PLAYERDATA_LEN_MAX) return dp_RES_BADSIZE; /* Get this player's table. */ peer = (pv_peer_t *)assoctab_subscript(pv->peers, player); if (!peer) { /* No peer yet. Create one. */ peer = pv_addPlayer(pv, player); if (!peer) return dp_RES_NOMEM; } /* Add a key/value pair to it */ pvar = (pv_var_t *)assoctab_subscript_grow(peer->vars, key); if (!pvar) { DPRINT(("pv_set: can't grow player %d's vars[%d]!\n", player, key)); return dp_RES_NOMEM; } /* Delete old value from heap if present and not big enough */ if (!pvar->buf || pvar->len < len) { if (pvar->buf) dp_FREE(pvar->buf); pvar->buf = dp_MALLOC(len); } if (!pvar->buf) return dp_RES_NOMEM; /* Fill the key/value pair with the given data */ pvar->key = key; pvar->flags = flags; pvar->len = len; memcpy(pvar->buf, buf, len); pvar->crc = dp_crc32((unsigned char *)buf, len); DPRINT(("pv_set: player %d key %d len %d crc is %x\n", player, key, len, pvar->crc)); /* Inform other players of the change unless told not to. */ if (!(flags & dp_PLAYERDATA_FLAG_NOFLOOD)) { DPRINT(("Setting peer->dirty TRUE for player %d\n", player)); peer->dirty = TRUE; pvar->flags |= dp_PLAYERDATA_FLAG_DIRTY; } return dp_RES_OK; }
/*----------------------------------------------------------------------- Given a handle, return the corresponding IP address. Returns comm_STATUS_EMPTY on failure. -----------------------------------------------------------------------*/ comm_status_t trump_hdl2adr(trump_hdl_t hdl, ip_adr_t *adr) { ip_adr_t *peer; peer = (ip_adr_t *)assoctab_subscript(peertab, hdl); if (!peer) { DPRINT(("trump_hdl2adr: couldn't find peer %d\n", hdl)); dprint_peertab(peertab); return comm_STATUS_EMPTY; } *adr = *peer; return comm_STATUS_OK; }
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; }
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; }
/*------------------------------------------------------------------------ Handle any new connections, data, or handle state changes. rfds and wfds should be the same fd_set*'s that were passed to select as it's second and third parameters. nsocks is the return value of select. Call this once after each select, regardless of whether select times out. Returns 0 upon success. Returns -1 on error; detailed error message is printed to log file if this is a debug build. ------------------------------------------------------------------------*/ int bhttp_poll(bhttp_t *bhttp, fd_set *rfds, fd_set *wfds, int nsocks) { clock_t now; int sock; int i; if (!bhttp || !bhttp->conns) return -1; bhttp_assertValid(bhttp); assert(nsocks >= 0); bhttp->cursock = 0; /* FIXME: should be min(socks) */ bhttp->nsocks = nsocks; if (FD_ISSET(bhttp->sockin, rfds) && (bhttp->nsocks > 0)) { int newsock; struct sockaddr_in client_addr; int len = sizeof(struct sockaddr_in); newsock = accept(bhttp->sockin, (struct sockaddr *)&client_addr,&len); if (newsock == -1) { DPRINT(("bhttp_poll: accept error:%d on sock:%d\n", errno, bhttp->sockin)); } else { int flags; #ifdef VERBOSE DPRINT(("bhttp_poll: accepting connection from %s on sock:%d\n", inet_ntoa(client_addr.sin_addr), newsock)); #endif flags = fcntl(newsock, F_GETFL, 0); if ((-1 == flags) || (-1 == fcntl(newsock, F_SETFL, (flags | O_NONBLOCK)))) { DPRINT(("bhttp_poll: fcntl error:%d on sock:%d\n", errno, newsock)); close(newsock); return -1; } else if (-1 == bhttp_handleOpened(bhttp, newsock)) { DPRINT(("bhttp_poll: can't add h:%d\n", newsock)); close(newsock); return -1; } } bhttp->nsocks--; } while ((bhttp->cursock <= bhttp->sockmax) && (bhttp->nsocks > 0)) { int used = FALSE; int len; int h = bhttp->cursock; bhttp_conn_t *pc = NULL; if (FD_ISSET(h, rfds) && FD_ISSET(h, &bhttp->rfds)) { pc = (bhttp_conn_t *)assoctab_subscript(bhttp->conns, h); if (!pc) { DPRINT(("bhttp_poll: no such handle h:%d\n", h)); return -1; } len = bhttp_readData(pc, h); if (len == -1) { DPRINT(("bhttp_readData error\n")); bhttp_handleClosed(bhttp, h); close(h); /* silently fails */ } else if (len > 0) { char buf[1024]; int buflen; bhttp_url2buf_result_t urlResult; #ifdef VERBOSE DPRINT(("bhttp_poll: h:%d finished receiving data\n", h)); #endif memset(&urlResult, 0, sizeof(urlResult)); buflen = bhttp->url2buf_cb(bhttp->url2buf_context, pc->purl, buf, sizeof(buf), &urlResult); buf[buflen] = 0; /* Finished reading http header, formulate http response */ bhttp_formatOutputBuffer(bhttp, h, urlResult.httpResultCode, urlResult.mimeType, buf); FD_SET(h, &bhttp->wfds); } else { #ifdef VERBOSE DPRINT(("bhttp_poll: h:%d waiting for more data\n", h)); #endif } used = TRUE; } if (FD_ISSET(h, wfds) && FD_ISSET(h, &bhttp->wfds)) { if (!pc) pc = (bhttp_conn_t *)assoctab_subscript(bhttp->conns, h); if (!pc) { DPRINT(("bhttp_poll: no such handle h:%d\n", h)); return -1; } len = bhttp_writeData(pc, h); if (len == -1) { DPRINT(("bhttp_writeData error\n")); bhttp_handleClosed(bhttp, h); close(h); /* silently fails */ } else if (len > 0) { #ifdef VERBOSE DPRINT(("bhttp_poll: h:%d finished sending data\n", h)); #endif bhttp_handleClosed(bhttp, h); if (-1 == close(h)) { DPRINT(("bhttp_poll: close error:%d\n", errno)); } } else { #ifdef VERBOSE DPRINT(("bhttp_poll: h:%d waiting for more data\n", h)); #endif } used = TRUE; } if (used) bhttp->nsocks--; bhttp->cursock++; } now = eclock(); if ((long)(now - bhttp->t_last_poll) < 10 * ECLOCKS_PER_SEC) return 0; /* don't poll more than once every 10s */ bhttp->t_last_poll = now; #ifdef VERBOSE DPRINT(("bhttp_poll: t:%d checking %d conns for timeouts\n", now, bhttp->conns->n_used)); #endif /* Iterate through handles, deleting those that have timed out, * in reverse order, since deleting will change indices of later entries. */ for (i = bhttp->conns->n_used - 1; i >= 0; i--) { assoctab_item_t *pi; bhttp_conn_t *pc; int h; pi = assoctab_getkey(bhttp->conns, i); if (!pi) { DPRINT(("bhttp_poll: assoctab_getkey(%d) returns NULL?\n", i)); return -1; } h = pi->key; pc = (bhttp_conn_t *)pi->value; if (!pc) { DPRINT(("bhttp_poll: conns[%d] == NULL?\n", i)); return -1; } if ((long)(now - pc->t_connect) > bhttp_CONN_TIMEOUT * ECLOCKS_PER_SEC) { DPRINT(("bhttp_poll: h:%d timed out\n", h)); bhttp_handleClosed(bhttp, h); if (-1 == close(h)) { DPRINT(("bhttp_poll: close(%d) error:%d\n", h, errno)); return -1; } } } return 0; }
/*------------------------------------------------------------------------ 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 */ }
void test1(assoctab_t *pt, int callnum) { int i; int errs; int *p; /* Try to delete something that's not there */ if (assoctab_subscript_delete(pt, 0) == 0) { printf("delete0: test failed\n"); exit(1); } /* Fill it up */ for (i=32; i>=0; i--) { p = assoctab_subscript_grow(pt, i); if (p == NULL) { printf("grow1: test failed on i=%d\n", i); exit(1); } if (*p != 0) { printf("initial value test: test failed, i %d, *p %d\n", i, *p); errs++; } *p = i; printf("."); } /* Make sure it got filled up right */ for (i=0; i<33; i++) { p = assoctab_subscript(pt, i); if (p == NULL) { printf("subscript2: test failed\n"); exit(1); } if (*p != i) { printf("value test: test failed\n"); exit(1); } } /* Make sure it's empty past the end */ if (assoctab_subscript(pt, i) != NULL) { printf("subscript3: test failed\n"); exit(1); } /* Delete halfway thru the table */ if (assoctab_subscript_delete(pt, 16)) { printf("delete1: test failed\n"); exit(1); } /* Verify that rest of table still there */ errs = 0; for (i=0; i<33; i++) { p = assoctab_subscript(pt, i); if (i == 16) { if (p != NULL) { printf("subscript5: test failed, i %d: expected null, got %p, %d\n", i, p, *p); errs ++; } } else { if (p == NULL) { printf("subscript5: test failed, i %d, got p NULL\n", i); errs ++; } if (*p != i) { printf("value test 2: test failed, i %d. Expected %d, got %d\n", i, i, *p); errs ++; } } } /* Delete elements, get ready for next run */ for (i=0; i<33; i++) { int b; b = assoctab_subscript_delete(pt, i); if (b ^ (i == 16)) { printf("delete2: test failed, i %d. Expected %d, got %d\n", i==16, b); errs ++; } } if (pt->n_used != 0) { printf("delete3: test failed. Expected n_used = zero, got %d\n", pt->n_used); errs ++; } if (errs) { printf("test: failed on call %d\n", callnum); exit(1); } }
dp_result_t pv_update(pv_t *pv, dpid_t owner) { size_t len, hdrlen; pv_peer_t *peer; assoctab_item_t *pe; pv_var_t *pvar; dp_result_t err; playerHdl_t errHdl; time_t interval; int i, j; char buf[dpio_MAXLEN_RELIABLE]; if (!pv) { DPRINT(("pv_update: pv null\n")); return dp_RES_BUG; } /* Wait 'til previous transmission has had time to get sent. */ if ((long)(pv->dp->now - pv->next_send) < 0) return dp_RES_OK; /* Set default next-check-time if no transmission this time. */ pv->next_send = pv->dp->now + pv->dp->clocksPerSec / 8; /* To propagate the given id's variables, need to get access to them */ peer = (pv_peer_t *)assoctab_subscript(pv->peers, owner); if (!peer) { /*DPRINT(("pv_update: no variables for player %d\n", owner)); */ return dp_RES_OK; } /*DPRINT(("pv_update: peer->dirty is %d for player %d\n", peer->dirty, owner));*/ /* If it's time to start a new cycle, do it. */ if (pv->cur_key_index == -1) { if (pv->new_ndests > 0) { /* New hosts have been added since last cycle. */ /* Start a host update cycle. */ /* Copy new host list. */ pv->cur_ndests = pv->new_ndests; memcpy(pv->cur_dests, pv->new_dests, pv->new_ndests * sizeof(pv->new_dests[0])); /* Reset new host list. */ pv->new_ndests = 0; /* Fill key list with all public keys for this player. */ for (i=j=0; i<peer->vars->n_used && j<dp_PLAYERDATA_NKEYS_MAX; i++) { pe = assoctab_getkey(peer->vars, i); if (!pe) break; /* horrible error */ pvar = (pv_var_t *) &pe->value; if (!(pvar->flags & dp_PLAYERDATA_FLAG_NOFLOOD)) { pv->cur_keys[j++] = pe->key; } } /* Early exit if no public variables. */ if (j == 0) return dp_RES_OK; pv->cur_nkeys = j; DPRINT(("pv_update: starting new host update cycle. nkeys %d, ndests %d\n", pv->cur_nkeys, pv->cur_ndests)); /* Trigger start. */ pv->cur_key_index = 0; pv->cur_offset = 0; } else if (peer->dirty > 0) { /* Varible values have changed since last cycle. */ /* Start a variable update cycle. */ peer->dirty = FALSE; DPRINT(("pv_update: Clearing peer->dirty for player %d\n", owner)); /* Set host list to 'all other hosts in game'. */ pv->cur_ndests = dp_getBroadcastHdls(pv->dp, pv->cur_dests); /* Fill key list with all dirty variables. Mark them clean. */ for (i=j=0; i<peer->vars->n_used && j<dp_PLAYERDATA_NKEYS_MAX; i++) { pe = assoctab_getkey(peer->vars, i); if (!pe) break; /* horrible error */ pvar = (pv_var_t *) &pe->value; if ((pvar->flags & dp_PLAYERDATA_FLAG_DIRTY) && !(pvar->flags & dp_PLAYERDATA_FLAG_NOFLOOD)) { pv->cur_keys[j++] = pe->key; pvar->flags &= ~dp_PLAYERDATA_FLAG_DIRTY; } } /* Early exit if no other hosts in game (after clearing dirty!). */ if (pv->cur_ndests <= 0) return dp_RES_OK; /* Early exit if no dirty public variables. */ if (j == 0) return dp_RES_OK; pv->cur_nkeys = j; DPRINT(("pv_update: starting new variable update cycle. nkeys %d, ndests %d\n", pv->cur_nkeys, pv->cur_ndests)); /* Trigger start. */ pv->cur_key_index = 0; pv->cur_offset = 0; } } /* Are we in the middle of an update? */ if (pv->cur_key_index == -1) return dp_RES_OK; /* No. */ /* Get a pointer to the variable we're working on. Make sure it's * still there, still clean, and still needs data transferred. */ do { pvar = (pv_var_t *)assoctab_subscript(peer->vars, pv->cur_keys[pv->cur_key_index]); /* Has it been deleted or changed or finished? */ if (!pvar || (pvar->flags & dp_PLAYERDATA_FLAG_DIRTY) || (pvar->len <= pv->cur_offset)) { DPRINT(("pv_update: pvar %p, flags %x; advancing to next var\n", pvar, pvar?pvar->flags:0x666)); /* Yes. Just advance to the next variable. */ pv->cur_key_index++; pv->cur_offset = 0; if (pv->cur_key_index >= pv->cur_nkeys) { /* That was the last variable. The cycle is over. */ pv->cur_key_index = -1; DPRINT(("pv_update: cycle over\n")); return dp_RES_OK; } pvar = NULL; } } while (!pvar); /* Send the current chunk of the current variable. */ if (pv->cur_offset == 0) { dp_packetType_t *tag = (dp_packetType_t *)buf; pv_playerData_initial_packet_t *body = (pv_playerData_initial_packet_t *)(buf + sizeof(dp_packetType_t)); void *payload = ((char *)body) + sizeof(pv_playerData_initial_packet_t); /* Send an initial chunk. */ len = pvar->len; if (len > pv_PLAYERDATA_INITIAL_MAXLEN) len = pv_PLAYERDATA_INITIAL_MAXLEN; *tag = pv_PLAYERDATA_INITIAL_PACKET_ID; body->len = pvar->len; body->id = owner; body->flags = (short) pvar->flags; body->key = pvar->key; body->crc = pvar->crc; /* call dpSwapPvUpdateInitial to byte swap body */ dpSwapPvUpdateInitial(body); memcpy(payload, pvar->buf, len); hdrlen = sizeof(dp_packetType_t)+sizeof(pv_playerData_initial_packet_t); } else { dp_packetType_t *tag = (dp_packetType_t *)buf; char *pastTag = buf + sizeof(dp_packetType_t); pv_playerData_body_packet_t *body = (pv_playerData_body_packet_t *)pastTag; void *payload = ((char *)body) + sizeof(pv_playerData_body_packet_t); len = pvar->len - pv->cur_offset; if (len > pv_PLAYERDATA_BODY_MAXLEN) len = pv_PLAYERDATA_BODY_MAXLEN; #ifndef __MWERKS__ assert(len>0); #endif *tag = pv_PLAYERDATA_BODY_PACKET_ID; body->id = owner; /* call dpSwapPvUpdate to byte swap body */ dpSwapPvUpdate(body); memcpy(payload, (char *)(pvar->buf)+pv->cur_offset, len); hdrlen = sizeof(dp_packetType_t)+sizeof(pv_playerData_body_packet_t); } err = dpio_put_reliable(pv->dp->dpio, pv->cur_dests, pv->cur_ndests, buf, len + hdrlen, &errHdl); /* Estimate the bandwidth used by this send. */ interval = (pv->dp->clocksPerSec * (len + hdrlen)) / pv_BYTES_PER_SEC; pv->next_send = pv->dp->now + interval; DPRINT(("pv_update: Sent key %d off %d len %d to %d hosts; result %d, errHdl %d; next send in %d tix.\n", pv->cur_keys[pv->cur_key_index], pv->cur_offset, len, pv->cur_ndests, err, errHdl, interval)); if (err == dp_RES_OK) pv->cur_offset += len; else if (err != dp_RES_FULL) DPRINT(("pv_update: serious send problem: dpio_put_reliable returned %d\n", err)); return err; }
/*-------------------------------------------------------------------------- Process a network message generated by this module on another computer. Note: player deletion should be noticed by some other handler, and should cause a call to pv_deletePlayer(). Large variables will be handled as follows: The buffer is broken into pieces. The first piece is sent with tag pv_PLAYERDATA_INITIAL_PACKET_ID and has a header structure describing the length, owner, and key of the incoming variables. Subsequent pieces are sent with tag pv_PLAYERDATA_BODY_PACKET_ID, and lack headers. When a first piece comes in, it goes into a holding area for that source; When middle or last pieces come in, they are appended to the holding area; When a last piece comes in, the holding area is copied to the real variable area, and the user code is informed. On success, if not the final packet, Returns dp_RES_EMPTY. else, Returns dp_RES_OK, and places a tagged dp_user_playerData_packet_t in buffer. --------------------------------------------------------------------------*/ dp_result_t pv_handlePacket(pv_t *pv, size_t len, void *buf) { dp_packetType_t *tag = (dp_packetType_t *)buf; pv_peer_t *peer; pv_var_t *pvar; dp_result_t err; dpid_t id; #ifdef DPRINTBUFS DPRINT(("pv_handlePacket(pv, %d, ", len)); dprint_buf(buf, len); #endif if (!pv) return dp_RES_BUG; if (*tag == pv_PLAYERDATA_INITIAL_PACKET_ID) { pv_playerData_initial_packet_t *body = (pv_playerData_initial_packet_t *)((char *)buf + sizeof(dp_packetType_t)); void *payload = ((char *)body) + sizeof(pv_playerData_initial_packet_t); size_t datalen = len - sizeof(dp_packetType_t) - sizeof(pv_playerData_initial_packet_t); if (datalen < 1 || datalen > pv_PLAYERDATA_INITIAL_MAXLEN) return dp_RES_BUG; /* call dpSwapPvUpdateInitial to byte swap body */ dpSwapPvUpdateInitial(body); id = body->id; /* Locate the sender's holding area and check for sanity. */ peer = (pv_peer_t *)assoctab_subscript(pv->peers, id); if (!peer) { /* No peer yet. Create one. Hope we can trust id. */ peer = pv_addPlayer(pv, id); if (!peer) { DPRINT(("pv_handlePacket: pv_addPlayer returns NULL\n")); return dp_RES_NOMEM; } } pvar = &peer->incoming; if (peer->allocated < body->len) { void *p = dp_REALLOC(pvar->buf, body->len); if (!p) return dp_RES_NOMEM; pvar->buf = p; peer->allocated = body->len; } /* Clear the holding area & copy this in. */ pvar->key = body->key; pvar->flags = body->flags; pvar->len = body->len; pvar->crc = body->crc; memcpy(pvar->type, body->type, sizeof(pvar->type)); memcpy(pvar->buf, payload, datalen); pvar->offset = datalen; DPRINT(("pv_handlePacket: after 1st packet, offset is %d, len is %d\n", pvar->offset, pvar->len)); } else if (*tag == pv_PLAYERDATA_BODY_PACKET_ID) { pv_playerData_body_packet_t *body = (pv_playerData_body_packet_t *)((char *)buf + sizeof(dp_packetType_t)); void *payload = ((char *)body) + sizeof(pv_playerData_body_packet_t); size_t datalen = len - sizeof(dp_packetType_t) - sizeof(pv_playerData_body_packet_t); if (datalen < 1 || datalen > pv_PLAYERDATA_BODY_MAXLEN) { DPRINT(("pv_handlePacket: datalen\n")); return dp_RES_BUG; } /* call dpSwapPvUpdate to byte swap body */ dpSwapPvUpdate(body); id = body->id; /* Locate the sender's holding area and check for sanity. */ peer = (pv_peer_t *)assoctab_subscript(pv->peers, id); if (!peer) { DPRINT(("pv_handlePacket: no variables for player %d\n", id)); return dp_RES_BAD; } pvar = &peer->incoming; if (peer->allocated < pvar->offset + datalen) { DPRINT(("pv_handlePacket: allocated %d need %d + %d\n", peer->allocated, pvar->offset, datalen)); return dp_RES_BUG; } /* Append to holding area. */ memcpy((char *)pvar->buf + pvar->offset, payload, datalen); pvar->offset += datalen; DPRINT(("pv_handlePacket: after: id %d, key %d, offset %d, len %d\n", id, pvar->key, pvar->offset, pvar->len)); } else return dp_RES_EMPTY; /* no error - but no pv packet recognized */ if (pvar->offset == pvar->len) { /* The variable has arrived! Obey the value change it carries. * Don't echo to other machines! */ long newcrc = dp_crc32((unsigned char *)pvar->buf, pvar->len); DPRINT(("pv_handlePacket: got crc %x\n", newcrc)); if (newcrc != pvar->crc) { DPRINT(("pv_handlePacket: bad crc %x, expected %x!\n", newcrc, pvar->crc)); return dp_RES_BAD; } err = pv_set(pv, id, pvar->key, pvar->len, pvar->buf, dp_PLAYERDATA_FLAG_NOFLOOD); DPRINT(("pv_handlePacket: called pv_set; returning err %d\n", err)); if (err == dp_RES_OK) { dp_user_playerData_packet_t *body = (dp_user_playerData_packet_t *)((char *)buf + sizeof(dp_packetType_t)); /* Notify local players - overwrite buffer with user packet. */ *tag = dp_USER_PLAYERDATA_PACKET_ID; body->len = pvar->len; body->id = id; body->key = pvar->key; body->data = pvar->buf; return dp_RES_OK; } return err; } return dp_RES_EMPTY; }