[MOD] Modular Python

v.3 alpha

A minor update, this makes adding python to spells easier. It also enables PyHelp and random events to use modular python.

The previous method of prepending "__import__('modname')." to the function call in xml still works.
 
reading through modulareventmanager.py

There's a hell of a lot of things I'm seeing here, where you've disabled an event for modular loading ebcause it's called often. I believe one is onCombatResult

Don't you think people might want to run some sort of python after a battle ?? It's a pretty common area to modify. Am I misunderstanding what this means ?
 
ok, the ini isn't quite self explanatory. Can you confirm if my analysis is correct


Code:
[modular python]
templatemod : modularPythonDemo
This is the contents of the ini

I see it as:

Code:
[modular python]
nameofpythonfile : nameOfModule

Is this right ?
 
I'm noticing in general, that you seem to have rebuild cvEventManager entirely, into the fallfurtherevents.py

I see lots of things broken up into seperate functions and marked by the event they correspond to. Rather than, as it was before, a huge mass of if statements within a few specific events (there were pages and pages of code within onUnitBuilt.

This method looks neater and more logical. Does it impair flexibility at all? And how about performance? does it have a significant effect on performance in either direction ?

And is this method a supplement to, or replacement of, the old method? if we merged it, would all event code have to be done this way?

Not that I'm complaining, I think it's better.
 
For creating my own python for my module, should I copy the first 9 lines from templatemod.py, and then add my stuff below? The first 9 lines look important
 
I see you online there Odalrick

*pokes you with a +2 Stick of Harassment*


does the @event.UnitBuilt() basically allow us to do modular python for any event, inserting it there as appropriate ?
 
Oh, and another question, since I'm already drowning you in them.

Can I have modular functions that call other functions?

Specifically, I want to make a "change back to druid" effect, and call it both from the Change Back spell, and also from the pyPerTurn while polymorphed. Can I do this without having to duplicate that code in two places ?
 
ok, the ini isn't quite self explanatory. Can you confirm if my analysis is correct


Code:
[modular python]
templatemod : modularPythonDemo
This is the contents of the ini

I see it as:

Code:
[modular python]
nameofpythonfile : nameOfModule

Is this right ?

Almost.

nameofpythonfile is correct

nameOfModule is the path to the python file starting in Assets\Modules\NormalModules

So if your pythonfile is in Assets\Modules\NormalModules\nameOfModule, you are correct. If you place it in Assets\Modules\NormalModules\nameOfModule\python it needs to be "nameOfModule\python".

You can even give an absolute path like d:\pythonstuff and it will work (for you).

Come to think of it, backslash ("\") may be interpreted as an escape character and cause bugs. If so, just use slash ("/").



The ini file is really only a temporary measure. I want to have the modules loaded in the same order as the xml, but that requires dll work.

I have considered just loading any python modules found anywhere in NormalModules. That sort of liberal execution of random code scares me though, which is why this last update (v.3) wasn't the first.
 
reading through modulareventmanager.py

There's a hell of a lot of things I'm seeing here, where you've disabled an event for modular loading ebcause it's called often. I believe one is onCombatResult

Don't you think people might want to run some sort of python after a battle ?? It's a pretty common area to modify. Am I misunderstanding what this means ?

My thinking is that it is easier to add events that are needed, than it is to remove events that are slow. New events will never break anything.

Feedback about needed events is great.


In the particular case of onCombatResult, I think that was a casualty of overzealous editing on my part. The combat log can be disabled because it is a resource hog, so I disabled all onCombatSomething without thinking.

I'll add a hook for onCombatResult in the next version.
 
I'm noticing in general, that you seem to have rebuild cvEventManager entirely, into the fallfurtherevents.py

Yes, CvEventManager is split in two, ModularEventManager which essentially handles the program logic and fallFurtherEvents which handles the game logic.

I see lots of things broken up into seperate functions and marked by the event they correspond to. Rather than, as it was before, a huge mass of if statements within a few specific events (there were pages and pages of code within onUnitBuilt.

This method looks neater and more logical. Does it impair flexibility at all? And how about performance? does it have a significant effect on performance in either direction ?

It is much more flexible, and I don't really know how to make things modular without it.

There is a performance penalty, but only time will tell if it is significant.

On the other hand, using a hash table as the event system does, rather than a mass of if statements should be quicker. In other words, registering an event for having a library built should be quicker than looking at every building that is built to see if it is a library.

And is this method a supplement to, or replacement of, the old method? if we merged it, would all event code have to be done this way?

Not that I'm complaining, I think it's better.

It is a supplement, though anything that should be possible to replace needs to be done in this fashion. Which is why I rebuilt CvEventManager.

All logging still uses the old system, if you want to see how that works.
 
For creating my own python for my module, should I copy the first 9 lines from templatemod.py, and then add my stuff below? The first 9 lines look important

Code:
import modularEventKeeper
event = modularEventKeeper.EventKeeper()
spell = modularEventKeeper.CumulatingDict()
randomEvent = modularEventKeeper.CumulatingDict()

are the important lines. Anything before "# module code".

You will have to import anything else you want to use yourself.

fallFurtherEvents has a huge list of imports:
Spoiler :
Code:
from CvPythonExtensions import *
import CvUtil
import CvScreensInterface
import CvDebugTools
import CvWBPopups
import PyHelpers
import Popup as PyPopup
import CvCameraControls
import CvTopCivs
import sys
import CvWorldBuilderScreen
import CvAdvisorUtils
import CvTechChooser
import CvIntroMovieScreen
import CustomFunctions
import ScenarioFunctions
#FfH: Card Game: begin
import CvSomniumInterface
import CvCorporationScreen
#FfH: Card Game: end
#FfH: Added by Kael 10/15/2008 for OOS Logging
import OOSLogger
#FfH: End Add
# globals
cf = CustomFunctions.CustomFunctions()
gc = CyGlobalContext()
localText = CyTranslator()
PyPlayer = PyHelpers.PyPlayer
PyInfo = PyHelpers.PyInfo
sf = ScenarioFunctions.ScenarioFunctions()
#FfH: Card Game: begin
cs = CvCorporationScreen.cs
#FfH: Card Game: end

While templatemod makes do with a minimalistic
Code:
import CvPythonExtensions
gc = CvPythonExtensions.CyGlobalContext()


does the @event.UnitBuilt() basically allow us to do modular python for any event, inserting it there as appropriate ?

Yes, look in modularEventKeeper to see what events are available.
Currently:
Spoiler :
BeginGameTurn
BeginPlayerTurn
BeginPlayerTurnCivilizationType
BeginPlayerTurnStateReligion
BuildingBuilt
BuildingClassBuilt
BuildingTypeBuilt
ChangeWar
Chat
CityAcquired
CityAcquiredAndKept
CityBuilt
CityDoTurn
CityGrowth
CityLost
CityRazed
CorporationFounded
CorporationRemove
CorporationSpread
CultureExpansion
EndGameTurn
EndGoldenAge
EndPlayerTurn
EndTurnReady
FirstContact
GameEnd
GameStart
GoldenAge
GoodyReceived
GreatPersonBorn
ImprovementBuilt
ImprovementDestroyed
KbdEvent
LoadGame
ModNetMessage
MortalUnitKilled
NukeExplosion
PlayerChangeStateReligion
PlayerGameStart
PlayerGoldTrade
PlotFeatureRemoved
PreSave
ProjectBuilt
ProjectTypeBuilt
ReligionFounded
ReligionRemove
ReligionSpread
RouteBuilt
SetPlayerAlive
TechAcquired
UnInit
UnitBuilt
UnitClassCreated
UnitCreated
UnitKilled
UnitLost
UnitPillage
UnitPromoted
UnitSpreadReligionAttempt
UnitTypeCreated
UnitUnitCombatCreated
VassalState
WindowActivation


Then search in ModularEventManger to find out what data the function receives when called.

Or look in fallFurtherEvents and hope there is a previous example.

Yes, the documentation leaves something to be desired.

Oh, and another question, since I'm already drowning you in them.

Questions are good, it let's me know what parts are unclear.

Can I have modular functions that call other functions?

Specifically, I want to make a "change back to druid" effect, and call it both from the Change Back spell, and also from the pyPerTurn while polymorphed. Can I do this without having to duplicate that code in two places ?

Yes.

Look at findCommander in CvSpellInterface. It is used in reqAttachMinion, attachMinion and getHelpAttachMinion.
 
It is much more flexible, and I don't really know how to make things modular without it.

There is a performance penalty, but only time will tell if it is significant.

On the other hand, using a hash table as the event system does, rather than a mass of if statements should be quicker. In other words, registering an event for having a library built should be quicker than looking at every building that is built to see if it is a library.

I'm a bit confused here. What exactly would be slower, then? Are we likely to see a net gain or loss in terms of performance, for various types of functions?
 
I'm a bit confused here. What exactly would be slower, then? Are we likely to see a net gain or loss in terms of performance, for various types of functions?

Yes.

This is the sort of question only profiling can answer.

I don't anticipate any noticeable effects.

<rambling>Basically the event system replaces straightforward executing code with iterating though functions that execute that same code. All things being equal, the modular system has to execute the same code as non-modular systems plus the code that makes things modular.

My guess is that it will be on the order of milliseconds per turn though, hardly worth mentioning.

Then there are the huge sawtooth if statements that I've changed to using hashes essentially. Where there previously was tens of comparisons, there is now straight jump to the appropriate place.

Again, probably on the order of milliseconds per turn.

In the extreme where there are a billion buildings, for instance, and each building has a python effect on being built, the modular system will be much quicker...</rambling>
 
This also includes a somniumintterface.py

What exactly did you change in there? and why wouldn't changing that back also be necessary to uninstall ?

Missed this question earlier.

CvSomniumInterface imports another module which imports another module which in turn imports CvSomniumInterface, creating a cicular dependency.

Apparently importing the modules in precisely the right order won't produce any problems, but guessing what that order is...

I moved an import statement after the CvSomnium class in CvSomniumInterface which fixed my problems. It's a minor change and doesn't actually change anything.
 
Ok. I'm just getting around to actually trying this. Tested on our latest release. I have a big cascading failure of python errors on startup. They are as follows, in order:
Spoiler :
Code:
---------------------------
Python Exception
---------------------------
Traceback (most recent call last):

---------------------------
OK   
---------------------------
Code:
---------------------------
Python Exception
---------------------------
  File "<string>", line 1, in ?

---------------------------
OK   
---------------------------
Code:
---------------------------
Python Exception
---------------------------
  File "<string>", line 52, in load_module

---------------------------
OK   
---------------------------

Code:
---------------------------
Python Exception
---------------------------
  File "CvEventInterface", line 13, in ?

---------------------------
OK   
---------------------------

Code:
---------------------------
Python Exception
---------------------------
  File "<string>", line 52, in load_module

---------------------------
OK   
---------------------------

Code:
---------------------------
Python Exception
---------------------------
  File "ModularEventManager", line 1729, in ?

---------------------------
OK   
---------------------------

Code:
---------------------------
Python Exception
---------------------------
ImportError
---------------------------
OK   
---------------------------

Code:
---------------------------
Python Exception
---------------------------
: 
---------------------------
OK   
---------------------------
Code:
---------------------------
Python Exception
---------------------------
Cant find the install directory.
---------------------------
OK   
---------------------------

Code:
---------------------------
Python Exception
---------------------------


---------------------------
OK   
---------------------------

Code:
---------------------------
Python Exception
---------------------------
Failed to load python module CvEventInterface.
---------------------------
OK   
---------------------------


I'll hazard a guess that it's not supposed to do that.
 
I'll hazard a guess that it's not supposed to do that.

Not for ordinary values of "supposed to".

It means the mod can't find the file "modular_python_marker.txt".

It is supposed to be in the mod directory, so that the mod knows where to look for files. (The contents of the file is unimportant, only the position in your file system.)

Check if there is such a file in your mod dir, I'm sure I included it in the zip.

It must be exactly that name, down to capitalization, and there can only be one such file in the Beyond the Sword hierarchy.

The other reason I can think of is that you have installed the mod in some way I didn't know was possible.
 
That explains it. I just copied and pasted the assets folder. my bad.

But why does it need to give 12 different errors for one file. Couldn't you just make it say "Error: modular_python_marker.txt is missing"
 
That part is something strange Fall Further or Fall from heaven added or broke or something.

It is actually just one error message, but somewhere each line turns into a separate message.

It's actually supposed to read:
Code:
Traceback (most recent call last):
  File "<string>", line 1, in ?
  File "<string>", line 52, in load_module
  File "CvEventInterface", line 13, in ?
  File "<string>", line 52, in load_module
  File "ModularEventManager", line 1729, in ?
ImportError
: 
Cant find the install directory.

Failed to load python module CvEventInterface.

I've added "modular_python_marker.txt is missing" to the error message though.
 
hmm, I copied the text file to the mod directory. Same level as assets folder, as it was in the archive. It hasn't fixed the errors.

Did you put it in the wrong part of the archive, perhaps? Where in the folder structure should it go, exactly ?
 
Back
Top Bottom