I'm new to this whole bot development thing, and I have recently finished coding the necessary code to connect to Battle.net and receive/parse chat events. Now I am moving on to writing the database, and I am wondering how to do this. How do I represent a user? I was thinking of just having the bot read from a database file and get the username and their flags. That brings me to my next question, how should I represent flags? I was thinking of writing a class for each flag, and in that class write each of the commands as a function. But then I get to the point of how do I add a flag (class) to that username, and limit access to other flags (classes)? I havn't put much thought into it, but I will be thinking of ideas as I get feedback from those who can assist in this.
Keep in mind, this is being written in C++.
For someone who knows 10 or so language's Fluently this should be an easy task..
You guys are fast.
I never said I knew 10 languages, let alone knew them fluently.
Additionally, I said in the post I havn't put much thought into it, I'm already applying some ideas. This is just to get some feedback on experienced people in doing this, so I don't go implement a system which would be otherwise infurior to other methods which can be used.
Edit: Well, I suppose it's 10 if you count English. But I still stand by the fact I never said I knew them fluently.
Go to the C/C++ programming forum.
If this was C/C++ specific, I think someone would have moved it by now.
Additionally, you don't necessarily have to know C++ to help me out with this.
Represent a user's flags as a bitmask. When you retrieve a user's information from the database file, convert each flag into the equivalent value, then construct the bitmask out of them. Then when processing a command, simply see if they have the necessary flag by using the & operator.
I like to use a std::map to map battle.net names to unique bot names, to map to user info.
something like
class BotUserInfo
{
public:
// the name to call the user
std::string ReferenceName;
std::vector<std::string> AccountNames;
std::string Email;
std::string AIM;
bool CanBan() { return (Flags & bn::BAN_MASK); }
bool CanKick() { return (Flags & bn::KICK_MASK); }
// etc or:
bool GetPriv(unsigned int mask) { return (Flags & mask); }
void SetPriv(unsigned int mask) { Flags |= mask; }
private:
unsigned int Flags;
};
// now we can translate one of several battle.net names
// into a unique user account name
std::map<std::string, std::string> UserLookup;
// and that account name into the user's info.
std::map<std::string, bn::BotUserInfo> UserInfo;
I'm big on extensions, so I created a simple interface (IUser) that any extension assembly (note that I'm writing in C#, so it's .NET) can provide their own implementation and then just pass that around. I also defined an interface for a data provider such as:
public interface IDataProvider {
IUser GetUser(string szUserName);
IUser CreateUser(string szUserName, int dwRank);
//etc...
void SendBotMail(IUser sender, IUser receiver, string Message);
IBotMaiil[] GetMessages(IUser receiver);
}
That is obviously not the entire idea, but I believe if you define well first how you want your database to behave, you'll find yourself in a much better position down the road.
I tend to like having flags. If 32 flags is enough for you, use a bitmap. For maximum configurability, allow users to be given any combination of flags, and allow commands or actions to require any combination of flags.
A user could then be:
struct User {
char name[MAX_NAME];
unsigned flags;
};
A command could then be:
struct Command {
char trigger[MAX_TRIGGER];
unsigned positiveflags;
unsigned negativeflags;
void (*processcommand)( ... );
};
if((user.flags & cmd.positiveflags) && !(user.flags & cmd.negativeflags))
cmd.processcommand(...);
First I have a struct:
struct User{
char szMask[32];
unsigned long dwFlags;
};
Then a list (if you want my linked list class you can have that too, just ask):
List<User *> Database;
Then I use these 3 functions to set/remove/check my flags (thanks Sky for these, btw):
bool CheckFlag(unsigned long dwMask, char bFlag)
{
if(bFlag < 'A' || bFlag > 'Z')
return false;
return !!(dwMask & (1 << (bFlag - 'A')));
}
void SetFlag(unsigned long *dwMask, char bFlag)
{
if(bFlag < 'A' || bFlag > 'Z')
return;
*dwMask |= (1 << (bFlag - 'A'));
}
void RemoveFlag(unsigned long *dwMask, char bFlag)
{
if(bFlag < 'A' || bFlag > 'Z')
return;
*dwMask &= ~(1 << (bFlag - 'A'));
}
So say I wanted to make a new DB entry I would do something like...
User *pNew = new User;
strcpy(pNew->szMask, "testuser"); //set the user mask
pNew->dwFlags = 0;
SetFlag(pNew->dwFlags, 'A'); //set flag(s)
Database.Add(pNew);
If I wanted to modify an entry's flags, I would do something like...
//find the user
User *p;
for(int i = 0; i < Database.Count(); i++){
p = Database.Get(i);
if(!stricmp(szEntryToFind, p->szMask))
break;
}
//modify
RemoveFlag(p->dwFlags, 'N'); //or whatever
If you have questions, feel free to ask...I'll post a link to my linked list class if you want it.
Minor suggested improvement to DarkMinion's approach -- since you're already using new to allocate the structure, it'll take care of calling a constructor if you provide one. As such, you could have two constructors: default, which sets name = "" and flags to 0, and one that takes a const char* and an unsigned, and saves the string + flags into the user structure before returning. It wouldn't be any more or less efficient runtime wise afaik, but it would make for simpler user creation code. :)
[Edit: just noticed that DarkMinion's code will actually produce a user with unpredictable flags. SetFlag is more properly AddFlag, and the flags aren't being set to a known value first. Therefore, your only guarantee is that the resulting user will have 'A' as a flag -- he might have other flags as well. To avoid this, explicitly set the flags variable to zero.]
Sorry, forgot to initialize flags...thanks for reminding me Kp, fixed.