Bug #692 - Initial attempt at replay capability

git-svn-id: https://svn.code.sf.net/p/speed-dreams/code/trunk@5803 30fe4595-0a0c-4342-8851-515496e4dcbd
This commit is contained in:
mungewell 2014-07-30 03:19:34 +00:00
parent 560a5b9068
commit 79acb89c9b
33 changed files with 1944 additions and 34 deletions

View File

@ -1,4 +1,4 @@
INCLUDE(../cmake/macros.cmake)
SD_INSTALL_FILES(DATA cmake FILES splitargn.cmake robot.def.in.cmake FindENET.cmake FindPLIB.cmake FindSOLID.cmake
FindOGG.cmake FindVORBISFILE.cmake FindVORBIS.cmake)
FindOGG.cmake FindVORBISFILE.cmake FindVORBIS.cmake FindSQLITE3.cmake)

74
cmake/FindSQLITE3.cmake Normal file
View File

@ -0,0 +1,74 @@
# Find Sqlite3
# ~~~~~~~~~~~~
# Copyright (c) 2007, Martin Dobias <wonder.sk at gmail.com>
# Redistribution and use is allowed according to the terms of the BSD license.
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
#
# CMake module to search for Sqlite3 library
#
# If it's found it sets SQLITE3_FOUND to TRUE
# and following variables are set:
# SQLITE3_INCLUDE_DIR
# SQLITE3_LIBRARY
# FIND_PATH and FIND_LIBRARY normally search standard locations
# before the specified paths. To search non-standard paths first,
# FIND_* is invoked first with specified paths and NO_DEFAULT_PATH
# and then again with no specified paths to search the default
# locations. When an earlier FIND_* succeeds, subsequent FIND_*s
# searching for the same item do nothing.
# try to use framework on mac
# want clean framework path, not unix compatibility path
IF (APPLE)
IF (CMAKE_FIND_FRAMEWORK MATCHES "FIRST"
OR CMAKE_FRAMEWORK_PATH MATCHES "ONLY"
OR NOT CMAKE_FIND_FRAMEWORK)
SET (CMAKE_FIND_FRAMEWORK_save ${CMAKE_FIND_FRAMEWORK} CACHE STRING "" FORCE)
SET (CMAKE_FIND_FRAMEWORK "ONLY" CACHE STRING "" FORCE)
#FIND_PATH(SQLITE3_INCLUDE_DIR SQLite3/sqlite3.h)
FIND_LIBRARY(SQLITE3_LIBRARY SQLite3)
IF (SQLITE3_LIBRARY)
# FIND_PATH doesn't add "Headers" for a framework
SET (SQLITE3_INCLUDE_DIR ${SQLITE3_LIBRARY}/Headers CACHE PATH "Path to a file.")
ENDIF (SQLITE3_LIBRARY)
SET (CMAKE_FIND_FRAMEWORK ${CMAKE_FIND_FRAMEWORK_save} CACHE STRING "" FORCE)
ENDIF ()
ENDIF (APPLE)
FIND_PATH(SQLITE3_INCLUDE_DIR sqlite3.h
"$ENV{LIB_DIR}/include"
"$ENV{LIB_DIR}/include/sqlite"
#mingw
c:/msys/local/include
NO_DEFAULT_PATH
)
FIND_PATH(SQLITE3_INCLUDE_DIR sqlite3.h)
FIND_LIBRARY(SQLITE3_LIBRARY NAMES sqlite3 sqlite3_i PATHS
"$ENV{LIB_DIR}/lib"
#mingw
c:/msys/local/lib
NO_DEFAULT_PATH
)
FIND_LIBRARY(SQLITE3_LIBRARY NAMES sqlite3)
IF (SQLITE3_INCLUDE_DIR AND SQLITE3_LIBRARY)
SET(SQLITE3_FOUND TRUE)
ENDIF (SQLITE3_INCLUDE_DIR AND SQLITE3_LIBRARY)
IF (SQLITE3_FOUND)
IF (NOT SQLITE3_FIND_QUIETLY)
MESSAGE(STATUS "Found Sqlite3: ${SQLITE3_LIBRARY}")
ENDIF (NOT SQLITE3_FIND_QUIETLY)
ELSE (SQLITE3_FOUND)
IF (SQLITE3_FIND_REQUIRED)
MESSAGE(FATAL_ERROR "Could not find Sqlite3")
ENDIF (SQLITE3_FIND_REQUIRED)
ENDIF (SQLITE3_FOUND)

View File

@ -289,5 +289,31 @@ MACRO(CHECK_LIBRARIES)
ENDIF(OPTION_3RDPARTY_SOLID)
# SQLITE3
IF(OPTION_3RDPARTY_SQLITE3)
Find_Package(SQLITE3)
IF(SQLITE3_FOUND)
SET(HAVE_LIBSQLITE3 1)
MESSAGE(STATUS "Looking for library SQLITE3 - found")
ELSE(SQLITE3_FOUND)
MESSAGE(STATUS "Looking for library SQLITE3 - NOT found")
ENDIF(SQLITE3_FOUND)
ENDIF(OPTION_3RDPARTY_SQLITE3)
# SQLITE3
IF(OPTION_3RDPARTY_SQLITE3)
Find_Package(SQLITE3)
IF(SQLITE3_FOUND)
SET(HAVE_LIBSQLITE3 1)
MESSAGE(STATUS "Looking for library SQLITE3 - found")
ELSE(SQLITE3_FOUND)
MESSAGE(STATUS "Looking for library SQLITE3 - NOT found")
ENDIF(SQLITE3_FOUND)
ENDIF(OPTION_3RDPARTY_SQLITE3)
ENDMACRO(CHECK_LIBRARIES)

View File

@ -125,6 +125,11 @@ MACRO(_FIND_3RDPARTY_DEPENDENCIES ROOT_DIR)
# ENet.
_FIND_3RDPARTY_DEPENDENCY(ENET enet/enet.h "" enet ${ROOT_DIR} "")
# SQlite.
IF(OPTION_3RDPARTY_SQLITE3)
_FIND_3RDPARTY_DEPENDENCY(SQLITE3 sqlite3.h "" sqlite3 ${ROOT_DIR} "")
ENDIF(OPTION_3RDPARTY_SQLITE3)
# OpenSceneGraph
IF(OPTION_OSGGRAPH)

View File

@ -153,16 +153,16 @@ MACRO(ADD_SDLIB_INCLUDEDIR)
FIND_PATH(SDLIB_NETWORKING_INCLUDE_DIR network.h PATHS ${INCLUDE_CANDIDATE} /usr/include /usr/local/include)
MARK_AS_ADVANCED(SDLIB_NETWORKING_INCLUDE_DIR)
SET(SDLIB_EXTRA_INCLUDEDIR SDLIB_NETWORKING_INCLUDE_DIR)
ELSEIF(SDLIB_LIB STREQUAL "raceengine")
ELSEIF(SDLIB_LIB STREQUAL "standardgame")
IF(IN_SOURCETREE)
SET(INCLUDE_CANDIDATE ${SOURCE_DIR}/src/libs/raceengine)
SET(INCLUDE_CANDIDATE ${SOURCE_DIR}/src/modules/racing/standardgame)
ELSE(IN_SOURCETREE)
SET(INCLUDE_CANDIDATE ${SD_INCLUDEDIR_ABS})
ENDIF(IN_SOURCETREE)
FIND_PATH(SDLIB_RACEENGINE_INCLUDE_DIR raceengine.h PATHS ${INCLUDE_CANDIDATE} /usr/include /usr/local/include NO_DEFAULT_PATH)
FIND_PATH(SDLIB_RACEENGINE_INCLUDE_DIR raceengine.h PATHS ${INCLUDE_CANDIDATE} /usr/include /usr/local/include)
MARK_AS_ADVANCED(SDLIB_RACEENGINE_INCLUDE_DIR)
SET(SDLIB_EXTRA_INCLUDEDIR SDLIB_RACEENGINE_INCLUDE_DIR)
FIND_PATH(SDLIB_STANDARDGAME_INCLUDE_DIR standardgame.h PATHS ${INCLUDE_CANDIDATE} /usr/include /usr/local/include NO_DEFAULT_PATH)
FIND_PATH(SDLIB_STANDARDGAME_INCLUDE_DIR standardgame.h PATHS ${INCLUDE_CANDIDATE} /usr/include /usr/local/include)
MARK_AS_ADVANCED(SDLIB_STANDARDGAME_INCLUDE_DIR)
SET(SDLIB_EXTRA_INCLUDEDIR SDLIB_STANDARDGAME_INCLUDE_DIR)
ELSE(SDLIB_LIB STREQUAL "portability")
SET(SDLIB_FOUND_LIB FALSE)
IF(NOT SDLIB_LIB STREQUAL "OPTIONAL")
@ -300,14 +300,14 @@ MACRO(ADD_SDLIB_LIBRARY TARGET)
ENDIF(IN_SOURCETREE)
FIND_LIBRARY(SDLIB_NETWORKING_LIBRARY networking PATHS ${LIBRARY_CANDIDATE} /usr/lib /usr/local/lib PATH_SUFFIXES "" lib)
SET(SDLIB_EXTRA_LIBRARY SDLIB_NETWORKING_LIBRARY)
ELSEIF(SDLIB_LIB STREQUAL "raceengine")
ELSEIF(SDLIB_LIB STREQUAL "standardgame")
IF(IN_SOURCETREE)
SET(LIBRARY_CANDIDATE ${SOURCE_DIR}/src/libs/raceengine)
SET(LIBRARY_CANDIDATE ${SOURCE_DIR}/src/modules/racing/standardgame)
ELSE(IN_SOURCETREE)
SET(LIBRARY_CANDIDATE ${SD_LIBDIR_ABS})
ENDIF(IN_SOURCETREE)
FIND_LIBRARY(SDLIB_RACEENGINE_LIBRARY raceengine PATHS ${LIBRARY_CANDIDATE} /usr/lib /usr/local/lib PATH_SUFFIXES "" lib)
SET(SDLIB_EXTRA_LIBRARY SDLIB_RACEENGINE_LIBRARY)
FIND_LIBRARY(SDLIB_STANDARDGAME_LIBRARY standardgame PATHS ${LIBRARY_CANDIDATE} /usr/lib /usr/local/lib PATH_SUFFIXES "" lib)
SET(SDLIB_EXTRA_LIBRARY SDLIB_STANDARDGAME_LIBRARY)
ELSEIF(SDLIB_LIB STREQUAL "legacymenu")
IF(IN_SOURCETREE)
SET(LIBRARY_CANDIDATE ${SOURCE_DIR}/src/modules/userinterface/legacymenu)

View File

@ -74,6 +74,10 @@ MACRO(ADD_SD_COMPILE_OPTIONS)
SET(OPTION_3RDPARTY_EXPAT true CACHE BOOL "Use 3rd party Expat library rather than bundled TXML")
SET(OPTION_3RDPARTY_SQLITE3 false CACHE BOOL "Use SQLite3 as database for record/replay")
SET(OPTION_3RDPARTY_SQLITE3 false CACHE BOOL "Use SQLite3 as database for record/replay")
# Enable building with 3rd party SOLID library under Windows, as we ship the binary package,
# but not under Linux, where FreeSolid seems not to be available by default on most distros.
IF(WIN32)
@ -150,6 +154,14 @@ MACRO(ADD_SD_COMPILE_OPTIONS)
ADD_DEFINITIONS(-DTHIRD_PARTY_EXPAT)
ENDIF(OPTION_3RDPARTY_EXPAT)
IF(OPTION_3RDPARTY_SQLITE3)
ADD_DEFINITIONS(-DTHIRD_PARTY_SQLITE3)
ENDIF(OPTION_3RDPARTY_SQLITE3)
IF(OPTION_3RDPARTY_SQLITE3)
ADD_DEFINITIONS(-DTHIRD_PARTY_SQLITE3)
ENDIF(OPTION_3RDPARTY_SQLITE3)
IF(OPTION_3RDPARTY_SOLID)
ADD_DEFINITIONS(-DTHIRD_PARTY_SOLID)
ENDIF(OPTION_3RDPARTY_SOLID)

View File

@ -20,6 +20,54 @@
# @author Mart Kelder, J.-P. Meuret
# @version $Id$
MACRO(ADD_SQLITE3_INCLUDEDIR)
FIND_PACKAGE(SQLITE3)
IF(SQLITE3_FOUND)
INCLUDE_DIRECTORIES(${SQLITE3_INCLUDE_DIR})
ELSE(SQLITE3_FOUND)
MESSAGE(FATAL_ERROR "Cannot find SQLITE3 header files")
ENDIF(SQLITE3_FOUND)
ENDMACRO(ADD_SQLITE3_INCLUDEDIR)
MACRO(ADD_SQLITE3_LIBRARY TARGET)
FIND_PACKAGE(SQLITE3)
IF(SQLITE3_FOUND)
TARGET_LINK_LIBRARIES(${TARGET} ${SQLITE3_LIBRARY})
ELSE(SQLITE3_FOUND)
MESSAGE(FATAL_ERROR "Cannot find SQLITE3 libraries")
ENDIF(SQLITE3_FOUND)
ENDMACRO(ADD_SQLITE3_LIBRARY TARGET)
MACRO(ADD_SQLITE3_INCLUDEDIR)
FIND_PACKAGE(SQLITE3)
IF(SQLITE3_FOUND)
INCLUDE_DIRECTORIES(${SQLITE3_INCLUDE_DIR})
ELSE(SQLITE3_FOUND)
MESSAGE(FATAL_ERROR "Cannot find SQLITE3 header files")
ENDIF(SQLITE3_FOUND)
ENDMACRO(ADD_SQLITE3_INCLUDEDIR)
MACRO(ADD_SQLITE3_LIBRARY TARGET)
FIND_PACKAGE(SQLITE3)
IF(SQLITE3_FOUND)
TARGET_LINK_LIBRARIES(${TARGET} ${SQLITE3_LIBRARY})
ELSE(SQLITE3_FOUND)
MESSAGE(FATAL_ERROR "Cannot find SQLITE3 libraries")
ENDIF(SQLITE3_FOUND)
ENDMACRO(ADD_SQLITE3_LIBRARY TARGET)
MACRO(ADD_PLIB_INCLUDEDIR)
FIND_PACKAGE(PLIB)

View File

@ -166,6 +166,27 @@
<attstr name="pushed image" val="data/img/button-left-pushed.png"/>
</section>
<section name="ReplayButton">
<attstr name="type" val="text button"/>
<attstr name="show box" val="no"/>
<attstr name="text" val="Replay"/>
<attstr name="tip" val="View a replay of the race"/>
<attstr name="h align" val="left"/>
<attnum name="x" val="160"/>
<attnum name="y" val="44"/>
<attnum name="width" val="150"/>
<attstr name="font" val="medium"/>
<attstr name="color" val="0xFFFFFF"/>
<attstr name="focused color" val="0xFFFFFF"/>
<attstr name="pushed color" val="0xFFFFFF"/>
<attnum name="image x" val="-10"/>
<attnum name="image y" val="4"/>
<attnum name="image width" val="60"/>
<attnum name="image height" val="16"/>
<attstr name="focused image" val="data/img/button-left-focused.png"/>
<attstr name="enabled image" val="data/img/button-left.png"/>
<attstr name="pushed image" val="data/img/button-left-pushed.png"/>
</section>
<!--
<section name="SaveButton">
<attstr name="type" val="text button"/>

View File

@ -205,6 +205,28 @@
<attstr name="pushed image" val="data/img/button-left-pushed.png"/>
</section>
<section name="ReplayButton">
<attstr name="type" val="text button"/>
<attstr name="show box" val="no"/>
<attstr name="text" val="Replay"/>
<attstr name="tip" val="View a replay of the race"/>
<attstr name="h align" val="left"/>
<attnum name="x" val="160"/>
<attnum name="y" val="44"/>
<attnum name="width" val="150"/>
<attstr name="font" val="medium"/>
<attstr name="color" val="0xFFFFFF"/>
<attstr name="focused color" val="0xFFFFFF"/>
<attstr name="pushed color" val="0xFFFFFF"/>
<attnum name="image x" val="-10"/>
<attnum name="image y" val="4"/>
<attnum name="image width" val="60"/>
<attnum name="image height" val="16"/>
<attstr name="focused image" val="data/img/button-left-focused.png"/>
<attstr name="enabled image" val="data/img/button-left.png"/>
<attstr name="pushed image" val="data/img/button-left-pushed.png"/>
</section>
<!--
<section name="SaveButton">
<attstr name="type" val="text button"/>

View File

@ -53,7 +53,7 @@
<section name="simulabel">
<attstr name="type" val="label"/>
<attnum name="max len" val="5"/>
<attnum name="max len" val="6"/>
<attstr name="tip" val="Choose your simulation engine (Warning: Physics setups designed only for V2.1 and V4.0)"/>
<attnum name="x" val="295"/>
<attnum name="y" val="322"/>
@ -139,6 +139,44 @@
<attstr name="font" val="medium"/>
</section>
<section name="replayrateleftarrow">
<attstr name="type" val="image button"/>
<attnum name="width" val="24"/>
<attnum name="height" val="24"/>
<attstr name="tip" val=""/>
<attnum name="x" val="265"/>
<attnum name="y" val="228"/>
<attstr name="disabled image" val="data/img/arrow-left-top-disabled.png"/>
<attstr name="enabled image" val="data/img/arrow-left-top.png"/>
<attstr name="focused image" val="data/img/arrow-left-top-focused.png"/>
<attstr name="pushed image" val="data/img/arrow-left-top-pushed.png"/>
</section>
<section name="replayraterightarrow">
<attstr name="type" val="image button"/>
<attnum name="width" val="24"/>
<attnum name="height" val="24"/>
<attstr name="tip" val=""/>
<attnum name="x" val="465"/>
<attnum name="y" val="228"/>
<attstr name="disabled image" val="data/img/arrow-right-top-disabled.png"/>
<attstr name="enabled image" val="data/img/arrow-right-top.png"/>
<attstr name="focused image" val="data/img/arrow-right-top-focused.png"/>
<attstr name="pushed image" val="data/img/arrow-right-top-pushed.png"/>
</section>
<section name="replayratelabel">
<attstr name="type" val="label"/>
<attnum name="max len" val="8"/>
<attstr name="tip" val="Determines the quality (and size) of the replay file"/>
<attnum name="x" val="295"/>
<attnum name="y" val="232"/>
<attnum name="width" val="160"/>
<attstr name="h align" val="center"/>
<attstr name="color" val="0xFFFFFF"/>
<attstr name="font" val="medium"/>
</section>
<section name="CancelButton">
<attstr name="type" val="text button"/>
<attstr name="show box" val="no"/>
@ -240,6 +278,16 @@
<attstr name="font" val="small_t"/>
</section>
<section name="7">
<attstr name="type" val="label"/>
<attnum name="x" val="20"/>
<attnum name="y" val="232"/>
<attstr name="text" val="Replay Quality:"/>
<attstr name="h align" val="right"/>
<attnum name="width" val="220"/>
<attstr name="font" val="small_t"/>
</section>
</section>
</params>

View File

@ -6,6 +6,7 @@ SD_ADD_SUBDIRECTORY(kilo2008)
SD_ADD_SUBDIRECTORY(networkhuman)
SD_ADD_SUBDIRECTORY(simplix)
SD_ADD_SUBDIRECTORY(usr)
SD_ADD_SUBDIRECTORY(replay)
# Work-in-progress / unofficial robots.
IF(NOT OPTION_OFFICIAL_ONLY)

View File

@ -0,0 +1,11 @@
INCLUDE(../../../cmake/macros.cmake)
SET(ROBOT_NAME "replay")
SET(ROBOT_SOURCES ${ROBOT_NAME}.cpp)
ROBOT_MODULE(NAME ${ROBOT_NAME} VERSION 1.0.0 SOVERSION 1.0.0
INTERFACE LEGACY_MIN
SOURCES ${ROBOT_SOURCES})
# For data associated to the robot module, see data/drivers/hymie/CMakeLists.txt

View File

@ -0,0 +1,137 @@
/***************************************************************************
file : replay.cpp
created : Wed Jan 8 18:31:16 CET 2003
copyright : (C) 2007 Andrew Sumner, 2002-2004 Bernhard Wymann
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. *
* *
***************************************************************************/
#ifdef _WIN32
#include <windows.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <tgf.h>
#include <track.h>
#include <car.h>
#include <raceman.h>
#include <robottools.h>
#include <robot.h>
#define BUFSIZE 20
#define NBBOTS 10
static char const* botname[] = {
"RP: Replay 1",
"RP: Replay 2",
"RP: Replay 3",
"RP: Replay 4",
"RP: Replay 5",
"RP: Replay 6",
"RP: Replay 7",
"RP: Replay 8",
"RP: Replay 9",
"RP: Replay 10"
};
static void initTrack(int index, tTrack* track, void *carHandle, void **carParmHandle, tSituation *s);
static void newRace(int index, tCarElt* car, tSituation *s);
static void drive(int index, tCarElt* car, tSituation *s);
static int pitcmd(int index, tCarElt* car, tSituation *s);
static void shutdown(int index);
static int InitFuncPt(int index, void *pt);
static void endRace(int index, tCarElt *car, tSituation *s);
// Module entry point.
extern "C" int replay(tModInfo *modInfo)
{
int i;
// Clear all structures.
memset(modInfo, 0, NBBOTS*sizeof(tModInfo));
for (i = 0; i < NBBOTS; i++) {
modInfo[i].name = botname[i]; // name of the module (short).
modInfo[i].desc = botname[i]; // Description of the module (can be long).
modInfo[i].fctInit = InitFuncPt; // Init function.
modInfo[i].gfId = ROB_IDENT; // Supported framework version.
modInfo[i].index = i+1; // Indices from 0 to 9.
}
return 0;
}
// Module interface initialization.
static int InitFuncPt(int index, void *pt)
{
tRobotItf *itf = (tRobotItf *)pt;
// Create robot instance for index.
itf->rbNewTrack = initTrack; // Give the robot the track view called.
itf->rbNewRace = newRace; // Start a new race.
itf->rbDrive = drive; // Drive during race.
itf->rbPitCmd = pitcmd; // Pit commands.
itf->rbEndRace = endRace; // End of the current race.
itf->rbShutdown = shutdown; // Called before the module is unloaded.
itf->index = index; // Index used if multiple interfaces.
return 0;
}
// Called for every track change or new race.
static void initTrack(int index, tTrack* track, void *carHandle, void **carParmHandle, tSituation *s)
{
//driver[index-1]->initTrack(track, carHandle, carParmHandle, s);
}
// Start a new race.
static void newRace(int index, tCarElt* car, tSituation *s)
{
//driver[index-1]->newRace(car, s);
}
// Drive during race.
static void drive(int index, tCarElt* car, tSituation *s)
{
//driver[index-1]->drive(s);
}
// Pitstop callback.
static int pitcmd(int index, tCarElt* car, tSituation *s)
{
//return driver[index-1]->pitCommand(s);
}
// End of the current race.
static void endRace(int index, tCarElt *car, tSituation *s)
{
//driver[index-1]->endRace(s);
}
// Called before the module is unloaded.
static void shutdown(int index)
{
}

View File

@ -177,6 +177,7 @@ typedef struct {
double curTime;
tdble topSpeed;
int laps;
int bestLap;
int nbPitStops;
int remainingLaps;
int pos;
@ -204,6 +205,7 @@ typedef struct {
#define _curTime race.curTime
#define _topSpeed race.topSpeed
#define _laps race.laps
#define _bestLap race.bestLap
#define _nbPitStops race.nbPitStops
#define _remainingLaps race.remainingLaps
#define _pos race.pos

View File

@ -416,15 +416,25 @@ typedef struct RmInfo
#define RM_VAL_MOD_SIMU_V2_1 "simuv2.1"
#define RM_VAL_MOD_SIMU_V3 "simuv3"
#define RM_VAL_MOD_SIMU_V4 "simuv4"
#define RM_VAL_MOD_SIMU_REPLAY "simureplay"
#define RM_VAL_MOD_TRACK "track"
#define RM_VAL_MOD_SSGRAPH "ssggraph"
/* Replay Settings */
#define RM_VAL_REPLAY_OFF "0"
#define RM_VAL_REPLAY_LOW "2"
#define RM_VAL_REPLAY_NORMAL "10"
#define RM_VAL_REPLAY_HIGH "30"
#define RM_VAL_REPLAY_PERFECT "100"
/* Race Engine itself */
#define RM_SECT_RACE_ENGINE "Race Engine"
#define RM_ATTR_MULTI_THREADING "multi-threading"
#define RM_ATTR_THREAD_AFFINITY "thread affinity"
#define RM_ATTR_REPLAY_RATE "replay rate"
#define RM_VAL_AUTO "auto"
#define RM_VAL_ON "on"

View File

@ -23,7 +23,37 @@
#define RPL_IDENT 0
#include <car.h>
#define REPLAY_TIMESTEP 2 // 2Hz Data logging
#define REPLAY_CHUNK 100
typedef struct ReplayElt
{
double currentTime;
tInitCar info; /**< public */
tPublicCar pub; /**< public */
tCarRaceInfo race; /**< public */
tPrivCar priv; /**< private */
tCarCtrl ctrl; /**< private */
tCarPitCmd pitcmd; /**< private */
} tReplayElt;
extern int replayRecord;
extern int replayReplay;
extern double replayTimestamp;
extern int ghostcarActive;
extern double ghostcarTimeOffset;
extern tReplayElt curGhostcarData, nextGhostcarData;
#ifdef THIRD_PARTY_SQLITE3
#include <sqlite3.h>
extern sqlite3 *replayDB;
extern sqlite3_stmt *replayBlobs[50]; // Hard coded 50 car limit to race
extern sqlite3_stmt *ghostcarBlob;
#endif
#endif /* __REPLAYV1_H__ */

View File

@ -238,12 +238,23 @@ void ReSituationUpdater::runOneStep(double deltaTimeIncrement)
}
// Update times.
pCurrReInfo->_reCurTime += deltaTimeIncrement * pCurrReInfo->_reTimeMult; /* "Real" time */
pCurrReInfo->_reCurTime += deltaTimeIncrement * fabs(pCurrReInfo->_reTimeMult); /* "Real" time */
#if 0
s->currentTime += deltaTimeIncrement; /* Simulated time */
#else
if (pCurrReInfo->_reTimeMult > 0)
s->currentTime += deltaTimeIncrement;
else
s->currentTime -= deltaTimeIncrement;
#endif
if (s->currentTime < 0) {
/* no simu yet */
pCurrReInfo->s->_raceState = RM_RACE_PRESTART;
if (pCurrReInfo->_reTimeMult < 0)
/* Revert to forward time x1 */
pCurrReInfo->_reTimeMult = 1;
else
/* no simu yet */
pCurrReInfo->s->_raceState = RM_RACE_PRESTART;
} else if (pCurrReInfo->s->_raceState == RM_RACE_PRESTART) {
pCurrReInfo->s->_raceState = RM_RACE_RUNNING;
s->currentTime = 0.0; /* resynchronize */

View File

@ -23,6 +23,10 @@ ADD_SDLIB_INCLUDEDIR(math portability robottools tgf tgfdata networking)
ADD_PLIB_INCLUDEDIR()
ADD_SDL_INCLUDEDIR()
IF(OPTION_3RDPARTY_SQLITE3)
ADD_SQLITE3_INCLUDEDIR()
ENDIF(OPTION_3RDPARTY_SQLITE3)
# Disable developer warning
IF (COMMAND CMAKE_POLICY)
CMAKE_POLICY(SET CMP0003 NEW)
@ -46,4 +50,8 @@ SD_ADD_LIBRARY(standardgame MODULE ${_SOURCES} ${_HEADERS} ${_OTHER_SOURCES})
ADD_SDLIB_LIBRARY(standardgame portability tgf tgfdata robottools networking)
IF(OPTION_3RDPARTY_SQLITE3)
ADD_SQLITE3_LIBRARY(standardgame)
ENDIF(OPTION_3RDPARTY_SQLITE3)
SD_INSTALL_FILES(LIB modules/racing TARGETS standardgame)

View File

@ -31,6 +31,7 @@
#include <robot.h>
#include <robottools.h>
#include <teammanager.h>
#include <replay.h>
#include "standardgame.h"
@ -39,6 +40,12 @@
#include "raceresults.h"
#include "racecars.h"
int ghostcarActive;
double ghostcarTimeOffset;
tReplayElt curGhostcarData, nextGhostcarData;
#ifdef THIRD_PARTY_SQLITE3
sqlite3_stmt *ghostcarBlob;
#endif
/* Compute Pit stop time */
void
@ -166,6 +173,7 @@ reCarsApplyRaceRules(tCarElt *car)
// to enjoy the landscape.
// Also - don't remove cars that are currently being repaired in pits
// TODO: Make it configurable.
if ((car->_curLapTime > 84.5 + ReInfo->track->length/10.0) &&
!(car->_state & RM_CAR_STATE_PIT) &&
(car->_driverType != RM_DRV_HUMAN))
@ -560,7 +568,9 @@ ReCarsManageCar(tCarElt *car, bool& bestLapChanged)
}
if ((car->_lastLapTime < car->_bestLapTime) || (car->_bestLapTime == 0)) {
car->_bestLapTime = car->_lastLapTime;
car->_bestLap = car->_laps - 1;
memcpy(car->_bestSplitTime, car->_curSplitTime, sizeof(double)*(ReInfo->track->numberOfSectors - 1) );
if (s->_raceType != RM_TYPE_RACE && s->_ncars > 1)
{
/* Best lap time is made better : update times behind leader */
@ -677,6 +687,34 @@ ReCarsManageCar(tCarElt *car, bool& bestLapChanged)
return;
}
#if 0 //def THIRD_PARTY_SQLITE3
// Re-read the best lap for ghostcar
if (replayDB != NULL && car->_bestLap) {
char command[200];
int result;
GfLogInfo("Re-reading best lap\n");
sprintf(command, "SELECT datablob FROM car0 where lap=%d", car->_bestLap);
result = sqlite3_prepare_v2(replayDB, command, -1, &ghostcarBlob, 0);
if (result) {
GfLogInfo("Unable to read ghostlap %d: %s\n", car->_bestLap, sqlite3_errmsg(replayDB));
} else {
// read the first 2 records
result = sqlite3_step(ghostcarBlob);
if (result == SQLITE_ROW) {
memcpy(&curGhostcarData, sqlite3_column_blob(ghostcarBlob, 0), sizeof(tReplayElt));
}
result = sqlite3_step(ghostcarBlob);
if (result == SQLITE_ROW) {
memcpy(&nextGhostcarData, sqlite3_column_blob(ghostcarBlob, 0), sizeof(tReplayElt));
}
ghostcarTimeOffset = s->currentTime - curGhostcarData.currentTime;
ghostcarActive = 1;
}
}
#endif
} else {
info->lapFlag--;
}

View File

@ -28,10 +28,15 @@
#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>
@ -61,6 +66,13 @@ 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)
@ -463,7 +475,13 @@ static tCarElt* reLoadSingleCar( int carindex, int listindex, int modindex, int
/* good robot found */
curModInfo = &((*(ReInfo->robModList))->modInfo[modindex]);
GfLogInfo("Driver's name: %s\n", curModInfo->name);
#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;
@ -475,7 +493,14 @@ static tCarElt* reLoadSingleCar( int carindex, int listindex, int modindex, int
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;
@ -650,7 +675,7 @@ static tCarElt* reLoadSingleCar( int carindex, int listindex, int modindex, int
}
else
handle = NULL;
if (handle) {
if (handle && !replayReplay) {
GfLogTrace("Checking/Merging %s specific setup into %s setup.\n",
curModInfo->name, elt->_carName);
if (GfParmCheckHandle(carhdle, handle)) {
@ -728,7 +753,14 @@ ReInitCars(void)
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);
snprintf(path, sizeof(path), "%sdrivers/%s/%s.%s", GfLibDir(), robotModuleName, robotModuleName, DLLEXT);
#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))
@ -805,13 +837,91 @@ ReInitCars(void)
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.
@ -836,6 +946,15 @@ ReRaceCleanup(void)
ReStoreRaceResults(ReInfo->_reRaceName);
ReRaceCleanDrivers();
#ifdef THIRD_PARTY_SQLITE3
GfLogInfo("Replay: Database closed\n");
if (replayDB)
sqlite3_close(replayDB);
replayDB = NULL;
#endif
replayRecord = 0;
}

View File

@ -124,6 +124,7 @@ static void ReSStCarInit( tCarElt *car )
car->_curTime = 0.3f * car->_pos;
car->_bestLapTime = 0.0f;
car->_laps = 0;
car->_bestLap = 0;
}
static tSimuSimuData* ReSSInit()
@ -196,8 +197,10 @@ static void ReSSStep( SimuSimuData *data )
//Change structure
car->_curTime += laptime;
if( car->_bestLapTime > laptime || car->_bestLapTime == 0.0f )
if( car->_bestLapTime > laptime || car->_bestLapTime == 0.0f ) {
car->_bestLapTime = laptime;
car->_bestLap = car->_laps;
}
++car->_laps;
}

View File

@ -29,10 +29,15 @@
#include <SDL.h>
#include <SDL_thread.h>
#ifdef THIRD_PARTY_SQLITE3
#include <sqlite3.h>
#endif
#include <portability.h>
#include <network.h>
#include <robot.h>
#include <raceman.h>
#include <replay.h>
#include "standardgame.h"
@ -43,7 +48,6 @@
#include "racemessage.h"
#include "racenetwork.h"
// The singleton.
ReSituation* ReSituation::_pSelf = 0;
@ -157,10 +161,26 @@ void ReSituation::accelerateTime(double fMultFactor)
{
lock("accelerateTime");
_pReInfo->_reTimeMult *= fMultFactor;
if (_pReInfo->_reTimeMult > 0.0)
_pReInfo->_reTimeMult *= fMultFactor;
else
_pReInfo->_reTimeMult /= fMultFactor;
if (fMultFactor == 0.0)
_pReInfo->_reTimeMult = 1.0;
else if (_pReInfo->_reTimeMult > 64.0)
else if (replayReplay) {
// allow for the reversal of time on replays (note this is divider not multipler)
if (_pReInfo->_reTimeMult > 4.0) {
GfLogInfo("Reversing Time %f\n", _pReInfo->_reCurTime);
_pReInfo->_reTimeMult = -4.0;
} else if (_pReInfo->_reTimeMult < -4.0) {
GfLogInfo("Correcting Time at %f\n", _pReInfo->_reCurTime);
_pReInfo->_reTimeMult = 4.0;
} else if (_pReInfo->_reTimeMult > -0.0625 && _pReInfo->_reTimeMult < 0.0)
_pReInfo->_reTimeMult = -0.0625;
else if (_pReInfo->_reTimeMult < 0.0625 && _pReInfo->_reTimeMult > 0.0)
_pReInfo->_reTimeMult = 0.0625;
} else if (_pReInfo->_reTimeMult > 64.0)
_pReInfo->_reTimeMult = 64.0;
else if (_pReInfo->_reTimeMult < 0.0625)
_pReInfo->_reTimeMult = 0.0625;
@ -256,12 +276,20 @@ void ReSituationUpdater::runOneStep(double deltaTimeIncrement)
}
// Update times.
pCurrReInfo->_reCurTime += deltaTimeIncrement * pCurrReInfo->_reTimeMult; /* "Real" time */
s->currentTime += deltaTimeIncrement; /* Simulated time */
pCurrReInfo->_reCurTime += deltaTimeIncrement * fabs(pCurrReInfo->_reTimeMult); /* "Real" time */
if (pCurrReInfo->_reTimeMult > 0)
s->currentTime += deltaTimeIncrement;
else
s->currentTime -= deltaTimeIncrement;
if (s->currentTime < 0) {
/* no simu yet */
pCurrReInfo->s->_raceState = RM_RACE_PRESTART;
if (pCurrReInfo->_reTimeMult < 0)
/* Revert to forward time x1 */
pCurrReInfo->_reTimeMult = 1;
else
/* no simu yet */
pCurrReInfo->s->_raceState = RM_RACE_PRESTART;
} else if (pCurrReInfo->s->_raceState == RM_RACE_PRESTART) {
pCurrReInfo->s->_raceState = RM_RACE_RUNNING;
s->currentTime = 0.0; /* resynchronize */
@ -297,7 +325,8 @@ void ReSituationUpdater::runOneStep(double deltaTimeIncrement)
for (int i = 0; i < s->_ncars; i++) {
if ((s->cars[i]->_state & RM_CAR_STATE_NO_SIMU) == 0) {
robot = s->cars[i]->robot;
robot->rbDrive(robot->index, s->cars[i], s);
if (replayReplay == 0)
robot->rbDrive(robot->index, s->cars[i], s);
}
else if (! (s->cars[i]->_state & RM_CAR_STATE_ENDRACE_CALLED ) && ( s->cars[i]->_state & RM_CAR_STATE_OUT ) == RM_CAR_STATE_OUT )
{ // No simu, look if it is out
@ -336,6 +365,10 @@ void ReSituationUpdater::runOneStep(double deltaTimeIncrement)
else if (pCurrReInfo->s->_raceType == RM_TYPE_QUALIF)
ReUpdateQualifCurRes(pCurrReInfo->s->cars[0]);
}
if (replayRecord && pCurrReInfo->s->currentTime >= replayTimestamp) {
replaySituation(pCurrReInfo);
}
}
int ReSituationUpdater::threadLoop(void* pUpdater)
@ -560,11 +593,18 @@ int ReSituationUpdater::terminate()
GfLogInfo("Terminating situation updater.\n");
/* need to ensure the last record gets writeen */
tRmInfo* pCurrReInfo = ReSituation::self().data();
if (replayRecord) {
replaySituation(pCurrReInfo);
GfLogInfo("Last replay entry done.\n");
}
// Lock the race engine data.
ReSituation::self().lock("ReSituationUpdater::terminate");
// Set the death flag.
_bTerminate = true;
_bTerminate = true;
// Unlock the race engine data.
ReSituation::self().unlock("ReSituationUpdater::terminate");
@ -661,6 +701,7 @@ tRmInfo* ReSituationUpdater::copySituation(tRmInfo*& pTarget, const tRmInfo* pSo
pTgtCar->_curTime = pSrcCar->_curTime;
pTgtCar->_topSpeed = pSrcCar->_topSpeed;
pTgtCar->_laps = pSrcCar->_laps;
pTgtCar->_bestLap = pSrcCar->_bestLap;
pTgtCar->_nbPitStops = pSrcCar->_nbPitStops;
pTgtCar->_remainingLaps = pSrcCar->_remainingLaps;
pTgtCar->_pos = pSrcCar->_pos;
@ -693,9 +734,9 @@ tRmInfo* ReSituationUpdater::copySituation(tRmInfo*& pTarget, const tRmInfo* pSo
//{
// tCarPenalty *newPenalty = (tCarPenalty*)malloc(sizeof(tCarPenalty));
// newPenalty->penalty = penalty->penalty;
// newPenalty->lapToClear = penalty->lapToClear;
// GfLogDebug("ReSituationCopy(car #%d) : Copying penalty %p to %p\n",
// pSrcCar->index, penalty, newPenalty);
// newPenalty->lapToClear = penalty->lapToClear;
// GfLogDebug("ReSituationCopy(car #%d) : Copying penalty %p to %p\n",
// pSrcCar->index, penalty, newPenalty);
// GF_TAILQ_INSERT_TAIL(&(pTgtCar->_penaltyList), newPenalty, link);
// penalty = GF_TAILQ_NEXT(penalty, link);
//}
@ -787,6 +828,152 @@ tRmInfo* ReSituationUpdater::copySituation(tRmInfo*& pTarget, const tRmInfo* pSo
return pTarget;
}
void ReSituationUpdater::replaySituation(tRmInfo*& pSource)
{
#ifdef THIRD_PARTY_SQLITE3
char command[200];
int result;
tReplayElt dummyTarget;
tReplayElt* pTgtCar;
tCarElt* pSrcCar;
if (!replayDB) return;
// Do everything in 1 transaction for speed
sqlite3_exec(replayDB, "BEGIN TRANSACTION", NULL, NULL, NULL);
for (int nCarInd = 0; nCarInd < _nInitDrivers; nCarInd++)
{
pTgtCar = &dummyTarget;
pSrcCar = &pSource->carList[nCarInd];
// Assemble the data we record
pTgtCar->currentTime = pSource->s->currentTime;
memcpy(&pTgtCar->info, &pSrcCar->info, sizeof(tInitCar));
memcpy(&pTgtCar->pub, &pSrcCar->pub, sizeof(tPublicCar));
memcpy(&pTgtCar->race, &pSrcCar->race, sizeof(tCarRaceInfo));
memcpy(&pTgtCar->priv, &pSrcCar->priv, sizeof(tPrivCar));
memcpy(&pTgtCar->ctrl, &pSrcCar->ctrl, sizeof(tCarCtrl));
memcpy(&pTgtCar->pitcmd, &pSrcCar->pitcmd, sizeof(tCarPitCmd));
// and write to database
sprintf(command, "INSERT INTO car%d (timestamp, lap, datablob) VALUES (%f, %d, ?)", nCarInd,
pSource->s->currentTime, pSrcCar->_laps);
result = sqlite3_prepare_v2(replayDB, command, -1, &replayBlobs[nCarInd], 0);
if (result) {
GfLogInfo("Replay: Unable to instert into table car%d: %s\n", nCarInd, sqlite3_errmsg(replayDB));
} else {
/* push binary blob into database */
result = sqlite3_bind_blob(replayBlobs[nCarInd], 1, (void *) &dummyTarget, sizeof(dummyTarget), SQLITE_STATIC);
result = sqlite3_step(replayBlobs[nCarInd]);
}
//GfLogInfo("Replay wrote car%d = time %f, lap %d\n", nCarInd, pSource->s->currentTime, pSrcCar->_laps);
}
sqlite3_exec(replayDB, "END TRANSACTION", NULL, NULL, NULL);
replayTimestamp = pSource->s->currentTime + (1/(float)replayRecord);
#endif
}
void ReSituationUpdater::ghostcarSituation(tRmInfo*& pTarget)
{
#ifdef THIRD_PARTY_SQLITE3
tCarElt *pTgtCar;
tReplayElt *pSrcCar, *pSrc2Car;
int result;
if (!replayDB) return;
if (ghostcarActive) {
if (pTarget->s->currentTime - ghostcarTimeOffset >= nextGhostcarData.currentTime) {
result = sqlite3_step(ghostcarBlob);
if (result == SQLITE_ROW) {
curGhostcarData = nextGhostcarData;
memcpy(&nextGhostcarData, sqlite3_column_blob(ghostcarBlob, 0), sizeof(tReplayElt));
} else {
// don't do anything untill next lap is started
// GfLogInfo("Ghostcar completed lap\n");
ghostcarActive = 0;
return;
}
// Ghostcar uses the last carElt
pTgtCar = &pTarget->carList[_nInitDrivers];
pSrcCar = &curGhostcarData;
// GfLogInfo("Read ghostcar data: time %f %f, lap %d\n", pTarget->s->currentTime, pSrcCar->currentTime, pSrcCar->_laps);
// Really this should only be read once at start of race
memcpy(&pTgtCar->race, &pSrcCar->race, sizeof(tCarRaceInfo));
pTgtCar->race.pit = NULL;
// hack to fix trkpos
pSrcCar->pub.trkPos = pTgtCar->pub.trkPos;
memcpy(&pTgtCar->pub, &pSrcCar->pub, sizeof(tPublicCar));
memcpy(&pTgtCar->info, &pSrcCar->info, sizeof(tInitCar));
for(int i=0; i < 4; i++) {
pTgtCar->priv.wheel[i] = pSrcCar->priv.wheel[i];
pTgtCar->priv.wheel[i].seg = NULL;
}
pTgtCar->priv.gear = pSrcCar->priv.gear;
pTgtCar->priv.fuel = pSrcCar->priv.fuel;
pTgtCar->priv.enginerpm = pSrcCar->priv.enginerpm;
pTgtCar->priv.dammage = pSrcCar->priv.dammage;
}
if (pTarget->s->currentTime - ghostcarTimeOffset < nextGhostcarData.currentTime) {
// Interpolate position in between records
double timeFrac;
double yaw, roll, pitch;
pTgtCar = &pTarget->carList[_nInitDrivers];
pSrcCar = &curGhostcarData;
pSrc2Car = &nextGhostcarData;
timeFrac = (pTarget->s->currentTime - ghostcarTimeOffset - curGhostcarData.currentTime) /
(nextGhostcarData.currentTime - curGhostcarData.currentTime);
pTgtCar->_pos_X = pSrcCar->_pos_X + (pSrc2Car->_pos_X - pSrcCar->_pos_X) * timeFrac;
pTgtCar->_pos_Y = pSrcCar->_pos_Y + (pSrc2Car->_pos_Y - pSrcCar->_pos_Y) * timeFrac;
pTgtCar->_pos_Z = pSrcCar->_pos_Z + (pSrc2Car->_pos_Z - pSrcCar->_pos_Z) * timeFrac - pTgtCar->_statGC_z;
yaw = pSrc2Car->_yaw;
roll = pSrc2Car->_roll;
pitch = pSrc2Car->_pitch;
// assumes that these can't change at high rate
if (yaw < pSrcCar->_yaw - PI)
yaw += 2 * PI;
else if (yaw > pSrcCar->_yaw + PI)
yaw -= 2 * PI;
if (roll < pSrcCar->_roll - PI)
roll += 2 * PI;
else if (roll > pSrcCar->_roll + PI)
roll -= 2 * PI;
if (pitch < pSrcCar->_pitch - PI)
pitch += 2 * PI;
else if (pitch > pSrcCar->_pitch + PI)
pitch -= 2 * PI;
pTgtCar->_yaw = pSrcCar->_yaw + (yaw - pSrcCar->_yaw) * timeFrac;
pTgtCar->_roll = pSrcCar->_roll + (roll - pSrcCar->_roll) * timeFrac;
pTgtCar->_pitch = pSrcCar->_pitch + (pitch - pSrcCar->_pitch) * timeFrac;
sgMakeCoordMat4(pTgtCar->pub.posMat, pTgtCar->_pos_X, pTgtCar->_pos_Y, pTgtCar->_pos_Z,
(tdble) RAD2DEG(pTgtCar->_yaw), (tdble) RAD2DEG(pTgtCar->_roll),
(tdble) RAD2DEG(pTgtCar->_pitch));
}
}
#endif
}
void ReSituationUpdater::freezSituation(tRmInfo*& pSituation)
{
if (pSituation)
@ -891,6 +1078,13 @@ tRmInfo* ReSituationUpdater::getPreviousStep()
return 0;
}
if (replayRecord && _pPrevReInfo->s->currentTime >= replayTimestamp) {
replaySituation(_pPrevReInfo);
}
if (replayRecord)
ghostcarSituation(_pPrevReInfo);
return _pPrevReInfo;
}

View File

@ -133,6 +133,12 @@ private:
//! (Deep) Copy the given situation.
struct RmInfo* copySituation(struct RmInfo*& pTarget, const struct RmInfo* pSource);
// Record the situation to a replay database
void replaySituation(struct RmInfo*& pTarget);
// Record the situation to a replay database
void ghostcarSituation(struct RmInfo*& pTarget);
//! Free the given situation
void freezSituation(struct RmInfo*& pSituation);

View File

@ -31,6 +31,7 @@
#include <tgfdata.h>
#include <race.h>
#include <tracks.h>
#include <replay.h>
#include "racesituation.h"
#include "racemain.h"
@ -40,6 +41,7 @@
#include "standardgame.h"
int replayReplay;
// The singleton.
StandardGame* StandardGame::_pSelf = 0;
@ -316,6 +318,13 @@ bool StandardGame::loadPhysicsEngine()
if (pmodPhysEngine && !_piPhysEngine)
GfModule::unload(pmodPhysEngine);
// don't record if we're 'replaying'
printf("Checking type of SIMU\n");
if (strcmp(RM_VAL_MOD_SIMU_REPLAY, strModName.c_str()) == 0)
replayReplay = 1;
else
replayReplay = 0;
return _piPhysEngine ? true : false;
}

View File

@ -11,3 +11,6 @@ IF(NOT OPTION_OFFICIAL_ONLY)
ENDIF()
IF(OPTION_3RDPARTY_SQLITE3)
SD_ADD_SUBDIRECTORY(simureplay)
ENDIF(OPTION_3RDPARTY_SQLITE3)

View File

@ -0,0 +1,47 @@
IF(NOT OPTION_3RDPARTY_SOLID)
INCLUDE_DIRECTORIES(BEFORE ../simuv2.1/SOLID-2.0/include)
ELSE(NOT OPTION_3RDPARTY_SOLID)
ADD_SOLID_INCLUDEDIR()
ENDIF(NOT OPTION_3RDPARTY_SOLID)
INCLUDE(../../../../cmake/macros.cmake)
ADD_SDLIB_INCLUDEDIR(portability)
IF(WIN32)
# DLL export stuff under Windows (to avoid .def file)
ADD_DEFINITIONS(-DSIMUVREPLAY_DLL)
ENDIF(WIN32)
IF(MSVC)
# Ignore some run-time libs to avoid link time warnings and sometimes even crashes.
SET(CMAKE_SHARED_LINKER_FLAGS_DEBUG "${CMAKE_MODULE_LINKER_FLAGS_DEBUG} /NODEFAULTLIB:msvcrt.lib")
ENDIF(MSVC)
SET(SIMU_SOURCES simu.cpp simureplay.cpp)
SET(SIMU_HEADERS sim.h simureplay.h)
IF(NOT OPTION_3RDPARTY_SOLID)
# BEFORE in order to prevent Windows 3rdParty/include/SOLID from taking the flag.
INCLUDE_DIRECTORIES(BEFORE SOLID-2.0/include)
ELSE(NOT OPTION_3RDPARTY_SOLID)
ADD_SOLID_INCLUDEDIR()
ENDIF(NOT OPTION_3RDPARTY_SOLID)
ADD_PLIB_INCLUDEDIR()
ADD_INTERFACE_INCLUDEDIR()
ADD_SDLIB_INCLUDEDIR(math tgf robottools)
# Note: Headers needed for having them available in IDEs.
SD_ADD_LIBRARY(simureplay MODULE ${SIMU_SOURCES} ${SIMU_HEADERS})
# Might not work with GCC 4.5 or + (non-robot modules crash at 1st reload = after 1 dlclose)
#SET_TARGET_PROPERTIES(simuv2.1 PROPERTIES VERSION ${VERSION} SOVERSION 0.0.0)
ADD_SOLID_LIBRARY(simureplay) # Ignored if not OPTION_3RDPARTY_SOLID
ADD_SDLIB_LIBRARY(simureplay portability tgf robottools solid) # solid ignored if not OPTION_3RDPARTY_SOLID
SD_INSTALL_FILES(LIB modules/simu TARGETS simureplay)

View File

@ -0,0 +1,156 @@
/***************************************************************************
file : carstruct.h
created : Sun Mar 19 00:06:07 CET 2000
copyright : (C) 2000 by Eric Espie
email : torcs@free.fr
version : $Id: carstruct.h 2917 2010-10-17 19:03:40Z pouillot $
***************************************************************************/
/***************************************************************************
* *
* 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. *
* *
***************************************************************************/
#ifndef _CAR__H_
#define _CAR__H_
#include <plib/sg.h>
#include <SOLID/solid.h>
typedef struct
{
tCarCtrl *ctrl;
void *params;
tCarElt *carElt;
tDynPt DynGC; /* GC local data except position */
tDynPt DynGCg; /* GC global data */
tTrkLocPos trkPos; /* current track position */
#if 0
/* driver's interface */
tCarCtrl *ctrl;
void *params;
tCarElt *carElt;
tCarCtrl preCtrl;
/* components */
tAxle axle[2];
tWheel wheel[4];
tSteer steer;
tBrakeSyst brkSyst;
tAero aero;
tWing wing[2];
tTransmission transmission; /* includes clutch, gearbox and driveshaft */
tEngine engine;
/* static */
t3Dd dimension; /* car's mesures */
tdble mass; /* mass with pilot (without fuel) */
tdble Minv; /* 1 / mass with pilot (without fuel) */
tdble tank; /* fuel tank capa */
t3Dd statGC; /* static pos of GC */
t3Dd Iinv; /* inverse of inertial moment along the car's 3 axis */
/* dynamic */
tdble fuel; /* current fuel load */
tDynPt DynGC; /* GC local data except position */
tDynPt DynGCg; /* GC global data */
tPosd VelColl; /* resulting velocity after collision */
tDynPt preDynGC; /* previous one */
tTrkLocPos trkPos; /* current track position */
tdble airSpeed2; /* current air speed (squared) for aerodynamic forces */
/* internals */
tdble Cosz;
tdble Sinz;
tDynPt corner[4]; /* x,y,z for static relative pos, ax,ay,az for dyn. world coord */
int collision;
t3Dd normal;
t3Dd collpos;
tdble wheelbase;
tdble wheeltrack;
sgMat4 posMat;
DtShapeRef shape; /* for collision */
int blocked; // Flag to show if the car has had already a collision in the same timestep.
int dammage;
/* enabling features */
int features;
tDynPt restPos; /* target rest position after the car is broken */
int collisionAware;
#endif
} tCar;
#if 0
#define CHECK_VAR(_var_, _msg_) do { \
if (isnan(_var_) || isinf(_var_)) { \
printf("%s = %f in %s line %d\n", _msg_, _var_, __FILE__, __LINE__); \
assert (0); \
exit(0); \
} \
} while (0)
#define DUMP_CAR(_car_) do { \
printf("DynGC.acc.x = %f\n", (_car_)->DynGC.acc.x); \
printf("DynGC.acc.y = %f\n", (_car_)->DynGC.acc.y); \
printf("DynGC.acc.y = %f\n", (_car_)->DynGC.acc.y); \
printf("DynGC.vel.x = %f\n", (_car_)->DynGC.vel.x); \
printf("DynGC.vel.y = %f\n", (_car_)->DynGC.vel.y); \
printf("DynGCg.pos.x = %f\n", (_car_)->DynGCg.pos.x); \
printf("DynGCg.pos.y = %f\n", (_car_)->DynGCg.pos.y); \
printf("DynGCg.acc.x = %f\n", (_car_)->DynGCg.acc.x); \
printf("DynGCg.acc.y = %f\n", (_car_)->DynGCg.acc.y); \
printf("DynGCg.vel.x = %f\n", (_car_)->DynGCg.vel.x); \
printf("DynGCg.vel.y = %f\n", (_car_)->DynGCg.vel.y); \
printf("DynGCg.pos.x = %f\n", (_car_)->DynGCg.pos.x); \
printf("DynGCg.pos.y = %f\n", (_car_)->DynGCg.pos.y); \
printf("aero.drag = %f\n", (_car_)->aero.drag); \
} while (0)
#define CHECK(_car_) do { \
if (isnan((_car_)->DynGC.acc.x) || isinf((_car_)->DynGC.acc.x) || \
isnan((_car_)->DynGC.acc.y) || isinf((_car_)->DynGC.acc.y) || \
isnan((_car_)->DynGC.vel.x) || isinf((_car_)->DynGC.vel.x) || \
isnan((_car_)->DynGC.vel.y) || isinf((_car_)->DynGC.vel.y) || \
isnan((_car_)->DynGC.acc.x) || isinf((_car_)->DynGC.acc.x) || \
isnan((_car_)->DynGCg.acc.y) || isinf((_car_)->DynGCg.acc.y) || \
isnan((_car_)->DynGCg.vel.x) || isinf((_car_)->DynGCg.vel.x) || \
isnan((_car_)->DynGCg.vel.y) || isinf((_car_)->DynGCg.vel.y) || \
isnan((_car_)->DynGCg.pos.x) || isinf((_car_)->DynGCg.pos.x) || \
isnan((_car_)->DynGCg.pos.y) || isinf((_car_)->DynGCg.pos.y) || \
isnan((_car_)->aero.drag) || isinf((_car_)->aero.drag)) { \
printf("Problem for %s in %s line %d\n", (_car_)->carElt->_name, __FILE__, __LINE__); \
DUMP_CAR(_car_); \
assert (0); \
/* GfScrShutdown(); */ \
exit(0); \
} \
} while (0)
#else
#define CHECK_VAR(_var_, _msg_)
#define CHECK(_car_)
#endif
#endif /* _CAR__H_ */

View File

@ -0,0 +1,128 @@
/***************************************************************************
file : sim.h
created : Sun Mar 19 00:07:42 CET 2000
copyright : (C) 2000 by Eric Espie
email : torcs@free.fr
version : $Id: sim.h 3568 2011-05-15 15:55:24Z pouillot $
***************************************************************************/
/***************************************************************************
* *
* 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. *
* *
***************************************************************************/
#ifndef _SIMFCT_H_
#define _SIMFCT_H_
#include <cmath>
#include <cstdio>
#include <cstring>
#include <portability.h>
#include <tgf.h>
#include <track.h>
#include <car.h>
#include <raceman.h>
#include <robottools.h>
#include "carstruct.h"
extern void SimConfig(tCarElt *carElt);
extern void SimReConfig(tCarElt *carElt);
extern void SimUpdate(tSituation*, double deltaTime);
extern void SimInit(int nbcars, tTrack* track);
extern void SimShutdown(void);
extern void SimCarTelemetry(int nCarIndex, bool bOn = true);
extern void UpdateSimCarTable(tDynPt DynGCG,int index);
extern void SimUpdateSingleCar(int index, double deltaTime,tSituation *s);
extern tDynPt* GetSimCarTable(int index);
#if 0
extern void SimAxleConfig(tCar *car, int index);
extern void SimAxleUpdate(tCar *car, int index);
extern void SimCarConfig(tCar *car);
extern void SimCarUpdate(tCar *car, tSituation*);
extern void SimCarUpdate2(tCar *car, tSituation*);
extern void SimSuspCheckIn(tSuspension *susp);
extern void SimSuspUpdate(tSuspension *susp);
extern void SimSuspConfig(void *hdle, const char *section, tSuspension *susp, tdble F0, tdble X0);
extern void SimWheelConfig(tCar *car, int index);
extern void SimWheelUpdateRide(tCar *car, int index);
extern void SimWheelUpdateForce(tCar *car, int index);
extern void SimWheelUpdateRotation(tCar *car);
extern void SimUpdateFreeWheels(tCar *car, int axlenb);
extern void SimSteerConfig(tCar *car);
extern void SimSteerUpdate(tCar *car);
extern void SimBrakeConfig(void *hdle, const char *section, tBrake *brake);
extern void SimBrakeUpdate(tCar *car, tWheel *wheel, tBrake *brake);
extern void SimBrakeSystemConfig(tCar *car);
extern void SimBrakeSystemUpdate(tCar *car);
extern void SimAeroConfig(tCar *car);
extern void SimAeroUpdate(tCar *car, tSituation *s);
extern void SimWingConfig(tCar *car, int index);
extern void SimWingUpdate(tCar *car, int index, tSituation *s);
extern void SimCarUpdateWheelPos(tCar *car);
extern void SimTransmissionConfig(tCar *car);
extern void SimTransmissionUpdate(tCar *car);
extern void SimGearboxUpdate(tCar *car);
extern void SimDifferentialConfig(void *hdle, const char *section, tDifferential *differential);
extern void SimDifferentialUpdate(tCar *car, tDifferential *differential, int first);
extern void SimEngineConfig(tCar *car);
extern void SimEngineUpdateTq(tCar *car);
extern tdble SimEngineUpdateRpm(tCar *car, tdble axleRpm);
extern void SimEngineShutdown(tCar *car);
extern void SimCarCollideZ(tCar *car);
extern void SimCarCollideXYScene(tCar *car);
extern void SimCarCollideCars(tSituation*);
extern void SimCarCollideConfig(tCar *car, tTrack *track);
extern void SimCarCollideShutdown(int nbcars);
extern void SimCarCollideInit(tTrack *track);
extern void SimCollideRemoveCar(tCar *car, int nbcars);
extern tdble SimDeltaTime;
extern int SimTelemetry;
extern tCar *SimCarTable;
extern t3Dd vectStart[];
extern t3Dd vectEnd[];
extern tdble simDammageFactor[];
extern tdble simSkidFactor[];
#endif
/// return a number drawn uniformly from [0,1]
inline float urandom() {
return ((((float)rand()-1)/((float)RAND_MAX)));
}
#define SIM_VECT_COLL 12
#define SIM_VECT_SPD 13
#define SIM_VECT_ACCEL 14
#define SIM_WHEEL_SPD 16
#define SIM_WHEEL_ACCEL 20
#define SIM_Z_OFFSET 2.0
#endif /* _SIMFCT_H_ */

View File

@ -0,0 +1,436 @@
/***************************************************************************
file : simu.cpp
created : Sun Mar 19 00:07:53 CET 2000
copyright : (C) 2000 by Eric Espie
email : torcs@free.fr
version : $Id: simu.cpp 3945 2011-10-07 13:38:15Z wdbee $
***************************************************************************/
/***************************************************************************
* *
* 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. *
* *
***************************************************************************/
#include <cstdlib>
#include <cstdio>
#include <memory.h>
#include <cmath>
#include <sqlite3.h>
#include <portability.h>
#include <tgf.h>
#include <robottools.h>
#include <replay.h>
#include "sim.h"
tCar *SimCarTable = 0;
int SimTelemetry = -1;
static tTrack *PTrack = 0;
static int SimNbCars = 0;
int replayForward;
double lastCurTime;
double replayTimeOffset;
static tReplayElt curReplayData[50];
static tReplayElt nextReplayData[50];
static int old_id;
/*
* Check the input control from robots
*/
static void
ctrlCheck(tCar *car)
{
}
/* Initial configuration */
void
SimConfig(tCarElt *carElt)
{
GfLogInfo("Replay SimConfig\n");
tCar *car = &(SimCarTable[carElt->index]);
memset(car, 0, sizeof(tCar));
car->carElt = carElt;
car->DynGC = carElt->_DynGC;
car->DynGCg = carElt->pub.DynGCg;
car->trkPos = carElt->_trkPos;
car->ctrl = &carElt->ctrl;
car->params = carElt->_carHandle;
#if 0
SimCarConfig(car);
SimCarCollideConfig(car, PTrack);
#endif
sgMakeCoordMat4(carElt->pub.posMat, carElt->_pos_X, carElt->_pos_Y, carElt->_pos_Z - carElt->_statGC_z,
(float) RAD2DEG(carElt->_yaw), (float) RAD2DEG(carElt->_roll), (float) RAD2DEG(carElt->_pitch));
replayTimeOffset = 0;
}
/* After pit stop */
void
SimReConfig(tCarElt *carElt)
{
}
static void
RemoveCar(tCar *car, tSituation *s)
{
}
void
SimCarTelemetry(int nCarIndex, bool bOn)
{
SimTelemetry = bOn ? nCarIndex : -1;
}
void
SimUpdate(tSituation *s, double deltaTime)
{
tCar *car;
tCarElt *pTgtCar;
tReplayElt *pSrcCar, *pSrc2Car;
char command[200];
int result, result2;
int reload = 0;
if (replayDB == NULL) {
GfLogInfo("Replay NULL\n");
return;
}
// Check for reversal of time
if (replayForward && s->currentTime < lastCurTime) {
GfLogInfo("REPLAY: time running backward at timestamp %f\n", s->currentTime);
reload = 1;
replayForward = 0;
} else if (replayForward == 0 && s->currentTime > lastCurTime) {
GfLogInfo("REPLAY: time running forward at timestamp %f\n", s->currentTime);
reload = 1;
replayForward = 1;
}
lastCurTime = s->currentTime;
// readback next race record as required
for (int nCarInd = 0; nCarInd < s->_ncars; nCarInd++) {
if (reload ||
(replayForward && s->currentTime >= nextReplayData[nCarInd].currentTime) ||
(replayForward == 0 && s->currentTime <= nextReplayData[nCarInd].currentTime)) {
result = sqlite3_step(replayBlobs[nCarInd]);
// check if the previous SELECT has returned all it's rows
if (replayForward && (result == SQLITE_DONE || reload)) {
// and read another chunk
GfLogInfo("REPLAY: Chunk forward timestamp %f (%f)\n", s->currentTime, deltaTime);
sprintf(command, "SELECT datablob FROM car%d WHERE timestamp >= %f LIMIT %d",
nCarInd, s->currentTime, REPLAY_CHUNK);
result2 = sqlite3_prepare_v2(replayDB, command, -1, &replayBlobs[nCarInd], 0);
if (result2) {
GfLogInfo("Unable to prepare car%d: %s\n", nCarInd, sqlite3_errmsg(replayDB));
} else {
result = sqlite3_step(replayBlobs[nCarInd]);
}
}
if (replayForward == 0 && (result == SQLITE_DONE || reload)) {
// and read another chunk
GfLogInfo("REPLAY: Chunk backward timestamp %f (%f)\n", s->currentTime, deltaTime);
sprintf(command, "SELECT datablob FROM car%d WHERE timestamp <= %f ORDER BY timestamp DESC LIMIT %d",
nCarInd, s->currentTime, REPLAY_CHUNK);
result2 = sqlite3_prepare_v2(replayDB, command, -1, &replayBlobs[nCarInd], 0);
if (result2) {
GfLogInfo("Unable to prepare car%d: %s\n", nCarInd, sqlite3_errmsg(replayDB));
} else {
result = sqlite3_step(replayBlobs[nCarInd]);
}
}
if (result == SQLITE_ROW) {
curReplayData[nCarInd] = nextReplayData[nCarInd];
memcpy(&nextReplayData[nCarInd], sqlite3_column_blob(replayBlobs[nCarInd], 0), sizeof(tReplayElt));
nextReplayData[nCarInd].currentTime += replayTimeOffset;
}
// allow for replaying data again and again
if (result == SQLITE_DONE) replayTimeOffset = s->currentTime;
//GfLogInfo("Replay read car%d: %f\n", nCarInd, curReplayData[nCarInd].currentTime);
car = &(SimCarTable[nCarInd]);
pTgtCar = car->carElt;
pSrcCar = &curReplayData[nCarInd];
#if 1
// Really this should only be read once at start of race
pSrcCar->race.pit = pTgtCar->race.pit;
pSrcCar->race.bestSplitTime = pTgtCar->race.bestSplitTime;
pSrcCar->race.curSplitTime = pTgtCar->race.curSplitTime;
memcpy(&pTgtCar->race, &pSrcCar->race, sizeof(tCarRaceInfo));
#endif
#if 1
// hack to fix trkpos
pSrcCar->pub.trkPos = pTgtCar->pub.trkPos;
memcpy(&pTgtCar->pub, &pSrcCar->pub, sizeof(tPublicCar));
pTgtCar->_glance = 0;
pTgtCar->_oldglance = 0;
#endif
#if 1
memcpy(&pTgtCar->info, &pSrcCar->info, sizeof(tInitCar));
pTgtCar->_wrongWayTime = s->currentTime + 5.0;
#endif
#if 0
memcpy(&pTgtCar->priv, &pSrcCar->priv, sizeof(tPrivCar));
#else
// Selectively pick 'priv' data
for(int i=0; i < 4; i++) {
pTgtCar->priv.wheel[i] = pSrcCar->priv.wheel[i];
pTgtCar->priv.wheel[i].seg = RtTrackGetSeg(&pTgtCar->_trkPos);
pTgtCar->_skid[i] = pSrcCar->_skid[i];
pTgtCar->priv.wheel[i].spinVel = pSrcCar->priv.wheel[i].spinVel;
}
pTgtCar->priv.gear = pSrcCar->priv.gear;
pTgtCar->priv.fuel = pSrcCar->priv.fuel;
pTgtCar->priv.enginerpm = pSrcCar->priv.enginerpm;
pTgtCar->priv.enginerpmRedLine = pSrcCar->priv.enginerpmRedLine;
pTgtCar->priv.dammage = pSrcCar->priv.dammage;
#endif
#if 0
memcpy(&pTgtCar->ctrl, &pSrcCar->ctrl, sizeof(tCarCtrl));
#else
pTgtCar->_steerCmd = pSrcCar->_steerCmd;
pTgtCar->_brakeCmd = pSrcCar->_brakeCmd;
pTgtCar->_clutchCmd = pSrcCar->_clutchCmd;
pTgtCar->_gearCmd = pSrcCar->_gearCmd;
#endif
#if 0
memcpy(&pTgtCar->pitcmd, &pSrcCar->pitcmd, sizeof(tCarPitCmd));
#endif
// figure out track segment (allowing that they may be allocated to different memory location)
pTgtCar->_distFromStartLine = pSrcCar->_distFromStartLine;
#if 0
if (pTgtCar->pub.trkPos.seg->id != old_id) {
printf("New TrkSeg %d @ %fs: DfS %f : %s = %fm", pTgtCar->pub.trkPos.seg->id, s->currentTime,
pTgtCar->_distFromStartLine,
pTgtCar->pub.trkPos.seg->name, pTgtCar->pub.trkPos.seg->lgfromstart);
if (pTgtCar->pub.trkPos.seg->raceInfo & TR_LAST) printf(" LAST");
if (pTgtCar->pub.trkPos.seg->raceInfo & TR_START) printf(" START");
printf("\n");
old_id = pTgtCar->pub.trkPos.seg->id;
}
#endif
if (pTgtCar->_distFromStartLine > pTgtCar->pub.trkPos.seg->next->lgfromstart) {
// Next segment, forward direction
pTgtCar->pub.trkPos.seg = pTgtCar->pub.trkPos.seg->next;
} else if (pTgtCar->pub.trkPos.seg->raceInfo & TR_LAST == TR_LAST &&
pTgtCar->pub.trkPos.seg->next->raceInfo & TR_START == TR_START &&
pTgtCar->_distFromStartLine < pTgtCar->pub.trkPos.seg->lgfromstart/2) {
// Crossing Start Line, forward direction
// something weird here, sometime reports wrong lap completed
pTgtCar->pub.trkPos.seg = pTgtCar->pub.trkPos.seg->next;
} else if (pTgtCar->_distFromStartLine < pTgtCar->pub.trkPos.seg->lgfromstart) {
// Previous segment, reverse direction
pTgtCar->pub.trkPos.seg = pTgtCar->pub.trkPos.seg->prev;
}
}
if ((replayForward && s->currentTime < nextReplayData[nCarInd].currentTime) ||
(replayForward == 0 && s->currentTime > nextReplayData[nCarInd].currentTime)) {
// Interpolate position in between records
double timeFrac;
double yaw, roll, pitch;
car = &(SimCarTable[nCarInd]);
pTgtCar = car->carElt;
pSrcCar = &curReplayData[nCarInd];
pSrc2Car = &nextReplayData[nCarInd];
if (replayForward)
timeFrac = (s->currentTime - curReplayData[nCarInd].currentTime) /
(nextReplayData[nCarInd].currentTime - curReplayData[nCarInd].currentTime);
else
timeFrac = (curReplayData[nCarInd].currentTime - s->currentTime) /
(curReplayData[nCarInd].currentTime - nextReplayData[nCarInd].currentTime);
// position on track
pTgtCar->_pos_X = pSrcCar->_pos_X + (pSrc2Car->_pos_X - pSrcCar->_pos_X) * timeFrac;
pTgtCar->_pos_Y = pSrcCar->_pos_Y + (pSrc2Car->_pos_Y - pSrcCar->_pos_Y) * timeFrac;
pTgtCar->_statGC_z = pSrcCar->_statGC_z + (pSrc2Car->_statGC_z - pSrcCar->_statGC_z) * timeFrac;
pTgtCar->_pos_Z = pSrcCar->_pos_Z + (pSrc2Car->_pos_Z - pSrcCar->_pos_Z) * timeFrac - pTgtCar->_statGC_z;
yaw = pSrc2Car->_yaw;
roll = pSrc2Car->_roll;
pitch = pSrc2Car->_pitch;
// assumes that these can't change at high rate
if (yaw < pSrcCar->_yaw - PI)
yaw += 2 * PI;
else if (yaw > pSrcCar->_yaw + PI)
yaw -= 2 * PI;
if (roll < pSrcCar->_roll - PI)
roll += 2 * PI;
else if (roll > pSrcCar->_roll + PI)
roll -= 2 * PI;
if (pitch < pSrcCar->_pitch - PI)
pitch += 2 * PI;
else if (pitch > pSrcCar->_pitch + PI)
pitch -= 2 * PI;
pTgtCar->_yaw = pSrcCar->_yaw + (yaw - pSrcCar->_yaw) * timeFrac;
pTgtCar->_roll = pSrcCar->_roll + (roll - pSrcCar->_roll) * timeFrac;
pTgtCar->_pitch = pSrcCar->_pitch + (pitch - pSrcCar->_pitch) * timeFrac;
sgMakeCoordMat4(pTgtCar->pub.posMat, pTgtCar->_pos_X, pTgtCar->_pos_Y, pTgtCar->_pos_Z,
(tdble) RAD2DEG(pTgtCar->_yaw), (tdble) RAD2DEG(pTgtCar->_roll),
(tdble) RAD2DEG(pTgtCar->_pitch));
// some cameras use speed to determine rain behaviour
pTgtCar->_speed_x = pSrcCar->_speed_x + (pSrc2Car->_speed_x - pSrcCar->_speed_x) * timeFrac;
pTgtCar->_speed_y = pSrcCar->_speed_y + (pSrc2Car->_speed_y - pSrcCar->_speed_y) * timeFrac;
pTgtCar->_speed_z = pSrcCar->_speed_z + (pSrc2Car->_speed_z - pSrcCar->_speed_z) * timeFrac;
pTgtCar->_speed_xy = pSrcCar->_speed_xy + (pSrc2Car->_speed_xy - pSrcCar->_speed_xy) * timeFrac;
pTgtCar->_enginerpm = pSrcCar->_enginerpm + (pSrc2Car->_enginerpm - pSrcCar->_enginerpm) * timeFrac;
// Audio works in world cordinates
pTgtCar->pub.DynGCg.pos.x = pSrcCar->pub.DynGCg.pos.x + (pSrc2Car->pub.DynGCg.pos.x - pSrcCar->pub.DynGCg.pos.x) * timeFrac;
pTgtCar->pub.DynGCg.pos.y = pSrcCar->pub.DynGCg.pos.y + (pSrc2Car->pub.DynGCg.pos.y - pSrcCar->pub.DynGCg.pos.y) * timeFrac;
pTgtCar->pub.DynGCg.pos.z = pSrcCar->pub.DynGCg.pos.z + (pSrc2Car->pub.DynGCg.pos.z - pSrcCar->pub.DynGCg.pos.z) * timeFrac;
pTgtCar->pub.DynGCg.vel.x = pSrcCar->pub.DynGCg.vel.x + (pSrc2Car->pub.DynGCg.vel.x - pSrcCar->pub.DynGCg.vel.x) * timeFrac;
pTgtCar->pub.DynGCg.vel.y = pSrcCar->pub.DynGCg.vel.y + (pSrc2Car->pub.DynGCg.vel.y - pSrcCar->pub.DynGCg.vel.y) * timeFrac;
pTgtCar->pub.DynGCg.vel.z = pSrcCar->pub.DynGCg.vel.z + (pSrc2Car->pub.DynGCg.vel.z - pSrcCar->pub.DynGCg.vel.z) * timeFrac;
// Sound uses accelCmd to antenuate engine
pTgtCar->_accelCmd = pSrcCar->_accelCmd + (pSrc2Car->_accelCmd - pSrcCar->_accelCmd) * timeFrac;
}
}
}
void
SimInit(int nbcars, tTrack* track)
{
tCar *car;
tCarElt *pTgtCar;
char command[200];
int result;
GfLogInfo("Replay SimInit\n");
if (replayDB == NULL) {
GfLogInfo("Replay Database Not Opened!!\n\n\n\n");
return;
}
SimNbCars = nbcars;
SimCarTable = (tCar*)calloc(nbcars, sizeof(tCar));
PTrack = track;
//SimCarCollideInit(PTrack);
for (int nCarInd = 0; nCarInd < nbcars; nCarInd++) {
sprintf(command, "SELECT datablob FROM car%d LIMIT %d", nCarInd, REPLAY_CHUNK);
result = sqlite3_prepare_v2(replayDB, command, -1, &replayBlobs[nCarInd], 0);
if (result) {
GfLogInfo("Unable to prepare car%d: %s\n", nCarInd, sqlite3_errmsg(replayDB));
} else {
replayForward = 1;
lastCurTime = -10.0;
// read the first 2 records for each car
result = sqlite3_step(replayBlobs[nCarInd]);
if (result == SQLITE_ROW) {
memcpy(&curReplayData[nCarInd], sqlite3_column_blob(replayBlobs[nCarInd], 0), sizeof(tReplayElt));
lastCurTime = curReplayData[nCarInd].currentTime;
}
result = sqlite3_step(replayBlobs[nCarInd]);
if (result == SQLITE_ROW) {
memcpy(&nextReplayData[nCarInd], sqlite3_column_blob(replayBlobs[nCarInd], 0), sizeof(tReplayElt));
}
#if 0
// preload info into structure
car = &(SimCarTable[nCarInd]);
pTgtCar = car->carElt;
memcpy(&pTgtCar->info, &curReplayData[nCarInd].info, sizeof(tInitCar));
#endif
}
}
GfLogInfo("SimuReplay recording starts at %f\n", lastCurTime);
}
void
SimShutdown(void)
{
tCar *car;
int ncar;
//SimCarCollideShutdown(SimNbCars);
if (SimCarTable) {
for (ncar = 0; ncar < SimNbCars; ncar++) {
car = &(SimCarTable[ncar]);
//SimEngineShutdown(car);
}
free(SimCarTable);
SimCarTable = 0;
}
PTrack = 0;
}
/* Used for network games to update client physics */
void
UpdateSimCarTable(tDynPt DynGCG,int index)
{
tCar *pCar = SimCarTable;
pCar[index].DynGCg = DynGCG;
}
/* Used for network games get current physics values*/
tDynPt *
GetSimCarTable(int index)
{
tCar *pCar = SimCarTable;
return &pCar[index].DynGCg;
}
void
SimUpdateSingleCar(int index, double deltaTime,tSituation *s)
{
GfLogInfo("Replay UpdateSingleCar\n");
}

View File

@ -0,0 +1,114 @@
/***************************************************************************
file : simureplay.cpp
created : Sun Mar 19 00:08:04 CET 2000
copyright : (C) 2000 by Eric Espie
email : torcs@free.fr
version : $Id: simuv4.cpp 3568 2011-05-15 15:55:24Z pouillot $
***************************************************************************/
/***************************************************************************
* *
* 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. *
* *
***************************************************************************/
#include "simureplay.h"
#include "sim.h"
// The Simureplay: singleton.
SimuReplay* SimuReplay::_pSelf = 0;
int openGfModule(const char* pszShLibName, void* hShLibHandle)
{
// Instanciate the (only) module instance.
SimuReplay::_pSelf = new SimuReplay(pszShLibName, hShLibHandle);
// Register it to the GfModule module manager if OK.
if (SimuReplay::_pSelf)
GfModule::register_(SimuReplay::_pSelf);
// Report about success or error.
return SimuReplay::_pSelf ? 0 : 1;
}
int closeGfModule()
{
// Unregister it from the GfModule module manager.
if (SimuReplay::_pSelf)
GfModule::unregister(SimuReplay::_pSelf);
// Delete the (only) module instance.
delete SimuReplay::_pSelf;
SimuReplay::_pSelf = 0;
// Report about success or error.
return 0;
}
SimuReplay& SimuReplay::self()
{
// Pre-condition : 1 successfull openGfModule call.
return *_pSelf;
}
SimuReplay::SimuReplay(const std::string& strShLibName, void* hShLibHandle)
: GfModule(strShLibName, hShLibHandle)
{
}
SimuReplay::~SimuReplay()
{
}
// Implementation of IPhysicsEngine.
void SimuReplay::initialize(int nCars, struct Track* pTrack)
{
::SimInit(nCars, pTrack);
}
void SimuReplay::configureCar(struct CarElt* pCar)
{
::SimConfig(pCar);
}
void SimuReplay::reconfigureCar(struct CarElt* pCar)
{
::SimReConfig(pCar);
}
void SimuReplay::toggleCarTelemetry(int nCarIndex, bool bOn)
{
::SimCarTelemetry(nCarIndex, bOn);
}
void SimuReplay::updateSituation(struct Situation *pSituation, double fDeltaTime)
{
::SimUpdate(pSituation, fDeltaTime);
}
void SimuReplay::updateCar(struct Situation *pSituation, double fDeltaTime, int nCarIndex)
{
::SimUpdateSingleCar(nCarIndex, fDeltaTime, pSituation);
}
void SimuReplay::setCar(const struct DynPt& dynGCG, int nCarIndex)
{
::UpdateSimCarTable(dynGCG, nCarIndex);
}
tDynPt* SimuReplay::getCar(int nCarIndex)
{
return ::GetSimCarTable(nCarIndex);
}
void SimuReplay::shutdown()
{
::SimShutdown();
}

View File

@ -0,0 +1,86 @@
/***************************************************************************
file : simuv4.h
copyright : (C) 2011 by Jean-Philippe Meuret
(C) 2013 by Wolf-Dieter Beelitz
email : pouillot@users.sourceforge.net
version : $Id: simuv4.h 4903 2012-08-27 11:31:33Z kmetykog $
***************************************************************************/
/***************************************************************************
* *
* 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
The "Simu V4" physics engine module
@version $Id: simuv4.h 4903 2012-08-27 11:31:33Z kmetykog $
*/
#ifndef _SIMUREPLAY_H_
#define _SIMUREPLAY_H_
#include <iphysicsengine.h>
#include <tgf.hpp>
// DLL exported symbols declarator for Windows.
#ifdef WIN32
# ifdef SIMUREPLAY_DLL
# define SIMUREPLAY_API __declspec(dllexport)
# else
# define SIMUREPLAY_API __declspec(dllimport)
# endif
#else
# define SIMUREPLAY_API
#endif
// The C interface of the module.
extern "C" int SIMUREPLAY_API openGfModule(const char* pszShLibName, void* hShLibHandle);
extern "C" int SIMUREPLAY_API closeGfModule();
// The module main class (Singleton, inherits GfModule, and implements IPhysicsEngine).
class SIMUREPLAY_API SimuReplay : public GfModule, public IPhysicsEngine
{
public:
// Implementation of IPhysicsEngine.
virtual void initialize(int nCars, struct Track* pTrack);
virtual void configureCar(struct CarElt* pCar);
virtual void reconfigureCar(struct CarElt* pCar);
virtual void toggleCarTelemetry(int nCarIndex, bool bOn = true);
virtual void updateSituation(struct Situation *pSituation, double fDeltaTime);
virtual void updateCar(struct Situation *pSituation, double fDeltaTime, int nCarIndex);
virtual void setCar(const struct DynPt& dynGCG, int nCarIndex);
virtual struct DynPt* getCar(int nCarIndex);
virtual void shutdown();
// Accessor to the singleton.
static SimuReplay& self();
// Destructor.
virtual ~SimuReplay();
protected:
// Protected constructor to avoid instanciation outside (but friends).
SimuReplay(const std::string& strShLibName, void* hShLibHandle);
// Make the C interface functions nearly member functions.
friend int openGfModule(const char* pszShLibName, void* hShLibHandle);
friend int closeGfModule();
protected:
// The singleton.
static SimuReplay* _pSelf;
};
#endif /* _SIMUREPLAY_H_ */

View File

@ -38,8 +38,8 @@
/* list of available simulation engine */
static const int DefaultSimuVersion = 1;
static const char *SimuVersionList[] =
{RM_VAL_MOD_SIMU_V2, RM_VAL_MOD_SIMU_V2_1, RM_VAL_MOD_SIMU_V3, RM_VAL_MOD_SIMU_V4};
static const char *SimuVersionDispNameList[] = {"V2.0 (old)", "V2.1 (obsolete)", "V3.0 (incomplete)", "V4.0 (default)"};
{RM_VAL_MOD_SIMU_V2, RM_VAL_MOD_SIMU_V2_1, RM_VAL_MOD_SIMU_V3, RM_VAL_MOD_SIMU_V4, RM_VAL_MOD_SIMU_REPLAY};
static const char *SimuVersionDispNameList[] = {"V2.0", "V2.1", "V3.0", "V4.0", "Replay"};
static const int NbSimuVersions = sizeof(SimuVersionList) / sizeof(SimuVersionList[0]);
static int CurSimuVersion = DefaultSimuVersion;
@ -54,10 +54,17 @@ static const int NbThreadAffinitySchemes = sizeof(ThreadAffinitySchemeList) / si
static int CurMultiThreadScheme = 0; // Auto
static int CurThreadAffinityScheme = 0; // On
/* list of available replay record schemes */
static const char *ReplaySchemeList[] = {RM_VAL_REPLAY_OFF, RM_VAL_REPLAY_LOW, RM_VAL_REPLAY_NORMAL, RM_VAL_REPLAY_HIGH, RM_VAL_REPLAY_PERFECT};
static const char *ReplaySchemeDispNameList[] = {"off", "Low", "Normal", "High", "Perfect"};
static const int NbReplaySchemes = sizeof(ReplaySchemeList) / sizeof(ReplaySchemeList[0]);
static int CurReplayScheme = 0;
/* gui label ids */
static int SimuVersionId;
static int MultiThreadSchemeId;
static int ThreadAffinitySchemeId;
static int ReplayRateSchemeId;
/* gui screen handles */
static void *ScrHandle = NULL;
@ -69,6 +76,7 @@ static void loadSimuCfg(void)
const char *simuVersionName;
const char *multiThreadSchemeName;
const char *threadAffinitySchemeName;
const char *replayRateSchemeName;
int i;
char buf[1024];
@ -112,11 +120,28 @@ static void loadSimuCfg(void)
}
}
// Replay Rate
#ifdef THIRD_PARTY_SQLITE3
replayRateSchemeName = GfParmGetStr(paramHandle, RM_SECT_RACE_ENGINE, RM_ATTR_REPLAY_RATE, ReplaySchemeList[0]);
for (i = 0; i < NbReplaySchemes; i++) {
if (strcmp(replayRateSchemeName, ReplaySchemeList[i]) == 0) {
CurReplayScheme = i;
break;
}
}
#else
CurReplayScheme = 0;
#endif
GfParmReleaseHandle(paramHandle);
GfuiLabelSetText(ScrHandle, SimuVersionId, SimuVersionDispNameList[CurSimuVersion]);
GfuiLabelSetText(ScrHandle, MultiThreadSchemeId, MultiThreadSchemeList[CurMultiThreadScheme]);
GfuiLabelSetText(ScrHandle, ThreadAffinitySchemeId, ThreadAffinitySchemeList[CurThreadAffinityScheme]);
GfuiLabelSetText(ScrHandle, ReplayRateSchemeId, ReplaySchemeDispNameList[CurReplayScheme]);
#ifndef THIRD_PARTY_SQLITE3
GfuiEnable(ScrHandle, ReplayRateSchemeId, GFUI_DISABLE);
#endif
}
@ -130,6 +155,7 @@ static void storeSimuCfg(void * /* dummy */)
GfParmSetStr(paramHandle, RM_SECT_MODULES, RM_ATTR_MOD_SIMU, SimuVersionList[CurSimuVersion]);
GfParmSetStr(paramHandle, RM_SECT_RACE_ENGINE, RM_ATTR_MULTI_THREADING, MultiThreadSchemeList[CurMultiThreadScheme]);
GfParmSetStr(paramHandle, RM_SECT_RACE_ENGINE, RM_ATTR_THREAD_AFFINITY, ThreadAffinitySchemeList[CurThreadAffinityScheme]);
GfParmSetStr(paramHandle, RM_SECT_RACE_ENGINE, RM_ATTR_REPLAY_RATE, ReplaySchemeList[CurReplayScheme]);
GfParmWriteFile(NULL, paramHandle, "raceengine");
GfParmReleaseHandle(paramHandle);
@ -182,6 +208,17 @@ onChangeThreadAffinityScheme(void *vp)
}
/* Change the replay rate scheme */
static void
onChangeReplayRateScheme(void *vp)
{
CurReplayScheme =
(CurReplayScheme + NbReplaySchemes + (int)(long)vp) % NbReplaySchemes;
GfuiLabelSetText(ScrHandle, ReplayRateSchemeId, ReplaySchemeDispNameList[CurReplayScheme]);
}
static void onActivate(void * /* dummy */)
{
loadSimuCfg();
@ -217,6 +254,12 @@ SimuMenuInit(void *prevMenu)
GfuiMenuCreateButtonControl(ScrHandle, menuDescHdle, "threadaffleftarrow", (void*)-1, onChangeThreadAffinityScheme);
GfuiMenuCreateButtonControl(ScrHandle, menuDescHdle, "threadaffrightarrow", (void*)1, onChangeThreadAffinityScheme);
ReplayRateSchemeId = GfuiMenuCreateLabelControl(ScrHandle, menuDescHdle, "replayratelabel");
#ifdef THIRD_PARTY_SQLITE3
GfuiMenuCreateButtonControl(ScrHandle, menuDescHdle, "replayrateleftarrow", (void*)-1, onChangeReplayRateScheme);
GfuiMenuCreateButtonControl(ScrHandle, menuDescHdle, "replayraterightarrow", (void*)1, onChangeReplayRateScheme);
#endif
GfuiMenuCreateButtonControl(ScrHandle, menuDescHdle, "ApplyButton", PrevScrHandle, storeSimuCfg);
GfuiMenuCreateButtonControl(ScrHandle, menuDescHdle, "CancelButton", PrevScrHandle, GfuiScreenActivate);

View File

@ -37,12 +37,19 @@
static int rmSaveButtonId;
static int rmReplayButtonId;
static void *rmScrHdle = NULL;
static void rmPracticeResults(void *prevHdle, tRmInfo *info, int start);
static void rmRaceResults(void *prevHdle, tRmInfo *info, int start);
static void rmQualifResults(void *prevHdle, tRmInfo *info, const char*pszTitle, int start);
static const int DefaultSimuVersion = 1;
static const char *SimuVersionList[] =
{RM_VAL_MOD_SIMU_V2, RM_VAL_MOD_SIMU_V2_1, RM_VAL_MOD_SIMU_V3, RM_VAL_MOD_SIMU_V4, RM_VAL_MOD_SIMU_REPLAY};
static const int NbSimuVersions = sizeof(SimuVersionList) / sizeof(SimuVersionList[0]);
static int CurSimuVersion = DefaultSimuVersion;
typedef struct
{
void *prevHdle;
@ -55,6 +62,37 @@ tRaceCall RmNextRace;
tRaceCall RmPrevRace;
static void
rmReplayRace(void * /* dummy */)
{
const char *simuVersionName;
int i;
char buf[1024];
snprintf(buf, sizeof(buf), "%s%s", GfLocalDir(), RACE_ENG_CFG);
void *paramHandle = GfParmReadFile(buf, GFPARM_RMODE_REREAD | GFPARM_RMODE_CREAT);
// Temporarily overwrite Simulation Type
CurSimuVersion = DefaultSimuVersion;
simuVersionName = GfParmGetStr(paramHandle, RM_SECT_MODULES, RM_ATTR_MOD_SIMU, SimuVersionList[DefaultSimuVersion]);
for (i = 0; i < NbSimuVersions; i++) {
if (strcmp(simuVersionName, SimuVersionList[i]) == 0) {
CurSimuVersion = i;
break;
}
}
GfParmSetStr(paramHandle, RM_SECT_MODULES, RM_ATTR_MOD_SIMU, SimuVersionList[4]);
GfParmWriteFile(NULL, paramHandle, "raceengine");
LmRaceEngine().startNewRace();
// Restore original Simulation type
GfParmSetStr(paramHandle, RM_SECT_MODULES, RM_ATTR_MOD_SIMU, SimuVersionList[CurSimuVersion]);
GfParmWriteFile(NULL, paramHandle, "raceengine");
GfParmReleaseHandle(paramHandle);
}
static void
rmSaveRes(void *vInfo)
{
@ -89,6 +127,8 @@ rmPracticeResults(void *prevHdle, tRmInfo *info, int start)
static char path[1024];
char *str;
int damages;
void *paramHandle;
const char *replayRateSchemeName;
// Create screen, load menu XML descriptor and create static controls.
rmScrHdle = GfuiScreenCreate();
@ -182,6 +222,16 @@ rmPracticeResults(void *prevHdle, tRmInfo *info, int start)
// Add "Continue" button
GfuiMenuCreateButtonControl(rmScrHdle, hmenu, "ContinueButton", prevHdle, GfuiScreenReplace);
// Add "Replay" button (if available)
snprintf(buf, sizeof(buf), "%s%s", GfLocalDir(), RACE_ENG_CFG);
paramHandle = GfParmReadFile(buf, GFPARM_RMODE_REREAD | GFPARM_RMODE_CREAT);
replayRateSchemeName = GfParmGetStr(paramHandle, RM_SECT_RACE_ENGINE, RM_ATTR_REPLAY_RATE, RM_VAL_REPLAY_OFF);
rmReplayButtonId = GfuiMenuCreateButtonControl(rmScrHdle, hmenu, "ReplayButton", prevHdle, rmReplayRace);
if (strcmp(replayRateSchemeName, RM_VAL_REPLAY_OFF) == 0)
GfuiEnable(rmScrHdle, rmReplayButtonId, GFUI_DISABLE);
GfParmReleaseHandle(paramHandle);
//Create 'save' button in the bottom right
//rmSaveButtonId = GfuiMenuCreateButtonControl(rmScrHdle, hmenu, "SaveButton", info, rmSaveRes);
@ -221,6 +271,8 @@ rmRaceResults(void *prevHdle, tRmInfo *info, int start)
static char buf[256];
static char path[512];
char *str;
void *paramHandle;
const char *replayRateSchemeName;
GfLogTrace("Entering Race Results menu\n");
@ -337,7 +389,17 @@ rmRaceResults(void *prevHdle, tRmInfo *info, int start)
// Add "Continue" button
GfuiMenuCreateButtonControl(rmScrHdle, hmenu, "ContinueButton", prevHdle, GfuiScreenReplace);
// Add "Replay" button (if available)
snprintf(buf, sizeof(buf), "%s%s", GfLocalDir(), RACE_ENG_CFG);
paramHandle = GfParmReadFile(buf, GFPARM_RMODE_REREAD | GFPARM_RMODE_CREAT);
replayRateSchemeName = GfParmGetStr(paramHandle, RM_SECT_RACE_ENGINE, RM_ATTR_REPLAY_RATE, RM_VAL_REPLAY_OFF);
rmReplayButtonId = GfuiMenuCreateButtonControl(rmScrHdle, hmenu, "ReplayButton", prevHdle, rmReplayRace);
if (strcmp(replayRateSchemeName, RM_VAL_REPLAY_OFF) == 0)
GfuiEnable(rmScrHdle, rmReplayButtonId, GFUI_DISABLE);
GfParmReleaseHandle(paramHandle);
//Create 'save' button in the bottom right
//rmSaveButtonId = GfuiMenuCreateButtonControl(rmScrHdle, hmenu, "SaveButton", info, rmSaveRes);