| [ LiB ] |
This chapter gave you a brief
I've expanded on and improved those concepts in a number of areas, making the system more reusable and flexible, and because of this, I was able to reduce the code at higher levels. Whenever I want to make a database load a directory of entities, all I need to do is issue a command like this: DB.LoadDirectory( "data/blah/" ) .
In the
| [ LiB ] |
| [ LiB ] |
The previous chapter described the base entity and database classes and outlined the accessor classes, but didn't show you how to actually implement the final entity or database classes. This chapter does so. The good news is that since most of the work has already been accomplished in the base classes, the final entity and database classes are really quite simple.
In this chapter, you will learn to:
Use
Work with the six entity classes: accounts,
Use entity databases to store files to disk
Use entity accessors as iterators
| [ LiB ] |
| [ LiB ] |
Before discussing entities, I'd like to show you a special class. In the previous chapter, I mentioned that there is a mixin class called
DataEntity
, which wraps around a
A databank gives flexibility to the BetterMUD. I'm sure you're used to figuring out what kinds of data you need, programming the data, and then
The flexible structure I've described requires storing objects with arbitrary
std::map<std::string, int> intbank; intbank["pie"] = 20; intbank["cool"] = 30; int a = intbank["pie"]; // 20 a = intbank["cool"]; // 30
Pretty cool, isn't it? You can insert as many items as you want, and since it's a map, you'll have some decent performance ( O(log n ) for those algorithm-obsessed folks) when inserting or retrieving items. Sure, it's not nearly as fast as accessing variables directly by a memory offset (which is the way that precompiled variables are accessed), but I believe that the time has finally come when the benefits of having such an extensible system far outweigh the need to squeeze every drop of speed out of your system.
The
Databank
class itself is
template< typename type >
class Databank {
public:
typedef std::map< std::string, type > container;
typedef container::iterator iterator;
iterator begin() { return m_bank.begin(); }
iterator end() { return m_bank.end(); }
bool Has( const std::string& p_name )
void Set( const std::string& p_name, const type& p_val )
type& Get( const std::string& p_name )
void Add( const std::string& p_name, const type& p_val )
void Del( const std::string& p_name )
void Save( std::ostream& p_stream )
void Load( std::istream& p_stream )
void Clear()
size_t size()
protected:
container m_bank;
};
You can create databanks of any type you want, since this is a template class: for example, a Databank<int> , or Databank<float> . You can also iterate through databanks to see which variables they hold.
The second grouping of functions allows you to access and use the databank. To make databanks more "consistent," they throw exceptions when you try getting or changing attributes that don't exist, rather than
Since the functions simply wrap around the std::map functions and add exception throwing in the appropriate places, I'm not going to show you the code. You can find this class in /BetterMUD/entities/Attributes.h.
Using a databank is pretty simple, as you can see from this example:
Databank<int> bank; bank.Add( "health", 10 ); bank.Add( "strength", 20 ); int i = bank.Get( "health" ); // 10 i = bank.Get( "strength" ); // 20 bank.Del( "health" ); i = bank.Get( "health" ); // *THROWS EXCEPTION* bank.Set( "strength", 30 );
A databank is string-based, so you can easily add any variables to it that you want. Every entity (except accounts) in the BetterMUD has a databank that you can use and access through the scripts, which will allow you almost limitless freedom to add variables to characters in the game.
Databanks also have stream loading and saving functions, which makes it easy to load and save databanks to disk. Here's an example of the saving code:
void Save( std::ostream& p_stream ) {
p_stream << "[DATABANK]\n";
iterator itr = m_bank.begin();
while( itr != m_bank.end() ) {
p_stream << BasicLib::tostring( itr->first, 24 ) <<
itr->second << "\n";
++itr;
}
p_stream << "[/DATABANK]\n";
}
This will create files that look like this:
[DATABANK] health 10 strength 20 hitpoints 100 [/DATABANK]
Nice, pretty, and readable. The call to the
BasicLib::tostring
makes sure there are 24
A databank is loaded in a similar way:
void Load( std::istream& p_stream ) {
std::string temp;
p_stream >> temp; // extract "[DATABANK]"
while( BasicLib::extract( p_stream, temp ) != "[/DATABANK]" ) {
type t;
p_stream >> t;
Add( temp, t );
}
}
The loop
If the databank doesn't have an attribute extracted from the stream, it is automatically loaded. This is a particularly useful feature, because it allows you to load a databank from a file without manually adding all the attributes first.
| [ LiB ] |