diff --git a/changelog.txt b/changelog.txt index 44b72c1df..01a190eb1 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,6 +1,6 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- -== 09/21/2014 == +== 09/21/2014 == Akkadius: Player Profile Blob to Database Conversion - Summary: HUGE difference in database speeds reads/writes and 1:10 datasize difference - The new character storage engine unlike the character_ table before, is able to properly index data and make use of @@ -94,6 +94,24 @@ Akkadius: Player Profile Blob to Database Conversion WHERE `table_name` LIKE 'character_%' ORDER BY DATA DESC; +== 09/20/2014 == +demonstar55: Fix crash in SendEnterWorld on illegally long names +demonstar55: The client only lets you enter 15 characters for your name (UF at least) +demonstar55: Add rule Spells:SHDProcIDOffByOne for pre-UF spell file, set to true, UF+ set to false +KLS: #suspend and #ban now have required messages to record the reason for the ban/suspension. + +== 09/19/2014 == +demonstar55: Added Client::Tell_StringID (used in tell queue messages) +demonstar55: Tell queues (and offline) messages now show correctly +demonstar55: Fix starting with capital check + +== 09/18/2014== +demonstar55: Implement tell queues + Currently set to a limit of 20 by default (World:TellQueueSize) I was unable to hit the limit on live though (100+) + The required SQL nukes the old tell queue table, which may or may not be in your DB + Optional SQL adds the rule to the DB to allow easy of change + Note: this does not play well with multiple sessions with the same name on (crash and relog and have multiple sessions) but normal tells don't play well either + == 09/16/2014 == demonstar55: Implement spell formula 137 (BER AA Desperation) Uleat (NateDog): Fix for LoadBuffs() crash when a spell with a non-persistent Illusion effect was loaded. diff --git a/common/database.cpp b/common/database.cpp index d53141e71..78eec13a7 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -770,10 +770,20 @@ uint32 Database::GetCharacterID(const char *name) { the name "name" or zero if no character with that name was found Zero will also be returned if there is a database error. */ -uint32 Database::GetAccountIDByChar(const char* charname) { - uint32 accountId = 0; - std::string query = StringFormat("SELECT `account_id`, `id` FROM `character_data` WHERE `name` = '%s' LIMIT 1", charname); - auto results = QueryDatabase(query); +uint32 Database::GetAccountIDByChar(const char* charname, uint32* oCharID) { + std::string query = StringFormat("SELECT account_id, id FROM character_ WHERE name='%s'", EscapeString(charname).c_str()); + + auto results = QueryDatabase(query); + + if (!results.Success()) + { + std::cerr << "Error in GetAccountIDByChar query '" << query << "' " << results.ErrorMessage() << std::endl; + return 0; + } + + if (results.RowCount() != 1) + return 0; + auto row = results.begin(); if (row[0]){ accountId = atoi(row[0]); } return accountId; @@ -2244,7 +2254,7 @@ bool Database::CheckNameFilter(const char* name, bool surname) else { // the minimum 4 is enforced by the client too - if(!name || strlen(name) < 4 || strlen(name) > 64) + if(!name || strlen(name) < 4 || strlen(name) > 15) { return false; } diff --git a/common/mysql_request_result.cpp b/common/mysql_request_result.cpp index df598c8cd..b4bf30842 100644 --- a/common/mysql_request_result.cpp +++ b/common/mysql_request_result.cpp @@ -10,7 +10,7 @@ MySQLRequestResult::MySQLRequestResult() MySQLRequestResult::MySQLRequestResult(MYSQL_RES* result, uint32 rowsAffected, uint32 rowCount, uint32 columnCount, uint32 lastInsertedID, uint32 errorNumber, char *errorBuffer) : m_CurrentRow(result), m_OneBeyondRow() { - m_Result = result; + m_Result = result; m_RowsAffected = rowsAffected; m_RowCount = rowCount; m_ColumnCount = columnCount; @@ -22,12 +22,12 @@ MySQLRequestResult::MySQLRequestResult(MYSQL_RES* result, uint32 rowsAffected, u m_ColumnLengths = nullptr; m_Fields = nullptr; - if (errorBuffer != nullptr) + m_Success = true; + if (errorBuffer != nullptr) m_Success = false; - m_Success = true; - m_ErrorNumber = errorNumber; - m_ErrorBuffer = errorBuffer; + m_ErrorNumber = errorNumber; + m_ErrorBuffer = errorBuffer; } void MySQLRequestResult::FreeInternals() diff --git a/common/ruletypes.h b/common/ruletypes.h index c61590ac4..058d5ce6f 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -171,6 +171,7 @@ RULE_INT ( World, PVPSettings, 0) // Sets the PVP settings for the server, 1 = R RULE_BOOL (World, IsGMPetitionWindowEnabled, false) RULE_INT (World, FVNoDropFlag, 0) // Sets the Firiona Vie settings on the client. If set to 2, the flag will be set for GMs only, allowing trading of no-drop items. RULE_BOOL (World, IPLimitDisconnectAll, false) +RULE_INT (World, TellQueueSize, 20) RULE_CATEGORY_END() RULE_CATEGORY( Zone ) @@ -320,6 +321,7 @@ RULE_INT ( Spells, AI_PursueDetrimentalChance, 90) // Chance while chasing targe RULE_INT ( Spells, AI_IdleNoSpellMinRecast, 500) // AI spell recast time(MS) check when no spell is cast while idle. (min time in random) RULE_INT ( Spells, AI_IdleNoSpellMaxRecast, 2000) // AI spell recast time(MS) check when no spell is cast while chasing target. (max time in random) RULE_INT ( Spells, AI_IdleBeneficialChance, 100) // Chance while idle to do a beneficial spell on self or others. +RULE_BOOL ( Spells, SHDProcIDOffByOne, true) // pre June 2009 SHD spell procs were off by 1, they stopped doing this in June 2009 (so UF+ spell files need this false) RULE_CATEGORY_END() diff --git a/common/servertalk.h b/common/servertalk.h index 687abf2d7..8c30715d9 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -83,6 +83,7 @@ #define ServerOP_QGlobalUpdate 0x0063 #define ServerOP_QGlobalDelete 0x0064 #define ServerOP_DepopPlayerCorpse 0x0065 +#define ServerOP_RequestTellQueue 0x0066 // client asks for it's tell queues #define ServerOP_RaidAdd 0x0100 //in use #define ServerOP_RaidRemove 0x0101 //in use @@ -348,6 +349,7 @@ struct ServerChannelMessage_Struct { uint16 chan_num; uint32 guilddbid; uint16 language; + uint8 queued; // 0 = not queued, 1 = queued, 2 = queue full, 3 = offline char message[0]; }; @@ -1250,6 +1252,10 @@ struct ReloadWorld_Struct{ uint32 Option; }; +struct ServerRequestTellQueue_Struct { + char name[64]; +}; + #pragma pack() #endif diff --git a/utils/sql/git/optional/2014_09_18_TellQueueRule.sql b/utils/sql/git/optional/2014_09_18_TellQueueRule.sql new file mode 100644 index 000000000..2a9b43611 --- /dev/null +++ b/utils/sql/git/optional/2014_09_18_TellQueueRule.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'World:TellQueueSize', '20', 'Maximum tell queue size.'); diff --git a/utils/sql/git/optional/2014_09_20_SHDProCIDOffByOne.sql b/utils/sql/git/optional/2014_09_20_SHDProCIDOffByOne.sql new file mode 100644 index 000000000..de6eb7d85 --- /dev/null +++ b/utils/sql/git/optional/2014_09_20_SHDProCIDOffByOne.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:SHDProcIDOffByOne', 'true', 'SHD procs are off by 1. Set true for pre-UF spell files, false for UF+.'); diff --git a/utils/sql/git/required/2014_09_18_tellqueuesclean.sql b/utils/sql/git/required/2014_09_18_tellqueuesclean.sql new file mode 100644 index 000000000..04d9b38d1 --- /dev/null +++ b/utils/sql/git/required/2014_09_18_tellqueuesclean.sql @@ -0,0 +1 @@ +DROP TABLE `tellque`; diff --git a/utils/sql/git/required/2014_09_20_ban_messages.sql b/utils/sql/git/required/2014_09_20_ban_messages.sql new file mode 100644 index 000000000..4612f9962 --- /dev/null +++ b/utils/sql/git/required/2014_09_20_ban_messages.sql @@ -0,0 +1 @@ +ALTER TABLE `account` ADD COLUMN `ban_reason` TEXT NULL DEFAULT NULL AFTER `expansion`, ADD COLUMN `suspend_reason` TEXT NULL DEFAULT NULL AFTER `ban_reason`; diff --git a/world/client.cpp b/world/client.cpp index 73c4a9493..cd028d409 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -126,8 +126,9 @@ void Client::SendLogServer() safe_delete(outapp); } -void Client::SendEnterWorld(std::string name) { - char char_name[32]= { 0 }; +void Client::SendEnterWorld(std::string name) +{ + char char_name[64] = { 0 }; if (pZoning && database.GetLiveChar(GetAccountID(), char_name)) { if(database.GetAccountIDByChar(char_name) != GetAccountID()) { eqs->Close(); @@ -468,7 +469,8 @@ bool Client::HandleSendLoginInfoPacket(const EQApplicationPacket *app) { return true; } -bool Client::HandleNameApprovalPacket(const EQApplicationPacket *app) { +bool Client::HandleNameApprovalPacket(const EQApplicationPacket *app) +{ if (GetAccountID() == 0) { clog(WORLD__CLIENT_ERR,"Name approval request with no logged in account"); return false; @@ -478,7 +480,7 @@ bool Client::HandleNameApprovalPacket(const EQApplicationPacket *app) { uchar race = app->pBuffer[64]; uchar clas = app->pBuffer[68]; - clog(WORLD__CLIENT,"Name approval request. Name=%s, race=%s, class=%s",char_name,GetRaceName(race),GetEQClassName(clas)); + clog(WORLD__CLIENT, "Name approval request. Name=%s, race=%s, class=%s", char_name, GetRaceName(race), GetEQClassName(clas)); EQApplicationPacket *outapp; outapp = new EQApplicationPacket; @@ -495,9 +497,8 @@ bool Client::HandleNameApprovalPacket(const EQApplicationPacket *app) { QueuePacket(outapp); safe_delete(outapp); - if(!valid) { + if (!valid) memset(char_name, 0, sizeof(char_name)); - } return true; } diff --git a/world/cliententry.cpp b/world/cliententry.cpp index d4b85acc3..fa5b60c43 100644 --- a/world/cliententry.cpp +++ b/world/cliententry.cpp @@ -93,6 +93,7 @@ ClientListEntry::~ClientListEntry() { Camp(); // updates zoneserver's numplayers client_list.RemoveCLEReferances(this); } + tell_queue.clear(); } void ClientListEntry::SetChar(uint32 iCharID, const char* iCharName) { @@ -233,6 +234,7 @@ void ClientListEntry::ClearVars(bool iAll) { pLFG = 0; gm = 0; pClientVersion = 0; + tell_queue.clear(); } void ClientListEntry::Camp(ZoneServer* iZS) { @@ -295,3 +297,21 @@ bool ClientListEntry::CheckAuth(uint32 id, const char* iKey, uint32 ip) { return false; } +void ClientListEntry::ProcessTellQueue() +{ + if (!Server()) + return; + + ServerPacket *pack; + auto it = tell_queue.begin(); + while (it != tell_queue.end()) { + pack = new ServerPacket(ServerOP_ChannelMessage, sizeof(ServerChannelMessage_Struct) + strlen((*it)->message) + 1); + memcpy(pack->pBuffer, *it, pack->size); + pack->Deflate(); + Server()->SendPacket(pack); + safe_delete(pack); + it = tell_queue.erase(it); + } + return; +} + diff --git a/world/cliententry.h b/world/cliententry.h index dcf79ca4f..cb096950c 100644 --- a/world/cliententry.h +++ b/world/cliententry.h @@ -5,6 +5,8 @@ #include "../common/md5.h" //#include "../common/eq_packet_structs.h" #include "../common/servertalk.h" +#include "../common/rulesys.h" +#include #define CLE_Status_Never -1 @@ -80,6 +82,11 @@ public: inline const char* GetLFGComments() const { return pLFGComments; } inline uint8 GetClientVersion() { return pClientVersion; } + inline bool TellQueueFull() const { return tell_queue.size() >= RuleI(World, TellQueueSize); } + inline bool TellQueueEmpty() const { return tell_queue.empty(); } + inline void PushToTellQueue(ServerChannelMessage_Struct *scm) { tell_queue.push_back(scm); } + void ProcessTellQueue(); + private: void ClearVars(bool iAll = false); @@ -120,6 +127,9 @@ private: uint8 pLFGToLevel; bool pLFGMatchFilter; char pLFGComments[64]; + + // Tell Queue -- really a vector :D + std::vector tell_queue; }; #endif /*CLIENTENTRY_H_*/ diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index c05962816..1023dbe69 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -437,45 +437,48 @@ bool ZoneServer::Process() { Console* con = 0; con = console_list.FindByAccountName(&scm->deliverto[1]); if (((!con) || (!con->SendChannelMessage(scm))) && (!scm->noreply)) - zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "You told %s, '%s is not online at this time'", scm->to, scm->to); + zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "%s is not online at this time.", scm->to); break; } ClientListEntry* cle = client_list.FindCharacter(scm->deliverto); - if (cle == 0 || cle->Online() < CLE_Status_Zoning || (cle->TellsOff() && ((cle->Anon() == 1 && scm->fromadmin < cle->Admin()) || scm->fromadmin < 80))) { - if (!scm->noreply) - zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "You told %s, '%s is not online at this time'", scm->to, scm->to); - } - else if (cle->Online() == CLE_Status_Zoning) { - if (!scm->noreply) - { - // time_t rawtime; - // struct tm * timeinfo; - // time ( &rawtime ); - // timeinfo = localtime ( &rawtime ); - // char *telldate=asctime(timeinfo); - // - // std::string query = StringFormat("SELECT name FROM `character_data` WHERE name = '%s'",scm->deliverto); - // auto results = database.QueryDatabase(query); - // if (!results.Success()) - // break; - // - // if (results.RowCount() == 0) { - // zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "You told %s, '%s is not online at this time'", scm->to, scm->to); - // break; - // } - // - // query = StringFormat("INSERT INTO tellque " - // "(Date, Receiver, Sender, Message) " - // "VALUES('%s', '%s', '%s', '%s')", - // telldate, scm->deliverto, scm->from, scm->message); - // results = database.QueryDatabase(query); - // if (results.Success()) - // zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "Your message has been added to the %s's que.", scm->to); - // else - // zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "You told %s, '%s is not online at this time'", scm->to, scm->to); - + if (cle == 0 || cle->Online() < CLE_Status_Zoning || + (cle->TellsOff() && ((cle->Anon() == 1 && scm->fromadmin < cle->Admin()) || scm->fromadmin < 80))) { + if (!scm->noreply) { + ClientListEntry* sender = client_list.FindCharacter(scm->from); + if (!sender) + break; + scm->noreply = true; + scm->queued = 3; // offline + strcpy(scm->deliverto, scm->from); + // ideally this would be trimming off the message too, oh well + sender->Server()->SendPacket(pack); + } + } else if (cle->Online() == CLE_Status_Zoning) { + if (!scm->noreply) { + ClientListEntry* sender = client_list.FindCharacter(scm->from); + if (cle->TellQueueFull()) { + if (!sender) + break; + scm->noreply = true; + scm->queued = 2; // queue full + strcpy(scm->deliverto, scm->from); + sender->Server()->SendPacket(pack); + } else { + size_t struct_size = sizeof(ServerChannelMessage_Struct) + strlen(scm->message) + 1; + ServerChannelMessage_Struct *temp = (ServerChannelMessage_Struct *) new uchar[struct_size]; + memset(temp, 0, struct_size); // just in case, was seeing some corrupt messages, but it shouldn't happen + memcpy(temp, scm, struct_size); + temp->noreply = true; + cle->PushToTellQueue(temp); // deallocation is handled in processing or deconstructor + + if (!sender) + break; + scm->noreply = true; + scm->queued = 1; // queued + strcpy(scm->deliverto, scm->from); + sender->Server()->SendPacket(pack); + } } - // zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "You told %s, '%s is not online at this time'", scm->to, scm->to); } else if (cle->Server() == 0) { if (!scm->noreply) @@ -1285,6 +1288,16 @@ bool ZoneServer::Process() { zoneserver_list.SendPacket(pack); break; } + case ServerOP_RequestTellQueue: + { + ServerRequestTellQueue_Struct* rtq = (ServerRequestTellQueue_Struct*) pack->pBuffer; + ClientListEntry *cle = client_list.FindCharacter(rtq->name); + if (!cle || cle->TellQueueEmpty()) + break; + + cle->ProcessTellQueue(); + break; + } default: { zlog(WORLD__ZONE_ERR,"Unknown ServerOPcode from zone 0x%04x, size %d",pack->opcode,pack->size); diff --git a/zone/client.cpp b/zone/client.cpp index 11735ca6f..ed2475779 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -3077,6 +3077,14 @@ void Client::FilteredMessage_StringID(Mob *sender, uint32 type, eqFilterType fil safe_delete(outapp); } +void Client::Tell_StringID(uint32 string_id, const char *who, const char *message) +{ + char string_id_str[10]; + snprintf(string_id_str, 10, "%d", string_id); + + Message_StringID(MT_TellEcho, TELL_QUEUED_MESSAGE, who, string_id_str, message); +} + void Client::SetTint(int16 in_slot, uint32 color) { Color_Struct new_color; new_color.color = color; diff --git a/zone/client.h b/zone/client.h index e78c37c42..0ad8b043a 100644 --- a/zone/client.h +++ b/zone/client.h @@ -254,6 +254,7 @@ public: const char *message5 = nullptr, const char *message6 = nullptr, const char *message7 = nullptr, const char *message8 = nullptr, const char *message9 = nullptr); + void Tell_StringID(uint32 string_id, const char *who, const char *message); void SendBazaarResults(uint32 trader_id,uint32 class_,uint32 race,uint32 stat,uint32 slot,uint32 type,char name[64],uint32 minprice,uint32 maxprice); void SendTraderItem(uint32 item_id,uint16 quantity); uint16 FindTraderItem(int32 SerialNumber,uint16 Quantity); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index d3c0f5f42..ea832f28b 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -9696,6 +9696,8 @@ void Client::CompleteConnect() { } entity_list.RefreshClientXTargets(this); + + worldserver.RequestTellQueue(GetName()); } void Client::Handle_OP_KeyRing(const EQApplicationPacket *app) diff --git a/zone/command.cpp b/zone/command.cpp index 6f1cdeca3..9a5dd3f6c 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -331,8 +331,6 @@ int command_init(void) { command_add("guilds",nullptr,0,command_guild) || command_add("zonestatus","- Show connected zoneservers, synonymous with /servers",150,command_zonestatus) || command_add("manaburn","- Use AA Wizard class skill manaburn on target",10,command_manaburn) || - command_add("viewmessage","[id] - View messages in your tell queue",100,command_viewmessage) || - command_add("viewmessages",nullptr,0,command_viewmessage) || command_add("doanim","[animnum] [type] - Send an EmoteAnim for you or your target",50,command_doanim) || command_add("randomfeatures","- Temporarily randomizes the Facial Features of your target",80,command_randomfeatures) || command_add("rf",nullptr,80,command_randomfeatures) || @@ -5191,53 +5189,6 @@ void command_manaburn(Client *c, const Seperator *sep) } } -void command_viewmessage(Client *c, const Seperator *sep) -{ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if(sep->arg[1][0]==0) - { - if (database.RunQuery(query, MakeAnyLenString(&query, "SELECT id,date,receiver,sender,message from tellque where receiver='%s'",c->GetName()), errbuf, &result)) - { - if (mysql_num_rows(result)>0) - { - c->Message(0,"You have messages waiting for you to view."); - c->Message(0,"Type #Viewmessage to view the message."); - c->Message(0," ID , Message Sent Date, Message Sender"); - while ((row = mysql_fetch_row(result))) - c->Message(0,"ID: %s Sent Date: %s Sender: %s ",row[0],row[1],row[3]); - } - else - c->Message(0,"You have no new messages"); - mysql_free_result(result); - } - safe_delete_array(query); - } - else - { - if (database.RunQuery(query, MakeAnyLenString(&query, "SELECT id,date,receiver,sender,message from tellque where id=%s",sep->argplus[1]), errbuf, &result)) - { - if (mysql_num_rows(result)==1) - { - row = mysql_fetch_row(result); - mysql_free_result(result); - if (strcasecmp((const char *) c->GetName(), (const char *) row[2]) == 0) - { - c->Message(15,"ID: %s,Sent Date: %s,Sender: %s,Message: %s",row[0],row[1],row[3],row[4]); - database.RunQuery(query, MakeAnyLenString(&query, "Delete from tellque where id=%s",row[0]), errbuf); - } - else - c->Message(13,"Invalid Message Number, check the number and try again."); - } - else - c->Message(13,"Invalid Message Number, check the number and try again."); - } - safe_delete_array(query); - } -} - void command_doanim(Client *c, const Seperator *sep) { if (!sep->IsNumber(1)) @@ -6204,36 +6155,49 @@ void command_stun(Client *c, const Seperator *sep) c->Message(0, "Usage: #stun [duration]"); } + void command_ban(Client *c, const Seperator *sep) { char errbuf[MYSQL_ERRMSG_SIZE]; char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if(sep->arg[1][0] == 0) + if(sep->arg[1][0] == 0 || sep->arg[2][0] == 0) { - c->Message(0, "Usage: #ban [charname]"); + c->Message(0, "Usage: #ban "); } else { - database.RunQuery(query, MakeAnyLenString(&query, "SELECT account_id from `character_data` where name = '%s'", sep->arg[1]), errbuf, &result); - if(query) - { - safe_delete_array(query); + auto account_id = database.GetAccountIDByChar(sep->arg[1]); + + std::string message; + int i = 2; + while(1) { + if(sep->arg[i][0] == 0) { + break; + } + + if(message.length() > 0) { + message.push_back(' '); + } + + message += sep->arg[i]; + ++i; } - if(mysql_num_rows(result)) - { - row = mysql_fetch_row(result); - database.RunQuery(query, MakeAnyLenString(&query, "UPDATE account set status = -2 where id = %i", atoi(row[0])), errbuf, 0); - c->Message(13,"Account number %i with the character %s has been banned.", atoi(row[0]), sep->arg[1]); + if(message.length() == 0) { + c->Message(0, "Usage: #ban "); + return; + } - ServerPacket* pack = new ServerPacket(ServerOP_FlagUpdate, 6); - *((uint32*) pack->pBuffer) = atoi(row[0]); - *((int16*) &pack->pBuffer[4]) = -2; - worldserver.SendPacket(pack); - safe_delete(pack); + if(account_id > 0) + { + database.RunQuery(query, MakeAnyLenString(&query, "UPDATE account set status = -2, ban_reason = '%s' where id = %i", EscapeString(message).c_str(), account_id), errbuf, 0); + c->Message(13, "Account number %i with the character %s has been banned with message: \"%s\"", account_id, sep->arg[1], message.c_str()); + + ServerPacket pack(ServerOP_FlagUpdate, 6); + *((uint32*)&pack.pBuffer[0]) = account_id; + *((int16*)&pack.pBuffer[4]) = -2; + worldserver.SendPacket(&pack); Client *client = nullptr; client = entity_list.GetClientByName(sep->arg[1]); @@ -6243,25 +6207,20 @@ void command_ban(Client *c, const Seperator *sep) } else { - ServerPacket* pack = new ServerPacket(ServerOP_KickPlayer, sizeof(ServerKickPlayer_Struct)); - ServerKickPlayer_Struct* skp = (ServerKickPlayer_Struct*) pack->pBuffer; + ServerPacket pack(ServerOP_KickPlayer, sizeof(ServerKickPlayer_Struct)); + ServerKickPlayer_Struct* skp = (ServerKickPlayer_Struct*)pack.pBuffer; strcpy(skp->adminname, c->GetName()); strcpy(skp->name, sep->arg[1]); skp->adminrank = c->Admin(); - worldserver.SendPacket(pack); - safe_delete(pack); + worldserver.SendPacket(&pack); } - - mysql_free_result(result); } else { - c->Message(13,"Character does not exist."); - } - if(query) - { - safe_delete_array(query); + c->Message(13, "Character does not exist."); } + + safe_delete_array(query); } } @@ -6271,7 +6230,7 @@ void command_suspend(Client *c, const Seperator *sep) char *query = nullptr; if((sep->arg[1][0] == 0) || (sep->arg[2][0] == 0)) - c->Message(0, "Usage: #suspend (Specify 0 days to lift the suspension immediately)"); + c->Message(0, "Usage: #suspend (Specify 0 days to lift the suspension immediately) "); else { int Duration = atoi(sep->arg[2]); @@ -6279,22 +6238,40 @@ void command_suspend(Client *c, const Seperator *sep) if(Duration < 0) Duration = 0; - char *EscName = new char[strlen(sep->arg[1]) * 2 + 1]; + std::string message; + if(Duration > 0) { + int i = 3; + while(1) { + if(sep->arg[i][0] == 0) { + break; + } - database.DoEscapeString(EscName, sep->arg[1], strlen(sep->arg[1])); + if(message.length() > 0) { + message.push_back(' '); + } + + message += sep->arg[i]; + ++i; + } + + if(message.length() == 0) { + c->Message(0, "Usage: #suspend (Specify 0 days to lift the suspension immediately) "); + return; + } + } int AccountID; - if((AccountID = database.GetAccountIDByChar(EscName)) > 0) + if((AccountID = database.GetAccountIDByChar(sep->arg[1])) > 0) { - database.RunQuery(query, MakeAnyLenString(&query, "UPDATE `account` SET `suspendeduntil` = DATE_ADD(NOW(), INTERVAL %i DAY)" - " WHERE `id` = %i", Duration, AccountID), errbuf, 0); + database.RunQuery(query, MakeAnyLenString(&query, "UPDATE `account` SET `suspendeduntil` = DATE_ADD(NOW(), INTERVAL %i DAY), " + "suspend_reason = '%s' WHERE `id` = %i", Duration, EscapeString(message).c_str(), AccountID), errbuf, 0); if(Duration) - c->Message(13,"Account number %i with the character %s has been temporarily suspended for %i day(s).", AccountID, sep->arg[1], - Duration); + c->Message(13, "Account number %i with the character %s has been temporarily suspended for %i day(s) with the message: \"%s\"", AccountID, sep->arg[1], + Duration, message.c_str()); else - c->Message(13,"Account number %i with the character %s is no longer suspended.", AccountID, sep->arg[1]); + c->Message(13, "Account number %i with the character %s is no longer suspended.", AccountID, sep->arg[1]); safe_delete_array(query); @@ -6304,22 +6281,20 @@ void command_suspend(Client *c, const Seperator *sep) BannedClient->WorldKick(); else { - ServerPacket* pack = new ServerPacket(ServerOP_KickPlayer, sizeof(ServerKickPlayer_Struct)); - ServerKickPlayer_Struct* sks = (ServerKickPlayer_Struct*) pack->pBuffer; + ServerPacket pack(ServerOP_KickPlayer, sizeof(ServerKickPlayer_Struct)); + ServerKickPlayer_Struct* sks = (ServerKickPlayer_Struct*)pack.pBuffer; strn0cpy(sks->adminname, c->GetName(), sizeof(sks->adminname)); strn0cpy(sks->name, sep->arg[1], sizeof(sks->name)); sks->adminrank = c->Admin(); - worldserver.SendPacket(pack); - - safe_delete(pack); + worldserver.SendPacket(&pack); } - } else - c->Message(13,"Character does not exist."); - - safe_delete_array(EscName); + } + else { + c->Message(13, "Character does not exist."); + } } } diff --git a/zone/command.h b/zone/command.h index c190a8ecb..c02c127dc 100644 --- a/zone/command.h +++ b/zone/command.h @@ -215,7 +215,6 @@ void command_guild(Client *c, const Seperator *sep); bool helper_guild_edit(Client *c, uint32 dbid, uint32 eqid, uint8 rank, const char* what, const char* value); void command_zonestatus(Client *c, const Seperator *sep); void command_manaburn(Client *c, const Seperator *sep); -void command_viewmessage(Client *c, const Seperator *sep); void command_doanim(Client *c, const Seperator *sep); void command_randomfeatures(Client *c, const Seperator *sep); void command_face(Client *c, const Seperator *sep); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index f0bc8498b..a81377f5d 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -5598,28 +5598,29 @@ void Mob::CheckNumHitsRemaining(uint8 type, uint32 buff_slot, uint16 spell_id) } //for some stupid reason SK procs return theirs one base off... -uint16 Mob::GetProcID(uint16 spell_id, uint8 effect_index) { +uint16 Mob::GetProcID(uint16 spell_id, uint8 effect_index) +{ + if (!RuleB(Spells, SHDProcIDOffByOne)) // UF+ spell files + return spells[spell_id].base[effect_index]; + + // We should actually just be checking if the mob is SHD, but to not force + // custom servers to create new spells, we will still do this bool sk = false; bool other = false; - for(int x = 0; x < 16; x++) - { - if(x == 4){ - if(spells[spell_id].classes[4] < 255) + for (int x = 0; x < 16; x++) { + if (x == 4) { + if (spells[spell_id].classes[4] < 255) sk = true; - } - else{ - if(spells[spell_id].classes[x] < 255) + } else { + if (spells[spell_id].classes[x] < 255) other = true; } } - if(sk && !other) - { - return(spells[spell_id].base[effect_index] + 1); - } - else{ - return(spells[spell_id].base[effect_index]); - } + if (sk && !other) + return spells[spell_id].base[effect_index] + 1; + else + return spells[spell_id].base[effect_index]; } bool Mob::TryDivineSave() diff --git a/zone/string_ids.h b/zone/string_ids.h index 056513874..a443fabd9 100644 --- a/zone/string_ids.h +++ b/zone/string_ids.h @@ -249,6 +249,8 @@ #define PLAYER_CHARMED 1461 //You lose control of yourself! #define TRADER_BUSY 1468 //That Trader is currently with a customer. Please wait until their transaction is finished. #define SENSE_CORPSE_DIRECTION 1563 //You sense a corpse in this direction. +#define QUEUED_TELL 2458 //[queued] +#define QUEUE_TELL_FULL 2459 //[zoing and queue is full] #define SUSPEND_MINION_UNSUSPEND 3267 //%1 tells you, 'I live again...' #define SUSPEND_MINION_SUSPEND 3268 //%1 tells you, 'By your command, master.' #define ONLY_SUMMONED_PETS 3269 //3269 This effect only works with summoned pets. @@ -269,6 +271,8 @@ #define CORPSEDRAG_STOP 4066 //You stop dragging the corpse. #define TARGET_TOO_CLOSE 4602 //You are too close to your target. Get farther away. #define WHOALL_NO_RESULTS 5029 //There are no players in EverQuest that match those who filters. +#define TELL_QUEUED_MESSAGE 5045 //You told %1 '%T2. %3' +#define TOLD_NOT_ONLINE 5046 //%1 is not online at this time. #define PETITION_NO_DELETE 5053 //You do not have a petition in the queue. #define PETITION_DELETED 5054 //Your petition was successfully deleted. #define GAIN_RAIDEXP 5085 //You gained raid experience! diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 92d34ff71..a1100b7cb 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -168,18 +168,24 @@ void WorldServer::Process() { break; } case ServerOP_ChannelMessage: { - if (!ZoneLoaded) break; + if (!ZoneLoaded) + break; ServerChannelMessage_Struct* scm = (ServerChannelMessage_Struct*) pack->pBuffer; if (scm->deliverto[0] == 0) { entity_list.ChannelMessageFromWorld(scm->from, scm->to, scm->chan_num, scm->guilddbid, scm->language, scm->message); - } - else { - Client* client; - client = entity_list.GetClientByName(scm->deliverto); - if (client != 0) { + } else { + Client* client = entity_list.GetClientByName(scm->deliverto); + if (client) { if (client->Connected()) { - client->ChannelMessageSend(scm->from, scm->to, scm->chan_num, scm->language, scm->message); - if (!scm->noreply && scm->chan_num!=2) { //dont echo on group chat + if (scm->queued == 1) // tell was queued + client->Tell_StringID(QUEUED_TELL, scm->to, scm->message); + else if (scm->queued == 2) // tell queue was full + client->Tell_StringID(QUEUE_TELL_FULL, scm->to, scm->message); + else if (scm->queued == 3) // person was offline + client->Message_StringID(MT_TellEcho, TOLD_NOT_ONLINE, scm->to); + else // normal stuff + client->ChannelMessageSend(scm->from, scm->to, scm->chan_num, scm->language, scm->message); + if (!scm->noreply && scm->chan_num != 2) { //dont echo on group chat // if it's a tell, echo back so it shows up scm->noreply = true; scm->chan_num = 14; @@ -1874,6 +1880,7 @@ bool WorldServer::SendChannelMessage(Client* from, const char* to, uint8 chan_nu scm->chan_num = chan_num; scm->guilddbid = guilddbid; scm->language = language; + scm->queued = 0; strcpy(scm->message, buffer); pack->Deflate(); @@ -2198,3 +2205,19 @@ void WorldServer::HandleLFPMatches(ServerPacket *pack) { safe_delete(outapp); } } + +void WorldServer::RequestTellQueue(const char *who) +{ + if (!who) + return; + + ServerPacket* pack = new ServerPacket(ServerOP_RequestTellQueue, sizeof(ServerRequestTellQueue_Struct)); + ServerRequestTellQueue_Struct* rtq = (ServerRequestTellQueue_Struct*) pack->pBuffer; + + strn0cpy(rtq->name, who, sizeof(rtq->name)); + + SendPacket(pack); + safe_delete(pack); + return; +} + diff --git a/zone/worldserver.h b/zone/worldserver.h index 3c2e03fa6..6feead496 100644 --- a/zone/worldserver.h +++ b/zone/worldserver.h @@ -57,6 +57,8 @@ public: void HandleLFGMatches(ServerPacket *pack); void HandleLFPMatches(ServerPacket *pack); + void RequestTellQueue(const char *who); + private: virtual void OnConnected();