From: kainazzzo Date: Wed, 7 Apr 2004 23:26:02 +0000 (+0000) Subject: Implemented a player timeout scheme X-Git-Url: https://jfr.im/git/irc/gameservirc.git/commitdiff_plain/4025195255323de684e16d0845717a4eee057366?hp=ce7556ca3a977c5978c399a24a17ccf8a97eda25 Implemented a player timeout scheme git-svn-id: https://svn.code.sf.net/p/gameservirc/code/trunk@165 bc333340-6410-0410-a689-9d09f3c113fa --- diff --git a/gameserv/TODO b/gameserv/TODO index 254134e..5b1666a 100644 --- a/gameserv/TODO +++ b/gameserv/TODO @@ -52,6 +52,7 @@ X = Finsihed versions of GameServ to be running on the same network using the same data. * Classes of players with special abilities like spells and such. + - Thieves can rob the bank if they're strong enough :) * Error handling diff --git a/gameserv/config.cpp b/gameserv/config.cpp index d377ebd..c653fc4 100644 --- a/gameserv/config.cpp +++ b/gameserv/config.cpp @@ -26,6 +26,8 @@ int updateperiod; // Seconds until another player database update int forestfights; // Forest fights per day int maxafightdistance; // Max levels above a player they can fight player->player int maxbfightdistance; // Max levels below a player they can fight player->player +int maxidletime; // Max time (in seconds) a player can be idle for +int idlecheckperiod; // Period for checking every player's idle time // Remote server stuff. This is used for the outgoing connection gameserv needs to make // to a real ircd. @@ -77,7 +79,7 @@ int load_config_file(char *config) { char *buf, *directive, *value; - #define numdirectives 19 + #define numdirectives 21 unload_config_file(); @@ -109,6 +111,11 @@ int load_config_file(char *config) "that you can fight player->player"; directives[18].desc = "MAXBFIGHTDISTANCE - The maximum number of levels below you "\ "that you can fight player->player"; + directives[19].desc = "MAXIDLETIME - The maximum amount of time (in seconds) "\ + "that a player can be idle before something happens"; + directives[20].desc = "IDLECHECKPERIOD - The period (in seconds) in which the entire "\ + "players list will be checked for idlers. See also: "\ + "MAXIDLETIME"; for (int count = 0; count < numdirectives; count++) { @@ -274,6 +281,18 @@ int load_config_file(char *config) maxbfightdistance = stringtoint(value); directives[18].done = true; } + else if (stricmp(directive, "MAXIDLETIME") == 0) + { + value = strtok(NULL, " "); + maxidletime = stringtoint(value); + directives[19].done = true; + } + else if (stricmp(directive, "IDLECHECKPERIOD") == 0) + { + value = strtok(NULL, " "); + idlecheckperiod = stringtoint(value); + directives[20].done = true; + } else { #ifdef DEBUGMODE diff --git a/gameserv/extern.h b/gameserv/extern.h index 8ce04da..a92bf72 100644 --- a/gameserv/extern.h +++ b/gameserv/extern.h @@ -70,6 +70,8 @@ E int updateperiod; E int forestfights; E int maxafightdistance; E int maxbfightdistance; +E int maxidletime; +E int idlecheckperiod; /* config.cpp end */ @@ -77,6 +79,9 @@ E List players[U_TABLE_SIZE]; E List clients[U_TABLE_SIZE]; E Monster boss; +/** tcpclient.cpp **/ +E void check_idles(); + /** List search functions **/ E aClient *find(char *nick); E aClient *find(const char *nick); @@ -89,6 +94,8 @@ E aClient *findIRCplayer(const char *nick); E aClient *findbyrealnick(char *realnick); #endif +/** tcpclient.cpp **/ + /** Sock writing functions **/ E void notice(const char *source, const char *dest, const char *fmt, ...); E void raw(const char *fmt, ...); @@ -136,6 +143,9 @@ E Monster *getNewMonster(Monster *m); E void deleteMonster(Monster *m); E void refresh(Player *p); E void refreshall(); +E void updateTS(Player *p); +E bool timedOut(Player *p); +E void timeOutEvent(Player *p); E void reset(Player *p); E void resetall(); diff --git a/gameserv/gameserv.cpp b/gameserv/gameserv.cpp index 6d84fe4..0937cd7 100644 --- a/gameserv/gameserv.cpp +++ b/gameserv/gameserv.cpp @@ -59,6 +59,10 @@ bool check_password(char *name, char *plaintext); // Finds a password for the gi /********** GameServ Booleans **********/ bool shuttingdown; +bool timedOut(Player *p); +void updateTS(Player *p); +void timeOutEvent(Player *p); + bool is_playing(char *u); // True if the given nickname in the clients list is playing. bool is_playing(aClient *user); @@ -85,6 +89,7 @@ long int stringtoint(char *number); char *spaces(int len, char *seperator); void refresh(Player *p); void refreshall(); +void updateTS(Player *p); void reset(Player *p); void init_masters(); void init_monsters(); @@ -646,7 +651,7 @@ void logout(aClient *user) void do_register(char *u) { char *password, *name; - aClient *user, *p; + aClient *user; name = strtok(NULL, " "); password = strtok(NULL, " "); @@ -672,14 +677,14 @@ void do_register(char *u) } else if ((user = find(u))) { - p = findplayer(u); - if (!user->stats && !p) + if (!user->stats) { user->stats = new Player(user); user->stats->client = user; // Set the backwards pointer strcpy(user->stats->password, crypt(password, salt)); strcpy(user->stats->name, name); unsigned long hv = HASH((unsigned char *) name, U_TABLE_SIZE); + updateTS(user->stats); players[hv].insertAtBack(user); notice(s_GameServ, u, "Player %s registered with password %s.", user->stats->name, password); notice(s_GameServ, u, "Write this password down. If you lose it, there is no way to retrieve it!"); @@ -737,6 +742,8 @@ void do_identify(char *u) #ifdef DEBUGMODE log("Setting data for identified"); #endif + updateTS(user->stats); + user->stats->setData(p->stats); #ifdef DEBUGMODE @@ -769,7 +776,10 @@ void do_stats(char *u) return; } else + { + updateTS(user->stats); showstats(u, user->stats->name); + } } else showstats(u, nick); @@ -967,18 +977,14 @@ bool is_playing(char *u) { aClient *user; if (!(user = find(u))) - { return false; - } else - { - return user->stats != NULL; - } + return is_playing(user); } bool is_playing(aClient *user) { - return user->stats != NULL && (stricmp(user->getNick(), "!NULL!") != 0); + return user->stats != NULL; } bool is_fighting(char *u) @@ -986,17 +992,11 @@ bool is_fighting(char *u) aClient *user; if (!(user = find(u))) - { return false; - } - else if (user->stats) - { - return user->stats->fight != NULL || user->stats->battle != NULL - || user->stats->master != NULL; - } else - return false; + return is_fighting(user); } + bool is_fighting(aClient *user) { if (!is_playing(user)) @@ -1011,10 +1011,8 @@ bool player_fight(char *u) if (!(user = find(u))) return false; - else if (user->stats) - return user->stats->battle != NULL; - else - return false; + else + return player_fight(user); } bool player_fight(aClient *user) { @@ -1030,11 +1028,10 @@ bool master_fight(char *u) if (!(user = find(u))) return false; - else if (user->stats) - return user->stats->master != NULL; else - return false; + return master_fight(user); } + bool master_fight(aClient *user) { if (!is_playing(user)) @@ -1052,16 +1049,22 @@ void do_fight(char *u) if (!nick) { notice(s_GameServ, u, "SYNTAX: /msg %S FIGHT PLAYER"); + return; } else if (!(ni = find(u))) { notice(s_GameServ, u, "Fatal error. Contact a(n) %S admin. buf: %s", strtok(NULL, "")); + return; } else if (!is_playing(ni)) { notice(s_GameServ, u, "You are not playing!"); + return; } - else if (ni->stats->player_fights <= 0) + + updateTS(ni->stats); + + if (ni->stats->player_fights <= 0) { ni->stats->player_fights = 0; // just to be safe notice(s_GameServ, u, "You are out of player fights for the "\ @@ -1145,6 +1148,7 @@ void do_fight(char *u) display_players(ni); } } + void do_use(char *u) { aClient *user; @@ -1169,6 +1173,8 @@ void do_use(char *u) return; } + updateTS(user->stats); + p = &user->stats->inventory; if (stricmp(item, "HEALTH") == 0) @@ -1251,6 +1257,7 @@ void do_run(char *u) return; } + updateTS(user->stats); p = user->stats; if (p->battle) @@ -1397,6 +1404,7 @@ void do_attack(char *u) // One has to be !NULL based on the previous else if // We wouldn't be here if they were all NULL } + updateTS(ni->stats); if (!player_fight(ni)) { @@ -1708,6 +1716,7 @@ void do_heal(char *u) if (!amount) { notice(s_GameServ, u, "SYNTAX: /msg %S HEAL {ALL | #}"); + return; } else if (!(ni = find(u))) { @@ -1727,12 +1736,16 @@ void do_heal(char *u) else if (is_fighting(ni)) { notice(s_GameServ, u, "You can't heal in battle!"); + return; } else if (ni->stats->hp >= ni->stats->maxhp) { notice(s_GameServ, u, "You don't need healing!"); + return; } - else if (stricmp(amount, "ALL") == 0) + + updateTS(ni->stats); + if (stricmp(amount, "ALL") == 0) { price = ni->stats->level * 3; if (ni->stats->gold < (ni->stats->maxhp - ni->stats->hp) * price) @@ -1987,15 +2000,21 @@ void do_store(char *u) notice(s_GameServ, u, "SYNTAX: STORE LIST {ARMOR | WEAPONS}"); notice(s_GameServ, u, " STORE SELL {ARMOR | WEAPON}"); notice(s_GameServ, u, " STORE BUY {ARMOR | WEAPON} NUMBER"); + return; } else if (!(user = find(u)) || !is_playing(user)) + { notice(s_GameServ, u, "You must be playing to use the store!"); + return; + } else if (!isAlive(user->stats)) { notice(s_GameServ, u, "You are dead. Wait until tomorrow to purchase weapons and armor!"); return; } - else if (stricmp(cmd, "LIST") == 0) + updateTS(user->stats); + + if (stricmp(cmd, "LIST") == 0) { if (stricmp(item, "WEAPONS") == 0) { @@ -2164,6 +2183,7 @@ void do_inventory(char *u) notice(s_GameServ, u, "You must be playing to check your inventory!"); return; } + updateTS(user->stats); showinventory(user, user); } void showinventory(aClient *from, aClient *to) @@ -2204,7 +2224,10 @@ void do_tavern(char *u) notice(s_GameServ, u, "You cannot go to the Tavern during a fight!"); return; } + + updateTS(user->stats); p = user->stats; + if (!cmd) { notice(s_GameServ, u, "Welcome to Boot Liquors Mystic Apothecary"); @@ -2319,14 +2342,21 @@ void do_bank(char *u) notice (s_GameServ, u, "BANK BALANCE"); return; } - - user = find(u); - if (!is_playing(user)) + else if (!(user = find(u))) + { + notice(s_GameServ, u, "Fatal Error. Couldn't find your aClient. Contact a(n) %S "\ + " admin for help"); + log("Fatal Error. Couldn't find %s while executing do_bank()", u); + return; + } + else if (!is_playing(user)) { notice(s_GameServ, u, "You must be playing to use the bank!"); return; } - else if (stricmp(cmd, "BALANCE") == 0) + + updateTS(user->stats); + if (stricmp(cmd, "BALANCE") == 0) { showBankBalance(u); return; @@ -2474,7 +2504,8 @@ void do_master(char *u) notice(s_GameServ, u, "You must be playing to see your master!"); return; } - + + updateTS(user->stats); char *cmd = strtok(NULL, " "); Player *p = user->stats; long int need = 0; @@ -2709,6 +2740,34 @@ void reset(Player *p) p->reset(); } +void updateTS(Player *p) +{ + if (!p) + return; + p->lastcommand = time(NULL); +} + +bool timedOut(Player *p) +{ + if (!p) + return false; + else + return ((time(NULL) - p->lastcommand) >= maxidletime); +} + +void timeOutEvent(Player *p) +{ + aClient *user = p->client; + + if (!user) // then they're not playing + return; + + char *nick = user->getNick(); + + notice(s_GameServ, nick, "You just idle timed out"); + logout(user); +} + void do_reset(char *u) { char *nick = strtok(NULL, " "); diff --git a/gameserv/gameserv.example.conf b/gameserv/gameserv.example.conf index 9217871..b064436 100644 --- a/gameserv/gameserv.example.conf +++ b/gameserv/gameserv.example.conf @@ -90,5 +90,17 @@ maxafightdistance 2 # player fights only maxbfightdistance 1 +# This is the maximum time (in seconds) that a player may remain idle +# before something naughty happens to them :) +maxidletime 300 + +# This is the period (in seconds) that gameserv will wait between +# checking the entire players list for idlers. +# Warning: bigger networks should not set this too low! +# Minor Warning: This should be fairly close to the maxidletime, but +# it doesn't have to be. The farther the two are apart, the more +# random it gets. +idlecheckperiod 300 + # Delete or comment this line so that GameServ will Load die You must read the entire config file! diff --git a/gameserv/player.cpp b/gameserv/player.cpp index 7278cf8..75ab302 100644 --- a/gameserv/player.cpp +++ b/gameserv/player.cpp @@ -26,9 +26,11 @@ void Player::reset() master = NULL; battle = NULL; flags = 0; + lastcommand = 0; setAlive(this); inventory.reset(); } + Player::Player() { name = new char[256]; @@ -123,6 +125,7 @@ void Player::setData(Player *right) flags = right->flags; inventory.setInventory(&right->inventory); client = right->client; + lastcommand = right->lastcommand; } else { diff --git a/gameserv/player.h b/gameserv/player.h index c6c331b..92e95b4 100644 --- a/gameserv/player.h +++ b/gameserv/player.h @@ -40,6 +40,7 @@ public: int player_fights; // Amount of player<->player fights for today char *password; // Player's encrypted password Pouch inventory; // This contains their potions, etc. + long int lastcommand; // timestamp for the last command typed aClient *client; // Pointer to the aClient this player is from Monster *fight; // Pointer to the monster the player is currently fighting diff --git a/gameserv/tcpclient.cpp b/gameserv/tcpclient.cpp index e276ae3..66f7139 100644 --- a/gameserv/tcpclient.cpp +++ b/gameserv/tcpclient.cpp @@ -46,6 +46,7 @@ List clients[U_TABLE_SIZE]; void save_day(); void load_day(); void prettyIntro(); +void check_idles(); // Make this a daemon int daemon(int nochdir, int noclose); @@ -57,6 +58,7 @@ int main(int argc, char *argv[]) { char buffer[1024], buf[1024]; int connected; + long lastidlecheck; char *cmd, *source = NULL, *conf = "gameserv.conf"; srand(time(NULL)); @@ -121,6 +123,8 @@ int main(int argc, char *argv[]) long int loadtime = time(NULL); long int currentTime; long int oldTime = loadtime; + + lastidlecheck = loadtime; bool loaded = false; ignore_pipe(); @@ -211,6 +215,15 @@ int main(int argc, char *argv[]) retry = 0; // Start the reconnection cycle over } } + else + { + long TIME = time(NULL); + if (TIME - lastidlecheck >= idlecheckperiod) + { + check_idles(); + lastidlecheck = TIME; + } + } // Save the player data every updateperiod seconds currentTime = time(NULL); @@ -546,6 +559,7 @@ aClient *findIRCplayer(const char *nick) } return NULL; } + aClient *findplayer(const char *name) { ListNode *newPtr; @@ -561,6 +575,24 @@ aClient *findplayer(const char *name) return NULL; } +void check_idles() +{ + ListNode *newPtr; + Player *p = NULL; + + for (int x = 0; x < U_TABLE_SIZE; x++) + { + for (newPtr = players[x].First(); newPtr; newPtr = newPtr->Next()) + { + p = newPtr->getData()->stats; + if (timedOut(p)) + { + timeOutEvent(p); + } + } + } +} + aClient *findbynick(const char *nick) { ListNode *newPtr;