Potentially fixed the Random Event OOS bug!

lemmy101

Emperor
Joined
Apr 10, 2006
Messages
1,064
Hello all!

I *think* I may have fixed the Random Event OOS bug. After a few days of poking about, I came up with the not so elegant solution of creating massive buffers of pre-generated random values for each civ, generated with its own random number generator seeded from the initial synced seed value. Therefore it's completely detached from the game random from that point on, and each civ maintains their own list of random numbers, so if the order the civ's events are processed doesn't matter either...

I'm posting this now as i've been writing up the changes needed. I've got it in my mod but not sure when I'll release that, so here it is.

NOTE: Unless you're willing to help me test it, and just want to use it, I'd hold off implementing it a while longer until I've given it more of a test. I'm pretty confident but I'd hate to see anyone go to all the effort of implementing it and it still OSSing. If you'd be willing to help us with a large scale test then that would be much appreciated! :)

Here are the changes:

CvGame.h

Add above class:
Code:
#define MAX_EVENTS_PER_CIV_PER_TURN		3
#define MAX_ROLLS_PER_EVENT				20
#define MAX_EVENT_TURNS					5000
Add in class:
Code:
	unsigned long getRandomEventBufferRoll(int iPlayer, int index);

	void EventRandomizer_init();

	int m_iEventSeed;

	bool m_bEventInit;

	unsigned long** m_aiTurnRands;
CvMessageControl.h

Change: (add last argument)
Code:
	void sendEventTriggered(PlayerTypes ePlayer, EventTypes eEvent, int iEventTriggeredId, int iPositionInBuffer);

CvMessageData.h

Change: (add last argument)
Code:
	CvNetEventTriggered(PlayerTypes ePlayer = NO_PLAYER, EventTypes eEvent = NO_EVENT, int iEventTriggeredId = -1, int iPositionInBuffer = -1);


Add in class:
Code:
	int m_iPositionInBuffer;
CvPlayer.h

Add in class:
Code:
	int getCurrentEventBufferPlace();
	void setPlaceInEventBuffer(int val);
	int generateEventSeeds(int LastSeed);
	int getEventRollFromBuffer(int iWeight, const TCHAR* str);
	int m_iEventBufferPos;

Change: (remove const)

	CvCity* pickTriggerCity(EventTriggerTypes eTrigger);
	CvUnit* pickTriggerUnit(EventTriggerTypes eTrigger, CvPlot* pPlot, bool bPickPlot);
CyPlayer.h

Add to class:
Code:
	int getEventRollFromBuffer(int iPlayer, std::wstring szStr);
CvDLLButtonPopup.h

Change: (Add last parameter)
Code:
	CvMessageControl::getInstance().sendEventTriggered(GC.getGameINLINE().getActivePlayer(), (EventTypes)pPopupReturn->getButtonClicked(), info.getData1(), GET_PLAYER(GC.getGameINLINE().getActivePlayer()).getCurrentEventBufferPlace());
CvGame.cpp

Add to CvGame::CvGame:
Code:
	m_aiTurnRands = new unsigned long*[MAX_CIV_PLAYERS];
	for (int i = 0; i < MAX_PLAYERS; i++)
	{
		m_aiTurnRands[i] = new unsigned long[MAX_EVENT_TURNS * MAX_EVENTS_PER_CIV_PER_TURN * MAX_ROLLS_PER_EVENT];
	}
Add to CvGame::~CvGame:
Code:
	for (int i = 0; i < MAX_PLAYERS; i++)
	{
		SAFE_DELETE_ARRAY(m_aiTurnRands[i]);
	}
	SAFE_DELETE_ARRAY(m_aiTurnRands);
Add:
Code:
#define CJSRANDOM_A      (1103515245)
#define CJSRANDOM_C      (12345)
#define CJSRANDOM_SHIFT  (16)

unsigned long CvGame::getRandomEventBufferRoll(int iPlayer, int index)
{
	if(!m_bEventInit)
		EventRandomizer_init();

	FAssertMsg(m_aiTurnRands[iPlayer][index] != 0, "On roll: We don't seem to have random values CRY");

	return m_aiTurnRands[iPlayer][index];
}

void CvGame::EventRandomizer_init()
{
	if(m_bEventInit)
		return;

	m_bEventInit = true;

	int seed = GC.getInitCore().getSyncRandSeed() % 52319761;

	if(m_iEventSeed==0)
		m_iEventSeed = seed;

	// Now generate a seed list for each player...
	int eventSeed = m_iEventSeed;
	for (int i = 0; i < MAX_CIV_PLAYERS; i++)
	{
		eventSeed = ((CJSRANDOM_A * eventSeed) + CJSRANDOM_C);
		unsigned __int64 us = eventSeed;

		for (int n=0;n<MAX_EVENT_TURNS*(MAX_EVENTS_PER_CIV_PER_TURN * MAX_ROLLS_PER_EVENT);n++)
		{
			us = ((CJSRANDOM_A * us) + CJSRANDOM_C);
			m_aiTurnRands[i][n] = us;
		}
	}

}
Add to bottom of CvGame::init below AI_init()
Code:
	EventRandomizer_init();
Add to CvGame::reset:
Code:
	m_iEventSeed = 0;
	m_bEventInit = false;
	for (int iJ = 0; iJ < MAX_PLAYERS; iJ++)
	{
		for (int n=0;n<MAX_EVENT_TURNS * MAX_EVENTS_PER_CIV_PER_TURN * MAX_ROLLS_PER_EVENT; n++)
		{
			m_aiTurnRands[iJ][n] = 0;

		}
	}
Add to CvGame::write:
Code:
	pStream->Write(m_iEventSeed);



Add to CvGame::read (IN SAME ORDER AND POSITION AS THE WRITE)

	pStream->Read(&m_iEventSeed);
In CvMessageControl.cpp:

Change: (add last parameter)
Code:
void CvMessageControl::sendEventTriggered(PlayerTypes ePlayer, EventTypes eEvent, int iEventTriggeredId, int iPositionInBuffer)
{
	gDLL->sendMessageData(new CvNetEventTriggered(ePlayer, eEvent, iEventTriggeredId, iPositionInBuffer));
}
In CvMessageData.cpp:

Change: (add last constructor call)
Code:
CvNetEventTriggered::CvNetEventTriggered(PlayerTypes ePlayer, EventTypes eEvent, int iEventTriggeredId, int iPositionInBuffer) : CvMessageData(GAMEMESSAGE_EVENT_TRIGGERED),
	m_ePlayer(ePlayer),
	m_eEvent(eEvent),
	m_iEventTriggeredId(iEventTriggeredId),
	m_iPositionInBuffer(iPositionInBuffer)
{
}
In CvNetEventTriggered::Execute() Change: (Last parameter)
Code:
	GET_PLAYER(m_ePlayer).applyEvent(m_eEvent, m_iEventTriggeredId, true, m_iPositionInBuffer);
In CvNetEventTriggered::putInBuffer(FDataStreamBase* pStream) add
Code:
	pStream->Write(m_iPositionInBuffer);
In CvNetEventTriggered::SetFromBuffer add (IN SAME POSITION):
Code:
	pStream->Read((int*)&m_iPositionInBuffer);
In CvPlayer.cpp:

Add to CvPlayer::CvPlayer:
Code:
	m_iEventBufferPos = 0;
Add:
Code:
int CvPlayer::generateEventSeeds(int inSeed)
{
	return 0;

} 
#define CJSRANDOM_A      (1103515245)
#define CJSRANDOM_C      (12345)
#define CJSRANDOM_SHIFT  (16)
int CvPlayer::getEventRollFromBuffer(int iWeight, const TCHAR* szBuf)
{
	int index = m_iEventBufferPos;

	m_iEventBufferPos++;
	unsigned short us = ((unsigned short)((((GC.getGame().getRandomEventBufferRoll(getID(), index) >> CJSRANDOM_SHIFT) & MAX_UNSIGNED_SHORT) * ((unsigned long)iWeight)) / (MAX_UNSIGNED_SHORT + 1)));

	TCHAR szOut[1024];
	
	if(GC.getGame().getActivePlayer()==getID())
		sprintf(szOut, "Turn %d - Local Player %d (%s) %s = %d of %d - roll #%d\n", GC.getGame().getGameTurn(), getID(), getName(), szBuf, us, iWeight, m_iEventBufferPos-1);
	else if(isHuman())
		sprintf(szOut, "Turn %d - Remote Player %d (%s) %s = %d of %d - roll #%d\n", GC.getGame().getGameTurn(), getID(), getName(), szBuf, us, iWeight, m_iEventBufferPos-1);
	else
		sprintf(szOut, "Turn %d - AI Player %d (%s) %s = %d of %d - roll #%d\n", GC.getGame().getGameTurn(), getID(), getName(), szBuf, us, iWeight, m_iEventBufferPos-1);
	gDLL->messageControlLog(szOut);
	
	return us;

}

void CvPlayer::setPlaceInEventBuffer(int val)
{
	m_iEventBufferPos = val;
}
int CvPlayer::getCurrentEventBufferPlace()
{
	int index = m_iEventBufferPos;

	return index;

}

Add to CvPlayer::read
Code:
	pStream->Read(&m_iEventBufferPos);
And in same place in write:
Code:
	pStream->Write(m_iEventBufferPos);
Select everything from:
Code:
	EventTriggeredData* CvPlayer::initTriggeredData(EventTriggerTypes eEventTrigger, bool bFire, int iCityId, int iPlotX, int iPlotY, PlayerTypes eOtherPlayer, int iOtherPlayerCityId, ReligionTypes eReligion, CorporationTypes eCorporation, int iUnitId, BuildingTypes eBuilding)

to above (not including):
Code:
	bool CvPlayer::splitEmpire(int iAreaId)

and so a replace on the selected text only:

Replace: GC.getGameINLINE().getSorenRandNum

with: getEventRollFromBuffer

Change:
Code:
	void CvPlayer::applyEvent(EventTypes eEvent, int iEventTriggeredId, bool bUpdateTrigger)
	{
		FAssert(eEvent != NO_EVENT);

To:

Code:
void CvPlayer::applyEvent(EventTypes eEvent, int iEventTriggeredId, bool bUpdateTrigger, int iPlaceInBuffer)
	{
		FAssert(eEvent != NO_EVENT);
	
		if(iPlaceInBuffer != -1)
			setPlaceInEventBuffer(iPlaceInBuffer);
In CyPlayer.cpp:

Add:
Code:
	int CyPlayer::getEventRollFromBuffer(int iPlayer, std::wstring szStr)
	{
		return m_pPlayer ? m_pPlayer->getEventRollFromBuffer(iPlayer, CvString(szStr.c_str())) : 0;
	}

In CyPlayerInterface.cpp:

Add:
Code:
		.def("getEventRollFromBuffer", &CyPlayer::getEventRollFromBuffer, "int (int max, std::wstring str)")
In CvRandomEventInterface.py

Replace all:
Code:
	gc.getGame().getSorenRandNum
With:
Code:
	gc.getPlayer(gc.getGame().getActivePlayer()).getEventRollFromBuffer
And ta da!!! Random Events should now work in multiplayer, Windows 7 / Vista or whatever!
 
Top Bottom