/*----------------------------------------------------------------------- Return the address of the given element of the table. If the table isn't that big, grow. Start all elements with 0. Returns NULL on failure. -----------------------------------------------------------------------*/ void *dynatab_subscript_grow(dynatab_t *tab, int subscript) { assert(tab != NULL); if (subscript < 0) return NULL; if (subscript >= tab->n_alloced) { void *newbuf; int new_n_alloced = tab->n_alloced + 16; if (new_n_alloced < subscript + 1) new_n_alloced = subscript + 16; if (tab->buf) newbuf = dp_REALLOC(tab->buf, new_n_alloced * tab->unit); else newbuf = dp_MALLOC(new_n_alloced * tab->unit); if (!newbuf) return NULL; tab->buf = newbuf; /* tab[n_alloced...new_n_alloced] = 0; */ /* memset(dynatab_subscript(tab, tab->n_alloced), 0, new area)); */ memset((char *)tab->buf + (tab->n_alloced * tab->unit), 0, (new_n_alloced - tab->n_alloced) * tab->unit); tab->n_alloced = new_n_alloced; } if (tab->n_alloced < subscript + 1) { /* BUG: Should have enough allocated by now. */ return NULL; } if (tab->n_used < subscript + 1) tab->n_used = subscript + 1; return ((char *)tab->buf) + (subscript * tab->unit); }
/*----------------------------------------------------------------------- Inserts a record into the table before the nth record. Grows the table. Returns NULL on failure. -----------------------------------------------------------------------*/ void *dynatab_subscript_insert(dynatab_t *tab, int n) { void *p; assert(tab != NULL); if (n < 0) return NULL; if (n > tab->n_used) return NULL; if (tab->n_used >= tab->n_alloced) { void *newbuf; int new_n_alloced = tab->n_alloced + 16; if (new_n_alloced < tab->n_used + 1) new_n_alloced = tab->n_used + 16; if (tab->buf) newbuf = dp_REALLOC(tab->buf, new_n_alloced * tab->unit); else newbuf = dp_MALLOC(new_n_alloced * tab->unit); if (!newbuf) return NULL; tab->buf = newbuf; /* tab[n_alloced...new_n_alloced] = 0; */ /* memset(dynatab_subscript(tab, tab->n_alloced), 0, new area)); */ memset((char *)tab->buf + (tab->n_alloced * tab->unit), 0, (new_n_alloced - tab->n_alloced) * tab->unit); tab->n_alloced = new_n_alloced; } if (tab->n_alloced < tab->n_used + 1) { /* BUG: Should have enough allocated by now. */ return NULL; } /* Make pointer to new record */ p = ((char *)tab->buf) + (n * tab->unit); /* If needed, move elements up to make space */ if (n < tab->n_used) { void* dest = ((char *)p) + tab->unit; size_t count = (tab->n_used - n) * tab->unit; memmove(dest, p, count); } tab->n_used = tab->n_used + 1; /* Zero out new record */ memset(p, 0, tab->unit); return ((char *)p); }
/*-------------------------------------------------------------------------- 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; }