-
Working with a database
Firstly, forgive me - I'm pretty much a noob at C++, and only began re-learning it last week.
I'm trying to make a simple game (mostly as practice). I want to access a database so that I can read the definition of an item directly from there. If given the unique ID of an item (short int), I need to be able to use that to look up various statistics about the item, without having to load the entire list of items into the memory.
Thing is, I only need to read from the database - I don't need to write anything to it, so maybe there's a way other than databases that I could do this? My original idea was to use an XML file, but I figured a database might be better.
I'm using Microsoft visual C++ 2010 as my IDE/compiler. I know how to work with classes, arrays, structs, and other basics.
Any advice or links would be much appreciated. Thanks in advance!
-
Re: Working with a database
Depends on what database you're using, but you may find the CRecordset class useful.
-
Re: Working with a database
How would I go about using CRecordset?
-
Re: Working with a database
-
Re: Working with a database
Mkay, well knowing how to access a database is fine and dandy, but how do I create a database? Everywhere I look, the program is built almost entirely for use with a server, but I don't need that. I need something simple.
Or, here's a better question. Since I don't intend to actually write anything to the database via the program, and I don't need online connectivity, am I better off just defining the items right in the program? I didn't want to initially, because for one, isn't that really costly for memory? For two, that hurts modularity, and I'd like to be able to change item definitions without having to re-compile the whole program.
What do you guys think is the best way?
-
Re: Working with a database
In order to use databases you need a database engine or a full database server. There are some for free.
Engines: MS Access Jet engine, MSDE.
Full servers: MS SQL 2005 Express Edition, MySql.
It's very unusual for a game to install a database engine on a player's computer. No matter how complex is a game it cannot be that complex to make a database engine a vital necessity.
My suggestion is to use arrays of structures or, for very sophisticated situations, even arrays of class instances.
-
Re: Working with a database
If you want to use a database and don't want to have to install anything, consider using the MS Access (Jet) db.
One way to read from it is to use the ATL OLEDB consumer classes. You create the database using Access, but you don't need to have Access installed on the user's machines.
By default Access files are .mdb but you can rename these to anything you wish.
__________________________________________________________
Arjay
See my latest series on using WCF to communicate between a Windows Service and WPF task bar application.
Tray Notify - Part I Tray Notify - Part II
Need a little help with Win32 thread synchronization? Check out the following CG articles and posts:
Sharing a thread safe std::queue between threads w/progress bar updating
Simple Thread: Part I Simple Thread: Part II
Win32 Thread Synchronization, Part I: Overview Win32 Thread Synchronization, Part 2: Helper Classes
www.iridyn.com
-
Re: Working with a database
Basically, the inventory I have right now is set up like this:
Code:
invItem inventory[30];
invItem is a struct, defined thusly:
Code:
struct invItem
{
unsigned short ID;
char Quantity; //Yes, the maximum you're allowed to carry is 255. This is intentional.
};
Thus, the inventory is composed entirely of ID's. Using said ID, I need to be able to look up additional information about the item, such as it's name and other properties.
I'm not picky about how the item's definitions are stored. Ideally, they'll be in their own file so that I can look up items without having to load the entire list into memory. I'm not picky about what kind of file they're stored in, or how that file is accessed.
-
Re: Working with a database
Quote:
I need to be able to use that to look up various statistics about the item, without having to load the entire list of items into the memory.
Thing is, I only need to read from the database - I don't need to write anything to it
Typically "statistics" is implied to be collected somehow, and therefore, updated time to time. :)
Besides, I can see "having to load the entire list into memory" thing, twice. I dare say, database access components may use quite a lot of memory. So the question is: are you sure about this kind of "saving"? A golden rule says: do not optimize prematurely. :)
-
Re: Working with a database
Quote:
Originally Posted by
Igor Vartanov
So the question is: are you sure about this kind of "saving"?
No, no I'm not. That's why I'm here lol.
So, if I were to do everything in the executable, I *think* that my best bet would be to save the entire item in the array, instead of the item's ID? Or is there still a way that I can use a numeric ID to look up information?
-
Re: Working with a database
Array or hash map would do. In case the number of items is less than a thousand, the map would look more attractive, as for me.
-
Re: Working with a database
Aaaah, I see. So, I could make a really huge array, store every item definition in there, and use the index as the item ID. Makes sense.
Still open to ideas though, if anybody has a better idea. I just don't want my small, practice game to end up with a minimum requirement of 512mb of ram.
-
Re: Working with a database
Quote:
I could make a really huge array
You could, but arrays aren't dynamic. This means they don't grow if you need them to. Allocating a huge array takes memory you don't know if you need it, and, if you need more, you need to reallocate the entire array. You don't want that. If you don't know how much data there is (or will be), a hashmap is a better idea.
-
Re: Working with a database
Well, right now I'm defining the array at the same time as defining the items. I'm just adding 1 to the array size every time I make another item, so it's only as large as I need it.
I don't plan to create new item definitions on the fly, so I don't really need it to be dynamic.
Edit: Alright, I just managed to test out some sizes. The size of a single item (so far) is 72 bytes. If we have 1,000 items, that means that the game will use up about 72kb worth of ram, just for allowing the item list to exist. I guess this isn't too bad, really. If we use a full list, that's still only about 4mb. This can work.
-
Re: Working with a database
Quote:
Originally Posted by
candlemaster
Well, right now I'm defining the array at the same time as defining the items. I'm just adding 1 to the array size every time I make another item, so it's only as large as I need it.
Arrays are not resizable. So what are you really doing?
Regards,
Paul McKenzie
-
Re: Working with a database
simple. The array right now is:
When I add a new item to the list, I increase it (manually) to:
No programs are resizing anything (well, except my IDE), it's all hard-coded.
Well, I have another question. This one might be a bit trickier. Would it be possible to save a function's name as a variable, and then use that variable to call the function? For example:
Code:
std::string functionVariable = "MyFunction()";
//or
specialvartype functionVariable = MyFunction();
Can this be done, in part or in whole? This would let me store item scripts (as functions) as one of the variables in the definition. The alternative would be to store a script ID instead, and use the ID with a switch-case to determine which function to run. Any advice?
-
Re: Working with a database
Quote:
Originally Posted by
candlemaster
simple. The array right now is:
When I add a new item to the list, I increase it (manually) to:
No programs are resizing anything (well, except my IDE), it's all hard-coded.
Instead of this, why not do this programatically?
The std::vector is resizable at runtime, so I don't see any need for doing what you're saying you're doing.
Code:
#include <vector>
//...
std::vector<Item> ItemDef;
//...
Then you can add a new item at runtime by just calling ItemDef.push_back(). Then you're not scrambling, recompiling a new executable every time.
Regards,
Paul McKenzie
-
Re: Working with a database
It could even be something as simple as this:
Code:
#include <fstream>
//...
std::ifstream infile("infile.txt");
//..
int num;
// this file contains the number of items.
// Assume you've read this file, and the number is stored in the variable called "num"
//...
std::vector<Item> ItemDef(num);
Now all you need to do is update the file, and not have to recompile your program if the number of items changes.
Regards,
Paul McKenzie
-
Re: Working with a database
Well right now, the item list is defined in a .h file, so I have to recompile every time I change the list anyway.
If I could store the item list in an external file, such as .txt, it would be very helpful.
-
Re: Working with a database
Quote:
Originally Posted by
candlemaster
If I could store the item list in an external file, such as .txt, it would be very helpful.
Please strive to do that. Then there would be no need to recompile every time something changes.
Regards,
Paul McKenzie
-
Re: Working with a database
Any idea where i should start?
-
Re: Working with a database
Place the information you want in a file, and read the information.
Regards,
Paul McKenzie
-
Re: Working with a database
Well, the best way I can think of to do that, I could just as easily do that with a .xml file, but from what I understand of those, reading them directly is really clumsy, plus there's a xml schema thing that I can use to make it easier (if I could figure it out that is...)
Do you know of any good tutorials that'll help me out?
-
Re: Working with a database
Quote:
Originally Posted by
candlemaster
Would it be possible to save a function's name as a variable, and then use that variable to call the function? For example:
Code:
std::string functionVariable = "MyFunction()";
//or
specialvartype functionVariable = MyFunction();
For the first ideea, the answer is no. That's because the name of a function is not a string. From the programmer's perspective it looks like a text but from the compiler's perspective it's just the relative address where the body of the function can be foud. Probably the best definition would be to say that a function name it's a "named address" (do I have too much imagination ?)
During the compilation all of the function names are replaced with their addresses. If you open the generated exe file with a binary editor, you'll find none of the names of your functions. You cannot relay on function names because they'll be all gone in the exe file.
For the second ideea, the answer is yes.
1. Declare a pointer type matching the declaration of your function:
Code:
typedef std::string (*MY_TYPE_PTR)();
2. Declare a pointer variable of that type:
Code:
MY_TYPE_PTR pMyFunc;
3. Assign the address of your function to that pointer variable:
Code:
pMyFunc = MyFunction;
4. Call the function using the pointer variable:
-
Re: Working with a database
Quote:
Originally Posted by
srelu
For the first ideea, the answer is no. That's because the name of a function is not a string. From the programmer's perspective it looks like a text but from the compiler's perspective it's just the relative address where the body of the function can be foud. Probably the best definition would be to say that a function name it's a "named address" (do I have too much imagination ?)
During the compilation all of the function names are replaced with their addresses. If you open the generated exe file with a binary editor, you'll find none of the names of your functions. You cannot relay on function names because they'll be all gone in the exe file.
For the second ideea, the answer is yes.
1. Declare a pointer type matching the declaration of your function:
Code:
typedef std::string (*MY_TYPE_PTR)();
2. Declare a pointer variable of that type:
Code:
MY_TYPE_PTR pMyFunc;
3. Assign the address of your function to that pointer variable:
Code:
pMyFunc = MyFunction;
4. Call the function using the pointer variable:
I'm trying, but it seems the closest I'm getting gives me this error message:
Code:
cannot convert from 'void (__cdecl *)(void)' to 'scriptVar (__cdecl *)'
Here's how I have it set:
Just before the "Item" class definition:
Code:
typedef std::string *(scriptVar)();
Inside the "Item" class definition:
Code:
scriptVar* itemScript;
In the "Item()" function, I set it to NULL
This is part of item #1's definition:
Code:
ItemDef[1].itemScript = &ItemScript_apple;
"ItemScript_apple()" is the name of the function that I want to use.
I tried using "&ItemScript_apple()" instead of "&ItemScript_apple", but I got
Code:
error C2102: '&' requires l-value
as an error code.
-
Re: Working with a database
Form what you wrote, I assumed that the declaration of your function is:
Code:
std::string MyFunction();
It seems I was wrong. Please specify the right declaration.
-
Re: Working with a database
Ah, I see. The function is a void, not a string.
-
Re: Working with a database
Getting back to basics for a moment---
Quote:
Originally Posted by
candlemaster
If given the unique ID of an item (short int), I need to be able to use that to look up various statistics about the item,
It sounds like you want a std::map, or a std::unordered_map (if your compiler supports it; if not, there's one in Boost).
You could of course use an array or vector, if you can be sure that all of your unique IDs will be contiguous. The map option is preferable if you don't want restrictions on what those IDs can be other than "unique". I'm going to use the word "map" below for simplicity.
Quote:
without having to load the entire list of items into the memory.
Well, one option that hasn't been suggested yet----store everything a file, as a series of records. But then, instead of loading the entire file into memory (presumably into objects stored in the map), you can parse the file once and simply store offsets into the file in the map instead. Then, when you look something up in the map, you just jump to the appropriate offset and read that part of the file.
Will that be more efficient? I very much doubt it. But it would allow you to use a massive file without cluttering RAM up too much. Maybe that's worth it, I don't know.
-
Re: Working with a database
I changed "typedef std::string *(scriptVar)();" to "typedef void *(scriptVar)();", but nothing's changed...
-
Re: Working with a database
Quote:
Originally Posted by
candlemaster
I changed "typedef std::string *(scriptVar)();" to "typedef void *(scriptVar)();", but nothing's changed...
Code:
typedef void* (*scriptVar)();
Syntax for typedef declaration of 'C"-style function pointer:
Code:
typedef return_type (*typedef_name)(args);
Regards,
Paul McKenzie
-
Re: Working with a database
Wow, it works. Thanks paul!
Now, as for how items are set up, the way I see it I have 2 choices.
1) I can define all of the items in my custom header file. While un-elegant, and hurting modularity, I know that it works, and I can make it work without *too* much hassle.
2) I can define the items in an XML file, and use an XML schema file and some file-reading magic to read the items from the file. Whether or not I have to load the whole file into memory (after doing some "sizeof" tests with option 1, I found out the list really isn't that big, data-wise), this'll be great for modularity, and it'll even allow users to mod item's stats and whatnot.
I'd like to do option 2, but for now I'll just stick with what works. If anybody has a good tutorial for getting an XML and XML schema file to work nicely, it would be infinitely appreciated.