BVB Source Codes

CRYENGINE Show AIRecorder.cpp Source code

Return Download CRYENGINE: download AIRecorder.cpp Source code - Download CRYENGINE Source code - Type:.cpp
  1. // Copyright 2001-2016 Crytek GmbH / Crytek Group. All rights reserved.
  2.  
  3. /********************************************************************
  4.    -------------------------------------------------------------------------
  5.    File name:   AIRecorder.cpp
  6.    Description: Detailed event-based AI recorder for visual debugging
  7.  
  8.    -------------------------------------------------------------------------
  9.    History:
  10.    -01:07:2005 : Created by Kirill Bulatsev
  11.    -19:11:2008 : Separated from simple text CAIRecorder by Matthew
  12.  
  13.    Notes:        Don't include the header in CAISystem - below it is redundant
  14.  
  15.  *********************************************************************/
  16.  
  17. #include "StdAfx.h"
  18. #include "AIRecorder.h"
  19. #include "PipeUser.h"
  20. #include <CryString/StringUtils.h>
  21. #include <CryGame/IGameFramework.h>
  22.  
  23. #ifdef CRYAISYSTEM_DEBUG
  24.  
  25. // New AI recorder
  26.         #define AIRECORDER_FOLDER             "Recordings"
  27.         #define AIRECORDER_DEFAULT_FILE       "AIRECORD"
  28.         #define AIRECORDER_EDITOR_AUTO_APPEND "EDITORAUTO"
  29.         #define AIRECORDER_VERSION            4
  30.  
  31. // We use some magic numbers to help when debugging the save structure
  32.         #define RECORDER_MAGIC 0xAA1234BB
  33.         #define UNIT_MAGIC     0xCC9876DD
  34.         #define EVENT_MAGIC    0xEE0921FF
  35.  
  36. // Anything larger than this is assumed to be a corrupt file
  37.         #define MAX_NAME_LENGTH         1024
  38.         #define MAX_UNITS               10024
  39.         #define MAX_EVENT_STRING_LENGTH 1024
  40.  
  41. FILE* CAIRecorder::m_pFile = NULL;  // Hack!
  42.  
  43. struct RecorderFileHeader
  44. {
  45.         int version;
  46.         int unitsNumber;
  47.         int magic;
  48.  
  49.         RecorderFileHeader()
  50.                 : version(AIRECORDER_VERSION)
  51.                 , unitsNumber(0)
  52.                 , magic(RECORDER_MAGIC)
  53.         {}
  54.  
  55.         bool check(void) { return RECORDER_MAGIC == magic && unitsNumber < MAX_UNITS; }
  56.  
  57. };
  58.  
  59. struct UnitHeader
  60. {
  61.         int               nameLength;
  62.         TAIRecorderUnitId ID;
  63.         int               magic;
  64.         // And the variable length name follows this
  65.         UnitHeader()
  66.                 : nameLength(0)
  67.                 , ID(0)
  68.                 , magic(UNIT_MAGIC)
  69.         {}
  70.  
  71.         bool check(void) { return (UNIT_MAGIC == magic && nameLength < MAX_NAME_LENGTH); }
  72. };
  73.  
  74. struct StreamedEventHeader
  75. {
  76.         TAIRecorderUnitId unitID;
  77.         int               streamID;
  78.         int               magic;
  79.  
  80.         StreamedEventHeader()
  81.                 : unitID(0)
  82.                 , streamID(0)
  83.                 , magic(EVENT_MAGIC)
  84.         {}
  85.  
  86.         bool check(void) { return EVENT_MAGIC == magic; }
  87.  
  88. };
  89.  
  90. //----------------------------------------------------------------------------------------------
  91. void CRecorderUnit::RecordEvent(IAIRecordable::e_AIDbgEvent event, const IAIRecordable::RecorderEventData* pEventData)
  92. {
  93.         if (!m_pRecorder || !m_pRecorder->IsRunning()) return;
  94.  
  95.         float time = (GetAISystem()->GetFrameStartTime() - m_startTime).GetSeconds();
  96.  
  97.         if (CAIRecorder::m_pFile) //(Hack!)
  98.         {
  99.                 bool bSuccess;
  100.                 StreamedEventHeader header;
  101.                 header.unitID = m_id;
  102.                 header.streamID = event;
  103.                 bSuccess = (fwrite(&header, sizeof(header), 1, CAIRecorder::m_pFile) == 1);
  104.                 // We go through the stream to get the right virtual function
  105.  
  106.                 bSuccess &= m_Streams[event]->WriteValue(pEventData, time);
  107.                 if (!bSuccess)
  108.                 {
  109.                         gEnv->pLog->LogError("[AI Recorder] Failed to write stream event");
  110.                 }
  111.         }
  112.         else
  113.         {
  114.                 m_Streams[event]->AddValue(pEventData, time);
  115.         }
  116. }
  117.  
  118. //----------------------------------------------------------------------------------------------
  119. bool CRecorderUnit::LoadEvent(IAIRecordable::e_AIDbgEvent stream)
  120. {
  121.         if (CAIRecorder::m_pFile) //(Hack!)
  122.                 return m_Streams[stream]->LoadValue(CAIRecorder::m_pFile);
  123.         return true;
  124. }
  125.  
  126. //
  127. //----------------------------------------------------------------------------------------------
  128. CRecordable::CRecordable()
  129.         : m_pMyRecord(NULL)
  130. {
  131.  
  132. }
  133.  
  134. //
  135. //----------------------------------------------------------------------------------------------
  136. CRecorderUnit* CRecordable::GetOrCreateRecorderUnit(CAIObject* pAIObject, bool bForce)
  137. {
  138.         if (!m_pMyRecord && pAIObject && !gEnv->pSystem->IsSerializingFile())
  139.         {
  140.                 m_pMyRecord = gAIEnv.GetAIRecorder()->AddUnit(pAIObject->GetSelfReference(), bForce);
  141.         }
  142.  
  143.         return m_pMyRecord;
  144. }
  145.  
  146. //
  147. //----------------------------------------------------------------------------------------------
  148. CRecorderUnit::CRecorderUnit(CAIRecorder* pRecorder, CTimeValue startTime, CWeakRef<CAIObject> refUnit, uint32 lifeIndex)
  149.         : m_startTime(startTime)
  150.         , m_pRecorder(pRecorder)
  151.         , m_id(lifeIndex, refUnit.GetObjectID())
  152. {
  153.         CRY_ASSERT(refUnit.IsSet());
  154.         CRY_ASSERT(m_pRecorder);
  155.  
  156.         // Get the name of the unit object
  157.         if (refUnit.IsSet())
  158.         {
  159.                 m_sName = refUnit.GetAIObject()->GetName();
  160.         }
  161.         else
  162.         {
  163.                 m_sName.Format("UNKNOWN_UNIT_%u", (tAIObjectID)m_id);
  164.         }
  165.  
  166.         m_Streams[IAIRecordable::E_RESET] = new CRecorderUnit::StreamStr("Reset");
  167.         m_Streams[IAIRecordable::E_SIGNALRECIEVED] = new CRecorderUnit::StreamStr("Signal Received", true);
  168.         m_Streams[IAIRecordable::E_SIGNALRECIEVEDAUX] = new CRecorderUnit::StreamStr("Auxilary Signal Received", true);
  169.         m_Streams[IAIRecordable::E_SIGNALEXECUTING] = new CRecorderUnit::StreamStr("Signal Executing", true);
  170.         m_Streams[IAIRecordable::E_GOALPIPESELECTED] = new CRecorderUnit::StreamStr("Goalpipe Selected", true);
  171.         m_Streams[IAIRecordable::E_GOALPIPEINSERTED] = new CRecorderUnit::StreamStr("Goalpipe Inserted", true);
  172.         m_Streams[IAIRecordable::E_GOALPIPERESETED] = new CRecorderUnit::StreamStr("Goalpipe Reset", true);
  173.         m_Streams[IAIRecordable::E_BEHAVIORSELECTED] = new CRecorderUnit::StreamStr("Behavior Selected", true);
  174.         m_Streams[IAIRecordable::E_BEHAVIORDESTRUCTOR] = new CRecorderUnit::StreamStr("Behavior Destructor", true);
  175.         m_Streams[IAIRecordable::E_BEHAVIORCONSTRUCTOR] = new CRecorderUnit::StreamStr("Behavior Constructor", true);
  176.         m_Streams[IAIRecordable::E_ATTENTIONTARGET] = new CRecorderUnit::StreamStr("AttTarget", true);
  177.         m_Streams[IAIRecordable::E_ATTENTIONTARGETPOS] = new CRecorderUnit::StreamVec3("AttTarget Pos", true);
  178.         m_Streams[IAIRecordable::E_REGISTERSTIMULUS] = new CRecorderUnit::StreamStr("Stimulus", true);
  179.         m_Streams[IAIRecordable::E_HANDLERNEVENT] = new CRecorderUnit::StreamStr("Handler Event", true);
  180.         m_Streams[IAIRecordable::E_ACTIONSUSPEND] = new CRecorderUnit::StreamStr("Action Suspend", true);
  181.         m_Streams[IAIRecordable::E_ACTIONRESUME] = new CRecorderUnit::StreamStr("Action Resume", true);
  182.         m_Streams[IAIRecordable::E_ACTIONEND] = new CRecorderUnit::StreamStr("Action End", true);
  183.         m_Streams[IAIRecordable::E_EVENT] = new CRecorderUnit::StreamStr("Event", true);
  184.         m_Streams[IAIRecordable::E_REFPOINTPOS] = new CRecorderUnit::StreamVec3("Ref Point", true);
  185.         m_Streams[IAIRecordable::E_AGENTPOS] = new CRecorderUnit::StreamVec3("Agent Pos", true);
  186.         // (Kevin) Direction should be able to be filtered? But needs a lower threshold. So instead of bool, maybe supply threshold value? (10/08/2009)
  187.         m_Streams[IAIRecordable::E_AGENTDIR] = new CRecorderUnit::StreamVec3("Agent Dir", false);
  188.         m_Streams[IAIRecordable::E_LUACOMMENT] = new CRecorderUnit::StreamStr("Lua Comment", true);
  189.         m_Streams[IAIRecordable::E_PERSONALLOG] = new CRecorderUnit::StreamStr("Personal Log", true);
  190.         m_Streams[IAIRecordable::E_HEALTH] = new CRecorderUnit::StreamFloat("Health", true);
  191.         m_Streams[IAIRecordable::E_HIT_DAMAGE] = new CRecorderUnit::StreamStr("Hit Damage", false);
  192.         m_Streams[IAIRecordable::E_DEATH] = new CRecorderUnit::StreamStr("Death", true);
  193.         m_Streams[IAIRecordable::E_PRESSUREGRAPH] = new CRecorderUnit::StreamFloat("Pressure", true);
  194.         m_Streams[IAIRecordable::E_BOOKMARK] = new CRecorderUnit::StreamFloat("Bookmark", false);
  195. }
  196.  
  197. //
  198. //----------------------------------------------------------------------------------------------
  199. CRecorderUnit::~CRecorderUnit()
  200. {
  201.         for (TStreamMap::iterator strItr(m_Streams.begin()); strItr != m_Streams.end(); ++strItr)
  202.                 delete strItr->second;
  203.         m_Streams.clear();
  204. }
  205.  
  206. //
  207. //----------------------------------------------------------------------------------------------
  208. void CRecorderUnit::StreamBase::Seek(float whereTo)
  209. {
  210.         m_CurIdx = 0;
  211.  
  212.         if (whereTo > FLT_EPSILON)
  213.         {
  214.                 const int iMaxIndex = max((int)m_Stream.size() - 1, 0);
  215.                 while (m_CurIdx < iMaxIndex && m_Stream[m_CurIdx]->m_StartTime <= whereTo)
  216.                 {
  217.                         ++m_CurIdx;
  218.                 }
  219.                 m_CurIdx = max(m_CurIdx - 1, 0);
  220.         }
  221. }
  222.  
  223. //
  224. //----------------------------------------------------------------------------------------------
  225. int CRecorderUnit::StreamBase::GetCurrentIdx()
  226. {
  227.         return m_CurIdx;
  228. }
  229.  
  230. //
  231. //----------------------------------------------------------------------------------------------
  232. int CRecorderUnit::StreamBase::GetSize()
  233. {
  234.         return (int)(m_Stream.size());
  235. }
  236.  
  237. //
  238. //----------------------------------------------------------------------------------------------
  239. void CRecorderUnit::StreamBase::ClearImpl()
  240. {
  241.         for (size_t i = 0; i < m_Stream.size(); ++i)
  242.                 delete m_Stream[i];
  243.         m_Stream.clear();
  244. }
  245.  
  246. //
  247. //----------------------------------------------------------------------------------------------
  248. float CRecorderUnit::StreamBase::GetStartTime()
  249. {
  250.         if (m_Stream.empty())
  251.                 return 0.0f;
  252.         return m_Stream.front()->m_StartTime;
  253. }
  254.  
  255. //
  256. //----------------------------------------------------------------------------------------------
  257. float CRecorderUnit::StreamBase::GetEndTime()
  258. {
  259.         if (m_Stream.empty())
  260.                 return 0.0f;
  261.         return m_Stream.back()->m_StartTime;
  262. }
  263.  
  264. //
  265. //----------------------------------------------------------------------------------------------
  266. CRecorderUnit::StreamStr::StreamStr(char const* name, bool bUseIndex /*= false*/)
  267.         : StreamBase(name)
  268.         , m_bUseIndex(bUseIndex)
  269.         , m_uIndexGen(INVALID_INDEX)
  270. {
  271.         if (bUseIndex)
  272.         {
  273.                 // Index not supported in disk mode currently
  274.         #if !defined(_RELEASE) && !defined(CONSOLE_CONST_CVAR_MODE)
  275.                 if (gAIEnv.CVars.Recorder == eAIRM_Disk)
  276.                 {
  277.                         gEnv->pLog->LogWarning("[AI Recorder] %s is set to use String Index mode but this is not supported in Disk mode yet!", name);
  278.                         m_bUseIndex = false;
  279.                 }
  280.         #endif
  281.         }
  282. }
  283.  
  284. //
  285. //----------------------------------------------------------------------------------------------
  286. void* CRecorderUnit::StreamStr::GetCurrent(float& startingFrom)
  287. {
  288.         if (m_CurIdx < 0 || m_CurIdx >= (int)m_Stream.size())
  289.                 return NULL;
  290.         startingFrom = m_Stream[m_CurIdx]->m_StartTime;
  291.         return (void*)(static_cast<StreamUnit*>(m_Stream[m_CurIdx])->m_String.c_str());
  292. }
  293.  
  294. //
  295. //----------------------------------------------------------------------------------------------
  296. bool CRecorderUnit::StreamStr::GetCurrentString(string& sOut, float& startingFrom)
  297. {
  298.         sOut = (char*)GetCurrent(startingFrom);
  299.         return (!sOut.empty());
  300. }
  301.  
  302. //
  303. //----------------------------------------------------------------------------------------------
  304. void* CRecorderUnit::StreamStr::GetNext(float& startingFrom)
  305. {
  306.         if (++m_CurIdx >= (int)m_Stream.size())
  307.         {
  308.                 m_CurIdx = (int)m_Stream.size();
  309.                 return NULL;
  310.         }
  311.         startingFrom = m_Stream[m_CurIdx]->m_StartTime;
  312.         return (void*)(static_cast<StreamUnit*>(m_Stream[m_CurIdx])->m_String.c_str());
  313. }
  314.  
  315. //
  316. //----------------------------------------------------------------------------------------------
  317. bool CRecorderUnit::StreamStr::LoadStringIndex(FILE* pFile)
  318. {
  319.         // Read number of indexes made
  320.         if (!fread(&m_uIndexGen, sizeof(m_uIndexGen), 1, pFile)) return false;
  321.  
  322.         for (uint32 uCount = 0; uCount < m_uIndexGen; ++uCount)
  323.         {
  324.                 uint32 uIndex = INVALID_INDEX;
  325.                 if (!fread(&uIndex, sizeof(uIndex), 1, pFile)) return false;
  326.  
  327.                 int strLen;
  328.                 if (!fread(&strLen, sizeof(strLen), 1, pFile)) return false;
  329.  
  330.                 if (strLen < 0 || strLen > MAX_EVENT_STRING_LENGTH) return false;
  331.  
  332.                 string sString;
  333.                 if (strLen != 0)
  334.                 {
  335.                         std::vector<char> buffer;
  336.                         buffer.resize(strLen);
  337.                         if (fread(&buffer[0], sizeof(char), strLen, pFile) != strLen) return false;
  338.                         sString.assign(&buffer[0], strLen);
  339.                 }
  340.                 else
  341.                 {
  342.                         sString = "<Empty string>";
  343.                 }
  344.  
  345.                 // Add to map
  346.                 m_StrIndexLookup.insert(TStrIndexLookup::value_type(sString.c_str(), uIndex));
  347.         }
  348.  
  349.         return true;
  350. }
  351.  
  352. //
  353. //----------------------------------------------------------------------------------------------
  354. bool CRecorderUnit::StreamStr::SaveStringIndex(FILE* pFile)
  355. {
  356.         // Write number of indexes made
  357.         if (!fwrite(&m_uIndexGen, sizeof(m_uIndexGen), 1, pFile)) return false;
  358.  
  359.         TStrIndexLookup::const_iterator itIter = m_StrIndexLookup.begin();
  360.         TStrIndexLookup::const_iterator itIterEnd = m_StrIndexLookup.end();
  361.         while (itIter != itIterEnd)
  362.         {
  363.                 const string& str(itIter->first);
  364.                 const uint32& uIndex(itIter->second);
  365.                 const int strLen = (str ? str.size() : 0);
  366.  
  367.                 if (!fwrite(&uIndex, sizeof(uIndex), 1, pFile)) return false;
  368.                 if (!fwrite(&strLen, sizeof(strLen), 1, pFile)) return false;
  369.                 if (!str.empty())
  370.                 {
  371.                         if (fwrite(str.c_str(), sizeof(char), strLen, pFile) != strLen) return false;
  372.                 }
  373.  
  374.                 ++itIter;
  375.         }
  376.  
  377.         return true;
  378. }
  379.  
  380. //
  381. //----------------------------------------------------------------------------------------------
  382. uint32 CRecorderUnit::StreamStr::GetOrMakeStringIndex(const char* szString)
  383. {
  384.         uint32 uResult = INVALID_INDEX;
  385.  
  386.         if (m_bUseIndex && szString)
  387.         {
  388.                 uResult = stl::find_in_map(m_StrIndexLookup, szString, INVALID_INDEX);
  389.                 if (INVALID_INDEX == uResult)
  390.                 {
  391.                         // Add it and make new one
  392.                         uResult = ++m_uIndexGen;
  393.                         m_StrIndexLookup.insert(TStrIndexLookup::value_type(szString, uResult));
  394.                 }
  395.         }
  396.  
  397.         return uResult;
  398. }
  399.  
  400. //
  401. //----------------------------------------------------------------------------------------------
  402. bool CRecorderUnit::StreamStr::GetStringFromIndex(uint32 uIndex, string& sOut) const
  403. {
  404.         CRY_ASSERT(m_bUseIndex);
  405.  
  406.         sOut.clear();
  407.  
  408.         bool bResult = false;
  409.         TStrIndexLookup::const_iterator itIter = m_StrIndexLookup.begin();
  410.         TStrIndexLookup::const_iterator itIterEnd = m_StrIndexLookup.end();
  411.         while (itIter != itIterEnd)
  412.         {
  413.                 if (itIter->second == uIndex)
  414.                 {
  415.                         sOut = itIter->first;
  416.                         bResult = true;
  417.                         break;
  418.                 }
  419.                 ++itIter;
  420.         }
  421.  
  422.         return bResult;
  423. }
  424.  
  425. //
  426. //----------------------------------------------------------------------------------------------
  427. void CRecorderUnit::StreamStr::AddValue(const IAIRecordable::RecorderEventData* pEventData, float t)
  428. {
  429.         // Make an index for it, but don't record it. It'll be saved to file later with the right index
  430.         if (m_bUseIndex)
  431.         {
  432.                 GetOrMakeStringIndex(pEventData->pString);
  433.         }
  434.  
  435.         m_Stream.push_back(new StreamUnit(t, pEventData->pString));
  436. }
  437.  
  438. //
  439. //----------------------------------------------------------------------------------------------
  440. bool CRecorderUnit::StreamStr::WriteValue(const IAIRecordable::RecorderEventData* pEventData, float t)
  441. {
  442.         return WriteValue(t, pEventData->pString, CAIRecorder::m_pFile);
  443. }
  444.  
  445. //
  446. //----------------------------------------------------------------------------------------------
  447. bool CRecorderUnit::StreamStr::WriteValue(float t, const char* str, FILE* pFile)
  448. {
  449.         // When using index, we have the string length be all bits on to represent this fact
  450.         int strLen = ~0;
  451.         if (!m_bUseIndex)
  452.         {
  453.                 strLen = (str ? strlen(str) : 0);
  454.         }
  455.  
  456.         if (!fwrite(&t, sizeof(t), 1, pFile)) return false;
  457.         if (!fwrite(&strLen, sizeof(strLen), 1, pFile)) return false;
  458.         if (m_bUseIndex)
  459.         {
  460.                 uint32 uIndex = GetOrMakeStringIndex(str);
  461.                 if (uIndex > INVALID_INDEX)
  462.                 {
  463.                         if (!fwrite(&uIndex, sizeof(uint32), 1, pFile)) return false;
  464.                 }
  465.         }
  466.         else if (str)
  467.         {
  468.                 if (fwrite(str, sizeof(char), strLen, pFile) != strLen) return false;
  469.         }
  470.  
  471.         return true;
  472. }
  473.  
  474. //----------------------------------------------------------------------------------------------
  475. bool CRecorderUnit::StreamStr::LoadValue(FILE* pFile)
  476. {
  477.         string name;
  478.         float time;
  479.         if (!LoadValue(time, name, pFile)) return false;
  480.  
  481.         // Check chronological ordering
  482.         if (!m_Stream.empty() && m_Stream.back()->m_StartTime > time)
  483.         {
  484.                 gEnv->pLog->LogError("[AI Recorder] Aborting - events are not recorded in chronological order");
  485.                 return false;
  486.         }
  487.  
  488.         m_Stream.push_back(new StreamUnit(time, name.c_str()));
  489.         return true;
  490. }
  491.  
  492. //
  493. //----------------------------------------------------------------------------------------------
  494. void CRecorderUnit::StreamStr::Clear()
  495. {
  496.         CRecorderUnit::StreamBase::Clear();
  497.  
  498.         m_StrIndexLookup.clear();
  499.         m_uIndexGen = INVALID_INDEX;
  500. }
  501.  
  502. //
  503. //----------------------------------------------------------------------------------------------
  504. bool CRecorderUnit::StreamStr::SaveStream(FILE* pFile)
  505. {
  506.         int counter(m_Stream.size());
  507.         if (!fwrite(&counter, sizeof(counter), 1, pFile)) return false;
  508.         for (uint32 idx(0); idx < m_Stream.size(); ++idx)
  509.         {
  510.                 StreamUnit* pCurUnit = static_cast<StreamUnit*>(m_Stream[idx]);
  511.                 float t = pCurUnit->m_StartTime;
  512.                 const char* pStr = pCurUnit->m_String.c_str();
  513.                 if (!WriteValue(t, pStr, pFile)) return false;
  514.         }
  515.         return true;
  516. }
  517.  
  518. bool CRecorderUnit::StreamStr::LoadValue(float& t, string& name, FILE* pFile)
  519. {
  520.         int strLen;
  521.         if (!fread(&t, sizeof(t), 1, pFile)) return false;
  522.         if (!fread(&strLen, sizeof(strLen), 1, pFile)) return false;
  523.  
  524.         // When using index, we have the string length be all bits on to represent this fact
  525.         bool bIsEmpty = true;
  526.         if (strLen == ~0)
  527.         {
  528.                 uint32 uIndex = INVALID_INDEX;
  529.                 if (!fread(&uIndex, sizeof(uint32), 1, pFile)) return false;
  530.                 bIsEmpty = (!GetStringFromIndex(uIndex, name));
  531.         }
  532.         else
  533.         {
  534.                 if (strLen < 0 || strLen > MAX_EVENT_STRING_LENGTH) return false;
  535.                 if (strLen != 0)
  536.                 {
  537.                         std::vector<char> buffer;
  538.                         buffer.resize(strLen);
  539.                         if (fread(&buffer[0], sizeof(char), strLen, pFile) != strLen) return false;
  540.                         name.assign(&buffer[0], strLen);
  541.                         bIsEmpty = false;
  542.                 }
  543.         }
  544.  
  545.         if (bIsEmpty)
  546.         {
  547.                 name = "<Empty string>";
  548.         }
  549.  
  550.         return true;
  551. }
  552.  
  553. //
  554. //----------------------------------------------------------------------------------------------
  555. bool CRecorderUnit::StreamStr::LoadStream(FILE* pFile)
  556. {
  557.         string name;
  558.         int counter;
  559.         fread(&counter, sizeof(counter), 1, pFile);
  560.  
  561.         for (int idx(0); idx < counter; ++idx)
  562.                 if (!LoadValue(pFile)) return false;
  563.  
  564.         return true;
  565. }
  566.  
  567. //
  568. //----------------------------------------------------------------------------------------------
  569. void* CRecorderUnit::StreamVec3::GetCurrent(float& startingFrom)
  570. {
  571.         if (m_CurIdx < 0 || m_CurIdx >= (int)m_Stream.size())
  572.                 return NULL;
  573.         startingFrom = m_Stream[m_CurIdx]->m_StartTime;
  574.         return (void*)(&static_cast<StreamUnit*>(m_Stream[m_CurIdx])->m_Pos);
  575. }
  576.  
  577. //
  578. //----------------------------------------------------------------------------------------------
  579. bool CRecorderUnit::StreamVec3::GetCurrentString(string& sOut, float& startingFrom)
  580. {
  581.         sOut.clear();
  582.  
  583.         Vec3* pVec = (Vec3*)GetCurrent(startingFrom);
  584.         if (pVec)
  585.         {
  586.                 const Vec3& v(*pVec);
  587.                 sOut = CryStringUtils::toString(v);
  588.         }
  589.  
  590.         return (!sOut.empty());
  591. }
  592.  
  593. //
  594. //----------------------------------------------------------------------------------------------
  595. void* CRecorderUnit::StreamVec3::GetNext(float& startingFrom)
  596. {
  597.         if (++m_CurIdx >= (int)m_Stream.size())
  598.         {
  599.                 m_CurIdx = m_Stream.size();
  600.                 return NULL;
  601.         }
  602.         startingFrom = m_Stream[m_CurIdx]->m_StartTime;
  603.         return (void*)(&static_cast<StreamUnit*>(m_Stream[m_CurIdx])->m_Pos);
  604. }
  605.  
  606. //
  607. //----------------------------------------------------------------------------------------------
  608. bool CRecorderUnit::StreamVec3::FilterPoint(const IAIRecordable::RecorderEventData* pEventData) const
  609. {
  610.         bool bResult = true;
  611.  
  612.         // Simple point filtering.
  613.         if (m_bUseFilter && !m_Stream.empty())
  614.         {
  615.                 StreamUnit* pLastUnit(static_cast<StreamUnit*>(m_Stream.back()));
  616.                 const Vec3 vDelta = pLastUnit->m_Pos - pEventData->pos;
  617.                 bResult = (vDelta.len2() > 0.25f);
  618.         }
  619.  
  620.         return bResult;
  621. }
  622.  
  623. //
  624. //----------------------------------------------------------------------------------------------
  625. void CRecorderUnit::StreamVec3::AddValue(const IAIRecordable::RecorderEventData* pEventData, float t)
  626. {
  627.         if (FilterPoint(pEventData))
  628.         {
  629.                 m_Stream.push_back(new StreamUnit(t, pEventData->pos));
  630.         }
  631. }
  632.  
  633. //
  634. //----------------------------------------------------------------------------------------------
  635. bool CRecorderUnit::StreamVec3::WriteValue(const IAIRecordable::RecorderEventData* pEventData, float t)
  636. {
  637.         bool bResult = true;
  638.  
  639.         if (FilterPoint(pEventData))
  640.         {
  641.                 bResult = WriteValue(t, pEventData->pos, CAIRecorder::m_pFile);
  642.         }
  643.  
  644.         return bResult;
  645. }
  646.  
  647. //
  648. //----------------------------------------------------------------------------------------------
  649. bool CRecorderUnit::StreamVec3::WriteValue(float t, const Vec3& vec, FILE* pFile)
  650. {
  651.         if (!fwrite(&t, sizeof(t), 1, pFile)) return false;
  652.         if (!fwrite(&vec.x, sizeof(vec.x), 1, pFile)) return false;
  653.         if (!fwrite(&vec.y, sizeof(vec.y), 1, pFile)) return false;
  654.         if (!fwrite(&vec.z, sizeof(vec.z), 1, pFile)) return false;
  655.         return true;
  656. }
  657.  
  658. //
  659. //----------------------------------------------------------------------------------------------
  660. bool CRecorderUnit::StreamVec3::LoadValue(float& t, Vec3& vec, FILE* pFile)
  661. {
  662.         if (!fread(&t, sizeof(t), 1, pFile)) return false;
  663.         if (!fread(&vec.x, sizeof(vec.x), 1, pFile)) return false;
  664.         if (!fread(&vec.y, sizeof(vec.y), 1, pFile)) return false;
  665.         if (!fread(&vec.z, sizeof(vec.z), 1, pFile)) return false;
  666.         return true;
  667. }
  668.  
  669. //----------------------------------------------------------------------------------------------
  670. bool CRecorderUnit::StreamVec3::LoadValue(FILE* pFile)
  671. {
  672.         Vec3 vec;
  673.         float time;
  674.         if (!LoadValue(time, vec, pFile)) return false;
  675.  
  676.         // Check chronological ordering
  677.         if (!m_Stream.empty() && m_Stream.back()->m_StartTime > time)
  678.         {
  679.                 gEnv->pLog->LogError("[AI Recorder] Aborting - events are not recorded in chronological order");
  680.                 return false;
  681.         }
  682.  
  683.         m_Stream.push_back(new StreamUnit(time, vec));
  684.         return true;
  685. }
  686.  
  687. //
  688. //----------------------------------------------------------------------------------------------
  689. bool CRecorderUnit::StreamVec3::LoadStream(FILE* pFile)
  690. {
  691.         int counter;
  692.         fread(&counter, sizeof(counter), 1, pFile);
  693.  
  694.         for (int idx(0); idx < counter; ++idx)
  695.                 if (!LoadValue(pFile)) return false;
  696.  
  697.         return true;
  698. }
  699.  
  700. //
  701. //----------------------------------------------------------------------------------------------
  702. bool CRecorderUnit::StreamVec3::SaveStream(FILE* pFile)
  703. {
  704.         int counter(m_Stream.size());
  705.         if (!fwrite(&counter, sizeof(counter), 1, pFile)) return false;
  706.         for (uint32 idx(0); idx < m_Stream.size(); ++idx)
  707.         {
  708.                 StreamUnit* pCurUnit(static_cast<StreamUnit*>(m_Stream[idx]));
  709.                 float time = pCurUnit->m_StartTime;
  710.                 Vec3& vect = pCurUnit->m_Pos;
  711.                 if (!WriteValue(time, vect, pFile)) return false;
  712.         }
  713.         return true;
  714. }
  715.  
  716. //
  717. //----------------------------------------------------------------------------------------------
  718. void* CRecorderUnit::StreamFloat::GetCurrent(float& startingFrom)
  719. {
  720.         if (m_CurIdx < 0 || m_CurIdx >= (int)m_Stream.size())
  721.                 return NULL;
  722.         startingFrom = m_Stream[m_CurIdx]->m_StartTime;
  723.         return (void*)(&static_cast<StreamUnit*>(m_Stream[m_CurIdx])->m_Val);
  724. }
  725.  
  726. //
  727. //----------------------------------------------------------------------------------------------
  728. bool CRecorderUnit::StreamFloat::GetCurrentString(string& sOut, float& startingFrom)
  729. {
  730.         sOut.clear();
  731.  
  732.         float* pF = (float*)GetCurrent(startingFrom);
  733.         if (pF)
  734.         {
  735.                 sOut = CryStringUtils::toString(*pF);
  736.         }
  737.  
  738.         return (!sOut.empty());
  739. }
  740.  
  741. //
  742. //----------------------------------------------------------------------------------------------
  743. void* CRecorderUnit::StreamFloat::GetNext(float& startingFrom)
  744. {
  745.         if (++m_CurIdx >= (int)m_Stream.size())
  746.         {
  747.                 m_CurIdx = (int)m_Stream.size();
  748.                 return NULL;
  749.         }
  750.         startingFrom = m_Stream[m_CurIdx]->m_StartTime;
  751.         return (void*)(&static_cast<StreamUnit*>(m_Stream[m_CurIdx])->m_Val);
  752. }
  753.  
  754. //
  755. //----------------------------------------------------------------------------------------------
  756. bool CRecorderUnit::StreamFloat::FilterPoint(const IAIRecordable::RecorderEventData* pEventData) const
  757. {
  758.         bool bResult = true;
  759.  
  760.         // Simple point filtering.
  761.         if (m_bUseFilter && !m_Stream.empty())
  762.         {
  763.                 StreamUnit* pLastUnit(static_cast<StreamUnit*>(m_Stream.back()));
  764.                 float fDelta = pLastUnit->m_Val - pEventData->val;
  765.                 bResult = (fabsf(fDelta) > FLT_EPSILON);
  766.         }
  767.  
  768.         return bResult;
  769. }
  770.  
  771. //
  772. //----------------------------------------------------------------------------------------------
  773. void CRecorderUnit::StreamFloat::AddValue(const IAIRecordable::RecorderEventData* pEventData, float t)
  774. {
  775.         if (FilterPoint(pEventData))
  776.         {
  777.                 m_Stream.push_back(new StreamUnit(t, pEventData->val));
  778.         }
  779. }
  780.  
  781. //
  782. //----------------------------------------------------------------------------------------------
  783. bool CRecorderUnit::StreamFloat::WriteValue(const IAIRecordable::RecorderEventData* pEventData, float t)
  784. {
  785.         bool bResult = true;
  786.  
  787.         if (FilterPoint(pEventData))
  788.         {
  789.                 bResult = WriteValue(t, pEventData->val, CAIRecorder::m_pFile);
  790.         }
  791.  
  792.         return bResult;
  793. }
  794.  
  795. //
  796. //----------------------------------------------------------------------------------------------
  797. bool CRecorderUnit::StreamFloat::WriteValue(float t, float val, FILE* pFile)
  798. {
  799.         if (!fwrite(&t, sizeof(t), 1, pFile)) return false;
  800.         if (!fwrite(&val, sizeof(val), 1, pFile)) return false;
  801.         return true;
  802. }
  803.  
  804. //
  805. //----------------------------------------------------------------------------------------------
  806. bool CRecorderUnit::StreamFloat::LoadValue(float& t, float& val, FILE* pFile)
  807. {
  808.         if (!fread(&t, sizeof(t), 1, pFile)) return false;
  809.         if (!fread(&val, sizeof(val), 1, pFile)) return false;
  810.         return true;
  811. }
  812.  
  813. //----------------------------------------------------------------------------------------------
  814. bool CRecorderUnit::StreamFloat::LoadValue(FILE* pFile)
  815. {
  816.         float val;
  817.         float time;
  818.         if (!LoadValue(time, val, pFile)) return false;
  819.  
  820.         // Check chronological ordering
  821.         if (!m_Stream.empty() && m_Stream.back()->m_StartTime > time)
  822.         {
  823.                 gEnv->pLog->LogError("[AI Recorder] Aborting - events are not recorded in chronological order");
  824.                 return false;
  825.         }
  826.  
  827.         m_Stream.push_back(new StreamUnit(time, val));
  828.         return true;
  829. }
  830.  
  831. //
  832. //----------------------------------------------------------------------------------------------
  833. bool CRecorderUnit::StreamFloat::LoadStream(FILE* pFile)
  834. {
  835.         int counter;
  836.         fread(&counter, sizeof(counter), 1, pFile);
  837.  
  838.         for (int idx(0); idx < counter; ++idx)
  839.                 if (!LoadValue(pFile)) return false;
  840.  
  841.         return true;
  842. }
  843.  
  844. //
  845. //----------------------------------------------------------------------------------------------
  846. bool CRecorderUnit::StreamFloat::SaveStream(FILE* pFile)
  847. {
  848.         int counter(m_Stream.size());
  849.         if (!fwrite(&counter, sizeof(counter), 1, pFile)) return false;
  850.         for (uint32 idx(0); idx < m_Stream.size(); ++idx)
  851.         {
  852.                 StreamUnit* pCurUnit(static_cast<StreamUnit*>(m_Stream[idx]));
  853.                 float time = pCurUnit->m_StartTime;
  854.                 float val = pCurUnit->m_Val;
  855.                 if (!WriteValue(time, val, pFile)) return false;
  856.         }
  857.         return true;
  858. }
  859.  
  860. //
  861. //----------------------------------------------------------------------------------------------
  862. IAIDebugStream* CRecorderUnit::GetStream(IAIRecordable::e_AIDbgEvent streamTag)
  863. {
  864.         return m_Streams[streamTag];
  865. }
  866.  
  867. //
  868. //----------------------------------------------------------------------------------------------
  869. void CRecorderUnit::ResetStreams(CTimeValue startTime)
  870. {
  871.         for (TStreamMap::iterator strItr(m_Streams.begin()); strItr != m_Streams.end(); ++strItr)
  872.         {
  873.                 StreamBase* pStream(strItr->second);
  874.                 if (pStream)
  875.                         pStream->Clear();
  876.         }
  877.         m_startTime = startTime;
  878. }
  879.  
  880. //
  881. //----------------------------------------------------------------------------------------------
  882. bool CRecorderUnit::Save(FILE* pFile)
  883. {
  884.         {
  885.                 // Make a list of all that are using string indexes
  886.                 TStreamMap stringIndexMap;
  887.                 for (TStreamMap::iterator strItr(m_Streams.begin()); strItr != m_Streams.end(); ++strItr)
  888.                 {
  889.                         StreamBase* pStream(strItr->second);
  890.                         if (pStream && pStream->IsUsingStringIndex())
  891.                         {
  892.                                 IAIRecordable::e_AIDbgEvent type = ((strItr->first)); // The kind of stream
  893.                                 stringIndexMap[type] = pStream;
  894.                         }
  895.                 }
  896.  
  897.                 // Write the number of streams using string-index lookups
  898.                 int numStreams(stringIndexMap.size());
  899.                 if (!fwrite(&numStreams, sizeof(numStreams), 1, pFile)) return false;
  900.  
  901.                 for (TStreamMap::iterator strItr(stringIndexMap.begin()); strItr != stringIndexMap.end(); ++strItr)
  902.                 {
  903.                         StreamBase* pStream(strItr->second);
  904.                         const int intType((strItr->first)); // The kind of stream
  905.                         if (!fwrite(&intType, sizeof(intType), 1, pFile)) return false;
  906.                         if (pStream && !pStream->SaveStringIndex(pFile)) return false;
  907.                 }
  908.         }
  909.  
  910.         {
  911.                 // Write the number of streams stored
  912.                 int numStreams(m_Streams.size());
  913.                 if (!fwrite(&numStreams, sizeof(numStreams), 1, pFile)) return false;
  914.  
  915.                 for (TStreamMap::iterator strItr(m_Streams.begin()); strItr != m_Streams.end(); ++strItr)
  916.                 {
  917.                         StreamBase* pStream(strItr->second);
  918.                         const int intType((strItr->first)); // The kind of stream
  919.                         if (!fwrite(&intType, sizeof(intType), 1, pFile)) return false;
  920.                         if (pStream && !pStream->SaveStream(pFile)) return false;
  921.                 }
  922.         }
  923.  
  924.         return true;
  925. }
  926.  
  927. //
  928. //----------------------------------------------------------------------------------------------
  929. bool CRecorderUnit::Load(FILE* pFile)
  930. {
  931.         for (TStreamMap::iterator strItr(m_Streams.begin()); strItr != m_Streams.end(); ++strItr)
  932.         {
  933.                 StreamBase* pStream(strItr->second);
  934.                 if (pStream)
  935.                         pStream->Clear();
  936.         }
  937.  
  938.         {
  939.                 // Read the number of streams using string-index lookups stored
  940.                 int numStreams;
  941.                 if (!fread(&numStreams, sizeof(numStreams), 1, pFile)) return false;
  942.  
  943.                 for (int i(0); i < numStreams; ++i)
  944.                 {
  945.                         IAIRecordable::e_AIDbgEvent intType; // The kind of stream
  946.                         if (!fread(&intType, sizeof(intType), 1, pFile)) return false;
  947.  
  948.                         if (!m_Streams[intType]->LoadStringIndex(pFile)) return false;
  949.                 }
  950.         }
  951.  
  952.         {
  953.                 // Read the number of streams stored
  954.                 int numStreams;
  955.                 if (!fread(&numStreams, sizeof(numStreams), 1, pFile)) return false;
  956.  
  957.                 for (int i(0); i < numStreams; ++i)
  958.                 {
  959.                         IAIRecordable::e_AIDbgEvent intType; // The kind of stream
  960.                         if (!fread(&intType, sizeof(intType), 1, pFile)) return false;
  961.  
  962.                         if (!m_Streams[intType]->LoadStream(pFile)) return false;
  963.                 }
  964.         }
  965.  
  966.         return true;
  967. }
  968.  
  969. //
  970. //----------------------------------------------------------------------------------------------
  971. CAIRecorder::CAIRecorder()
  972. {
  973.         m_recordingMode = eAIRM_Off;
  974.         m_lowLevelFileBufferSize = 1024;
  975.         m_lowLevelFileBuffer = new char[m_lowLevelFileBufferSize];
  976.         m_pLog = NULL;
  977.         m_unitLifeCounter = 0;
  978. }
  979.  
  980. //
  981. //----------------------------------------------------------------------------------------------
  982. void CAIRecorder::Init(void)
  983. {
  984.         if (!m_pLog)
  985.                 m_pLog = gEnv->pLog;
  986.  
  987.         if (gEnv->pSystem)
  988.         {
  989.                 ISystemEventDispatcher* pDispatcher = gEnv->pSystem->GetISystemEventDispatcher();
  990.                 if (pDispatcher)
  991.                         pDispatcher->RegisterListener(this);
  992.         }
  993. }
  994.  
  995. //
  996. //----------------------------------------------------------------------------------------------
  997. bool CAIRecorder::IsRunning(void) const
  998. {
  999.         return (m_recordingMode != eAIRM_Off);
  1000. }
  1001.  
  1002. //
  1003. //----------------------------------------------------------------------------------------------
  1004. void CAIRecorder::GetCompleteFilename(char const* szFilename, bool bAppendFileCount, string& sOut) const
  1005. {
  1006.         const bool bIsEditor = gEnv->IsEditor();
  1007.  
  1008.         if (!szFilename || !szFilename[0])
  1009.         {
  1010.                 // Use current level
  1011.                 szFilename = PathUtil::GetFileName(gEnv->pGameFramework->GetLevelName());
  1012.         }
  1013.  
  1014.         if (!szFilename || !szFilename[0])
  1015.         {
  1016.                 // Use defualt
  1017.                 szFilename = AIRECORDER_DEFAULT_FILE;
  1018.         }
  1019.  
  1020.         CRY_ASSERT(szFilename && szFilename[0]);
  1021.  
  1022.         string sVersion;
  1023.         if (bIsEditor)
  1024.         {
  1025.                 sVersion = AIRECORDER_EDITOR_AUTO_APPEND;
  1026.         }
  1027.         else
  1028.         {
  1029.                 // Get current date line
  1030.                 time_t ltime;
  1031.                 time(&ltime);
  1032.                 tm* pTm = localtime(&ltime);
  1033.                 char szDate[128];
  1034.                 strftime(szDate, 128, "Date(%d %b %Y) Time(%H %M %S)", pTm);
  1035.  
  1036.                 // Get current version line
  1037.                 const SFileVersion& fileVersion = gEnv->pSystem->GetFileVersion();
  1038.  
  1039.                 sVersion.Format("Build(%d) %s", fileVersion[0], szDate);
  1040.  
  1041.         }
  1042.  
  1043.         string sBaseFile;
  1044.         sBaseFile.Format("%s_%s", szFilename, sVersion.c_str());
  1045.  
  1046.         sOut = PathUtil::Make(AIRECORDER_FOLDER, sBaseFile.c_str(), "rcd");
  1047.         gEnv->pCryPak->MakeDir(AIRECORDER_FOLDER);
  1048.  
  1049.         // Check if file already exists
  1050.         int iFileCount = 0;
  1051.         FILE* pFileChecker = (bAppendFileCount && !bIsEditor ? fopen(sOut.c_str(), "rb") : NULL);
  1052.         if (pFileChecker)
  1053.         {
  1054.                 string sNewOut;
  1055.                 while (pFileChecker)
  1056.                 {
  1057.                         fclose(pFileChecker);
  1058.                         sNewOut.Format("%s(%d)", sBaseFile.c_str(), ++iFileCount);
  1059.                         sOut = PathUtil::Make(AIRECORDER_FOLDER, sNewOut.c_str(), "rcd");
  1060.                         pFileChecker = fopen(sOut.c_str(), "rb");
  1061.                 }
  1062.         }
  1063. }
  1064.  
  1065. //
  1066. //----------------------------------------------------------------------------------------------
  1067. void CAIRecorder::OnReset(IAISystem::EResetReason reason)
  1068. {
  1069.         //if (!gEnv->IsEditor())
  1070.         //      return;
  1071.  
  1072.         if (gAIEnv.CVars.DebugRecordAuto == 0)
  1073.                 return;
  1074.  
  1075.         const bool bIsSerializingFile = 0 != gEnv->pSystem->IsSerializingFile();
  1076.         if (!bIsSerializingFile)
  1077.         {
  1078.                 // Handle starting/stoping the recorder
  1079.                 switch (reason)
  1080.                 {
  1081.                 case IAISystem::RESET_INTERNAL:
  1082.                 case IAISystem::RESET_INTERNAL_LOAD:
  1083.                         Stop();
  1084.                         Reset();
  1085.                         break;
  1086.  
  1087.                 case IAISystem::RESET_EXIT_GAME:
  1088.                         Stop();
  1089.                         break;
  1090.  
  1091.                 case IAISystem::RESET_ENTER_GAME:
  1092.                         Reset();
  1093.                         Start(eAIRM_Memory);
  1094.                         break;
  1095.                 }
  1096.         }
  1097. }
  1098.  
  1099. //
  1100. //----------------------------------------------------------------------------------------------
  1101. bool CAIRecorder::AddListener(IAIRecorderListener* pListener)
  1102. {
  1103.         return stl::push_back_unique(m_Listeners, pListener);
  1104. }
  1105.  
  1106. //
  1107. //----------------------------------------------------------------------------------------------
  1108. bool CAIRecorder::RemoveListener(IAIRecorderListener* pListener)
  1109. {
  1110.         return stl::find_and_erase(m_Listeners, pListener);
  1111. }
  1112.  
  1113. //
  1114. //----------------------------------------------------------------------------------------------
  1115. void CAIRecorder::Start(EAIRecorderMode mode, const char* filename)
  1116. {
  1117.         if (IsRunning()) return;
  1118.  
  1119.         Init(); // make sure
  1120.  
  1121.         // Clear any late arriving events from last run
  1122.         Reset();
  1123.  
  1124.         string sFile;
  1125.         GetCompleteFilename(filename, true, sFile);
  1126.  
  1127.         switch (mode)
  1128.         {
  1129.         case eAIRM_Memory:
  1130.                 {
  1131.                         m_recordingMode = eAIRM_Memory;
  1132.  
  1133.                         // No action required to start recording
  1134.                 }
  1135.                 break;
  1136.         case eAIRM_Disk:
  1137.                 {
  1138.                         m_recordingMode = eAIRM_Disk;
  1139.  
  1140.                         // Redundant check
  1141.                         if (m_pFile)
  1142.                         {
  1143.                                 fclose(m_pFile);
  1144.                                 m_pFile = NULL;
  1145.                         }
  1146.  
  1147.                         // File is closed, so we have the chance to adjust buffer size
  1148.                         int newBufferSize = gAIEnv.CVars.DebugRecordBuffer;
  1149.                         newBufferSize = clamp_tpl(newBufferSize, 128, 1024000);
  1150.                         if (newBufferSize != m_lowLevelFileBufferSize)
  1151.                         {
  1152.                                 delete[] m_lowLevelFileBuffer;
  1153.                                 m_lowLevelFileBufferSize = newBufferSize;
  1154.                                 m_lowLevelFileBuffer = new char[newBufferSize];
  1155.                         }
  1156.  
  1157.                         // Open for streaming, using static file pointer
  1158.                         m_pFile = fxopen(sFile.c_str(), "wb");
  1159.  
  1160.                         if (m_pFile)
  1161.                         {
  1162.                                 // Note - must use own buffer or memory manager may break!
  1163.                                 PREFAST_SUPPRESS_WARNING(6001)
  1164.                                 setvbuf(m_pFile, m_lowLevelFileBuffer, _IOFBF, m_lowLevelFileBufferSize);
  1165.  
  1166.                                 // Write preamble
  1167.                                 if (!Write(m_pFile))
  1168.                                 {
  1169.                                         fclose(m_pFile);
  1170.                                         m_pFile = NULL;
  1171.                                 }
  1172.                         }
  1173.                         // Leave it open
  1174.                 }
  1175.                 break;
  1176.         default:
  1177.                 m_recordingMode = eAIRM_Off;
  1178.  
  1179.                 // In other modes does nothing
  1180.                 // In mode 0, will quite happily be started and stopped doing nothing
  1181.                 break;
  1182.         }
  1183.  
  1184.         // Notify listeners
  1185.         TListeners::iterator itNext = m_Listeners.begin();
  1186.         while (itNext != m_Listeners.end())
  1187.         {
  1188.                 TListeners::iterator itListener = itNext++;
  1189.                 IAIRecorderListener* pListener = *itListener;
  1190.                 CRY_ASSERT(pListener);
  1191.  
  1192.                 pListener->OnRecordingStart(m_recordingMode, sFile.c_str());
  1193.         }
  1194. }
  1195.  
  1196. //
  1197. //----------------------------------------------------------------------------------------------
  1198. void CAIRecorder::Stop(const char* filename)
  1199. {
  1200.         if (!IsRunning()) return;
  1201.  
  1202.         EAIRecorderMode mode = m_recordingMode;
  1203.         m_recordingMode = eAIRM_Off;
  1204.         m_unitLifeCounter = 0;
  1205.  
  1206.         // Close the recorder file, should have been streaming
  1207.         if (m_pFile)
  1208.         {
  1209.                 fclose(m_pFile);
  1210.                 m_pFile = NULL;
  1211.         }
  1212.  
  1213.         switch (mode)
  1214.         {
  1215.         case eAIRM_Disk:
  1216.                 {
  1217.                         // Only required to close the recorder file
  1218.                 }
  1219.                 break;
  1220.         case eAIRM_Memory:
  1221.                 {
  1222.                         // Dump the history to disk
  1223.                         Save(filename);
  1224.                 }
  1225.                 break;
  1226.         default:
  1227.                 break;
  1228.         }
  1229.  
  1230.         string sFile;
  1231.         GetCompleteFilename(filename, true, sFile);
  1232.  
  1233.         // Notify listeners
  1234.         TListeners::iterator itNext = m_Listeners.begin();
  1235.         while (itNext != m_Listeners.end())
  1236.         {
  1237.                 TListeners::iterator itListener = itNext++;
  1238.                 IAIRecorderListener* pListener = *itListener;
  1239.                 CRY_ASSERT(pListener);
  1240.  
  1241.                 pListener->OnRecordingStop(sFile.c_str());
  1242.         }
  1243. }
  1244.  
  1245. //
  1246. //----------------------------------------------------------------------------------------------
  1247. CAIRecorder::~CAIRecorder()
  1248. {
  1249.         Shutdown();
  1250. }
  1251.  
  1252. //
  1253. //----------------------------------------------------------------------------------------------
  1254. void CAIRecorder::Shutdown(void)
  1255. {
  1256.         if (IsRunning())
  1257.                 Stop();
  1258.  
  1259.         if (gEnv->pSystem)
  1260.         {
  1261.                 ISystemEventDispatcher* pDispatcher = gEnv->pSystem->GetISystemEventDispatcher();
  1262.                 if (pDispatcher)
  1263.                         pDispatcher->RemoveListener(this);
  1264.         }
  1265.  
  1266.         DestroyDummyObjects();
  1267.  
  1268.         for (TUnits::iterator it = m_Units.begin(); it != m_Units.end(); ++it)
  1269.                 delete it->second;
  1270.         m_Units.clear();
  1271.  
  1272.         if (m_lowLevelFileBuffer)
  1273.                 delete[] m_lowLevelFileBuffer;
  1274. }
  1275.  
  1276. //
  1277. //----------------------------------------------------------------------------------------------
  1278. void CAIRecorder::OnSystemEvent(ESystemEvent event, UINT_PTR wparam, UINT_PTR lparam)
  1279. {
  1280.         if (event == ESYSTEM_EVENT_FAST_SHUTDOWN ||
  1281.             event == ESYSTEM_EVENT_LEVEL_UNLOAD)
  1282.         {
  1283.                 if (IsRunning())
  1284.                         Stop();
  1285.         }
  1286. }
  1287.  
  1288. //
  1289. //----------------------------------------------------------------------------------------------
  1290. void CAIRecorder::Update()
  1291. {
  1292.         // This is never called so far
  1293. }
  1294.  
  1295. //
  1296. //----------------------------------------------------------------------------------------------
  1297. bool CAIRecorder::Load(const char* filename)
  1298. {
  1299.         if (IsRunning()) return false; // To avoid undefined behaviour
  1300.  
  1301.         // Open the AI recorder dump file
  1302.         string sFile = filename;
  1303.         if (sFile.empty())
  1304.         {
  1305.                 GetCompleteFilename(filename, false, sFile);
  1306.         }
  1307.         FILE* pFile = fxopen(sFile.c_str(), "rb");
  1308.  
  1309.         bool bSuccess = false;
  1310.         if (pFile)
  1311.         {
  1312.                 // Update static file pointer
  1313.                 m_pFile = pFile;
  1314.  
  1315.                 bSuccess = Read(pFile);
  1316.  
  1317.                 m_pFile = NULL;
  1318.                 fclose(pFile);
  1319.         }
  1320.  
  1321.         if (bSuccess)
  1322.         {
  1323.                 // Notify listeners
  1324.                 TListeners::iterator itNext = m_Listeners.begin();
  1325.                 while (itNext != m_Listeners.end())
  1326.                 {
  1327.                         TListeners::iterator itListener = itNext++;
  1328.                         IAIRecorderListener* pListener = *itListener;
  1329.                         CRY_ASSERT(pListener);
  1330.  
  1331.                         pListener->OnRecordingLoaded(sFile.c_str());
  1332.                 }
  1333.         }
  1334.  
  1335.         return bSuccess;
  1336. }
  1337.  
  1338. bool CAIRecorder::Read(FILE* pFile)
  1339. {
  1340.         CAISystem* pAISystem = GetAISystem();
  1341.         CRY_ASSERT(pAISystem);
  1342.  
  1343.         // File header
  1344.         RecorderFileHeader fileHeader;
  1345.         fread(&fileHeader, sizeof(fileHeader), 1, pFile);
  1346.         if (!fileHeader.check())
  1347.                 return false;
  1348.         if (fileHeader.version > AIRECORDER_VERSION)
  1349.         {
  1350.                 m_pLog->LogError("[AI Recorder] Saved AI Recorder log is of wrong version number");
  1351.                 fclose(pFile);
  1352.                 return false;
  1353.         }
  1354.  
  1355.         // Clear all units streams
  1356.         Reset();
  1357.  
  1358.         // String stores name of each unit
  1359.         string name;
  1360.  
  1361.         //std::vector <tAIObjectID> tempIDs;  // Ids for units we create just during loading, to skip over data
  1362.         //std::map <tAIObjectID,tAIObjectID> idMap;   // Mapping the ids recorded in the file into those used in this run
  1363.  
  1364.         // For each record
  1365.         for (int i = 0; i < fileHeader.unitsNumber; i++)
  1366.         {
  1367.                 UnitHeader header;
  1368.                 // Read entity name and ID
  1369.                 if (!fread(&header, sizeof(header), 1, pFile)) return false;
  1370.                 if (!header.check()) return false;
  1371.                 if (header.nameLength > 0)
  1372.                 {
  1373.                         std::vector<char> buffer;
  1374.                         buffer.resize(header.nameLength);
  1375.                         if (fread(&buffer[0], sizeof(char), header.nameLength, pFile) != header.nameLength) return false;
  1376.                         name.assign(&buffer[0], header.nameLength);
  1377.                 }
  1378.                 else
  1379.                 {
  1380.                         name.clear();
  1381.                 }
  1382.  
  1383.                 string sDummyName;
  1384.                 uint32 uLifeCounter = (header.ID >> 32);
  1385.                 sDummyName.Format("%s_%u", name.c_str(), uLifeCounter);
  1386.  
  1387.                 // Create a dummy object to represent this recording
  1388.                 TDummyObjects::value_type refDummy;
  1389.                 gAIEnv.pAIObjectManager->CreateDummyObject(refDummy, sDummyName);
  1390.                 if (!refDummy.IsNil())
  1391.                 {
  1392.                         m_DummyObjects.push_back(refDummy);
  1393.  
  1394.                         // Create map entry from the old, possibly changed ID to the current one
  1395.                         //idMap[header.ID] = liveID;
  1396.                         CAIObject* pLiveObject = refDummy.GetAIObject();
  1397.                         CRecorderUnit* unit = pLiveObject->CreateAIDebugRecord();
  1398.  
  1399.                         if (!unit) return false;
  1400.                         if (!unit->Load(pFile)) return false;
  1401.                 }
  1402.                 else
  1403.                 {
  1404.                         m_pLog->LogError("[AI Recorder] Failed to create a Recorder Unit for \'%s\'", name.c_str());
  1405.                 }
  1406.         }
  1407.  
  1408.         // After the "announced" data in the file, we continue, looking for more
  1409.         // A streamed log will typically have 0 length announced data (providing the IDs/names etc)
  1410.         // and all data will follow afterwards
  1411.  
  1412.         // For each event that follows
  1413.         // (Kevin) - What's the purpose of this section? Is it still needed? (24/8/09)
  1414.         bool bEndOfFile = false;
  1415.         do
  1416.         {
  1417.                 // Read event metadata
  1418.                 StreamedEventHeader header;
  1419.                 bEndOfFile = (!fread(&header, sizeof(header), 1, pFile));
  1420.                 if (!bEndOfFile)
  1421.                 {
  1422.                         if (!header.check())
  1423.                         {
  1424.                                 m_pLog->LogError("[AI Recorder] corrupt event found reading streamed section");
  1425.                                 return false;
  1426.                         }
  1427.                         else
  1428.                         {
  1429.                                 CRY_ASSERT_MESSAGE(false, "Recorder has streamded data. This code needs to be readded.");
  1430.  
  1431.                                 //int liveID = idMap[header.unitID];
  1432.                                 //if (!liveID)
  1433.                                 //{
  1434.                                 //      // New unannounced ID
  1435.                                 //      m_pLog->LogWarning("[AI Recorder] Unknown ID %d found in stream", header.unitID);
  1436.                                 //      // Generate a random ID - unlikely to collide.
  1437.                                 //      liveID = abs( (int)cry_rand()%100000 ) + 10000;
  1438.                                 //      tempIDs.push_back(liveID);
  1439.                                 //      idMap[header.unitID] = liveID;
  1440.                                 //}
  1441.  
  1442.                                 //CRecorderUnit *unit = AddUnit(liveID, true);
  1443.                                 //if (!unit) return false;
  1444.                                 //if (!unit->LoadEvent( (IAIRecordable::e_AIDbgEvent) header.streamID)) return false;
  1445.                         }
  1446.                 }
  1447.         }
  1448.         while (!bEndOfFile);
  1449.  
  1450.         return true;
  1451. }
  1452.  
  1453. //
  1454. //----------------------------------------------------------------------------------------------
  1455. bool CAIRecorder::Save(const char* filename)
  1456. {
  1457.         if (IsRunning()) return false; // To avoid undefined behaviour
  1458.  
  1459.         // This method should not change state at all
  1460.         bool bSuccess = false;
  1461.  
  1462.         // Open the AI recorder dump file
  1463.         string sFile;
  1464.         GetCompleteFilename(filename, true, sFile);
  1465.         FILE* pFile = fxopen(sFile.c_str(), "wb");
  1466.  
  1467.         if (pFile)
  1468.         {
  1469.                 // Update static file pointer
  1470.                 m_pFile = pFile;
  1471.  
  1472.                 // Note - must use own buffer or memory manager may break!
  1473.                 setvbuf(pFile, m_lowLevelFileBuffer, _IOFBF, m_lowLevelFileBufferSize);
  1474.                 bSuccess = Write(pFile);
  1475.  
  1476.                 m_pFile = NULL;
  1477.                 fclose(pFile);
  1478.         }
  1479.  
  1480.         if (bSuccess)
  1481.         {
  1482.                 // Notify listeners
  1483.                 TListeners::iterator itNext = m_Listeners.begin();
  1484.                 while (itNext != m_Listeners.end())
  1485.                 {
  1486.                         TListeners::iterator itListener = itNext++;
  1487.                         IAIRecorderListener* pListener = *itListener;
  1488.                         CRY_ASSERT(pListener);
  1489.  
  1490.                         pListener->OnRecordingSaved(sFile.c_str());
  1491.                 }
  1492.         }
  1493.         else
  1494.         {
  1495.                 m_pLog->LogError("[AI Recorder] Save dump failed");
  1496.         }
  1497.  
  1498.         return bSuccess;
  1499. }
  1500.  
  1501. bool CAIRecorder::Write(FILE* pFile)
  1502. {
  1503.         // File header
  1504.         RecorderFileHeader fileHeader;
  1505.         fileHeader.version = AIRECORDER_VERSION;
  1506.         fileHeader.unitsNumber = m_Units.size();
  1507.         if (!fwrite(&fileHeader, sizeof(fileHeader), 1, pFile)) return false;
  1508.  
  1509.         // For each record
  1510.         for (TUnits::iterator unitIter = m_Units.begin(); unitIter != m_Units.end(); ++unitIter)
  1511.         {
  1512.                 CRecorderUnit* pUnit = unitIter->second;
  1513.                 CRY_ASSERT(pUnit);
  1514.                 if (!pUnit)
  1515.                         continue;
  1516.  
  1517.                 // Record entity name and ID (ID may be not be preserved)
  1518.                 const string& name = pUnit->GetName();
  1519.  
  1520.                 UnitHeader header;
  1521.                 header.nameLength = (int)name.size();
  1522.                 header.ID = pUnit->GetId();
  1523.  
  1524.                 if (!fwrite(&header, sizeof(header), 1, pFile)) return false;
  1525.                 if (fwrite(name.c_str(), sizeof(char), header.nameLength, pFile) != header.nameLength) return false;
  1526.  
  1527.                 if (!pUnit->Save(pFile)) return false;
  1528.         }
  1529.         return true;
  1530. }
  1531.  
  1532. //
  1533. //----------------------------------------------------------------------------------------------
  1534. CRecorderUnit* CAIRecorder::AddUnit(CWeakRef<CAIObject> refObject, bool force)
  1535. {
  1536.         CRY_ASSERT(refObject.IsSet());
  1537.  
  1538.         CRecorderUnit* pNewUnit = NULL;
  1539.  
  1540.         if ((IsRunning() || force) && refObject.IsSet())
  1541.         {
  1542.                 // Create a new unit only if activated or required
  1543.                 const uint32 lifeIndex = ++m_unitLifeCounter;
  1544.                 pNewUnit = new CRecorderUnit(this, GetAISystem()->GetFrameStartTime(), refObject, lifeIndex);
  1545.                 m_Units[pNewUnit->GetId()] = pNewUnit;
  1546.         }
  1547.  
  1548.         return pNewUnit;
  1549. }
  1550.  
  1551. //
  1552. //----------------------------------------------------------------------------------------------
  1553. void CAIRecorder::Reset(void)
  1554. {
  1555.         DestroyDummyObjects();
  1556.  
  1557.         const CTimeValue frameStartTime = gEnv->pTimer->GetFrameStartTime();
  1558.         for (TUnits::iterator unitIter = m_Units.begin(); unitIter != m_Units.end(); ++unitIter)
  1559.         {
  1560.                 unitIter->second->ResetStreams(frameStartTime);
  1561.         }
  1562. }
  1563.  
  1564. //
  1565. //----------------------------------------------------------------------------------------------
  1566. void CAIRecorder::DestroyDummyObjects()
  1567. {
  1568.         for (TDummyObjects::iterator dummyIter = m_DummyObjects.begin(); dummyIter != m_DummyObjects.end(); ++dummyIter)
  1569.         {
  1570.                 TDummyObjects::value_type refDummy = *dummyIter;
  1571.                 refDummy.Release();
  1572.         }
  1573.  
  1574.         m_DummyObjects.clear();
  1575. }
  1576.  
  1577. #endif //CRYAISYSTEM_DEBUG
  1578.  
downloadAIRecorder.cpp Source code - Download CRYENGINE Source code
Related Source Codes/Software:
postal - 2017-06-11
reactide - Reactide is the first dedicated IDE for React web ... 2017-06-11
rkt - rkt is a pod-native container engine for Linux. It... 2017-06-11
uWebSockets - Tiny WebSockets https://for... 2017-06-11
realworld - TodoMVC for the RealWorld - Exemplary fullstack Me... 2017-06-11
CRYENGINE - CRYENGINE is a powerful real-time game development... 2017-06-11
goreplay - GoReplay is an open-source tool for capturing and ... 2017-06-10
pyenv - Simple Python version management 2017-06-10
redux-saga - An alternative side effect model for Redux apps ... 2017-06-10
angular-starter - 2017-06-10

 Back to top