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:
Add in class:
CvMessageControl.h
Change: (add last argument)
CvMessageData.h
Change: (add last argument)
Add in class:
CvPlayer.h
Add in class:
CyPlayer.h
Add to class:
CvDLLButtonPopup.h
Change: (Add last parameter)
CvGame.cpp
Add to CvGame::CvGame:
Add to CvGame::~CvGame:
Add:
Add to bottom of CvGame::init below AI_init()
Add to CvGame::reset:
Add to CvGame::write:
In CvMessageControl.cpp:
Change: (add last parameter)
In CvMessageData.cpp:
Change: (add last constructor call)
In CvNetEventTriggered::Execute() Change: (Last parameter)
In CvNetEventTriggered:utInBuffer(FDataStreamBase* pStream) add
In CvNetEventTriggered::SetFromBuffer add (IN SAME POSITION):
In CvPlayer.cpp:
Add to CvPlayer::CvPlayer:
Add:
Add to CvPlayer::read
And in same place in write:
Select everything from:
to above (not including):
and so a replace on the selected text only:
Replace: GC.getGameINLINE().getSorenRandNum
with: getEventRollFromBuffer
Change:
To:
In CyPlayer.cpp:
Add:
In CyPlayerInterface.cpp:
Add:
In CvRandomEventInterface.py
Replace all:
With:
And ta da!!! Random Events should now work in multiplayer, Windows 7 / Vista or whatever!
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 // The buffers could probably be a LOT smaller, I'm just playing safe since it seems I have to precreate it and can't calculate in advance...
Code:
unsigned long getRandomEventBufferRoll(int iPlayer, int index);
void EventRandomizer_init();
int m_iEventSeed;
bool m_bEventInit;
unsigned long** m_aiTurnRands;
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;
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);
Add to class:
Code:
int getEventRollFromBuffer(int iPlayer, std::wstring szStr);
Change: (Add last parameter)
Code:
CvMessageControl::getInstance().sendEventTriggered(GC.getGameINLINE().getActivePlayer(), (EventTypes)pPopupReturn->getButtonClicked(), info.getData1(), GET_PLAYER(GC.getGameINLINE().getActivePlayer()).getCurrentEventBufferPlace());
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];
}
Code:
for (int i = 0; i < MAX_PLAYERS; i++)
{
SAFE_DELETE_ARRAY(m_aiTurnRands[i]);
}
SAFE_DELETE_ARRAY(m_aiTurnRands);
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;
}
}
}
Code:
EventRandomizer_init();
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;
}
}
Code:
pStream->Write(m_iEventSeed);
Add to CvGame::read (IN SAME ORDER AND POSITION AS THE WRITE)
pStream->Read(&m_iEventSeed);
Change: (add last parameter)
Code:
void CvMessageControl::sendEventTriggered(PlayerTypes ePlayer, EventTypes eEvent, int iEventTriggeredId, int iPositionInBuffer)
{
gDLL->sendMessageData(new CvNetEventTriggered(ePlayer, eEvent, iEventTriggeredId, iPositionInBuffer));
}
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)
{
}
Code:
GET_PLAYER(m_ePlayer).applyEvent(m_eEvent, m_iEventTriggeredId, true, m_iPositionInBuffer);
Code:
pStream->Write(m_iPositionInBuffer);
Code:
pStream->Read((int*)&m_iPositionInBuffer);
Add to CvPlayer::CvPlayer:
Code:
m_iEventBufferPos = 0;
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);
Code:
pStream->Write(m_iEventBufferPos);
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);
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)")
Replace all:
Code:
gc.getGame().getSorenRandNum
Code:
gc.getPlayer(gc.getGame().getActivePlayer()).getEventRollFromBuffer