/*
* command1.cpp
* Command handling/parsing routines.
* ____ _
* | _ \ ___ __ _| |_ __ ___ ___
* | |_) / _ \/ _` | | '_ ` _ \/ __|
* | _ < __/ (_| | | | | | | \__ \
* |_| \_\___|\__,_|_|_| |_| |_|___/
*
* Permission to use, modify and distribute is granted via the
* Creative Commons - Attribution - Non Commercial - Share Alike 3.0 License
* http://creativecommons.org/licenses/by-nc-sa/3.0/
*
* Copyright (C) 2007-2009 Jason Mitchell, Randi Mitchell
* Contributions by Tim Callahan, Jonathan Hseu
* Based on Mordor (C) Brooke Paul, Brett J. Vickers, John P. Freeman
*
*/
#include "mud.h"
#include "login.h"
#include "commands.h"
#include "factions.h"
#include "calendar.h"
#include <sys/stat.h>
#include <sstream>
#include <iomanip>
#include <locale>
//static int newline;
//static int color;
//#include <arpa/telnet.h>
//#define TELOPT_COMPRESS2 86
//*********************************************************************
// cmdNoExist
//*********************************************************************
// This function tells the player the command does not exist.
int cmdNoExist(Player* player, cmd* cmnd) {
player->print("The command \"%s\" does not exist.\n", cmnd->str[0]);
return(0);
}
//*********************************************************************
// cmdNoAuth
//*********************************************************************
// This function tells the player they're not allowed to use that command
int cmdNoAuth(Player* player) {
player->print("You do not have the proper authorization to use that command.\n");
return(0);
}
//*********************************************************************
// getFailFd
//*********************************************************************
// This function grabs the fd to print error messages to.
// A check of "fd != ffd" means messages are going to different places,
// hence fd is a pet and ffd is a player. "fd == ffd" means a
// player is running the command.
int getFailFd(Creature *user) {
if(!user)
return(-1);
return(user->isPet() ? user->following->fd : user->fd);
}
//*********************************************************************
// command
//*********************************************************************
// This function handles the main prompt commands, and calls the
// appropriate function, depending on what service is requested by the
// player.
void command(Socket* sock, char* str) {
cmd cmnd;
bool needFree = false;
int n;
Player* ply = sock->getPlayer();
ASSERTLOG( ply );
/*
this logn command will print out all the commands entered by players.
It should be used in extreme cases when trying to isolate a players
input which may be causing a crash.
*/
//zero(&cmnd, sizeof(cmd));
if(ply->getClass() == CARETAKER && !dmIson() )
log_immort(false, ply, "%s-%d (%s): %s\n", ply->name, sock->getFd(),
ply->getRoom()->fullName().c_str(), str);
if(!strcmp(str, "!")) {
str = strdup(ply->getLastCommand().c_str());
needFree = true;
//strncpy(str, ply->lastcommand, 79);
}
if(str[0])
ply->setLastCommand(str);
strncpy(cmnd.fullstr, str, 255);
stripBadChars(str); // removes '.' and '/'
lowercize(str, 0);
parse(str, &cmnd);
if(needFree)
free(str);
n = 0;
if(cmnd.num)
n = cmdProcess(ply, &cmnd);
else
n = PROMPT;
if(n == DISCONNECT) {
sock->write("Goodbye!\n\r\n\r");
sock->disconnect();
} else if(n == PROMPT) {
ply->sendPrompt();
}
}
//#ifdef NEW_PARSER
//*********************************************************************
// parse
//*********************************************************************
// This function takes the string in the first parameter and breaks it
// up into its component words, stripping out useless words. The
// resulting words are stored in a command structure pointed to by the
// second argument.
void parse(char *str, cmd *cmnd) {
int i=0, j=0, l=0, n=0;
char token[MAX_TOKEN_SIZE];
int isquote=0;
j = strlen(str);
for(i=0; i<=j; i++) {
// look for first non space or comment
if(str[i] == ' ')
continue;
// ok we at first non space
if(str[i] == '\"') {
isquote = 1;
// skip quote char
i++;
}
// save this position as the begining of a token
l = i;
// now find the end of the token
if(isquote) {
while(str[i] != '\0' && str[i] != '\"')
i++;
// terminate the token
if(str[i] == '\"')
str[i] = '\0';
} else {
while(str[i] != '\0' && str[i] != ' ')
i++;
// terminate the token
str[i] = '\0';
}
// don't overflow the buffers
strncpy(token, &str[l], MAX_TOKEN_SIZE);
token[MAX_TOKEN_SIZE - 1] = 0;
/* whas there any thing here? */
if(!strlen(token)) {
isquote = 0;
continue;
}
if(isquote) {
strncpy(cmnd->str[n], token, MAX_TOKEN_SIZE);
cmnd->str[n][MAX_TOKEN_SIZE - 1] = '\0';
cmnd->val[n] = 1L;
isquote = 0;
} else {
// Copy into command structure
if(n == 0) {
strncpy(cmnd->str[n], token, MAX_TOKEN_SIZE);
cmnd->str[n][MAX_TOKEN_SIZE - 1] = '\0';
// set the value to 1 in case there is non following
cmnd->val[n] = 1L;
n++;
}
else if(isdigit((int)token[0]) || (token[0] == '-' &&
isdigit((int)token[1]))) {
// this is a value for the previous command
cmnd->val[MAX(0, n - 1)] = atol(token);
} else {
strncpy(cmnd->str[n], token, MAX_TOKEN_SIZE);
cmnd->str[n][MAX_TOKEN_SIZE - 1] = '\0';
// set the value to 1 in case there is non following
cmnd->val[n] = 1L;
n++;
}
}
if(n >= COMMANDMAX)
break;
}
// set the number of tokens in the command struct
cmnd->num = n;
}
//void parse(char *str, cmd* cmnd) {
// int i, j, l, n, o, art;
// char tempstr[25];
// int isquote;
//
// l = n = 0;
// j = strlen(str);
// isquote = 0;
//
// for(i=0; i<=j; i++) {
//
// // look for first non space
// // apparently # is treated like a space
// if(str[i] == ' ')// || str[i] == '#' )
// continue;
//
// // ok we at first non space
// if( str[i] == '\"' ) {
// isquote = 1;
// // skip quote char
// i++;
// }
//
// // save this position as the begining of a token
// l = i;
//
// // now find the end of the token
// if( isquote ) {
// while( str[i] != '\0' && str[i] != '\"' ) {
// i++;
// }
//
// // terminate the token
// if( str[i] == '\"' ) {
// str[i] = '\0';
// }
// } else {
// while( str[i] != '\0' && str[i] != ' ' && str[i] != '#') {
// i++;
// }
//
// // terminate the token
// str[i] = '\0';
// }
//
// strncpy(tempstr, &str[l], 24);
// tempstr[24] = 0;
//
// // whas there any thing here?
// if(!strlen(tempstr)) {
// isquote = 0;
// continue;
// }
//
// if( isquote ) {
// strncpy(cmnd->str[n], tempstr, 20);
// cmnd->str[n][21] = '\0';
// cmnd->val[n] = 1L;
// isquote = 0;
// n++;
// } else {
// // Copy into command structure
// if(n == 0) {
// strncpy(cmnd->str[n], tempstr, 20);
// cmnd->str[n][21] = '\0';
// // set the value to 1 in case there is non following
// cmnd->val[n] = 1L;
// n++;
// } else if(isdigit(tempstr[0]) || (tempstr[0] == '-' &&
// isdigit(tempstr[1]))) {
// // this is a value for the previous command
// cmnd->val[MAX(0, n - 1)] = atol(tempstr);
// } else {
// strncpy(cmnd->str[n], tempstr, 20);
// cmnd->str[n][21] = '\0';
// // set the value to 1 in case there is non following
// cmnd->val[n] = 1L;
// n++;
// }
// }
//
// if(n >= COMMANDMAX) {
// break;
// }
// }
//
// // set the number of tokens in the command struct
// cmnd->num = n;
//
// return;
//}
//
#if 0
//*********************************************************************
// parse
//*********************************************************************
// This function takes the string in the first parameter and breaks it
// up into its component words, stripping out useless words. The
// resulting words are stored in a command structure pointed to by the
// second argument.
void parse(char *str, cmd* cmnd) {
int i, j, l, m, n;
//int o, art;
char tempstr[25];
l = m = n = 0;
j = strlen(str);
for(i=0; i<=j; i++) {
// if(str[i] == ' ' || str[i] == '#' || str[i] == 0) {
if(str[i] == ' ' || str[i] == 0) {
str[i] = 0; // tokenize
// Strip extra white-space
// while((str[i+1] == ' ' || str[i] == '#') && i < j+1)
while(i < j && (str[i+1] == ' '))
str[++i] = 0;
strncpy(tempstr, &str[l], 24);
tempstr[24] = 0;
l = i+1;
if(!strlen(tempstr))
continue;
// Copy into command structure
if(n == m) {
strncpy(cmnd->str[n++], tempstr, 20);
cmnd->val[m] = 1L;
} else if(isdigit(tempstr[0]) || (tempstr[0] == '-' &&
isdigit(tempstr[1]))) {
cmnd->val[m++] = atol(tempstr);
} else {
strncpy(cmnd->str[n++], tempstr, 20);
cmnd->val[m++] = 1L;
}
}
if(m >= COMMANDMAX) {
n = 5;
break;
}
}
if(n > m)
cmnd->val[m++] = 1L;
cmnd->num = n;
}
#endif
//*********************************************************************
// checkdouble
//*********************************************************************
void checkdouble(int fd, int i) {
// ASSERTLOG(fd);
// ASSERTLOG(i);
//
// if(strstr(sock->getHostname().c_str(), "localhost"))
// return;
// if(!ply->name[0] || ply->name[0] == ' ' || ply->name[0] == '\0'
// || ply->name[0] == 0)
// return;
// if(!Ply[i].ply->name[0] || Ply[i].ply->name[0] == ' ' || Ply[i].ply->name[0] == '\0'
// || Ply[i].ply->name[0] == 0)
// return;
// if(!Ply[i].ply || !ply)
// return;
// if(Ply[i].ply->flagIsSet(P_ON_PROXY) || ply->flagIsSet(P_ON_PROXY))
// return;
// if(Ply[i].ply->isWatcher() || ply->isWatcher())
// return;
// if(ply->isCt() || Ply[i].ply->isCt())
// return;
// if(Ply[i].ply->flagIsSet(P_LINKDEAD) || ply->flagIsSet(P_LINKDEAD))
// return;
//
// {
// Ply[i].ply->print("The watcher just arrived.\nThe watcher says, \"Don't double log or I'm going to jail you.\"\nThe watcher just wandered away.\n");
// ply->print("The watcher just arrived.\nThe watcher says, \"Don't double log or I'm going to jail you.\"\nThe watcher just wandered away.\n");
// logn("log.double", "%s and %s were doublelogging (%s).\n", ply->name, Ply[i].ply->name, Ply[i].sock->getHostname().c_str());
// if(
// ply->getClass() != CARETAKER &&
// Ply[i].ply->getClass() != CARETAKER &&
// ply->getClass() != BUILDER &&
// Ply[i].ply->getClass() != BUILDER
// ) {
// broadcast(isWatcher, "^C%s%s and %s%s were double logging (%s) - (%s/%s).\n", ply->name,
// (ply->flagIsSet(P_ON_PROXY) ? "(Proxy)":""), Ply[i].ply->name,
// (Ply[i].ply->flagIsSet(P_ON_PROXY) ? "(proxy)":""), Ply[i].sock->getHostname().c_str(),
// ply->getRoom()->fullName().c_str(), Ply[i].ply->getRoom()->fullName().c_str());
// } else {
// broadcast(isCt, "^y%s%s and %s%s were double logging (%s) - (%s/%s).\n", ply->name,
// (ply->flagIsSet(P_ON_PROXY) ? "(Proxy)":""), Ply[i].ply->name,
// (Ply[i].ply->flagIsSet(P_ON_PROXY) ? "(proxy)":""), Ply[i].sock->getHostname().c_str(),
// ply->getRoom()->fullName().c_str(), Ply[i].ply->getRoom()->fullName().c_str());
// }
//
// if(gConfig->checkDouble)
// disconnect(i);
//
// }
}
//*********************************************************************
// pushObj
//*********************************************************************
int pushObj(Player* player, cmd* cmnd) {
return(special_cmd(player, 2, cmnd));
}
//*********************************************************************
// doFinger
//*********************************************************************
// sending 0 to cls means we're not a player and we want reduced padding
bstring doFinger(const Player* player, bstring name, unsigned short cls) {
struct stat f_stat;
char tmp[80];
Player* target=0;
std::ostringstream oStr;
bool online=true;
// set left aligned
oStr.setf(std::ios::left, std::ios::adjustfield);
oStr.imbue(std::locale(""));
if(name == "")
return("Finger who?\n");
name = name.toLower();
name.setAt(0, up(name.getAt(0)));
target = gServer->findPlayer(name);
if(!target) {
if(!loadPlayer(name.c_str(), &target))
return("Player does not exist.\n");
online = false;
}
if(target->isStaff() && cls < target->getClass()) {
if(!online)
free_crt(target);
return("You are currently unable to finger that player.\n");
}
// cls=0 means we don't want padding
if(cls) {
oStr << std::setw(25) << target->name << " "
<< std::setw(15) << gConfig->getRace(target->getDisplayRace())->getName();
// will they see through the illusion?
if(player && player->willIgnoreIllusion() && target->getDisplayRace() != target->getRace())
oStr << " (" << gConfig->getRace(target->getRace())->getName() << ")";
oStr << " "
<< target->getTitle() << "\n";
} else {
oStr << target->name << " the "
<< gConfig->getRace(target->getDisplayRace())->getAdjective();
// will they see through the illusion?
if(player && player->willIgnoreIllusion() && target->getDisplayRace() != target->getRace())
oStr << " (" << gConfig->getRace(target->getRace())->getAdjective() << ")";
oStr << " "
<< target->getTitle() << "\n";
}
sprintf(tmp, "%s/%s.txt", POSTPATH, name.c_str());
if(stat(tmp, &f_stat))
oStr << "No mail.\n";
else if(f_stat.st_atime > f_stat.st_mtime)
oStr << "No unread mail since: " << ctime(&f_stat.st_atime);
else
oStr << "New mail since: " << ctime(&f_stat.st_mtime);
if(target->getForum() != "")
oStr << "Forum account: " << target->getForum() << "\n";
if(online) {
oStr << "Currently logged on.\n";
} else {
long t = target->getLastLogin();
free_crt(target);
oStr << "Last login: " << ctime(&t);
}
return(oStr.str());
}
//*********************************************************************
// cmdFinger
//*********************************************************************
int cmdFinger(Player* player, cmd* cmnd) {
player->clearFlag(P_AFK);
if(!player->ableToDoCommand())
return(0);
player->print("%s", doFinger(player, cmnd->str[1], player->getClass()).c_str());
return(0);
}
//*********************************************************************
// cmdPayToll
//*********************************************************************
// This function will allow players to pay to get through exits. Two
// flags are used for it: X_TOLL_TO_PASS, and X_LEVEL_BASED_TOLL. X_TOLL_TO_PASS sets the exit as
// a toll booth. A toll field will be required in the Exit structure
// defined as a long. X_LEVEL_BASED_TOLL will make the toll vary depending on the
// players' level, making the cost equal exit->toll*player->getLevel().
int cmdPayToll(Player* player, cmd* cmnd) {
Monster* target=0;
BaseRoom *newRoom=0;
Exit *exit=0;
unsigned long tc=0, amt=0;
if(cmnd->num < 4) {
player->print("Pay what to whom to get in where?\n");
player->print("Syntax: pay $(amount) (target) (exit name)\n");
return(0);
}
if(cmnd->num < 3) {
player->print("Pay to get in where?\n");
player->print("Syntax: pay $(amount) (target) (exit name)\n");
return(0);
}
if(cmnd->str[1][0] != '$') {
player->print("Syntax: pay $(amount) (target) (exit name)\n");
return(0);
}
amt = atol(&cmnd->str[1][1]);
if(!amt || amt < 1) {
player->print("Please enter the amount to pay the tollkeeper.\n");
return(0);
}
amt = MIN(amt, 30000);
target = player->getRoom()->findMonster(player, cmnd, 2);
if(!target) {
player->print("That creature is not here.\n");
return(0);
}
if(!Faction::willDoBusinessWith(player, target->getPrimeFaction())) {
player->print("%M refuses to do business with you.\n", target);
return(0);
}
if(!target->flagIsSet(M_TOLLKEEPER)) {
player->print("%M doesn't take toll payments.\n", target);
return(0);
}
exit = findExit(player, cmnd, 3);
if(!exit) {
player->print("That exit is not here.\n");
return(0);
}
if(!exit->flagIsSet(X_TOLL_TO_PASS)) {
player->print("That is not a tolled exit.\n");
return(0);
}
tc = tollcost(player, exit, target);
if(player->coins[GOLD] < amt) {
player->print("You do not have that much gold on you.\n");
return(0);
}
if(amt < tc) {
player->print("You must pay at least %ld gold coins to pass through the %s.\n", tc, exit->name);
return(0);
}
if(amt > tc) {
player->print("That is too much. The cost to pass through %s is %ld gold coins.\n", exit->name, tc);
return(0);
}
newRoom = exit->target.loadRoom(player);
if(!newRoom) {
player->print("The %s appears to be jammed shut.\nPlease try again later.\n", exit->name);
return(0);
}
Room* uRoom = newRoom->getUniqueRoom();
if(uRoom && !player->canEnter(uRoom, true))
return(0);
player->coins.sub(amt, GOLD);
player->print("%M accepts your toll and ushers you through the %s.\n", target, exit->name);
broadcast(player->getSock(), player->getRoom(), "%M pays %N some coins and goes through the %s.", player, target, exit->name);
player->deleteFromRoom();
player->addToRoom(newRoom);
player->doPetFollow();
return(0);
}
//*********************************************************************
// tollcost
//*********************************************************************
unsigned long tollcost(const Player* player, const Exit* exit, Monster* keeper) {
unsigned long cost = exit->getToll() ? exit->getToll() : DEFAULT_TOLL;
if(!player)
return(cost);
if(exit->flagIsSet(X_LEVEL_BASED_TOLL))
cost = cost * player->getLevel() * 2;
if(!keeper)
keeper = player->getRoom()->getTollkeeper();
if(keeper) {
Money money;
money.set(cost, GOLD);
money = Faction::adjustPrice(player, keeper->getPrimeFaction(), money, true);
cost = money[GOLD];
}
return(cost);
}
//*********************************************************************
// infoGamestat
//*********************************************************************
int infoGamestat(Player* player, cmd* cmnd) {
int players=0, immorts=0;
int daytime=0;
long t=0, days=0, hours=0, minutes=0;
char *str;
std::pair<bstring, Player*> p;
Player* target=0;
foreach(p, gServer->players) {
target = p.second;
if(!target->isConnected())
continue;
if(target->isStaff())
immorts++;
if(!target->isStaff())
players++;
}
player->print("\nCurrent Realms Gamestat Information\n\n");
t = time(0);
daytime = gConfig->currentHour();
days = (t - StartTime) / (60*60*24);
hours = (t - StartTime) / (60*60);
hours %= 24;
minutes = (t - StartTime) / 60L;
minutes %= 60;
player->printColor("^cGame-Time: %d:%02d %s. %s.\n",
gConfig->currentHour(true),
gConfig->currentMinutes(),
daytime > 11 ? "PM" : "AM",
isDay() ? "It is day" : "It is night"
);
if(player->isCt())
player->printColor("^gThe mud has been running for %d game days.\n", gConfig->calendar->getTotalDays());
str = ctime(&t);
str[strlen(str) - 1] = 0;
player->printColor("^MReal-Time: %s (%s).\n", str, gServer->getTimeZone().c_str());
if(!days)
player->printColor("^RRealms Uptime: %02ld:%02ld:%02ld\n", hours, minutes, (t - StartTime) % 60L);
else if(days == 1)
player->printColor("^RRealms Uptime: %ld day %02ld:%02ld:%02ld\n", days, hours, minutes, (t - StartTime) % 60L);
else
player->printColor("^RRealms Uptime: %ld days %02ld:%02ld:%02ld\n", days, hours, minutes, (t - StartTime) % 60L);
player->printColor("^yTotal players currently online: %d\n", players);
if(player->isDm())
player->printColor("^yTotal staffs online: %d\n", immorts);
player->print("\n");
return(0);
}
//*********************************************************************
// cmdDescription
//*********************************************************************
// this allows a player to set his/her description that is seen when you
// look at them.
int cmdDescription(Player* player, cmd* cmnd) {
player->clearFlag(P_AFK);
if(!player->ableToDoCommand())
return(0);
if(cmnd->num < 2) {
player->print("Syntax: description [text|-d]\n.");
return(0);
}
if(!strcmp(cmnd->str[1], "-d")) {
player->print("Description cleared.\n");
player->setDescription("");
return(0);
}
player->setDescription(getFullstrText(cmnd->fullstr, 1));
player->escapeText();
player->print("Description set.\n");
return(0);
}