//networkphysics.cpp //sends and receives physics data to and from the network //also stores and loads packets to and from the replay log, //as these share the same format as network packets. #include #include #include #include #include "network.h" #include "entities.h" #include "vectors.h" #include "carphysics.h" #include "particles.h" #include "tracks.h" #include "gameframe.h" #include "networkphysics.h" #include "log.h" #include "error.h" #include "gameinitexit.h" #include "gamemem.h" #include "gamesound.h" #include "gametime.h" #include "interfacemultiplayer.h" #include "collision.h" #include "environment.h" int gReplayIndex=0; int gSynchsRecevied=0; //compressed subset of tWheelPhysics for network broadcasts typedef struct{ unsigned char rotation,glow; char angularVelo; char slipVelo; char surfaceType; } tWheelNetPhysics; //compressed subset of tCarPhysics for network broadcasts typedef struct{ unsigned char lightFlags; unsigned char rpm,throttle; unsigned char shiftDelay,handbrake; unsigned short damage; char steering; tWheelNetPhysics wheels[kMaxWheels]; tVector3 cameraPos; } tCarNetPhysics; //a structure containing all the physics data necessary //to transmit a cars state for network broadcasts typedef struct{ tPhysicsMessage entity; tCarNetPhysics phys; } tCarPhysicsMessage; typedef struct{ tPhysicsMessage entity; char resends; char filler[sizeof(tCarNetPhysics)-1]; } tSolidPhysicsMessage; #define kMaxGameChatMessages 4 #define kMaxOutboxSize (kNetworkToolPacketSize/sizeof(tCarPhysicsMessage)) int gNumGameChatMessages=0; tGameChatMessage gGameChatMessagesRecv[kMaxGameChatMessages]; tCarPhysicsMessage gHostOutbox[kMaxOutboxSize]; int gOutboxPos=0,gOutboxSender=-1; float gLastOutboxSend=0; void GameChatMessagesScroll() { for(int i=0;i=kMaxGameChatMessages) GameChatMessagesScroll(); gGameChatMessagesRecv[gNumGameChatMessages].timestamp=TimeGetSeconds(); strcpy(gGameChatMessagesRecv[gNumGameChatMessages].message,message); gNumGameChatMessages++; } void InsertToOutbox(tCarPhysicsMessage *msg,int from) { for(int i=0;ientity.id) { int mFrame=msg->entity.frame; int bFrame=gHostOutbox[i].entity.frame; S32Swap(mFrame); S32Swap(bFrame); if(bFramex=dirZ.x*(1/32767.0); MatrixGetZVector(m)->y=dirZ.y*(1/32767.0); MatrixGetZVector(m)->z=dirZ.z*(1/32767.0); MatrixGetYVector(m)->x=dirY.x*(1/32767.0); MatrixGetYVector(m)->y=dirY.y*(1/32767.0); MatrixGetYVector(m)->z=dirY.z*(1/32767.0); *MatrixGetXVector(m)=*MatrixGetYVector(m)%*MatrixGetZVector(m); } //compresses generic entity physics data into a tPhysicsMessage tVector3 PreparePhysicsMessage(tGameEntity *entity,tPhysicsMessage *message) { message->prioritized=false; message->id=entity->id; message->pos=entity->pos; tVector3 v=Vector(0,0,0),a=Vector(0,0,0); tVector3 rz=Vector(0,0,0),ry=Vector(0,0,0); for(int i=0;i<8;i++)//smooth suspension inbalance { rz=rz+*MatrixGetZVector(entity->lastRVelos[i]); ry=ry+*MatrixGetYVector(entity->lastRVelos[i]); a=a+entity->lastAccel[i]; v=v+entity->lastVelos[i]; } a=a*1.0/8; v=entity->velo; v.y=0; a.y=0; for(int i=0;i<16;i++) v.y+=entity->lastVelos[i].y; for(int i=0;i<32;i++) a.y+=entity->lastAccel[i].y; v.y/=16; a.y/=32; if(entity->physicsType==kPhysicsTypeSolid) { rz=*MatrixGetZVector(entity->rVelo); ry=*MatrixGetYVector(entity->rVelo); v=entity->velo; } //if(sqr(v)>sqr(3)) message->velo.x=v.x*255; message->velo.y=v.y*255; message->velo.z=v.z*255; if(sqr(entity->accel)accel; message->accel.x=a.x*255; message->accel.y=a.y*255; message->accel.z=a.z*255; message->frame=gFrameCount; tVector3 z=*MatrixGetZVector(entity->dir); message->dirZ.x=fabs(z.x)<1?z.x*32767:sign(z.x)*32767; message->dirZ.y=fabs(z.y)<1?z.y*32767:sign(z.y)*32767; message->dirZ.z=fabs(z.z)<1?z.z*32767:sign(z.z)*32767; tVector3 y=*MatrixGetYVector(entity->dir); message->dirY.x=fabs(y.x)<1?y.x*32767:sign(y.x)*32767; message->dirY.y=fabs(y.y)<1?y.y*32767:sign(y.y)*32767; message->dirY.z=fabs(y.z)<1?y.z*32767:sign(y.z)*32767; rz=!Vector(rz.x,0,rz.z); ry=Vector(0,1,0); /* rz=!rz; tVector3 rx=!(ry%rz); ry=!(rz%rx);*/ message->rVeloZ.x=fabs(rz.x)<1?rz.x*32767:sign(rz.x)*32767; message->rVeloZ.y=fabs(rz.y)<1?rz.y*32767:sign(rz.y)*32767; message->rVeloZ.z=fabs(rz.z)<1?rz.z*32767:sign(rz.z)*32767; message->rVeloY.x=fabs(ry.x)<1?ry.x*32767:sign(ry.x)*32767; message->rVeloY.y=fabs(ry.y)<1?ry.y*32767:sign(ry.y)*32767; message->rVeloY.z=fabs(ry.z)<1?ry.z*32767:sign(ry.z)*32767; return rz; } #define kMinPacketDelay 5 #define kMaxPacketDelay 30 #define kMaxPacketRVeloDiff 0.005 float RVeloDiff(tMatrix3 v1,tMatrix3 v2) { float result=~(*MatrixGetXVector(v1)-*MatrixGetXVector(v2)); result+=~(*MatrixGetYVector(v1)-*MatrixGetYVector(v2)); result+=~(*MatrixGetZVector(v1)-*MatrixGetZVector(v2)); return result; } float RemoteCarDistance(tGameEntity *entity) { float minDist=INFINITY; for(int i=0;inumPlayers;i++) if(gCarEntities[i]->physicsMachine==kPhysicsRemote) { float dist=sqr(entity->pos-gCarEntities[i]->remoteCameraPos); if(distlastPacketSent; float dist=RemoteCarDistance(entity)*0.001; float score=0; float veloChangePerc; if(~entity->velo>1) { veloChangePerc=~(entity->velo-entity->lastVeloSent)/~entity->velo; /*if(veloChangePerc>0.3&&dist<0.25&&~(entity->velo-entity->lastVeloSent)>1) { //printf("fp\n"); return 2; }*/ } else veloChangePerc=~(entity->velo-entity->lastVeloSent); score+=veloChangePerc; score+=~(rz-entity->lastRZSent)*kFPS; score+=~(entity->lastDirZSent-*MatrixGetZVector(entity->dir)); score*=lastPacket; score-=dist; if(lastPacket>kFPS*0.5) return 1; else if(score<0.6||lastPacket<5) return 0; else return 1; } int SendNewPacketLog(tGameEntity *entity,tVector3 rz) { int lastPacket=gFrameCount-entity->lastPacketSaved; float score=0; float veloChangePerc; if(~entity->velo>1) { veloChangePerc=~(entity->velo-entity->lastVeloSaved)/~entity->velo; /*if(veloChangePerc>0.3&&dist<0.25&&~(entity->velo-entity->lastVeloSent)>1) { //printf("fp\n"); return 2; }*/ } else veloChangePerc=~(entity->velo-entity->lastVeloSaved); score+=veloChangePerc; score+=~(rz-entity->lastRZSaved)*kFPS; score+=~(entity->lastDirZSaved-*MatrixGetZVector(entity->dir)); score*=lastPacket; if(lastPacket>kFPS*0.5) return 1; else if(score<0.4||lastPacket<3) return 0; else return 1; } void PhysicsMessageSwap(tPhysicsMessage* m) { S32Swap(m->frame); F32Swap(m->pos.x); F32Swap(m->pos.y); F32Swap(m->pos.z); S16Swap(m->id); S16Swap(m->velo.x); S16Swap(m->velo.y); S16Swap(m->velo.z); S16Swap(m->accel.x); S16Swap(m->accel.y); S16Swap(m->accel.z); S16Swap(m->dirZ.x); S16Swap(m->dirZ.y); S16Swap(m->dirZ.z); S16Swap(m->dirY.x); S16Swap(m->dirY.y); S16Swap(m->dirY.z); S16Swap(m->rVeloZ.x); S16Swap(m->rVeloZ.y); S16Swap(m->rVeloZ.z); S16Swap(m->rVeloY.x); S16Swap(m->rVeloY.y); S16Swap(m->rVeloY.z); } //sends out a car's state to other players (and logs it for the replay log) void SendCarPhysicsMessage(tGameEntity *entity) { tCarPhysics *phys=(tCarPhysics*)entity->physics; tCarPhysicsMessage message; //compress position, etc.. tVector3 rz=PreparePhysicsMessage(entity,&message.entity); //compress car data message.phys.lightFlags=phys->lightFlags; message.phys.handbrake=phys->handbrake*255; float shiftDelay=gFrameCount*kFrameTime-phys->lastGearSwitch; message.phys.shiftDelay=(shiftDelay<1)?shiftDelay*255:255; message.phys.rpm=phys->rpm*(255.0/10000.0); message.phys.throttle=phys->throttle*255; // message.phys.brake=phys->brake*255; // message.phys.gear=phys->gear; message.phys.steering=fabs(phys->steering)<1?phys->steering*127:sign(phys->steering)*127; message.phys.damage=phys->damage; U16Swap(message.phys.damage); message.phys.cameraPos=gCameraEntity->pos; F32Swap(message.phys.cameraPos.x); F32Swap(message.phys.cameraPos.y); F32Swap(message.phys.cameraPos.z); for(int i=0;icar.numWheels;i++) { message.phys.wheels[i].rotation=phys->wheels[i].rotation*(255.0/(2*PI)); message.phys.wheels[i].slipVelo=phys->wheels[i].slipVelo; message.phys.wheels[i].surfaceType=phys->wheels[i].surfaceType; message.phys.wheels[i].glow=phys->wheels[i].glow*255.0; if(fabs(phys->wheels[i].angularVelo)<127) message.phys.wheels[i].angularVelo=phys->wheels[i].angularVelo; else message.phys.wheels[i].angularVelo=127*sign(phys->wheels[i].angularVelo); } for(int i=phys->car.numWheels;ilastPacketSaved=gFrameCount; entity->lastVeloSaved=entity->velo; entity->lastRZSaved=rz; entity->lastDirZSaved=*MatrixGetZVector(entity->dir); } //broadcast packet over network if(gGameInfo->network) if(int prio=SendNewPacket(entity,rz)) { if(prio==2) message.entity.prioritized=true; if(gGameInfo->playerID) NetworkSendPacket(kMessageTypePhysics,&message,sizeof(tCarPhysicsMessage),kMessagePriorityNormal,kMessageSendToHostOnly); else InsertToOutbox(&message,0); entity->lastPacketSent=gFrameCount; entity->lastVeloSent=entity->velo; entity->lastRZSent=rz; entity->lastDirZSent=*MatrixGetZVector(entity->dir); } } void SendSolidPhysicsMessage(tGameEntity *entity,int resends) { static int lastRockUpdate=0; if(entity->lastFrame==gFrameCount&&gFrameCount>0) { tSolidPhysicsMessage message; for(int i=0;inetwork) if(gGameInfo->playerID) { if(gFrameCountlastRockUpdate+kFPS*0.25) { NetworkSendPacket(kMessageTypePhysics,&message,sizeof(tSolidPhysicsMessage),kMessagePriorityHigh,kMessageSendToHostOnly); lastRockUpdate=gFrameCount; } else entity->lastFrame++; } else InsertToOutbox((tCarPhysicsMessage*)&message,0); } } void AddRockUpdateToOutbox() { tGameEntity *entity=(tGameEntity*)gFirstEntity->next; if(gOutboxPos==kMaxOutboxSize) return; while(entity!=gFirstEntity) { if(entity->physicsType==kPhysicsTypeSolid) { tSolidEntityPhysics *ent=(tSolidEntityPhysics*)FileGetParsedDataPtr(entity->physicsData,kParserTypeSolidEntityDesc,sizeof(tSolidEntityPhysics)); if(ent->movable&&ent->mass>=kSolidEntityNetworkMass) if(entity->lastActivitylastFramenumPlayers;i++) if(sqr(gCarEntities[i]->pos-entity->pos)pos-entity->pos); if(minDist>sqr(80)) { entity->lastFrame=gFrameCount; SendSolidPhysicsMessage(entity,0); return; } } } entity=(tGameEntity*)entity->next; } } void CleanOutbox() { for(int i=0;inext; //see if we find the entity it belongs to while(entity!=gFirstEntity&&iid==id) { if(entity->physicsType==kPhysicsTypeSolid) { if(((tSolidPhysicsMessage*)gHostOutbox+i)->resends==0||((tSolidPhysicsMessage*)gHostOutbox+i)->resends>3) { memmove(gHostOutbox+i,gHostOutbox+i+1,sizeof(tSolidPhysicsMessage)*(gOutboxPos-i-1)); gOutboxPos--; entity=gFirstEntity; } else ((tSolidPhysicsMessage*)gHostOutbox+i)->resends--; } else { memmove(gHostOutbox+i,gHostOutbox+i+1,sizeof(tSolidPhysicsMessage)*(gOutboxPos-i-1)); gOutboxPos--; entity=gFirstEntity; } } entity=(tGameEntity*)entity->next; } } } //sends out physics information for this frame void NetworkSendPhysics() { tGameEntity *entity=(tGameEntity*)gFirstEntity->next; while(entity!=gFirstEntity) { if(entity->physicsMachine==kPhysicsLocal) { switch(entity->physicsType) { case kPhysicsTypeCar: SendCarPhysicsMessage(entity); break; case kPhysicsTypeSolid: SendSolidPhysicsMessage(entity,2); break; } } entity=(tGameEntity*)entity->next; } float t=TimeGetSeconds(); if(gGameInfo->playerID==0&&gGameInfo->network) { int fastPath=false; for(int i=0;i0&&(gLastOutboxSendpos=Vector(-10000,-10000,-10000); ent->netPos=Vector(-10000,-10000,-10000); ent->velo=Vector(0,0,0); ent->id=-1; tCarPhysics *phys=(tCarPhysics*)ent->physics; phys->position=0; phys->checkPoint=0; for(int i=0;ilapTimes[i]=0; gGameInfo->netID[i]=-1; gGameInfo->playerCarNames[i][0]='\0'; int numPlayers=0; for(int i=0;inumPlayers;i++) if(gCarEntities[i]->id>=0) numPlayers++; if(numPlayers==1) { tCarPhysics *lastPhys=(tCarPhysics*)gCarEntities[0]; lastPhys->lead=0; } if(gReplayViewedEntityID==i) { gViewedEntity=gCarEntities[0]; gReplayViewedEntityID=0; } } //receive physics data from other players on the network void NetworkReceivePhysics() { if(gGameInfo->network) { tNetworkPacket message; int exit=false; while(NetworkReceivePacket(&message)) { switch(message.what) { case kMessageTypePhysics: if(gGameInfo->playerID==0) InsertToOutbox((tCarPhysicsMessage*)message.data,message.from); //we have received a physics state update for(int i=0;iid-gCarEntities[0]->id,physMessage->frame,gFrameCount); tGameEntity *entity=(tGameEntity*)gFirstEntity->next; //see if we find the entity it belongs to while(entity!=gFirstEntity) { //is this the entity the message refers to? if(entity->id==physMessage->id&&entity!=gCarEntities[gGameInfo->playerID]) //is this the most recent physics update for the entity? //(we want to avoid packets coming in in the wrong order messung up everything) if(entity->lastFrameframe)//&&entity->lastCollFrame<=physMessage->frame) { //entity->lastCollFrame=0; //decompress physics info entity->netPos=physMessage->pos; entity->netVelo.x=physMessage->velo.x/255.0; entity->netVelo.y=physMessage->velo.y/255.0; entity->netVelo.z=physMessage->velo.z/255.0; entity->collVelo=entity->netVelo; entity->accel.x=physMessage->accel.x/255.0; entity->accel.y=physMessage->accel.y/255.0; entity->accel.z=physMessage->accel.z/255.0; entity->lastFrame=physMessage->frame; if(entity->lastFrame>gFrameCount) entity->lastFrame=gFrameCount; ShortVectorsToMatrix(physMessage->dirZ,physMessage->dirY,entity->netDir); ShortVectorsToMatrix(physMessage->rVeloZ,physMessage->rVeloY,entity->rVelo); //is this a car? if(entity->physicsType==kPhysicsTypeCar) { //decompress car physics info tCarPhysics *phys=(tCarPhysics*)entity->physics; tCarNetPhysics *inPhys=&(((tCarPhysicsMessage*)message.data)+i)->phys; phys->lightFlags=inPhys->lightFlags; phys->rpm=inPhys->rpm/(255.0/10000.0); phys->throttle=inPhys->throttle/255.0; phys->steering=inPhys->steering/127.0; phys->handbrake=inPhys->handbrake/255.0; // phys->brake=inPhys->brake/255.0; // phys->gear=inPhys->gear; phys->lastGearSwitch=gFrameCount*kFrameTime-inPhys->shiftDelay/255.0; unsigned short damage=inPhys->damage; U16Swap(damage); phys->damage=damage; for(int i=0;iwheels[i].rotation=inPhys->wheels[i].rotation/(255.0/(2*PI)); phys->wheels[i].slipVelo=inPhys->wheels[i].slipVelo; phys->wheels[i].surfaceType=inPhys->wheels[i].surfaceType; phys->wheels[i].glow=inPhys->wheels[i].glow/255.0; phys->wheels[i].angularVelo=inPhys->wheels[i].angularVelo; } entity->remoteCameraPos=inPhys->cameraPos; F32Swap(entity->remoteCameraPos.x); F32Swap(entity->remoteCameraPos.y); F32Swap(entity->remoteCameraPos.z); //dead recogning int frameDiff=gFrameCount-physMessage->frame; //entity->netPos=entity->netPos+entity->netVelo*kFrameTime*frameDiff+entity->accel*kFrameTime*kFrameTime*frameDiff*frameDiff*0.5; //entity->netVelo=entity->netVelo+entity->accel*kFrameTime*frameDiff; int collIndex=kNumLastCollisions-1; while(frameDiff>0) { while(phys->lastCollisions[collIndex].frameCountregData=NULL; if(phys->lastCollisions[collIndex].frameCount==gFrameCount-frameDiff) ApplyImpulse(entity,phys->lastCollisions[collIndex].attackPoint,phys->lastCollisions[collIndex].veloDiff,phys->lastCollisions[collIndex].rotationFactor,true); entity->netPos=entity->netPos+entity->netVelo*kFrameTime; entity->netVelo=entity->netVelo+entity->accel*kFrameTime; MatrixMult(entity->netDir,entity->rVelo,entity->netDir); frameDiff--; } } else if(entity->physicsType==kPhysicsTypeSolid) { entity->pos=entity->netPos; entity->velo=entity->netVelo; MatrixCopy(entity->netDir,entity->dir); int frameDiff=gFrameCount-physMessage->frame; while(frameDiff>0) { if(entity->lastActivity+kFPS>gFrameCount-frameDiff) { entity->velo.y-=gEnvironment->gravity*kFrameTime; float dampfactor=0; float v=~entity->velo; if(v<7)dampfactor=(7-v)/7; *MatrixGetXVector(entity->rVelo)=Vector(1,0,0)+(1-dampfactor*kFrameTime)*(*MatrixGetXVector(entity->rVelo)-Vector(1,0,0)); *MatrixGetYVector(entity->rVelo)=Vector(0,1,0)+(1-dampfactor*kFrameTime)*(*MatrixGetYVector(entity->rVelo)-Vector(0,1,0)); *MatrixGetZVector(entity->rVelo)=Vector(0,0,1)+(1-dampfactor*kFrameTime)*(*MatrixGetZVector(entity->rVelo)-Vector(0,0,1)); entity->velo=(1-dampfactor*kFrameTime)*entity->velo; entity->pos=entity->pos+entity->velo*kFrameTime; MatrixMult(entity->dir,entity->rVelo,entity->dir); } MatrixReAdjust(entity->rVelo); //Check if the entity is moving, and update lastActivity if(entity->velo*entity->velo>kMinActivityVelo*kMinActivityVelo) entity->lastActivity=gFrameCount-frameDiff; if(entity->lastActivity+kFPS>gFrameCount-frameDiff) if(!((gFrameCount-frameDiff)% kSolidCollisionRate)) SolidCheckCollision(entity); frameDiff--; } } /*if(physMessage->prioritized) { entity->pos=entity->netPos; entity->velo=entity->netVelo; MatrixCopy(entity->netDir,entity->dir); }*/ } entity=(tGameEntity*)entity->next; } } break; case kMessageTypeChat: InsertGameChatMessage(((tChatMessage*)message.data)->str); ChatBufferInsert(((tChatMessage*)message.data),gChatBuffer); if(((tChatMessage*)message.data)->flags&(kChatFlagSystem|kChatFlagAlert)) PlayInterfaceSound(FileGetReference("systemchat.wav")); else PlayInterfaceSound(FileGetReference("chat.wav")); break; case kMessageTypeBye: { if(message.from==0) sprintf(gDisconnectString,"%s quit and stopped hosting.",gGameInfo->playerNames[0]); int netID=message.from; int id=-1; //find the players id for(int i=0;inumNetPlayers;i++) if(gGameInfo->netID[i]==netID) id=i; if(id!=-1) { gGameInfo->playerVersions[id]=-2; /*tChatMessage m; m.flags=kChatFlagSystem; sprintf(m.str,"### %s is quitting.",gGameInfo->playerNames[id]); SoundReInit(); PlayInterfaceSound(FileGetReference("systemchat.wav")); InsertGameChatMessage(m.str); ChatBufferInsert(&m,gChatBuffer);*/ } } break; case kMessageTypeKicked: sprintf(gDisconnectString,"You have been disconnected by the host."); case kMessageTypeGameTerminated: gGameEnd=kEndGame; exit=true; break; case kMessageTypeEndGame: gGameEnd=kEndGameNoLeave; break; case kMessageTypePause: if(gNetPauseTime==0) { gNetPauseTime=*(int*)message.data; S32Swap(gNetPauseTime); } break; case kMessageTypeSynch: gSynchsRecevied++; break; case kMessageTypeResume: if(gPaused) { gNetPauseTime=0; for(int i=1;inumNetPlayers;i++) if(gGameInfo->netID[i]==-1) gSynchsRecevied++; NetworkSynch(gGameInfo->numNetPlayers-gSynchsRecevied); gSynchsRecevied=0; UnPauseGame(); } break; case kMessageTypePlayerLeft: { int netID=message.from; int id=-1; //find the players id for(int i=0;inumPlayers;i++) if(gGameInfo->netID[i]==netID) id=i; if(id!=-1) { tChatMessage m; m.flags=kChatFlagSystem; if(gGameInfo->playerVersions[id]!=-2) sprintf(m.str,"### %s is disconnected due to network problems.",gGameInfo->playerNames[id]); else sprintf(m.str,"### %s quit the game.",gGameInfo->playerNames[id]); SoundReInit(); PlayInterfaceSound(FileGetReference("systemchat.wav")); InsertGameChatMessage(m.str); ChatBufferInsert(&m,gChatBuffer); KillPlayer(id); } } break; case kMessageTypeID: NetworkQueuePacket(&message); exit=true; break; case kMessageTypeFinishTime: { tFinishTimeMessage *m=(tFinishTimeMessage*)message.data; S32Swap(m->time); S32Swap(m->player); //printf("%d,%d\n",m->time,m->player); tCarPhysics *phys=(tCarPhysics*)gCarEntities[m->player]->physics; phys->finishTime=m->time; } break; case kMessageTypePlayerJoined: if(gGameInfo->demolition||gGameInfo->numNetPlayers<=1) { NetworkQueuePacket(&message); gGameEnd=kEndGameNoLeave; exit=true; } else { UInt8 reason=kJoinDeniedInProgress; NetworkSendPacket(kMessageTypeJoinDenied,&reason,sizeof(UInt8),kMessagePriorityHigh,kMessageSendToAllButSelf); } break; } //release the message. NetworkDisposePacket(&message); if(exit) return; } } } #define kMaxLookAhead 0.5 //similar to NetworkReceivePhysics but does not wait for packets from the network, //instead it processes packets in the replay log. void LogReplayFrame() { int type,size; char message[1024]; while(LogGetPacket(gReplayIndex,&type,&size,gGameInfo->numLaps==-1?kLogGhostLog:kLogReplayLog,message)) { switch(type) { case kMessageTypePhysics: { tPhysicsMessage *physMessage=(tPhysicsMessage*)((char*)message); PhysicsMessageSwap(physMessage); //if(physMessage->idid) // physMessage->id=gCarEntities[0]->id; if(physMessage->frame>gFrameCount)return; tGameEntity *entity=(tGameEntity*)gFirstEntity->next; //see if we find the entity it belongs to while(entity!=gFirstEntity) { //is this the entity the message refers to? if(entity->id==physMessage->id) { //decompress physics info entity->pos=physMessage->pos; entity->velo.x=physMessage->velo.x/255.0; entity->velo.y=physMessage->velo.y/255.0; entity->velo.z=physMessage->velo.z/255.0; entity->accel.x=physMessage->accel.x/255.0; entity->accel.y=physMessage->accel.y/255.0; entity->accel.z=physMessage->accel.z/255.0; // ShortVectorsToMatrix(physMessage->dirZ,physMessage->dirY,entity->dir); ShortVectorsToMatrix(physMessage->dirZ,physMessage->dirY,entity->netDir); ShortVectorsToMatrix(physMessage->rVeloZ,physMessage->rVeloY,entity->rVelo); //is this a car? if(entity->physicsType==kPhysicsTypeCar) { //decompress car physics info tCarPhysics *phys=(tCarPhysics*)entity->physics; tCarNetPhysics *inPhys=&((tCarPhysicsMessage*)message)->phys; phys->lightFlags=inPhys->lightFlags; phys->rpm=inPhys->rpm/(255.0/10000.0);; phys->throttle=inPhys->throttle/255.0; phys->steering=inPhys->steering/127.0; phys->handbrake=inPhys->handbrake/255.0; // phys->brake=inPhys->brake/255.0; // phys->gear=inPhys->gear; phys->lastGearSwitch=gFrameCount*kFrameTime-inPhys->shiftDelay/255.0; unsigned short damage=inPhys->damage; U16Swap(damage); phys->damage=damage; for(int i=0;iwheels[i].rotation=inPhys->wheels[i].rotation/(255.0/(2*PI)); phys->wheels[i].slipVelo=inPhys->wheels[i].slipVelo; phys->wheels[i].surfaceType=inPhys->wheels[i].surfaceType; phys->wheels[i].glow=inPhys->wheels[i].glow/255.0; phys->wheels[i].angularVelo=inPhys->wheels[i].angularVelo; } } } entity=(tGameEntity*)entity->next; } } break; } gReplayIndex++; } } void LogReplayGhostFrame() { int type,size; char message[1024]; while(LogGetPacket(gReplayIndex,&type,&size,kLogGhostLog,message)) { switch(type) { case kMessageTypePhysics: { tPhysicsMessage *physMessage=(tPhysicsMessage*)message; PhysicsMessageSwap(physMessage); if(physMessage->frame>gFrameCount-gCurrentLapStart+gBestLapStart) return; gGhostEntity->pos=physMessage->pos; gGhostEntity->velo.x=physMessage->velo.x/255.0; gGhostEntity->velo.y=physMessage->velo.y/255.0; gGhostEntity->velo.z=physMessage->velo.z/255.0; gGhostEntity->lastFrame=physMessage->frame; ShortVectorsToMatrix(physMessage->dirZ,physMessage->dirY,gGhostEntity->dir); tCarPhysics *phys=(tCarPhysics*)gGhostEntity->physics; tCarNetPhysics *inPhys=(tCarNetPhysics*)((char*)message+sizeof(tPhysicsMessage)); phys->lightFlags=inPhys->lightFlags; phys->rpm=inPhys->rpm/(255.0/10000.0);; phys->throttle=inPhys->throttle/255.0; phys->steering=inPhys->steering/127.0; for(int i=0;iwheels[i].rotation=inPhys->wheels[i].rotation/(255.0/(2*PI)); phys->wheels[i].slipVelo=inPhys->wheels[i].slipVelo; phys->wheels[i].surfaceType=inPhys->wheels[i].surfaceType; } } break; } gReplayIndex++; } gGhostEntity->pos=Vector(10000,10000,10000); gGhostEntity->velo=Vector(0,0,0); }