S3rgeus
Emperor
I've created a new game system as a part of a total conversion mod by modding the DLL. Now, unfortunately, in the process of doing that, I have introduced one of the worst kinds of bugs to the game. I now have an intermittent CTD caused by a memory access exception when my new system is activated. It doesn't happen every time (maybe 1/4 times) but it's definitely a problem.
Now, I think this is due to my lack of understanding of the defines Firaxis is using in the place of new and delete, and I'm probably not using them properly.
I've introduced the following code files:
I've added an instance of that object as a member of CvMap:
The crash seems to occur when the Horn of Valere is first placed on the map, using this function:
I've used Firaxis' FNEW to create a new instance of my custom class there, but the third parameter to FNEW confuses me a bit. The second parameter, I believe, tells the game which pool of memory we're drawing from? So it makes sense to use the GamePlayDLL pool. But the third is just marked as 'Tag'? (All of the other calls to FNEW that I've seen in CvMap use a value of 0 here, so that's why I've done that.)
Any help would be much appreciated! I know this is an almost impossible error to debug remotely, but any insight into FNEW or SAFE_DELETE and their counterparts would be very helpful.
EDIT: Oops, I forgot to say, and this is probably quite important, by attaching the VS debugger to CivV (thanks for the suggestion, hulkster!) I found the exception was thrown here: (I've typed out the stack trace, long story why I can't just copy and paste it)
Now, I think this is due to my lack of understanding of the defines Firaxis is using in the place of new and delete, and I'm probably not using them properly.
I've introduced the following code files:
Spoiler :
WoTHornOfValere.h
WoTHornOfValere.cpp
Code:
// ----------------------------------------------------------------
// WoTMod New File
// Created by: S3rgeus
// ----------------------------------------------------------------
#pragma once
#include "CvPlot.h"
#include "CvUnit.h"
// Horn of Valere
class HornOfValere
{
public:
HornOfValere();
HornOfValere(int iXPos, int iYPos);
HornOfValere(CvPlot* pkPlot);
~HornOfValere();
CvPlot* GetPlot() const;
int GetX() const;
int GetY() const;
int GetTurnsSinceHornBlown() const;
void SetPlot(CvPlot* pkPlot);
void SetX(int iNewXPos);
void SetY(int iNewYPos);
void SetPosition(int iNewXPos, int iNewYPos);
void SetTurnsSinceHornBlown(int iNewValue);
void IncrementTurnsSinceHornBlown();
void DoTurn();
void FindHorn(CvUnit* pUnit);
void MoveHorn(int iNewXPos, int iNewYPos);
void MoveHorn(CvPlot* pkNewPlot);
bool CanFindHornOfValere(CvUnit* pUnit);
protected:
CvPlot* m_pkPlot;
int m_iXPosition;
int m_iYPosition;
bool m_bFound;
int m_iTurnsSinceHornBlown;
};
WoTHornOfValere.cpp
Code:
#include "WoTHornOfValere.h"
#include "CvGlobals.h"
#include "CvGameCoreDLLPCH.h"
#include "CvGameCoreUtils.h"
HornOfValere::HornOfValere(): m_bFound(false)
{
}
HornOfValere::HornOfValere(int iXPos, int iYPos)
{
HornOfValere(GC.getMap().plot(iXPos, iYPos));
}
HornOfValere::HornOfValere(CvPlot* pkPlot)
: m_pkPlot(pkPlot), m_iXPosition(pkPlot->getX()),
m_iYPosition(pkPlot->getY()), m_bFound(false),
m_iTurnsSinceHornBlown(50000) // arbitrarily large but not enough to overflow
{
}
HornOfValere::~HornOfValere()
{
m_pkPlot = NULL;
m_iYPosition = 0;
m_iXPosition = 0;
m_iTurnsSinceHornBlown = 0;
}
CvPlot* HornOfValere::GetPlot() const
{
return m_pkPlot;
}
int HornOfValere::GetX() const
{
return m_iXPosition;
}
int HornOfValere::GetY() const
{
return m_iYPosition;
}
int HornOfValere::GetTurnsSinceHornBlown() const
{
return m_iTurnsSinceHornBlown;
}
void HornOfValere::SetPlot(CvPlot* pkPlot)
{
m_pkPlot = pkPlot;
}
void HornOfValere::SetX(int iNewXPos)
{
m_iXPosition = iNewXPos;
}
void HornOfValere::SetY(int iNewYPos)
{
m_iYPosition = iNewYPos;
}
void HornOfValere::SetPosition(int iNewXPos, int iNewYPos)
{
SetX(iNewXPos);
SetY(iNewYPos);
}
void HornOfValere::SetTurnsSinceHornBlown(int iNewValue)
{
m_iTurnsSinceHornBlown = iNewValue;
}
void HornOfValere::IncrementTurnsSinceHornBlown()
{
m_iTurnsSinceHornBlown++;
}
void HornOfValere::DoTurn()
{
IDInfoVector currentUnits;
if (!m_bFound && m_pkPlot->getUnits(¤tUnits) > 0)
{
for (IDInfoVector::const_iterator itr = currentUnits.begin(); itr != currentUnits.end(); ++itr)
{
CvUnit* pUnit = ::getUnit(*itr);
if(pUnit && pUnit->CanDiscoverHornOfValere())
{
FindHorn(pUnit);
}
}
}
IncrementTurnsSinceHornBlown();
}
void HornOfValere::FindHorn(CvUnit* pUnit)
{
// TODO UI popup
if (pUnit)
{
m_bFound = true;
m_pkPlot->SetHornOfValere(false);
ICvEngineScriptSystem1* pkScriptSystem = gDLL->GetScriptSystem();
if (pkScriptSystem)
{
CvLuaArgsHandle args;
args->Push(pUnit->getOwner());
args->Push(pUnit->GetID());
bool bResult;
LuaSupport::CallHook(pkScriptSystem, "UnitDiscoveredHornOfValere", args.get(), bResult);
}
}
}
void HornOfValere::MoveHorn(int iNewXPos, int iNewYPos)
{
CvPlot* pkNewPlot = GC.getMap().plot(iNewXPos, iNewYPos);
MoveHorn(pkNewPlot);
}
void HornOfValere::MoveHorn(CvPlot* pkNewPlot)
{
m_pkPlot->SetHornOfValere(false);
m_iXPosition = pkNewPlot->getX();
m_iYPosition = pkNewPlot->getY();
pkNewPlot->SetHornOfValere(true);
m_pkPlot = pkNewPlot;
}
I've added an instance of that object as a member of CvMap:
Spoiler :
CvMap.h
Code:
// ----------------------------------------------------------------
// WoTMod Addition
// ----------------------------------------------------------------
bool m_bHasHornOfValere;
HornOfValere* m_pHornOfValere;
The crash seems to occur when the Horn of Valere is first placed on the map, using this function:
Spoiler :
Code:
void CvMap::PlaceHornOfValere(int iX, int iY)
{
if (IsHasHornOfValere())
{
m_pHornOfValere->MoveHorn(iX, iY);
}
else
{
CvPlot* pkPlot = plot(iX, iY);
m_pHornOfValere = FNEW(HornOfValere(pkPlot), c_eCiv5GameplayDLL, 0);
SetHasHornOfValere(true);
}
}
I've used Firaxis' FNEW to create a new instance of my custom class there, but the third parameter to FNEW confuses me a bit. The second parameter, I believe, tells the game which pool of memory we're drawing from? So it makes sense to use the GamePlayDLL pool. But the third is just marked as 'Tag'? (All of the other calls to FNEW that I've seen in CvMap use a value of 0 here, so that's why I've done that.)
Any help would be much appreciated! I know this is an almost impossible error to debug remotely, but any insight into FNEW or SAFE_DELETE and their counterparts would be very helpful.
EDIT: Oops, I forgot to say, and this is probably quite important, by attaching the VS debugger to CivV (thanks for the suggestion, hulkster!) I found the exception was thrown here: (I've typed out the stack trace, long story why I can't just copy and paste it)
Code:
CvDllGameContext::Free(void*) Line 183
CvDllGameContext::operatordelete(void*) Line 79
CvDllGame::DecrementReference() Line 57
CvDllGame::Destroy() Line 75