/*-------------------------------------------------------------------------- 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; }
/*----------------------------------------------------------------------- Given a local port number, a remote ip address, and a remote port number, return a handle that can be used to send an address to that address. If 0 is given for the local port number, picks an unused one. Not suitable for a handle that will be used to listen for incoming service requests on a standard port number. If 0 is given for the remote port number, return a handle that can be used for receiving packets sent from any port at the remote host. If 255.255.255.255 is given for the remote address, return a handle that can be used for sending broadcast packets, or for receiving without regard to source address. If no handle exists for that address, If insert is non-zero, allocate one. else return trump_HDL_NONE Multiple calls with the same arg will return the same result. There is no upper bound on handles. Returns trump_HDL_NONE on failure. -----------------------------------------------------------------------*/ trump_hdl_t trump_adr2hdl(ip_adr_t remoteadr, short localport, short remoteport, int insert) { // Search peer tab. If not found, append it. trump_hdl_t h; ip_adr_t *peer; int i; unsigned int lport; OSStatus err; #if 0 DPRINT(("trump_adr2hdl: adr ")); printAdr(remoteadr); DPRINT((", dest_port %d, localport %d, insert %d\n", remoteport, localport, insert)); #endif // Look for existing address/handle for (i=0; i<peertab->n_used; i++) { assoctab_item_t *pe = assoctab_getkey(peertab, i); if (!pe) DPRINT(("trump_adr2hdl: couldn't find entry\n")); else { peer = (ip_adr_t *)pe->value; //DPRINT(("trump_adr2hdl: pe->value %p, *pe->value %x, remoteadr %x\n", //pe->value, *peer, remoteadr)); if (memcmp(peer, &remoteadr, sizeof(ip_adr_t)) == 0) { //DPRINT(("trump_adr2hdl: found match, hdl %d\n", pe->key)); return pe->key; } } } if (!insert) return trump_HDL_NONE; // Open new handle for this address h = tcpabi_udp_open(remoteadr, remoteport, localport, UDP_OPEN_NORMAL, &lport); if (h == trump_HDL_NONE) return h; // Save address peer = (ip_adr_t *)assoctab_subscript_grow(peertab, h); if (!peer) { // Abort! Out of memory! return trump_HDL_NONE; } dprint_peertab(peertab); //DPRINT(("trump_adr2hdl: saving %x at %p\n", remoteadr, peer)); *peer = remoteadr; //dprint_peertab(peertab); //DPRINT(("trump_adr2hdl: udp_open successful. localport %d\n", lport)); return h; }
/*------------------------------------------------------------------------ Destroy an instance of the bhttp module. ------------------------------------------------------------------------*/ void bhttp_destroy(bhttp_t *bhttp) { int i; if (!bhttp) return; close(bhttp->sockin); for (i = 0; i < bhttp->conns->n_used; i++) { assoctab_item_t *pi = assoctab_getkey(bhttp->conns, i); close(pi->key); } assoctab_destroy(bhttp->conns); memset(bhttp, 0, sizeof(bhttp_t)); free(bhttp); }
/*------------------------------------------------------------------------ Destroy an instance of the sbdserv module. ------------------------------------------------------------------------*/ void sbdserv_destroy(sbdserv_t *sbdserv) { int i; if (!sbdserv) return; close(sbdserv->sockin); for (i = 0; i < sbdserv->conns->n_used; i++) { assoctab_item_t *pi = assoctab_getkey(sbdserv->conns, i); close(pi->key); } assoctab_destroy(sbdserv->conns); memset(sbdserv, 0, sizeof(sbdserv_t)); free(sbdserv); }
/*----------------------------------------------------------------------- Dump the player table to the log file. -----------------------------------------------------------------------*/ static void dprint_peertab(assoctab_t *p) { #if defined(DPRNT) || defined(DEBUG) int i; for (i=0; i < p->n_used; i++) { assoctab_item_t *ip = assoctab_getkey(p, i); trump_hdl_t h = (trump_hdl_t) ip->key; ip_adr_t *pAdr = (ip_adr_t *) ip->value; DPRINT(("p[%d] = ", h)); printAdr(*pAdr); DPRINT((", at %p\n", pAdr)); } #else (void)p; #endif }
/*-------------------------------------------------------------------------- Destroy a pv_t. --------------------------------------------------------------------------*/ void pv_destroy(pv_t *pv) { int i; if (!pv) return; /* For all players. Step backwards to avoid having to move in delete. */ for (i=pv->peers->n_used-1; i>=0; i--) { assoctab_item_t *pe; pe = assoctab_getkey(pv->peers, i); if (!pe) break; /* horrible error */ /* Delete this player */ pv_deletePlayer(pv, pe->key); } /* Destroy the main table */ assoctab_destroy(pv->peers); }
/*----------------------------------------------------------------------- Save the state of a pv_peer_t to disk along with who it's for. -----------------------------------------------------------------------*/ static dp_result_t pv_peer_freeze(pv_peer_t *peer, dpid_t id, FILE *fp) { int i; short dirty = peer->dirty; unsigned long n_vars; long sig = 0x12340009; dp_result_t err; DPRINT(("pv_peer_freeze:\n")); n_vars = (unsigned long) peer->vars->n_used; /* Header */ if ((fwrite(&sig, sizeof(sig), 1, fp) != 1) || (fwrite(&dirty, sizeof(dirty), 1, fp) != 1) || (fwrite(&id, sizeof(id), 1, fp) != 1) || (fwrite(&n_vars, sizeof(n_vars), 1, fp) != 1)) return dp_RES_FULL; DPRINT(("pv_peer_freeze: peer %d, n_vars %d\n", id, n_vars)); /* Body */ for (i=0; i < peer->vars->n_used; i++) { assoctab_item_t *pe; pv_var_t *pvar; pe = assoctab_getkey(peer->vars, i); if (!pe) break; /* horrible error */ pvar = (pv_var_t *)&pe->value; DPRINT(("pv_peer_freeze: peer %d, var %d = %d, len %d\n", id, pe->key, pvar->key, pvar->len)); err = pv_var_freeze(pvar, fp); if (err != dp_RES_OK) return err; } DPRINT(("pv_peer_freeze: freezing incoming\n")); err = pv_var_freeze(&peer->incoming, fp); if (err != dp_RES_OK) return err; /* No trailer */ return dp_RES_OK; }
int commTerm( commTermReq_t * req, // Request (or NULL) commTermResp_t * resp) // Response (or NULL) { commTermReq_t reqDummy; commTermResp_t respDummy; DPRINT(("@TRUMP commTerm(): ")); if (req == NULL) req = (commTermReq_t *)memset(&reqDummy, 0, sizeof(*req)); if (resp == NULL) resp = &respDummy; if (peertab) { int i; for (i=0; i<peertab->n_used; i++) { assoctab_item_t *pe = assoctab_getkey(peertab, i); if (!pe) DPRINT(("commTerm: couldn't find entry\n")); else if (pe->key != trump_HDL_ME) { DPRINT(("commTerm: closing handle %d\n", pe->key)); tcpabi_udp_close(pe->key, UDP_CLOSE_NORMAL); } else { DPRINT(("commTerm: not closing handle %d\n", pe->key)); } } assoctab_destroy(peertab); } free(scratch_flat); resp->status = comm_STATUS_OK; DPRINT(("commTerm: success\n")); // unbind our endpoint and close open transport ShutDownUDP(); return TRUE; }
/*----------------------------------------------------------------------- Save the state of a pv_t to disk. -----------------------------------------------------------------------*/ dp_result_t pv_freeze(pv_t *pv, FILE *fp) { int i; unsigned long n_peers; pv_t temp; DPRINT(("pv_freeze:\n")); if (!pv) { DPRINT(("pv_freeze: pv null\n")); return dp_RES_BUG; } temp = *pv; /* Header */ fwrite(pv_SIG_START, sizeof(pv_SIG_START), 1, fp); temp.peers = (assoctab_t *)0x12345679; /* use as signature */ temp.dp = NULL; n_peers = pv->peers->n_used; if ((fwrite(&temp, sizeof(pv_t), 1, fp) != 1) || (fwrite(&n_peers, sizeof(n_peers), 1, fp) != 1)) return dp_RES_FULL; /* Body */ for (i=0; i<pv->peers->n_used; i++) { assoctab_item_t *pe; pe = assoctab_getkey(pv->peers, i); if (!pe) break; /* horrible error */ /* save this player */ pv_peer_freeze((pv_peer_t *)&pe->value, pe->key, fp); } /* Trailer */ fwrite(pv_SIG_END, sizeof(pv_SIG_END), 1, fp); return dp_RES_OK; }
/*------------------------------------------------------------------------ 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; }
/*------------------------------------------------------------------------ Handle any new connections, data, or handle state changes. rfds should be the same fd_set* that was passed to select as it's second parameter. After a select (and regardless of whether select times out), call this repeatedly until it returns no data or an error. Returns the length of data received if any, and returns the data in buf. The caller should call sbdserv_poll again, since multiple handles might have data. Returns 0 if no data is available now. Returns -1 on error. ------------------------------------------------------------------------*/ int sbdserv_poll(sbdserv_t *sbdserv, fd_set *rfds, char *buf) { clock_t now; int sock; int i; if (!sbdserv || !sbdserv->conns || !buf) return -1; sbdserv_assertValid(sbdserv); if (FD_ISSET(sbdserv->sockin, rfds) && (sbdserv->nsocks > 0)) { int newsock; struct sockaddr_in client_addr; int len = sizeof(struct sockaddr_in); newsock = accept(sbdserv->sockin, (struct sockaddr *)&client_addr,&len); if (newsock == -1) { DPRINT(("sbdserv_poll: accept error:%d on sock:%d\n", errno, sbdserv->sockin)); } else { int flags; #ifdef VERBOSE DPRINT(("sbdserv_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(("sbdserv_poll: fcntl error:%d on sock:%d\n", errno, newsock)); close(newsock); return -1; } else if (-1 == sbdserv_handleOpened(sbdserv, newsock)) { DPRINT(("sbdserv_poll: can't add h:%d\n", newsock)); close(newsock); return -1; } } sbdserv->nsocks--; } while ((sbdserv->cursock <= sbdserv->sockmax) && (sbdserv->nsocks > 0)) { if (FD_ISSET(sbdserv->cursock, rfds) && FD_ISSET(sbdserv->cursock, &sbdserv->fds)) { char *bufp = NULL; int len; len = sbdserv_readData(sbdserv, sbdserv->cursock, &bufp); if (len == -1) { DPRINT(("sbdserv_readData error\n")); sbdserv_handleClosed(sbdserv, sbdserv->cursock); close(sbdserv->cursock); /* silently fails */ } else if (len > 0) { #ifdef VERBOSE DPRINT(("sbdserv_poll: h:%d finished sending data\n", sbdserv->cursock)); #endif /* must return copy, since closing will destroy original */ memcpy(buf, bufp, len); sbdserv_handleClosed(sbdserv, sbdserv->cursock); if (-1 == close(sbdserv->cursock)) { DPRINT(("sbdserv_poll: close error:%d\n", errno)); } sbdserv->nsocks--; sbdserv->cursock++; return len; } else { #ifdef VERBOSE DPRINT(("sbdserv_poll: h:%d waiting for more data\n", sbdserv->cursock)); #endif } sbdserv->nsocks--; } sbdserv->cursock++; } now = eclock(); if ((long)(now - sbdserv->t_last_poll) < 10 * ECLOCKS_PER_SEC) return 0; /* don't poll more than once every 10s */ sbdserv->t_last_poll = now; #ifdef VERBOSE DPRINT(("sbdserv_poll: t:%d checking %d conns for timeouts\n", now, sbdserv->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 = sbdserv->conns->n_used - 1; i >= 0; i--) { assoctab_item_t *pi; sbdserv_conn_t *pc; int h; pi = assoctab_getkey(sbdserv->conns, i); if (!pi) { DPRINT(("sbdserv_poll: assoctab_getkey(%d) returns NULL?\n", i)); return -1; } h = pi->key; pc = (sbdserv_conn_t *)pi->value; if (!pc) { DPRINT(("sbdserv_poll: conns[%d] == NULL?\n", i)); return -1; } if ((long)(now - pc->t_connect) > sbdserv_CONN_TIMEOUT * ECLOCKS_PER_SEC) { DPRINT(("sbdserv_poll: h:%d timed out\n", h)); sbdserv_handleClosed(sbdserv, h); if (-1 == close(h)) { DPRINT(("sbdserv_poll: close(%d) error:%d\n", h, errno)); return -1; } } } return 0; }
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; }