playernet.c

Go to the documentation of this file.
00001 
00024 #include "color.h"
00025 #include "playernet.h"
00026 #include "game.h"
00027 #include "net.h"
00028 #include <assert.h>
00029 #include <string.h>
00030 #ifdef USE_AUDIO
00031 #include "audio.h"
00032 #endif
00033 
00034 #if (MAX_PLAYERS > 256)
00035 #error More than 256 players is not supported.
00036 #endif
00037 
00038 MsgDescr *AppendPlayerData(MsgDescr *msg, Player *p, Uint32 time) {
00039     Uint8 *ptr;
00040     int flags, len = 3, nameLen;  // this is NOT used uninitialized
00041     
00042     // either there is no message to append to, or the message must be
00043     // a player update message
00044     assert((msg == NULL) || (msg->buffer->data[0] == MSG_TYPE_PLAYER_UPDATE));
00045     
00046     // limit the update and other flags to what is valid to send
00047     if (networkState == NETSTATE_CLIENT) {
00048         // clients can only transmit a subset of the flags
00049         flags = p->flags & (PLAYER_CLIENT_MASK | PLAYER_MOVEMENT_MASK);
00050     }
00051     else {
00052         // servers can transmit any flag
00053         flags = p->flags;
00054     }
00055     // there must be something to update
00056     assert(flags & PLAYER_UPDATE_ITEMS);
00057     // compute the length of this addition
00058     if (flags & PLAYER_UPDATE_IDENT) {
00059         len += 1 + ((nameLen = (int)rtsa_strlen(p->name)) << 1);
00060         assert(nameLen < MAX_NAME_LEN); // name must fit
00061     }
00062     if (flags & PLAYER_UPDATE_TANK) {
00063         len += 10;
00064     }
00065     if (flags & PLAYER_UPDATE_SHELL) {
00066         len += 12;
00067     }
00068     // only the server can include the DFS data set
00069     if (flags & PLAYER_UPDATE_DFS) {
00070         len += 5;
00071     }
00072     // for no message or inadequate room . . .
00073     if ((msg == NULL) || ((MSG_MAX_SIZE - msg->buffer->length) < len)) {
00074         // if a message was being built, add it to the system
00075         if ((msg != NULL) && !AddMessage(msg)) {
00076             // failed; send back an error code
00077             return NULL;
00078         }
00079         // create a new message
00080         if ((msg = MessageNew()) == NULL) {
00081             // failed; send back an error code
00082             return NULL;
00083         }
00084         // create a new buffer for the message
00085         if ((msg->buffer = BufferNew()) == NULL) {
00086             // failed; remove the message send back an error code
00087             MessageFree(msg);
00088             return NULL;
00089         }
00090         // setup the message descriptor
00091         msg->approveFunc = NULL;
00093         msg->ackFunc = NULL;
00094         msg->failFunc = MsgFailDisconnect;
00095         // Write the start of the player update message
00096         msg->buffer->length = 7;
00097         msg->buffer->data[0] = MSG_TYPE_PLAYER_UPDATE;
00098         MSG_WRITE_32C(msg->buffer->data, 3, time);
00099         // set the destination
00100         if (networkState == NETSTATE_SERVER) {
00101             // tell everyone of the update
00103             SetDestToAll(msg);
00104         }
00105         else {
00106             // this must be set to indicate the server is the destination
00107             msg->destMap[0] = 1;
00108         }
00109     }
00110     // get a pointer to the next byte of the buffer
00111     ptr = &(msg->buffer->data[msg->buffer->length]);
00112     // advance the length
00113     msg->buffer->length += len;
00114     // add the player data to the message's buffer
00115     MSG_WRITE_8(ptr, p->pid);
00116     // the player flags include the items updated in the message and often
00117     // updated flags, like the ones for player input
00118     MSG_WRITE_16(ptr, flags);
00119     // write name and team
00120     if (flags & PLAYER_UPDATE_IDENT) { // <65 bytes
00121         int loop;
00122         // lower 5 bits are the length of the name string
00123         // upper 2 bits are the team ID
00124         MSG_WRITE_8(ptr, nameLen | p->team << 6);
00125         // produce the name
00126         loop = TextNativeToUTF16((Uint16*)ptr, MAX_NAME_LEN, p->name,
00127         nameLen);
00128         assert(loop == nameLen);
00129         //memcpy(ptr, p->name, nameLen);
00130         ptr += nameLen << 1;
00131     }
00132     // write dead time, flags, and score
00133     if (flags & PLAYER_UPDATE_DFS) {
00134         // 5 bytes -- only transmitted by the server
00135         //MSG_WRITE_16(ptr, p->flags);
00136         MSG_WRITE_16(ptr, p->kills);
00137         MSG_WRITE_16(ptr, p->captures);
00138         MSG_WRITE_8(ptr, p->deadTime);
00139     }
00140     // write a tank position update?
00141     if (flags & PLAYER_UPDATE_TANK) { // 10 bytes
00142         MSG_WRITE_32(ptr, p->x);
00143         MSG_WRITE_32(ptr, p->y);
00144         MSG_WRITE_16(ptr, p->rot);
00145     }
00146     // write shell position
00147     if (flags & PLAYER_UPDATE_SHELL) { // 12 bytes
00148         MSG_WRITE_32(ptr, p->shellX);
00149         MSG_WRITE_32(ptr, p->shellY);
00150         MSG_WRITE_16(ptr, p->shellRot);
00151         MSG_WRITE_16(ptr, p->shellDist);
00152     }
00153     // assure the correct amount of data was written to the buffer
00154     assert(ptr == msg->buffer->data + msg->buffer->length);
00155     // send back the current message
00156     return msg;
00157 }
00158 
00171 static int CalculateOffset(NetBuffer *buffer, int offset) {
00172     int flags;
00173     // grab the upper byte of the player flags
00174     flags = buffer->data[++offset] << 8; // counts the ID
00175     // if the data is from a client . . .
00176     if (networkState == NETSTATE_SERVER) {
00177         // DFS data set cannot be included
00178         flags &= PLAYER_CLIENT_MASK;
00179     }
00180     // if the identity is included . . .
00181     if ((flags & PLAYER_UPDATE_IDENT) && (++offset < buffer->length)) {
00182         // . . . count it its length
00183         offset += (buffer->data[offset + 1] & 0x1F) << 1;
00184     }
00185     // compute the length for everything except the identity
00186     offset += ((flags & PLAYER_UPDATE_TANK)  ? 10 : 0) +
00187               ((flags & PLAYER_UPDATE_SHELL) ? 12 : 0) +
00188               ((flags & PLAYER_UPDATE_DFS)   ?  5 : 0) + 2; // counts the flags
00189     // final bounds check
00190     if (offset > buffer->length) {
00191         // too big
00192         offset = -offset;
00193     }
00194     return offset;
00195 }
00196 
00210 static Uint8 *ReadPlayer(Uint8 *ptr, Uint32 time) {
00211     Player *p;
00212     int flags, pid;
00213     Bool readTank = FALSE;
00214     
00215     // read the player index
00216     MSG_READ_8(ptr, pid);
00217     #if (MAX_PLAYERS < 256)
00218     // check for an index that is outside allowable bounds
00219     if (pid >= MAX_PLAYERS) return FALSE;
00220     #endif
00221     // get a pointer to the player object
00222     p = &(players[pid]);
00223     // read the player's flags
00224     MSG_READ_16(ptr, flags);
00225     // for servers . . .
00226     if (networkState == NETSTATE_SERVER) {
00227         // the client cannot send the DFS data set
00228         flags &= PLAYER_CLIENT_MASK | PLAYER_MOVEMENT_MASK;
00229         // store the new flags, but do not let the client change the
00230         // active or dead flags
00231         p->flags = (p->flags & (PLAYER_DEAD | PLAYER_ACTIVE |
00232         PLAYER_MOVEMENT_MASK)) | (flags & ~PLAYER_MOVEMENT_MASK); 
00233     }
00234     // for clients . . .
00235     else {
00236         // when updating the local player . . .
00237         if (p->pid == localPlayer) {
00238             // ignore all player input related flags when the server updates
00239             // this client's player, and assure it reamins active
00240             flags = (flags & PLAYER_LOCAL_MASK) | PLAYER_ACTIVE;
00241             // already dead?
00242             if (p->flags & PLAYER_DEAD) {
00243                 // ok to read in the tank position
00244                 readTank = TRUE;
00245             }
00246             #ifdef USE_AUDIO
00247             // just killed? only handles remote (non-server) players
00248             else if (flags & PLAYER_DEAD) {
00249                 // play the death sound
00250                 PlayLocalDeath();
00251             }
00252             #endif
00253             // update the flags
00254             p->flags = (flags & ~(PLAYER_UPDATE_IDENT | PLAYER_MOVEMENT_MASK |
00255             PLAYER_UPDATE_TANK | PLAYER_UPDATE_SHELL)) |
00256             (p->flags & (PLAYER_UPDATE_IDENT | PLAYER_MOVEMENT_MASK |
00257             PLAYER_UPDATE_TANK | PLAYER_UPDATE_SHELL));
00258         }
00259         else {
00260             // update the flags, but do not change the active flag
00261             p->flags = (flags & ~(PLAYER_ACTIVE | PLAYER_MOVEMENT_MASK)) |
00262             (p->flags & (PLAYER_ACTIVE | PLAYER_MOVEMENT_MASK));
00263             #ifdef USE_AUDIO
00264             // dead?
00265             if ((p->flags & PLAYER_DEAD) && ((p->deadTime == 0) ||
00266             (p->deadTime > DEAD_TIME))) {
00267                 // compute the square of the distance
00268                 int distsq = DistSq(p, &players[localPlayer]);
00269                 // close enough to hear?
00270                 if (distsq < MAX_SQ_DIST) {
00271                     // play the death sound
00272                     PlayTankKilled(distsq);
00273                 }
00274             }
00275             #endif
00276         }
00277         // if the player is dead . . .
00278         if (p->flags & PLAYER_DEAD) {
00279             // clear the movement flags and get rid of the shell
00280             p->flags &= ~(PLAYER_SHOT | PLAYER_FORWARD | PLAYER_BACKWARD |
00281             PLAYER_ROTRIGHT | PLAYER_ROTLEFT | PLAYER_SHOT);
00282             // assure no shell is drawn
00283             ClearRenderItem(&(p->shell));
00284             RemoveShell(p->pid);
00285             // ok to read in the tank position
00286             readTank = TRUE;
00287         }
00288     }
00289     // read name and team
00290     if (flags & PLAYER_UPDATE_IDENT) { // <33 bytes
00291         int nameLen;
00292         // read the combined string length and team
00293         MSG_READ_8(ptr, nameLen);
00294         // check for a new update
00295         if (time > p->lastUpdate.identTime) {
00296             int len;
00297             // clear the update flag
00298             p->flags &= ~PLAYER_UPDATE_IDENT;
00299             // set the update time
00300             p->lastUpdate.identTime = time;
00301             // upper 2 bits are the team ID
00302             p->team = nameLen >> 6;
00303             // the lower 5 bits are the length of the name string
00304             // copy the name
00305             len = TextUTF16toNative(p->name, MAX_NAME_LEN, (Uint16*)ptr,
00306             nameLen &= 0x1F);
00307             assert(len == nameLen);
00308             //memcpy(p->name, ptr, (nameLen &= 0x1F));
00309             ptr += nameLen << 1;
00310             //p->name[nameLen] = 0;  // null terminator not transmitted
00311             #ifdef MSG_DEBUG
00312             printf("Got ident for player #%d, %s\n", p->pid, p->name);
00313             #endif
00314         }
00315         else {
00316             // skip past the old data
00317             ptr += (nameLen & 0x1F) << 1;
00318             #ifdef MSG_DEBUG
00319             printf("Ignoring ident for player #%d, %s\n", p->pid, p->name);
00320             #endif
00321         }
00322     }
00323 
00324     if (!(p->flags & PLAYER_ACTIVE)) {
00325         p->pid = pid;
00326         p->flags |= PLAYER_DEAD;
00327         AddPlayer(p);
00328     }
00329     
00330     assert(pid == p->pid);
00331 
00332     // read dead time, flags, and score
00333     if (flags & PLAYER_UPDATE_DFS) { // 5 bytes
00334         // check for a new update
00335         if (time > p->lastUpdate.dfsTime) {
00336             // set the update time
00337             p->lastUpdate.dfsTime = time;
00338             // read in the data
00339             MSG_READ_16(ptr, p->kills);
00340             MSG_READ_16(ptr, p->captures);
00341             MSG_READ_8(ptr, p->deadTime);
00342             // no shell -- this player may have scored a kill
00343             if (!(flags & PLAYER_SHOT)) {
00344                 p->flags &= ~PLAYER_SHOT;
00345                 // assure no visible shell
00346                 ClearRenderItem(&(p->shell));
00347                 RemoveShell(p->pid);
00348             }
00349             // if a client and the player is alive
00350             if (!(flags & PLAYER_DEAD)) {
00351                 // assure the dead flag is not set
00352                 p->flags &= ~PLAYER_DEAD;
00353             }
00354             #ifdef MSG_DEBUG
00355             printf("Got DFS update for player #%d at time %d\n", p->pid, time);
00356             #endif
00357         }
00358         else {
00359             // skip past the old data
00360             ptr += 5;
00361         }
00362     }
00363     
00364     // read tank position
00365     if (flags & PLAYER_UPDATE_TANK) { // 10 bytes
00366         // check for a new update
00367         if ((readTank || (pid != localPlayer)) &&
00368         (time > p->lastUpdate.tankPosTime)) {
00369             Uint16 oldRot = p->rot;
00370             // clear the update flag and apply the new movement flags
00371             p->flags = (p->flags & ~(PLAYER_UPDATE_TANK | PLAYER_MOVEMENT_MASK))
00372             | (flags & PLAYER_MOVEMENT_MASK);
00373             // set the update time
00374             p->lastUpdate.tankPosTime = time;
00375             // read in the data
00376             MSG_READ_32(ptr, p->x);
00377             MSG_READ_32(ptr, p->y);
00378             MSG_READ_16(ptr, p->rot);
00379             // place the tank
00380             if (!(p->flags & PLAYER_DEAD)) {
00381                 MoveTank(p, 0);
00382                 // rotation only update?
00383                 if (!p->tank.placed && ((oldRot & 0xF00) != (p->rot & 0xF00))) {
00384                     // record that the whole tank must be redrawn
00385                     QueuePlaceTank(p->pid);
00386                 }
00387             }
00388             #ifdef MSG_DEBUG
00389             printf("Got tank update for player #%d at time %d\n", p->pid, time);
00390             printf("\tcoords: x = %d   y = %d\n", p->x >> 8, p->y >> 8);
00391             #endif
00392         }
00393         else {
00394             // skip past the old or local data
00395             ptr += 10;
00396             #ifdef MSG_DEBUG
00397             printf("Ignoring tank update for player #%d at time %d\n", p->pid,
00398             time);
00399             #endif
00400         }
00401     }
00402     
00403     // read shell position
00404     if (flags & PLAYER_UPDATE_SHELL) { // 12 bytes
00405         // check for a new update
00406         if ((pid != localPlayer) && !(p->flags & PLAYER_DEAD) &&
00407         (time > p->lastUpdate.tankPosTime)) {
00408             // clear the update flag
00409             p->flags &= ~PLAYER_UPDATE_SHELL;
00410             // set the update time
00411             p->lastUpdate.shellPosTime = time;
00412             // read in the data
00413             MSG_READ_32(ptr, p->shellX);
00414             MSG_READ_32(ptr, p->shellY);
00415             MSG_READ_16(ptr, p->shellRot);
00416             MSG_READ_16(ptr, p->shellDist);
00417             // setup the shell's location
00418             p->shell.loc.x = (p->shellX >> 8) * dInfo.luh;
00419             p->shell.loc.y = (p->shellY >> 8) * dInfo.luh;
00420             // the shell (shot) flag
00421             if (flags & PLAYER_SHOT) {
00422                 // set the flag
00423                 p->flags |= PLAYER_SHOT;
00424                 // place it onscreen
00425                 QueuePlaceShell(p->pid);
00426                 #ifdef USE_AUDIO
00427                 // is this not the local player?
00428                 if ((p->pid != localPlayer) &&
00429                 !(players[localPlayer].flags & PLAYER_DEAD)) {
00430                     // compute the distance to this sound
00431                     int distsq = DistSq(p, &players[localPlayer]);
00432                     // if close enough to hear . . .
00433                     if (distsq < MAX_SQ_DIST) {
00434                         // play it
00435                         PlayTankShoot(distsq);
00436                     }
00437                 }
00438                 #endif
00439             }
00440             else {
00441                 // clear the flag
00442                 p->flags &= ~PLAYER_SHOT;
00443                 // take it off-screen
00444                 ClearRenderItem(&(p->shell));
00445                 RemoveShell(p->pid);
00446             }
00447         }
00448         else {
00449             // skip past the old or local data
00450             ptr += 12;
00451         }
00452     }
00453 
00454     // for servers . . .
00455     if (networkState == NETSTATE_SERVER) {
00456         // keep the update flags -- needed to update all clients
00457         p->flags |= flags & PLAYER_UPDATE_ITEMS;
00458     }
00459     
00460     // return the buffer cursor
00461     return ptr;
00462 }
00463 
00464 Bool HandlePlayerUpdate(SocketAddr *origin, NetBuffer *buffer, Uint8 *clientId,
00465 Uint16 messageId) {
00466     int len = 7;  // iterating part of message starts after 7 bytes
00467     Uint32 time;  // common game time of the player update
00468     Uint8 *ptr;
00469     // enforce a minimum length
00470     if (buffer->length < 12) return FALSE;
00471     // see if this process is a client
00472     if ((networkState >= NETSTATE_RCVCFG) && (networkState <= NETSTATE_CLIENT)
00473     && ValidateServer(origin)) {
00474         int numplay = 0;
00475         // assure a proper fit for all players
00476         do {
00477             // find the next length and check for an error condition
00478             if ((len = CalculateOffset(buffer, len)) < 0) {
00479                 #ifdef MSG_DEBUG
00480                 printf("Got bad player update in message %d\n", messageId);
00481                 #endif
00482                 // bad data; ignore it
00483                 return FALSE;
00484             }
00485             // count another player's data
00486             numplay++;
00487         } while (len < buffer->length);
00488         // assure proper length
00489         if (len != buffer->length) {
00490             #ifdef MSG_DEBUG
00491             printf("Got bad player update in message %d\n", messageId);
00492             printf("\tMessage length: %i   Calculated length: %i   Players: %i\n",
00493             buffer->length, len, numplay);
00494             #endif
00495             // bad data; ignore it
00496             return FALSE;
00497         }
00498         // the data length is good, so prepare to read it
00499         ptr = &(buffer->data[3]);
00500         // parse all the data starting with the time
00501         MSG_READ_32(ptr, time);
00502         // next, parse the player data
00503         for (; numplay > 0; numplay--) {
00504             ptr = ReadPlayer(ptr, time);
00505             assert((ptr - buffer->data) <= len);
00506         }
00507         assert((ptr - buffer->data) == buffer->length);
00508     }
00509     // see if this process is a server
00510     else if ((networkState == NETSTATE_SERVER) &&
00511     ValidateClient(origin, buffer->data[len]) &&  // attempt to validate origin
00512     (CalculateOffset(buffer, len) == buffer->length)) { // check its length
00513         // set the ID to acknowledge
00514         *clientId = buffer->data[7];
00515         // the data length is good, so prepare to read it
00516         ptr = &(buffer->data[3]);
00517         // parse all the data starting with the time
00518         MSG_READ_32(ptr, time);
00519         // only one player update from a client
00520         ptr = ReadPlayer(ptr, time);
00521         assert((ptr - buffer->data) == buffer->length);
00522     }
00523     else {
00524         #ifdef MSG_DEBUG
00525         printf("Got bad player update in message %d\n", messageId);
00526         #endif
00527         // bad data -- ignore this message
00528         return FALSE;
00529     }
00530     return TRUE;
00531 }
00532 
00533 Bool SendSpawnRequest() {
00534     MsgDescr *msg;
00535     // Send a message to the server to request spawning.
00536     // get the descriptor
00537     if ((msg = MessageNew()) == NULL) {
00538         // failed; nothing more can be done
00539         return FALSE;
00540     }
00541     // get a buffer
00542     if ((msg->buffer = BufferNew()) == NULL) {
00543         // failed; relase the message
00544         MessageFree(msg);
00545         return FALSE;
00546     }
00547     // setup the message descriptor
00548     msg->approveFunc = NULL;
00549     msg->ackFunc = NULL;
00550     msg->failFunc = MsgFailDropConnection;
00551     msg->buffer->length = 4;
00552     // build the response message
00553     msg->buffer->data[0] = MSG_TYPE_PLAYER_SPWAN;
00554     msg->buffer->data[3] = localPlayer;
00555     msg->bcast = TRUE;  // broadcast OK
00556     // set the destination
00557     msg->destMap[0] = 1;
00558     // add the message to the system
00559     return AddMessage(msg);
00560 }
00561 
00562 Bool HandlePlayerSpawn(SocketAddr *origin, NetBuffer *buffer, Uint8 *clientId,
00563 Uint16 noid) {
00564     Player *p;
00565     // see if this message is bogus
00566     if ((networkState != NETSTATE_SERVER) || (buffer->length != 4) ||
00567     !ValidateClient(origin, buffer->data[3])) {  // attempt to validate origin
00568         // do not ack a bogus message
00569         return FALSE;
00570     }
00571     // grab a pointer to the player
00572     p = &players[*clientId = buffer->data[3]];
00573     // see if the request to spawn should be honored
00574     if ((p->flags & PLAYER_DEAD) && (p->deadTime > DEAD_TIME)) {
00575         // spawn the player
00576         SpawnTank(p);
00577     }
00578     return TRUE;
00579 }

Generated on Mon May 28 04:41:39 2007 for Retro Tank Super Attack by  doxygen 1.5.2