DLL coding thread

The Civic option iModdersCode1, should be replaced with XML tags for the Options. This was something I added in an attempt to help me simplify adding new XML tags and reduce the number of times I had to rebuild the DLL, which use to take 20 minutes, now much much faster thanks to Night. I originally just had MODER_CODE_1 and such but slowly replaced the names as I added effects.

The Codes...
Code:
MODER_CODE_FASTER_CHOP_TREES, -> As stated
MODER_CODE_SPICE_ROUTE,  ->Allows Spice route, this has been replaced by AllowsTradeScreen
MODER_CODE_SILK_ROAD_ROUTE,  ->same as above
MODER_CODE_TRADING_POST,  ->Allows Establishing Trading Post in Foreign villages
MODER_CODE_ONLY_NONE_NATIVES,  ->Not Used
MODER_CODE_NATIVES_ONLY,  ->Not used
MODER_CODE_ANYONE,  ->Not Used
MODER_CODE_NATIVES_VIKING_AGE, ->Not Used
MODER_CODE_ALLOWS_TRADE_FAIR,  ->same as SPICE_ROUTE
MODER_CODE_CANNOT_WORK_BUILDINGS,   ->not Used
MODER_CODE_NONE_MILITARY,  ->Not Used 
MODER_CODE_SQUIRE,  ->Not Used 
MODER_CODE_CRAFTMEN_NOBLES,  ->Not Used 
MODER_CODE_AI_ONLY,  ->Not Used 
MODER_CODE_CANNOT_LEARN_NATIVE,  ->Not Used

So, only two of the above options need to be turn into XML tags, then they all can be removed.
 
MODER_CODE_FASTER_CHOP_TREES, -> As stated
I think FasterBuildFeatureTypes should be able to handle that, at least based on the text. The code itself needs a bit of digging into.

MODER_CODE_TRADING_POST, ->Allows Establishing Trading Post in Foreign villages
Adding a tag for that one should be minor compared to the fact that this flag has 15 different features.
 
Hello Night and Kailric.

I am having a conversation with Archid about the concept of Save Game Compatibility across development versions. I know you guys did a lot of work around this, but I cannot remember how much.

Like do new tags break saves, or was that one of the things you 'fixed'.

I just thought I would ask incase FTTW could beefit from your vast pool of wisdom :bowdown::cooool:
 
That is a huge "depends", mostly what do the new tags do is the question? In other words, just adding tags should not break a saved game, its what you do with those tags that can break a game. There are tons of things you can add though and if you get trained by Night you can even add things that would break saved games if you didn't allow for it with flags.

Adding huge things like new Yields of course will kill saved games, way to many variables are saved based on the size of YieldInfos. In M:C you could probably add a new Tech and it wouldn't kill saved games as Techs are applied at load up and are not saved. Also, I know before if you added a new promotion it would break saved games no matter what you did, unless Night fixed that. Anyway, I am sure Night will set me straight on this.
 
Adding huge things like new Yields of course will kill saved games, way to many variables are saved based on the size of YieldInfos.
You are wrong. You need to be taught a lesson for saying my work is broken :ar15::run:

The concept is as follows: (let's use yields as an example)
Saving is done like vanilla (more or less). No magic there. The only difference is that CvGame saves a bunch of strings. To be precise, loops CvYieldInfo and saves Type for each of them. It also saves how many there are.

On load, it makes a vector. It then loads the strings. For each string, it checks the current YieldInfo Types to find a match for the saved one. If it is found, the new index is added to the end of the vector. If it isn't found (removed), -1 will be added.

Say we added YIELD_FOOD_POOR before YIELD_LUXURY_FOOD. YIELD_LUXURY_FOOD is 1 at the time of saving, but now it is 2. This mean vector[1] = 2.

Now whenever YieldTypes is loaded, it will load a number, look up that index in the array and it will then use the number from the vector rather than the one from the savegame. We know the savegame called luxury food 1, but we call it 2 now, hence we "fake read" 2 and your city will still have 10 luxury food stored.
The trick here is that I modified FDataStreamBase.h to do this automatically. This mean you activate the conversion automatically by using read(&YieldArray).

The real magic comes with JIT arrays. They have a buildin read and write. When it is writing, it first loops the array to find the index of the highest non-default value. It saves this index and then all values from 0 to this index.

On read: (say it's an int array, just for simplicity. Works with many var types)
The JIT array reads the number of saved ints(obviously). For each int, it looks up the index in the conversion vector and it then writes the int to the index from the conversion vector.
Result: new types stays with the array's default value (often 0). Removed types are thrown away and will not enter the array.
The really important part here is that JIT arrays always read the same number of bytes as they write even if the array length has changed. That's the key to savegame compatibility.

One feature I added is that if the number of yields didn't change, but a yield is missing, it assumes the yield to have been renamed. It might check if the ID is used by another yield in the savegame. Can't remember.

This is done with all JIT array types. All 30 of them.
Spoiler :
enum JIT_ARRAY_TYPES
{
// add to end for max savegame compatibility
JIT_ARRAY_BONUS = 0,
JIT_ARRAY_BUILD,
JIT_ARRAY_BUILDING,
JIT_ARRAY_BUILDING_CLASS,
JIT_ARRAY_BUILDING_SPECIAL,
JIT_ARRAY_CIVIC,
JIT_ARRAY_CIVIC_OPTION,
JIT_ARRAY_CULTURE,
JIT_ARRAY_ERA,
JIT_ARRAY_EMPHASIZE,
JIT_ARRAY_EUROPE,
JIT_ARRAY_EVENT_TRIGGER,
JIT_ARRAY_FATHER,
JIT_ARRAY_FATHER_POINT,
JIT_ARRAY_FEATURE,
JIT_ARRAY_HANDICAP,
JIT_ARRAY_HURRY,
JIT_ARRAY_IMPROVEMENT,
JIT_ARRAY_LEADER_HEAD,
JIT_ARRAY_PROFESSION,
JIT_ARRAY_PROMOTION,
JIT_ARRAY_ROUTE,
JIT_ARRAY_TERRAIN,
JIT_ARRAY_TRAIT,
JIT_ARRAY_UNIT,
JIT_ARRAY_UNIT_AI,
JIT_ARRAY_UNIT_CLASS,
JIT_ARRAY_UNIT_COMBAT,
JIT_ARRAY_UNIT_SPECIAL,
JIT_ARRAY_YIELD,

It turns out that it is missing a few, such as game options :(

I am having a conversation with Archid about the concept of Save Game Compatibility across development versions.
Where is this discussion? I have a feeling Archid could benefit from not having the info relayed by Lib.Spi't :p
 
Maybe I should write a wiki page on how to preserve savegames, seeing that I came up with a unique approach to the problem. I also added other interesting code changes, such as functions called readWrite(). There is a bool to tell if it is reading or writing, which mean it ends up doing the same as vanilla. The difference is that there is only one function to list variables in, meaning the order can never go out of sync.

Seeing as I have the coding ability of a brain damaged gibbon!
You doubled your science score :eek:
 
It happened purely by the osmosis of being near your words!

You guys are fun to watch. Osmosis? We are studying that right now in A&P class. The teacher made an example of me the other week speaking about Diffusion. He said, "now if this guy right here farts (gesturing towards me), you there would smell it, those around there would start to smell, and eventually those in the back of the room would smell it." I just shrugged and said, "sorry guys":blush:

You are wrong. You need to be taught a lesson for saying my work is broken :ar15::run:

Unless you've managed to mod FTTW while I wasn't looking, then I was partly right :)

I know you've done stuff to M:C that I don't even know about or have forgot so I need to just hush and let you answer all these types of questions:cool:
 
Unless you've managed to mod FTTW while I wasn't looking, then I was partly right :)
I did, but not related to savegames. However you are wrong if you talk about FTTW saving as well since even just adding a tech or civic would screw up. The problem is that the vanilla approach saves and loads the length of the XML files and if just one of them changed, then the savegame is broken. This mean you can only add to XML files if that file isn't saved as arrays. The only one I can think of, which might work in vanilla BTS is civics because players save the length of civicTypes and for each int in that array it saves the selected civic. I'm not sure though.
 
I added a new class called InfoArray, which does the job as some of the vectors of int pairs in InfoClasses. However InfoArray works a little better internally and the primary function is to make it way easier to use without adding bugs.

Usage:
Read from XML:
Code:
m_iaRandomGrowthClasses.assign(pXML, RandomGrowthClasses);
Replaces:
Code:
m_aRandomGrowthUnits.clear();
if (gDLL->getXMLIFace()->SetToChildByTagName(pXML->GetXML(),"RandomGrowthClasses"))
{
	if (gDLL->getXMLIFace()->SetToChildByTagName(pXML->GetXML(),"RandomGrowthClass"))
	{
		do
		{
			pXML->GetChildXmlValByName(szTextVal, "UnitClassType");
			int iUnitClass = pXML->FindInInfoClass(szTextVal);
			int iPercentChance = 0;
			pXML->GetChildXmlValByName(&iPercentChance, "iPercentChance");
			m_aRandomGrowthUnits.push_back(std::make_pair((UnitClassTypes) iUnitClass, iPercentChance));
		} while(gDLL->getXMLIFace()->NextSibling(pXML->GetXML()));
		// set the current xml node to it's parent node
		gDLL->getXMLIFace()->SetToParent(pXML->GetXML());
	}
	// set the current xml node to it's parent node
	gDLL->getXMLIFace()->SetToParent(pXML->GetXML());
}

Access function:
Simple. Just return a const pointer to the instance of InfoArray.

The class itself supplies get(index, iNum) where the latter optional part tells which of the pair you want. getLength() is also available, but get(index) will return -1 if it is outside the array, hence another stop condition.

Applying changes to an array:
JITarray.add(InfoArray, 1);
That 1 can be skipped, but it can also be -1, in which case the InfoArray is subtracted from the array.

The code isn't restricted by using pairs. In fact on creation it takes an int (default 2) of how many variables it should have for each index. It mean it is easily possible to use 1 (BoolArray, is index present or not) or 3 (say buildallow for terrain, route, iAllow).

InfoArray::assign() can also take a JIT array or BoolArray as argument, in which case it copies the content of those. You can't edit the content, only overwrite meaning merging two InfoArrays would be to add both to a temp JIT array and then assign that array to the InfoArray, which should have the result.
This approach simplifies the internal code and it also ensures that the indexes are in chronological order.

Now that I wrote that, I realized that they might not be in chronological order in the XML file. I just assumed that when writing the code. Not sure what to do about that.
 
announcement.gif

Major breakthrough in InfoArray

Normally we would have to enter XML code like this:
Code:
<FreeBuildingClasses>
	<FreeBuildingClass>
		<BuildingClassType>BUILDINGCLASS_GRANARY</BuildingClassType>
		<bFreeBuildingClass>1</bFreeBuildingClass>
	</FreeBuildingClass>
	<FreeBuildingClass>
		<BuildingClassType>BUILDINGCLASS_MINT</BuildingClassType>
		<bFreeBuildingClass>1</bFreeBuildingClass>
	</FreeBuildingClass>
</FreeBuildingClasses>
However that is a bit lame considering BuildingClassType is a bool. Either it's there or it isn't.

I changed the schema file and entered:
Code:
<FreeBuildingClasses>
	<BuildingClassType>BUILDINGCLASS_GRANARY</BuildingClassType>
	<BuildingClassType>BUILDINGCLASS_MINT</BuildingClassType>
</FreeBuildingClasses>
The adaptive InfoArray code is working and it read the same thing as the first layout. No DLL codechange at all. Naturally I planned this when I wrote the reading code, but I didn't make a fullscale test of it until now.

Also I think I figured out how the DLL reads XML files and how to control what it does and what it expects. Half the XML handling code is in the exe and it is kind of abstract, but unlike most of the vanilla DLL code, this part is surprisingly well commented. It has like 3-4 lines on average telling what each function is supposed to do. I wish we commented that well :lol:

While I wrote InfoArray for CivEffects, it is generic enough to use for any XML info class and now I'm considering if it is a good idea to use it everywhere... or at least for all bool arrays.
 
Normally we would have to enter XML code like this:
Code:
<FreeBuildingClasses>
	<FreeBuildingClass>
		<BuildingClassType>BUILDINGCLASS_GRANARY</BuildingClassType>
		<bFreeBuildingClass>1</bFreeBuildingClass>
	</FreeBuildingClass>
	<FreeBuildingClass>
		<BuildingClassType>BUILDINGCLASS_MINT</BuildingClassType>
		<bFreeBuildingClass>1</bFreeBuildingClass>
	</FreeBuildingClass>
</FreeBuildingClasses>
However that is a bit lame considering BuildingClassType is a bool. Either it's there or it isn't.

I changed the schema file and entered:
Code:
<FreeBuildingClasses>
	<BuildingClassType>BUILDINGCLASS_GRANARY</BuildingClassType>
	<BuildingClassType>BUILDINGCLASS_MINT</BuildingClassType>
</FreeBuildingClasses>
The adaptive InfoArray code is working and it read the same thing as the first layout.
Nice one. :)

This always was a pain just to have to read it. Anyway, one question: in Civ4CivilizationInfos.xml (from the top of my head, haven't checked it in the files) you do not add, but exclude certain professions.
Should read like this:
Code:
<ProfessionType>PROFESSION_BRAVE</ProfessionType>
<bValid>0</bValid>
Does your new code cope with that as well?
 
Back
Top Bottom