Redline/source/networkphysics.cpp
2016-04-04 17:08:13 +02:00

953 lines
29 KiB
C++

//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 <string.h>
#include <stdio.h>
#include <math.h>
#include <AmbrosiaTools/network_tool.h>
#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<gNumGameChatMessages-1;i++)
gGameChatMessagesRecv[i]=gGameChatMessagesRecv[i+1];
gNumGameChatMessages--;
}
void InsertGameChatMessage(char *message)
{
while(gNumGameChatMessages>=kMaxGameChatMessages)
GameChatMessagesScroll();
gGameChatMessagesRecv[gNumGameChatMessages].timestamp=TimeGetSeconds();
strcpy(gGameChatMessagesRecv[gNumGameChatMessages].message,message);
gNumGameChatMessages++;
}
void InsertToOutbox(tCarPhysicsMessage *msg,int from)
{
for(int i=0;i<gOutboxPos;i++)
if(gHostOutbox[i].entity.id==msg->entity.id)
{
int mFrame=msg->entity.frame;
int bFrame=gHostOutbox[i].entity.frame;
S32Swap(mFrame);
S32Swap(bFrame);
if(bFrame<mFrame)
gHostOutbox[i]=*msg;
return;
}
if(gOutboxPos<kMaxOutboxSize)
gHostOutbox[gOutboxPos++]=*msg;
if(gOutboxPos==1)
gOutboxSender=from;
else if(gOutboxSender!=from)
gOutboxSender=-1;
}
//converts 6 shorts to a 3x3 rotation matrix
void ShortVectorsToMatrix(tVector3Short dirZ,tVector3Short dirY,tMatrix3 m)
{
MatrixGetZVector(m)->x=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)<sqr(a))
a=entity->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;i<gGameInfo->numPlayers;i++)
if(gCarEntities[i]->physicsMachine==kPhysicsRemote)
{
float dist=sqr(entity->pos-gCarEntities[i]->remoteCameraPos);
if(dist<minDist)
minDist=dist;
}
return sqrt(minDist);
}
int SendNewPacket(tGameEntity *entity,tVector3 rz)
{
int lastPacket=gFrameCount-entity->lastPacketSent;
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;i<phys->car.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;i<kMaxWheels;i++)
{
message.phys.wheels[i].rotation=0;
message.phys.wheels[i].slipVelo=0;
message.phys.wheels[i].surfaceType=0;
message.phys.wheels[i].glow=0;
message.phys.wheels[i].angularVelo=0;
}
PhysicsMessageSwap(&message.entity);
if(SendNewPacketLog(entity,rz))
{
LogPacket(sizeof(tCarPhysicsMessage),&message,kMessageTypePhysics);
entity->lastPacketSaved=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;i<sizeof(tCarNetPhysics)-1;i++)
message.filler[i]=0;
PreparePhysicsMessage(entity,&message.entity);
message.resends=resends;
PhysicsMessageSwap(&message.entity);
LogPacket(sizeof(tSolidPhysicsMessage),&message,kMessageTypePhysics);
if(gGameInfo->network)
if(gGameInfo->playerID)
{
if(gFrameCount<lastRockUpdate)
lastRockUpdate=0;
if(gFrameCount>lastRockUpdate+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->lastActivity<gFrameCount-kFPS)
if(entity->lastFrame<gFrameCount-kFPS)
{
float minDist=INFINITY;
for(int i=0;i<gGameInfo->numPlayers;i++)
if(sqr(gCarEntities[i]->pos-entity->pos)<minDist)
minDist=sqr(gCarEntities[i]->pos-entity->pos);
if(minDist>sqr(80))
{
entity->lastFrame=gFrameCount;
SendSolidPhysicsMessage(entity,0);
return;
}
}
}
entity=(tGameEntity*)entity->next;
}
}
void CleanOutbox()
{
for(int i=0;i<gOutboxPos;i++)
{
tGameEntity *entity=(tGameEntity*)gFirstEntity->next;
//see if we find the entity it belongs to
while(entity!=gFirstEntity&&i<gOutboxPos)
{
//is this the entity the message refers to?
short id=gHostOutbox[i].entity.id;
S16Swap(id);
if(entity->id==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;i<gOutboxPos;i++)
if(gHostOutbox[i].entity.prioritized)
fastPath=true;
if(gOutboxPos>0&&(gLastOutboxSend<t-0.06||gOutboxPos==kMaxOutboxSize||fastPath))//send packet every 60ms
{
AddRockUpdateToOutbox();
if(gOutboxSender!=-1)
NetworkSendPacket(kMessageTypePhysics,gHostOutbox,sizeof(tCarPhysicsMessage)*gOutboxPos,kMessagePriorityNormal,kMessageSendToAllButSelf,gOutboxSender);
else
NetworkSendPacket(kMessageTypePhysics,gHostOutbox,sizeof(tCarPhysicsMessage)*gOutboxPos,kMessagePriorityNormal,kMessageSendToAllButSelf);
gLastOutboxSend=t;
CleanOutbox();
}
}
}
void KillPlayer(int i)
{
//PrintConsoleString("Removing Player %d from game",i);
tGameEntity *ent=gCarEntities[i];
ent->pos=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;i<kMaxLaps+1;i++)
phys->lapTimes[i]=0;
gGameInfo->netID[i]=-1;
gGameInfo->playerCarNames[i][0]='\0';
int numPlayers=0;
for(int i=0;i<gGameInfo->numPlayers;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;i<message.size/sizeof(tCarPhysicsMessage);i++)
{
//log the packet for replay
LogPacket(sizeof(tCarPhysicsMessage),(char*)message.data+i*sizeof(tCarPhysicsMessage),kMessageTypePhysics);
tPhysicsMessage *physMessage=(tPhysicsMessage*)((char*)message.data+i*sizeof(tCarPhysicsMessage));
PhysicsMessageSwap(physMessage);
//printf("rec: %d. %d,%d\n",physMessage->id-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->lastFrame<physMessage->frame)//&&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;i<kMaxWheels;i++)
{
phys->wheels[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].frameCount<gFrameCount-frameDiff)
collIndex++;
entity->regData=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;i<gGameInfo->numNetPlayers;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;i<gGameInfo->numNetPlayers;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;i<gGameInfo->numPlayers;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->id<gCarEntities[0]->id)
// 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;i<kMaxWheels;i++)
{
phys->wheels[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;i<kMaxWheels;i++)
{
phys->wheels[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);
}