speed-dreams/src/modules/racing/standardgame/raceinit.cpp

1024 lines
34 KiB
C++

/***************************************************************************
file : raceinit.cpp
created : Sat Nov 16 10:34:35 CET 2002
copyright : (C) 2002 by Eric Espie
email : eric.espie@torcs.org
version : $Id$
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
/** @file
Race initialization routines
@author <a href=mailto:eric.espie@torcs.org>Eric Espie</a>
@version $Id$
*/
#include <cstdlib>
#include <cstdio>
#include <string>
#include <sstream>
#include <map>
#ifdef THIRD_PARTY_SQLITE3
#include <sqlite3.h>
#endif
#include <raceman.h>
#include <robot.h>
#include <teammanager.h>
#include <robottools.h>
#include <replay.h>
#include <portability.h>
#include <tgf.hpp>
#include <racemanagers.h>
#include <race.h>
#include "standardgame.h"
#include "racesituation.h"
#include "racemain.h"
#include "raceupdate.h"
#include "racestate.h"
#include "raceresults.h"
#include "racecareer.h"
#include "raceinit.h"
static const char *aPszSkillLevelNames[] =
{ ROB_VAL_ROOKIE, ROB_VAL_AMATEUR, ROB_VAL_SEMI_PRO, ROB_VAL_PRO };
static const int NSkillLevels = (int)(sizeof(aPszSkillLevelNames)/sizeof(char*));
// The list of robot modules loaded for the race.
tModList *ReRacingRobotsModList = 0;
// The race situation
tRmInfo *ReInfo = 0;
int replayRecord;
double replayTimestamp;
#ifdef THIRD_PARTY_SQLITE3
sqlite3 *replayDB;
sqlite3_stmt *replayBlobs[50];
#endif
// Race Engine reset
void
ReReset(void)
{
// Allocate race engine info structures if not already done.
ReInfo = ReSituation::self().data();
ReInfo->robModList = &ReRacingRobotsModList;
// Load Race engine params.
char buf[256];
snprintf(buf, sizeof(buf), "%s%s", GfLocalDir(), RACE_ENG_CFG);
ReInfo->_reParam = GfParmReadFile(buf, GFPARM_RMODE_REREAD | GFPARM_RMODE_CREAT);
}
// Race Engine cleanup
void ReCleanup(void)
{
if (!ReInfo)
return;
// Free ReInfo memory.
ReSituation::terminate();
ReInfo = 0;
}
// Race Engine Exit
int
ReExit(void)
{
// Stop and cleanup the race engine.
ReStop();
StandardGame::self().cleanup();
// Notify the user interface.
ReUI().quit();
return RM_QUIT;
}
// Select the given manager for the race.
void
ReRaceSelectRaceman(GfRaceManager* pRaceMan, bool bKeepHumans)
{
// Trace the chosen raceman full type.
std::string strFullType(pRaceMan->getType());
if (!pRaceMan->getSubType().empty())
{
strFullType += " / ";
strFullType += pRaceMan->getSubType();
}
GfLogTrace("'%s' race mode selected\n", strFullType.c_str());
// Re-init. race engine info about the race manager (= the race mode / type / class).
ReInfo->_reName = pRaceMan->getName().c_str();
ReInfo->_reFilename = pRaceMan->getId().c_str();
// (Re-)initialize the currrent race configuration from the selected race manager.
StandardGame::self().race()->load(pRaceMan, bKeepHumans);
}
// Start configuring the race
void
ReRaceConfigure(bool bInteractive)
{
// Update race engine info.
ReInfo->mainParams = ReInfo->params =
StandardGame::self().race()->getManager()->getDescriptorHandle();
GfParmRemoveVariable(ReInfo->params, "/", "humanInGroup");
GfParmSetVariable(ReInfo->params, "/", "humanInGroup", ReHumanInGroup() ? 1.0f : 0.0f);
// Enter CONFIG state and return to the race engine automaton if interactive mode.
if (bInteractive)
ReStateApply((void*)RE_STATE_CONFIG);
}
// Restore the race from the given results file
void
ReRaceRestore(void* hparmResults)
{
// Update race engine info in order to set it in the exact state
// it was in when the race mode was saved.
GfRace* pRace = StandardGame::self().race();
ReInfo->mainParams = pRace->getManager()->getDescriptorHandle();
ReInfo->mainResults = pRace->getResultsDescriptorHandle();
if (!pRace->getManager()->hasSubFiles())
{
// Non-Career mode.
ReInfo->params = ReInfo->mainParams;
ReInfo->results = ReInfo->mainResults;
ReInfo->_reRaceName = pRace->getSessionName().c_str(); //ReInfo->_reName;
}
else
{
// Career mode : More complicated, as everything is not in one params/results file
// (the target state is right after the end of the previous event,
// which was from the previous group).
const char* pszPrevParamsFile =
GfParmGetStr(ReInfo->mainResults, RE_SECT_CURRENT, RE_ATTR_PREV_FILE, 0);
if (!pszPrevParamsFile)
GfLogWarning("Career : No previous file in MainResults\n");
ReInfo->params =
pszPrevParamsFile ? GfParmReadFile(pszPrevParamsFile, GFPARM_RMODE_STD) : ReInfo->mainParams;
const char* pszPrevResultsFile =
GfParmGetStr(ReInfo->params, RM_SECT_SUBFILES, RM_ATTR_RESULTSUBFILE, 0);
if (!pszPrevResultsFile)
GfLogWarning("Career : Failed to load previous results from previous params\n");
ReInfo->results =
pszPrevResultsFile ? GfParmReadFile(pszPrevResultsFile, GFPARM_RMODE_STD) : ReInfo->mainResults;
ReInfo->_reRaceName = ReGetPrevRaceName(/* bLoop = */true);
}
GfParmRemoveVariable(ReInfo->params, "/", "humanInGroup");
GfParmSetVariable(ReInfo->params, "/", "humanInGroup", ReHumanInGroup() ? 1.0f : 0.0f);
}
// Start a new race for the previously configured race manager
void
ReStartNewRace()
{
// Save the race settings to the race manager file is anything changed.
GfRace* pRace = StandardGame::self().race();
if (pRace->isDirty())
{
pRace->store(); // Save data to params.
GfParmWriteFile(NULL, ReInfo->params, ReInfo->_reName); // Save params to disk.
}
// Initialize the result system (different way for the Career mode).
if (pRace->getManager()->hasSubFiles())
ReCareerNew();
else
ReInitResults();
// Enter EVENT_INIT state and return to the race engine automaton.
ReStateApply((void*)RE_STATE_EVENT_INIT);
}
// Resume the previously restored race from a results file
void
ReResumeRace()
{
ReUI().onRaceResuming();
}
/*
* Function
* initStartingGrid
*
* Description
* Place the cars on the starting grid
*
* Parameters
* Race Information structure initialized
*
* Return
* none
*/
static void
initStartingGrid(void)
{
char path[64];
int i;
tTrackSeg *curseg;
int rows;
tdble a, b;
//tdble wi2; // Never used.
tdble d1, d2,d3;
tdble startpos, tr, ts;
tdble speedInit;
tdble heightInit;
tCarElt *car;
const char *pole;
void *trHdle = ReInfo->track->params;
void *params = ReInfo->params;
snprintf(path, sizeof(path), "%s/%s", ReInfo->_reRaceName, RM_SECT_STARTINGGRID);
/* Search for the first turn for find the pole side */
curseg = ReInfo->track->seg->next;
while (curseg->type == TR_STR) {
/* skip the straight segments */
curseg = curseg->next;
}
/* Set the pole for the inside of the first turn */
if (curseg->type == TR_LFT) {
pole = GfParmGetStr(params, path, RM_ATTR_POLE, "left");
} else {
pole = GfParmGetStr(params, path, RM_ATTR_POLE, "right");
}
/* Tracks definitions can force the pole side */
pole = GfParmGetStr(trHdle, RM_SECT_STARTINGGRID, RM_ATTR_POLE, pole);
if (strcmp(pole, "left") == 0) {
a = ReInfo->track->width;
b = -a;
} else {
a = 0;
b = ReInfo->track->width;
}
//wi2 = ReInfo->track->width * 0.5f; // Never used.
rows = (int)GfParmGetNum(params, path, RM_ATTR_ROWS, (char*)NULL, 2);
rows = (int)GfParmGetNum(trHdle, RM_SECT_STARTINGGRID, RM_ATTR_ROWS, (char*)NULL, (tdble)rows);
d1 = GfParmGetNum(params, path, RM_ATTR_TOSTART, (char*)NULL, 10);
d1 = GfParmGetNum(trHdle, RM_SECT_STARTINGGRID, RM_ATTR_TOSTART, (char*)NULL, d1);
d2 = GfParmGetNum(params, path, RM_ATTR_COLDIST, (char*)NULL, 10);
d2 = GfParmGetNum(trHdle, RM_SECT_STARTINGGRID, RM_ATTR_COLDIST, (char*)NULL, d2);
d3 = GfParmGetNum(params, path, RM_ATTR_COLOFFSET, (char*)NULL, 5);
d3 = GfParmGetNum(trHdle, RM_SECT_STARTINGGRID, RM_ATTR_COLOFFSET, (char*)NULL, d3);
speedInit = GfParmGetNum(params, path, RM_ATTR_INITSPEED, (char*)NULL, 0.0);
heightInit = GfParmGetNum(params, path, RM_ATTR_INITHEIGHT, (char*)NULL, 0.3f);
heightInit = GfParmGetNum(trHdle, RM_SECT_STARTINGGRID, RM_ATTR_INITHEIGHT, (char*)NULL, heightInit);
if (rows < 1) {
rows = 1;
}
for (i = 0; i < ReInfo->s->_ncars; i++) {
car = &(ReInfo->carList[i]);
car->_speed_x = speedInit;
startpos = ReInfo->track->length - (d1 + (i / rows) * d2 + (i % rows) * d3);
tr = a + b * ((i % rows) + 1) / (rows + 1);
curseg = ReInfo->track->seg; /* last segment */
while (startpos < curseg->lgfromstart) {
curseg = curseg->prev;
}
ts = startpos - curseg->lgfromstart;
car->_trkPos.seg = curseg;
car->_trkPos.toRight = tr;
switch (curseg->type) {
case TR_STR:
car->_trkPos.toStart = ts;
RtTrackLocal2Global(&(car->_trkPos), &(car->_pos_X), &(car->_pos_Y), TR_TORIGHT);
car->_yaw = curseg->angle[TR_ZS];
break;
case TR_RGT:
car->_trkPos.toStart = ts / curseg->radius;
RtTrackLocal2Global(&(car->_trkPos), &(car->_pos_X), &(car->_pos_Y), TR_TORIGHT);
car->_yaw = curseg->angle[TR_ZS] - car->_trkPos.toStart;
break;
case TR_LFT:
car->_trkPos.toStart = ts / curseg->radius;
RtTrackLocal2Global(&(car->_trkPos), &(car->_pos_X), &(car->_pos_Y), TR_TORIGHT);
car->_yaw = curseg->angle[TR_ZS] + car->_trkPos.toStart;
break;
}
car->_pos_Z = RtTrackHeightL(&(car->_trkPos)) + heightInit;
FLOAT_NORM0_2PI(car->_yaw);
RePhysicsEngine().configureCar(car);
}
}
static void
initPits(void)
{
tTrackPitInfo *pits;
int i, j;
/*
typedef std::map<std::string, int> tTeamsMap;
typedef tTeamsMap::const_iterator tTeamsMapIterator;
tTeamsMap teams;
tTeamsMapIterator teamsIterator;
// create a list with the teams, a pit can just be used by one team.
for (i = 0; i < ReInfo->s->_ncars; i++) {
tCarElt *car = &(ReInfo->carList[i]);
teams[car->_teamname] = teams[car->_teamname] + 1;
}
for (teamsIterator = teams.begin(); teamsIterator != teams.end(); ++teamsIterator) {
GfLogDebug("----------------- %s\t%d\n", (teamsIterator->first).c_str(), teamsIterator->second);
}
*/
// How many cars are sharing a pit?
int carsPerPit = (int) GfParmGetNum(ReInfo->params, ReInfo->_reRaceName, RM_ATTR_CARSPERPIT, NULL, 1.0f);
if (carsPerPit < 1) {
carsPerPit = 1;
} else if (carsPerPit > TR_PIT_MAXCARPERPIT) {
carsPerPit = TR_PIT_MAXCARPERPIT;
}
GfLogInfo("Cars per pit: %d\n", carsPerPit);
switch (ReInfo->track->pits.type) {
case TR_PIT_ON_TRACK_SIDE:
case TR_PIT_NO_BUILDING:
pits = &(ReInfo->track->pits);
pits->driversPitsNb = ReInfo->s->_ncars;
pits->carsPerPit = carsPerPit;
// Initialize pit data for every pit, necessary because of restarts
// (track gets not reloaded, no calloc).
for (i = 0; i < pits->nMaxPits; i++) {
tTrackOwnPit *pit = &(pits->driversPits[i]);
pit->freeCarIndex = 0;
pit->pitCarIndex = TR_PIT_STATE_FREE;
for (j = 0; j < TR_PIT_MAXCARPERPIT; j++) {
pit->car[j] = NULL;
}
}
// Assign cars to pits. Inefficient (O(n*n)), but just at initialization, so do not care.
// One pit can just host cars of one team (this matches with the reality)
for (i = 0; i < ReInfo->s->_ncars; i++) {
// Find pit for the cars team.
tCarElt *car = &(ReInfo->carList[i]);
for (j = 0; j < pits->nMaxPits; j++) {
tTrackOwnPit *pit = &(pits->driversPits[j]);
// Put car in this pit if the pit is unused or used by a teammate and there is
// space left.
if (pit->freeCarIndex <= 0 ||
(strcmp(pit->car[0]->_teamname, car->_teamname) == 0 && pit->freeCarIndex < carsPerPit))
{
// Assign car to pit.
pit->car[pit->freeCarIndex] = car;
// If this is the first car, set up more pit values ; assumption: the whole team
// uses the same car. If not met, it does not matter much, but the car might be
// captured a bit too easy or too hard.
if (pit->freeCarIndex == 0) {
pit->pitCarIndex = TR_PIT_STATE_FREE;
pit->lmin = pit->pos.seg->lgfromstart + pit->pos.toStart - pits->len / 2.0f + car->_dimension_x / 2.0f;
if (pit->lmin > ReInfo->track->length) {
pit->lmin -= ReInfo->track->length;
}
pit->lmax = pit->pos.seg->lgfromstart + pit->pos.toStart + pits->len / 2.0f - car->_dimension_x / 2.0f;
if (pit->lmax > ReInfo->track->length) {
pit->lmax -= ReInfo->track->length;
}
}
(pit->freeCarIndex)++;
ReInfo->carList[i]._pit = pit;
// Assigned, continue with next car.
break;
}
}
}
// Print out assignments.
for (i = 0; i < pits->nMaxPits; i++) {
tTrackOwnPit *pit = &(pits->driversPits[i]);
for (j = 0; j < pit->freeCarIndex; j++) {
if (j == 0) {
GfLogTrace("Pit %d, Team: %s, ", i, pit->car[j]->_teamname);
}
GfLogTrace("%d: %s ", j, pit->car[j]->_name);
}
if (j > 0) {
GfOut("\n");
}
}
break;
case TR_PIT_ON_SEPARATE_PATH:
break;
case TR_PIT_NONE:
break;
}
}
/**
* Function to load a car.
*
* @param carindex The index whichs will be used as car->index for the car.
* @param listindex The listindex in RM_SECT_DRIVERS_RACING
* @param modindex The index of the mod; must be MAX_MOD_ITF if normal_carname is FALSE.
* @param robotIdx The index of the robot.
* @param normal_carname If this member is TRUE, the car is treated as an ordinary car;
* if this member is FALSE, then the car used is the one given
* in the xml-file, and there is no restriction on the number of instances.
* @param cardllname The dllname of the driver
* @return A pointer to the newly created car if successfull; NULL otherwise
*/
static tCarElt* reLoadSingleCar( int carindex, int listindex, int modindex, int relativeRobotIdx, char normal_carname, char const *cardllname )
{
tCarElt *elt;
tMemoryPool oldPool;
char path[256];
char path2[256];
char buf[256];
char buf2[256];
char const *str;
char const *category;
char const *teamname;
tModInfoNC *curModInfo;
tRobotItf *curRobot;
void *robhdle;
void *cathdle;
void *carhdle;
void *handle;
int k;
int xx;
char isHuman;
int robotIdx = relativeRobotIdx;
/* good robot found */
curModInfo = &((*(ReInfo->robModList))->modInfo[modindex]);
#if 0 //SDW
if (replayReplay)
GfLogInfo("Driver in car %d being driven by replay\n", carindex);
else
#endif
GfLogInfo("Driver's name: %s\n", curModInfo->name);
isHuman = strcmp( cardllname, "human" ) == 0 || strcmp( cardllname, "networkhuman" ) == 0;
/* Extended is forced for humans, so no need to increase robotIdx */
if (!normal_carname && !isHuman)
robotIdx += curModInfo->index;
/* Retrieve the driver interface (function pointers) */
curRobot = (tRobotItf*)calloc(1, sizeof(tRobotItf));
/* ... and initialize the driver */
#if 0 // SDW
if (replayReplay) {
// Register against the Replay driver (which does nothing)
curModInfo->fctInit(carindex, (void*)(curRobot));
} else if (!(ReInfo->_displayMode & RM_DISP_MODE_SIMU_SIMU)) {
#else
if (!(ReInfo->_displayMode & RM_DISP_MODE_SIMU_SIMU)) {
#endif
curModInfo->fctInit(robotIdx, (void*)(curRobot));
} else {
curRobot->rbNewTrack = NULL;
curRobot->rbNewRace = NULL;
curRobot->rbResumeRace = NULL;
curRobot->rbDrive = NULL;
curRobot->rbPitCmd = NULL;
curRobot->rbEndRace = NULL;
curRobot->rbShutdown = NULL;
curRobot->index = 0;
}
/* Retrieve and load the robotXML file :
1) from user settings dir (local dir)
2) from installed data dir */
snprintf(buf, sizeof(buf), "%sdrivers/%s/%s.xml", GfLocalDir(), cardllname, cardllname);
robhdle = GfParmReadFile(buf, GFPARM_RMODE_STD);
if (!robhdle) {
snprintf(buf, sizeof(buf), "drivers/%s/%s.xml", cardllname, cardllname);
robhdle = GfParmReadFile(buf, GFPARM_RMODE_STD);
}
if (normal_carname || isHuman)
snprintf(path, sizeof(path), "%s/%s/%d", ROB_SECT_ROBOTS, ROB_LIST_INDEX, robotIdx);
else
snprintf(path, sizeof(path), "%s", ROB_SECT_ARBITRARY);
/* Load car/driver info (in race engine data structure) */
if (robhdle)
{
elt = &(ReInfo->carList[carindex]);
GF_TAILQ_INIT(&(elt->_penaltyList));
const std::string strDType = GfParmGetStr(robhdle, path, ROB_ATTR_TYPE, ROB_VAL_ROBOT);
if (strDType == ROB_VAL_ROBOT){
elt->_driverType = RM_DRV_ROBOT;
elt->_networkPlayer = 0;
}
else if (strDType == ROB_VAL_HUMAN)
{
elt->_driverType = RM_DRV_HUMAN;
std::string strNetPlayer = GfParmGetStr(robhdle, path, "networkrace", "no");
elt->_networkPlayer = (strNetPlayer == "yes") ? 1 : 0;
}
elt->index = carindex;
elt->robot = curRobot;
elt->_paramsHandle = robhdle;
elt->_driverIndex = robotIdx;
elt->_moduleIndex = relativeRobotIdx;
strncpy(elt->_modName, cardllname, MAX_NAME_LEN - 1);
elt->_modName[MAX_NAME_LEN - 1] = 0;
//snprintf(path, sizeof(path), "%s/%s/%d", ROB_SECT_ROBOTS, ROB_LIST_INDEX, robotIdx);
snprintf( path2, sizeof(path2), "%s/%s/%d/%d", RM_SECT_DRIVERINFO, elt->_modName, normal_carname ? 0 : 1, elt->_moduleIndex );
if (normal_carname || elt->_driverType == RM_DRV_HUMAN) {
strncpy(elt->_name, GfParmGetStr(robhdle, path, ROB_ATTR_NAME, "none"), MAX_NAME_LEN - 1);
strncpy(elt->_sname, GfParmGetStr(robhdle, path, ROB_ATTR_SNAME, "none"), MAX_NAME_LEN - 1);
strncpy(elt->_cname, GfParmGetStr(robhdle, path, ROB_ATTR_CODE, "---"), 3);
} else {
strncpy(elt->_name, GfParmGetStr(ReInfo->params, path2, ROB_ATTR_NAME, "none"), MAX_NAME_LEN - 1);
strncpy(elt->_sname, GfParmGetStr(ReInfo->params, path2, ROB_ATTR_SNAME, "none"), MAX_NAME_LEN - 1);
strncpy(elt->_cname, GfParmGetStr(ReInfo->params, path2, ROB_ATTR_CODE, "---"), 3);
}
elt->_name[MAX_NAME_LEN - 1] = 0;
elt->_sname[MAX_NAME_LEN - 1] = 0;
elt->_cname[3] = 0;
teamname = GfParmGetStr(robhdle, path, ROB_ATTR_TEAM, "none");
teamname = GfParmGetStr(ReInfo->params, path2, ROB_ATTR_TEAM, teamname ); //Use the name in params if it has a team name
strncpy(elt->_teamname, teamname, MAX_NAME_LEN - 1);
elt->_teamname[MAX_NAME_LEN - 1] = 0;
elt->_driveSkill = GfParmGetNum(ReInfo->params, path2, RM_ATTR_SKILLLEVEL, NULL, -1.0f);
if (normal_carname) /* Even if we get a normal_carname for humans we use it despite of forced extended mode*/
strncpy(elt->_carName, GfParmGetStr(robhdle, path, ROB_ATTR_CAR, ""), MAX_NAME_LEN - 1);
else
strncpy(elt->_carName, GfParmGetStr(ReInfo->params, path2, RM_ATTR_CARNAME, ""), MAX_NAME_LEN - 1);
elt->_carName[MAX_NAME_LEN - 1] = 0; /* XML file name */
// Load custom skin name and targets from race info (if specified).
snprintf(path2, sizeof(path2), "%s/%d", RM_SECT_DRIVERS_RACING, listindex);
if (GfParmGetStr(ReInfo->params, path2, RM_ATTR_SKINNAME, 0))
{
strncpy(elt->_skinName, GfParmGetStr(ReInfo->params, path2, RM_ATTR_SKINNAME, ""), MAX_NAME_LEN - 1);
elt->_skinName[MAX_NAME_LEN - 1] = 0; // Texture name
}
elt->_skinTargets = (int)GfParmGetNum(ReInfo->params, path2, RM_ATTR_SKINTARGETS, (char*)NULL, 0);
// Load other data from robot descriptor.
elt->_raceNumber = (int)GfParmGetNum(robhdle, path, ROB_ATTR_RACENUM, (char*)NULL, 0);
if (!normal_carname && elt->_driverType != RM_DRV_HUMAN) // Increase racenumber if needed
elt->_raceNumber += elt->_moduleIndex;
elt->_skillLevel = 0;
str = GfParmGetStr(robhdle, path, ROB_ATTR_LEVEL, ROB_VAL_SEMI_PRO);
for(k = 0; k < NSkillLevels; k++) {
if (strcmp(aPszSkillLevelNames[k], str) == 0) {
elt->_skillLevel = k;
break;
}
}
elt->_startRank = carindex;
elt->_pos = carindex+1;
elt->_remainingLaps = ReInfo->s->_totLaps;
elt->_newTrackMemPool = NULL;
elt->_newRaceMemPool = NULL;
elt->_endRaceMemPool = NULL;
elt->_shutdownMemPool = NULL;
GfLogTrace("Driver #%d(%d) : module='%s', name='%s', car='%s', cat='%s', skin='%s' on %x\n",
carindex, listindex, elt->_modName, elt->_name, elt->_carName,
elt->_category, elt->_skinName, elt->_skinTargets);
/* Retrieve and load car specs : merge car default specs,
category specs and driver modifications (=> handle) */
/* Read Car model specifications */
snprintf(buf, sizeof(buf), "cars/models/%s/%s.xml", elt->_carName, elt->_carName);
carhdle = GfParmReadFile(buf, GFPARM_RMODE_STD | GFPARM_RMODE_CREAT);
category = GfParmGetStr(carhdle, SECT_CAR, PRM_CATEGORY, NULL);
if (category) {
GfLogTrace("Checking/Merging %s specs into %s base setup for %s ...\n",
category, elt->_carName, curModInfo->name);
strncpy(elt->_category, category, MAX_NAME_LEN - 1);
elt->_category[MAX_NAME_LEN - 1] = 0;
/* Read Car Category specifications */
snprintf(buf2, sizeof(buf2), "cars/categories/%s.xml", elt->_category);
cathdle = GfParmReadFile(buf2, GFPARM_RMODE_STD | GFPARM_RMODE_CREAT);
int errorcode = 0;
if ((errorcode = GfParmCheckHandle(cathdle, carhdle)))
{
switch (errorcode)
{
case -1:
GfLogError("Car %s NOT in category %s (driver %s) !!!\n", elt->_carName, category, elt->_name);
break;
case -2:
GfLogError("Parameters out of bound for car %s (driver %s)!!!\n",elt->_carName, elt->_name);
break;
case -3:
GfLogError("Parameter not allowed for car %s (driver %s)!!!\n",elt->_carName, elt->_name);
break;
default:
GfLogError("Unknown error for %s (driver %s)!!!\n",elt->_carName, elt->_name);
break;
}
return NULL;
}
carhdle = GfParmMergeHandles(cathdle, carhdle,
GFPARM_MMODE_SRC | GFPARM_MMODE_DST | GFPARM_MMODE_RELSRC | GFPARM_MMODE_RELDST);
/* The code below stores the carnames to a separate xml-file
such that at newTrack it is known which car is used.
TODO: find a better method for this */
snprintf (buf, sizeof(buf), "%sdrivers/curcarnames.xml", GfLocalDir());
handle = GfParmReadFile(buf, GFPARM_RMODE_CREAT);
if (handle) {
snprintf(path, sizeof(path), "drivers/%s/%d", cardllname, elt->_driverIndex);
GfParmSetStr (handle, path, RM_ATTR_CARNAME, elt->_carName);
GfParmWriteFile (0, handle, "Car names");
GfParmReleaseHandle (handle);
}
if (!(ReInfo->_displayMode & RM_DISP_MODE_SIMU_SIMU))
{
GfPoolMove(&elt->_newTrackMemPool, &oldPool);
curRobot->rbNewTrack(elt->_driverIndex, ReInfo->track, carhdle, &handle, ReInfo->s);
GfPoolFreePool( &oldPool );
}
else
handle = NULL;
if (handle && !replayReplay) {
GfLogTrace("Checking/Merging %s specific setup into %s setup.\n",
curModInfo->name, elt->_carName);
if (GfParmCheckHandle(carhdle, handle)) {
GfLogError("Bad Car parameters for driver %s\n", elt->_name);
return NULL;
}
handle = GfParmMergeHandles(carhdle, handle,
GFPARM_MMODE_SRC | GFPARM_MMODE_DST | GFPARM_MMODE_RELSRC | GFPARM_MMODE_RELDST);
} else {
GfLogTrace("Keeping %s setup as is for %s (no specific setup).\n",
elt->_carName, curModInfo->name);
handle = carhdle;
}
elt->_carHandle = handle;
/* Initialize sectors */
elt->_currentSector = 0;
elt->_curSplitTime = (double*)malloc( sizeof(double) * ( ReInfo->track->numberOfSectors - 1 ) );
elt->_bestSplitTime = (double*)malloc( sizeof(double) * ( ReInfo->track->numberOfSectors - 1 ) );
for (xx = 0; xx < ReInfo->track->numberOfSectors - 1; ++xx)
{
elt->_curSplitTime[xx] = -1.0f;
elt->_bestSplitTime[xx] = -1.0f;
}
} else {
elt->_category[ 0 ] = '\0';
GfLogError("Bad Car category for driver %s\n", elt->_name);
return NULL;
}
return elt;
} else {
GfLogError("No description file for robot %s\n", cardllname);
}
return NULL;
}
/** Initialize the cars for a race.
The cars are positionned on the starting grid.
@return 0 Ok, -1 Error
*/
int
ReInitCars(void)
{
char buf[512];
char path[512];
int nCars;
int index;
int i, j;
const char *robotModuleName;
int robotIdx;
void *robhdle;
tCarElt *elt;
//const char *focused; // Never used.
//int focusedIdx; // Never used.
void *params = ReInfo->params;
/* Get the number of cars (= drivers) racing */
nCars = GfParmGetEltNb(params, RM_SECT_DRIVERS_RACING);
GfLogTrace("Loading %d car(s)\n", nCars);
FREEZ(ReInfo->carList);
ReInfo->carList = (tCarElt*)calloc(nCars, sizeof(tCarElt));
FREEZ(ReInfo->rules);
ReInfo->rules = (tRmCarRules*)calloc(nCars, sizeof(tRmCarRules));
//focused = GfParmGetStr(ReInfo->params, RM_SECT_DRIVERS, RM_ATTR_FOCUSED, "");
//focusedIdx = (int)GfParmGetNum(ReInfo->params, RM_SECT_DRIVERS, RM_ATTR_FOCUSEDIDX, NULL, 0);
index = 0;
/* For each car/driver : */
for (i = 1; i < nCars + 1; i++)
{
/* Get the name of the module (= shared library) of the robot */
snprintf(path, sizeof(path), "%s/%d", RM_SECT_DRIVERS_RACING, i);
robotModuleName = GfParmGetStr(ReInfo->params, path, RM_ATTR_MODULE, "");
robotIdx = (int)GfParmGetNum(ReInfo->params, path, RM_ATTR_IDX, NULL, 0);
#if 0 // SDW
if (replayReplay)
// Register against the Replay driver
snprintf(path, sizeof(path), "%sdrivers/replay/replay.%s", GfLibDir(), DLLEXT);
else
#endif
snprintf(path, sizeof(path), "%sdrivers/%s/%s.%s", GfLibDir(), robotModuleName, robotModuleName, DLLEXT);
/* Load the robot shared library */
if (GfModLoad(CAR_IDENT, path, ReInfo->robModList))
{
GfLogError("Failed to load robot module %s\n", path);
continue;
}
/* Load the racing driver info in the race data structure */
elt = NULL;
snprintf(path, sizeof(path), "%s/%d", RM_SECT_DRIVERS_RACING, i);
if ((int)GfParmGetNum(ReInfo->params, path, RM_ATTR_EXTENDED, NULL, 0) == 0)
{
/* Search for the index of the racing driver in the list of interfaces
of the module */
for (j = 0; j < (*(ReInfo->robModList))->modInfoSize; j++)
{
if ((*(ReInfo->robModList))->modInfo[j].name && (*(ReInfo->robModList))->modInfo[j].index == robotIdx)
{
/* We have the right driver : load it */
elt = reLoadSingleCar( index, i, j, robotIdx, TRUE, robotModuleName );
if (!elt)
{
GfLogError("No descriptor file for robot %s or parameter errors (1)\n", robotModuleName);
snprintf(buf, sizeof(buf), "Error: May be no driver, or some parameters are out of bound");
ReUI().addLoadingMessage(buf);
snprintf(buf, sizeof(buf), " Have a look at the console window for mode details about the error");
ReUI().addLoadingMessage(buf);
snprintf(buf, sizeof(buf), " Back to the config menu in 10 s ...");
ReUI().addLoadingMessage(buf);
// Wait some time to allow the user to read the message!
GfSleep(10.0); // 10 seconds
}
}
}
}
else
{
GfLogTrace("Loading robot %s descriptor file\n", robotModuleName );
snprintf(buf, sizeof(buf), "%sdrivers/%s/%s.xml", GfLocalDir(), robotModuleName, robotModuleName);
robhdle = GfParmReadFile(buf, GFPARM_RMODE_STD);
if (!robhdle)
{
snprintf(buf, sizeof(buf), "drivers/%s/%s.xml", robotModuleName, robotModuleName);
robhdle = GfParmReadFile(buf, GFPARM_RMODE_STD);
}
if (robhdle && ( strcmp( robotModuleName, "human" ) == 0 || strcmp( robotModuleName, "networkhuman" ) == 0 ) )
{
/* Human driver */
elt = reLoadSingleCar( index, i, robotIdx - (*(ReInfo->robModList))->modInfo[0].index, robotIdx, FALSE, robotModuleName );
}
else if (robhdle && ( strcmp( GfParmGetStr( robhdle, ROB_SECT_ARBITRARY, ROB_ATTR_TEAM, "foo" ),
GfParmGetStr( robhdle, ROB_SECT_ARBITRARY, ROB_ATTR_TEAM, "bar" ) ) == 0 ) )
{
elt = reLoadSingleCar( index, i, (*(ReInfo->robModList))->modInfoSize, robotIdx, FALSE, robotModuleName );
}
else
GfLogError("No descriptor for robot %s (2)\n", robotModuleName );
}
if (elt)
++index;
}
nCars = index; /* real number of cars */
if (nCars == 0)
{
GfLogError("No driver for that race ; exiting ...\n");
return -1;
}
else
{
GfLogInfo("%d driver(s) ready to race\n", nCars);
}
if (replayReplay)
replayRecord = 0;
else {
char buf[1024];
const char *replayRateSchemeName;
snprintf(buf, sizeof(buf), "%s%s", GfLocalDir(), RACE_ENG_CFG);
void *paramHandle = GfParmReadFile(buf, GFPARM_RMODE_REREAD | GFPARM_RMODE_CREAT);
replayRateSchemeName = GfParmGetStr(paramHandle, RM_SECT_RACE_ENGINE, RM_ATTR_REPLAY_RATE, "0");
GfParmReleaseHandle(paramHandle);
replayRecord = atoi(replayRateSchemeName);
}
if (replayRecord || replayReplay) {
int result;
#ifdef THIRD_PARTY_SQLITE3
result = sqlite3_open("/tmp/race.sqlite", &replayDB);
if (result) {
GfLogError("Replay: Unable to open Database: %s\n", sqlite3_errmsg(replayDB));
sqlite3_close(replayDB);
replayDB = NULL;
} else {
GfLogInfo("Replay: Database Opened 0x8%8.8X\n", replayDB);
if (replayRecord)
GfLogInfo("Replay: Record Timestep = %f\n", 1/(float)replayRecord);
if (replayReplay)
GfLogInfo("Replay: Playback from file\n");
/* speed up database by turning of synchronous behaviour/etc */
sqlite3_exec(replayDB, "PRAGMA synchronous = OFF", NULL, NULL, NULL);
sqlite3_exec(replayDB, "PRAGMA journal_mode = OFF", NULL, NULL, NULL);
sqlite3_exec(replayDB, "PRAGMA count_changes = OFF", NULL, NULL, NULL);
#if 0 // This pragma seems to prevent re-opening the sqlite3 database
sqlite3_exec(replayDB, "PRAGMA locking_mode = EXCLUSIVE", NULL, NULL, NULL);
#endif
sqlite3_exec(replayDB, "PRAGMA default_temp_store = MEMORY", NULL, NULL, NULL);
//replayBlobs = (sqlite3_stmt *) calloc(nCars, sizeof(void *)); //sqlite3_stmt));
replayTimestamp = -5;
ghostcarActive = 0;
}
#endif
}
ReInfo->s->_ncars = nCars;
FREEZ(ReInfo->s->cars);
ReInfo->s->cars = (tCarElt **)calloc(nCars, sizeof(tCarElt *));
for (i = 0; i < nCars; i++)
{
ReInfo->s->cars[i] = &(ReInfo->carList[i]);
#ifdef THIRD_PARTY_SQLITE3
//open a table for each car
if (replayDB) {
char command[200];
int result;
if (replayRecord) {
sprintf(command, "DROP TABLE IF EXISTS car%d", i);
result = sqlite3_exec(replayDB, command, 0, 0, 0);
if (result) GfLogInfo("Replay: Unable to drop table car%d: %s\n", i, sqlite3_errmsg(replayDB));
}
sprintf(command, "CREATE TABLE IF NOT EXISTS car%d (timestamp, lap, datablob BLOB)", i);
result = sqlite3_exec(replayDB, command, 0, 0, 0);
if (result) {
GfLogInfo("Replay: Unable to create table car%d: %s\n", i, sqlite3_errmsg(replayDB));
exit(0);
}
if (replayReplay) {
// Build index to allow faster read access
sprintf(command, "CREATE UNIQUE INDEX IF NOT EXISTS index%d ON car%d (timestamp)", i, i);
result = sqlite3_exec(replayDB, command, 0, 0, 0);
if (result) GfLogInfo("Replay: Unable to create index car%d: %s\n", i, sqlite3_errmsg(replayDB));
}
}
#endif
}
ReInfo->_rePitRequester = 0;
// TODO: reconsider splitting the call into one for cars, track and maybe other objects.
// I stuff for now anything into one call because collision detection works with the same
// library on all objects, so it is a bit dangerous to distribute the handling to various
// locations (because the library maintains global state like a default collision handler etc.).
RePhysicsEngine().initialize(nCars, ReInfo->track);
initStartingGrid();
initPits();
return 0;
}
void
ReRaceCleanup(void)
{
RePhysicsEngine().shutdown();
StandardGame::self().unloadPhysicsEngine();
ReStoreRaceResults(ReInfo->_reRaceName);
ReRaceCleanDrivers();
#ifdef THIRD_PARTY_SQLITE3
GfLogInfo("Replay: Database closed\n");
if (replayDB)
sqlite3_close(replayDB);
replayDB = NULL;
#endif
replayRecord = 0;
}
void
ReRaceCleanDrivers(void)
{
int i;
tRobotItf *robot;
int nCars;
tMemoryPool oldPool = NULL;
nCars = ReInfo->s->_ncars;
for (i = 0; i < nCars; i++)
{
robot = ReInfo->s->cars[i]->robot;
GfPoolMove( &ReInfo->s->cars[i]->_shutdownMemPool, &oldPool );
if (robot->rbShutdown && !(ReInfo->_displayMode & RM_DISP_MODE_SIMU_SIMU))
{
robot->rbShutdown(robot->index);
}
GfPoolFreePool( &oldPool );
GfParmReleaseHandle(ReInfo->s->cars[i]->_paramsHandle);
free(robot);
free(ReInfo->s->cars[i]->_curSplitTime);
free(ReInfo->s->cars[i]->_bestSplitTime);
}
RtTeamManagerRelease();
FREEZ(ReInfo->s->cars);
ReInfo->s->cars = 0;
ReInfo->s->_ncars = 0;
GfModUnloadList(&ReRacingRobotsModList);
}
// Get the name of the current "race"
// (actually the current "race session", like quali.1, quali2, ... 1st race, ...).
char *
ReGetCurrentRaceName(void)
{
char path[64];
int curRaceIdx;
void *params = ReInfo->params;
void *results = ReInfo->results;
curRaceIdx = (int)GfParmGetNum(results, RE_SECT_CURRENT, RE_ATTR_CUR_RACE, NULL, 1);
snprintf(path, sizeof(path), "%s/%d", RM_SECT_RACES, curRaceIdx);
return GfParmGetStrNC(params, path, RM_ATTR_NAME, 0);
}
// Get the previous "race" (actually the previous "race session").
char *
ReGetPrevRaceName(bool bLoop)
{
char path[64];
int curRaceIdx;
void *params = ReInfo->params;
void *results = ReInfo->results;
curRaceIdx = (int)GfParmGetNum(results, RE_SECT_CURRENT, RE_ATTR_CUR_RACE, NULL, 1) - 1;
if (bLoop && curRaceIdx <= 0)
curRaceIdx = (int)GfParmGetEltNb(params, RM_SECT_RACES);
snprintf(path, sizeof(path), "%s/%d", RM_SECT_RACES, curRaceIdx);
return GfParmGetStrNC(params, path, RM_ATTR_NAME, 0);
}