[C++] Reading database entries

Lord Yanaek

Emperor
Joined
Aug 15, 2003
Messages
1,662
Hi there. With few people being able to mod the C++ code, and those people having their own modding needs and limited free time, i guess if i want some modifications done, i'll have to do them myself.
That's OK except for the fact that when i mess with C++ code, i can't help but feel like a mad scientist mixing together various chemicals without knowing what they do :hammer2:
Of course, i know i could end up not blowing everything up :crazyeye: but i try to stay on the safe side by not touching it.

Now, the first thing i would need is the ability to get info from the database. I looked at an old Civ4 tuto by Kael but it was Civ4 and the database was in xml files and not SQL.
I tried to follow the track of some data used by the code (unit production cost) and i think i found it's origin in CvUnitClasses.cpp
Code:
m_iProductionCost = kResults.GetInt("Cost");
So i searched for this kResults and found it in CvDatabaseUtility.cpp. It's also used plenty of times when accessing data that i know is in the database. Seems good so far and if there is a high level instruction available, i might be able to use it without frying my computer with bad code :blush:
However, i see only one argument when kResults is called, and it would be the table column where the data is stored. I don't see where the table is defined. Many tables have a "Cost" column. :confused:

Am i at least on the right tracks with this kResults. I can't really find where it is defined nor what kind of code "stuff" (object being something specific) it is.
The lower the level i have to work with, the lower my chances of understanding. My hope, if i am to have some success, is to find some methods i can use because i can't write my own.
 
Database access in most languages is

1) Construct the query
2) Execute the query to get a "results set" (a list of rows that the query returns)
3) Loop over each result (row) in the results set (list)
4) Extract individual values (colums) from the result (row)

This is how both Lua and C++ do it, eg (from my CustomMods.cpp file)

Code:
Database::Results kResults;
DB.SelectAll(kResults, "CustomModOptions");

while (kResults.Step()) {
  const char* szName = kResults.GetText("Name");
  const int iValue = kResults.GetInt("Value");

  CUSTOMLOG("Load: %s = %d", szName, iValue);
  m_options[string(szName)] = iValue;
}

The XML that both creates and populates the custom table is

Code:
<GameData>
  <Table name="CustomModOptions">
    <Column name="Name" type="text" notnull="true"/>
    <Column name="Value" type="integer" default="1"/>
  </Table>

  <CustomModOptions>
    <!-- Adds a notification when someone circumnavigates the globe -->
    <Row Name="GLOBAL_ENABLE_MAGELLAN" Value="1"/>

    SNIP
  </CustomModOptions>
</GameData>

There is also DB.Execute for when you don't just want everything in the table, so the above could also be written as

Code:
Database::Results kResults;
if (DB.Execute(kResults, "SELECT Name, Value FROM CustomModOptions")) {
  while (kResults.Step()) {
    const char* szName = kResults.GetText("Name");
    const int iValue = kResults.GetInt("Value");

    CUSTOMLOG("Load: %s = %d", szName, iValue);
    m_options[string(szName)] = iValue;
  }
}

Note that execute returns a boolean that tells you if the SQL was valid.

If you're writing advanced/complex SQL with place-holders (? in where clauses etc) you can also bind values into them before calling Step(), and if you are executing the same query many times with different place-holder values you can also Reset() the query and then bind different values. Personally I just build the query with the values I want and then execute it. It may be fractionally less efficient, but is significantly easier to understand by others or yourself several weeks/months later!
 
Now that assumes that you are after custom data from the database, if you just want the C++ equivalent of GameInfo.Units[iUnit].Cost etc almost all of that data is pre-cached by the system and you just need to find the right class and accessor to get at it
 
Yep, custom data added to the SQL database. I'm familiar with looping through database entries or raw text lines (conceptually, it's really not that different) and can do it in Lua or JScript.

Do those DB.Execute, kResults.Step(), Database::Results methods come from some standard C++ library for which i could find a documentation somewhere? Google wasn't very helpful with those as keywords ( www.footballresults.org was the first site for a search on "Database::Results" :wallbash: )
I made the assumption the database accessors had been written specifically for Civ and was trying to find where in order to get, at least, method names, but as i said i didn't found the "birth place" of kResults and we still have no C++ wiki.
I don't want to simply take those lines and change some arguments hoping it would work. That kResults just seem to magically appear for me. If the dot has the same meaning in C++ than in Lua, kResults.Step() would mean kResults is an object and Step() a method, but i can't find where this object is defined.

I'm really unfamiliar with C++. I'm not against learning some languages, i already learned by myself the basics of Lua and a couple other "scripting" languages, but C++ is several orders of magnitude more complex. Re-using a comparison you made in another thread, i can probably learn to drive (use pre-built methods), but i couldn't build a car from raw iron (write those methods) ;)

EDIT : Let's see if i can at least guess this right.
Code:
Database::Results kResults;
???? I'm not familiar enough with C++ syntax i fear to understand whether kResults is an argument or anything.
Code:
if (DB.Execute(kResults, "SELECT Name, Value FROM CustomModOptions")) {
if to check everyhting works fine and on the same line you make a call to DB.Execute method, passing an SQLite querry.
Code:
  while (kResults.Step()) {
    const char* szName = kResults.GetText("Name");
    const int iValue = kResults.GetInt("Value");

    CUSTOMLOG("Load: %s = %d", szName, iValue);
    m_options[string(szName)] = iValue;
  }
So you are using a step method on kResults, kResults must be an array of some sort and .Step() a standard array method. Might explain why i didn't found a constructor for kResults.
Those const i know are a special case of variable declaration in C++. Don't know when to use them, but i know what they mean.
CUSTOMLOG must be a function you built for logging what you do.
Finally, you store key/value pair in m_options so you will be able to get the value using m_options.Name later.
 
If you're using VS-2010 you can highlight a method/object, right click and go to definition

DB is in CvGlobals.h and is defined as "#define DB (*GC.GetGameDatabase())"

GetGameDatabase() is a few lines lower down and is "inline Database::Connection* CvGlobals::GetGameDatabase()" and "Connection" is in Database.h which is where you can find SelectAll(), Execute() etc

Execute() starts "Execute(Results& kResults," and Results is in DatabaseResults.h and that gives you Bind(), Step(), GetText() etc

BUT Database.h and DatabaseResults.h come from "CvGameCoreSource\CvGameDatabase\include" which are just the headers and lib files for one of the other Civ DLLs we don't have the source for - so you're down to deducing how those functions work/are used by looking at where/how they are used in the source we do have.

Documentation - you're having a laff! It's all investigation ;)

And sometimes you don't need to be able to navigate/drive, if all you have to do if follow (copy) the person in front of you ;)
 
Thanks. Well, i'll have to reinstall VS since i'll need it for compilation anyway. Currently i was just using Notepad++, my default editor, to look at the code.
And sometimes you don't need to be able to navigate/drive, if all you have to do if follow (copy) the person in front of you
Well, even if your car is attached to the one in front of you, you'll end up out of the road if you don't know how to turn the wheels ;)
I learned the hard way that copy-pasting code is the perfect example of false time-saver. When you start getting bugs and you have no clue what your code does, you realize you just wasted your time doing all those copy/pastes.
So now, i try to get some general idea of how the code works before changing anything.

In this particular case, I somehow put too much importance in kResults, while DB was the important object. Thanks for putting me in the right direction. :)
 
Back
Top Bottom