Random number generation OOS error - is there a solution?

Niceness. I had glanced in that .cpp when 3.19 came out, but I didn't realize that the SetFrom/PutIn functions were actually going to communicate to the other machines. I thought that the specific communication portion was still out of our grasp, making this new file more nuisance than help :)
 
i just cant get it :scared:
i've searched for CvNetFoundReligion and for functions where it's used. Finally I've got to here:
Code:
	case BUTTONPOPUP_FOUND_RELIGION:
		CvMessageControl::getInstance().sendFoundReligion(GC.getGameINLINE().getActivePlayer(), (ReligionTypes)pPopupReturn->getButtonClicked(), (ReligionTypes)info.getData1());
		break;
There are no more uses of sendFoundReligion in SDK. So, when religion was founded in an ordinary way (by researching a tech) there is no need in sending message? I mean, i have to send message only in the cases when player do something unusual for the game, e.g. pushing self-made buttons, am i right? But when I add a new behavior to the function that is being launched when existing button got pushed, i do not need to send anything?
 
for example, i have added a code in CvUnit::Kill() function, resulted in getting gold on disbanding a unit. This function is being initialized by pushing disband button. There is a code in dll written by firaxis that sends messages to all players notifying them that unit X was disbanded. And CvUnit::Kill() run on their PCs and a sertain player get :gold: on all of those PCs (sounds like parallel worlds). So i do not need to send messages manually. But what if amount of :gold: is determined by using a random number? should i send a message informing other PCs that RNG was used?
 
You are correct. In the first case, all players receive a message that player X acquired a tech. When that tech ends up founding a religion for that player, the code is already running on every player's machine so there's no need to send another network message.

In your example as well, each machine is already running CvUnit::kill(), so they all give gold to the unit's owner. This is where the random number generator comes into play. It's not really random at all. It generates each number based on the previous number in a predictable sequence that only appears random to humans. As long as every machine grabs a random number to calculate the gold amount, the sequences remain synchronized.

Again, no need to send a new message.
 
OK, I think I've got most of this figured out, but had a few questions of why things are the way they are. It looks like there are 4 blocks within two files.

Block 1 in file CvMainInterface.py:

PHP:
		# Eliminates Religion from city
		if (inputClass.getNotifyCode() == 11 and inputClass.getData1() == 665 and inputClass.getData2() == 665):
			pPlot = CyMap( ).plot( g_pSelectedUnit.getX( ), g_pSelectedUnit.getY( ) )
			
			iMessageID = pGodsOfOld.m_iNetMessage_Inquisitor  #Question how does this work???
			iPlotX = pPlot.getX()
			iPlotY = pPlot.getY()
			iOwner = g_pSelectedUnit.getOwner()
			iUnitID = g_pSelectedUnit.getID()
			
			CyMessageControl( ).sendModNetMessage( iMessageID, iPlotX, iPlotY, iOwner, iUnitID )

The part that I have marked - is this defined in another file? Can you change the name at will?

Block 2 in file CvEventManager.py

PHP:
		#Net Messages
		self.m_iNetMessage_Inquisitor = 0
		self.m_iNetMessage_ProphetKi = 1
		self.m_iNetMessage_ProphetEnki = 2
		self.m_iNetMessage_ProphetEnlil = 3
		self.m_iNetMessage_ProphetNanna = 4
		self.m_iNetMessage_ProphetUtu = 5
		self.m_iNetMessage_ProphetAn = 6

Here the net messages are defined. Is there a reason for this added step? Could you just define iMessageID = 0, and then in the next block take you cue of iMessageID == 0 (then do the rest of the stuff)

Block 3 in file CvEventManager.py

PHP:
	def onModNetMessage(self, argsList):
		'Called whenever CyMessageControl().sendModNetMessage() is called - this is all for you modders!'
		
		iData1, iData2, iData3, iData4, iData5 = argsList
		
		print("Modder's net message!")
		
		CvUtil.pyPrint( 'onModNetMessage' )

		iMessageID = iData1
		
		#Inquisitor's Effect on a City
		if ( iMessageID == self.m_iNetMessage_Inquisitor ):
			
			iPlotX = iData2
			iPlotY = iData3
			iOwner = iData4
			iUnitID = iData5
			
			pPlot = CyMap( ).plot( iPlotX, iPlotY )
			pCity = pPlot.getPlotCity( )
			pPlayer = gc.getPlayer( iOwner )
			pUnit = pPlayer.getUnit( iUnitID )
			
			self.doInquisitorPersecution( pCity, pUnit )

This block takes the net message integers, converts them back into pointers, and then runs the command. Why is this broken into two blocks? Couldn't you just tack on the actual command instead of the self.doInquisitorPersecution( pCity, pUnit ) ?

Also, is it important that the arguements are pointers? could you pass the integers, and convert to pointers in the next block? (where the "action" is taking place


Block 4 in file CvEventManager.py
PHP:
	def doInquisitorPersecution( self, pCity, pUnit ):
		pPlayer = gc.getPlayer( pCity.getOwner( ) )
		
		#gets a list of all religions in the city except state religion
		lCityReligions = [ ]
		for iReligionLoop in range(gc.getNumReligionInfos( )):
			if pCity.isHasReligion( iReligionLoop ):
				if pPlayer.getStateReligion( ) != iReligionLoop:
					lCityReligions.append( iReligionLoop )

Here we finally get to the action. Does this block have to be placed in a specific location, or is it ok to put it at the end?

Thanks again!

If you think there's interest, and if my understanding turn out correct, I could clean this stuff up and post a "mini-tutorial"
 
The reason for defining named constants ("variables" that never change their value) is to make the code easier to read and work with later. If you read "if iMessageID == 4" you don't have to wonder, "Gee, which message was #6?"

In CvMainInterface there must be some code that looks up the event manager and assigns it to pGodsOfOld. This works, but I find it better design to define constants at the module-level so they are accessible with a mere import of the module. Also, name your constants IN_ALL_CAPS_WITH_UNDERSCORES so it's clear to everyone they are constants, e.g. NET_MESSAGE_GODS_INQUISITOR.

The use of a separate function for each message has a few benefits. First, it means these functions can easily be called from other places in the code should that be needed. For example, you might create a building that performs an automatic inquisition. Since the call to onBuildingBuilt() already happens on every computer, there's no need for a net message here. The onBildingBuilt() event handler could bypass the message ID -> function code and call the function directly.

Second, shorter functions make the code more readable. The more things a function does, the longer it takes to read and understand it as well as the space in your memory to store that information. A good tenant for a function is "do one thing only and do it well" and to keep them shorter than one screen in length.

Along these lines, the function that tests for each message has one purpose: dispatch a single net message. This requires three smaller related steps: 1. determine which message was sent, 2. look up the information needed (cities, units, etc) from the parameters of the message, and 3. call the appropriate handler function. Since 2 and 3 both require doing 1, it would be overkill to break this function up further given there are only seven messages.

While you could make the handler functions responsible for converting from the message's integer arguments to "pointers", that would mean the handlers couldn't be used from other parts of the mod without making those other parts convert to integers first. It also means the handlers have to know they are called from onNetMessage(). There's no advantage to doing that and two disadvantages off the top of my head, so I think they made the right choice here. onNetMessage() should be solely responsible for converting a net message into a call to a game function.

In Python it doesn't matter in what order you define your functions. All of the function definitions in a class such as CvEventManager are parsed and defined before any code is run, so they can refer to each other without worry about order. For all intents and purposes, order only matters inside a function.
 
OK, understood - The defining of constant is useful if you have a lot of net messages (like gods of old) for one or two its probably just as easy to set like this

iMessageID = 1
then in the onNetMessage
if ( iMessageID == 1 ):

Does any any other block of python have to be modified? (other than the 4 specified in my last post.)

One more thing, both CvEventManager.py and CvMainInterface.py have an
"import GodsOfOld"

But this file contains just two lines:

iPushButton = 0
iPushedButtonUnit = 0

What exactly does this accomplish?
 
It is easy enough to assign a constant integer in your __init__ and use that instead of "1". It is helpful if, later on, you want to find all the places where this value is used. Searching for "1" is hopeless, it will find a lot of things. If you have defined an integer with a nice name, searching is easy. Also, while you only plan a couple of messages now, things tend to grow, and someday you will type in "2" when you meant "3".

The globals in GodsOfOld.py are a bit of a hack, in my opinion. If you search for one of these names in assets/python/screens/CvMainInterface.py, where the action buttons are used, you will see that these globals are set when the action button is pushed, and then used in CvEventManager.onPlotPicked. I have not investigated into the details. I would have thought that onPlotPicked would allow you to pass along some data. But, either the GOO programmer overlooked this, or there is no way. So instead they added some globals to pass the needed data. IMHO, globals are like "goto". You should never use them, unless you can prove there is no other way to do it.
 
As davidallen said, always use constants. Their value far outweighs their infinitesimal "cost" (extra typing and doing a name lookup). You don't even need to put them into an __init__ function; declare them at the top of the module with "gc":

Code:
# My cool mod

from CvPythonExtensions import *
import CvUtil
...

gc = CyGlobalContext()

# net messages
NET_MESSAGE_SMITE = 1

...

As for those globals, I'd bet that you cannot pass any information in the plot picking event. You basically tell the interface to go into plot-picking mode, and then each tile you click gets passed as an event. Since that event will be fired for other times you pick a plot (e.g. air strike), it needs a way to tell it why it's doing a plot pick. It's hacky, but it works.
 
Back
Top Bottom