/************************************************************** * FFTacticsMUD : desc.cpp * ************************************************************** * (c) 2002 Damien Dailidenas (Trenton). All rights reserved. * **************************************************************/ #include "main.h" #include <strstream> #include <cstdio> #include <cerrno> #include <unistd.h> #include "mysql/mysql.h" bool wizlock, newlock; DESC::DESC() { snoop_by=NULL; ch=NULL; fcommand=0; desc=0; connected=0; repeat=0; timer=0; connected=CON_GET_NAME; next=desc_list; desc_list=this; return; } DESC::~DESC() { if(this == desc_list) desc_list = next; else { for(DESC *d = desc_list; d; d = d->next) { if(d->next == this) { d->next = next; break; } } } return; } bool DESC::load_ch(const string name) { CH *ch = new CH; bool found = false; MYSQL mysql; MYSQL_RES *res; MYSQL_ROW row; this->ch = ch; ch->desc = this; ch->name = name; ch->confirm_delete = false; mysql_init(&mysql); if(!mysql_real_connect(&mysql, DB_LOC, DB_USER, DB_PASS, DB_NAME, 0, NULL, 0)) return false; ostrstream ost; ost << "SELECT * FROM players WHERE name='" << name << "'" << ends; mysql_query(&mysql, ost.str()); res = mysql_store_result(&mysql); if(mysql_num_rows(res)) { found = true; row = mysql_fetch_row(res); short x = 1; ch->pwd = row[x]; ch->email = row[++x]; ch->birthday[0] = atoi(row[(x += 3)]); ch->birthday[1] = atoi(row[++x]); ch->HPP = atof(row[++x]); ch->MPP = atof(row[++x]); ch->SpP = atof(row[++x]); ch->PAP = atof(row[++x]); ch->MAP = atof(row[++x]); ch->money = atoi(row[++x]); ch->sex = atoi(row[++x]); ch->cjob = atoi(row[++x]); ch->lvl = atoi(row[++x]); ch->exp = atoi(row[++x]); ch->HP[0] = atoi(row[++x]); ch->HP[1] = atoi(row[++x]); ch->MP[0] = atoi(row[++x]); ch->MP[1] = atoi(row[++x]); ch->PA = atoi(row[++x]); ch->MA = atoi(row[++x]); ch->Sp = atoi(row[++x]); ch->Mv = atoi(row[++x]); ch->Ju = atoi(row[++x]); ch->Ev = atoi(row[++x]); ch->Br = atoi(row[++x]); ch->Fa = atoi(row[++x]); ch->area = get_area(atoi(row[++x])); ch->mins_total = atol(row[++x]); ch->mins_ave = atoi(row[++x]); ch->mins_login = atoi(row[++x]); ch->disabled = atoi(row[++x]); } mysql_free_result(res); mysql_close(&mysql); ch->load_jobs(); ch->load_skills(); ch->load_objects(); ch->load_ignored(); ch->admin = ch->is_admin(); ch->restore(); return found; } void DESC::close_socket() { if(!outbuf.empty()) process_output(false); if(snoop_by) snoop_by->printf("Your victim has left the game.\n\r"); for(DESC *d = desc_list; d; d = d->next) if(d->snoop_by == this) d->snoop_by = NULL; if(ch) { if(connected == CON_PLAYING) { log_string(ch->name + " has lost " + ch->his_her() + " link."); wiz_echo("wiznet> links> " + ch->name + " has lost " + ch->his_her() + " link.\n\r", WIZ_LINKS); ch->desc = NULL; } else zap(ch); } close(desc); DESC *d_this = this; zap(d_this); return; } bool DESC::read() { if(!incomm.empty()) return true; if(inbuf.length() > MIL) { log_string(host + " input overflow!"); return false; } char c_temp[MIL]; for(;;) { int nRead = ::read(desc, c_temp, MIL); if(nRead > 0) { c_temp[nRead] = '\0'; inbuf += c_temp; if(inbuf.find('\n') != string::npos || inbuf.find('\r') != string::npos) break; } else if(!nRead) { log_string("EOF encountered on read."); return false; } else if(errno == EWOULDBLOCK) break; else { perror("DESC::read()"); return false; } } return true; } void DESC::read_buffer() { if(!incomm.empty() || inbuf.empty()) return; if(inbuf.find('\n') == string::npos) return; string::size_type pos; if((pos = inbuf.find('\r')) != string::npos) inbuf.erase(pos, 1); if(inbuf.length() > MIL) { write("Ling too long.\n\r"); inbuf = inbuf.substr(0, MIL - 1); inbuf += '\n'; } incomm = inbuf.substr(0, (pos = inbuf.find('\n'))); inbuf.erase(0, pos + 1); // printf("'" + incomm + "' '" + inbuf + "'\n\r"); if(incomm.empty()) incomm = " "; if((incomm[0] != '!' && incomm != inlast) || !ch || ch->admin) repeat = 0; else { repeat++; if(repeat == 25) { log_string("spam> " + ch->name + ": " + incomm); wiz_echo("wiznet> spam> " + ch->name + ": " + incomm + "\n\r", WIZ_SPAM); } /* else if(repeat >= 50) { log_string("spam> " + ch->name + " disconnected."); wiz_echo("wiznet> spam> " + ch->name + " disconnected.\n\r", WIZ_SPAM); repeat = 0; close_socket(); return; } */ } if(incomm[0] == '!') incomm = inlast; else inlast = incomm; return; } bool DESC::process_output(const bool fPrompt) { extern bool game_down; if(!game_down) if(fPrompt && connected == CON_PLAYING) ch->prompt(); if(outbuf.empty()) return true; if(snoop_by) { if(ch) snoop_by->printf(ch->name); snoop_by->printf("> " + outbuf + ENDL); } if(!write(outbuf)) { perror("write"); outbuf.erase(outbuf.begin(), outbuf.end()); return false; } else { outbuf.erase(outbuf.begin(), outbuf.end()); return true; } } void DESC::printf(const string str) { if(!this || str.empty()) return; if(outbuf.empty() && !fcommand) outbuf = ENDL; outbuf += str; return; } void DESC::nanny(string argument) { bool fOld; ostrstream ost; string str; argument.erase(0, argument.find_first_not_of(' ')); switch(connected) { default: ost << "Nanny: bad connected " << connected << "." << ends; bug(ost.str()); close_socket(); return; case CON_GET_NAME: if(argument.empty()) { close_socket(); return; } argument[0] = UPPER(argument[0]); if(!check_name(argument)) { printf("login: ERROR: Illegal name, try another.\n\rName: "); return; } fOld = load_ch(argument); if(ch->disabled) { printf("ERROR: This account has been disabled.\n\r"); close_socket(); return; } if(multiplaying(ch->name)) return; if(reconnect(argument, false)) fOld = true; else { if(wizlock && !ch->admin) { printf("Temporarily closed...check back later.\n\r"); close_socket(); return; } } if(fOld || reconnect(argument, false)) { printf("Password: "); connected = CON_GET_OLD_PASSWORD; return; } else { if(newlock) { printf("The game is newlocked.\n\r"); close_socket(); return; } ch->printf(ENDL + f_str_box("{!IF YOUR NAME CONTAINS VERBS, ADJECTIVES, OR SPELLED OUT NUMBERS, YOUR CHARACTER WILL BE ERASED!{0") + ENDL); ch->printf("Create new character " + argument + "? [y/n] "); connected = CON_CONFIRM_NEW_NAME; return; } break; case CON_GET_OLD_PASSWORD: if(argument != ch->pwd) { printf("login: ERROR: Password incorrect.\n\r"); close_socket(); return; } if(playing(ch->name)) return; ch->socket = host; if(multiplaying(ch->name)) return; if(reconnect(ch->name, true)) return; str = ch->name + "@" + host + " has logged in."; log_string(str); wiz_echo(str += ENDL); ch->socket = host; if(ch->pwd.empty()) printf("Warning! Null password!\n\rType password null [new password] to fix.\n\r"); connected = CON_PLAYING; ch->next = ch_list; ch_list = ch; ch->login = str_time().substr(0, 3) + str_time().substr(11, 5); ch->printf(ENDL); ch->to(ch->area->room[ROOM_OUTSIDE]); ch->print_MOTD(); ch->check_battle(); break; case CON_BREAK_CONNECT: switch(argument[0]) { case 'y' : case 'Y': for(DESC *d_old = desc_list, *d_next; d_old; d_old = d_next) { d_next = d_old->next; if(d_old == this || !d_old->ch || ch->name != d_old->ch->name) continue; d_old->close_socket(); } ch->socket = host; if(reconnect(ch->name, true)) return; printf("Reconnect attempt failed.\n\rName: "); connected = CON_GET_NAME; break; case 'n' : case 'N': printf("Name: "); connected = CON_GET_NAME; break; default: printf("Type Y or N? "); break; } break; case CON_CONFIRM_NEW_NAME: switch(argument[0]) { case 'y': case 'Y': ch->printf(ENDL + f_str_box("Choose a password that others won't guess and you won't forget.")); ch->printf(f_str_box("{!DO NOT FORGET OR SHARE YOUR PASSWORD WITH ANYONE. IF YOU LOSE YOUR PASSWORD YOU LOSE YOUR CHARACTER.{0", false) + ENDL); ch->socket = host; printf("Enter password: "); connected = CON_GET_NEW_PASSWORD; break; case 'n': case 'N': printf("\n\rName: "); connected = CON_GET_NAME; break; default: ch->printf("Create new character? [y/n] "); break; } break; case CON_GET_NEW_PASSWORD: if(argument.length() < 5 || argument.length() > 12) { ch->printf("login: ERROR: Password must be between 5 and 12 characters long.\n\rEnter password: "); return; } if(argument.find('/') != string::npos || argument.find('\\') != string::npos || argument.find("'") != string::npos) { ch->printf("login: ERROR: Password contains illegal characters, try again.\n\rEnter password: "); return; } ch->pwd = argument; printf("Re-enter password: "); connected = CON_CONFIRM_NEW_PASSWORD; break; case CON_CONFIRM_NEW_PASSWORD: if(argument != ch->pwd) { ch->printf("login: ERROR: Passwords don't match.\n\rEnter password: "); connected = CON_GET_NEW_PASSWORD; return; } ch->printf(ENDL + f_str_box("Your email address is required to receive news concerning the MUD and most importantly, for password retrieval if you happen to lose or forget your password.") + ENDL); ch->printf("What is your email address? "); connected = CON_GET_EMAIL; break; case CON_GET_EMAIL: ch->email = argument; ch->printf(ENDL + f_str_box("Your sex affects your overall stat growth and in some cases, which items you can equip and which jobs you can be.", true, false)); ch->printf(f_str_box("{#NOTE: Males make better fighters, females make better magic users.{0", false) + ENDL); ch->printf("\t[1] male\n\r\t[2] female\n\r\n\rWhat is your sex? "); connected = CON_GET_SEX; break; case CON_GET_SEX: ch->sex = atoi(argument.c_str()); if(ch->sex != 1 && ch->sex != 2) { ch->printf("sex: ERROR: Invalid sex.\n\rWhat is your sex? "); break; } --ch->sex; ch->printf(ENDL + f_str_box("Your birthday determines your zodiac sign which affects several relationships between you and another character, such as hit percentage and magic damage.")); for(short x = 1; month_table[x].name; ++x) ch->printf("\n\r\t[%02d] %s", x, month_table[x].name); ch->printf("\n\r\n\rSelect month you were born: "); connected = CON_GET_BIRTHMONTH; break; case CON_GET_BIRTHMONTH: ch->birthday[0] = atoi(argument.c_str()); if(ch->birthday[0] < 1 || ch->birthday[0] > 12) { ch->printf("ERROR: Invalid month.\n\rWhat month were you born? "); break; } { ostrstream ost; ost << "Select day you were born [1 - " << month_table[ch->birthday[0]].length << "]: " << ends; ch->printf(ost.str()); } connected = CON_GET_BIRTHDAY; break; case CON_GET_BIRTHDAY: ch->birthday[1] = atoi(argument.c_str()); if(ch->birthday[1] < 1 || ch->birthday[1] > month_table[ch->birthday[0]].length) { ch->printf("ERROR: Invalid day.\n\rWhat day were you born? "); break; } ch->printf(ENDL + f_str_box("PK stands for player killing. A PK character can kill and be killed by other PK characters only. A NONPK character can't kill or be killed by any other player.")); ch->printf(f_str_box("{!PK STATUS CAN'T BE CHANGED AFTER THIS POINT!{0", false)); ch->printf("\n\r\t[1] PK\n\r\t[2] NONPK\n\r\n\r"); ch->printf("Select PK status? "); connected = CON_GET_PK; break; case CON_GET_PK: if(argument != "1" && argument != "2") { ch->printf("PK: ERROR: Invalid selection.\n\rSelect PK status? "); break; } ch->PK -= (ch->PK = atoi(argument.c_str())); ch->printf(ENDL + f_str_box("{^CHARACTER CREATION COMPLETE!{0", true, false)); ch->printf(f_str_box("Press [enter] to continue.")); connected = CON_FINISH; break; case CON_FINISH: connected = CON_PLAYING; ch->next = ch_list; ch_list = ch; log_string(ch->name + "@" + host + " new player."); ch->printf(ENDL); ch->login = str_time().substr(0, 3) + str_time().substr(11, 5); ch->init_stats(); ch->lvl = 1; ch->set_job(JOB_SQUIRE); ch->job[ch->cjob].lvl = 1; OBJ *obj; (obj = create_obj("Broad Sword"))->to(ch); ch->equip(obj, LOC_RHAND); (obj = create_obj("Leather Hat"))->to(ch); ch->equip(obj, LOC_HEAD); (obj = create_obj("Clothes"))->to(ch); ch->equip(obj, LOC_BODY); (obj = create_obj("Battle Boots"))->to(ch); ch->equip(obj, LOC_ACC); for(short x = 0; x < 4; ++x) create_obj("Potion")->to(ch); ch->restore(); ch->to(get_area(AREA_ORBONNE_MONASTERY)->room[ROOM_OUTSIDE]); ch->print_MOTD(); ch->save(); wiz_echo("New player " + ch->name + "@" + ch->socket + " has logged in.\n\r"); do_look(ch); ch->check_battle(); break; } return; } bool DESC::playing(const string name) { for(DESC *d = desc_list; d; d = d->next) { if(d != this && d->ch && d->connected != CON_GET_NAME && d->connected != CON_GET_OLD_PASSWORD && capitalize(name) == capitalize(d->ch->name)) { /* printf("That character is already playing. Connect anyway? [y/n] "); connected = CON_BREAK_CONNECT; */ printf(f_str("That character is already playing from another location. Either log on and quit out from that location, or wait for your character to autoquit after being idle for 10 minutes.")); close_socket(); return true; } } return false; } bool DESC::multiplaying(const string name) { //TEMP if(ch->admin) return false; for(CH *ch = ch_list; ch; ch = ch->next) { if(!ch->npc && ch->name != name && !ch->admin && ch->socket == host && ch != this->ch) { printf("Multiplaying is not allowed.\n\r"); close_socket(); return true; } } for(DESC *d = desc_list; d; d = d->next) { if(d->ch->name != name && !d->ch->admin && d->host == host) { printf("Multiplaying is not allowed.\n\r"); close_socket(); return true; } } return false; } bool DESC::reconnect(const string name, bool fConn) { for(CH *ch2 = ch_list; ch2; ch2 = ch2->next) { if(!ch2->npc && (!fConn || !ch2->desc) && ch->name == ch2->name) { if(!fConn) ch->pwd = ch2->pwd; else { zap(ch); ch = ch2; ch->desc = this; ch->timer = 0; ch->printf("Reconnecting...\n\r"); log_string(ch->name + "@" + host + " reconnected."); wiz_echo("wiznet> links> " + ch->name + " reconnected.\n\r", WIZ_LINKS); connected = CON_PLAYING; } return true; } } return false; } string DESC::conn_status() { return connected_table[connected].status; } bool DESC::write(const string str) { if(!(::write(desc, str.c_str(), str.length()))) { perror("DESC::write"); return false; } return true; } bool write_to_desc(const short desc, const string str) { if(!(write(desc, str.c_str(), str.length()))) { perror("write_to_desc"); return false; } return true; }