Multiplayer OOS and python...I think I need to eat script data pickles?

sab89

Chieftain
Joined
Mar 6, 2008
Messages
25
This is a biggie. I don't have script data set up and tried to play my mod (which works beautifully single player...though the AI isn't set up).

Two things I do in this mod is:

- Spawn a unit when a xy is triggered
- add a point if that spawned unit is held on another xy. When 10 points are reached, that player wins.

So I assume I would need to store the units created and the scoring in script data? Digging through American Revolution for examples, but any tips or explanations are highly appreciated.

A lot of python in Civ 4 seems to be very self-exploratory, so if I figure this out I'll post my working code here.
 
So here is what I 'think' I understand about script data, pickles, and making your python multiplayer friendly (please correct me).

When something is changed in the game it must be stored in data. The next player 'gets' the thing which is changed from data. Initially, the something which can change is 'set'.

To make this process easier, most modders create functions which set everything up.

For my mod I have players score a point when they return a specific unit to a specific tile.
Code:
        def getP1Score (self, iPlayer):
		pPlayer = gc.getPlayer(iPlayer)
		szScriptData = pickle.loads(pPlayer.getScriptData())
		iHScore = szScriptData[0]
		return P1Score
	
	def setP1Score (self, iPlayer, iValue):
		pPlayer = gc.getPlayer(iPlayer)
		szScriptData = [iValue]
		pPlayer.setScriptData(pickle.dumps(szScriptData))		
	
	def changeP1Score (self, iPlayer, iChange):
		pPlayer = gc.getPlayer(iPlayer)
		szScriptData = pickle.loads(pPlayer.getScriptData())
		iP1Score = szScriptData[0]
		
		iP1Score += iChange
		szScriptData[0] = iP1Score
		pPlayer.setScriptData(pickle.dumps(szScriptData))

When the game starts, in my onGameStart function, I set the value (to 0)

Code:
	def onGameStart(self, argsList):
		
		for iPlayerLoop in range(self.iNumPlayers):
			self.setP1Score(iPlayerLoop, 0)

When the score is changed, onEndGameTurn, I call changeP1Score.

Code:
		if iX == 28 and iY == 24: 
			self.changeP1Score(iP1, 1) #iP1 is player 0

Also within onEndGameTurn, I get the score to check if anyone won.

Code:
		if self.getP1Score == 10:
			CyGame().setWinner(0, 2)

IF that works, then I need to do it 3 more times (cause this is a 3 player mod). Since units are spawned via python, will I also need to 'set, get, and change' them as well? If so, I assume it will be similar to above. Set it at start, change it when they die, and get it at onEndGameTurn?

This community has been great so far, thanks and I hope you can help me solve this last one.
 
Well. Really hit a wall on this one. Attached my code, hope someone can help shine a light.

Happy Easter!
 

Attachments

help me. any hints on making SP friendly code MP friendly?

So it's not working eh? I'm going to need to learn about this as well if I'm going to be able to do my next project. I hope someone can help you.
 
I figured it out.

My main problem were in 2 areas.

1 - Spawning units using massive random counters. Basically, at the end of game turn, I would run a function which rolled like 5 sorenrands. Game didn't like that. Instead I tidied things up.

2 - I ripped off clearFunction, getFunction and setFunction code from some mods. After a few runs, figured out how to do it. (seems the community here is dead...too bad)
Code:
	def onPreSave(self, argsList):
		"called before a game is actually saved"
		CvUtil.pyPrint('OnPreSave')
		self.setScoreVariables()
	
	def onSaveGame(self, argsList):
		"return the string to be saved - Must be a string"
		return ""

	def onLoadGame(self, argsList):
		self.getScoreVariables()
		return 0    
    
	def clearScoreVariables( self ):
		
		self.iP1Score = 0
		self.iP2Score = 0
		self.iP3Score = 0

	def setScoreVariables(self):
		pPlayer = gc.getPlayer(0)
		
		szScriptData = []

		szScriptData.append(self.iP1Score)
		szScriptData.append(self.iP2Score)
		szScriptData.append(self.iP3Score)	
		
		print szScriptData

		# Save Script Data - Score
		pPlayer.setScriptData(pickle.dumps(szScriptData))	
		
	def getScoreVariables(self):
		
		pPlayer = gc.getPlayer(0)
		
		# Load Script Data - Score
		szScriptData = pickle.loads(pPlayer.getScriptData())
		
		self.iP1Score = szScriptData[0]
		self.iP2Score = szScriptData[1]
		self.iP3Score = szScriptData[2]	
		return

Having set it up under def init I call self.clearScoreVariables()

At the end of a turn (when I reward points) I use :

Code:
				if iX == 20 and iY == 32:  
					self.iP1Score = self.iP1Score + 1

and check to see if P1, P2, or P3 have reached a target score (of 10) with

Code:
		if self.iP1Score >= 50:
			CyGame().setWinner(0, 2)

Would love to figure out how Charlemagne has the UI set up showing score...but interface is waaaay beyond me (and haven't found anything on this forum or anywhere teaching it.)

Best of luck.
 
I also have MP saves problem in my mod. In Single player - all works fine, but when I load MP saves - crash.
But if modular system=1 - MP saves dont loaded, if modular system=0 - the MP saves loaded
 
As far as I can tell, pickling is only needed for saving your game. If I recall, sorenrand is okay to use in multiplayer, as it is designed to keep everything in sync. But yes, during an init, like your event manager or some other class, set up your variable using your self.variable = 0, then reference it in your function as self.counter += 1 or somesuch. You'll need to make another couple of functions that pickle and unpickle this data in the event of a save, but you can leave that as a feature for the finished product.
 
Ok, let me see if I have this straight. Let's say I want to have an alternate scoring system leading to some alternate victory type, similar to what sab89 is doing, and I want to use this score not only to determine victory, but also to appear in the graph on the info screen. So I need to save and load the score and also the score history for each turn so I can draw the graph after a load.

Can I put the data in my event manager? Then make a function for accessing this data from another module (like the info screen), and functions for save and load. Where would I call the save and load functions? Is there an event in the event manager where I can pickle them? Am I forgetting something?
 
Take a look at Final Frontier - It handles the pickling of the entire collection of solar systems and such on game saving and game loading. So you can put your picking calls in the same places that Final Frontier does to preserve data between saves. As for accessing them from another module, that will take some knowledge of object oriented referencing. I don't think I ever got the hang of it, but I sure tried when I was doing my pre-DLL python coding.
 
Thanks Mylon,

I think I figured this out. In Final Frontier, the data instance is part of the event manager and is saved and loaded by attaching the data to a CvPlot or CvGame with setScriptData.

To access this data from some info screen, the screen imports the CvEventInterface.py and calls the getEventManager() function to retrieve the event managers instance where you can get the relevant data.
 
Back
Top Bottom