Messages with python

Alrik2002

Warlord
Joined
May 24, 2012
Messages
214
Location
Berlin, Germany
Hi,

I´ve merged the agriculture Mod in my Mod. In the CvEventManager get.ActivePlayer is used to send messages. But for multiplayer games every human player would get the message, since everyone is the active player at his computer.

How could I change this so that only the person, which unit has build the improvement will get the message? Alternative it could be the person to which the culture the plot belongs.

Here is the code which is used now, the lines with the messages are red:
Code:
def onImprovementBuilt(self, argsList):
		'Improvement Built'
		iImprovement, iX, iY = argsList

## Terraform Begin (avain)

		if(iImprovement==gc.getInfoTypeForString('IMPROVEMENT_FOREST')):
			pPlot = CyMap().plot(iX, iY)
			pPlot.setBonusType(-1)		
##			CyInterface().addMessage(CyGame().getActivePlayer(),True,25,'A tree nursery has matured into a Forest!','AS2D_DISCOVERBONUS',1,'Art/Interface/Buttons/TerrainFeatures/Forest.dds',ColorTypes(8),iX,iY,True,True)
			pPlot.setFeatureType(gc.getInfoTypeForString('FEATURE_FOREST'), 0)
			pPlot.setImprovementType(-1)

###neuBeginn Popup1 terraforming
		pPlot = CyMap().plot(iX, iY)
		if iImprovement == gc.getInfoTypeForString('IMPROVEMENT_LANDAUFSCHUETTUNG'):
			pPlot.setPlotType(PlotTypes.PLOT_LAND, True, True)
		    	pPlot.setImprovementType(-1)
			iPlayerNum = 0
			for iPlayer in range(gc.getMAX_PLAYERS()):
				player = gc.getPlayer(iPlayer)
				if player.isAlive():
					iPlayerNum = iPlayerNum + 1
					if player.isHuman():
						CyCamera().JustLookAtPlot( CyMap().plot(iX, iY))
						popupInfo = CyPopupInfo()
						popupInfo.setButtonPopupType(ButtonPopupTypes.BUTTONPOPUP_PYTHON)
						popupInfo.setText(CyTranslator().getText("TXT_KEY_POPUP_SELECT_TERRAINTYPE",()))
						popupInfo.setData1(iPlayer)
						popupInfo.setData2(iX)
						popupInfo.setData3(iY)
						popupInfo.setOnClickedPythonCallback("SelectTerrainType")
						popupInfo.addPythonButton(CyTranslator().getText("TXT_KEY_POPUP_GRAS", ()), "")
						popupInfo.addPythonButton(CyTranslator().getText("TXT_KEY_POPUP_EBENE", ()), "")
						popupInfo.addPythonButton(CyTranslator().getText("TXT_KEY_POPUP_WÜSTE", ()), "")
						popupInfo.addPythonButton(CyTranslator().getText("TXT_KEY_POPUP_TUNDRA", ()), "")
						popupInfo.addPythonButton(CyTranslator().getText("TXT_KEY_POPUP_SCHNEE", ()), "")
						popupInfo.addPopup(iPlayer)
###Ende Popup1
		pPlot = CyMap().plot(iX, iY)
		if iImprovement == gc.getInfoTypeForString('IMPROVEMENT_LANDVERAENDERUNG'):
			pPlot.setImprovementType(-1)
			iPlayerNum = 0
			for iPlayer in range(gc.getMAX_PLAYERS()):
				player = gc.getPlayer(iPlayer)
				if player.isAlive():
					iPlayerNum = iPlayerNum + 1
					if player.isHuman():
						CyCamera().JustLookAtPlot( CyMap().plot(iX, iY))
						popupInfo = CyPopupInfo()
						popupInfo.setButtonPopupType(ButtonPopupTypes.BUTTONPOPUP_PYTHON)
						popupInfo.setText(CyTranslator().getText("TXT_KEY_POPUP_SELECT_TERRAINTYPE",()))
						popupInfo.setData1(iPlayer)
						popupInfo.setData2(iX)
						popupInfo.setData3(iY)
						popupInfo.setOnClickedPythonCallback("SelectTerrainType")
						popupInfo.addPythonButton(CyTranslator().getText("TXT_KEY_POPUP_GRAS", ()), "")
						popupInfo.addPythonButton(CyTranslator().getText("TXT_KEY_POPUP_EBENE", ()), "")
						popupInfo.addPythonButton(CyTranslator().getText("TXT_KEY_POPUP_WÜSTE", ()), "")
						popupInfo.addPythonButton(CyTranslator().getText("TXT_KEY_POPUP_TUNDRA", ()), "")
						popupInfo.addPythonButton(CyTranslator().getText("TXT_KEY_POPUP_SCHNEE", ()), "")
						popupInfo.addPopup(iPlayer)
###Ende Popups
		if iImprovement == gc.getInfoTypeForString('IMPROVEMENT_LANDABTRAGUNG'):
			pPlot.setPlotType(PlotTypes.PLOT_OCEAN, True, True)
		    	pPlot.setImprovementType(-1)
		if iImprovement == gc.getInfoTypeForString('IMPROVEMENT_PLATTMACHEN'):
			pPlot.setPlotType(PlotTypes.PLOT_LAND, True, True)
		    	pPlot.setImprovementType(-1)
###Ende alles

# Aggriculture Mod
		# Purpose: Places a agricultural bonus on plot upon worker
		# completion of an improvement (Corn, Rice or Wheat)
		iPlayerID = gc.getPlayer(CyGame().getActivePlayer()).getID()			
		player = gc.getPlayer(iPlayerID)
		
		if (iImprovement == gc.getInfoTypeForString('IMPROVEMENT_CORN')) :
			pPlot = CyMap().plot(iX, iY)
			pPlot.setImprovementType(-1)
			pPlot.setBonusType(gc.getInfoTypeForString("BONUS_CORN"))			
			
			if (player.isAlive() and player.isHuman()):
				[COLOR="Red"]CyInterface().addMessage(CyGame().getActivePlayer(),True,25,'Mais wurde gepflanzt!','AS2D_DISCOVERBONUS',1,'Art/Interface/Buttons/general/happy_person.dds',ColorTypes(8),iX,iY,False,False)[/COLOR]

		if (iImprovement == gc.getInfoTypeForString('IMPROVEMENT_RICE')) :
			pPlot = CyMap().plot(iX, iY)
			pPlot.setImprovementType(0)
			pPlot.setBonusType(gc.getInfoTypeForString("BONUS_RICE"))
			
			if (player.isAlive() and player.isHuman()):
				[COLOR="Red"]CyInterface().addMessage(CyGame().getActivePlayer(),True,25,'Reis wurde gepflanzt!','AS2D_DISCOVERBONUS',1,'Art/Interface/Buttons/general/happy_person.dds',ColorTypes(8),iX,iY,False,False)[/COLOR]

		if (iImprovement == gc.getInfoTypeForString('IMPROVEMENT_WHEAT')) :
			pPlot = CyMap().plot(iX, iY)
			pPlot.setImprovementType(0)
			pPlot.setBonusType(gc.getInfoTypeForString("BONUS_WHEAT"))
			
			if (player.isAlive() and player.isHuman()):
				[COLOR="Red"]CyInterface().addMessage(CyGame().getActivePlayer(),True,25,'Weizen wurde gepflanzt!','AS2D_DISCOVERBONUS',1,'Art/Interface/Buttons/general/happy_person.dds',ColorTypes(8),iX,iY,False,False)[/COLOR]
# End Aggriculture Mod		
		
		if (not self.__LOG_IMPROVEMENT):
			return
		CvUtil.pyPrint('Improvement %s was built at %d, %d'
			%(PyInfo.ImprovementInfo(iImprovement).getDescription(), iX, iY))

Thank you very much!!!
 
If you want that kind of code to work in multiplayer, you will also need to make sure that the popup is only displayed to the owner and its result is sent through a net message (as the callback from a popup is unsynced).
 
Thank you both for your help.

@The J: Since the Improvement can only be build in your own territory it will work with CyMap().plot(iX,iY).getOwner().

@AIAndy: How should the code look like? Sorry, but I´m lucky if I understand what a python code is doing when I look at it.:blush:

In another Mod I´ve merged is onModNetMessage used to send the result to the other players. The code is at the end of this message. Is that the way it has to be?

Code:
from CvPythonExtensions import *
import CvUtil

gc = CyGlobalContext()

class SettlersEventManager:
	def __init__(self, eventManager):
		eventManager.addEventHandler("cityBuilt", self.onCityBuilt)
		eventManager.addEventHandler("ModNetMessage", self.onModNetMessage)

	def onCityBuilt(self, argsList):
		'City Built'
		city = argsList[0]

		if city.getOwner() == gc.getGame().getActivePlayer() :
                        pUnit = CyInterface().getHeadSelectedUnit() # note that if the unit is grouped,headUnit is not forced to be the settler
                        if pUnit:
                                iOwner = city.getOwner()
                                iCityID = city.getID()
                                iUnitClass = pUnit.getUnitClassType()
                                [COLOR="Red"]CyMessageControl().sendModNetMessage(167, iOwner, iCityID, iUnitClass, -1) # be sure 167 is not use for another mod net message, the best should be to add an id in CvUtil like for events[/COLOR]
                        
[COLOR="Red"]	def onModNetMessage(self, argsList):
		'Called whenever CyMessageControl().sendModNetMessage() is called - this is all for you modders!'
		
		iData1, iData2, iData3, iData4, iData5 = argsList
		if iData1 == 167 :
                        addBuildings(iData2, iData3, iData4)

		CvUtil.pyPrint( 'onModNetMessage' )[/COLOR]

def addBuildings(iOwner, iCityID, iUnitClass):
        city = gc.getPlayer(iOwner).getCity(iCityID)
        pPlayer = gc.getPlayer(iOwner)
		
        #if iUnitClass == gc.getInfoTypeForString("UNITCLASS_SETTLER"):
        #	## Do Nothing
        #	pass

        if iUnitClass == gc.getInfoTypeForString("UNITCLASS_COLONIST"):
                city.setPopulation(3)
                if pPlayer.hasTrait(gc.getInfoTypeForString("TRAIT_EXPANSIVE")):
                        city.changePopulation(1)
                _addBuilding(city, "BUILDINGCLASS_BARRACKS")
                _addBuilding(city, "BUILDINGCLASS_GRANARY")
                _addBuilding(city, "BUILDINGCLASS_FORGE")
                _addBuilding(city, "BUILDINGCLASS_MARKET")
                if city.plot().isCoastalLand():
                        _addBuilding(city, "BUILDINGCLASS_HARBOR")
                        _addBuilding(city, "BUILDINGCLASS_LIGHTHOUSE")
                        _addBuilding(city, "BUILDINGCLASS_FISHERMAN_HUT")

        elif iUnitClass == gc.getInfoTypeForString("UNITCLASS_PIONEER"):
                city.setPopulation(4)
                if pPlayer.hasTrait(gc.getInfoTypeForString("TRAIT_EXPANSIVE")):
                        city.changePopulation(1)
                _addBuilding(city, "BUILDINGCLASS_GARRISON")
                _addBuilding(city, "BUILDINGCLASS_GRANARY")
                _addBuilding(city, "BUILDINGCLASS_FORGE")
                _addBuilding(city, "BUILDINGCLASS_COURTHOUSE")
                _addBuilding(city, "BUILDINGCLASS_MARKET")
                _addBuilding(city, "BUILDINGCLASS_STABLE")
                _addBuilding(city, "BUILDINGCLASS_GROCER")
                _addBuilding(city, "BUILDINGCLASS_DOCTOR")
                _addBuilding(city, "BUILDINGCLASS_BANK")
                _addBuilding(city, "BUILDINGCLASS_LIBRARY")
                if city.plot().isCoastalLand():
                        _addBuilding(city, "BUILDINGCLASS_PORT")
                        _addBuilding(city, "BUILDINGCLASS_LIGHTHOUSE")
                        _addBuilding(city, "BUILDINGCLASS_FISHERMAN_HUT")

        elif iUnitClass == gc.getInfoTypeForString("UNITCLASS_ARCHITECT"):
        	_addBuilding(city, "BUILDINGCLASS_BARRACKS")
        	_addBuilding(city, "BUILDINGCLASS_GRANARY")
        	_addBuilding(city, "BUILDINGCLASS_COURTHOUSE")
        	_addBuilding(city, "BUILDINGCLASS_MARKET")
        	_addBuilding(city, "BUILDINGCLASS_STABLE")
        	_addBuilding(city, "BUILDINGCLASS_GROCER")
        	_addBuilding(city, "BUILDINGCLASS_AQUEDUCT")
        	_addBuilding(city, "BUILDINGCLASS_BANK")
        	_addBuilding(city, "BUILDINGCLASS_FORGE")
        	_addBuilding(city, "BUILDINGCLASS_LIBRARY")
        	_addBuilding(city, "BUILDINGCLASS_JAIL")
        	_addBuilding(city, "BUILDINGCLASS_OBSERVATORY")
        	_addBuilding(city, "BUILDINGCLASS_THEATRE")
        	_addBuilding(city, "BUILDINGCLASS_CUSTOM_HOUSE")
        	_addBuilding(city, "BUILDINGCLASS_INDUSTRIAL_PARK")
        	_addBuilding(city, "BUILDINGCLASS_LEVEE")
        	_addBuilding(city, "BUILDINGCLASS_PUBLIC_TRANSPORTATION")
        	if city.plot().isCoastalLand():
        		_addBuilding(city, "BUILDINGCLASS_HARBOR")
        		_addBuilding(city, "BUILDINGCLASS_LIGHTHOUSE")

def _addBuilding(pCity, szBuilding):
	iBuilding = gc.getInfoTypeForString(szBuilding)
	iUniqueBuilding = gc.getCivilizationInfo(gc.getPlayer(pCity.getOwner()).getCivilizationType()).getCivilizationBuildings(iBuilding)
	if iUniqueBuilding > -1:
		if pCity.canConstruct(iUniqueBuilding, False, True, False):
			pCity.setNumRealBuilding(iUniqueBuilding, pCity.getNumRealBuilding(iUniqueBuilding) + 1)
 
@AIAndy: How should the code look like? Sorry, but I´m lucky if I understand what a python code is doing when I look at it.:blush:

In another Mod I´ve merged is onModNetMessage used to send the result to the other players. The code is at the end of this message. Is that the way it has to be?
Yes, that is the kind of message you need to send in the popup result callback.
 
Ok. I tried to erase the getActivePlayer and make sure, that the Popups will only get to the owner of the plot. In my tests it worked well, but unfortunatly I had not the possibility to test it in a network game. So it would be very nice if you could take a look at it, if it should work correct or if you see any problems in the code like it is now.

I know that I still have to add the ModNetMessages and I´m sure that I will need some help there too.:blush:

Here is the code like it is now. The lines I´ve changed are red:
Code:
	def onImprovementBuilt(self, argsList):
		'Improvement Built'
		iImprovement, iX, iY = argsList
	
## Terraform Begin (avain)

		if(iImprovement==gc.getInfoTypeForString('IMPROVEMENT_FOREST')):
			pPlot = CyMap().plot(iX, iY)
			pPlot.setBonusType(-1)		
##			CyInterface().addMessage(CyMap().plot(iX,iY).getOwner(),True,25,'A tree nursery has matured into a Forest!','AS2D_DISCOVERBONUS',1,'Art/Interface/Buttons/TerrainFeatures/Forest.dds',ColorTypes(8),iX,iY,True,True)
			pPlot.setFeatureType(gc.getInfoTypeForString('FEATURE_FOREST'), 0)
			pPlot.setImprovementType(-1)

###neuBeginn Popup1 terraforming
		pPlot = CyMap().plot(iX, iY)
		if iImprovement == gc.getInfoTypeForString('IMPROVEMENT_LANDAUFSCHUETTUNG'):
			pPlot.setPlotType(PlotTypes.PLOT_LAND, True, True)
		    	pPlot.setImprovementType(-1)
[COLOR="Red"]##neu Alrik Beginn
			iPlayer = pPlot.getOwner()
			pPlayer = gc.getPlayer(iPlayer)
			if (pPlayer.isAlive() and pPlayer.isHuman()):
##neu Alrik Ende				
##			iPlayerNum = 0
##			for iPlayer in range(gc.getMAX_PLAYERS()):
##				player = gc.getPlayer(iPlayer)			
##				if player.isAlive():
##					iPlayerNum = iPlayerNum + 1
##					if player.isHuman():[/COLOR]
				CyCamera().JustLookAtPlot( CyMap().plot(iX, iY))
				popupInfo = CyPopupInfo()
				popupInfo.setButtonPopupType(ButtonPopupTypes.BUTTONPOPUP_PYTHON)
				popupInfo.setText(CyTranslator().getText("TXT_KEY_POPUP_SELECT_TERRAINTYPE",()))
				popupInfo.setData1(iPlayer)
				popupInfo.setData2(iX)
				popupInfo.setData3(iY)
				popupInfo.setOnClickedPythonCallback("SelectTerrainType")
				popupInfo.addPythonButton(CyTranslator().getText("TXT_KEY_POPUP_GRAS", ()), "")
				popupInfo.addPythonButton(CyTranslator().getText("TXT_KEY_POPUP_EBENE", ()), "")
				popupInfo.addPythonButton(CyTranslator().getText("TXT_KEY_POPUP_WÜSTE", ()), "")
				popupInfo.addPythonButton(CyTranslator().getText("TXT_KEY_POPUP_TUNDRA", ()), "")
				popupInfo.addPythonButton(CyTranslator().getText("TXT_KEY_POPUP_SCHNEE", ()), "")
				popupInfo.addPopup(iPlayer)
###Ende Popup1
		pPlot = CyMap().plot(iX, iY)
		if iImprovement == gc.getInfoTypeForString('IMPROVEMENT_LANDVERAENDERUNG'):
			pPlot.setImprovementType(-1)
[COLOR="Red"]##neu Alrik Beginn			
			iPlayer = pPlot.getOwner()
			pPlayer = gc.getPlayer(iPlayer)
			if (pPlayer.isAlive() and pPlayer.isHuman()):
##neu Alrik Ende			
##			iPlayerNum = 0
##			for iPlayer in range(gc.getMAX_PLAYERS()):
##				player = gc.getPlayer(iPlayer)
##				if player.isAlive():
##					iPlayerNum = iPlayerNum + 1
##					if player.isHuman():[/COLOR]
				CyCamera().JustLookAtPlot( CyMap().plot(iX, iY))
				popupInfo = CyPopupInfo()
				popupInfo.setButtonPopupType(ButtonPopupTypes.BUTTONPOPUP_PYTHON)
				popupInfo.setText(CyTranslator().getText("TXT_KEY_POPUP_SELECT_TERRAINTYPE",()))
				popupInfo.setData1(iPlayer)
				popupInfo.setData2(iX)
				popupInfo.setData3(iY)
				popupInfo.setOnClickedPythonCallback("SelectTerrainType")
				popupInfo.addPythonButton(CyTranslator().getText("TXT_KEY_POPUP_GRAS", ()), "")
				popupInfo.addPythonButton(CyTranslator().getText("TXT_KEY_POPUP_EBENE", ()), "")
				popupInfo.addPythonButton(CyTranslator().getText("TXT_KEY_POPUP_WÜSTE", ()), "")
				popupInfo.addPythonButton(CyTranslator().getText("TXT_KEY_POPUP_TUNDRA", ()), "")
				popupInfo.addPythonButton(CyTranslator().getText("TXT_KEY_POPUP_SCHNEE", ()), "")
				popupInfo.addPopup(iPlayer)
###Ende Popups
		if iImprovement == gc.getInfoTypeForString('IMPROVEMENT_LANDABTRAGUNG'):
			pPlot.setPlotType(PlotTypes.PLOT_OCEAN, True, True)
		    	pPlot.setImprovementType(-1)
		if iImprovement == gc.getInfoTypeForString('IMPROVEMENT_PLATTMACHEN'):
			pPlot.setPlotType(PlotTypes.PLOT_LAND, True, True)
		    	pPlot.setImprovementType(-1)
###Ende alles

# Aggriculture Mod
		# Purpose: Places a agricultural bonus on plot upon worker
		# completion of an improvement (Corn, Rice or Wheat)
[COLOR="Red"]##		iPlayerID = gc.getPlayer(CyGame().getActivePlayer()).getID()			
##		player = gc.getPlayer(iPlayerID)
##neu Alrik Beginn			
		iPlayer = pPlot.getOwner()
		pPlayer = gc.getPlayer(iPlayer)
##neu Alrik Ende[/COLOR]
		
		if (iImprovement == gc.getInfoTypeForString('IMPROVEMENT_CORN')) :
			pPlot = CyMap().plot(iX, iY)
			pPlot.setImprovementType(-1)
			pPlot.setBonusType(gc.getInfoTypeForString("BONUS_CORN"))	
			
[COLOR="Red"]##			if (player.isAlive() and player.isHuman()):
##neu Alrik Beginn			
			if (pPlayer.isAlive() and pPlayer.isHuman()):
##neu Alrik Ende[/COLOR]			
				CyInterface().addMessage([COLOR="Red"]CyMap().plot(iX,iY).getOwner()[/COLOR],True,25,'Mais wurde gepflanzt!','AS2D_DISCOVERBONUS',1,'Art/Interface/Buttons/general/happy_person.dds',ColorTypes(8),iX,iY,False,False)

		if (iImprovement == gc.getInfoTypeForString('IMPROVEMENT_RICE')) :
			pPlot = CyMap().plot(iX, iY)
			pPlot.setImprovementType(0)
			pPlot.setBonusType(gc.getInfoTypeForString("BONUS_RICE"))
			
[COLOR="Red"]##			if (player.isAlive() and player.isHuman()):
##neu Alrik Beginn			
			if (pPlayer.isAlive() and pPlayer.isHuman()):
##neu Alrik Ende[/COLOR]
				CyInterface().addMessage([COLOR="Red"]CyMap().plot(iX,iY).getOwner()[/COLOR],True,25,'Reis wurde gepflanzt!','AS2D_DISCOVERBONUS',1,'Art/Interface/Buttons/general/happy_person.dds',ColorTypes(8),iX,iY,False,False)

		if (iImprovement == gc.getInfoTypeForString('IMPROVEMENT_WHEAT')) :
			pPlot = CyMap().plot(iX, iY)
			pPlot.setImprovementType(0)
			pPlot.setBonusType(gc.getInfoTypeForString("BONUS_WHEAT"))
			
[COLOR="Red"]##			if (player.isAlive() and player.isHuman()):
##neu Alrik Beginn			
			if (pPlayer.isAlive() and pPlayer.isHuman()):
##neu Alrik Ende[/COLOR]
				CyInterface().addMessage([COLOR="Red"]CyMap().plot(iX,iY).getOwner()[/COLOR],True,25,'Weizen wurde gepflanzt!','AS2D_DISCOVERBONUS',1,'Art/Interface/Buttons/general/happy_person.dds',ColorTypes(8),iX,iY,False,False)
# End Aggriculture Mod		
		
		if (not self.__LOG_IMPROVEMENT):
			return
		CvUtil.pyPrint('Improvement %s was built at %d, %d'
			%(PyInfo.ImprovementInfo(iImprovement).getDescription(), iX, iY))
 
Some redundancies in the variables, but else it looks IMHO okay.
If you want to make sure that it works in MP: Deactivate it in MP :D. Most people will not play a mod in MP unless they've played it in SP before, and then they should know the mechanics. So would probably not hurt to deactivate the popups for MP games.
 
Some redundancies in the variables, but else it looks IMHO okay.
If you want to make sure that it works in MP: Deactivate it in MP :D. Most people will not play a mod in MP unless they've played it in SP before, and then they should know the mechanics. So would probably not hurt to deactivate the popups for MP games.
If I get this right then the popup lets you select the terrain you want to terraform to and in that case deactivating them is not an option.
Btw, if you have a decent computer, then you can do some MP testing by running two instances of Civ4 on your computer and playing a network game in between them. To do that you just need to add a command line option called multiple to the shortcuts you start the game from.
 
:goodjob:

Thx both of you. Yes I´ve seen some parts which are used in several places. I´ll try to put them at the beginning and test again. I´ve a second computer here to test it in MP, but before this, I´ve to explain it to my wife.:mischief:

For the ModNetMessages I´m a little bit confused, because I don´t really know what they do exactly. F.e. the message that I´ve posted above:

Code:
def onModNetMessage(self, argsList):
		'Called whenever CyMessageControl().sendModNetMessage() is called - this is all for you modders!'
		
		iData1, iData2, iData3, iData4, iData5 = argsList
		if iData1 == 167 :
                        [COLOR="Red"]addBuildings(iData2, iData3, iData4)[/COLOR]

		CvUtil.pyPrint( 'onModNetMessage' )

  • What are iData1, iData2, iData3, iData4, iData5?
  • Is iData1 just the number of the specific Message which is used in other codes to send this message?
  • If yes, where do I find the already used numbers, so that I don´t use a number twice?
  • I believe the red part is the content of the message. Correct?
  • If yes, what does it send (iData2, iData3, iData4)?
  • What is iDate5 for?
  • In which cases I have to use these messages? For example in the code, the message is only about the buildings. What is with the population? Should this information be part of the message too?
  • If not, why not?

I know, that are many questions but the answers would hopefully help me to understand.

@the J: You could send me a pm in german too, if you want to.:)
 
:goodjob:

Thx both of you. Yes I´ve seen some parts which are used in several places. I´ll try to put them at the beginning and test again. I´ve a second computer here to test it in MP, but before this, I´ve to explain it to my wife.:mischief:

For the ModNetMessages I´m a little bit confused, because I don´t really know what they do exactly. F.e. the message that I´ve posted above:

Code:
def onModNetMessage(self, argsList):
		'Called whenever CyMessageControl().sendModNetMessage() is called - this is all for you modders!'
		
		iData1, iData2, iData3, iData4, iData5 = argsList
		if iData1 == 167 :
                        [COLOR="Red"]addBuildings(iData2, iData3, iData4)[/COLOR]

		CvUtil.pyPrint( 'onModNetMessage' )

  • What are iData1, iData2, iData3, iData4, iData5?
  • Is iData1 just the number of the specific Message which is used in other codes to send this message?
  • If yes, where do I find the already used numbers, so that I don´t use a number twice?
  • I believe the red part is the content of the message. Correct?
  • If yes, what does it send (iData2, iData3, iData4)?
  • What is iDate5 for?
  • In which cases I have to use these messages? For example in the code, the message is only about the buildings. What is with the population? Should this information be part of the message too?
  • If not, why not?

I know, that are many questions but the answers would hopefully help me to understand.

@the J: You could send me a pm in german too, if you want to.:)
In general the 5 data that are sent with the message are whatever you want them to be but all Python mods share that message so usually the first one is used to identify that it was you who sent the message. You will need to look through the other occurances of ModNetMessage to find out which IDs are already used.

In general you have to use a message whenever you want to cause an effect on the game state (anything that has an effect on the game beyond simple UI displays). You send the message from unsynced local code like the callback from a button pressed or the result of a popup. Then that message is sent to all computers and executed at the same time (including the one you send it from). So the event from the message is synced and you can do stuff like creating buildings or units or changing population or whatever from there and it will not desync the game.
 
OK. Step by step I think I understand. Thank you for that!

Where are the iData´s defined? Are this the things in the brackets?

For example:

CyMessageControl().sendModNetMessage(167, iOwner, iCityID, iUnitClass, -1)

iData1 = 167
iData2 = iOwner
iData3 = iCityID
iData4 = iUnitClass
iData5 = -1

Does the ModNetMessage of my code with the content addBuildings(iData2, iData3, iData4) send all the results of the whole function "def addBuildings", so that the added buildings and the change of the population would be send to the other players?
 
OK. Step by step I think I understand. Thank you for that!

Where are the iData´s defined? Are this the things in the brackets?

For example:

CyMessageControl().sendModNetMessage(167, iOwner, iCityID, iUnitClass, -1)

iData1 = 167
iData2 = iOwner
iData3 = iCityID
iData4 = iUnitClass
iData5 = -1
Correct.

Does the ModNetMessage of my code with the content addBuildings(iData2, iData3, iData4) send all the results of the whole function "def addBuildings", so that the added buildings and the change of the population would be send to the other players?
What it sends is just the iData, but on all computers that triggers the event that calls onModNetMessage and then you can do anything you like with the iData like calling addBuilding, changing population or anything.
 
The CyMessageControl().sendModNetMessage send the message. The message is just the 5 values used as arguments. The only thing you can send for each value is an integer.

The onModNetMessage is what happens when the message is received. Your addBuildings function is just what you are doing with the 3 out of the 5 integers that have been received. While your computer is doing this for you, all of the other players' computers are doing it too. That keeps the game's data the same for everyone.

You have to use the mod net message any time a human does something that the game itself does not already transmit the data for. This is generally the result of clicking on either something in a pop-up you have created or some other button which has its effects defined in Python (although if you add new things in the DLL you can also have the same issue). When you pick something off a list on a pop-up the only computer that knows about it is yours. You have to send the result to everyone so the changes can be made everywhere or you will go out of synch. This is not an issue for things done by the AI since the same AI code is running on all the computers so they will all do the same thing. It's just those pesky humans who mess things up.
 
:goodjob:

I think I got it. Just to make it sure:
  • Every time you have a game stat change, which is caused by a human action using a button which effects are defined with python, you have to send a ModNetMessage to make sure the game stats are not going out of synch.
  • The code will be executed on all computers including mine at the same time.
  • For this I have to give the other computers the relevant values (iData1, iData2, iData3, iData4, iData5) for running the codes via the ModNetMessage, before running the code on my computer.
  • This is done with CyMessageControl().sendModNetMessage.
  • The values can only be integers.
  • The value iData1 has to be a unique number so that the receiving computers are able to know which codes they should run because of the ModNetMessage.
  • When the computers receive the ModNetMessage they run the function "onModNetMessage(self, argsList):"
  • There has to be a check for the unique number in iData1. If it matches, the following functions with the values of the received ModNetMessage are run on every computer in the network game at the same time.

I hadn´t realized the bold part before, but I think this is important. Right?
 
Ok. Here is my first attempt for the Immigration Mod.

I´ve changed the following codes. Red deleted, green added or changed.

CvMainInterface.py, where the point is, where the player initiates the immigration with a button.
Code:
	def handleInput (self, inputClass):

# Immigrant Mod
		if (inputClass.getNotifyCode() == 11 and inputClass.getData1() == 680 and inputClass.getData2() == 680):			
			self.pPushedButtonUnit = g_pSelectedUnit		
			iOwner = g_pSelectedUnit.getOwner()
			iUnitID = g_pSelectedUnit.getID()
[COLOR="Red"]#			pPlayer = gc.getPlayer(iOwner)
#			pUnit = pPlayer.getUnit(iUnitID)[/COLOR]
[COLOR="DarkGreen"]## Alrik ModNetMessage Start			
			CyMessageControl().sendModNetMessage(168, iOwner, iUnitID, -1, -1) # be sure 168 is not use for another mod net message, the best should be to add an id in CvUtil like for events
## Alrik ModNetMessage Ende[/COLOR]			
[COLOR="Red"]#			Immigration.doJoinCity(pUnit)[/COLOR]
			CyInterface().setDirty(InterfaceDirtyBits.SelectionButtons_DIRTY_BIT, True)
# End Immigrant Mod

In the Immigration.py
Code:
[COLOR="DarkGreen"]## Alrik Start
class ImmigrationEventManager:
	def __init__(self, eventManager):
		eventManager.addEventHandler("ModNetMessage", self.onModNetMessage)
                       
	def onModNetMessage(self, argsList):
		'Called whenever CyMessageControl().sendModNetMessage() is called - this is all for you modders!'
		
		iData1, iData2, iData3, iData4, iData5 = argsList
		if iData1 == 168 :
                        doJoinCity(iData2, iData3)

		CvUtil.pyPrint( 'onModNetMessage' )
## Alrik Ende[/COLOR]

# ###################################
#	Begin Immigration Functions		#
# ###################################

def doImmigrantPlacementAI(pUnit, pCity):
# Orion's Immigration Mod
	iOwner = pUnit.getOwner()
	pPlayer = gc.getPlayer(iOwner)
	MyPopulation = pCity.getPopulation()
	
	if MyPopulation > 0 and MyPopulation < 7:
		if pUnit.getX() != pCity.getX() or pUnit.getY() != pCity.getY():
			pUnit.getGroup().pushMission(MissionTypes.MISSION_MOVE_TO, pCity.getX(), pCity.getY(), 0, False, True, MissionAITypes.NO_MISSIONAI, pUnit.plot(), pUnit)
			#CyInterface().addImmediateMessage("Push Immigrant", "")
			
		else:
			#CyInterface().addImmediateMessage("Do Join City", "")
			doJoinCity[COLOR="DarkGreen"]AI[/COLOR](pUnit)
# Orion's Immigration Mod			
				
				
def doJoinCity[COLOR="DarkGreen"](iOwner, iUnitID)[/COLOR]:
	# Orion's Immigration Mod
[COLOR="Red"]#	iOwner = pUnit.getOwner()[/COLOR]
	pPlayer = gc.getPlayer(iOwner)
	[COLOR="DarkGreen"]pUnit = pPlayer.getUnit(iUnitID)[/COLOR]
	iPlotX = pUnit.getX()
	iPlotY = pUnit.getY()
	pPlot = CyMap( ).plot( pUnit.getX( ), pUnit.getY( ) )
	pCity = pPlot.getPlotCity()
	NewCityPopulation = pCity.getPopulation() + 1
	pCity.setPopulation(NewCityPopulation)	
	if pPlayer.isHuman():
		CyInterface().addMessage(iOwner,False,25,CyTranslator().getText("TXT_KEY_MESSAGE_IMMIGRATION",(pCity.getName(),)),"AS2D_WELOVEKING",InterfaceMessageTypes.MESSAGE_TYPE_INFO,pUnit.getButton(),ColorTypes(8),pCity.getX(),pCity.getY(),True,True)
	
	# Unit expended
	pUnit.kill(0, -1)	
	# Orion's Immigration Mod	

def doJoinCity[COLOR="DarkGreen"]AI[/COLOR](pUnit):
	# Orion's Immigration Mod
	iOwner = pUnit.getOwner()
	pPlayer = gc.getPlayer(iOwner)
	iPlotX = pUnit.getX()
	iPlotY = pUnit.getY()
	pPlot = CyMap( ).plot( pUnit.getX( ), pUnit.getY( ) )
	pCity = pPlot.getPlotCity()
	NewCityPopulation = pCity.getPopulation() + 1
	
	pCity.setPopulation(NewCityPopulation)	
	if pPlayer.isHuman():
		CyInterface().addMessage(iOwner,False,25,CyTranslator().getText("TXT_KEY_MESSAGE_IMMIGRATION",(pCity.getName(),)),"AS2D_WELOVEKING",InterfaceMessageTypes.MESSAGE_TYPE_INFO,pUnit.getButton(),ColorTypes(8),pCity.getX(),pCity.getY(),True,True)
	
	# Unit expended
	pUnit.kill(0, -1)	
	# Orion's Immigration Mod

Before the change there was only "def doJoinCity(pUnit)" used for the AI and the human players. Since I used two values for the ModNetMessage to initiate doJoinCity I had to change it to "def doJoinCity(iOwner, iUnitID)". I don´t want to mess around with the function for the AI because I don´t know how I could test if it still works, so I decided to change the name for the AI from "def doJoinCity(pUnit)" to "def doJoinCityAI(pUnit)". The following code is the same like the original code was, so I expect it will work.

I know it´s surely not very elegant but I hope it should work in MP now.
 
Ok. Now i would like to add the ModNetMessage for the following code:

CvEventManager.py
Code:
###neuBeginn Popup1 terraforming
		pPlot = CyMap().plot(iX, iY)
		if iImprovement == gc.getInfoTypeForString('IMPROVEMENT_LANDAUFSCHUETTUNG'):
			pPlot.setPlotType(PlotTypes.PLOT_LAND, True, True)
		    	pPlot.setImprovementType(-1)
			iPlayerNum = 0
			for iPlayer in range(gc.getMAX_PLAYERS()):
				player = gc.getPlayer(iPlayer)
				if player.isAlive():
					iPlayerNum = iPlayerNum + 1
					if player.isHuman():
						CyCamera().JustLookAtPlot( CyMap().plot(iX, iY))
						popupInfo = CyPopupInfo()
						popupInfo.setButtonPopupType(ButtonPopupTypes.BUTTONPOPUP_PYTHON)
						popupInfo.setText(CyTranslator().getText("TXT_KEY_POPUP_SELECT_TERRAINTYPE",()))
						popupInfo.setData1(iPlayer)
						popupInfo.setData2(iX)
						popupInfo.setData3(iY)
						[COLOR="Red"]popupInfo.setOnClickedPythonCallback("SelectTerrainType")[/COLOR]
						popupInfo.addPythonButton(CyTranslator().getText("TXT_KEY_POPUP_GRAS", ()), "")
						popupInfo.addPythonButton(CyTranslator().getText("TXT_KEY_POPUP_EBENE", ()), "")
						popupInfo.addPythonButton(CyTranslator().getText("TXT_KEY_POPUP_WÜSTE", ()), "")
						popupInfo.addPythonButton(CyTranslator().getText("TXT_KEY_POPUP_TUNDRA", ()), "")
						popupInfo.addPythonButton(CyTranslator().getText("TXT_KEY_POPUP_SCHNEE", ()), "")
						popupInfo.addPopup(iPlayer)

Is it correct, that I´ve to add the CyMessageControl().sendModNetMessage where the red line refers to?

Actually the code there looks like this:

CvScreensInterface.py
Code:
############Neu TerrainTypes Auswahl terraforming
def SelectTerrainType(argsList):
	iButtonId = argsList[0]
	iData1 = argsList[1]
	iData2 = argsList[2]
	iData3 = argsList[3]
	iData4 = argsList[4]
	szText = argsList[5]
	bOption1 = argsList[6]
	bOption2 = argsList[7]

	pPlot = CyMap().plot(iData2, iData3)
	if iButtonId == 0:
		pPlot.setTerrainType (TerrainTypes(0), True, True)
	if iButtonId == 1:
		pPlot.setTerrainType (TerrainTypes(1), True, True)
	if iButtonId == 2:
		pPlot.setTerrainType (TerrainTypes(2), True, True)
	if iButtonId == 3:
		pPlot.setTerrainType (TerrainTypes(3), True, True)
	if iButtonId == 4:
		pPlot.setTerrainType (TerrainTypes(4), True, True)
#############Das war´s mit den TerrainTypes... :)

I´m not sure if the following code will work because I don´t know how the results of the click on the button are forwarded. It seems to me, that iData2 and iData3 in the code above are the same like iX and iY, so I have to use them as values. Surely I need the iButtonID as a value too. But what are iData1, iData4, szText, bOption1 and bOption2 for? Do I need them to run the code, because I couldn´t find them anywhere else. Will it work when the names of some values of the Message are iData2 and iData3?

CvScreensInterface.py
Code:
############Neu TerrainTypes Auswahl terraforming
def SelectTerrainType(argsList):
	iButtonId = argsList[0]
	iData1 = argsList[1]
	[COLOR="Green"]iData2 = argsList[2]
	iData3 = argsList[3][/COLOR]
	iData4 = argsList[4]
	szText = argsList[5]
	bOption1 = argsList[6]
	bOption2 = argsList[7]
        CyMessageControl().sendModNetMessage(169, [COLOR="Green"]iData2, iData3[/COLOR], iButtonID, -1) # be sure 169 is not use for another mod net message, the best should be to add an id in CvUtil like for events

CvEventmanager.py
Code:
def onModNetMessage(self, argsList):
		'Called whenever CyMessageControl().sendModNetMessage() is called - this is all for you modders!'
		
		iData1, iData2, iData3, iData4, iData5 = argsList
		if iData1 == 169 :
                        doChangeTerrainType(iData2, iData3, iData4)

def doChangeTerrainType(iData2, iData3, iButtonId)	
	pPlot = CyMap().plot(iData2, iData3)
	if iButtonId == 0:
		pPlot.setTerrainType (TerrainTypes(0), True, True)
	if iButtonId == 1:
		pPlot.setTerrainType (TerrainTypes(1), True, True)
	if iButtonId == 2:
		pPlot.setTerrainType (TerrainTypes(2), True, True)
	if iButtonId == 3:
		pPlot.setTerrainType (TerrainTypes(3), True, True)
	if iButtonId == 4:
		pPlot.setTerrainType (TerrainTypes(4), True, True)
#############Das war´s mit den TerrainTypes... :)

Sorry for all these questions and thank you for your patience and help.:)
 
The message parts look correct.
Don't worry about the other iData in the popup callback. They are just used to transfer some information from the place where you create the popup to the callback (you can see the setData1 and similar there).

Mind though that the player that gets the popup will be able to select what the terraformed plot becomes. So addPopup needs to be called for the player that built the improvement. The code you posted just loops through all players and gives the popup to all which are human.
 
I tried to implement the code lines above but when I try to start Civ he says that there is a syntax error in the red following line :confused::

Code:
def onModNetMessage(self, argsList):
		'Called whenever CyMessageControl().sendModNetMessage() is called - this is all for you modders!'
		
		iData1, iData2, iData3, iData4, iData5 = argsList
		if iData1 == 169 :
                        doChangeTerrainType(iData2, iData3, iData4)

[COLOR="Red"]def doChangeTerrainType(iData2, iData3, iButtonId)[/COLOR]	
	pPlot = CyMap().plot(iData2, iData3)
	if iButtonId == 0:
		pPlot.setTerrainType (TerrainTypes(0), True, True)
	if iButtonId == 1:
		pPlot.setTerrainType (TerrainTypes(1), True, True)
	if iButtonId == 2:
		pPlot.setTerrainType (TerrainTypes(2), True, True)
	if iButtonId == 3:
		pPlot.setTerrainType (TerrainTypes(3), True, True)
	if iButtonId == 4:
		pPlot.setTerrainType (TerrainTypes(4), True, True)
#############Das war´s mit den TerrainTypes... :)

Any idea how I could fix it?
 
Top Bottom