Problem with BUG 4.0 and CvGameUtils.py (after BUG Mod Update 3.6 -> 4.0)

I've merged in your changes, mostly by creating ReligionUtil that handles all the dynamic elements of religions:

  • New religions are handled automatically
  • You can define new types of religious buildings (e.g. an Inquisition Office)
  • You can define new types of religious units (e.g. an Inquisitor)
The next step is to make the top part scrolling and have a dropdown or buttons to select which religions should show both in the top area and in the bottom list:

  • Religions in your cities
  • Founded religions (make default?)
  • All religions (current)
 
Is that function IsInquistionConditions() a new callback that exists in CvGameInterface.py and is called from the SDK or is it just a utility function used by the other callbacks? If the latter, you can ignore the warnings or move them outside of the class to be at module level (assuming they don't need attributes defined in the class).

If the former, you need to tell BUG about those new callbacks using the <callback> element. Check out Creating New Callbacks.

For each new callback (one that doesn't exist in the original CvGameUtils) you need to add a <callback> child element to the <gameutils> element providing the name and type/value of the default value--the value returned when the callback isn't overridden.

Code:
<gameutils ...>
	<callback name="IsInquistionConditions" type="..." default="..."/>
</gameutils>

I haven't used this functionality much yet myself, so you'll have to let me know how well this works for you. First, let me know if those extra functions are indeed new callbacks. If so, try this out and see if they work. I'm concerned that the gameutils class will be processed before the <callback>s are detected. This will give you the warnings but end up defining the callbacks correctly. If so, I will see about having the callbacks defined first before the whole gameutils is processed. In that case, I'll need to detect duplicated handlers and ignore them.

I'm merging FfH2 with BUG.
Latest version of FfH2 (patch"i") has some new callbacks. (New callbacks exist in CvGameInterface.py and CvGameUtils.py)
Therefore, I have added <callback/> elements to init.xml.

Code:
118	<gameutils module="CvFfHGameUtils" class="CvFfHGameUtils">
119		<callback name="AI_MageTurn"/>
120		<callback name="AI_MagePromotion"/>
121		<callback name="AI_Mage_UPGRADE_MANA"/>
122		<callback name="AI_ConquestPromotion"/>
123		<callback name="AI_GovannonPromotion"/>
124		<callback name="AI_PermDefensePromotion"/>
125		<callback name="AI_PatrolPromotion"/>
126		<callback name="AI_TowerMastery"/>
127		<callback name="AI_ArchmagePromotion"/>
128	</gameutils>

But callback elements cause parse error.

Spoiler :
20:29:54 DEBUG: BUG: looking up UnitUtil.init
20:29:54 DEBUG: BUG: looking up WidgetUtil.getWidgetHelp
20:29:54 DEBUG: BugGameUtils - getWidgetHelp - adding WidgetUtil handler
20:29:54 DEBUG: BUG: looking up CvTechChooser.resetTechPrefs
20:29:54 DEBUG: BUG: looking up CvTechChooser.resetTechPrefs
20:29:54 DEBUG: BUG: looking up CvFfHGameUtils.CvFfHGameUtils
load_module CvFfHGameUtils

20:29:54 DEBUG: BugGameUtils - registering CvFfHGameUtils.CvFfHGameUtils
20:29:54 DEBUG: BugGameUtils - isActionRecommended - adding CvFfHGameUtils handler
20:29:54 DEBUG: BugGameUtils - canRazeCity - adding CvFfHGameUtils handler
20:29:54 DEBUG: BugGameUtils - canConstruct - adding CvFfHGameUtils handler
20:29:54 DEBUG: BugGameUtils - getRecommendedBuilding - adding CvFfHGameUtils handler
20:29:54 DEBUG: BugGameUtils - getSecondRecommendedTech - adding CvFfHGameUtils handler
20:29:54 DEBUG: BugGameUtils - getUpgradePriceOverride - adding CvFfHGameUtils handler
20:29:54 DEBUG: BugGameUtils - AI_chooseTech - adding CvFfHGameUtils handler
20:29:54 DEBUG: BugGameUtils - skipProductionPopup - adding CvFfHGameUtils handler
20:29:54 DEBUG: BugGameUtils - doCulture - adding CvFfHGameUtils handler
20:29:54 DEBUG: BugGameUtils - cannotSelectionListGameNetMessage - adding CvFfHGameUtils handler
20:29:54 DEBUG: BugGameUtils - getExperienceNeeded - adding CvFfHGameUtils handler
20:29:54 DEBUG: BugGameUtils - canPickPlot - adding CvFfHGameUtils handler
20:29:54 DEBUG: BugGameUtils - updateColoredPlots - adding CvFfHGameUtils handler
20:29:54 DEBUG: BugGameUtils - getExtraCost - adding CvFfHGameUtils handler
20:29:54 TRACE: Unknown GameUtils callback AI_GovannonPromotion
20:29:54 TRACE: 'AI_GovannonPromotion'
20:29:54 TRACE: BugConfig - failure parsing C:\Program Files\Firaxis Games\Sid Meier's Civilization 4\Beyond the Sword\Mods\Fall from Heaven 2 with BUG 4.1\Assets\Config\init.xml at line 119
20:29:54 TRACE: 'AI_GovannonPromotion'
20:29:55 DEBUG: BUG: looking up CvFfHGameUtils.CvFfHGameUtils
20:29:55 DEBUG: BugGameUtils - registering CvFfHGameUtils.CvFfHGameUtils
20:29:55 DEBUG: BugGameUtils - isActionRecommended - adding CvFfHGameUtils handler
20:29:55 DEBUG: BugGameUtils - canRazeCity - adding CvFfHGameUtils handler
20:29:55 DEBUG: BugGameUtils - canConstruct - adding CvFfHGameUtils handler
20:29:55 DEBUG: BugGameUtils - getRecommendedBuilding - adding CvFfHGameUtils handler
20:29:55 DEBUG: BugGameUtils - getSecondRecommendedTech - adding CvFfHGameUtils handler
20:29:55 DEBUG: BugGameUtils - getUpgradePriceOverride - adding CvFfHGameUtils handler
20:29:55 DEBUG: BugGameUtils - AI_chooseTech - adding CvFfHGameUtils handler
20:29:55 DEBUG: BugGameUtils - skipProductionPopup - adding CvFfHGameUtils handler
20:29:55 DEBUG: BugGameUtils - doCulture - adding CvFfHGameUtils handler
20:29:55 DEBUG: BugGameUtils - cannotSelectionListGameNetMessage - adding CvFfHGameUtils handler
20:29:55 DEBUG: BugGameUtils - getExperienceNeeded - adding CvFfHGameUtils handler
20:29:55 DEBUG: BugGameUtils - canPickPlot - adding CvFfHGameUtils handler
20:29:55 DEBUG: BugGameUtils - updateColoredPlots - adding CvFfHGameUtils handler
20:29:55 DEBUG: BugGameUtils - getExtraCost - adding CvFfHGameUtils handler
20:29:55 TRACE: Unknown GameUtils callback AI_GovannonPromotion
20:29:55 TRACE: 'AI_GovannonPromotion'
20:29:55 TRACE: BugConfig - failure parsing C:\Program Files\Firaxis Games\Sid Meier's Civilization 4\Beyond the Sword\Mods\Fall from Heaven 2 with BUG 4.1\Assets\Config\init.xml at line 119
20:29:55 TRACE: 'AI_GovannonPromotion'
20:29:55 TRACE: BugConfig - failure parsing C:\Program Files\Firaxis Games\Sid Meier's Civilization 4\Beyond the Sword\Mods\Fall from Heaven 2 with BUG 4.1\Assets\Config\init.xml at line 120
20:29:55 TRACE: Element <bug> does not accept child <callback>
20:29:55 TRACE: BugConfig - failure parsing C:\Program Files\Firaxis Games\Sid Meier's Civilization 4\Beyond the Sword\Mods\Fall from Heaven 2 with BUG 4.1\Assets\Config\init.xml at line 121
20:29:55 TRACE: Element <None> does not accept child <callback>
20:29:55 TRACE: BugConfig - failure parsing C:\Program Files\Firaxis Games\Sid Meier's Civilization 4\Beyond the Sword\Mods\Fall from Heaven 2 with BUG 4.1\Assets\Config\init.xml at line 121
20:29:55 TRACE: 'NoneType' object has no attribute 'addText'
20:29:56 DEBUG: Timer - load mod [init] took 3211 ms
20:29:59 WARN : BugInit - init() already running
What am I wrong?:confused:


EDIT:
I can not resolve this problem.

Instead, I add new callback functions to original CvGameUtils.py as a dummy.
They only do return -1 and nothing other.
Other functions which already exist in original BtS are not changed.

Next, I added one line to init.xml file.

Code:
	<gameutils module="CvFfHGameUtils" class="CvFfHGameUtils">
CvFfHGameUtils.py is renamed file from original CvGameUtils.py of FfH2. (Of course, class name is renamed too.)

Then, it seems to go well.

Does this method cause any problem to BUG mechanism?
Thanks.
 
The problem is with BUG. The <gameutils> element tries to bind every function in the class to a callback and raises an error if any function doesn't match a callback name. This can cause other problems, but when defining new callbacks it will fail because the <callback> elements come later.

Try this change to Python/BUG/BugGameUtils.py, starting at line 263 replace the entire _addUtils() function with this:

Code:
	def _addUtils(self, utils, override=False, log=None):
		clazz = utils.__class__
		BugUtil.debug("BugGameUtils - registering %s.%s", clazz.__module__, clazz.__name__)
		for name, func in clazz.__dict__.iteritems():
			if not name.startswith("_") and isinstance(func, types.FunctionType):
				if name.endswith(LISTENER_SUFFIX):
[B]					name = name[:-len(LISTENER_SUFFIX)]
					if name in self._callbacks:[/B]
						self._addBoundListener([B]name[/B], utils, func, log)
				else:
					[B]if name in self._callbacks:[/B]
						self._addBoundHandler(name, utils, func, override, log)

This should make it only attach handlers to existing callbacks. Then when <callback> defines the new callback, it will attach the appropriate handler. Let me know if it works and I'll put it in the next fix release of BUG.
 
This should make it only attach handlers to existing callbacks. Then when <callback> defines the new callback, it will attach the appropriate handler. Let me know if it works and I'll put it in the next fix release of BUG.

I have tried new code. It seems to go well. :)
At least, I have not faced error. (But I have not verified whether new callback is called correctly or not yet.)

By the way, does <callback> element require either "type" or "default" attributes?
I can't abbreviate both of them.

Anyway, thanks for fixed code!
 
By the way, does <callback> element require either "type" or "default" attributes?

If you specify "default", "type" is required. If you give a callback a default value, then BUG will call all handlers defined for the callback until one of them returns a value different from the default.

For example, you can create several handlers for cannotMoveInto(). The default is "False" so that the SDK's normal code is used to determine this. As soon as one handler returns "True" BUG returns "True" to the SDK and stops calling the other handlers during that one call to the callback.

This would allow you to have two different mods that block units from entering tiles: one that allows you to create minefields and another that enables a spell. Both would have their own cannotMoveInto() handler. If they both return "False", the SDK runs normally. If either one returns "True", the unit is blocked from moving there.

For some callbacks it won't make sense to have a default, for example doGoodyHut() has no return value at all.

Thanks for reporting back on the success of the code. I'll commit it to BUG.
 
I could understand what default value means at last!
When one callback has only one handler, Is default value needless?

Once I have abbreviated both of "type" and "default", then BugConfig.py - createValue() function causes exception.
BugTypes.py - to() function said "to() requires a type and/or value"

Therefore, I specified only "type" attribute to avoid this exception now.
 
When one callback has only one handler, Is default value needless?

Yes, but keep in mind that modmodders might add handlers to a callback you've created. If the callback has the concept of "Don't do anything special in Python; let the SDK handle it," add a default value to the <callback> definition.

Once I have abbreviated both of "type" and "default", then BugConfig.py - createValue() function causes exception.
BugTypes.py - to() function said "to() requires a type and/or value"

Abbreviated means shortened, as in "abbr" is an abbreviation for "abbreviation". What exactly do you mean above? Did you omit one or the other or both? If you provide one, you must provide both, but you should be able to omit both.

Therefore, I specified only "type" attribute to avoid this exception now.

By omitting "default", "type" should be ignored. I neglected to allow you to specify just "type" and use the default value for that type (False for boolean, 0 for int, etc) as the default value for the callback. I should probably log a warning in this case, however.
 
Abbreviated means shortened, as in "abbr" is an abbreviation for "abbreviation". What exactly do you mean above? Did you omit one or the other or both? If you provide one, you must provide both, but you should be able to omit both.

Oh... I want to say "omit" at heart. I'm not good at English.;)

Yes, I hope to omit both of "type" and "default" attributes, and I have done so.
Then, BugTypes.py - to() function said "to() requires a type and/or value", because to() function received both of empty arguments ("type" argument and "value" argument).

I wondered this behavior, and posted #26 article.
I am sorry my lacking of talk.
 

Attachments

  • Civ4ScreenShot0013.JPG
    Civ4ScreenShot0013.JPG
    85.5 KB · Views: 73
Excellent! You've found another BUG bug. Modify the end of Python/BUG/BugTypes.py's to() function starting on line 159:

Code:
def to(type, value, noneIsDefault=True, emptyIsDefault=True):
	"""
	Converts <value> from a string to <type>.
	
	If <type> is None or empty (""), the value is evaluated directly using eval();
	otherwise <type> is normalized first. [B]If <value> is None or empty ("") as well,
	the special value None is returned.[/B]
	
	If <value> is None or empty (""), the default is used based on the optional parameters.
	
	The <type> is normalized first, and line endings (\r\n) are replaced with spaces in <value>.
	"""
	if type:
		if not value:
			if (value is None and noneIsDefault) or (value == "" and emptyIsDefault):
				return default(type)
			return None
		else:
			try:
				return CONVERT_FROM_STRING[normalize(type)](value.replace("\r\n", " "))
			except KeyError:
				raise BugUtil.ConfigError("Invalid type %s", type)
	else:
[B]		if value:
			try:
				return eval(value)
			except:
				raise BugUtil.ConfigError("Invalid expression %s", value)
		return None[/B]

This should allow you to omit both "type" and "default", making the callback have no default value. In this case all handlers for the callback will be called and their return values ignored.
 
Top Bottom