[PYTHONCOMP] Python utility library (includes INI file reading)

Awesome! I'll have a couple new mods coming out soon that will be using this ... just doing the final tweaking.

One thing I noticed with the new popup handler implementation ... when I use my popups this way, I get an interface message and a python debug output saying "DEBUG Event: abdicatePopup (jdog5000)" where my popup name is abdicatePopup. Is this because I have python debug output turned on? Or am I overiding some builtin event type and this is how it complains (using 7000 and 7001)? Where can you find a list of all the taken event type numbers anyway?

Thanks DEJ for making this great library.
 
jdog5000 said:
One thing I noticed with the new popup handler implementation ... when I use my popups this way, I get an interface message and a python debug output saying "DEBUG Event: abdicatePopup (jdog5000)" where my popup name is abdicatePopup. Is this because I have python debug output turned on?

Yes, I think it's just normal debugging output. What you're really doing when you call setPopupHandler is adding an item to the self.Events dictionary in the event manager. The three values that you pass in as the handler definition are:

  1. a name that's used by reportEvent for debugging output
  2. a function that's called by applyEvent
  3. a function that's called by beginEvent

This is all part of the base event manager that comes with the game, not something I added. The name, in particular, is used by the reportEvent function which is shown below. As you can see, it prints a message that looks like "DEBUG Event: <eventName> (<userName>)", which is exactly the message you're seeing.

Code:
	def reportEvent(self, entry, context, argsList):
		'Report an Event to Events.log '
		message = "DEBUG Event: %s (%s)" %(entry[0], gc.getActivePlayer().getName())
		CyInterface().addImmediateMessage(message,"")
		CvUtil.pyPrint(message)
		return 0

Or am I overiding some builtin event type and this is how it complains (using 7000 and 7001)? Where can you find a list of all the taken event type numbers anyway?

That isn't what's happening here, but it's a good question anyway. The built in ones seem to mostly be in CvUtil.py. Look in CvEventManager.py (again, the base one that comes with the game) to see where it initializes self.Events. Those are the existing ID's.

Of course, what I should have done is added the same checking I put into addEventHandler to throw an exception if the key already exists in the dictionary. I have no idea why I didn't do that :blush:. I guess one reason is that you might want to override an existing dialog handler, and if I throw an exception then you can't do that. Maybe what's needed is some kind of assertion function where you can check it separately. Or an overload where there are two different setPopupHandler functions, only one of which does the check.
 
I also found a bug.

Many mods using your CvCustomEventManager.py also use your CvConfigParser.py.
So there is in almost every mod a code similar to following code:
PHP:
	def __init__(self, eventManager, config):
		global g_bModChooser
		
		if (config != None):
			g_bModChooser = config.getint("Mod Chooser", "Mod Chooser", True)
		eventManager.addEventHandler("GameStart", self.onGameStart)
But, config never is None, so always the code in the if sequence is executed.
With a little addition to the "(mod name) Config ini" and the config-reading python file, this if sequence is executed the right way:
In "(mod name) Config ini":
PHP:
[Config Verifier]

; verifies this config
; if this value is false, the default values will be used
; Default value is true
Config Verifier = True
in the python file there must be this addition:
PHP:
		config = CvConfigParser.CvConfigParser("(mod name) Config.ini")
		bConfigVerifier = config.getboolean("Config Verifier", "Config Verifier", False)
		if not bConfigVerifier:
			config = None
Now, the code for all implemented codes will be used correctly.

As attachment a Config.ini and the CvCustomEventManager.py
 

Attachments

Caesium said:
PHP:
	def __init__(self, eventManager, config):
		global g_bModChooser
		
		if (config != None):
			g_bModChooser = config.getint("Mod Chooser", "Mod Chooser", True)
		eventManager.addEventHandler("GameStart", self.onGameStart)
But, config never is None, so always the code in the if sequence is executed.

Why is that a bug? It should work fine. g_bModChooser will be set to the default value of True if there is no "Mod Chooser" section. I'm guessing that you would prefer the default value to be False, which might be a legitimate issue, but that would be a problem with the mod, not the config file parsing.
 
Dr Elmer Jiggle said:
Why is that a bug? It should work fine. g_bModChooser will be set to the default value of True if there is no "Mod Chooser" section. I'm guessing that you would prefer the default value to be False, which might be a legitimate issue, but that would be a problem with the mod, not the config file parsing.
You misunderstand me.
My intention was, that there is in most of the mods following code:
Code:
if (config != None):
But the CvConfigParser doesn't return None, if there is no ini, so the if thread is worked, even if we don't want it.
So I added an entry to the ini, that is read, and its value is "True".
But, if there is no ini, the returned value is "False".
If the returned value is "False", then I define:
Code:
config = None
So, for the code
Code:
if (config != None):[code]while config is "None", the if thread isn't worked.

Or to say it more easy, though the code [code]if (config != None):
is rubbish, we could delete this line, or extend the ini and the CvCustomEventManger.py with posted code.
 
FYI, to make this mod compatible with the Warlords expansion you will need to change the _getInstallDir method in CvPath.py from:
Code:
def _getInstallDir():
    return __getRegValue(_winreg.HKEY_LOCAL_MACHINE, 
            r"Software\Firaxis Games\Sid Meier's Civilization 4",
            "INSTALLDIR")

to
Code:
def _getInstallDir():
    civ4Dir = __getRegValue(_winreg.HKEY_LOCAL_MACHINE, 
            r"Software\Firaxis Games\Sid Meier's Civilization 4",
            "INSTALLDIR")
    civ4Dir = os.path.join(civ4Dir,"Warlords")
    return civ4Dir

It's that easy. Happy Warlords modding :D
 
No just Warlords.
 
Hmm, I don't like the idea of having to release two copys of each version of my mods when most are only going to have this one method different.

What I'm doing is, in the CvPath.py file, changeing this
Code:
def _getInstallDir():
    return __getRegValue(_winreg.HKEY_LOCAL_MACHINE, 
            r"Software\Firaxis Games\Sid Meier's Civilization 4",
            "INSTALLDIR")

and this
Code:
activeModName = None
try:
    import CvModName
    activeModName = CvModName.modName
except:
    pass

to this
Code:
def _getInstallDir():
    civ4Dir = __getRegValue(_winreg.HKEY_LOCAL_MACHINE, 
            r"Software\Firaxis Games\Sid Meier's Civilization 4",
            "INSTALLDIR")

    if (activeExpansionWarlords):
            civ4Dir = os.path.join(civ4Dir,"Warlords")

    return civ4Dir

and this
Code:
activeModName = None
activeExpansionWarlords = False
try:
    import CvModName
    activeModName = CvModName.modName
    activeExpansionWarlords = CvModName.expansionWarlords
except:
    pass


Then, ofcourse, I add a variable in CvModName.py file called expansionWarlords. Then I can just release my mods with the expansionWarlords variable set to False and it will work like normal with Civ4.

For those that want to play it with Warlords I will offer a version of the CvModName.py file with expansionWarlords variable set to True. They can then download and install this one file and it will work for warlords.

Now this is probly not the most elegant way of doing it, so if someone knows an easyer way to check if the game is warlords or Civ4 (I don't know, say checking the version number of the game or somthing), please post up, would be much helpfull. :)
 
Jeckel said:
Now this is probly not the most elegant way of doing it, so if someone knows an easyer way to check if the game is warlords or Civ4 (I don't know, say checking the version number of the game or somthing), please post up, would be much helpfull. :)

What is possible is checking using os.path.file.exists() whether CvPath.py exists where it is supposed to be in the regular and Warlords directories across all possibilities in the assetsPath. Then if it is only one of the two, automatically setting activeExpansionWarlords appropriately.

That code is complicated enough. However, what do you do if someone has installed your mod in both directories? (I could fall back on that flag you made for CvModName.py...) Or worse, different versions of your mod? (I could add a version number to CvModName.py...)
 
Gaurav said:
What is possible is checking using os.path.file.exists() whether CvPath.py exists where it is supposed to be in the regular and Warlords directories across all possibilities in the assetsPath. Then if it is only one of the two, automatically setting activeExpansionWarlords appropriately.

That code is complicated enough. However, what do you do if someone has installed your mod in both directories? (I could fall back on that flag you made for CvModName.py...) Or worse, different versions of your mod? (I could add a version number to CvModName.py...)


Nods, yea, I had thought of this basic idea, but anyone that has warlords installed will also have civ4 installed, so you then run into that problem of not knowing which they are acually playing. You would think there would be some CyGame method that would tell you the version of civ, or if it is vanilla or an expansion.

As it stands now, if someone
1) has the JUnitReligion mod installed in both vanilla and warlords
2) has expansionWarlords in CvModName.py in the warlords dir set to false
3) loads and plays the mod in warlords
then the mod will use the config files in the vanilla directory, not the warlords.

If the above, except there is no mod in vanilla direcotry then the mod will not see any config files and will use default values.

As for having different versions of the mod, my goal is to get some form of CvPath that is compatiable with both warlords and civ4. If that is accomplished then redundent versions of my mods is unneccessary. :)

I am really interested to hear what, if anything, the Dr. has planned to upgrade it to warlords compatability. I'm just trying to find a way to rig it up until then. :D
 
Jeckel said:
I am really interested to hear what, if anything, the Dr. has planned to upgrade it to warlords compatability. I'm just trying to find a way to rig it up until then. :D

I just got back from a vacation and haven't had a chance to install Warlords, but I hope to look at it sometime this week. I'm thinking that there might be a way to probe for the existence of one of the new Warlords features and use that as the trigger for which directory you use.

For example, maybe you can see whether there's a "Temple of Artemis" wonder or whether there is a Great General great person. Or there might be some values defined in GlobalDefs.xml. Something like that.
 
Welcome back, hope you had fun on your vacation. :)

That is a good idea, checking for somthing in warlords, but I would try to stay away from checking for units, building, wonders, ect. as they could conflict with vanilla mods. For example, a few of TheLopez's mods have a Great General unit and the Lost Wonders Mod has most of the wonders from Warlords, but right off hand checking for somthing in the globaldefines file sounds like a good solution to me. :D

Glad to hear your on the case and looking forward to see how you work it out. :cool:
 
Jeckel said:
Welcome back, hope you had fun on your vacation. :)

That is a good idea, checking for somthing in warlords, but I would try to stay away from checking for units, building, wonders, ect. as they could conflict with vanilla mods. For example, a few of TheLopez's mods have a Great General unit and the Lost Wonders Mod has most of the wonders from Warlords, but right off hand checking for somthing in the globaldefines file sounds like a good solution to me. :D

Glad to hear your on the case and looking forward to see how you work it out. :cool:

I suppose you could also use a try/catch block to check for the existence of a (new) exposed python function from the SDK.
 
There is a CIV4_VERSION define in GlobalDefines.xml. In Warlords it is currently 200, and in an updated Classic it is 161. I'm making the assumption that patches to Warlords will continue to be 2xx and Classic will be 1xx. Thus, if CIV4_VERSION / 100 is 2, we're looking at Warlords.

Code:
def _getInstallDir():
    gc = CyGlobalContext()
    subkey = r"Software\Firaxis Games\Sid Meier's Civilization 4"
    if ((gc.getDefineINT("CIV4_VERSION") / 100) == 2):
        subkey += r" - Warlords"
    return __getRegValue(_winreg.HKEY_LOCAL_MACHINE, subkey, "INSTALLDIR")

This is the only necessary change. Of course, ideally this check would be done in some kind of isWarlords utility function, but I didn't want to create a new CvUtil.py unit just for one utility function, so it is what it is.

DrEJlib.zip
 
DrEJ -

I'm having some interesting challenges with this new version in Warlords. Here's what happens:

My mod, Revolution, is set to automatically load in the Warlords Civilization4.ini file. I start up Warlords and choose Custom Game or whatever and I get a popup from my mod saying it was unable to find the .ini config file. I added a line to CvPath.py to print out installDir, and when I check it after this initial game launch it's all vanilla directories.

But, if I then modify some Python line to force the game to reload Python, then check the print out again, it's now all Warlords directories!

Anyone else experiencing this?

EDIT: A little more info, it seems like on the initial game launch load, the value returned from gc.getDefineINT("CIV4_VERSION") is 0, I guess because XML hasn't been properly loaded yet. So this probably wouldn't be a problem if you started up the game, then loaded a mod? Still would be nice to have a good solution to this ... but I don't have any good ideas.
 
Try printing out the value of CIV4_VERSION too. Since that's what's determining which registry key to use for the install directory, that's really the critical question.

Does your mod include a custom version of GlobalDefines.xml? That's where CIV4_VERSION comes from. Maybe your mod has an old value for that?
 
Dr Elmer Jiggle said:
Try printing out the value of CIV4_VERSION too. Since that's what's determining which registry key to use for the install directory, that's really the critical question.

Does your mod include a custom version of GlobalDefines.xml? That's where CIV4_VERSION comes from. Maybe your mod has an old value for that?
Did that just after posting and editted it into my previous post ... my mod contains no xml at all, and my custom assets folder is empty.

On the initial game load, the value returned is 0 but on subsequent loads the value is 200 as it should be.
 
Hi

As a result of debugging Civ4Lerts on a Mac, I have posted a modified version of your CvPath.py module in the Civ4lerts thread. As this seems to be the main thread covering this module, I thought I should post the details here.

If it detects Mac OS X (sys.platform = 'darwin') it provides Mac-specific paths. Otherwise it provides the existing Windows registry-based paths. Assuming this still works OK on Windows (I can't test it myself), I recommend you update this module so that future mods using CvPath.py are more likely to work on Macs.

I replaced the three function defs that use _winreg with:

Code:
import sys
if (sys.platform == 'darwin'):
	""" Mac OS X """
	def _getUserDir():
		return os.path.join(os.environ['HOME'], "Documents", "Civilization IV")

	def _getInstallDir():
		import commands
		civ4name = "Civilization IV.app/Contents/MacOS/Civilization IV"
		str = commands.getoutput("ps -xo 'command' | grep " + "'" + civ4name + "'")
		m = str.find(civ4name)
		if (m >= 0):
			installDir = str[0:m]
		return installDir
else:
	import _winreg
	""" Windows """
	def __getRegValue(root, subkey, name):
		key = _winreg.OpenKey(root, subkey)
		try:
			value = _winreg.QueryValueEx(key, name)
			return value[0]
		finally:
			key.Close()
	
	
	def _getUserDir():
		myDocuments = __getRegValue(_winreg.HKEY_CURRENT_USER, 
				r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders",
				"Personal")
		civ4Dir = os.path.basename(_getInstallDir())
		return os.path.join(os.path.join(myDocuments, "My Games"), civ4Dir)
	
	
	def _getInstallDir():
		gc = CyGlobalContext()
		subkey = r"Software\Firaxis Games\Sid Meier's Civilization 4"
		if ((gc.getDefineINT("CIV4_VERSION") / 100) == 2):
			subkey += r" - Warlords"
		return __getRegValue(_winreg.HKEY_LOCAL_MACHINE, subkey, "INSTALLDIR")

The modified file is attached to my post here
 
Thanks for the file. I can't make my mod mac-compatible though, since it also has a custom CvGameCoreDLL. :(
 
Back
Top Bottom