Merging Legends of Revolutions and theLopez's immigration mod.

Last post updated. I was making sure I had the way to post code correctly Baldyr. You caught me inspecting my post.
 
Instead of using iPlayerID in the call to sdEntityInit, try "str(iPlayerID)". That is, convert it to a string and pass that. The tables and such all want their identifiers to be text strings. It ends up concatenating those first two arguments together (with a "." between them) and you can't concatenate an integer onto a string.

"Billy" + "Bob" = "BillyBob"
"Billy" + 1 = error
"Billy" + str(1) = "Billy" + "1" = "Billy1"
 
Traceback (most recent call last):
File "BugEventManager", line 361, in _handleDefaultEvent
File "CvImmigrationEventManager", line 62, in onEndPlayerTurn
File "ImmigrationUtils", line 721, in retargetImmigrant
KeyError: 40960

Am I not sending proper amount of arguments? Key error to me says that the id in the hash table may not be corect?


Code:
	def retargetImmigrant(self, objUnit):
	
		# Return immediately if the unit passed in is invalid
		if(objUnit == None):
			return

		# Return immediately if the unit passed in is invalid
		if(objUnit.isNone()):
			return

		# Return if the unit isn't an immigrant			
		if(objUnit.getUnitType() != gc.getInfoTypeForString("UNIT_IMMIGRANT")):
			return
		
		iPlayerID = objUnit.getOwner()
			
		# Setup the civilization immigration data if it doesn't exist
		if(not sdEntityExists("Immigration Mod", iPlayerID)):
			self.setupImmigrantData(iPlayerID)

		# Get the civilization immigration data			
		civilizationImmigrationHash = sdGetVal("Immigration Mod", str(iPlayerID), CIVILIZATION_IMMIGRATION_DATA)

		civilizationImmigrantHash = civilizationImmigrationHash[CIVILIZATION_IMMIGRANT_HASH]
		
		# get the immigrant data hash in the civilization immigration hash
[COLOR="Blue"]		immigrantDataHash = civilizationImmigrantHash[objUnit.getID()][/COLOR]

		(iX, iY) = immigrantDataHash[TARGET_PLOT].split("-")
		
		objUnit.getGroup().clearMissionQueue()
		
		# Make the immigrants move towards the intended target city
		objUnit.getGroup().pushMoveToMission(int(iX), int(iY))
 
The error means basically: "I'm searching for a value, and can't find it".
If you take a look at the mentioned line:
PHP:
immigrantDataHash = civilizationImmigrantHash[objUnit.getID()]

...do you remember something? Yes, it's related to the last change ;).
In the last change, we've converted an ID to a string value (from being an integer before).
Now here, the game is asking the "saving structure" for a value, and gives it an ID...an integer, but wait, we converted the integer before to a string!


...er...i guess that was now confusing :D.
So long explanation short, change the problematic line to
PHP:
immigrantDataHash = civilizationImmigrantHash[str(objUnit.getID())]
 
The error means basically: "I'm searching for a value, and can't find it".
If you take a look at the mentioned line:
PHP:
immigrantDataHash = civilizationImmigrantHash[objUnit.getID()]

...do you remember something? Yes, it's related to the last change ;).
In the last change, we've converted an ID to a string value (from being an integer before).
Now here, the game is asking the "saving structure" for a value, and gives it an ID...an integer, but wait, we converted the integer before to a string!


...er...i guess that was now confusing :D.
So long explanation short, change the problematic line to
PHP:
immigrantDataHash = civilizationImmigrantHash[str(objUnit.getID())]

Well when I tried that it gave me the exact same error. I then tried changing it to an int it gave me a different key error. It sounds like the problem is the value is already a string. I've also noticed occasionally the wrong city is generating an immigrant.
 
The previous issue does not appear to have anything to do with this one. They are not referring to the same dictionary.

From the Python documentation:
Code:
d[key]
    Return the item of d with key key. Raises a KeyError if key is not in the map.

So it just means the key you are looking for does not exist. At no point was an entry for that player ID put into the dictionary. It doesn't matter if you use a string or an integer as long as it is always the same when you look it up as when you add it - but you have not added it.

You are trying to look up an entry for a unit ID. Where was this unit ID supposed to be added to the dictionary? That's a good place to start checking as to why it is looking for something that isn't there.

Alternatively, if you can correctly end up trying to check for a key that is not present then you need to change the code to allow this. If you want to check if a key is in a dictionary, "if key in dict:" is the test. Or use "dict.get(key,default)" to get the value mapped to a key without raising a KeyError if it is not present, you'll get the value passed as default instead (or you can also leave off the ",default" part and it will return None if there is no such key).
 
So you're saying I need to basically do the oopposite of this correct?

Code:
	def removeImmigrantData(self, objUnit):
	
		# Return immediately if the unit passed in is invalid
		if(objUnit == None):
			return

		# Return immediately if the unit passed in is invalid
		if(objUnit.isNone()):
			return
		
		# Get the owner of the unit
		iPlayerID = objUnit.getOwner()

		# Setup the player's game data if it doesn't exist and return immediately
		if(not sdEntityExists("Immigration Mod", iPlayerID)):
			self.setupImmigrantData(iPlayerID)
			return

		# Get the civilization immigration data			
		civilizationImmigrationHash = sdGetVal("Immigration Mod", iPlayerID, CIVILIZATION_IMMIGRATION_DATA)
	
		# Decrement the immigrant count for the civilization
		civilizationImmigrationHash[CIVILIZATION_IMMIGRATION_COUNT] -= 1
											
		# Delete the immigrant data from the civilization hash
		del civilizationImmigrationHash[CIVILIZATION_IMMIGRANT_HASH][objUnit.getID()]

		# Save the updated civiliation immigration hash			
		sdSetVal("Immigration Mod", iPlayerID, CIVILIZATION_IMMIGRATION_DATA, civilizationImmigrationHash )

		
	# Sets up the immigration data for the player ID passed in. Returns True if
	# the data was setup successfully, False otherwise.
 
Traceback (most recent call last):
File "BugEventManager", line 361, in _handleDefaultEvent
File "CvImmigrationEventManager", line 62, in onEndPlayerTurn
File "ImmigrationUtils", line 721, in retargetImmigrant
KeyError: 40962


I really have no idea at this point. It looks like it is supposed to be being added at the end of the setupimmigrantdata, but the objUnit is not passed to setupimmigrantdata.

immigrantDataHash = civilizationImmigrantHash[objUnit.getID()]

That's the offending line.
 

Attachments

This is the immigrant data hash that is constructing the immigrant:
I've tried inserting objUnit : objUnit into it after defining it and checking to see if it exists. That did not work. I also noticed in the other data hash playerid was not being defined.
Code:
	def setupImmigrantData(self, iPlayerID):
		
		# Get the instance of the player
		objPlayer = gc.getPlayer(iPlayerID)
		
		# Return False immediately if the player ID passed in was invalid		
		if(	objPlayer == None):
			return False

		# Return False immediately if the player ID passed in was invalid		
		if(	objPlayer.isNone()):
			return False
			
		civilizationImmigrantHash = {}
		
		civilizationImmigrationHash = {
			CIVILIZATION_IMMIGRATION_COUNT : 0,
			CIVILIZATION_IMMIGRANT_HASH : civilizationImmigrantHash,
		}
		
		scriptDict = { 	CIVILIZATION_IMMIGRATION_DATA : civilizationImmigrationHash }

		# Initialize the actual entity. 
		sdEntityInit("Immigration Mod", str(iPlayerID), scriptDict)

		return True
 
The other hash table it looks like they are being combined into one.

Code:
	def saveImmigrantData(self, objUnit, objTargetCity, objCity, cultureDataHash, immigrantReligionList, strImmigrationTargetSuggestion):

		# Return immediately if the city passed in is invalid
		if(objCity == None):
			return

		# Return immediately if the city passed in is invalid
		if(objCity.isNone()):
			return

		# Return immediately if the target city passed in is invalid
		if(objTargetCity == None):
			return

		# Return immediately if the target city passed in is invalid
		if(objTargetCity.isNone()):
			return

		# Return immediately if the unit passed in is invalid
		if(objUnit == None):
			return

		# Return immediately if the unit passed in is invalid
		if(objUnit.isNone()):
			return

		# Return immediately if the culture data passed in is invalid
		if(cultureDataHash == None):
			return
	
		# Return immediately if the religion data passed in is invalid
		if(immigrantReligionList == None):
			return
	
		# Get the owner ID of the city
		iPlayerID = objCity.getOwner()

		# Setup the civilization immigration data if it doesn't exist
		if(not sdEntityExists("Immigration Mod", iPlayerID)):
			self.setupImmigrantData(iPlayerID)

		# Get the civilization immigration data			
		civilizationImmigrationHash = sdGetVal("Immigration Mod", str(iPlayerID), CIVILIZATION_IMMIGRATION_DATA)
	
		# Increment the immigrant count for the civilization
		civilizationImmigrationHash[CIVILIZATION_IMMIGRATION_COUNT] += 1
	
		# Construct the immigrant data hash
		immigrantDataHash = {
								CITY_ORIGINS : objCity.getID(), 
								TARGET_PLOT : str(objTargetCity.plot().getX())+"-"+str(objTargetCity.plot().getY()),
								IMMIGRANT_CULTURE : cultureDataHash, 
								IMMIGRANT_RELIGION : immigrantReligionList, 
								TARGET_CITY_SUGGESTION : strImmigrationTargetSuggestion,
							}

		# Get the civilization immigrant hash
		civilizationImmigrantHash = civilizationImmigrationHash[CIVILIZATION_IMMIGRANT_HASH]
									
		# Save the immigrant data hash in the civilization immigration hash
		civilizationImmigrantHash[objUnit.getID()] = immigrantDataHash
	
		civilizationImmigrationHash[CIVILIZATION_IMMIGRANT_HASH] = civilizationImmigrantHash
		
		# Save the updated civiliation immigration hash			
		sdSetVal("Immigration Mod", iPlayerID, CIVILIZATION_IMMIGRATION_DATA, civilizationImmigrationHash )
 
I find this thread very confusing. Sure, its not easy to combine scripts, but I don't think it should be this painful to combine two out-of-the-box functioning mods. You should only be doing work on the event level and not editing individual functions in other modules. Because that would indicate, at least to me, that the mods aren't working separately to begin with!

The thing I find most confusing, however, is why you refer to just about anything as a "hash table". :confused: What is a hash table?
 
(double post)
 
That's always been the problem to begin with. The immigration mod is warlords compatible. The orion's immigration mod is 3.19 is a different beast. I have thought about Lemonmerchant's suggestion that I build sections of the lopez's immigration mod into it, but with my limited knowledge of python, I will still run into this same problem even if I do it that way. And honestly, at this point this mod is generating the immigrant unit, pushing him where he should be going, but is failing because eveytime an end turn comes around we're not reaching the join city part of the code.

Added this to the generateImmigrant class.

immigrantDataHash [objUnit] = objUnit

but the class that creates the immigrantDataHash has not been created yet.
 
I'm thinking that it would be faster to rewrite the whole thing rather than to delve into the current code and try to figure out why it isn't working... :p

Any compatibility issue Warlords vs BtS shouldn't require you to add your own lines of code, on a guess basis, or so I would think. I would expect there only to be minimal inconsistencies between the Warlords Python API and the BtS Python API. Are you trying to convert a Warlords mod to BtS or the opposite?
 
Supposing that you managed to merge the two mods successfully, the only thing that should cause the result not to work would be something that was changed between Warlords and BtS. I would expect there mostly to be additions however, but apparently something fundamental was also changed. I'm guessing that the argsList tuples passed on from the DLL to Python call-backs have a somewhat different set of values, in some cases. But this should cause some pretty obvious exceptions on unpacking the data in the Event Manager module, while you seem to be getting weird errors in the actual modules that hold the mod scripts.

Another thought: Are you converting one or the other mod to a BUG setup? Because then you need to go to school and rewrite that mod in the style of BUG.
 
Update: No python error are being thrown. Unit is still not joining city when it arrives. Should be a simple fix.
 
In the last line of saveImmigrantData the iPlayer value is not being converted to a string. Why this doesn't produce an error like it did for the sdEntityInit fucntion, I don't know (apparently sdSetVal is doing something different when concatenating it with the other stuff, otherwise you get the error about not being able to concatenate an integer onto a string). The earlier sdGetVal call in the same function does convert it to a string. Even if there is no error being produced, that lack of conversion could be the source of the problem: the ID it is using (built from the passed arguments) when saving the data in that last line probably doesn't match the others so later when it is being looked up it is not being found (instead it is probably finding a still empty version and the real data is stored under some slightly different key).

The ID values that the functions are composing to store and retrieve the data via the "sd" routines must all be exactly the same or you will never be able to find your data.

So, with all that, converting the iPlayer value to a string in the last line may help. (Or there may be other problems, of course.)
 
The thing I find most confusing, however, is why you refer to just about anything as a "hash table". :confused: What is a hash table?

Since I didn't see an answer...
A [wiki]hash table[/wiki] is a data structure used to quickly access a large table of values using a key. I admit I didn't read the previous posts thoroughly, but I assume Terradive refers to Python's dictionary, which is probably implemented using a hash table behind the scenes.
 
Back
Top Bottom