#include #include "SDL.h" #include "SDL_net.h" // Requires SDL_net!! #include "InSim.h" // As provided by Scawen #include #include #include #include #include #include #include "irrXML.h" // Include the irrlicht XML engine #include #include #include #include using namespace std; using namespace irr; using namespace io; #undef main // Define some used data IS_STA CurrentState; // Contains: viewed Player, number of RaceLaps unsigned long int time; // A race is an array of laps: struct Lap { // A lap has up to 32 drivers that each scored a Lapresult // Prepare some additional space for people who join and leave unsigned char nextResult; // Holds the last written result IS_LAP Results[64]; float fuel[64]; }; // The laptime of the third car that crossed the line to complete lap 5 would be Laps[4].Results[3].Laptime Lap* pLaps; // We don't know how many laps there are yet // A qualification only has one table of data struct Quali { IS_RES Results[256]; // Stores one result per each PLID unsigned short PLID[3]; // Last 3 watched PLIDs float fuel[3]; // Corresponding fuel }; Quali* pQuali; struct Pitboard { unsigned char PLID; // For which player? unsigned short Position; unsigned char Lap; // Report for which lap? unsigned char TotalLaps; unsigned long int Laptime; long int GapForward; long int GapBackward; char Pit[4]; // Pit instruction ("PIT", "DT", "S&G", etc.) char Advice[24]; unsigned long int BirthTime; }; Pitboard newPitboard; Pitboard oldPitboard; void setFuel(float fNecessaryFuel, TCPsocket tcpsock) { // Uses the F12 menu to set the fuel for the driver unsigned int iNecessaryFuel = (unsigned int)(fNecessaryFuel*100) +1; if (iNecessaryFuel > 99) iNecessaryFuel = 99; cout << "Setting fuel to " << iNecessaryFuel << "%" << endl; unsigned int iNecRightPresses = iNecessaryFuel-1; // Necessary right presses is one smaller than the necessary fuel IS_MST* pSetFuel = new IS_MST[1+99+iNecRightPresses+1]; IS_MST OneCommand = { 68, ISP_MST, 0, 0, '0'}; strcpy(OneCommand.Msg, "/press F12"); *(pSetFuel+0) = OneCommand; // Open F12 menu strcpy(OneCommand.Msg, "/press left"); for (int c = 1; c < 100; c++) // Press left 99 times so 1% is reached memcpy((char*)(pSetFuel+c), (char*)&OneCommand, 68); strcpy(OneCommand.Msg, "/press right"); for (int d = 100; d < 100+iNecRightPresses; d++) // Press right until the desired amount of fuel is set memcpy((char*)(pSetFuel+d), (char*)&OneCommand, 68); strcpy(OneCommand.Msg, "/press F12"); *(pSetFuel+1+99+iNecRightPresses) = OneCommand; // Close F12 menu memcpy((char*)(pSetFuel+1+99+iNecRightPresses), (char*)&OneCommand, 68); SDLNet_TCP_Send(tcpsock, (char*)pSetFuel, sizeof(IS_MST)*(1+99+iNecRightPresses+1)); // Make LFS execute the list of commands cout << sizeof(IS_MST)*(1+99+iNecRightPresses+1) << " bytes sent." << endl; char NewAdvice[12] = "SET "; // Prepare the advice-line of the pitboard with the information itoa(iNecessaryFuel, &(NewAdvice[4]), 10); strcat(NewAdvice, "% F"); NewAdvice[12] = 0; cout << "Set Advice: " << NewAdvice; strcpy(&(newPitboard.Advice[0]), NewAdvice); return; } int main(int argc, char *argv[]) { cout << "LFSpitboard by Vain. Version R3." << endl; cout << "Reading cfg.xml using IrrXML... " << endl; unsigned int InSimPort; unsigned int OutGaugePort; string Password; unsigned int BoardTimeOut; string LFSlocation; bool startLFS = 0; bool InvokeInsim = 0; IrrXMLReader* xml = createIrrXMLReader("cfg.xml"); while (xml && xml->read()) { if (!strcmp("InSim", xml->getNodeName())) { InSimPort = xml->getAttributeValueAsInt("port"); cout << "Read InSim Port: " << InSimPort << endl; } if (!strcmp("OutGauge", xml->getNodeName())) OutGaugePort = xml->getAttributeValueAsInt("port"); if (!strcmp("Password", xml->getNodeName())) Password = xml->getAttributeValue("value"); if(!strcmp("BoardTimeOut", xml->getNodeName())) BoardTimeOut = 1000 * xml->getAttributeValueAsInt("value"); if(!strcmp("LFSExec", xml->getNodeName()) ) { LFSlocation = xml->getAttributeValue("location"); startLFS = (bool)(xml->getAttributeValueAsInt("start")); InvokeInsim = (bool)(xml->getAttributeValue("InvokeInSim")); } } if (!InSimPort) InSimPort = 12500; if (!OutGaugePort) OutGaugePort = 12501; if (!BoardTimeOut) BoardTimeOut = 15; cout << "Done!" << endl; if(SDL_Init(0)==-1) { printf("SDL_Init: %s\n", SDL_GetError()); exit(1); } if(SDLNet_Init()==-1) { printf("SDLNet_Init: %s\n", SDLNet_GetError()); exit(2); } cout << "Initialization done!" << endl; // Init LFS if requested: if (startLFS && InvokeInsim && LFSlocation.c_str()) { cout << "Starting LFS!" << endl; string commandline; if (InvokeInsim) { // strcat(&(commandline[0]), " /insim="); commandline += "/insim="; char ISport[9]; itoa(InSimPort, &(ISport[0]), 10); // strcat(&(commandline[0]), &(ISport[0])); commandline += ISport; } cout << "Starting LFS with " << LFSlocation.c_str() << "LFS.exe" << " " << commandline.c_str() << endl; if (_chdir(LFSlocation.c_str())) cout << "Failed to open LFS location!" << endl; if (spawnl(P_NOWAIT, "LFS.exe", "LFS.exe", commandline.c_str(), NULL) == -1) cout << "Failed to start LFS!" << endl; /*LFSlocation = "LFS.exe "+commandline; system(LFSlocation.c_str());*/ } cout << "Waiting for InSim... "; // connect to localhost at port 12500 using TCP (client) IPaddress ip; TCPsocket tcpsock; if(SDLNet_ResolveHost(&ip,"localhost",InSimPort)==-1) { printf("SDLNet_ResolveHost: %s\n", SDLNet_GetError()); exit(1); } tcpsock=SDLNet_TCP_Open(&ip); while (!tcpsock) { // cout << "Failed. Retrying... "; tcpsock=SDLNet_TCP_Open(&ip); } SDLNet_SocketSet mySSet = SDLNet_AllocSocketSet(5); if (mySSet == 0) { cout << "Failed to allocate socket set! Exiting!" << endl; exit(2); } if (!SDLNet_TCP_AddSocket(mySSet, tcpsock) ) { cout << "Failed to add socket to socket set! Exiting!" << endl; exit(2); } cout << "Connected!" << endl; bool done = false; bool started = false; bool PitIsMandatory = false; time = 0; IS_TINY header; // Prepare a IS_ISI packet: IS_ISI* pIS_ISI = new IS_ISI; pIS_ISI->Size = 44; pIS_ISI->Type = 1; pIS_ISI->ReqI = 1; pIS_ISI->UDPPort = OutGaugePort; pIS_ISI->Flags = ISF_LOCAL; pIS_ISI->Sp0 = 0; pIS_ISI->Prefix = 0; pIS_ISI->Interval = 0; strcpy(&(pIS_ISI->Admin[0]), Password.c_str()); pIS_ISI->Admin[15] = 0; strcpy(&(pIS_ISI->IName[0]), "LFSPitboard"); pIS_ISI->IName[15] = 0; SDLNet_TCP_Send(tcpsock, (void*)pIS_ISI, sizeof(IS_ISI)); cout << "Sent ISI. Waiting for reply..." << endl; IS_VER tmpVER;// LFS should reply with a IS_VER, receive it: SDLNet_TCP_Recv(tcpsock,&tmpVER,20); cout << "InSim version is " << (unsigned short)(tmpVER.InSimVer) << " - "; if (tmpVER.InSimVer != 4) { cout << "LFSpitboard only supports InSim version 4. Exiting." << endl; exit(2); } cout << "accepted." << endl << "Requesting OutGauge..." << endl; IS_SMALL tmpSmall; tmpSmall.ReqI = 0; tmpSmall.Type = ISP_SMALL; tmpSmall.SubT = SMALL_SSG; tmpSmall.UVal = 10000; tmpSmall.Size = sizeof(tmpSmall); SDLNet_TCP_Send(tcpsock, (char*)&tmpSmall, sizeof(tmpSmall)); UDPsocket udpsock; udpsock=SDLNet_UDP_Open(12501); if(SDLNet_ResolveHost(&ip,"localhost",OutGaugePort)==-1) { printf("SDLNet_ResolveHost: %s\n", SDLNet_GetError()); exit(1); } if (!SDLNet_UDP_Bind(udpsock, 1, &ip)) { cout << "Failed to bind to localhost! Exiting!" << endl; exit(2); } if (!udpsock) { cout << "Failed to initialize UDP: " << SDLNet_GetError() << " - Exiting!" << endl; exit(2); } cout << "Waiting for restart..." << endl; while (!done) // Main loop { // Start receiving if (SDLNet_CheckSockets(mySSet,100)) { if (SDLNet_TCP_Recv(tcpsock,&header,4) <= 0) // Receive only the header of the next packet { cout << "Failed to receive data: " << SDLNet_GetError() << " - Setting exit-flag!" << endl; done = 1; } else // We received a header { // Think about follow-up data and handle the recieved data switch(header.Type) { case ISP_TINY: switch(header.SubT) { case TINY_NONE: IS_TINY tmpTINY = { 4, ISP_TINY, 0, TINY_NONE }; SDLNet_TCP_Send(tcpsock,&tmpTINY, sizeof(tmpTINY)); break; } break; cout << "Between TINY and STA!" << endl; case ISP_STA: IS_STA tmpSTA; SDLNet_TCP_Recv(tcpsock,((char*)&tmpSTA)+4, 24); // Receive the rest of the packet // Do something? Did anything noteworthy change? if ((tmpSTA.RaceInProg == 0) && (started == 1)) { cout << "Session ended! Stopping tracking! Clearing buttons!" << endl; started = 0; delete pLaps; pLaps = 0; IS_BFN newBFN = { 8, ISP_BFN, 0, BFN_CLEAR, 0, 0, 0, 0}; SDLNet_TCP_Send(tcpsock, (char*)&newBFN, sizeof(newBFN)); } CurrentState = tmpSTA; if (started && (CurrentState.ViewPLID != newPitboard.PLID) && (CurrentState.RaceInProg == 1)) { // Clear the pitboard if its player isn't viewed IS_BFN newBFN = { 8, ISP_BFN, 0, BFN_CLEAR, 0, 0, 0, 0}; SDLNet_TCP_Send(tcpsock, (char*)&newBFN, sizeof(newBFN)); } if (started && (CurrentState.ViewPLID != oldPitboard.PLID) && (CurrentState.RaceInProg == 2)) { // Clear the pitboard if it's player isn't viewed IS_BFN newBFN = { 8, ISP_BFN, 0, BFN_CLEAR, 0, 0, 0, 0}; SDLNet_TCP_Send(tcpsock, (char*)&newBFN, sizeof(newBFN)); } break; cout << "Between STA and LAP!" << endl; case ISP_LAP: IS_LAP tmpLAP; SDLNet_TCP_Recv(tcpsock,((char*)&tmpLAP)+4,16); tmpLAP.PLID = header.SubT; // Complete the IS_LAP with data from the header if ((started) && (CurrentState.RaceInProg == 1)) // It's a race { cout << "New registered laptime by PLID " << (unsigned short)(tmpLAP.PLID) << ": " << tmpLAP.LTime/60000 << ":" << (tmpLAP.LTime/1000)%60 << "." << (tmpLAP.LTime%1000)/10 << endl; if ( ( (pLaps+tmpLAP.LapsDone-1)->nextResult <= 64 ) && pLaps && (tmpLAP.LapsDone <= CurrentState.RaceLaps) ) // Make sure we know where to write { // cout << "Reading: (pLaps+" << (unsigned short)(tmpLAP.LapsDone)-1 << ")->nextResult = " << (unsigned short)((pLaps+tmpLAP.LapsDone-1)->nextResult) << endl; // cout << "Writing: (pLaps+" << (unsigned short)(tmpLAP.LapsDone)-1 << ")->Results[" << (unsigned short)((pLaps+tmpLAP.LapsDone-1)->nextResult) << "].LTIME = " << tmpLAP.LTime/60000 << ":" << (tmpLAP.LTime/1000)%60 << "." << (tmpLAP.LTime%1000)/10 << endl; (pLaps+tmpLAP.LapsDone-1)->Results[(pLaps+tmpLAP.LapsDone-1)->nextResult] = tmpLAP; // cout << "Reading: (pLaps+" << (unsigned short)(tmpLAP.LapsDone)-1 << ")->Results[" << (unsigned short)((pLaps+tmpLAP.LapsDone-1)->nextResult) << "].PLID = " << (unsigned short)((pLaps+tmpLAP.LapsDone-1)->Results[(pLaps+tmpLAP.LapsDone-1)->nextResult].PLID) << endl; (pLaps+tmpLAP.LapsDone-1)->nextResult += 1; } else cout << "Memory allocation error!" << endl; if(tmpLAP.LapsDone > CurrentState.RaceLaps) { cout << "More laps than allocated! Ending tracking! Clearing buttons!" << endl; started = 0; IS_BFN newBFN = { 8, ISP_BFN, 0, BFN_CLEAR, 0, 0, 0, 0}; SDLNet_TCP_Send(tcpsock, (char*)&newBFN, sizeof(newBFN)); } // Pitboard-action! if ( (CurrentState.ViewPLID == tmpLAP.PLID) && ((unsigned short)(tmpLAP.LapsDone) > 1) ) { // Draw pitboard! newPitboard.PLID = CurrentState.ViewPLID; newPitboard.Lap = tmpLAP.LapsDone-1; newPitboard.TotalLaps = CurrentState.RaceLaps; switch(tmpLAP.Penalty) { case PENALTY_NONE: if ((tmpLAP.NumStops == 0) && (PitIsMandatory) && (tmpLAP.LapsDone+3 >= CurrentState.RaceLaps)) strcpy(&(newPitboard.Pit[0]), "PIT"); else strcpy(&(newPitboard.Pit[0]), " "); break; case PENALTY_DT_VALID: strcpy(&(newPitboard.Pit[0]), "D/T"); break; case PENALTY_SG_VALID: strcpy(&(newPitboard.Pit[0]), "S&G"); break; } newPitboard.Pit[3] = 0; // bool WillRunOOF = false; // Find this player's position in last lap's resultlist: cout << "Searching in " << (unsigned short)((pLaps+tmpLAP.LapsDone-2)->nextResult) << " results." << endl; for (unsigned short i = 0; i < (pLaps+tmpLAP.LapsDone-2)->nextResult; i++) {// Search in all results from the lap before if( (pLaps+tmpLAP.LapsDone-2)->Results[i].PLID == tmpLAP.PLID ) { // Driver's record from the lap before found // cout << "Calculating gaps: "; if (i > 0) { newPitboard.GapForward = abs((pLaps+tmpLAP.LapsDone-2)->Results[i].ETime - (pLaps+tmpLAP.LapsDone-2)->Results[i-1].ETime); // cout << "GapForward calculated! "; } else newPitboard.GapForward = 0; if (i != (pLaps+tmpLAP.LapsDone-2)->nextResult-1) // not the last result in the list { newPitboard.GapBackward = abs((pLaps+tmpLAP.LapsDone-2)->Results[i+1].ETime - (pLaps+tmpLAP.LapsDone-2)->Results[i].ETime); // cout << "GapBackward calculated! " << endl; } else newPitboard.GapBackward = 0; newPitboard.Position = i+1; newPitboard.Laptime = (pLaps+tmpLAP.LapsDone-2)->Results[i].LTime; if (tmpLAP.LapsDone > 2) { // Find the player's record in tmpLAP.LapsDone-3: for (unsigned short j = 0; j < (pLaps+tmpLAP.LapsDone-3)->nextResult; j++) { if ( (pLaps+tmpLAP.LapsDone-3)->Results[j].PLID == tmpLAP.PLID ) { // j is the index of this driver's record in tmpLAP.LapsDone-3 if (i != j) cout << "Index 2 laps ago = " << j << " but last lap = " << i << endl; float FuelPerLap = (pLaps+tmpLAP.LapsDone-3)->fuel[j] - (pLaps+tmpLAP.LapsDone-2)->fuel[i]; cout << "Fuel in " << tmpLAP.LapsDone-3 << ": " << (pLaps+tmpLAP.LapsDone-3)->fuel[j] << ". Fuel one lap later: " << (pLaps+tmpLAP.LapsDone-2)->fuel[i] << endl; float CurrentFuel = 2.0f * (pLaps+tmpLAP.LapsDone-2)->fuel[i] - (pLaps+tmpLAP.LapsDone-3)->fuel[j]; if (CurrentFuel < 0.0f) // Player will run OOF this lap CurrentFuel = 0.0f; cout << "Player has approx " << CurrentFuel*100.0f << "% fuel and consumes " << FuelPerLap*100.0f << "% per lap." << endl; unsigned short LapsRemaining = 0; if ((CurrentState.RaceLaps - tmpLAP.LapsDone) >= 0) LapsRemaining = CurrentState.RaceLaps - tmpLAP.LapsDone; else LapsRemaining = 0; // In case more laps have been done than necessary if ( (3.0f*FuelPerLap > CurrentFuel) // The player has less fuel than necessary for 2 full laps && (LapsRemaining > 2) // We need to do more than 2 laps && ((pLaps+tmpLAP.LapsDone-3)->fuel[j] != 0) && ((pLaps+tmpLAP.LapsDone-2)->fuel[i] != 0) ) { strcpy(&(newPitboard.Pit[0]),"PIT"); float NecessaryFuel = LapsRemaining*FuelPerLap; cout << "Player will run out of fuel! " << CurrentFuel*100.0f << "% < " << NecessaryFuel*100.0f << "%"<< endl; setFuel(NecessaryFuel, tcpsock); } } } } } /* else cout << (unsigned short)(tmpLAP.PLID) << " != " << "(pLaps+" << (unsigned short)(tmpLAP.LapsDone)-2 << ").Results[" << i << "].PLID = " << (unsigned short)((pLaps+tmpLAP.LapsDone-2)->Results[i].PLID) << endl; */ } if ((tmpLAP.LapsDone+1 == CurrentState.RaceLaps) && (newPitboard.Advice[0] == 0)) strcpy(&(newPitboard.Advice[0]), "LAST LAP"); if ((tmpLAP.LapsDone == CurrentState.RaceLaps) && (newPitboard.Advice[0] == 0)) strcpy(&(newPitboard.Advice[0]), "FINISHED"); cout << "Pitboard for " << (unsigned short)(newPitboard.PLID) << ": Lap " << (unsigned short)(newPitboard.Lap) << "/" << (unsigned short)(newPitboard.TotalLaps) << endl << "-" << (unsigned long int)(newPitboard.GapForward) << " | +" << (unsigned long int)(newPitboard.GapBackward) << endl << "P" << newPitboard.Position << " | " << newPitboard.Pit << endl; cout << newPitboard.Laptime/60000 << ":" << (newPitboard.Laptime/1000)%60 << "." << (newPitboard.Laptime%1000)/10 << endl; // Start sending buttons to LFS // Clear old pitboard? IS_BFN tmpBFN = { 8, ISP_BFN, 0, BFN_CLEAR, 0, 0, 0, 0 }; SDLNet_TCP_Send(tcpsock, (char*)&tmpBFN, sizeof(tmpBFN)); stringstream ss; ss << "L" << (unsigned short)(newPitboard.Lap) << "/" << (unsigned short)(newPitboard.TotalLaps) << " P"; if ((oldPitboard.PLID == newPitboard.PLID) && (oldPitboard.Position > newPitboard.Position)) ss << "^2"; if ((oldPitboard.PLID == newPitboard.PLID) && (oldPitboard.Position < newPitboard.Position)) ss << "^1"; ss << newPitboard.Position << " "; string tmpStr = ss.str(); const char* tmpcStr = tmpStr.c_str(); // cout << "Going to send label: " << tmpcStr << endl; IS_BTN BTN1 = { 12+16, ISP_BTN, 1, 0, 1, 0, ISB_DARK, 0, 10, 50, 20, 10}; SDLNet_TCP_Send(tcpsock, (char*)&BTN1, sizeof(BTN1)); SDLNet_TCP_Send(tcpsock, tmpcStr, 16); // Line 2 stringstream ss2; ss2 << setfill('0'); if ((oldPitboard.PLID == newPitboard.PLID) && (oldPitboard.GapForward < newPitboard.GapForward) && (oldPitboard.Position == newPitboard.Position)) ss2 << "^1"; // Color green if ((oldPitboard.PLID == newPitboard.PLID) && (oldPitboard.GapForward > newPitboard.GapForward) && (oldPitboard.Position == newPitboard.Position)) ss2 << "^2"; ss2 << "-"; if (newPitboard.GapForward < 10000) ss2 << (newPitboard.GapForward/1000)%60 << "." << setw(2) << (newPitboard.GapForward%1000)/10; else { if (newPitboard.GapForward < 100000) ss2 << (newPitboard.GapForward/1000)%60 << "." << (newPitboard.GapForward%1000)/100; else ss2 << (newPitboard.GapForward/1000)%60; } ss2 << "^9/"; if ((oldPitboard.PLID == newPitboard.PLID) && (oldPitboard.GapBackward > newPitboard.GapBackward) && (oldPitboard.Position == newPitboard.Position)) ss2 << "^1"; if ((oldPitboard.PLID == newPitboard.PLID) && (oldPitboard.GapBackward < newPitboard.GapBackward) && (oldPitboard.Position == newPitboard.Position)) ss2 << "^2"; ss2 << "+"; if (newPitboard.GapBackward < 10000) ss2 << (newPitboard.GapBackward/1000)%60 << "." << setw(2) << (newPitboard.GapBackward%1000)/10; else { if (newPitboard.GapBackward < 100000) ss2 << (newPitboard.GapBackward/1000)%60 << "." << (newPitboard.GapBackward%1000)/100; else ss2 << (newPitboard.GapBackward/1000)%60; } // ss2 << "+" << (newPitboard.GapForward/1000)%60 << "." << (newPitboard.GapForward%1000)/10 << "/-" << (newPitboard.GapBackward/1000)%60 << "." << (newPitboard.GapBackward%1000)/10 << " "; ss2 << " "; string Str2 = ss2.str(); const char* cStr2 = Str2.c_str(); // cout << "Going to send label: " << cStr2 << endl; IS_BTN BTN2 = { 12+20, ISP_BTN, 2, 0, 2, 0, ISB_DARK, 0, 10, 58, 20, 10}; SDLNet_TCP_Send(tcpsock, (char*)&BTN2, sizeof(BTN2)); SDLNet_TCP_Send(tcpsock, cStr2, 20); // Line 3 stringstream ss3; ss3 << setfill('0'); ss3 << newPitboard.Laptime/60000 << ":" << setw(2) << (newPitboard.Laptime/1000)%60 << "." << setw(2) << (newPitboard.Laptime%1000)/10 << " "; if ((oldPitboard.PLID == newPitboard.PLID) && (!strcmp((char*)&(oldPitboard.Pit[0]), (char*)&(newPitboard.Pit[0]))) ) ss3 << "^1"; ss3 << newPitboard.Pit << " "; string Str3 = ss3.str(); const char* cStr3 = Str3.c_str(); // cout << "Going to send label: " << cStr3 << endl; IS_BTN BTN3 = { 12+16, ISP_BTN, 3, 0, 3, 0, ISB_DARK, 0, 10, 66, 20, 10}; SDLNet_TCP_Send(tcpsock, (char*)&BTN3, sizeof(BTN3)); SDLNet_TCP_Send(tcpsock, cStr3, 16); if (newPitboard.Advice[0] != 0) { cout << "Sending advice:" << newPitboard.Advice << endl; IS_BTN BTN4 = { 12+12, ISP_BTN, 4, 0, 4, 0, ISB_DARK, 0, 10, 74, 20, 10}; SDLNet_TCP_Send(tcpsock, (char*)&BTN4, sizeof(BTN4)); SDLNet_TCP_Send(tcpsock, &(newPitboard.Advice[0]), 12); } memcpy(&oldPitboard, &newPitboard, sizeof(newPitboard)); // store the Pitboard before clearing the pit-field memset(&(newPitboard.Pit[0]), 0, 4); memset(&(newPitboard.Advice[0]), 0, 12); newPitboard.BirthTime = time; } } if ((started) && (CurrentState.RaceInProg == 2) && (tmpLAP.PLID == CurrentState.ViewPLID) && (oldPitboard.Laptime != 0)) // A quali is going on and we have to draw a pitboard { // Start sending the prepared pitboard stringstream qs1; qs1 << setfill('0'); qs1 << "P" << oldPitboard.Position; qs1 << " "; stringstream qs2; qs2 << setfill('0'); qs2 << "-"; if (oldPitboard.GapForward < 10000) qs2 << (oldPitboard.GapForward/1000)%60 << "." << setw(2) << (oldPitboard.GapForward%1000)/10; else { if (oldPitboard.GapForward < 100000) qs2 << (oldPitboard.GapForward/1000)%60 << "." << (oldPitboard.GapForward%1000)/100; else qs2 << (oldPitboard.GapForward/1000)%60; } qs2 << "/+"; if (oldPitboard.GapBackward < 10000) qs2 << (oldPitboard.GapBackward/1000)%60 << "." << setw(2) << (oldPitboard.GapBackward%1000)/10; else { if (oldPitboard.GapBackward < 100000) qs2 << (oldPitboard.GapBackward/1000)%60 << "." << (oldPitboard.GapBackward%1000)/100; else qs2 << (oldPitboard.GapBackward/1000)%60; } qs2 << " "; stringstream qs3; qs3 << setfill('0'); qs3 << oldPitboard.Laptime/60000 << ":" << setw(2) << (oldPitboard.Laptime/1000)%60 << "." << setw(2) << (oldPitboard.Laptime%1000)/10; qs3 << " "; string qStr1 = qs1.str(); const char* cqStr1 = qStr1.c_str(); IS_BTN BTNq1 = { 12+16, ISP_BTN, 1, 0, 1, 0, ISB_DARK, 0, 10, 50, 20, 10}; SDLNet_TCP_Send(tcpsock, (char*)&BTNq1, sizeof(IS_BTN)); SDLNet_TCP_Send(tcpsock, cqStr1, 16); string qStr2 = qs2.str(); const char* cqStr2 = qStr2.c_str(); IS_BTN BTNq2 = { 12+16, ISP_BTN, 2, 0, 2, 0, ISB_DARK, 0, 10, 58, 20, 10}; SDLNet_TCP_Send(tcpsock, (char*)&BTNq2, sizeof(IS_BTN)); SDLNet_TCP_Send(tcpsock, cqStr2, 16); string qStr3 = qs3.str(); const char* cqStr3 = qStr3.c_str(); IS_BTN BTNq3 = { 12+16, ISP_BTN, 3, 0, 3, 0, ISB_DARK, 0, 10, 66, 20, 10}; SDLNet_TCP_Send(tcpsock, (char*)&BTNq3, sizeof(IS_BTN)); SDLNet_TCP_Send(tcpsock, cqStr3, 16); cout << "Sent pitboard: " << cqStr1 << endl << cqStr2 << endl << cqStr3 << endl; oldPitboard.BirthTime = time; } break; cout << "Between LAP and RES!" << endl; case ISP_RES: IS_RES tmpRES; SDLNet_TCP_Recv(tcpsock,((char*)&tmpRES)+4,80); tmpRES.PLID = header.SubT; if ((started) && (CurrentState.RaceInProg == 2) && pQuali) // A quali is going on { cout << "New registered laptime by PLID " << (unsigned short)(tmpRES.PLID) << ": " << tmpRES.BTime/60000 << ":" << (tmpRES.BTime/1000)%60 << "." << (tmpRES.BTime%1000)/10 << endl; if (tmpRES.ResultNum != 255) // A driver has improved { cout << "ResultNum: " << tmpRES.ResultNum+1 << " recieved." << endl; // Try to find this result in the list: for (int v = 0; v < 256; v++) { if ( (pQuali->Results[v].PLID == tmpRES.PLID) && (pQuali->Results[v].BTime >= tmpRES.BTime) ) { // The driver already has a result and the new one is better cout << "Updating PLID " << (unsigned short)(tmpRES.PLID) << " time: " << tmpRES.BTime << endl; memcpy(&(pQuali->Results[v]), &tmpRES, sizeof(IS_RES)); v = 512; // exit } if ( v == 255 ) // LFS stores 255 results, this is the 256th -> our result wasn't found { cout << "New result for " << (unsigned short)(tmpRES.PLID) << ": " << tmpRES.BTime << endl; // Search for the first empty result and store tmpRES: for (int f = 0; f < 255; f++) { if ( pQuali->Results[f].BTime == 0 ) // empty { memcpy(&(pQuali->Results[f]), &tmpRES, sizeof(IS_RES)); f = 512; // exit } } } } } else cout << (unsigned short)(tmpRES.PLID) << " failed to improve." << endl; if (tmpRES.PLID == CurrentState.ViewPLID) // The viewed driver crossed the finish line { // Fill the pitboard memcpy(&oldPitboard, &newPitboard, sizeof(newPitboard)); newPitboard.PLID = tmpRES.PLID; // Find this driver's best laptime yet: unsigned long int BestLapYet = 0; for (int n = 0; n < 255; n++) { if ((pQuali->Results[n].PLID == tmpRES.PLID) && (pQuali->Results[n].BTime != 0) && (pQuali->Results[n].BTime <= tmpRES.BTime)) BestLapYet = pQuali->Results[n].BTime; } if (BestLapYet == 0) // Since tmpRES was stored above this should never be true { BestLapYet = tmpRES.BTime; cout << "Caution: Best Lap Yet == 0!" << endl; } newPitboard.Lap = tmpRES.LapsDone; newPitboard.Laptime = tmpRES.BTime; // Find how many quicker results there have been: unsigned short CurPosition = 1; // positions count from one for (int c = 0; c < 255; c++) { if ( (pQuali->Results[c].BTime) && (pQuali->Results[c].BTime < BestLapYet)) CurPosition++; } newPitboard.Position = CurPosition; // Find the result with the next best laptime than the viewed driver: unsigned long int tmpTime = 0; for (int m = 0; m < 256; m++) { if ((pQuali->Results[m].BTime > tmpTime) && (pQuali->Results[m].BTime < BestLapYet)) { tmpTime = pQuali->Results[m].BTime; m = 512; } if (m == 255) // End of list and still not found tmpTime = 0; } if (tmpTime > 0) { // There *is* a quicker driver, calculate GapForward newPitboard.GapForward = BestLapYet - tmpTime; } else newPitboard.GapForward = 0; // Find the result with the next worse laptime than the viewed driver: tmpTime = 0; tmpTime--; // Underflow -> biggest possible number reached for (int i = 0; i < 256; i++) { if ((pQuali->Results[i].BTime < tmpTime) && (pQuali->Results[i].BTime > BestLapYet)) { tmpTime = pQuali->Results[i].BTime; i = 512; // exit } if (i == 255) // End of list and still not found tmpTime = 0; } if (tmpTime > 0) { // There *is* a slower driver, calculate GapForward newPitboard.GapBackward = abs(tmpTime) - BestLapYet; } else newPitboard.GapBackward = 0; } } break; cout << "Between RES and RST!" << endl; case ISP_RST: IS_RST tmpRST; SDLNet_TCP_Recv(tcpsock,((char*)&tmpRST)+4,24); cout << "Restart detected: "; if (pLaps) delete pLaps; pLaps = 0; if ((tmpRST.RaceLaps > 0) && (tmpRST.RaceLaps < 100)) // A race has started { pLaps = new Lap[tmpRST.RaceLaps]; memset((char*)pLaps, 0, (unsigned short)(tmpRST.RaceLaps)*sizeof(Lap) ); cout << "Race! Number of laps: " << (unsigned short)(tmpRST.RaceLaps) << ". Allocated " << (unsigned short)(tmpRST.RaceLaps)*sizeof(Lap) << "bytes." << endl; started = 1; } else if ((tmpRST.RaceLaps == 0) && (tmpRST.QualMins != 0)) { cout << "Qualification! Duration: " << (unsigned short)(tmpRST.QualMins) << " Minutes." << endl; pQuali = new Quali; memset((char*)pQuali, 0, sizeof(Quali)); started = 1; } else { cout << "Strange restart! Number of laps: " << (unsigned short)(tmpRST.RaceLaps) << endl; } cout << "Cleared memory! "; { IS_BFN newBFN = { 8, ISP_BFN, 0, BFN_CLEAR, 0, 0, 0, 0}; SDLNet_TCP_Send(tcpsock, (char*)&newBFN, sizeof(newBFN)); cout << "Cleared pitboard!" << endl; } if ((tmpRST.Flags & 64) == 64) { cout << "Pitting is mandatory!" << endl; PitIsMandatory = 1; } else { cout << "Pitting isn't mandatory." << endl; PitIsMandatory = 0; } break; cout << "Between RST and SPX!" << endl; case ISP_SPX: // Clear old pitboard { IS_SPX tmpSPX; SDLNet_TCP_Recv(tcpsock,((char*)&tmpSPX)+4,16); tmpSPX.PLID = header.SubT; // Complete the tmpSPX packet if (CurrentState.ViewPLID == tmpSPX.PLID) // If the viewed vehicle passes a sectorline { // Clear the board IS_BFN newBFN = { 8, ISP_BFN, 0, BFN_CLEAR, 0, 0, 0, 0}; SDLNet_TCP_Send(tcpsock, (char*)&newBFN, sizeof(newBFN)); } } break; cout << "After SPX!" << endl; default: char* pBuffer = new char[header.Size]; SDLNet_TCP_Recv(tcpsock,pBuffer,header.Size-4); delete pBuffer; break; } // switch } // if (Could not receive) { } else { } } // CheckSockets // UDP - OutGauge UDPpacket* pPacket; pPacket = SDLNet_AllocPacket(512); if (!pPacket) { cout << "Failed to allocate new UDP packet! Exiting!" << endl; exit(2); } OutGaugePack* pOGP = (OutGaugePack*)pPacket->data; if (SDLNet_UDP_Recv(udpsock, pPacket)) { switch(pPacket->len) { case 92: // Recieved a OutGaugePacket // Timecode time = pOGP->Time; if ( (newPitboard.BirthTime != 0) && (pOGP->Time > (newPitboard.BirthTime + BoardTimeOut)) ) { cout << "Pitboard is too old!" << endl; IS_BFN tmpBFN = { 8, ISP_BFN, 0, BFN_CLEAR, 0, 0, 0, 0 }; SDLNet_TCP_Send(tcpsock, (char*)&tmpBFN, sizeof(tmpBFN)); newPitboard.BirthTime = 0; } if ( (oldPitboard.BirthTime != 0) && (pOGP->Time > (oldPitboard.BirthTime + BoardTimeOut)) ) { cout << "Pitboard is too old!" << endl; IS_BFN tmpBFN = { 8, ISP_BFN, 0, BFN_CLEAR, 0, 0, 0, 0 }; SDLNet_TCP_Send(tcpsock, (char*)&tmpBFN, sizeof(tmpBFN)); oldPitboard.BirthTime = 0; } // Extract fuel if((pLaps) && (CurrentState.RaceInProg == 1)) // Is memory allocated? { // Find the driver's current lap and position in the result-array for (int l = 0; l < CurrentState.RaceLaps; l++) // Search as long as we're not out of laps { for (int p = 0; (p < 64) && ((pLaps+l)->Results[p].PLID != 0); p++) // PLID=0 means we're out of results on this lap { if ( ((pLaps+l)->Results[p].PLID == CurrentState.ViewPLID) // Our driver && ((pLaps+l)->Results[p].LapsDone-1 == l) /*This is the lap the car is currently on*/ && ((pLaps+l)->fuel[p] == 0) // We only want the first fuel reading from this lap, which is approximately the last from the lap before ) { (pLaps+l)->fuel[p] = pOGP->Fuel; cout << "Fuel for " << (unsigned short)(CurrentState.ViewPLID) << ": (pLaps+" << l << "->fuel[" << p << "] = " << pOGP->Fuel << endl; } } } } break; default: cout << "Unknown packet size!" << endl; break; } } SDLNet_FreePacket(pPacket); pPacket = 0; } cout << "Exiting... "; SDLNet_FreeSocketSet(mySSet); SDLNet_TCP_Close(tcpsock); SDLNet_Quit(); SDL_Quit(); return 0; }