diff --git a/changelog.txt b/changelog.txt index 082463fe9..9d83c5974 100644 --- a/changelog.txt +++ b/changelog.txt @@ -3,6 +3,51 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) == 08/24/2014 == Uleat: Fix (attempted) for zone crashes related to zone shut-down. This change disables all Mob AI and disables/deletes all Mob timers once Zone::ShutDown() is called. More areas will be addressed as reports come in. Note: Perl and Lua quests tested to work..please post any aberrant behavior. (I finally set my spell-check to US English...) +Akkadius: Character creation process crash fix and query cleanup +Akkadius: Created `character_lookup` table for applications that mirrors all `character_` table fields minus blob fields for application lookups + - A 2.4GB character_ table will take 7 seconds to query on a SSD versus .1s on the character_lookup table + - This also causes applications like Magelo to burst reads of the entire character table because of the blob fields that come with the reads, as much as 500-600MB/s even if a indexed id filter is provided + - This field is synchronized on player save and has 0.001s DB hit + - When we split out from the blob, ideally this table can be removed + - Required SQL: utils\sql\git\required\2014_08_24_character_lookup.sql + +== 08/23/2014 == +Akkadius: Changed zone process window title format, example: 'crushbone :: clients: 6 inst_id: 1 inst_ver: 0 :: port: 7015' +Akkadius: Most of the following changes are QueryServ related, fully implemented its original functionality to be able to offload + intensive or metric based logging to a remote server process that could exist on another server entirely +Akkadius: Implemented Player Event Logging Types (Go to table `qs_player_events`): + 1 = Player_Log_Quest, + 2 = Player_Log_Zoning, + 3 = Player_Log_Deaths, + 4 = Player_Log_Connect_State, + 5 = Player_Log_Levels, + 6 = Player_Log_Keyring_Addition, + 7 = Player_Log_QGlobal_Update, + 8 = Player_Log_Task_Updates, + 9 = Player_Log_AA_Purchases, + 10 = Player_Log_Trade_Skill_Events, + 11 = Player_Log_Issued_Commands, + 12 = Player_Log_Money_Transactions, + 13 = Player_Log_Alternate_Currency_Transactions, + - All QueryServ logging will be implemented with a front end in EoC 2.0 very soon + - Architecture page: http://wiki.eqemulator.org/p?QueryServ_Architecture +Akkadius: Changed all QS Error related logging to 'QUERYSERV__ERROR' +Akkadius: (Natedog) (Crash Fix) Legacy MySQL bug revert for loading AA's COALESCE( from COALESCE ( +Akkadius: Implemented Perl Quest objects (LUA still needed to be exported): + - quest::qs_send_query("MySQL query") - Will send a raw query to the QueryServ process, useful for custom logging + - quest::qs_player_event(char_id, event_desc); - Will process a quest type event to table `qs_player_events` +Akkadius: Added MySQL Tables + - `qs_player_aa_rate_hourly` + - `qs_player_events` + - Source table structures from: + - utils\sql\git\queryserv\required\08_23_2014_player_events_and_player_aa_rate_hourly + To get the complete QueryServ schema, source from here: + - utils\sql\git\queryserv\required\Complete_QueryServ_Table_Structures.sql +Akkadius: Added rules for each logging type, source rules here with them enabled by default: + - utils\sql\git\queryserv\required\Complete_QueryServ_Rules_Enabled.sql +Akkadius: Spawn related logging cleanup +Akkadius: General code cleanup +Akkadius: More to come for QueryServ == 08/20/2014 == Uleat: Rework of Trade::AddEntity() - function used to move items into the trade window. Now accepts argument for 'stack_size' and updates client properly. diff --git a/common/database.cpp b/common/database.cpp index e82f350b1..8d27d5b46 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -608,10 +608,11 @@ bool Database::StoreCharacter(uint32 account_id, PlayerProfile_Struct* pp, Inven for (int16 i=EmuConstants::EQUIPMENT_BEGIN; i<=EmuConstants::BANK_BAGS_END;) { const ItemInst* newinv = inv->GetItem(i); - if (!newinv) + if (newinv) { - invquery = StringFormat("INSERT INTO inventory SET charid=%0u, slotid=%0d, itemid=%0u, charges=%0d, color=%0u", - charid, i, newinv->GetItem()->ID,newinv->GetCharges(), newinv->GetColor()); + invquery = StringFormat("INSERT INTO `inventory` (charid, slotid, itemid, charges, color) VALUES (%u, %i, %u, %i, %u)", + charid, i, newinv->GetItem()->ID, newinv->GetCharges(), newinv->GetColor()); + auto results = QueryDatabase(invquery); if (!results.RowsAffected()) diff --git a/common/logtypes.h b/common/logtypes.h index 1776c9518..8ebd48f9a 100644 --- a/common/logtypes.h +++ b/common/logtypes.h @@ -60,17 +60,17 @@ LOG_TYPE( UCS, PACKETS, DISABLED) LOG_CATEGORY( QUERYSERV ) LOG_TYPE( QUERYSERV, INIT, ENABLED ) -LOG_TYPE( QUERYSERV, ERROR, ENABLED ) +LOG_TYPE( QUERYSERV, ERROR, ENABLED ) LOG_TYPE( QUERYSERV, CLIENT, DISABLED ) LOG_TYPE( QUERYSERV, TRACE, DISABLED ) LOG_TYPE( QUERYSERV, PACKETS, DISABLED) -LOG_CATEGORY(SOCKET_SERVER) -LOG_TYPE(SOCKET_SERVER, INIT, ENABLED) -LOG_TYPE(SOCKET_SERVER, ERROR, ENABLED) -LOG_TYPE(SOCKET_SERVER, CLIENT, DISABLED) -LOG_TYPE(SOCKET_SERVER, TRACE, DISABLED) -LOG_TYPE(SOCKET_SERVER, PACKETS, DISABLED) +LOG_CATEGORY( SOCKET_SERVER) +LOG_TYPE( SOCKET_SERVER, INIT, ENABLED) +LOG_TYPE( SOCKET_SERVER, ERROR, ENABLED) +LOG_TYPE( SOCKET_SERVER, CLIENT, DISABLED) +LOG_TYPE( SOCKET_SERVER, TRACE, DISABLED) +LOG_TYPE( SOCKET_SERVER, PACKETS, DISABLED) LOG_CATEGORY( SPAWNS ) LOG_TYPE( SPAWNS, MAIN, DISABLED ) @@ -108,6 +108,7 @@ LOG_CATEGORY( FACTION ) LOG_CATEGORY( ZONE ) LOG_TYPE( ZONE, GROUND_SPAWNS, DISABLED ) +LOG_TYPE( ZONE, SPAWNS, ENABLED) LOG_TYPE( ZONE, INIT, ENABLED ) LOG_TYPE( ZONE, INIT_ERR, ENABLED ) LOG_TYPE( ZONE, WORLD, ENABLED ) @@ -116,7 +117,7 @@ LOG_TYPE( ZONE, WORLD_TRACE, DISABLED ) LOG_CATEGORY( TASKS ) LOG_TYPE( TASKS, GLOBALLOAD, DISABLED ) -LOG_TYPE( TASKS, CLIENTLOAD, DISABLED ) +LOG_TYPE( TASKS, CLIENTLOAD, DISABLED ) LOG_TYPE( TASKS, UPDATE, DISABLED ) LOG_TYPE( TASKS, CLIENTSAVE, DISABLED ) LOG_TYPE( TASKS, PACKETS, DISABLED ) diff --git a/common/ruletypes.h b/common/ruletypes.h index 91ade257b..73d538f8a 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -558,14 +558,28 @@ RULE_INT ( Console, SessionTimeOut, 600000 ) // Amount of time in ms for the con RULE_CATEGORY_END() RULE_CATEGORY( QueryServ ) -RULE_BOOL( QueryServ, PlayerChatLogging, false) // Logs Player Chat +RULE_BOOL( QueryServ, PlayerLogChat, false) // Logs Player Chat RULE_BOOL( QueryServ, PlayerLogTrades, false) // Logs Player Trades RULE_BOOL( QueryServ, PlayerLogHandins, false) // Logs Player Handins RULE_BOOL( QueryServ, PlayerLogNPCKills, false) // Logs Player NPC Kills RULE_BOOL( QueryServ, PlayerLogDeletes, false) // Logs Player Deletes RULE_BOOL( QueryServ, PlayerLogMoves, false) // Logs Player Moves -RULE_BOOL( QueryServ, MerchantLogTransactions, false) // Logs Merchant Transactions +RULE_BOOL( QueryServ, PlayerLogMerchantTransactions, false) // Logs Merchant Transactions RULE_BOOL( QueryServ, PlayerLogPCCoordinates, false) // Logs Player Coordinates with certain events +RULE_BOOL( QueryServ, PlayerLogDropItem, false) // Logs Player Drop Item +RULE_BOOL( QueryServ, PlayerLogZone, false) // Logs Player Zone Events +RULE_BOOL( QueryServ, PlayerLogDeaths, false) // Logs Player Deaths +RULE_BOOL( QueryServ, PlayerLogConnectDisconnect, false) // Logs Player Connect Disconnect State +RULE_BOOL( QueryServ, PlayerLogLevels, false) // Logs Player Leveling/Deleveling +RULE_BOOL( QueryServ, PlayerLogAARate, false) // Logs Player AA Experience Rates +RULE_BOOL( QueryServ, PlayerLogQGlobalUpdate, false) // Logs Player QGlobal Updates +RULE_BOOL( QueryServ, PlayerLogTaskUpdates, false) // Logs Player Task Updates +RULE_BOOL( QueryServ, PlayerLogKeyringAddition, false) // Log PLayer Keyring additions +RULE_BOOL( QueryServ, PlayerLogAAPurchases, false) // Log Player AA Purchases +RULE_BOOL( QueryServ, PlayerLogTradeSkillEvents, false) // Log Player Tradeskill Transactions +RULE_BOOL( QueryServ, PlayerLogIssuedCommandes, false ) // Log Player Issued Commands +RULE_BOOL( QueryServ, PlayerLogMoneyTransactions, false) // Log Player Money Transaction/Splits +RULE_BOOL( QueryServ, PlayerLogAlternateCurrencyTransactions, false) // Log Ploayer Alternate Currency Transactions RULE_CATEGORY_END() RULE_CATEGORY( Inventory ) diff --git a/common/servertalk.h b/common/servertalk.h index d5d7c3604..6d1a83a84 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -184,9 +184,11 @@ #define ServerOP_QSPlayerLogNPCKills 0x4012 #define ServerOP_QSPlayerLogDeletes 0x4013 #define ServerOP_QSPlayerLogMoves 0x4014 -#define ServerOP_QSMerchantLogTransactions 0x4015 +#define ServerOP_QSPlayerLogMerchantTransactions 0x4015 +#define ServerOP_QSSendQuery 0x4016 -enum { QSG_LFGuild = 0 }; +/* Query Serv Generic Packet Flag/Type Enumeration */ +enum { QSG_LFGuild = 0 }; enum { QSG_LFGuild_PlayerMatches = 0, QSG_LFGuild_UpdatePlayerInfo, QSG_LFGuild_RequestPlayerInfo, QSG_LFGuild_UpdateGuildInfo, QSG_LFGuild_GuildMatches, QSG_LFGuild_RequestGuildInfo }; @@ -1219,6 +1221,10 @@ struct QSMerchantLogTransaction_Struct { QSTransactionItems_Struct items[0]; }; +struct QSGeneralQuery_Struct { + char QueryString[0]; +}; + struct CZMessagePlayer_Struct { uint32 Type; char CharName[64]; diff --git a/queryserv/database.cpp b/queryserv/database.cpp index d03957140..4b94f215b 100644 --- a/queryserv/database.cpp +++ b/queryserv/database.cpp @@ -23,12 +23,13 @@ #include #include #include -#include +#include #include #include #include #include #include +#include // Disgrace: for windows compile #ifdef _WINDOWS @@ -95,42 +96,7 @@ Close the connection to the database Database::~Database() { } - -bool Database::GetVariable(const char* varname, char* varvalue, uint16 varvalue_len) { - - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - if (!RunQuery(query,MakeAnyLenString(&query, "select `value` from `variables` where `varname`='%s'", varname), errbuf, &result)) { - - _log(UCS__ERROR, "Unable to get message count from database. %s %s", query, errbuf); - - safe_delete_array(query); - - return false; - } - - safe_delete_array(query); - - if (mysql_num_rows(result) != 1) { - - mysql_free_result(result); - - return false; - } - - row = mysql_fetch_row(result); - - snprintf(varvalue, varvalue_len, "%s", row[0]); - - mysql_free_result(result); - - return true; -} - - + void Database::AddSpeech(const char* from, const char* to, const char* message, uint16 minstatus, uint32 guilddbid, uint8 type) { char errbuf[MYSQL_ERRMSG_SIZE]; char* query = 0; @@ -143,8 +109,8 @@ void Database::AddSpeech(const char* from, const char* to, const char* message, DoEscapeString(S3, message, strlen(message)); if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_player_speech` SET `from`='%s', `to`='%s', `message`='%s', `minstatus`='%i', `guilddbid`='%i', `type`='%i'", S1, S2, S3, minstatus, guilddbid, type), errbuf, 0, 0)) { - _log(NET__WORLD, "Failed Speech Entry Insert: %s", errbuf); - _log(NET__WORLD, "%s", query); + _log(QUERYSERV__ERROR, "Failed Speech Entry Insert: %s", errbuf); + _log(QUERYSERV__ERROR, "%s", query); } safe_delete_array(query); @@ -164,8 +130,8 @@ void Database::LogPlayerTrade(QSPlayerLogTrade_Struct* QS, uint32 Items) { QS->char1_id, QS->char1_money.platinum, QS->char1_money.gold, QS->char1_money.silver, QS->char1_money.copper, QS->char1_count, QS->char2_id, QS->char2_money.platinum, QS->char2_money.gold, QS->char2_money.silver, QS->char2_money.copper, QS->char2_count), errbuf, 0, 0, &lastid)) { - _log(NET__WORLD, "Failed Trade Log Record Insert: %s", errbuf); - _log(NET__WORLD, "%s", query); + _log(QUERYSERV__ERROR, "Failed Trade Log Record Insert: %s", errbuf); + _log(QUERYSERV__ERROR, "%s", query); } if(Items > 0) { @@ -176,15 +142,14 @@ void Database::LogPlayerTrade(QSPlayerLogTrade_Struct* QS, uint32 Items) { lastid, QS->items[i].from_id, QS->items[i].from_slot, QS->items[i].to_id, QS->items[i].to_slot, QS->items[i].item_id, QS->items[i].charges, QS->items[i].aug_1, QS->items[i].aug_2, QS->items[i].aug_3, QS->items[i].aug_4, QS->items[i].aug_5, errbuf, 0, 0))) { - _log(NET__WORLD, "Failed Trade Log Record Entry Insert: %s", errbuf); - _log(NET__WORLD, "%s", query); + _log(QUERYSERV__ERROR, "Failed Trade Log Record Entry Insert: %s", errbuf); + _log(QUERYSERV__ERROR, "%s", query); } } } } -void Database::LogPlayerHandin(QSPlayerLogHandin_Struct* QS, uint32 Items) { - +void Database::LogPlayerHandin(QSPlayerLogHandin_Struct* QS, uint32 Items) { char errbuf[MYSQL_ERRMSG_SIZE]; char* query = 0; uint32 lastid = 0; @@ -194,8 +159,8 @@ void Database::LogPlayerHandin(QSPlayerLogHandin_Struct* QS, uint32 Items) { QS->quest_id, QS->char_id, QS->char_money.platinum, QS->char_money.gold, QS->char_money.silver, QS->char_money.copper, QS->char_count, QS->npc_id, QS->npc_money.platinum, QS->npc_money.gold, QS->npc_money.silver, QS->npc_money.copper, QS->npc_count), errbuf, 0, 0, &lastid)) { - _log(NET__WORLD, "Failed Handin Log Record Insert: %s", errbuf); - _log(NET__WORLD, "%s", query); + _log(QUERYSERV__ERROR, "Failed Handin Log Record Insert: %s", errbuf); + _log(QUERYSERV__ERROR, "%s", query); } if(Items > 0) { @@ -206,8 +171,8 @@ void Database::LogPlayerHandin(QSPlayerLogHandin_Struct* QS, uint32 Items) { lastid, QS->items[i].action_type, QS->items[i].char_slot, QS->items[i].item_id, QS->items[i].charges, QS->items[i].aug_1, QS->items[i].aug_2, QS->items[i].aug_3, QS->items[i].aug_4, QS->items[i].aug_5, errbuf, 0, 0))) { - _log(NET__WORLD, "Failed Handin Log Record Entry Insert: %s", errbuf); - _log(NET__WORLD, "%s", query); + _log(QUERYSERV__ERROR, "Failed Handin Log Record Entry Insert: %s", errbuf); + _log(QUERYSERV__ERROR, "%s", query); } } } @@ -218,15 +183,15 @@ void Database::LogPlayerNPCKill(QSPlayerLogNPCKill_Struct* QS, uint32 Members){ char* query = 0; uint32 lastid = 0; if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_player_npc_kill_record` SET `npc_id`='%i', `type`='%i', `zone_id`='%i', `time`=NOW()", QS->s1.NPCID, QS->s1.Type, QS->s1.ZoneID), errbuf, 0, 0, &lastid)) { - _log(NET__WORLD, "Failed NPC Kill Log Record Insert: %s", errbuf); - _log(NET__WORLD, "%s", query); + _log(QUERYSERV__ERROR, "Failed NPC Kill Log Record Insert: %s", errbuf); + _log(QUERYSERV__ERROR, "%s", query); } if(Members > 0){ for (int i = 0; i < Members; i++) { if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_player_npc_kill_record_entries` SET `event_id`='%i', `char_id`='%i'", lastid, QS->Chars[i].char_id, errbuf, 0, 0))) { - _log(NET__WORLD, "Failed NPC Kill Log Entry Insert: %s", errbuf); - _log(NET__WORLD, "%s", query); + _log(QUERYSERV__ERROR, "Failed NPC Kill Log Entry Insert: %s", errbuf); + _log(QUERYSERV__ERROR, "%s", query); } } } @@ -241,8 +206,8 @@ void Database::LogPlayerDelete(QSPlayerLogDelete_Struct* QS, uint32 Items) { "`char_id`='%i', `stack_size`='%i', `char_items`='%i'", QS->char_id, QS->stack_size, QS->char_count, QS->char_count), errbuf, 0, 0, &lastid)) { - _log(NET__WORLD, "Failed Delete Log Record Insert: %s", errbuf); - _log(NET__WORLD, "%s", query); + _log(QUERYSERV__ERROR, "Failed Delete Log Record Insert: %s", errbuf); + _log(QUERYSERV__ERROR, "%s", query); } if(Items > 0) { @@ -253,15 +218,15 @@ void Database::LogPlayerDelete(QSPlayerLogDelete_Struct* QS, uint32 Items) { lastid, QS->items[i].char_slot, QS->items[i].item_id, QS->items[i].charges, QS->items[i].aug_1, QS->items[i].aug_2, QS->items[i].aug_3, QS->items[i].aug_4, QS->items[i].aug_5, errbuf, 0, 0))) { - _log(NET__WORLD, "Failed Delete Log Record Entry Insert: %s", errbuf); - _log(NET__WORLD, "%s", query); + _log(QUERYSERV__ERROR, "Failed Delete Log Record Entry Insert: %s", errbuf); + _log(QUERYSERV__ERROR, "%s", query); } } } } -void Database::LogPlayerMove(QSPlayerLogMove_Struct* QS, uint32 Items) { - +void Database::LogPlayerMove(QSPlayerLogMove_Struct* QS, uint32 Items) { + /* These are item moves */ char errbuf[MYSQL_ERRMSG_SIZE]; char* query = 0; uint32 lastid = 0; @@ -269,10 +234,9 @@ void Database::LogPlayerMove(QSPlayerLogMove_Struct* QS, uint32 Items) { "`char_id`='%i', `from_slot`='%i', `to_slot`='%i', `stack_size`='%i', `char_items`='%i', `postaction`='%i'", QS->char_id, QS->from_slot, QS->to_slot, QS->stack_size, QS->char_count, QS->postaction), errbuf, 0, 0, &lastid)) { - _log(NET__WORLD, "Failed Move Log Record Insert: %s", errbuf); - _log(NET__WORLD, "%s", query); - } - + _log(QUERYSERV__ERROR, "Failed Move Log Record Insert: %s", errbuf); + _log(QUERYSERV__ERROR, "%s", query); + } if(Items > 0) { for(int i = 0; i < Items; i++) { if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_player_move_record_entries` SET `event_id`='%i', " @@ -281,16 +245,15 @@ void Database::LogPlayerMove(QSPlayerLogMove_Struct* QS, uint32 Items) { QS->items[i].from_slot, QS->items[i].to_slot, QS->items[i].item_id, QS->items[i].charges, QS->items[i].aug_1, QS->items[i].aug_2, QS->items[i].aug_3, QS->items[i].aug_4, QS->items[i].aug_5, errbuf, 0, 0))) { - _log(NET__WORLD, "Failed Move Log Record Entry Insert: %s", errbuf); - _log(NET__WORLD, "%s", query); + _log(QUERYSERV__ERROR, "Failed Move Log Record Entry Insert: %s", errbuf); + _log(QUERYSERV__ERROR, "%s", query); } } } } void Database::LogMerchantTransaction(QSMerchantLogTransaction_Struct* QS, uint32 Items) { - // Merchant transactions are from the perspective of the merchant, not the player -U - + /* Merchant transactions are from the perspective of the merchant, not the player -U */ char errbuf[MYSQL_ERRMSG_SIZE]; char* query = 0; uint32 lastid = 0; @@ -300,8 +263,8 @@ void Database::LogMerchantTransaction(QSMerchantLogTransaction_Struct* QS, uint3 QS->zone_id, QS->merchant_id, QS->merchant_money.platinum, QS->merchant_money.gold, QS->merchant_money.silver, QS->merchant_money.copper, QS->merchant_count, QS->char_id, QS->char_money.platinum, QS->char_money.gold, QS->char_money.silver, QS->char_money.copper, QS->char_count), errbuf, 0, 0, &lastid)) { - _log(NET__WORLD, "Failed Transaction Log Record Insert: %s", errbuf); - _log(NET__WORLD, "%s", query); + _log(QUERYSERV__ERROR, "Failed Transaction Log Record Insert: %s", errbuf); + _log(QUERYSERV__ERROR, "%s", query); } if(Items > 0) { @@ -312,10 +275,29 @@ void Database::LogMerchantTransaction(QSMerchantLogTransaction_Struct* QS, uint3 lastid, QS->items[i].char_slot, QS->items[i].item_id, QS->items[i].charges, QS->items[i].aug_1, QS->items[i].aug_2, QS->items[i].aug_3, QS->items[i].aug_4, QS->items[i].aug_5, errbuf, 0, 0))) { - _log(NET__WORLD, "Failed Transaction Log Record Entry Insert: %s", errbuf); - _log(NET__WORLD, "%s", query); + _log(QUERYSERV__ERROR, "Failed Transaction Log Record Entry Insert: %s", errbuf); + _log(QUERYSERV__ERROR, "%s", query); } } } + safe_delete_array(query); } +void Database::GeneralQueryReceive(ServerPacket *pack) { + /* + These are general queries passed from anywhere in zone instead of packing structures and breaking them down again and again + */ + char *Query = nullptr; + Query = new char[pack->ReadUInt32() + 1]; + pack->ReadString(Query); + char errbuf[MYSQL_ERRMSG_SIZE]; + char* query = 0; + uint32 lastid = 0; + if (!RunQuery(query, MakeAnyLenString(&query, Query), errbuf, 0, 0, &lastid)) { + _log(QUERYSERV__ERROR, "Failed Delete Log Record Insert: %s", errbuf); + _log(QUERYSERV__ERROR, "%s", query); + } + safe_delete_array(query); + safe_delete(pack); + safe_delete(Query); +} diff --git a/queryserv/database.h b/queryserv/database.h index 61664f90d..ac002a0b5 100644 --- a/queryserv/database.h +++ b/queryserv/database.h @@ -42,7 +42,6 @@ public: bool Connect(const char* host, const char* user, const char* passwd, const char* database,uint32 port); ~Database(); - bool GetVariable(const char* varname, char* varvalue, uint16 varvalue_len); void AddSpeech(const char* from, const char* to, const char* message, uint16 minstatus, uint32 guilddbid, uint8 type); void LogPlayerTrade(QSPlayerLogTrade_Struct* QS, uint32 Items); void LogPlayerHandin(QSPlayerLogHandin_Struct* QS, uint32 Items); @@ -50,6 +49,7 @@ public: void LogPlayerDelete(QSPlayerLogDelete_Struct* QS, uint32 Items); void LogPlayerMove(QSPlayerLogMove_Struct* QS, uint32 Items); void LogMerchantTransaction(QSMerchantLogTransaction_Struct* QS, uint32 Items); + void GeneralQueryReceive(ServerPacket *pack); protected: void HandleMysqlError(uint32 errnum); private: diff --git a/queryserv/lfguild.cpp b/queryserv/lfguild.cpp index 9fc4d0b62..c135d4991 100644 --- a/queryserv/lfguild.cpp +++ b/queryserv/lfguild.cpp @@ -22,7 +22,7 @@ PlayerLookingForGuild::PlayerLookingForGuild(char *Name, char *Comments, uint32 GuildLookingForPlayers::GuildLookingForPlayers(char *Name, char *Comments, uint32 FromLevel, uint32 ToLevel, uint32 Classes, uint32 AACount, uint32 Timezone, uint32 TimePosted) { - this->Name = Name; + this->Name = Name; this->Comments = Comments; this->FromLevel = FromLevel; this->ToLevel = ToLevel; diff --git a/queryserv/queryserv.cpp b/queryserv/queryserv.cpp index 3827ceea1..22ddb87ee 100644 --- a/queryserv/queryserv.cpp +++ b/queryserv/queryserv.cpp @@ -33,56 +33,47 @@ volatile bool RunLoops = true; -uint32 MailMessagesSent = 0; -uint32 ChatMessagesSent = 0; - TimeoutManager timeout_manager; - Database database; LFGuildManager lfguildmanager; std::string WorldShortName; - const queryservconfig *Config; - WorldServer *worldserver = 0; - -void CatchSignal(int sig_num) { - - RunLoops = false; - +void CatchSignal(int sig_num) { + RunLoops = false; if(worldserver) worldserver->Disconnect(); } int main() { RegisterExecutablePlatform(ExePlatformQueryServ); - set_exception_handler(); - - Timer LFGuildExpireTimer(60000); - + set_exception_handler(); + Timer LFGuildExpireTimer(60000); Timer InterserverTimer(INTERSERVER_TIMER); // does auto-reconnect + /* Load XML from eqemu_config.xml + + 127.0.0.1 + 3306 + user + password + dbname + + */ + _log(QUERYSERV__INIT, "Starting EQEmu QueryServ."); - if (!queryservconfig::LoadConfig()) { - _log(QUERYSERV__INIT, "Loading server configuration failed."); - return 1; } - Config = queryservconfig::get(); - - if(!load_log_settings(Config->LogSettingsFile.c_str())) - _log(QUERYSERV__INIT, "Warning: Unable to read %s", Config->LogSettingsFile.c_str()); - else - _log(QUERYSERV__INIT, "Log settings loaded from %s", Config->LogSettingsFile.c_str()); - - WorldShortName = Config->ShortName; + Config = queryservconfig::get(); + WorldShortName = Config->ShortName; _log(QUERYSERV__INIT, "Connecting to MySQL..."); - + + /* MySQL Connection */ if (!database.Connect( Config->QSDatabaseHost.c_str(), Config->QSDatabaseUsername.c_str(), @@ -93,6 +84,12 @@ int main() { return 1; } + /* Initialize Logging */ + if (!load_log_settings(Config->LogSettingsFile.c_str())) + _log(QUERYSERV__INIT, "Warning: Unable to read %s", Config->LogSettingsFile.c_str()); + else + _log(QUERYSERV__INIT, "Log settings loaded from %s", Config->LogSettingsFile.c_str()); + if (signal(SIGINT, CatchSignal) == SIG_ERR) { _log(QUERYSERV__ERROR, "Could not set signal handler"); return 1; @@ -102,16 +99,15 @@ int main() { return 1; } + /* Initial Connection to Worldserver */ worldserver = new WorldServer; + worldserver->Connect(); - worldserver->Connect(); - + /* Load Looking For Guild Manager */ lfguildmanager.LoadDatabase(); - while(RunLoops) { - - Timer::SetCurrentTime(); - + while(RunLoops) { + Timer::SetCurrentTime(); if(LFGuildExpireTimer.Check()) lfguildmanager.ExpireEntries(); @@ -119,10 +115,8 @@ int main() { if (worldserver->TryReconnect() && (!worldserver->Connected())) worldserver->AsyncConnect(); } - worldserver->Process(); - - timeout_manager.CheckTimeouts(); - + worldserver->Process(); + timeout_manager.CheckTimeouts(); Sleep(100); } } diff --git a/queryserv/worldserver.cpp b/queryserv/worldserver.cpp index bb7523592..154db3a07 100644 --- a/queryserv/worldserver.cpp +++ b/queryserv/worldserver.cpp @@ -56,122 +56,109 @@ void WorldServer::OnConnected() void WorldServer::Process() { - WorldConnection::Process(); - + WorldConnection::Process(); if (!Connected()) return; - ServerPacket *pack = 0; - + ServerPacket *pack = 0; while((pack = tcpc.PopPacket())) { - _log(QUERYSERV__TRACE, "Received Opcode: %4X", pack->opcode); - - switch(pack->opcode) - { + _log(QUERYSERV__TRACE, "Received Opcode: %4X", pack->opcode); + switch(pack->opcode) { case 0: { break; } - case ServerOP_KeepAlive: - { + case ServerOP_KeepAlive: { break; } - case ServerOP_Speech: - { - Server_Speech_Struct *SSS = (Server_Speech_Struct*)pack->pBuffer; - + case ServerOP_Speech: { + Server_Speech_Struct *SSS = (Server_Speech_Struct*)pack->pBuffer; std::string tmp1 = SSS->from; - std::string tmp2 = SSS->to; - + std::string tmp2 = SSS->to; database.AddSpeech(tmp1.c_str(), tmp2.c_str(), SSS->message, SSS->minstatus, SSS->guilddbid, SSS->type); break; } - case ServerOP_QSPlayerLogTrades: - { + case ServerOP_QSPlayerLogTrades: { QSPlayerLogTrade_Struct *QS = (QSPlayerLogTrade_Struct*)pack->pBuffer; uint32 Items = QS->char1_count + QS->char2_count; database.LogPlayerTrade(QS, Items); break; } - case ServerOP_QSPlayerLogHandins: - { + case ServerOP_QSPlayerLogHandins: { QSPlayerLogHandin_Struct *QS = (QSPlayerLogHandin_Struct*)pack->pBuffer; uint32 Items = QS->char_count + QS->npc_count; database.LogPlayerHandin(QS, Items); break; } - case ServerOP_QSPlayerLogNPCKills: - { + case ServerOP_QSPlayerLogNPCKills: { QSPlayerLogNPCKill_Struct *QS = (QSPlayerLogNPCKill_Struct*)pack->pBuffer; uint32 Members = pack->size - sizeof(QSPlayerLogNPCKill_Struct); if (Members > 0) Members = Members / sizeof(QSPlayerLogNPCKillsPlayers_Struct); database.LogPlayerNPCKill(QS, Members); break; } - case ServerOP_QSPlayerLogDeletes: - { + case ServerOP_QSPlayerLogDeletes: { QSPlayerLogDelete_Struct *QS = (QSPlayerLogDelete_Struct*)pack->pBuffer; uint32 Items = QS->char_count; database.LogPlayerDelete(QS, Items); break; } - case ServerOP_QSPlayerLogMoves: - { + case ServerOP_QSPlayerLogMoves: { QSPlayerLogMove_Struct *QS = (QSPlayerLogMove_Struct*)pack->pBuffer; uint32 Items = QS->char_count; database.LogPlayerMove(QS, Items); break; } - case ServerOP_QSMerchantLogTransactions: - { + case ServerOP_QSPlayerLogMerchantTransactions: { QSMerchantLogTransaction_Struct *QS = (QSMerchantLogTransaction_Struct*)pack->pBuffer; uint32 Items = QS->char_count + QS->merchant_count; database.LogMerchantTransaction(QS, Items); - break; + break; } - case ServerOP_QueryServGeneric: - { - // The purpose of ServerOP_QueryServerGeneric is so that we don't have to add code to world just to relay packets - // each time we add functionality to queryserv. - // - // A ServerOP_QueryServGeneric packet has the following format: - // - // uint32 SourceZoneID - // uint32 SourceInstanceID - // char OriginatingCharacterName[0] // Null terminated name of the character this packet came from. This could be just - // // an empty string if it has no meaning in the context of a particular packet. - // uint32 Type - // - // The 'Type' field is a 'sub-opcode'. A value of 0 is used for the LFGuild packets. The next feature to be added - // to queryserv would use 1, etc. - // - // Obviously, any fields in the packet following the 'Type' will be unique to the particular type of packet. The - // 'Generic' in the name of this ServerOP code relates to the four header fields. + case ServerOP_QueryServGeneric: { + /* + The purpose of ServerOP_QueryServerGeneric is so that we don't have to add code to world just to relay packets + each time we add functionality to queryserv. + + A ServerOP_QueryServGeneric packet has the following format: + + uint32 SourceZoneID + uint32 SourceInstanceID + char OriginatingCharacterName[0] + - Null terminated name of the character this packet came from. This could be just + - an empty string if it has no meaning in the context of a particular packet. + uint32 Type + + The 'Type' field is a 'sub-opcode'. A value of 0 is used for the LFGuild packets. The next feature to be added + to queryserv would use 1, etc. + + Obviously, any fields in the packet following the 'Type' will be unique to the particular type of packet. The + 'Generic' in the name of this ServerOP code relates to the four header fields. + */ + char From[64]; pack->SetReadPosition(8); pack->ReadString(From); uint32 Type = pack->ReadUInt32(); - switch(Type) - { - case QSG_LFGuild: - { - lfguildmanager.HandlePacket(pack); + switch(Type) { + case QSG_LFGuild:{ + lfguildmanager.HandlePacket(pack); break; } - default: _log(QUERYSERV__ERROR, "Received unhandled ServerOP_QueryServGeneric", Type); break; } - break; } - - + case ServerOP_QSSendQuery: { + /* Process all packets here */ + database.GeneralQueryReceive(pack); + break; + } } - } - + } safe_delete(pack); return; } diff --git a/utils/sql/git/queryserv/required/08_23_2014_player_events_and_player_aa_rate_hourly.sql b/utils/sql/git/queryserv/required/08_23_2014_player_events_and_player_aa_rate_hourly.sql new file mode 100644 index 000000000..2d5cbfa17 --- /dev/null +++ b/utils/sql/git/queryserv/required/08_23_2014_player_events_and_player_aa_rate_hourly.sql @@ -0,0 +1,23 @@ +-- ---------------------------- +-- Table structure for qs_player_events +-- ---------------------------- +DROP TABLE IF EXISTS `qs_player_events`; +CREATE TABLE `qs_player_events` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `char_id` int(11) DEFAULT '0', + `event` int(11) unsigned DEFAULT '0', + `event_desc` varchar(255) DEFAULT NULL, + `time` int(11) unsigned DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1; + +-- ---------------------------- +-- Table structure for qs_player_aa_rate_hourly +-- ---------------------------- +DROP TABLE IF EXISTS `qs_player_aa_rate_hourly`; +CREATE TABLE `qs_player_aa_rate_hourly` ( + `char_id` int(11) NOT NULL DEFAULT '0', + `hour_time` int(11) NOT NULL, + `aa_count` varchar(11) DEFAULT NULL, + PRIMARY KEY (`char_id`,`hour_time`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; \ No newline at end of file diff --git a/utils/sql/git/queryserv/required/Complete_QueryServ_Rules_Disabled.sql b/utils/sql/git/queryserv/required/Complete_QueryServ_Rules_Disabled.sql new file mode 100644 index 000000000..65206ce4c --- /dev/null +++ b/utils/sql/git/queryserv/required/Complete_QueryServ_Rules_Disabled.sql @@ -0,0 +1,45 @@ +-- Disable Player Logging for All -- +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogPCCoordinates', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogNPCKills', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogTrades', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogMerchantTransactions', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogDeletes', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogHandins', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogMoves', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogChat', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogKeyringAddition', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogAAPurchases', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogIssuedCommandes', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogMoneyTransactions', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogAlternateCurrencyTransactions', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogTradeSkillEvents', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogPCCoordinates', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogDropItem', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogMerchantTransactions', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogDeletes', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogHandins', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogMoves', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogNPCKills', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogTrades', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogQGlobalUpdate', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogTaskUpdates', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogDeaths', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogZone', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogConnectDisconnect', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogLevels', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogAARate', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogChat', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogDropItem', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogZone', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogDeaths', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogLevels', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogEXPRate', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogAARate', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogQGlobalUpdate', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogTaskUpdates', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogKeyringAddition', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogAAPurchases', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogTradeSkillEvents', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogIssuedCommandes', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogMoneyTransactions', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogAlternateCurrencyTransactions', 'false', ''); diff --git a/utils/sql/git/queryserv/required/Complete_QueryServ_Rules_Enabled.sql b/utils/sql/git/queryserv/required/Complete_QueryServ_Rules_Enabled.sql new file mode 100644 index 000000000..9426acc8f --- /dev/null +++ b/utils/sql/git/queryserv/required/Complete_QueryServ_Rules_Enabled.sql @@ -0,0 +1,45 @@ +-- Enable Player Logging for All -- +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogPCCoordinates', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogNPCKills', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogTrades', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogMerchantTransactions', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogDeletes', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogHandins', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogMoves', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogChat', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogKeyringAddition', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogAAPurchases', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogIssuedCommandes', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogMoneyTransactions', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogAlternateCurrencyTransactions', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogTradeSkillEvents', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogPCCoordinates', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogDropItem', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogMerchantTransactions', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogDeletes', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogHandins', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogMoves', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogNPCKills', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogTrades', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogQGlobalUpdate', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogTaskUpdates', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogDeaths', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogZone', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogConnectDisconnect', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogLevels', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogAARate', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogChat', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogDropItem', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogZone', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogDeaths', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogLevels', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogEXPRate', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogAARate', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogQGlobalUpdate', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogTaskUpdates', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogKeyringAddition', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogAAPurchases', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogTradeSkillEvents', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogIssuedCommandes', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogMoneyTransactions', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogAlternateCurrencyTransactions', 'true', ''); diff --git a/utils/sql/git/queryserv/required/Complete_QueryServ_Table_Structures.sql b/utils/sql/git/queryserv/required/Complete_QueryServ_Table_Structures.sql new file mode 100644 index 000000000..40dc41377 --- /dev/null +++ b/utils/sql/git/queryserv/required/Complete_QueryServ_Table_Structures.sql @@ -0,0 +1,247 @@ +-- QS Table Structures -- + +SET FOREIGN_KEY_CHECKS=0; + +-- ---------------------------- +-- Table structure for qs_merchant_transaction_record +-- ---------------------------- +DROP TABLE IF EXISTS `qs_merchant_transaction_record`; +CREATE TABLE `qs_merchant_transaction_record` ( + `transaction_id` int(11) NOT NULL AUTO_INCREMENT, + `time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP, + `zone_id` int(11) DEFAULT '0', + `merchant_id` int(11) DEFAULT '0', + `merchant_pp` int(11) DEFAULT '0', + `merchant_gp` int(11) DEFAULT '0', + `merchant_sp` int(11) DEFAULT '0', + `merchant_cp` int(11) DEFAULT '0', + `merchant_items` mediumint(7) DEFAULT '0', + `char_id` int(11) DEFAULT '0', + `char_pp` int(11) DEFAULT '0', + `char_gp` int(11) DEFAULT '0', + `char_sp` int(11) DEFAULT '0', + `char_cp` int(11) DEFAULT '0', + `char_items` mediumint(7) DEFAULT '0', + PRIMARY KEY (`transaction_id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; + +-- ---------------------------- +-- Table structure for qs_merchant_transaction_record_entries +-- ---------------------------- +DROP TABLE IF EXISTS `qs_merchant_transaction_record_entries`; +CREATE TABLE `qs_merchant_transaction_record_entries` ( + `event_id` int(11) DEFAULT '0', + `char_slot` mediumint(7) DEFAULT '0', + `item_id` int(11) DEFAULT '0', + `charges` mediumint(7) DEFAULT '0', + `aug_1` int(11) DEFAULT '0', + `aug_2` int(11) DEFAULT '0', + `aug_3` int(11) DEFAULT '0', + `aug_4` int(11) DEFAULT '0', + `aug_5` int(11) DEFAULT '0' +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- ---------------------------- +-- Table structure for qs_player_aa_rate_hourly +-- ---------------------------- +DROP TABLE IF EXISTS `qs_player_aa_rate_hourly`; +CREATE TABLE `qs_player_aa_rate_hourly` ( + `char_id` int(11) NOT NULL DEFAULT '0', + `hour_time` int(11) NOT NULL, + `aa_count` varchar(11) DEFAULT NULL, + PRIMARY KEY (`char_id`,`hour_time`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +-- ---------------------------- +-- Table structure for qs_player_delete_record +-- ---------------------------- +DROP TABLE IF EXISTS `qs_player_delete_record`; +CREATE TABLE `qs_player_delete_record` ( + `delete_id` int(11) NOT NULL AUTO_INCREMENT, + `time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP, + `char_id` int(11) DEFAULT '0', + `stack_size` mediumint(7) DEFAULT '0', + `char_items` mediumint(7) DEFAULT '0', + PRIMARY KEY (`delete_id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; + +-- ---------------------------- +-- Table structure for qs_player_delete_record_entries +-- ---------------------------- +DROP TABLE IF EXISTS `qs_player_delete_record_entries`; +CREATE TABLE `qs_player_delete_record_entries` ( + `event_id` int(11) DEFAULT '0', + `char_slot` mediumint(7) DEFAULT '0', + `item_id` int(11) DEFAULT '0', + `charges` mediumint(7) DEFAULT '0', + `aug_1` int(11) DEFAULT '0', + `aug_2` int(11) DEFAULT '0', + `aug_3` int(11) DEFAULT '0', + `aug_4` int(11) DEFAULT '0', + `aug_5` int(11) DEFAULT '0' +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- ---------------------------- +-- Table structure for qs_player_events +-- ---------------------------- +DROP TABLE IF EXISTS `qs_player_events`; +CREATE TABLE `qs_player_events` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `char_id` int(11) DEFAULT '0', + `event` int(11) unsigned DEFAULT '0', + `event_desc` varchar(255) DEFAULT NULL, + `time` int(11) unsigned DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1; + +-- ---------------------------- +-- Table structure for qs_player_handin_record +-- ---------------------------- +DROP TABLE IF EXISTS `qs_player_handin_record`; +CREATE TABLE `qs_player_handin_record` ( + `handin_id` int(11) NOT NULL AUTO_INCREMENT, + `time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP, + `quest_id` int(11) DEFAULT '0', + `char_id` int(11) DEFAULT '0', + `char_pp` int(11) DEFAULT '0', + `char_gp` int(11) DEFAULT '0', + `char_sp` int(11) DEFAULT '0', + `char_cp` int(11) DEFAULT '0', + `char_items` mediumint(7) DEFAULT '0', + `npc_id` int(11) DEFAULT '0', + `npc_pp` int(11) DEFAULT '0', + `npc_gp` int(11) DEFAULT '0', + `npc_sp` int(11) DEFAULT '0', + `npc_cp` int(11) DEFAULT '0', + `npc_items` mediumint(7) DEFAULT '0', + PRIMARY KEY (`handin_id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; + +-- ---------------------------- +-- Table structure for qs_player_handin_record_entries +-- ---------------------------- +DROP TABLE IF EXISTS `qs_player_handin_record_entries`; +CREATE TABLE `qs_player_handin_record_entries` ( + `event_id` int(11) DEFAULT '0', + `action_type` char(6) DEFAULT 'action', + `char_slot` mediumint(7) DEFAULT '0', + `item_id` int(11) DEFAULT '0', + `charges` mediumint(7) DEFAULT '0', + `aug_1` int(11) DEFAULT '0', + `aug_2` int(11) DEFAULT '0', + `aug_3` int(11) DEFAULT '0', + `aug_4` int(11) DEFAULT '0', + `aug_5` int(11) DEFAULT '0' +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- ---------------------------- +-- Table structure for qs_player_move_record +-- ---------------------------- +DROP TABLE IF EXISTS `qs_player_move_record`; +CREATE TABLE `qs_player_move_record` ( + `move_id` int(11) NOT NULL AUTO_INCREMENT, + `time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP, + `char_id` int(11) DEFAULT '0', + `from_slot` mediumint(7) DEFAULT '0', + `to_slot` mediumint(7) DEFAULT '0', + `stack_size` mediumint(7) DEFAULT '0', + `char_items` mediumint(7) DEFAULT '0', + `postaction` tinyint(1) DEFAULT '0', + PRIMARY KEY (`move_id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; + +-- ---------------------------- +-- Table structure for qs_player_move_record_entries +-- ---------------------------- +DROP TABLE IF EXISTS `qs_player_move_record_entries`; +CREATE TABLE `qs_player_move_record_entries` ( + `event_id` int(11) DEFAULT '0', + `from_slot` mediumint(7) DEFAULT '0', + `to_slot` mediumint(7) DEFAULT '0', + `item_id` int(11) DEFAULT '0', + `charges` mediumint(7) DEFAULT '0', + `aug_1` int(11) DEFAULT '0', + `aug_2` int(11) DEFAULT '0', + `aug_3` int(11) DEFAULT '0', + `aug_4` int(11) DEFAULT '0', + `aug_5` int(11) DEFAULT '0' +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- ---------------------------- +-- Table structure for qs_player_npc_kill_record +-- ---------------------------- +DROP TABLE IF EXISTS `qs_player_npc_kill_record`; +CREATE TABLE `qs_player_npc_kill_record` ( + `fight_id` int(11) NOT NULL AUTO_INCREMENT, + `npc_id` int(11) DEFAULT NULL, + `type` int(11) DEFAULT NULL, + `zone_id` int(11) DEFAULT NULL, + `time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`fight_id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; + +-- ---------------------------- +-- Table structure for qs_player_npc_kill_record_entries +-- ---------------------------- +DROP TABLE IF EXISTS `qs_player_npc_kill_record_entries`; +CREATE TABLE `qs_player_npc_kill_record_entries` ( + `event_id` int(11) DEFAULT '0', + `char_id` int(11) DEFAULT '0' +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- ---------------------------- +-- Table structure for qs_player_speech +-- ---------------------------- +DROP TABLE IF EXISTS `qs_player_speech`; +CREATE TABLE `qs_player_speech` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `from` varchar(64) NOT NULL, + `to` varchar(64) NOT NULL, + `message` varchar(256) NOT NULL, + `minstatus` smallint(5) NOT NULL, + `guilddbid` int(11) NOT NULL, + `type` tinyint(3) NOT NULL, + `timerecorded` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; + +-- ---------------------------- +-- Table structure for qs_player_trade_record +-- ---------------------------- +DROP TABLE IF EXISTS `qs_player_trade_record`; +CREATE TABLE `qs_player_trade_record` ( + `trade_id` int(11) NOT NULL AUTO_INCREMENT, + `time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP, + `char1_id` int(11) DEFAULT '0', + `char1_pp` int(11) DEFAULT '0', + `char1_gp` int(11) DEFAULT '0', + `char1_sp` int(11) DEFAULT '0', + `char1_cp` int(11) DEFAULT '0', + `char1_items` mediumint(7) DEFAULT '0', + `char2_id` int(11) DEFAULT '0', + `char2_pp` int(11) DEFAULT '0', + `char2_gp` int(11) DEFAULT '0', + `char2_sp` int(11) DEFAULT '0', + `char2_cp` int(11) DEFAULT '0', + `char2_items` mediumint(7) DEFAULT '0', + PRIMARY KEY (`trade_id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; + +-- ---------------------------- +-- Table structure for qs_player_trade_record_entries +-- ---------------------------- +DROP TABLE IF EXISTS `qs_player_trade_record_entries`; +CREATE TABLE `qs_player_trade_record_entries` ( + `event_id` int(11) DEFAULT '0', + `from_id` int(11) DEFAULT '0', + `from_slot` mediumint(7) DEFAULT '0', + `to_id` int(11) DEFAULT '0', + `to_slot` mediumint(7) DEFAULT '0', + `item_id` int(11) DEFAULT '0', + `charges` mediumint(7) DEFAULT '0', + `aug_1` int(11) DEFAULT '0', + `aug_2` int(11) DEFAULT '0', + `aug_3` int(11) DEFAULT '0', + `aug_4` int(11) DEFAULT '0', + `aug_5` int(11) DEFAULT '0' +) ENGINE=InnoDB DEFAULT CHARSET=utf8; diff --git a/utils/sql/git/required/2014_08_24_character_lookup.sql b/utils/sql/git/required/2014_08_24_character_lookup.sql new file mode 100644 index 000000000..414dbbdcb --- /dev/null +++ b/utils/sql/git/required/2014_08_24_character_lookup.sql @@ -0,0 +1,33 @@ +-- chracter_lookup table structure -- + +CREATE TABLE `character_lookup` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `account_id` int(11) NOT NULL DEFAULT '0', + `name` varchar(64) NOT NULL DEFAULT '', + `timelaston` int(11) unsigned DEFAULT '0', + `x` float NOT NULL DEFAULT '0', + `y` float NOT NULL DEFAULT '0', + `z` float NOT NULL DEFAULT '0', + `zonename` varchar(30) NOT NULL DEFAULT '', + `zoneid` smallint(6) NOT NULL DEFAULT '0', + `instanceid` smallint(5) unsigned NOT NULL DEFAULT '0', + `pktime` int(8) NOT NULL DEFAULT '0', + `groupid` int(10) unsigned NOT NULL DEFAULT '0', + `class` tinyint(4) NOT NULL DEFAULT '0', + `level` mediumint(8) unsigned NOT NULL DEFAULT '0', + `lfp` tinyint(1) unsigned NOT NULL DEFAULT '0', + `lfg` tinyint(1) unsigned NOT NULL DEFAULT '0', + `mailkey` char(16) NOT NULL, + `xtargets` tinyint(3) unsigned NOT NULL DEFAULT '5', + `firstlogon` tinyint(3) NOT NULL DEFAULT '0', + `inspectmessage` varchar(256) NOT NULL DEFAULT '', + PRIMARY KEY (`id`), + UNIQUE KEY `name` (`name`), + KEY `account_id` (`account_id`) +) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=latin1; + +-- Initial population of the character_lookup table -- + +INSERT INTO `character_lookup` (id, account_id, `name`, timelaston, x, y, z, zonename, zoneid, instanceid, pktime, groupid, class, `level`, lfp, lfg, mailkey, xtargets, firstlogon, inspectmessage) +SELECT id, account_id, `name`, timelaston, x, y, z, zonename, zoneid, instanceid, pktime, groupid, class, `level`, lfp, lfg, mailkey, xtargets, firstlogon, inspectmessage +FROM `character_`; \ No newline at end of file diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index a719deecf..7c4f4f963 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -1268,7 +1268,7 @@ bool ZoneServer::Process() { UCSLink.SendPacket(pack); break; } - + case ServerOP_QSSendQuery: case ServerOP_QueryServGeneric: case ServerOP_Speech: case ServerOP_QSPlayerLogTrades: @@ -1296,7 +1296,7 @@ bool ZoneServer::Process() { QSLink.SendPacket(pack); break; } - case ServerOP_QSMerchantLogTransactions: + case ServerOP_QSPlayerLogMerchantTransactions: { QSLink.SendPacket(pack); break; diff --git a/zone/CMakeLists.txt b/zone/CMakeLists.txt index 8459fb79a..0b6ebc10a 100644 --- a/zone/CMakeLists.txt +++ b/zone/CMakeLists.txt @@ -16,7 +16,7 @@ SET(zone_sources command.cpp corpse.cpp doors.cpp - effects.cpp + effects.cpp embparser.cpp embparser_api.cpp embperl.cpp @@ -93,6 +93,7 @@ SET(zone_sources petitions.cpp pets.cpp qglobals.cpp + queryserv.cpp questmgr.cpp quest_parser_collection.cpp raids.cpp @@ -184,6 +185,8 @@ SET(zone_headers pets.h qglobals.h quest_interface.h + queryserv.h + quest_interface.h questmgr.h quest_parser_collection.h raid.h diff --git a/zone/aa.cpp b/zone/aa.cpp index 1bf2ba690..2964dc6f3 100644 --- a/zone/aa.cpp +++ b/zone/aa.cpp @@ -38,6 +38,9 @@ Copyright (C) 2001-2004 EQEMu Development Team (http://eqemulator.net) #include "../common/logsys.h" #include "zonedb.h" #include "string_ids.h" +#include "queryserv.h" + +extern QueryServ* QServ; //static data arrays, really not big enough to warrant shared mem. AA_DBAction AA_Actions[aaHighestID][MAX_AA_ACTION_RANKS]; //[aaid][rank] @@ -1039,16 +1042,16 @@ void Client::BuyAA(AA_Action* action) else real_cost = aa2->cost + (aa2->cost_inc * cur_level); - if(m_pp.aapoints >= real_cost && cur_level < aa2->max_level) { - SetAA(aa2->id, cur_level+1); + if (m_pp.aapoints >= real_cost && cur_level < aa2->max_level) { + SetAA(aa2->id, cur_level + 1); - mlog(AA__MESSAGE, "Set AA %d to level %d", aa2->id, cur_level+1); + mlog(AA__MESSAGE, "Set AA %d to level %d", aa2->id, cur_level + 1); m_pp.aapoints -= real_cost; Save(); if ((RuleB(AA, Stacking) && (GetClientVersionBit() >= 4) && (aa2->hotkey_sid == 4294967295u)) - && ((aa2->max_level == (cur_level+1)) && aa2->sof_next_id)){ + && ((aa2->max_level == (cur_level + 1)) && aa2->sof_next_id)){ SendAA(aa2->id); SendAA(aa2->sof_next_id); } @@ -1059,10 +1062,28 @@ void Client::BuyAA(AA_Action* action) //we are building these messages ourself instead of using the stringID to work around patch discrepencies //these are AA_GAIN_ABILITY (410) & AA_IMPROVE (411), respectively, in both Titanium & SoF. not sure about 6.2 - if(cur_level<1) - Message(15,"You have gained the ability \"%s\" at a cost of %d ability %s.", aa2->name, real_cost, (real_cost>1)?"points":"point"); - else - Message(15,"You have improved %s %d at a cost of %d ability %s.", aa2->name, cur_level+1, real_cost, (real_cost>1)?"points":"point"); + + /* Initial purchase of an AA ability */ + if (cur_level < 1){ + Message(15, "You have gained the ability \"%s\" at a cost of %d ability %s.", aa2->name, real_cost, (real_cost>1) ? "points" : "point"); + + /* QS: Player_Log_AA_Purchases */ + if (RuleB(QueryServ, PlayerLogAAPurchases)){ + std::string event_desc = StringFormat("Initial AA Purchase :: aa_name:%s aa_id:%i at cost:%i in zoneid:%i instid:%i", aa2->name, aa2->id, real_cost, this->GetZoneID(), this->GetInstanceID()); + QServ->PlayerLogEvent(Player_Log_AA_Purchases, this->CharacterID(), event_desc); + } + } + /* Ranked purchase of an AA ability */ + else{ + Message(15, "You have improved %s %d at a cost of %d ability %s.", aa2->name, cur_level + 1, real_cost, (real_cost > 1) ? "points" : "point"); + + /* QS: Player_Log_AA_Purchases */ + if (RuleB(QueryServ, PlayerLogAAPurchases)){ + std::string event_desc = StringFormat("Ranked AA Purchase :: aa_name:%s aa_id:%i at cost:%i in zoneid:%i instid:%i", aa2->name, aa2->id, real_cost, this->GetZoneID(), this->GetInstanceID()); + QServ->PlayerLogEvent(Player_Log_AA_Purchases, this->CharacterID(), event_desc); + } + } + SendAAStats(); @@ -1816,7 +1837,7 @@ SendAA_Struct* ZoneDatabase::GetAASkillVars(uint32 skill_id) } query = StringFormat("SELECT a.cost, a.max_level, a.hotkey_sid, a.hotkey_sid2, a.title_sid, a.desc_sid, a.type, " - "COALESCE (" //So we can return 0 if it's null. + "COALESCE(" //So we can return 0 if it's null. "(" // this is our derived table that has the row # // that we can SELECT from, because the client is stupid. "SELECT p.prereq_index_num " diff --git a/zone/aggro.cpp b/zone/aggro.cpp index d55dab4fd..109720e47 100644 --- a/zone/aggro.cpp +++ b/zone/aggro.cpp @@ -873,7 +873,30 @@ bool Mob::CombatRange(Mob* other) if (size_mod > 10000) size_mod = size_mod / 7; - if (DistNoRoot(*other) <= size_mod) + float _DistNoRoot = DistNoRoot(*other); + + if (GetSpecialAbility(NPC_CHASE_DISTANCE)){ + + float max_dist = static_cast(GetSpecialAbilityParam(NPC_CHASE_DISTANCE, 0)); + float min_dist = static_cast(GetSpecialAbilityParam(NPC_CHASE_DISTANCE, 1)); + + if (max_dist == 1) + max_dist = 250.0f; //Default it to 250 if you forget to put a value + + max_dist = max_dist * max_dist; + + if (!min_dist) + min_dist = size_mod; //Default to melee range + else + min_dist = min_dist * min_dist; + + if (CheckLastLosState() && (_DistNoRoot >= min_dist && _DistNoRoot <= max_dist)) + SetPseudoRoot(true); + else + SetPseudoRoot(false); + } + + if (_DistNoRoot <= size_mod) { return true; } @@ -887,6 +910,8 @@ bool Mob::CheckLosFN(Mob* other) { if(other) Result = CheckLosFN(other->GetX(), other->GetY(), other->GetZ(), other->GetSize()); + SetLastLosState(Result); + return Result; } diff --git a/zone/attack.cpp b/zone/attack.cpp index dd14b1770..5abba347b 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -42,6 +42,9 @@ #include "quest_parser_collection.h" #include "water_map.h" #include "worldserver.h" +#include "queryserv.h" + +extern QueryServ* QServ; extern WorldServer worldserver; #ifdef _WINDOWS @@ -1455,14 +1458,14 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes att int exploss = 0; mlog(COMBAT__HITS, "Fatal blow dealt by %s with %d damage, spell %d, skill %d", killerMob ? killerMob->GetName() : "Unknown", damage, spell, attack_skill); - // - // #1: Send death packet to everyone - // + /* + #1: Send death packet to everyone + */ uint8 killed_level = GetLevel(); SendLogoutPackets(); - //make our become corpse packet, and queue to ourself before OP_Death. + /* Make self become corpse packet */ EQApplicationPacket app2(OP_BecomeCorpse, sizeof(BecomeCorpse_Struct)); BecomeCorpse_Struct* bc = (BecomeCorpse_Struct*)app2.pBuffer; bc->spawn_id = GetID(); @@ -1471,7 +1474,7 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes att bc->z = GetZ(); QueuePacket(&app2); - // make death packet + /* Make Death Packet */ EQApplicationPacket app(OP_Death, sizeof(Death_Struct)); Death_Struct* d = (Death_Struct*)app.pBuffer; d->spawn_id = GetID(); @@ -1484,9 +1487,9 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes att app.priority = 6; entity_list.QueueClients(this, &app); - // - // #2: figure out things that affect the player dying and mark them dead - // + /* + #2: figure out things that affect the player dying and mark them dead + */ InterruptSpell(); SetPet(0); @@ -1541,9 +1544,9 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes att //remove ourself from all proximities ClearAllProximities(); - // - // #3: exp loss and corpse generation - // + /* + #3: exp loss and corpse generation + */ // figure out if they should lose exp if(RuleB(Character, UseDeathExpLossMult)){ @@ -1659,27 +1662,21 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes att LeftCorpse = true; } - -// if(!IsLD())//Todo: make it so an LDed client leaves corpse if its enabled -// MakeCorpse(exploss); } else { BuffFadeDetrimental(); } - // - // Finally, send em home - // + /* + Finally, send em home - // we change the mob variables, not pp directly, because Save() will copy - // from these and overwrite what we set in pp anyway - // + We change the mob variables, not pp directly, because Save() will copy + from these and overwrite what we set in pp anyway + */ if(LeftCorpse && (GetClientVersionBit() & BIT_SoFAndLater) && RuleB(Character, RespawnFromHover)) { - ClearDraggedCorpses(); - - RespawnFromHoverTimer.Start(RuleI(Character, RespawnFromHoverTimer) * 1000); - + ClearDraggedCorpses(); + RespawnFromHoverTimer.Start(RuleI(Character, RespawnFromHoverTimer) * 1000); SendRespawnBinds(); } else @@ -1696,17 +1693,22 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes att if(r) r->MemberZoned(this); - dead_timer.Start(5000, true); - + dead_timer.Start(5000, true); m_pp.zone_id = m_pp.binds[0].zoneId; m_pp.zoneInstance = 0; - database.MoveCharacterToZone(this->CharacterID(), database.GetZoneName(m_pp.zone_id)); - - Save(); - + database.MoveCharacterToZone(this->CharacterID(), database.GetZoneName(m_pp.zone_id)); + Save(); GoToDeath(); } + /* QS: PlayerLogDeaths */ + if (RuleB(QueryServ, PlayerLogDeaths)){ + const char * killer_name = ""; + if (killerMob && killerMob->GetCleanName()){ killer_name = killerMob->GetCleanName(); } + std::string event_desc = StringFormat("Died in zoneid:%i instid:%i by '%s', spellid:%i, damage:%i", this->GetZoneID(), this->GetInstanceID(), killer_name, spell, damage); + QServ->PlayerLogEvent(Player_Log_Deaths, this->CharacterID(), event_desc); + } + parse->EventPlayer(EVENT_DEATH_COMPLETE, this, buffer, 0); return true; } diff --git a/zone/client.cpp b/zone/client.cpp index d986dbe22..37740b472 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -62,8 +62,9 @@ extern volatile bool RunLoops; #include "client_logs.h" #include "guild_mgr.h" #include "quest_parser_collection.h" +#include "queryserv.h" - +extern QueryServ* QServ; extern EntityList entity_list; extern Zone* zone; extern volatile bool ZoneLoaded; @@ -629,6 +630,9 @@ bool Client::Save(uint8 iCommitNow) { return false; } + /* Mirror Character Data */ + database.StoreCharacterLookup(this->CharacterID()); + return true; } @@ -805,8 +809,8 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s } } - - if(RuleB(QueryServ, PlayerChatLogging)) { + /* Logs Player Chat */ + if (RuleB(QueryServ, PlayerLogChat)) { ServerPacket* pack = new ServerPacket(ServerOP_Speech, sizeof(Server_Speech_Struct) + strlen(message) + 1); Server_Speech_Struct* sem = (Server_Speech_Struct*) pack->pBuffer; @@ -841,7 +845,7 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s switch(chan_num) { - case 0: { // GuildChat + case 0: { /* Guild Chat */ if (!IsInAGuild()) Message_StringID(MT_DefaultText, GUILD_NOT_MEMBER2); //You are not a member of any guild. else if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_SPEAK)) @@ -850,7 +854,7 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s Message(0, "Error: World server disconnected"); break; } - case 2: { // GroupChat + case 2: { /* Group Chat */ Raid* raid = entity_list.GetRaidByClient(this); if(raid) { raid->RaidGroupSay((const char*) message, this); @@ -863,14 +867,14 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s } break; } - case 15: { //raid say + case 15: { /* Raid Say */ Raid* raid = entity_list.GetRaidByClient(this); if(raid){ raid->RaidSay((const char*) message, this); } break; } - case 3: { // Shout + case 3: { /* Shout */ Mob *sender = this; if (GetPet() && GetPet()->FindType(SE_VoiceGraft)) sender = GetPet(); @@ -878,7 +882,7 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s entity_list.ChannelMessage(sender, chan_num, language, lang_skill, message); break; } - case 4: { // Auction + case 4: { /* Auction */ if(RuleB(Chat, ServerWideAuction)) { if(!global_channel_timer.Check()) @@ -917,7 +921,7 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s } break; } - case 5: { // OOC + case 5: { /* OOC */ if(RuleB(Chat, ServerWideOOC)) { if(!global_channel_timer.Check()) @@ -964,15 +968,15 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s } break; } - case 6: // Broadcast - case 11: { // GMSay + case 6: /* Broadcast */ + case 11: { /* GM Say */ if (!(admin >= 80)) Message(0, "Error: Only GMs can use this channel"); else if (!worldserver.SendChannelMessage(this, targetname, chan_num, 0, language, message)) Message(0, "Error: World server disconnected"); break; } - case 7: { // Tell + case 7: { /* Tell */ if(!global_channel_timer.Check()) { if(strlen(targetname) == 0) @@ -1020,7 +1024,7 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s Message(0, "Error: World server disconnected"); break; } - case 8: { // /say + case 8: { /* Say */ if(message[0] == COMMAND_CHAR) { if(command_dispatch(this, message) == -2) { if(parse->PlayerHasQuestSub(EVENT_COMMAND)) { @@ -4029,13 +4033,17 @@ void Client::KeyRingAdd(uint32 item_id) bool bFound = KeyRingCheck(item_id); if(!bFound){ sprintf(query, "INSERT INTO keyring(char_id,item_id) VALUES(%i,%i)",character_id,item_id); - if(database.RunQuery(query, strlen(query), errbuf, 0, &affected_rows)) - { + if(database.RunQuery(query, strlen(query), errbuf, 0, &affected_rows)) { Message(4,"Added to keyring."); + + /* QS: PlayerLogKeyringAddition */ + if (RuleB(QueryServ, PlayerLogKeyringAddition)){ + std::string event_desc = StringFormat("itemid:%i in zoneid:%i instid:%i", item_id, this->GetZoneID(), this->GetInstanceID()); + QServ->PlayerLogEvent(Player_Log_Keyring_Addition, this->CharacterID(), event_desc); + } safe_delete_array(query); } - else - { + else { std::cerr << "Error in Doors::HandleClick query '" << query << "' " << errbuf << std::endl; safe_delete_array(query); return; @@ -6933,8 +6941,18 @@ void Client::SetAlternateCurrencyValue(uint32 currency_id, uint32 new_amount) SendAlternateCurrencyValue(currency_id); } -void Client::AddAlternateCurrencyValue(uint32 currency_id, int32 amount) +void Client::AddAlternateCurrencyValue(uint32 currency_id, int32 amount, int8 method) { + + /* Added via Quest, rest of the logging methods may be done inline due to information available in that area of the code */ + if (method == 1){ + /* QS: PlayerLogAlternateCurrencyTransactions :: Cursor to Item Storage */ + if (RuleB(QueryServ, PlayerLogAlternateCurrencyTransactions)){ + std::string event_desc = StringFormat("Added via Quest :: Cursor to Item :: alt_currency_id:%i amount:%i in zoneid:%i instid:%i", currency_id, this->GetZoneID(), this->GetInstanceID()); + QServ->PlayerLogEvent(Player_Log_Alternate_Currency_Transactions, this->CharacterID(), event_desc); + } + } + if(amount == 0) { return; } diff --git a/zone/client.h b/zone/client.h index b78530ae1..f2d16ea0f 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1097,7 +1097,7 @@ public: inline void ClearDraggedCorpses() { DraggedCorpses.clear(); } void SendAltCurrencies(); void SetAlternateCurrencyValue(uint32 currency_id, uint32 new_amount); - void AddAlternateCurrencyValue(uint32 currency_id, int32 amount); + void AddAlternateCurrencyValue(uint32 currency_id, int32 amount, int8 method = 0); void SendAlternateCurrencyValues(); void SendAlternateCurrencyValue(uint32 currency_id, bool send_if_null = true); uint32 GetAlternateCurrencyValue(uint32 currency_id) const; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index ce6b1a323..f35ec0565 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -16,16 +16,16 @@ */ #include "../common/debug.h" -#include -#include -#include -#include -#include -#include -#include #include -#include +#include +#include +#include #include +#include +#include +#include +#include +#include #ifdef _WINDOWS #define snprintf _snprintf @@ -38,8 +38,6 @@ #include #endif -#include "masterentity.h" -#include "zonedb.h" #include "../common/packet_functions.h" #include "../common/packet_dump.h" #include "worldserver.h" @@ -59,17 +57,21 @@ #include "../common/faction.h" #include "../common/crc32.h" #include "string_ids.h" -#include "map.h" #include "titles.h" -#include "pets.h" +#include "water_map.h" +#include "worldserver.h" +#include "zone.h" #include "zone_config.h" #include "guild_mgr.h" #include "pathing.h" #include "water_map.h" #include "merc.h" +#include "pets.h" #include "../common/zone_numbers.h" #include "quest_parser_collection.h" +#include "queryserv.h" +extern QueryServ* QServ; extern Zone* zone; extern volatile bool ZoneLoaded; extern WorldServer worldserver; @@ -5687,8 +5689,8 @@ void Client::Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app) safe_delete(outapp); // start QS code - if(RuleB(QueryServ, MerchantLogTransactions)) { - ServerPacket* qspack = new ServerPacket(ServerOP_QSMerchantLogTransactions, sizeof(QSMerchantLogTransaction_Struct) + sizeof(QSTransactionItems_Struct)); + if(RuleB(QueryServ, PlayerLogMerchantTransactions)) { + ServerPacket* qspack = new ServerPacket(ServerOP_QSPlayerLogMerchantTransactions, sizeof(QSMerchantLogTransaction_Struct) + sizeof(QSTransactionItems_Struct)); QSMerchantLogTransaction_Struct* qsaudit = (QSMerchantLogTransaction_Struct*)qspack->pBuffer; qsaudit->zone_id = zone->GetZoneID(); @@ -5823,8 +5825,8 @@ void Client::Handle_OP_ShopPlayerSell(const EQApplicationPacket *app) } // start QS code - if(RuleB(QueryServ, MerchantLogTransactions)) { - ServerPacket* qspack = new ServerPacket(ServerOP_QSMerchantLogTransactions, sizeof(QSMerchantLogTransaction_Struct) + sizeof(QSTransactionItems_Struct)); + if(RuleB(QueryServ, PlayerLogMerchantTransactions)) { + ServerPacket* qspack = new ServerPacket(ServerOP_QSPlayerLogMerchantTransactions, sizeof(QSMerchantLogTransaction_Struct) + sizeof(QSTransactionItems_Struct)); QSMerchantLogTransaction_Struct* qsaudit = (QSMerchantLogTransaction_Struct*)qspack->pBuffer; qsaudit->zone_id = zone->GetZoneID(); @@ -9180,8 +9182,7 @@ bool Client::FinishConnState2(DBAsyncWork* dbaw) { if(!GetAA(aaPersistentMinion)) memset(&m_suspendedminion, 0, sizeof(PetInfo)); - //////////////////////////////////////////////////////////// - // Server Zone Entry Packet + /* Server Zone Entry Packet */ outapp = new EQApplicationPacket(OP_ZoneEntry, sizeof(ServerZoneEntry_Struct)); ServerZoneEntry_Struct* sze = (ServerZoneEntry_Struct*)outapp->pBuffer; @@ -9191,43 +9192,31 @@ bool Client::FinishConnState2(DBAsyncWork* dbaw) { sze->player.spawn.z += 6; //arbitrary lift, seems to help spawning under zone. outapp->priority = 6; FastQueuePacket(&outapp); - //safe_delete(outapp); - //////////////////////////////////////////////////////////// - // Zone Spawns Packet + /* Zone Spawns Packet */ entity_list.SendZoneSpawnsBulk(this); entity_list.SendZoneCorpsesBulk(this); entity_list.SendZonePVPUpdates(this); //hack until spawn struct is fixed. - - - //////////////////////////////////////////////////////////// - // Time of Day packet + /* Time of Day packet */ outapp = new EQApplicationPacket(OP_TimeOfDay, sizeof(TimeOfDay_Struct)); TimeOfDay_Struct* tod = (TimeOfDay_Struct*)outapp->pBuffer; zone->zone_time.getEQTimeOfDay(time(0), tod); outapp->priority = 6; FastQueuePacket(&outapp); - //safe_delete(outapp); - //I think this should happen earlier, not sure - /* if(GetHideMe()) - SetHideMe(true); */ - // Moved to Handle_Connect_OP_SendExpZonein(); - - - //////////////////////////////////////////////////////////// - // Tribute Packets + /* Tribute Packets */ DoTributeUpdate(); if(m_pp.tribute_active) { //restart the tribute timer where we left off tribute_timer.Start(m_pp.tribute_time_remaining); } - //////////////////////////////////////////////////////////// - // Character Inventory Packet - //this is not quite where live sends inventory, they do it after tribute - if (loaditems) {//dont load if a length error occurs + /* + Character Inventory Packet + this is not quite where live sends inventory, they do it after tribute + */ + if (loaditems) { //dont load if a length error occurs BulkSendInventoryItems(); // Send stuff on the cursor which isnt sent in bulk @@ -9241,9 +9230,7 @@ bool Client::FinishConnState2(DBAsyncWork* dbaw) { } } - - //////////////////////////////////////////////////////////// - // Task Packets + /* Task Packets */ LoadClientTaskState(); if (GetClientVersion() >= EQClientRoF) @@ -9261,10 +9248,11 @@ bool Client::FinishConnState2(DBAsyncWork* dbaw) { FastQueuePacket(&outapp); } - ////////////////////////////////////// - // Weather Packet - // This shouldent be moved, this seems to be what the client - // uses to advance to the next state (sending ReqNewZone) + /* + Weather Packet + This shouldent be moved, this seems to be what the client + uses to advance to the next state (sending ReqNewZone) + */ outapp = new EQApplicationPacket(OP_Weather, 12); Weather_Struct *ws = (Weather_Struct *) outapp->pBuffer; ws->val1 = 0x000000FF; @@ -9279,16 +9267,6 @@ bool Client::FinishConnState2(DBAsyncWork* dbaw) { QueuePacket(outapp); safe_delete(outapp); - ////////////////////////////////////// - // Group Roles - // - ////////////////////////////////////// - /*if(group){ - group->NotifyMainTank(this, 1); - group->NotifyMainAssist(this, 1); - group->NotifyPuller(this, 1); - }*/ - SetAttackTimer(); conn_state = ZoneInfoSent; @@ -9296,9 +9274,8 @@ bool Client::FinishConnState2(DBAsyncWork* dbaw) { return true; } -// Finish client connecting state -void Client::CompleteConnect() -{ +/* Finish client connecting state */ +void Client::CompleteConnect() { UpdateWho(); client_state = CLIENT_CONNECTED; @@ -9311,14 +9288,14 @@ void Client::CompleteConnect() EnteringMessages(this); LoadZoneFlags(); - // Sets GM Flag if needed & Sends Petition Queue + /* Sets GM Flag if needed & Sends Petition Queue */ UpdateAdmin(false); - if(IsInAGuild()){ + if (IsInAGuild()){ SendAppearancePacket(AT_GuildID, GuildID(), false); SendAppearancePacket(AT_GuildRank, GuildRank(), false); } - for(uint32 spellInt= 0; spellInt < MAX_PP_SPELLBOOK; spellInt++) + for (uint32 spellInt = 0; spellInt < MAX_PP_SPELLBOOK; spellInt++) { if (m_pp.spell_book[spellInt] < 3 || m_pp.spell_book[spellInt] > 50000) m_pp.spell_book[spellInt] = 0xFFFFFFFF; @@ -9329,35 +9306,37 @@ void Client::CompleteConnect() uint32 raidid = database.GetRaidID(GetName()); Raid *raid = nullptr; - if(raidid > 0){ + if (raidid > 0){ raid = entity_list.GetRaidByID(raidid); - if(!raid){ + if (!raid){ raid = new Raid(raidid); - if(raid->GetID() != 0){ + if (raid->GetID() != 0){ entity_list.AddRaid(raid, raidid); } else raid = nullptr; } - if(raid){ + if (raid){ SetRaidGrouped(true); raid->LearnMembers(); raid->VerifyRaid(); raid->GetRaidDetails(); - //only leader should get this; send to all for now till - //I figure out correct creation; can probably also send a no longer leader packet for non leaders - //but not important for now. + /* + Only leader should get this; send to all for now till + I figure out correct creation; can probably also send a no longer leader packet for non leaders + but not important for now. + */ raid->SendRaidCreate(this); raid->SendMakeLeaderPacketTo(raid->leadername, this); raid->SendRaidAdd(GetName(), this); raid->SendBulkRaid(this); raid->SendGroupUpdate(this); uint32 grpID = raid->GetGroup(GetName()); - if(grpID < 12){ + if (grpID < 12){ raid->SendRaidGroupRemove(GetName(), grpID); raid->SendRaidGroupAdd(GetName(), grpID); } - if(raid->IsLocked()) + if (raid->IsLocked()) raid->SendRaidLockTo(this); } } @@ -9366,154 +9345,155 @@ void Client::CompleteConnect() //reapply some buffs uint32 buff_count = GetMaxTotalSlots(); - for (uint32 j1=0; j1 < buff_count; j1++) { - if (buffs[j1].spellid > (uint32)SPDAT_RECORDS) + for (uint32 j1 = 0; j1 < buff_count; j1++) { + if (buffs[j1].spellid >(uint32)SPDAT_RECORDS) continue; const SPDat_Spell_Struct &spell = spells[buffs[j1].spellid]; - for (int x1=0; x1 < EFFECT_COUNT; x1++) { + for (int x1 = 0; x1 < EFFECT_COUNT; x1++) { switch (spell.effectid[x1]) { - case SE_IllusionCopy: - case SE_Illusion: { - if (spell.base[x1] == -1) { - if (gender == 1) - gender = 0; - else if (gender == 0) - gender = 1; - SendIllusionPacket(GetRace(), gender, 0xFF, 0xFF); - } - else if (spell.base[x1] == -2) - { - if (GetRace() == 128 || GetRace() == 130 || GetRace() <= 12) - SendIllusionPacket(GetRace(), GetGender(), spell.max[x1], spell.max[x1]); - } - else if (spell.max[x1] > 0) - { - SendIllusionPacket(spell.base[x1], 0xFF, spell.max[x1], spell.max[x1]); - } - else - { - SendIllusionPacket(spell.base[x1], 0xFF, 0xFF, 0xFF); - } - switch(spell.base[x1]){ - case OGRE: - SendAppearancePacket(AT_Size, 9); - break; - case TROLL: - SendAppearancePacket(AT_Size, 8); - break; - case VAHSHIR: - case BARBARIAN: - SendAppearancePacket(AT_Size, 7); - break; - case HALF_ELF: - case WOOD_ELF: - case DARK_ELF: - case FROGLOK: - SendAppearancePacket(AT_Size, 5); - break; - case DWARF: - SendAppearancePacket(AT_Size, 4); - break; - case HALFLING: - case GNOME: - SendAppearancePacket(AT_Size, 3); - break; - default: - SendAppearancePacket(AT_Size, 6); - break; - } + case SE_IllusionCopy: + case SE_Illusion: { + if (spell.base[x1] == -1) { + if (gender == 1) + gender = 0; + else if (gender == 0) + gender = 1; + SendIllusionPacket(GetRace(), gender, 0xFF, 0xFF); + } + else if (spell.base[x1] == -2) + { + if (GetRace() == 128 || GetRace() == 130 || GetRace() <= 12) + SendIllusionPacket(GetRace(), GetGender(), spell.max[x1], spell.max[x1]); + } + else if (spell.max[x1] > 0) + { + SendIllusionPacket(spell.base[x1], 0xFF, spell.max[x1], spell.max[x1]); + } + else + { + SendIllusionPacket(spell.base[x1], 0xFF, 0xFF, 0xFF); + } + switch (spell.base[x1]){ + case OGRE: + SendAppearancePacket(AT_Size, 9); + break; + case TROLL: + SendAppearancePacket(AT_Size, 8); + break; + case VAHSHIR: + case BARBARIAN: + SendAppearancePacket(AT_Size, 7); + break; + case HALF_ELF: + case WOOD_ELF: + case DARK_ELF: + case FROGLOK: + SendAppearancePacket(AT_Size, 5); + break; + case DWARF: + SendAppearancePacket(AT_Size, 4); + break; + case HALFLING: + case GNOME: + SendAppearancePacket(AT_Size, 3); + break; + default: + SendAppearancePacket(AT_Size, 6); break; } - case SE_SummonHorse: { - SummonHorse(buffs[j1].spellid); - //hasmount = true; //this was false, is that the correct thing? - break; + break; + } + case SE_SummonHorse: { + SummonHorse(buffs[j1].spellid); + //hasmount = true; //this was false, is that the correct thing? + break; + } + case SE_Silence: + { + Silence(true); + break; + } + case SE_Amnesia: + { + Amnesia(true); + break; + } + case SE_DivineAura: + { + invulnerable = true; + break; + } + case SE_Invisibility2: + case SE_Invisibility: + { + invisible = true; + SendAppearancePacket(AT_Invis, 1); + break; + } + case SE_Levitate: + { + if (!zone->CanLevitate()) + { + if (!GetGM()) + { + SendAppearancePacket(AT_Levitate, 0); + BuffFadeByEffect(SE_Levitate); + Message(13, "You can't levitate in this zone."); + } } - case SE_Silence: - { - Silence(true); - break; - } - case SE_Amnesia: - { - Amnesia(true); - break; - } - case SE_DivineAura: - { - invulnerable = true; - break; - } - case SE_Invisibility2: - case SE_Invisibility: - { - invisible = true; - SendAppearancePacket(AT_Invis, 1); - break; - } - case SE_Levitate: - { - if( !zone->CanLevitate() ) - { - if(!GetGM()) - { - SendAppearancePacket(AT_Levitate, 0); - BuffFadeByEffect(SE_Levitate); - Message(13, "You can't levitate in this zone."); - } - }else{ - SendAppearancePacket(AT_Levitate, 2); - } - break; - } - case SE_InvisVsUndead2: - case SE_InvisVsUndead: - { - invisible_undead = true; - break; - } - case SE_InvisVsAnimals: - { - invisible_animals = true; - break; - } - case SE_AddMeleeProc: - case SE_WeaponProc: - { - AddProcToWeapon(GetProcID(buffs[j1].spellid, x1), false, 100+spells[buffs[j1].spellid].base2[x1], buffs[j1].spellid); - break; - } - case SE_DefensiveProc: - { - AddDefensiveProc(GetProcID(buffs[j1].spellid, x1), 100+spells[buffs[j1].spellid].base2[x1],buffs[j1].spellid); - break; - } - case SE_RangedProc: - { - AddRangedProc(GetProcID(buffs[j1].spellid, x1), 100+spells[buffs[j1].spellid].base2[x1],buffs[j1].spellid); - break; - } + else{ + SendAppearancePacket(AT_Levitate, 2); + } + break; + } + case SE_InvisVsUndead2: + case SE_InvisVsUndead: + { + invisible_undead = true; + break; + } + case SE_InvisVsAnimals: + { + invisible_animals = true; + break; + } + case SE_AddMeleeProc: + case SE_WeaponProc: + { + AddProcToWeapon(GetProcID(buffs[j1].spellid, x1), false, 100 + spells[buffs[j1].spellid].base2[x1], buffs[j1].spellid); + break; + } + case SE_DefensiveProc: + { + AddDefensiveProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].base2[x1], buffs[j1].spellid); + break; + } + case SE_RangedProc: + { + AddRangedProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].base2[x1], buffs[j1].spellid); + break; + } } } } - //sends appearances for all mobs not doing anim_stand aka sitting, looting, playing dead + /* Sends appearances for all mobs not doing anim_stand aka sitting, looting, playing dead */ entity_list.SendZoneAppearance(this); - //sends the Nimbus particle effects (up to 3) for any mob using them + /* Sends the Nimbus particle effects (up to 3) for any mob using them */ entity_list.SendNimbusEffects(this); entity_list.SendUntargetable(this); client_data_loaded = true; int x; - for(x=0;x<8;x++) + for (x = 0; x < 8; x++) SendWearChange(x); Mob *pet = GetPet(); - if(pet != nullptr) { - for(x=0;x<8;x++) + if (pet != nullptr) { + for (x = 0; x < 8; x++) pet->SendWearChange(x); } @@ -9521,14 +9501,14 @@ void Client::CompleteConnect() zoneinpacket_timer.Start(); - if(GetPet()){ + if (GetPet()){ GetPet()->SendPetBuffsToClient(); } - if(GetGroup()) + if (GetGroup()) database.RefreshGroupFromDB(this); - if(RuleB(TaskSystem, EnableTaskSystem)) + if (RuleB(TaskSystem, EnableTaskSystem)) TaskPeriodic_Timer.Start(); else TaskPeriodic_Timer.Disable(); @@ -9536,51 +9516,50 @@ void Client::CompleteConnect() conn_state = ClientConnectFinished; //enforce some rules.. - if(!CanBeInZone()) { + if (!CanBeInZone()) { _log(CLIENT__ERROR, "Kicking char from zone, not allowed here"); GoToSafeCoords(database.GetZoneID("arena"), 0); return; } - if(zone) + if (zone) zone->weatherSend(); TotalKarma = database.GetKarma(AccountID()); - SendDisciplineTimers(); parse->EventPlayer(EVENT_ENTER_ZONE, this, "", 0); - //This sub event is for if a player logs in for the first time since entering world. - if(firstlogon == 1) - parse->EventPlayer(EVENT_CONNECT, this, "", 0); + /* This sub event is for if a player logs in for the first time since entering world. */ + if (firstlogon == 1){ + parse->EventPlayer(EVENT_CONNECT, this, "", 0); + /* QS: PlayerLogConnectDisconnect */ + if (RuleB(QueryServ, PlayerLogConnectDisconnect)){ + std::string event_desc = StringFormat("Connect :: Logged into zoneid:%i instid:%i", this->GetZoneID(), this->GetInstanceID()); + QServ->PlayerLogEvent(Player_Log_Connect_State, this->CharacterID(), event_desc); + } + } - if(zone) - { - if(zone->GetInstanceTimer()) - { + if(zone) { + if(zone->GetInstanceTimer()) { uint32 ttime = zone->GetInstanceTimer()->GetRemainingTime(); uint32 day = (ttime/86400000); uint32 hour = (ttime/3600000)%24; uint32 minute = (ttime/60000)%60; uint32 second = (ttime/1000)%60; - if(day) - { + if(day) { Message(15, "%s(%u) will expire in %u days, %u hours, %u minutes, and %u seconds.", zone->GetLongName(), zone->GetInstanceID(), day, hour, minute, second); } - else if(hour) - { + else if(hour) { Message(15, "%s(%u) will expire in %u hours, %u minutes, and %u seconds.", zone->GetLongName(), zone->GetInstanceID(), hour, minute, second); } - else if(minute) - { + else if(minute) { Message(15, "%s(%u) will expire in %u minutes, and %u seconds.", zone->GetLongName(), zone->GetInstanceID(), minute, second); } - else - { + else { Message(15, "%s(%u) will expire in in %u seconds.", zone->GetLongName(), zone->GetInstanceID(), second); } @@ -9603,8 +9582,7 @@ void Client::CompleteConnect() if(GetClientVersion() >= EQClientSoD) entity_list.SendFindableNPCList(this); - if(IsInAGuild()) - { + if(IsInAGuild()) { SendGuildRanks(); guild_mgr.SendGuildMemberUpdateToWorld(GetName(), GuildID(), zone->GetZoneID(), time(nullptr)); guild_mgr.RequestOnlineGuildMembers(this->CharacterID(), this->GuildID()); @@ -9616,8 +9594,7 @@ void Client::CompleteConnect() worldserver.SendPacket(pack); delete pack; - if(IsClient() && CastToClient()->GetClientVersionBit() & BIT_UnderfootAndLater) - { + if(IsClient() && CastToClient()->GetClientVersionBit() & BIT_UnderfootAndLater) { EQApplicationPacket *outapp = MakeBuffsPacket(false); CastToClient()->FastQueuePacket(&outapp); } @@ -12749,6 +12726,12 @@ void Client::Handle_OP_AltCurrencyPurchase(const EQApplicationPacket *app) { return; } + /* QS: PlayerLogAlternateCurrencyTransactions :: Merchant Purchase */ + if (RuleB(QueryServ, PlayerLogAlternateCurrencyTransactions)){ + std::string event_desc = StringFormat("Merchant Purchase :: Spent alt_currency_id:%i cost:%i for itemid:%i in zoneid:%i instid:%i", alt_cur_id, cost, item->ID, this->GetZoneID(), this->GetInstanceID()); + QServ->PlayerLogEvent(Player_Log_Alternate_Currency_Transactions, this->CharacterID(), event_desc); + } + AddAlternateCurrencyValue(alt_cur_id, -((int32)cost)); int16 charges = 1; if(item->MaxCharges != 0) @@ -12780,20 +12763,37 @@ void Client::Handle_OP_AltCurrencyReclaim(const EQApplicationPacket *app) { return; } - if(reclaim->reclaim_flag == 1) { //item -> altcur + /* Item to Currency Storage */ + if(reclaim->reclaim_flag == 1) { uint32 removed = NukeItem(item_id, invWhereWorn | invWherePersonal | invWhereCursor); if(removed > 0) { AddAlternateCurrencyValue(reclaim->currency_id, removed); + + /* QS: PlayerLogAlternateCurrencyTransactions :: Item to Currency */ + if (RuleB(QueryServ, PlayerLogAlternateCurrencyTransactions)){ + std::string event_desc = StringFormat("Reclaim :: Item to Currency :: alt_currency_id:%i amount:%i to currency tab in zoneid:%i instid:%i", reclaim->currency_id, removed, this->GetZoneID(), this->GetInstanceID()); + QServ->PlayerLogEvent(Player_Log_Alternate_Currency_Transactions, this->CharacterID(), event_desc); + } } - } else { + } + /* Cursor to Item storage */ + else { uint32 max_currency = GetAlternateCurrencyValue(reclaim->currency_id); + + /* If you input more than you have currency wise, just give the max of the currency you currently have */ if(reclaim->count > max_currency) { SummonItem(item_id, max_currency); SetAlternateCurrencyValue(reclaim->currency_id, 0); - } else { + } + else { SummonItem(item_id, reclaim->count, 0, 0, 0, 0, 0, false, MainCursor); AddAlternateCurrencyValue(reclaim->currency_id, -((int32)reclaim->count)); } + /* QS: PlayerLogAlternateCurrencyTransactions :: Cursor to Item Storage */ + if (RuleB(QueryServ, PlayerLogAlternateCurrencyTransactions)){ + std::string event_desc = StringFormat("Reclaim :: Cursor to Item :: alt_currency_id:%i amount:-%i in zoneid:%i instid:%i", reclaim->currency_id, reclaim->count, this->GetZoneID(), this->GetInstanceID()); + QServ->PlayerLogEvent(Player_Log_Alternate_Currency_Transactions, this->CharacterID(), event_desc); + } } } @@ -12829,6 +12829,7 @@ void Client::Handle_OP_AltCurrencySell(const EQApplicationPacket *app) { uint32 cost = 0; uint32 current_currency = GetAlternateCurrencyValue(alt_cur_id); uint32 merchant_id = tar->MerchantType; + uint32 npc_id = tar->GetNPCTypeID(); bool found = false; std::list merlist = zone->merchanttable[merchant_id]; std::list::const_iterator itr; @@ -12881,6 +12882,12 @@ void Client::Handle_OP_AltCurrencySell(const EQApplicationPacket *app) { sell->cost = cost; + /* QS: PlayerLogAlternateCurrencyTransactions :: Sold to Merchant*/ + if (RuleB(QueryServ, PlayerLogAlternateCurrencyTransactions)){ + std::string event_desc = StringFormat("Sold to Merchant :: itemid:%u npcid:%u alt_currency_id:%u cost:%u in zoneid:%u instid:%i", item->ID, npc_id, alt_cur_id, cost, this->GetZoneID(), this->GetInstanceID()); + QServ->PlayerLogEvent(Player_Log_Alternate_Currency_Transactions, this->CharacterID(), event_desc); + } + FastQueuePacket(&outapp); AddAlternateCurrencyValue(alt_cur_id, cost); Save(1); @@ -12940,7 +12947,7 @@ void Client::Handle_OP_LFGuild(const EQApplicationPacket *app) switch(Command) { case 0: - { + { VERIFY_PACKET_LENGTH(OP_LFGuild, app, LFGuild_PlayerToggle_Struct); LFGuild_PlayerToggle_Struct *pts = (LFGuild_PlayerToggle_Struct *)app->pBuffer; diff --git a/zone/client_process.cpp b/zone/client_process.cpp index 3fea686ab..e39cef0bd 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -63,7 +63,9 @@ #include "guild_mgr.h" #include #include "quest_parser_collection.h" +#include "queryserv.h" +extern QueryServ* QServ; extern Zone* zone; extern volatile bool ZoneLoaded; extern WorldServer worldserver; @@ -770,38 +772,40 @@ bool Client::Process() { return ret; } -//just a set of actions preformed all over in Client::Process +/* Just a set of actions preformed all over in Client::Process */ void Client::OnDisconnect(bool hard_disconnect) { if(hard_disconnect) { - LeaveGroup(); - + LeaveGroup(); Raid *MyRaid = entity_list.GetRaidByClient(this); if (MyRaid) MyRaid->MemberZoned(this); - parse->EventPlayer(EVENT_DISCONNECT, this, "", 0); + parse->EventPlayer(EVENT_DISCONNECT, this, "", 0); + + /* QS: PlayerLogConnectDisconnect */ + if (RuleB(QueryServ, PlayerLogConnectDisconnect)){ + std::string event_desc = StringFormat("Disconnect :: in zoneid:%i instid:%i", this->GetZoneID(), this->GetInstanceID()); + QServ->PlayerLogEvent(Player_Log_Connect_State, this->CharacterID(), event_desc); + } } - Mob *Other = trade->With(); - - if(Other) - { - mlog(TRADING__CLIENT, "Client disconnected during a trade. Returning their items."); - + Mob *Other = trade->With(); + if(Other) { + mlog(TRADING__CLIENT, "Client disconnected during a trade. Returning their items."); FinishTrade(this); if(Other->IsClient()) Other->CastToClient()->FinishTrade(Other); - trade->Reset(); - + /* Reset both sides of the trade */ + trade->Reset(); Other->trade->Reset(); } database.SetFirstLogon(CharacterID(), 0); //We change firstlogon status regardless of if a player logs out to zone or not, because we only want to trigger it on their first login from world. - //remove ourself from all proximities + /* Remove ourself from all proximities */ ClearAllProximities(); EQApplicationPacket *outapp = new EQApplicationPacket(OP_LogoutReply); diff --git a/zone/command.cpp b/zone/command.cpp index 555804cfa..b14a173bc 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -62,8 +62,9 @@ #include "guild_mgr.h" #include "titles.h" #include "../common/patches/patches.h" +#include "queryserv.h" -// these should be in the headers... +extern QueryServ* QServ; extern WorldServer worldserver; extern TaskManager *taskmanager; void CatchSignal(int sig_num); @@ -588,6 +589,12 @@ int command_realdispatch(Client *c, const char *message) return(-1); } + /* QS: Player_Log_Issued_Commands */ + if (RuleB(QueryServ, PlayerLogIssuedCommandes)){ + std::string event_desc = StringFormat("Issued command :: '%s' in zoneid:%i instid:%i", message, c->GetZoneID(), c->GetInstanceID()); + QServ->PlayerLogEvent(Player_Log_Issued_Commands, c->CharacterID(), event_desc); + } + #ifdef COMMANDS_LOGGING if(cur->access >= COMMANDS_LOGGING_MIN_STATUS) { LogFile->write(EQEMuLog::Commands, "%s (%s) used command: %s (target=%s)", c->GetName(), c->AccountName(), message, c->GetTarget()?c->GetTarget()->GetName():"NONE"); diff --git a/zone/common.h b/zone/common.h index 3cc1af1cf..0c1fe28bf 100644 --- a/zone/common.h +++ b/zone/common.h @@ -127,7 +127,8 @@ enum { FLEE_PERCENT = 37, ALLOW_BENEFICIAL = 38, DISABLE_MELEE = 39, - MAX_SPECIAL_ATTACK = 40 + NPC_CHASE_DISTANCE = 40, + MAX_SPECIAL_ATTACK = 41 }; diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index b2c4b02ad..5adbdedbd 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -28,8 +28,10 @@ #include "embxs.h" #include "entity.h" #include "zone.h" +#include "queryserv.h" extern Zone* zone; +extern QueryServ* QServ; /* @@ -3370,6 +3372,36 @@ XS(XS__clear_npctype_cache) XSRETURN_EMPTY; } +XS(XS__qs_send_query); +XS(XS__qs_send_query) +{ + dXSARGS; + if (items != 1){ + Perl_croak(aTHX_ "Usage: qs_send_query(query)"); + } + else{ + // char *Query = (char *)SvPV_nolen(ST(0)); + std::string Query = (std::string)SvPV_nolen(ST(0)); + QServ->SendQuery(Query); + } + XSRETURN_EMPTY; +} + +XS(XS__qs_player_event); +XS(XS__qs_player_event) +{ + dXSARGS; + if (items != 2){ + Perl_croak(aTHX_ "Usage: qs_player_event(char_id, event_desc)"); + } + else{ + int char_id = (int)SvIV(ST(0)); + std::string event_desc = (std::string)SvPV_nolen(ST(1)); + QServ->PlayerLogEvent(Player_Log_Quest, char_id, event_desc); + } + XSRETURN_EMPTY; +} + /* This is the callback perl will look for to setup the quest package's XSUBs @@ -3591,6 +3623,8 @@ EXTERN_C XS(boot_quest) newXS(strcpy(buf, "enablerecipe"), XS__enablerecipe, file); newXS(strcpy(buf, "disablerecipe"), XS__disablerecipe, file); newXS(strcpy(buf, "clear_npctype_cache"), XS__clear_npctype_cache, file); + newXS(strcpy(buf, "qs_send_query"), XS__qs_send_query, file); + newXS(strcpy(buf, "qs_player_event"), XS__qs_player_event, file); XSRETURN_YES; } diff --git a/zone/entity.cpp b/zone/entity.cpp index 242d1a6b6..2ca3f640d 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -4170,6 +4170,20 @@ void EntityList::SignalAllClients(uint32 data) } } +uint16 EntityList::GetClientCount(){ + uint16 ClientCount = 0; + std::list client_list; + entity_list.GetClientList(client_list); + std::list::iterator iter = client_list.begin(); + while (iter != client_list.end()) { + Client *entry = (*iter); + entry->GetCleanName(); + ClientCount++; + iter++; + } + return ClientCount; +} + void EntityList::GetMobList(std::list &m_list) { m_list.clear(); diff --git a/zone/entity.h b/zone/entity.h index d61298b0a..cc302fe75 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -398,6 +398,7 @@ public: void UpdateFindableNPCState(NPC *n, bool Remove); void HideCorpses(Client *c, uint8 CurrentMode, uint8 NewMode); + uint16 GetClientCount(); void GetMobList(std::list &m_list); void GetNPCList(std::list &n_list); void GetMercList(std::list &n_list); diff --git a/zone/exp.cpp b/zone/exp.cpp index 73d339af6..9415022ca 100644 --- a/zone/exp.cpp +++ b/zone/exp.cpp @@ -22,6 +22,9 @@ #include "../common/string_util.h" #include "../common/rulesys.h" #include "quest_parser_collection.h" +#include "queryserv.h" + +extern QueryServ* QServ; static uint32 MaxBankedGroupLeadershipPoints(int Level) @@ -212,7 +215,6 @@ void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) { return; // Must be invalid class/race } - if ((set_exp + set_aaxp) > (m_pp.exp+m_pp.expAA)) { if (isrezzexp) this->Message_StringID(MT_Experience, REZ_REGAIN); @@ -288,6 +290,14 @@ void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) { //Message(15, "You have gained %d skill points!!", m_pp.aapoints - last_unspentAA); char val1[20]={0}; Message_StringID(MT_Experience, GAIN_ABILITY_POINT,ConvertArray(m_pp.aapoints, val1),m_pp.aapoints == 1 ? "" : "(s)"); //You have gained an ability point! You now have %1 ability point%2. + + /* QS: PlayerLogAARate */ + if (RuleB(QueryServ, PlayerLogAARate)){ + int add_points = (m_pp.aapoints - last_unspentAA); + std::string query = StringFormat("INSERT INTO `qs_player_aa_rate_hourly` (char_id, aa_count, hour_time) VALUES (%i, %i, UNIX_TIMESTAMP() - MOD(UNIX_TIMESTAMP(), 3600)) ON DUPLICATE KEY UPDATE `aa_count` = `aa_count` + %i", this->CharacterID(), add_points, add_points); + QServ->SendQuery(query.c_str()); + } + //Message(15, "You now have %d skill points available to spend.", m_pp.aapoints); } @@ -299,12 +309,10 @@ void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) { if(check_level > maxlevel) { check_level = maxlevel; - if(RuleB(Character, KeepLevelOverMax)) - { + if(RuleB(Character, KeepLevelOverMax)) { set_exp = GetEXPForLevel(GetLevel()+1); } - else - { + else { set_exp = GetEXPForLevel(maxlevel); } } @@ -314,8 +322,7 @@ void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) { if(MaxLevel){ if(GetLevel() >= MaxLevel){ uint32 expneeded = GetEXPForLevel(MaxLevel); - if(set_exp > expneeded) - { + if(set_exp > expneeded) { set_exp = expneeded; } } @@ -327,11 +334,11 @@ void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) { if (GetLevel() == check_level-1){ Message_StringID(MT_Experience, GAIN_LEVEL,ConvertArray(check_level,val1)); SendLevelAppearance(); - //Message(15, "You have gained a level! Welcome to level %i!", check_level); + /* Message(15, "You have gained a level! Welcome to level %i!", check_level); */ } if (GetLevel() == check_level){ Message_StringID(MT_Experience, LOSE_LEVEL,ConvertArray(check_level,val1)); - //Message(15, "You lost a level! You are now level %i!", check_level); + /* Message(15, "You lost a level! You are now level %i!", check_level); */ } else Message(15, "Welcome to level %i!", check_level); @@ -352,8 +359,7 @@ void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) { //If were at max level then stop gaining experience if we make it to the cap if(GetLevel() == maxlevel - 1){ uint32 expneeded = GetEXPForLevel(maxlevel); - if(set_exp > expneeded) - { + if(set_exp > expneeded) { set_exp = expneeded; } } @@ -405,15 +411,13 @@ void Client::SetLevel(uint8 set_level, bool command) level = set_level; - if(IsRaidGrouped()) - { + if(IsRaidGrouped()) { Raid *r = this->GetRaid(); if(r){ r->UpdateLevel(GetName(), set_level); } } - if(set_level > m_pp.level2) - { + if(set_level > m_pp.level2) { if(m_pp.level2 == 0) m_pp.points += 5; else @@ -423,6 +427,18 @@ void Client::SetLevel(uint8 set_level, bool command) } if(set_level > m_pp.level) { parse->EventPlayer(EVENT_LEVEL_UP, this, "", 0); + /* QS: PlayerLogLevels */ + if (RuleB(QueryServ, PlayerLogLevels)){ + std::string event_desc = StringFormat("Leveled UP :: to Level:%i from Level:%i in zoneid:%i instid:%i", set_level, m_pp.level, this->GetZoneID(), this->GetInstanceID()); + QServ->PlayerLogEvent(Player_Log_Levels, this->CharacterID(), event_desc); + } + } + else if (set_level < m_pp.level){ + /* QS: PlayerLogLevels */ + if (RuleB(QueryServ, PlayerLogLevels)){ + std::string event_desc = StringFormat("Leveled DOWN :: to Level:%i from Level:%i in zoneid:%i instid:%i", set_level, m_pp.level, this->GetZoneID(), this->GetInstanceID()); + QServ->PlayerLogEvent(Player_Log_Levels, this->CharacterID(), event_desc); + } } m_pp.level = set_level; @@ -432,34 +448,34 @@ void Client::SetLevel(uint8 set_level, bool command) lu->exp = 0; } else { - float tmpxp = (float) ( (float) m_pp.exp - GetEXPForLevel( GetLevel() )) / - ( (float) GetEXPForLevel(GetLevel()+1) - GetEXPForLevel(GetLevel())); + float tmpxp = (float) ( (float) m_pp.exp - GetEXPForLevel( GetLevel() )) / ( (float) GetEXPForLevel(GetLevel()+1) - GetEXPForLevel(GetLevel())); lu->exp = (uint32)(330.0f * tmpxp); } QueuePacket(outapp); safe_delete(outapp); this->SendAppearancePacket(AT_WhoLevel, set_level); // who level change - LogFile->write(EQEMuLog::Normal,"Setting Level for %s to %i", GetName(), set_level); + LogFile->write(EQEMuLog::Normal,"Setting Level for %s to %i", GetName(), set_level); CalcBonuses(); - if(!RuleB(Character, HealOnLevel)) - { + + if(!RuleB(Character, HealOnLevel)) { int mhp = CalcMaxHP(); if(GetHP() > mhp) SetHP(mhp); } - else - { + else { SetHP(CalcMaxHP()); // Why not, lets give them a free heal } - DoTributeUpdate(); + DoTributeUpdate(); SendHPUpdate(); SetMana(CalcMaxMana()); UpdateWho(); - if(GetMerc()) - UpdateMercLevel(); + + if(GetMerc()) + UpdateMercLevel(); + Save(); } @@ -508,23 +524,13 @@ uint32 Client::GetEXPForLevel(uint16 check_level) uint32 finalxp = uint32(base * mod); finalxp = mod_client_xp_for_level(finalxp, check_level); - return(finalxp); + return finalxp; } -void Client::AddLevelBasedExp(uint8 exp_percentage, uint8 max_level) { - - if (exp_percentage > 100) - { - exp_percentage = 100; - } - - if (!max_level || GetLevel() < max_level) - { - max_level = GetLevel(); - } - - uint32 newexp = GetEXP() + ((GetEXPForLevel(max_level + 1) - GetEXPForLevel(max_level)) * exp_percentage / 100); - +void Client::AddLevelBasedExp(uint8 exp_percentage, uint8 max_level) { + if (exp_percentage > 100) { exp_percentage = 100; } + if (!max_level || GetLevel() < max_level) { max_level = GetLevel(); } + uint32 newexp = GetEXP() + ((GetEXPForLevel(max_level + 1) - GetEXPForLevel(max_level)) * exp_percentage / 100); SetEXP(newexp, GetAAXP()); } @@ -666,28 +672,25 @@ void Client::SendLeadershipEXPUpdate() { } uint32 Client::GetCharMaxLevelFromQGlobal() { + QGlobalCache *char_c = nullptr; + char_c = this->GetQGlobals(); - QGlobalCache *char_c = nullptr; - char_c = this->GetQGlobals(); + std::list globalMap; + uint32 ntype = 0; - std::list globalMap; - uint32 ntype = 0; + if(char_c) { + QGlobalCache::Combine(globalMap, char_c->GetBucket(), ntype, this->CharacterID(), zone->GetZoneID()); + } - if(char_c) - { - QGlobalCache::Combine(globalMap, char_c->GetBucket(), ntype, this->CharacterID(), zone->GetZoneID()); - } + std::list::iterator iter = globalMap.begin(); + uint32 gcount = 0; + while(iter != globalMap.end()) { + if((*iter).name.compare("CharMaxLevel") == 0){ + return atoi((*iter).value.c_str()); + } + ++iter; + ++gcount; + } - std::list::iterator iter = globalMap.begin(); - uint32 gcount = 0; - while(iter != globalMap.end()) - { - if((*iter).name.compare("CharMaxLevel") == 0){ - return atoi((*iter).value.c_str()); - } - ++iter; - ++gcount; - } - - return false; // Default is false + return false; } diff --git a/zone/groups.cpp b/zone/groups.cpp index 598fa4989..5c34cf359 100644 --- a/zone/groups.cpp +++ b/zone/groups.cpp @@ -193,11 +193,10 @@ void Group::SplitMoney(uint32 copper, uint32 silver, uint32 gold, uint32 platinu for (i = 0; i < MAX_GROUP_MEMBERS; i++) { if (members[i] != nullptr && members[i]->IsClient()) { // If Group Member is Client - Client *c = members[i]->CastToClient(); - //I could not get MoneyOnCorpse to work, so we use this - c->AddMoneyToPP(cpsplit, spsplit, gpsplit, ppsplit, true); - - c->Message(2, msg.c_str()); + Client *c = members[i]->CastToClient(); + //I could not get MoneyOnCorpse to work, so we use this + c->AddMoneyToPP(cpsplit, spsplit, gpsplit, ppsplit, true); + c->Message(2, msg.c_str()); } } } diff --git a/zone/hate_list.cpp b/zone/hate_list.cpp index c7f44e6ba..86b20d313 100644 --- a/zone/hate_list.cpp +++ b/zone/hate_list.cpp @@ -311,7 +311,7 @@ Mob *HateList::GetTop(Mob *center) } } - if (cur->ent->Sanctuary()) { + if (cur->ent->Sanctuary()) { if(hate == -1) { top = cur->ent; diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index a1af1b499..32affc06e 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -1134,7 +1134,7 @@ void Lua_Client::Signal(uint32 id) { void Lua_Client::AddAlternateCurrencyValue(uint32 currency, int amount) { Lua_Safe_Call_Void(); - self->AddAlternateCurrencyValue(currency, amount); + self->AddAlternateCurrencyValue(currency, amount, 1); } void Lua_Client::SendWebLink(const char *site) { diff --git a/zone/mob.cpp b/zone/mob.cpp index f9cdba950..0d7002253 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -183,6 +183,7 @@ Mob::Mob(const char* in_name, has_MGB = false; has_ProjectIllusion = false; SpellPowerDistanceMod = 0; + last_los_check = false; if(in_aa_title>0) aa_title = in_aa_title; @@ -341,6 +342,7 @@ Mob::Mob(const char* in_name, viral_spells[i] = 0; } pStandingPetOrder = SPO_Follow; + pseudo_rooted = false; see_invis = in_see_invis; see_invis_undead = in_see_invis_undead != 0; diff --git a/zone/mob.h b/zone/mob.h index fe0919f7e..55a57892b 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -464,6 +464,8 @@ public: bool CheckLosFN(float posX, float posY, float posZ, float mobSize); inline void SetChanged() { pLastChange = Timer::GetCurrentTime(); } inline const uint32 LastChange() const { return pLastChange; } + inline void SetLastLosState(bool value) { last_los_check = value; } + inline bool CheckLastLosState() const { return last_los_check; } //Quest void QuestReward(Client *c = nullptr, uint32 silver = 0, uint32 gold = 0, uint32 platinum = 0); @@ -752,7 +754,8 @@ public: inline const bool IsRooted() const { return rooted || permarooted; } inline const bool HasVirus() const { return has_virus; } int GetSnaredAmount(); - + inline const bool IsPseudoRooted() const { return pseudo_rooted; } + inline void SetPseudoRoot(bool prState) { pseudo_rooted = prState; } int GetCurWp() { return cur_wp; } @@ -1119,6 +1122,8 @@ protected: bool has_MGB; bool has_ProjectIllusion; int16 SpellPowerDistanceMod; + bool last_los_check; + bool pseudo_rooted; // Bind wound Timer bindwound_timer; diff --git a/zone/net.cpp b/zone/net.cpp index d81ea71e3..8a8421786 100644 --- a/zone/net.cpp +++ b/zone/net.cpp @@ -48,6 +48,7 @@ #include "worldserver.h" #include "net.h" #include "zone.h" +#include "queryserv.h" #include "command.h" #include "zone_config.h" #include "titles.h" @@ -98,6 +99,7 @@ npcDecayTimes_Struct npcCorpseDecayTimes[100]; TitleManager title_manager; DBAsyncFinishedQueue MTdbafq; DBAsync *dbasync = nullptr; +QueryServ *QServ = 0; TaskManager *taskmanager = 0; QuestParserCollection *parse = 0; @@ -114,6 +116,8 @@ int main(int argc, char** argv) { const char *zone_name; + QServ = new QueryServ; + if(argc == 3) { worldserver.SetLauncherName(argv[2]); worldserver.SetLaunchedName(argv[1]); @@ -622,7 +626,7 @@ void LoadSpells(EQEmu::MemoryMappedFile **mmf) { SPDAT_RECORDS = records; } - +/* Update Window Title with relevant information */ void UpdateWindowTitle(char* iNewTitle) { #ifdef _WINDOWS char tmp[500]; @@ -634,7 +638,7 @@ void UpdateWindowTitle(char* iNewTitle) { #if defined(GOTFRAGS) || defined(_EQDEBUG) snprintf(tmp, sizeof(tmp), "%i: %s, %i clients, %i", ZoneConfig::get()->ZonePort, zone->GetShortName(), numclients, getpid()); #else - snprintf(tmp, sizeof(tmp), "%i: %s, %i clients", ZoneConfig::get()->ZonePort, zone->GetShortName(), numclients); + snprintf(tmp, sizeof(tmp), "%s :: clients: %i inst_id: %i inst_ver: %i :: port: %i", zone->GetShortName(), numclients, zone->GetInstanceID(), zone->GetInstanceVersion(), ZoneConfig::get()->ZonePort); #endif } else { diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index 476c47599..72e2865c9 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -1320,6 +1320,8 @@ XS(XS_Client_MovePCInstance) else _log(CLIENT__ERROR, "Perl(XS_Client_MovePCInstance) attempted to process an Unknown type reference"); + Perl_croak(aTHX_ "THIS is not of type Client"); + Perl_croak(aTHX_ "THIS is not of type Client"); } } diff --git a/zone/petitions.cpp b/zone/petitions.cpp index 4dce685ea..ba57fe7f2 100644 --- a/zone/petitions.cpp +++ b/zone/petitions.cpp @@ -300,8 +300,6 @@ void ZoneDatabase::RefreshPetitionsFromDB() newpet->SetSentTime2(atol(row[13])); newpet->SetGMText(row[14]); - std::cout << "Petition " << row[0] << " pettime = " << newpet->GetSentTime() << std::endl; - if (atoi(row[12]) == 1) newpet->SetCheckedOut(true); else diff --git a/zone/queryserv.cpp b/zone/queryserv.cpp new file mode 100644 index 000000000..2b02d193c --- /dev/null +++ b/zone/queryserv.cpp @@ -0,0 +1,52 @@ +/* EQEMu: Everquest Server Emulator +Copyright (C) 2001-2014 EQEMu Development Team (http://eqemulator.net) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY except by those people which sell it, which + are required to give you total support for your newly bought product; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "../common/debug.h" +#include "../common/servertalk.h" +#include "../common/string_util.h" +#include "queryserv.h" +#include "worldserver.h" +#include "net.h" + +#include + +extern WorldServer worldserver; +extern QueryServ* QServ; + +QueryServ::QueryServ(){ +} + +QueryServ::~QueryServ(){ +} + +void QueryServ::SendQuery(std::string Query) +{ + ServerPacket* pack = new ServerPacket(ServerOP_QSSendQuery, Query.length() + 5); + pack->WriteUInt32(Query.length()); /* Pack Query String Size so it can be dynamically broken out at queryserv */ + pack->WriteString(Query.c_str()); /* Query */ + worldserver.SendPacket(pack); + safe_delete(pack); +} + +void QueryServ::PlayerLogEvent(int Event_Type, int Character_ID, std::string Event_Desc) +{ + std::string query = StringFormat( + "INSERT INTO `qs_player_events` (event, char_id, event_desc, time) VALUES (%i, %i, '%s', UNIX_TIMESTAMP(now()))", + Event_Type, Character_ID, EscapeString(Event_Desc.c_str()).c_str()); + SendQuery(query); +} \ No newline at end of file diff --git a/zone/queryserv.h b/zone/queryserv.h new file mode 100644 index 000000000..8aafcafda --- /dev/null +++ b/zone/queryserv.h @@ -0,0 +1,35 @@ +#ifndef QUERYSERV_ZONE_H +#define QUERYSERV_ZONE_H + + +/* + enum PlayerGenericLogEventTypes + These Enums are for the generic logging table that are not complex and require more advanced logic +*/ + +enum PlayerGenericLogEventTypes { + Player_Log_Quest = 1, + Player_Log_Zoning, + Player_Log_Deaths, + Player_Log_Connect_State, + Player_Log_Levels, + Player_Log_Keyring_Addition, + Player_Log_QGlobal_Update, + Player_Log_Task_Updates, + Player_Log_AA_Purchases, + Player_Log_Trade_Skill_Events, + Player_Log_Issued_Commands, + Player_Log_Money_Transactions, + Player_Log_Alternate_Currency_Transactions, +}; + + +class QueryServ{ + public: + QueryServ(); + ~QueryServ(); + void SendQuery(std::string Query); + void PlayerLogEvent(int Event_Type, int Character_ID, std::string Event_Desc); +}; + +#endif /* QUERYSERV_ZONE_H */ \ No newline at end of file diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index d8b9094be..fc31178ae 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -75,12 +75,12 @@ And then at then end of embparser.cpp, add: #include "../common/rulesys.h" #include "qglobals.h" #include "quest_parser_collection.h" - +#include "queryserv.h" #ifdef BOTS #include "bot.h" #endif - +extern QueryServ* QServ; extern Zone* zone; extern WorldServer worldserver; extern EntityList entity_list; @@ -172,8 +172,7 @@ void QuestManager::EndQuest() { cur = QTimerList.erase(cur); else ++cur; - } - + } run.owner->Depop(); } quests_running_.pop(); @@ -1294,33 +1293,29 @@ void QuestManager::setglobal(const char *varname, const char *newvalue, int opti int qgNpcid = owner->GetNPCTypeID(); /* options value determines the availability of global variables to NPCs when a quest begins - ------------------------------------------------------------------ - value npcid player zone - ------------------------------------------------------------------ - 0 this this this - 1 all this this - 2 this all this - 3 all all this - 4 this this all - 5 all this all - 6 this all all - 7 all all all + ------------------------------------------------------------------ + value npcid player zone + ------------------------------------------------------------------ + 0 this this this + 1 all this this + 2 this all this + 3 all all this + 4 this this all + 5 all this all + 6 this all all + 7 all all all */ - if (initiator && initiator->IsClient()) // some events like waypoint and spawn don't have a player involved - { - qgCharid=initiator->CharacterID(); - } - else - { + if (initiator && initiator->IsClient()){ // some events like waypoint and spawn don't have a player involved + qgCharid=initiator->CharacterID(); + } + else { qgCharid=-qgNpcid; // make char id negative npc id as a fudge } - if (options < 0 || options > 7) - { + if (options < 0 || options > 7) { std::cerr << "Invalid options for global var " << varname << " using defaults" << std::endl; } // default = 0 (only this npcid,player and zone) - else - { + else { if (options & 1) qgNpcid=0; if (options & 2) @@ -1330,30 +1325,32 @@ void QuestManager::setglobal(const char *varname, const char *newvalue, int opti } InsertQuestGlobal(qgCharid, qgNpcid, qgZoneid, varname, newvalue, QGVarDuration(duration)); + + /* QS: PlayerLogQGlobalUpdate */ + if (RuleB(QueryServ, PlayerLogQGlobalUpdate) && qgCharid && qgCharid > 0 && initiator && initiator->IsClient()){ + std::string event_desc = StringFormat("Update :: qglobal:%s to qvalue:%s zoneid:%i instid:%i", varname, newvalue, initiator->GetZoneID(), initiator->GetInstanceID()); + QServ->PlayerLogEvent(Player_Log_QGlobal_Update, qgCharid, event_desc); + } } /* Inserts global variable into quest_globals table */ -int QuestManager::InsertQuestGlobal( - int charid, int npcid, int zoneid, - const char *varname, const char *varvalue, - int duration) -{ +int QuestManager::InsertQuestGlobal(int charid, int npcid, int zoneid, const char *varname, const char *varvalue, int duration) { char *query = 0; char errbuf[MYSQL_ERRMSG_SIZE]; // Make duration string either "unix_timestamp(now()) + xxx" or "NULL" - std::stringstream duration_ss; - if (duration == INT_MAX) - { + std::stringstream duration_ss; + if (duration == INT_MAX) { duration_ss << "NULL"; } - else - { + else { duration_ss << "unix_timestamp(now()) + " << duration; } - //NOTE: this should be escaping the contents of arglist - //npcwise a malicious script can arbitrarily alter the DB + /* + NOTE: this should be escaping the contents of arglist + npcwise a malicious script can arbitrarily alter the DB + */ uint32 last_id = 0; if (!database.RunQuery(query, MakeAnyLenString(&query, "REPLACE INTO quest_globals (charid, npcid, zoneid, name, value, expdate)" @@ -1365,9 +1362,8 @@ int QuestManager::InsertQuestGlobal( } safe_delete_array(query); - if(zone) - { - //first delete our global + if(zone) { + /* Delete existing qglobal data and update zone processes */ ServerPacket* pack = new ServerPacket(ServerOP_QGlobalDelete, sizeof(ServerQGlobalDelete_Struct)); ServerQGlobalDelete_Struct *qgd = (ServerQGlobalDelete_Struct*)pack->pBuffer; qgd->npc_id = npcid; @@ -1383,18 +1379,16 @@ int QuestManager::InsertQuestGlobal( worldserver.SendPacket(pack); safe_delete(pack); - //then create a new one with the new id + /* Create new qglobal data and update zone processes */ pack = new ServerPacket(ServerOP_QGlobalUpdate, sizeof(ServerQGlobalUpdate_Struct)); ServerQGlobalUpdate_Struct *qgu = (ServerQGlobalUpdate_Struct*)pack->pBuffer; qgu->npc_id = npcid; qgu->char_id = charid; qgu->zone_id = zoneid; - if(duration == INT_MAX) - { + if(duration == INT_MAX) { qgu->expdate = 0xFFFFFFFF; } - else - { + else { qgu->expdate = Timer::GetTimeSeconds() + duration; } strcpy((char*)qgu->name, varname); @@ -1420,8 +1414,7 @@ int QuestManager::InsertQuestGlobal( return 0; } -void QuestManager::targlobal(const char *varname, const char *value, const char *duration, int qgNpcid, int qgCharid, int qgZoneid) -{ +void QuestManager::targlobal(const char *varname, const char *value, const char *duration, int qgNpcid, int qgCharid, int qgZoneid) { InsertQuestGlobal(qgCharid, qgNpcid, qgZoneid, varname, value, QGVarDuration(duration)); } @@ -1432,15 +1425,24 @@ void QuestManager::delglobal(const char *varname) { int qgZoneid=zone->GetZoneID(); int qgCharid=0; int qgNpcid=owner->GetNPCTypeID(); + + + if (initiator && initiator->IsClient()) // some events like waypoint and spawn don't have a player involved { qgCharid=initiator->CharacterID(); } - else - { + else { qgCharid=-qgNpcid; // make char id negative npc id as a fudge } + + /* QS: PlayerLogQGlobalUpdate */ + if (RuleB(QueryServ, PlayerLogQGlobalUpdate) && qgCharid && qgCharid > 0 && initiator && initiator->IsClient()){ + std::string event_desc = StringFormat("Deleted :: qglobal:%s zoneid:%i instid:%i", varname, initiator->GetZoneID(), initiator->GetInstanceID()); + QServ->PlayerLogEvent(Player_Log_QGlobal_Update, qgCharid, event_desc); + } + if (!database.RunQuery(query, MakeAnyLenString(&query, "DELETE FROM quest_globals WHERE name='%s'" @@ -1451,8 +1453,7 @@ void QuestManager::delglobal(const char *varname) { } safe_delete_array(query); - if(zone) - { + if(zone) { ServerPacket* pack = new ServerPacket(ServerOP_QGlobalDelete, sizeof(ServerQGlobalDelete_Struct)); ServerQGlobalDelete_Struct *qgu = (ServerQGlobalDelete_Struct*)pack->pBuffer; @@ -1701,17 +1702,14 @@ void QuestManager::showgrid(int grid) { pts.push_back(pt); // Retrieve all waypoints for this grid - if(database.RunQuery(query,MakeAnyLenString(&query,"SELECT `x`,`y`,`z` FROM grid_entries WHERE `gridid`=%i AND `zoneid`=%i ORDER BY `number`",grid,zone->GetZoneID()),errbuf,&result)) - { - while((row = mysql_fetch_row(result))) - { + if(database.RunQuery(query,MakeAnyLenString(&query,"SELECT `x`,`y`,`z` FROM grid_entries WHERE `gridid`=%i AND `zoneid`=%i ORDER BY `number`",grid,zone->GetZoneID()),errbuf,&result)) { + while((row = mysql_fetch_row(result))) { pt.x = atof(row[0]); pt.y = atof(row[1]); pt.z = atof(row[2]); pts.push_back(pt); } - mysql_free_result(result); - + mysql_free_result(result); initiator->SendPathPacket(pts); } else // DB query error! diff --git a/zone/spawngroup.cpp b/zone/spawngroup.cpp index 04e37cd59..fc0360aee 100644 --- a/zone/spawngroup.cpp +++ b/zone/spawngroup.cpp @@ -28,8 +28,7 @@ extern EntityList entity_list; -SpawnEntry::SpawnEntry( uint32 in_NPCType, int in_chance, uint8 in_npc_spawn_limit ) -{ +SpawnEntry::SpawnEntry( uint32 in_NPCType, int in_chance, uint8 in_npc_spawn_limit ) { NPCType = in_NPCType; chance = in_chance; npc_spawn_limit = in_npc_spawn_limit; @@ -57,7 +56,6 @@ uint32 SpawnGroup::GetNPCType() { int npcType = 0; int totalchance = 0; - //check limits on this spawn group and npc type if(!entity_list.LimitCheckGroup(id, group_spawn_limit)) return(0); @@ -68,7 +66,6 @@ uint32 SpawnGroup::GetNPCType() { for(; cur != end; ++cur) { SpawnEntry *se = *cur; - //check limits on this spawn group and npc type if(!entity_list.LimitCheckType(se->NPCType, se->npc_spawn_limit)) continue; @@ -93,7 +90,6 @@ uint32 SpawnGroup::GetNPCType() { roll -= se->chance; } } - //CODER implement random table return npcType; } @@ -149,7 +145,6 @@ bool ZoneDatabase::LoadSpawnGroups(const char* zone_name, uint16 version, SpawnG MYSQL_RES *result; MYSQL_ROW row; - // CODER new spawn code query = 0; if (RunQuery(query, MakeAnyLenString(&query, "SELECT DISTINCT(spawngroupID), spawngroup.name, spawngroup.spawn_limit, spawngroup.dist, spawngroup.max_x, spawngroup.min_x, spawngroup.max_y, spawngroup.min_y, spawngroup.delay, spawngroup.despawn, spawngroup.despawn_timer, spawngroup.mindelay FROM spawn2,spawngroup WHERE spawn2.spawngroupID=spawngroup.ID and spawn2.version=%u and zone='%s'", version, zone_name), errbuf, &result)) { @@ -162,7 +157,7 @@ bool ZoneDatabase::LoadSpawnGroups(const char* zone_name, uint16 version, SpawnG } else { - std::cerr << "Error2 in PopulateZoneLists query '" << query << "' " << errbuf << std::endl; + _log(ZONE__SPAWNS, "Error2 in PopulateZoneLists query '%s' ", query); safe_delete_array(query); return false; } @@ -182,18 +177,17 @@ bool ZoneDatabase::LoadSpawnGroups(const char* zone_name, uint16 version, SpawnG if (sg) sg->AddSpawnEntry(newSpawnEntry); else - std::cout << "Error in SpawngroupID: " << row[0] << std::endl; + _log(ZONE__SPAWNS, "Error in LoadSpawnGroups %s ", query); } mysql_free_result(result); } else { - std::cerr << "Error3 in PopulateZoneLists query '" << query << "' " << errbuf << std::endl; + _log(ZONE__SPAWNS, "Error2 in PopulateZoneLists query '%'", query); safe_delete_array(query); return false; } - // CODER end new spawn code return true; } @@ -203,8 +197,6 @@ bool ZoneDatabase::LoadSpawnGroupsByID(int spawngroupid, SpawnGroupList* spawn_g MYSQL_RES *result; MYSQL_ROW row; - - // CODER new spawn code query = 0; if (RunQuery(query, MakeAnyLenString(&query, "SELECT DISTINCT spawngroup.id, spawngroup.name, spawngroup.spawn_limit, spawngroup.dist, spawngroup.max_x, spawngroup.min_x, spawngroup.max_y, spawngroup.min_y, spawngroup.delay, spawngroup.despawn, spawngroup.despawn_timer, spawngroup.mindelay FROM spawngroup WHERE spawngroup.ID='%i'", spawngroupid), errbuf, &result)) { @@ -217,7 +209,7 @@ bool ZoneDatabase::LoadSpawnGroupsByID(int spawngroupid, SpawnGroupList* spawn_g } else { - std::cerr << "Error2 in PopulateZoneLists query '" << query << "' " << errbuf << std::endl; + _log(ZONE__SPAWNS, "Error2 in PopulateZoneLists query %s", query); safe_delete_array(query); return false; } @@ -233,17 +225,16 @@ bool ZoneDatabase::LoadSpawnGroupsByID(int spawngroupid, SpawnGroupList* spawn_g if (sg) sg->AddSpawnEntry(newSpawnEntry); else - std::cout << "Error in SpawngroupID: " << row[0] << std::endl; + _log(ZONE__SPAWNS, "Error in SpawngroupID: %s ", row[0]); } mysql_free_result(result); } else { - std::cerr << "Error3 in PopulateZoneLists query '" << query << "' " << errbuf << std::endl; + _log(ZONE__SPAWNS, "Error3 in PopulateZoneLists query '%s'", row[0]); safe_delete_array(query); return false; } - // CODER end new spawn code return true; } diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index abc94f8f5..0431eb9b1 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -28,8 +28,6 @@ #include "../common/rulesys.h" - - int Mob::GetKickDamage() { int multiple=(GetLevel()*100/5); multiple += 100; @@ -64,11 +62,9 @@ int Mob::GetBashDamage() { } void Mob::ApplySpecialAttackMod(SkillUseTypes skill, int32 &dmg, int32 &mindmg) { - int item_slot = -1; //1: Apply bonus from AC (BOOT/SHIELD/HANDS) est. 40AC=6dmg if (IsClient()){ - switch (skill){ case SkillFlyingKick: @@ -112,10 +108,8 @@ void Mob::DoSpecialAttackDamage(Mob *who, SkillUseTypes skill, int32 max_damage, if(hate_override > -1) hate = hate_override; - if(skill == SkillBash) - { - if(IsClient()) - { + if(skill == SkillBash){ + if(IsClient()){ ItemInst *item = CastToClient()->GetInv().GetItem(MainSecondary); if(item) { @@ -186,6 +180,10 @@ void Client::OPCombatAbility(const EQApplicationPacket *app) { CombatAbility_Struct* ca_atk = (CombatAbility_Struct*) app->pBuffer; + /* Check to see if actually have skill */ + if (!MaxSkill(static_cast(ca_atk->m_skill))) + return; + if(GetTarget()->GetID() != ca_atk->m_target) return; //invalid packet. @@ -274,8 +272,7 @@ void Client::OPCombatAbility(const EQApplicationPacket *app) { return; } - if ((ca_atk->m_atk == 100) && (ca_atk->m_skill == SkillFrenzy)) - { + if ((ca_atk->m_atk == 100) && (ca_atk->m_skill == SkillFrenzy)){ CheckIncreaseSkill(SkillFrenzy, GetTarget(), 10); int AtkRounds = 3; int skillmod = 100*GetSkill(SkillFrenzy)/MaxSkill(SkillFrenzy); @@ -311,8 +308,7 @@ void Client::OPCombatAbility(const EQApplicationPacket *app) { return; } - switch(GetClass()) - { + switch(GetClass()){ case BERSERKER: case WARRIOR: case RANGER: @@ -384,8 +380,7 @@ void Client::OPCombatAbility(const EQApplicationPacket *app) { } ReuseTime = (ReuseTime*HasteMod)/100; - if(ReuseTime > 0) - { + if(ReuseTime > 0){ p_timers.Start(pTimerCombatAbility, ReuseTime); } } @@ -403,8 +398,7 @@ int Mob::MonkSpecialAttack(Mob* other, uint8 unchecked_type) SkillUseTypes skill_type; //to avoid casting... even though it "would work" uint8 itemslot = MainFeet; - switch(unchecked_type) - { + switch(unchecked_type){ case SkillFlyingKick:{ skill_type = SkillFlyingKick; max_dmg = ((GetSTR()+GetSkill(skill_type)) * RuleI(Combat, FlyingKickBonus) / 100) + 35; @@ -484,8 +478,7 @@ int Mob::MonkSpecialAttack(Mob* other, uint8 unchecked_type) else ht = ndamage = MakeRandomInt(min_dmg, max_dmg); } - else - { + else{ ht = max_dmg; } } @@ -535,7 +528,6 @@ void Mob::TryBackstab(Mob *other, int ReuseTime) { RogueBackstab(other,false,ReuseTime); if (level > 54) { - if(IsClient() && CastToClient()->CheckDoubleAttack(false)) { if(other->GetHP() > 0) @@ -619,12 +611,10 @@ void Mob::RogueBackstab(Mob* other, bool min_damage, int ReuseTime) } // determine minimum hits - if (level < 51) - { + if (level < 51) { min_hit = (level*15/10); } - else - { + else { // Trumpcard: Replaced switch statement with formula calc. This will give minhit increases all the way to 65. min_hit = (level * ( level*5 - 105)) / 100; } @@ -636,8 +626,7 @@ void Mob::RogueBackstab(Mob* other, bool min_damage, int ReuseTime) if(min_damage){ ndamage = min_hit; } - else - { + else { if (max_hit < min_hit) max_hit = min_hit; @@ -645,7 +634,6 @@ void Mob::RogueBackstab(Mob* other, bool min_damage, int ReuseTime) ndamage = max_hit; else ndamage = MakeRandomInt(min_hit, max_hit); - } } } @@ -792,28 +780,24 @@ void Client::RangedAttack(Mob* other, bool CanDoubleAttack) { return; } - SendItemAnimation(GetTarget(), AmmoItem, SkillArchery); - + SendItemAnimation(GetTarget(), AmmoItem, SkillArchery); DoArcheryAttackDmg(GetTarget(), RangeWeapon, Ammo); //EndlessQuiver AA base1 = 100% Chance to avoid consumption arrow. int ChanceAvoidConsume = aabonuses.ConsumeProjectile + itembonuses.ConsumeProjectile + spellbonuses.ConsumeProjectile; - if (!ChanceAvoidConsume || (ChanceAvoidConsume < 100 && MakeRandomInt(0,99) > ChanceAvoidConsume)){ - + if (!ChanceAvoidConsume || (ChanceAvoidConsume < 100 && MakeRandomInt(0,99) > ChanceAvoidConsume)){ DeleteItemInInventory(ammo_slot, 1, true); mlog(COMBAT__RANGED, "Consumed one arrow from slot %d", ammo_slot); } else { mlog(COMBAT__RANGED, "Endless Quiver prevented ammo consumption."); } - CheckIncreaseSkill(SkillArchery, GetTarget(), -15); - + CheckIncreaseSkill(SkillArchery, GetTarget(), -15); CommonBreakInvisible(); } -void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const ItemInst* Ammo, uint16 weapon_damage, int16 chance_mod, int16 focus, int ReuseTime) -{ +void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const ItemInst* Ammo, uint16 weapon_damage, int16 chance_mod, int16 focus, int ReuseTime) { if (!CanDoSpecialAttack(other)) return; @@ -842,8 +826,7 @@ void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Item if (focus) //From FcBaseEffects WDmg += WDmg*focus/100; - if((WDmg > 0) || (ADmg > 0)) - { + if((WDmg > 0) || (ADmg > 0)) { if(WDmg < 0) WDmg = 0; if(ADmg < 0) @@ -944,8 +927,7 @@ void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Item } //try proc on hits and misses - if((RangeWeapon != nullptr) && GetTarget() && other && !other->HasDied()) - { + if((RangeWeapon != nullptr) && GetTarget() && other && !other->HasDied()){ TryWeaponProc(RangeWeapon, other, MainRange); } @@ -965,14 +947,19 @@ void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Item void NPC::RangedAttack(Mob* other) { + + if (!other) + return; //make sure the attack and ranged timers are up //if the ranged timer is disabled, then they have no ranged weapon and shouldent be attacking anyhow - if((attack_timer.Enabled() && !attack_timer.Check(false)) || (ranged_timer.Enabled() && !ranged_timer.Check())) - { + if((attack_timer.Enabled() && !attack_timer.Check(false)) || (ranged_timer.Enabled() && !ranged_timer.Check())){ mlog(COMBAT__RANGED, "Archery canceled. Timer not up. Attack %d, ranged %d", attack_timer.GetRemainingTime(), ranged_timer.GetRemainingTime()); return; } + if(!CheckLosFN(other)) + return; + int attacks = GetSpecialAbilityParam(SPECATK_RANGED_ATK, 0); attacks = attacks > 0 ? attacks : 1; for(int i = 0; i < attacks; ++i) { @@ -1087,9 +1074,7 @@ void NPC::RangedAttack(Mob* other) } } -uint16 Mob::GetThrownDamage(int16 wDmg, int32& TotalDmg, int& minDmg) -{ - +uint16 Mob::GetThrownDamage(int16 wDmg, int32& TotalDmg, int& minDmg) { uint16 MaxDmg = (((2 * wDmg) * GetDamageTable(SkillThrowing)) / 100); if (MaxDmg == 0) @@ -1101,8 +1086,7 @@ uint16 Mob::GetThrownDamage(int16 wDmg, int32& TotalDmg, int& minDmg) TotalDmg = MakeRandomInt(1, MaxDmg); minDmg = 1; - if(GetLevel() > 25) - { + if(GetLevel() > 25){ TotalDmg += ((GetLevel()-25)/3); minDmg += ((GetLevel()-25)/3); minDmg += minDmg * GetMeleeMinDamageMod_SE(SkillThrowing) / 100; @@ -1195,9 +1179,8 @@ void Client::ThrowingAttack(Mob* other, bool CanDoubleAttack) { //old was 51 //consume ammo DeleteItemInInventory(ammo_slot, 1, true); - CheckIncreaseSkill(SkillThrowing, GetTarget()); - - CommonBreakInvisible(); + CheckIncreaseSkill(SkillThrowing, GetTarget()); + CommonBreakInvisible(); } void Mob::DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Item_Struct* item, uint16 weapon_damage, int16 chance_mod,int16 focus, int ReuseTime) @@ -1227,8 +1210,7 @@ void Mob::DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Ite if (GetClass() == ROGUE && (BehindMob(other, GetX(), GetY()))) Assassinate_Dmg = TryAssassinate(other, SkillThrowing, ranged_timer.GetDuration()); - if(WDmg > 0) - { + if(WDmg > 0){ int minDmg = 1; uint16 MaxDmg = GetThrownDamage(WDmg, TotalDmg, minDmg); @@ -1318,8 +1300,7 @@ void Mob::SendItemAnimation(Mob *to, const Item_Struct *item, SkillUseTypes skil safe_delete(outapp); } -void Mob::ProjectileAnimation(Mob* to, int item_id, bool IsArrow, float speed, float angle, float tilt, float arc, const char *IDFile, SkillUseTypes skillInUse) { - +void Mob::ProjectileAnimation(Mob* to, int item_id, bool IsArrow, float speed, float angle, float tilt, float arc, const char *IDFile, SkillUseTypes skillInUse) { if (!to) return; @@ -1453,17 +1434,11 @@ void NPC::DoClassAttacks(Mob *target) { break; case MONK: case MONKGM: { uint8 satype = SkillKick; - if(level > 29) { - satype = SkillFlyingKick; - } else if(level > 24) { - satype = SkillDragonPunch; - } else if(level > 19) { - satype = SkillEagleStrike; - } else if(level > 9) { - satype = SkillTigerClaw; - } else if(level > 4) { - satype = SkillRoundKick; - } + if(level > 29) { satype = SkillFlyingKick; } + else if(level > 24) { satype = SkillDragonPunch; } + else if(level > 19) { satype = SkillEagleStrike; } + else if(level > 9) { satype = SkillTigerClaw; } + else if(level > 4) { satype = SkillRoundKick; } reuse = MonkSpecialAttack(target, satype); reuse *= 1000; @@ -1472,8 +1447,7 @@ void NPC::DoClassAttacks(Mob *target) { } case WARRIOR: case WARRIORGM:{ if(level >= RuleI(Combat, NPCBashKickLevel)){ - if(MakeRandomInt(0, 100) > 25) //tested on live, warrior mobs both kick and bash, kick about 75% of the time, casting doesn't seem to make a difference. - { + if(MakeRandomInt(0, 100) > 25){ //tested on live, warrior mobs both kick and bash, kick about 75% of the time, casting doesn't seem to make a difference. DoAnim(animKick); int32 dmg = 0; @@ -1494,8 +1468,7 @@ void NPC::DoClassAttacks(Mob *target) { DoSpecialAttackDamage(target, SkillKick, dmg, 1, -1, reuse); did_attack = true; } - else - { + else { DoAnim(animTailRake); int32 dmg = 0; @@ -1518,8 +1491,7 @@ void NPC::DoClassAttacks(Mob *target) { } break; } - case BERSERKER: case BERSERKERGM: - { + case BERSERKER: case BERSERKERGM:{ int AtkRounds = 3; int32 max_dmg = 26 + ((GetLevel()-6) * 2); int32 min_dmg = 0; @@ -1535,8 +1507,7 @@ void NPC::DoClassAttacks(Mob *target) { reuse = FrenzyReuseTime * 1000; - while(AtkRounds > 0) { - + while(AtkRounds > 0) { if (GetTarget() && (AtkRounds == 1 || MakeRandomInt(0,100) < 75)){ DoSpecialAttackDamage(GetTarget(), SkillFrenzy, max_dmg, min_dmg, -1 , reuse, true); } @@ -1574,8 +1545,7 @@ void NPC::DoClassAttacks(Mob *target) { } case CLERIC: case CLERICGM: //clerics can bash too. case SHADOWKNIGHT: case SHADOWKNIGHTGM: - case PALADIN: case PALADINGM: - { + case PALADIN: case PALADINGM:{ if(level >= RuleI(Combat, NPCBashKickLevel)){ DoAnim(animTailRake); int32 dmg = 0; @@ -1615,8 +1585,7 @@ void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte) return; //check range for all these abilities, they are all close combat stuff - if(!CombatRange(ca_target)) - { + if(!CombatRange(ca_target)){ return; } @@ -1638,10 +1607,8 @@ void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte) uint16 skill_to_use = -1; - if (skill == -1){ - - switch(GetClass()) - { + if (skill == -1){ + switch(GetClass()){ case WARRIOR: case RANGER: case BEASTLORD: @@ -1692,14 +1659,11 @@ void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte) if(skill_to_use == -1) return; - if(skill_to_use == SkillBash) - { - if (ca_target!=this) - { + if(skill_to_use == SkillBash) { + if (ca_target!=this) { DoAnim(animTailRake); - if(GetWeaponDamage(ca_target, GetInv().GetItem(MainSecondary)) <= 0 && - GetWeaponDamage(ca_target, GetInv().GetItem(MainShoulders)) <= 0){ + if(GetWeaponDamage(ca_target, GetInv().GetItem(MainSecondary)) <= 0 && GetWeaponDamage(ca_target, GetInv().GetItem(MainShoulders)) <= 0){ dmg = -5; } else{ @@ -1720,16 +1684,14 @@ void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte) DoSpecialAttackDamage(ca_target, SkillBash, dmg, 1,-1,ReuseTime); - if(ReuseTime > 0 && !IsRiposte) - { + if(ReuseTime > 0 && !IsRiposte) { p_timers.Start(pTimerCombatAbility, ReuseTime); } } return; } - if(skill_to_use == SkillFrenzy) - { + if(skill_to_use == SkillFrenzy){ CheckIncreaseSkill(SkillFrenzy, GetTarget(), 10); int AtkRounds = 3; int skillmod = 100*GetSkill(SkillFrenzy)/MaxSkill(SkillFrenzy); @@ -1763,10 +1725,8 @@ void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte) return; } - if(skill_to_use == SkillKick) - { - if(ca_target!=this) - { + if(skill_to_use == SkillKick){ + if(ca_target!=this){ DoAnim(animKick); if(GetWeaponDamage(ca_target, GetInv().GetItem(MainFeet)) <= 0){ @@ -1790,12 +1750,7 @@ void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte) } } - if(skill_to_use == SkillFlyingKick || - skill_to_use == SkillDragonPunch || - skill_to_use == SkillEagleStrike || - skill_to_use == SkillTigerClaw || - skill_to_use == SkillRoundKick) - { + if(skill_to_use == SkillFlyingKick || skill_to_use == SkillDragonPunch || skill_to_use == SkillEagleStrike || skill_to_use == SkillTigerClaw || skill_to_use == SkillRoundKick) { ReuseTime = MonkSpecialAttack(ca_target, skill_to_use) - 1; MonkSpecialAttack(ca_target, skill_to_use); @@ -1809,8 +1764,7 @@ void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte) int MonkSPA [5] = { SkillFlyingKick, SkillDragonPunch, SkillEagleStrike, SkillTigerClaw, SkillRoundKick }; MonkSpecialAttack(ca_target, MonkSPA[MakeRandomInt(0,4)]); - int TripleChance = 25; - + int TripleChance = 25; if (bDoubleSpecialAttack > 100) TripleChance += TripleChance*(100-bDoubleSpecialAttack)/100; @@ -1820,8 +1774,7 @@ void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte) } } - if(skill_to_use == SkillBackstab) - { + if(skill_to_use == SkillBackstab){ ReuseTime = BackstabReuseTime-1; if (IsRiposte) @@ -1831,8 +1784,7 @@ void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte) } ReuseTime = (ReuseTime*HasteMod)/100; - if(ReuseTime > 0 && !IsRiposte) - { + if(ReuseTime > 0 && !IsRiposte){ p_timers.Start(pTimerCombatAbility, ReuseTime); } } @@ -1899,8 +1851,7 @@ void Mob::Taunt(NPC* who, bool always_succeed, float chance_bonus) { tauntchance /= 100.0f; - if (tauntchance > MakeRandomFloat(0, 1)) { - + if (tauntchance > MakeRandomFloat(0, 1)) { if (hate_top && hate_top != this){ newhate = (who->GetNPCHate(hate_top) - who->GetNPCHate(this)) + 1; who->CastToNPC()->AddToHateList(this, newhate); @@ -1922,8 +1873,7 @@ void Mob::Taunt(NPC* who, bool always_succeed, float chance_bonus) { if (HasSkillProcs()) TrySkillProc(who, SkillTaunt, TauntReuseTime*1000); - - + if (Success && HasSkillProcSuccess()) TrySkillProc(who, SkillTaunt, TauntReuseTime*1000, true); } @@ -1944,8 +1894,7 @@ void Mob::InstillDoubt(Mob *who) { if(!CombatRange(who)) return; - if(IsClient()) - { + if(IsClient()) { CastToClient()->CheckIncreaseSkill(SkillIntimidation, who, 10); } @@ -1975,14 +1924,12 @@ void Mob::InstillDoubt(Mob *who) { } } -uint32 Mob::TryHeadShot(Mob* defender, SkillUseTypes skillInUse) { - +uint32 Mob::TryHeadShot(Mob* defender, SkillUseTypes skillInUse) { //Only works on YOUR target. if(defender && (defender->GetBodyType() == BT_Humanoid) && !defender->IsClient() && (skillInUse == SkillArchery) && (GetTarget() == defender)) { - uint32 HeadShot_Dmg = aabonuses.HeadShot[1] + spellbonuses.HeadShot[1] + itembonuses.HeadShot[1]; - + uint32 HeadShot_Dmg = aabonuses.HeadShot[1] + spellbonuses.HeadShot[1] + itembonuses.HeadShot[1]; uint8 HeadShot_Level = 0; //Get Highest Headshot Level HeadShot_Level = aabonuses.HSLevel; if (HeadShot_Level < spellbonuses.HSLevel) @@ -1990,8 +1937,7 @@ uint32 Mob::TryHeadShot(Mob* defender, SkillUseTypes skillInUse) { else if (HeadShot_Level < itembonuses.HSLevel) HeadShot_Level = itembonuses.HSLevel; - if(HeadShot_Dmg && HeadShot_Level && (defender->GetLevel() <= HeadShot_Level)){ - + if(HeadShot_Dmg && HeadShot_Level && (defender->GetLevel() <= HeadShot_Level)){ float ProcChance = GetSpecialProcChances(MainRange); if(ProcChance > MakeRandomFloat(0,1)) return HeadShot_Dmg; @@ -2084,7 +2030,7 @@ float Mob::GetAssassinateProcChances(uint16 ReuseTime) ProcChance += ProcChance * ProcBonus / 100.0f; } else { - /*Kayen: Unable to find data on old proc rate of assassinate, no idea if our formula is real or made up.*/ + /* Kayen: Unable to find data on old proc rate of assassinate, no idea if our formula is real or made up. */ ProcChance = (10 + (static_cast(mydex/10) + static_cast(itembonuses.HeroicDEX /10)))/100.0f; } @@ -2097,8 +2043,10 @@ void Mob::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes if (!CanDoSpecialAttack(other)) return; - //For spells using skill value 98 (feral swipe ect) server sets this to 67 automatically. - //Kayen: This is unlikely to be completely accurate but use OFFENSE skill value for these effects. + /* + For spells using skill value 98 (feral swipe ect) server sets this to 67 automatically. + Kayen: This is unlikely to be completely accurate but use OFFENSE skill value for these effects. + */ if (skillinuse == SkillBegging) skillinuse = SkillOffense; @@ -2107,8 +2055,7 @@ void Mob::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes int Hand = MainPrimary; if (hate == 0 && weapon_damage > 1) hate = weapon_damage; - if(weapon_damage > 0){ - + if(weapon_damage > 0){ if (focus) //From FcBaseEffects weapon_damage += weapon_damage*focus/100; @@ -2118,12 +2065,10 @@ void Mob::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes } int32 min_hit = 1; - int32 max_hit = (2*weapon_damage*GetDamageTable(skillinuse)) / 100; - - if(GetLevel() >= 28 && IsWarriorClass() ) - { - int ucDamageBonus = GetWeaponDamageBonus((const Item_Struct*) nullptr ); + int32 max_hit = (2 * weapon_damage*GetDamageTable(skillinuse)) / 100; + if(GetLevel() >= 28 && IsWarriorClass() ) { + int ucDamageBonus = GetWeaponDamageBonus((const Item_Struct*) nullptr ); min_hit += (int) ucDamageBonus; max_hit += (int) ucDamageBonus; hate += ucDamageBonus; @@ -2142,8 +2087,7 @@ void Mob::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes } } - ApplySpecialAttackMod(skillinuse, max_hit, min_hit); - + ApplySpecialAttackMod(skillinuse, max_hit, min_hit); min_hit += min_hit * GetMeleeMinDamageMod_SE(skillinuse) / 100; if(max_hit < min_hit) @@ -2204,8 +2148,7 @@ void Mob::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes TrySkillProc(other, skillinuse, ReuseTime, true); } -bool Mob::CanDoSpecialAttack(Mob *other) -{ +bool Mob::CanDoSpecialAttack(Mob *other) { //Make sure everything is valid before doing any attacks. if (!other) { SetTarget(nullptr); @@ -2215,8 +2158,7 @@ bool Mob::CanDoSpecialAttack(Mob *other) if(!GetTarget()) SetTarget(other); - if ((other == nullptr || ((IsClient() && CastToClient()->dead) || (other->IsClient() && other->CastToClient()->dead)) - || HasDied() || (!IsAttackAllowed(other)))) { + if ((other == nullptr || ((IsClient() && CastToClient()->dead) || (other->IsClient() && other->CastToClient()->dead)) || HasDied() || (!IsAttackAllowed(other)))) { return false; } diff --git a/zone/tasks.cpp b/zone/tasks.cpp index 994ebdc5d..3da32c03c 100644 --- a/zone/tasks.cpp +++ b/zone/tasks.cpp @@ -34,18 +34,16 @@ Copyright (C) 2001-2008 EQEMu Development Team (http://eqemulator.net) #include "../common/features.h" #include "quest_parser_collection.h" #include "mob.h" +#include "queryserv.h" +extern QueryServ* QServ; TaskManager::TaskManager() { - for(int i=0; iActivityCount; j++) { @@ -62,11 +60,8 @@ TaskManager::~TaskManager() { } bool TaskManager::LoadTaskSets() { - - const char *TaskSetQuery = "SELECT `id`, `taskid` from `tasksets` WHERE `id` > 0 AND `id` < %i " "AND `taskid` >= 0 AND `taskid` < %i ORDER BY `id`, `taskid` ASC"; - const char *ERR_MYSQLERROR = "[TASKS]Error in TaskManager::LoadTaskSets: %s"; char errbuf[MYSQL_ERRMSG_SIZE]; @@ -1985,12 +1980,17 @@ void ClientTaskState::IncrementDoneCount(Client *c, TaskInformation* Task, int T // Inform the client the task has been updated, both by a chat message c->Message(0, "Your task '%s' has been updated.", Task->Title); - if(Task->Activity[ActivityID].GoalMethod != METHODQUEST) - { + if(Task->Activity[ActivityID].GoalMethod != METHODQUEST) { char buf[24]; snprintf(buf, 23, "%d %d", ActiveTasks[TaskIndex].TaskID, ActiveTasks[TaskIndex].Activity[ActivityID].ActivityID); buf[23] = '\0'; parse->EventPlayer(EVENT_TASK_STAGE_COMPLETE, c, buf, 0); + + /* QS: PlayerLogTaskUpdates :: Update */ + if (RuleB(QueryServ, PlayerLogTaskUpdates)){ + std::string event_desc = StringFormat("Task Stage Complete :: taskid:%i activityid:%i donecount:%i in zoneid:%i instid:%i", ActiveTasks[TaskIndex].TaskID, ActiveTasks[TaskIndex].Activity[ActivityID].ActivityID, ActiveTasks[TaskIndex].Activity[ActivityID].DoneCount, c->GetZoneID(), c->GetInstanceID()); + QServ->PlayerLogEvent(Player_Log_Task_Updates, c->CharacterID(), event_desc); + } } // If this task is now complete, the Completed tasks will have been @@ -2002,6 +2002,12 @@ void ClientTaskState::IncrementDoneCount(Client *c, TaskInformation* Task, int T buf[23] = '\0'; parse->EventPlayer(EVENT_TASK_COMPLETE, c, buf, 0); + /* QS: PlayerLogTaskUpdates :: Complete */ + if (RuleB(QueryServ, PlayerLogTaskUpdates)){ + std::string event_desc = StringFormat("Task Complete :: taskid:%i activityid:%i donecount:%i in zoneid:%i instid:%i", ActiveTasks[TaskIndex].TaskID, ActiveTasks[TaskIndex].Activity[ActivityID].ActivityID, ActiveTasks[TaskIndex].Activity[ActivityID].DoneCount, c->GetZoneID(), c->GetInstanceID()); + QServ->PlayerLogEvent(Player_Log_Task_Updates, c->CharacterID(), event_desc); + } + taskmanager->SendCompletedTasksToClient(c, this); c->SendTaskActivityComplete(ActiveTasks[TaskIndex].TaskID, 0, TaskIndex, false); taskmanager->SaveClientState(c, this); @@ -2011,6 +2017,7 @@ void ClientTaskState::IncrementDoneCount(Client *c, TaskInformation* Task, int T // If Experience and/or cash rewards are set, reward them from the task even if RewardMethod is METHODQUEST RewardTask(c, Task); //RemoveTask(c, TaskIndex); + } } diff --git a/zone/tradeskills.cpp b/zone/tradeskills.cpp index 4d4edb344..e8e787de0 100644 --- a/zone/tradeskills.cpp +++ b/zone/tradeskills.cpp @@ -34,6 +34,9 @@ #include "../common/string_util.h" #include "../common/rulesys.h" #include "quest_parser_collection.h" +#include "queryserv.h" + +extern QueryServ* QServ; static const SkillUseTypes TradeskillUnknown = Skill1HBlunt; /* an arbitrary non-tradeskill */ @@ -1067,7 +1070,7 @@ bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) { if(over_trivial < 0) CheckIncreaseTradeskill(bonusstat, stat_modifier, skillup_modifier, success_modifier, spec->tradeskill); - Message_StringID(4,TRADESKILL_SUCCEED,spec->name.c_str()); + Message_StringID(4, TRADESKILL_SUCCEED, spec->name.c_str()); _log(TRADESKILLS__TRACE, "Tradeskill success"); @@ -1076,16 +1079,24 @@ bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) { //should we check this crap? SummonItem(itr->first, itr->second); item = database.GetItem(itr->first); - if (this->GetGroup()) - { - entity_list.MessageGroup(this,true,MT_Skills,"%s has successfully fashioned %s!",GetName(),item->Name); + if (this->GetGroup()) { + entity_list.MessageGroup(this, true, MT_Skills, "%s has successfully fashioned %s!", GetName(), item->Name); } + + /* QS: Player_Log_Trade_Skill_Events */ + if (RuleB(QueryServ, PlayerLogTradeSkillEvents)){ + std::string event_desc = StringFormat("Success :: fashioned recipe_id:%i tskillid:%i trivial:%i chance:%4.2f in zoneid:%i instid:%i", spec->recipe_id, spec->tradeskill, spec->trivial, chance, this->GetZoneID(), this->GetInstanceID()); + QServ->PlayerLogEvent(Player_Log_Trade_Skill_Events, this->CharacterID(), event_desc); + } + if(RuleB(TaskSystem, EnableTaskSystem)) UpdateTasksForItem(ActivityTradeSkill, itr->first, itr->second); ++itr; } return(true); - } else { + } + /* Tradeskill Fail */ + else { success_modifier = 2; // Halves the chance if(over_trivial < 0) @@ -1097,6 +1108,13 @@ bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) { if (this->GetGroup()) { entity_list.MessageGroup(this,true,MT_Skills,"%s was unsuccessful in %s tradeskill attempt.",GetName(),this->GetGender() == 0 ? "his" : this->GetGender() == 1 ? "her" : "its"); + + } + + /* QS: Player_Log_Trade_Skill_Events */ + if (RuleB(QueryServ, PlayerLogTradeSkillEvents)){ + std::string event_desc = StringFormat("Failed :: recipe_id:%i tskillid:%i trivial:%i chance:%4.2f in zoneid:%i instid:%i", spec->recipe_id, spec->tradeskill, spec->trivial, chance, this->GetZoneID(), this->GetInstanceID()); + QServ->PlayerLogEvent(Player_Log_Trade_Skill_Events, this->CharacterID(), event_desc); } itr = spec->onfail.begin(); @@ -1106,6 +1124,8 @@ bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) { ++itr; } + /* Salvage Item rolls */ + // Rolls on each item, is possible to return everything int SalvageChance = aabonuses.SalvageChance + itembonuses.SalvageChance + spellbonuses.SalvageChance; // Skip check if not a normal TS or if a quest recipe these should be nofail, but check amyways diff --git a/zone/trap.cpp b/zone/trap.cpp index badd14c6c..44c177118 100644 --- a/zone/trap.cpp +++ b/zone/trap.cpp @@ -262,45 +262,36 @@ Mob* EntityList::GetTrapTrigger(Trap* trap) { //todo: rewrite this to not need direct access to trap members. bool ZoneDatabase::LoadTraps(const char* zonename, int16 version) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - // int char_num = 0; - unsigned long* lengths; - - if (RunQuery(query, MakeAnyLenString(&query, "SELECT id,x,y,z,effect,effectvalue,effectvalue2,skill,maxzdiff,radius,chance,message,respawn_time,respawn_var,level FROM traps WHERE zone='%s' AND version=%u", zonename, version), errbuf, &result)) { - safe_delete_array(query); - while ((row = mysql_fetch_row(result))) - { - lengths = mysql_fetch_lengths(result); - Trap* trap = new Trap(); - trap->trap_id = atoi(row[0]); - trap->x = atof(row[1]); - trap->y = atof(row[2]); - trap->z = atof(row[3]); - trap->effect = atoi(row[4]); - trap->effectvalue = atoi(row[5]); - trap->effectvalue2 = atoi(row[6]); - trap->skill = atoi(row[7]); - trap->maxzdiff = atof(row[8]); - trap->radius = atof(row[9]); - trap->chance = atoi(row[10]); - trap->message = row[11]; - trap->respawn_time = atoi(row[12]); - trap->respawn_var = atoi(row[13]); - trap->level = atoi(row[14]); - entity_list.AddTrap(trap); - trap->CreateHiddenTrigger(); - } - mysql_free_result(result); - } - else { - LogFile->write(EQEMuLog::Error, "Error in LoadTraps query '%s': %s", query, errbuf); - safe_delete_array(query); + std::string query = StringFormat("SELECT id, x, y, z, effect, effectvalue, effectvalue2, skill, " + "maxzdiff, radius, chance, message, respawn_time, respawn_var, level " + "FROM traps WHERE zone='%s' AND version=%u", zonename, version); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in LoadTraps query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); return false; - } + } + + for (auto row = results.begin(); row != results.end(); ++row) { + Trap* trap = new Trap(); + trap->trap_id = atoi(row[0]); + trap->x = atof(row[1]); + trap->y = atof(row[2]); + trap->z = atof(row[3]); + trap->effect = atoi(row[4]); + trap->effectvalue = atoi(row[5]); + trap->effectvalue2 = atoi(row[6]); + trap->skill = atoi(row[7]); + trap->maxzdiff = atof(row[8]); + trap->radius = atof(row[9]); + trap->chance = atoi(row[10]); + trap->message = row[11]; + trap->respawn_time = atoi(row[12]); + trap->respawn_var = atoi(row[13]); + trap->level = atoi(row[14]); + entity_list.AddTrap(trap); + trap->CreateHiddenTrigger(); + } return true; } diff --git a/zone/tribute.cpp b/zone/tribute.cpp index c70afc992..95da9b5a3 100644 --- a/zone/tribute.cpp +++ b/zone/tribute.cpp @@ -378,68 +378,60 @@ void Client::SendGuildTributes() { } bool ZoneDatabase::LoadTributes() { - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; - TributeData t; - memset(&t.tiers, 0, sizeof(t.tiers)); - t.tier_count = 0; + TributeData tributeData; + memset(&tributeData.tiers, 0, sizeof(tributeData.tiers)); + tributeData.tier_count = 0; tribute_list.clear(); - const char *query = "SELECT id,name,descr,unknown,isguild FROM tributes"; - if (RunQuery(query, strlen(query), errbuf, &result)) { - int r; - while ((row = mysql_fetch_row(result))) { - r = 0; - uint32 id = atoul(row[r++]); - t.name = row[r++]; - t.description = row[r++]; - t.unknown = strtoul(row[r++], nullptr, 10); - t.is_guild = atol(row[r++])==0?false:true; - - tribute_list[id] = t; - } - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error in LoadTributes first query '%s': %s", query, errbuf); + const std::string query = "SELECT id, name, descr, unknown, isguild FROM tributes"; + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in LoadTributes first query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); return false; } + for (auto row = results.begin(); row != results.end(); ++row) { + uint32 id = atoul(row[0]); + tributeData.name = row[1]; + tributeData.description = row[2]; + tributeData.unknown = strtoul(row[3], nullptr, 10); + tributeData.is_guild = atol(row[4]) == 0? false: true; - const char *query2 = "SELECT tribute_id,level,cost,item_id FROM tribute_levels ORDER BY tribute_id,level"; - if (RunQuery(query2, strlen(query2), errbuf, &result)) { - int r; - while ((row = mysql_fetch_row(result))) { - r = 0; - uint32 id = atoul(row[r++]); + tribute_list[id] = tributeData; + } - if(tribute_list.count(id) != 1) { - LogFile->write(EQEMuLog::Error, "Error in LoadTributes: unknown tribute %lu in tribute_levels", (unsigned long)id); - continue; - } - - TributeData &cur = tribute_list[id]; - - if(cur.tier_count >= MAX_TRIBUTE_TIERS) { - LogFile->write(EQEMuLog::Error, "Error in LoadTributes: on tribute %lu: more tiers defined than permitted", (unsigned long)id); - continue; - } - - TributeLevel_Struct &s = cur.tiers[cur.tier_count]; - - s.level = atoul(row[r++]); - s.cost = atoul(row[r++]); - s.tribute_item_id = atoul(row[r++]); - cur.tier_count++; - } - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error in LoadTributes level query '%s': %s", query, errbuf); + const std::string query2 = "SELECT tribute_id, level, cost, item_id FROM tribute_levels ORDER BY tribute_id, level"; + results = QueryDatabase(query2); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in LoadTributes level query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); return false; } + for (auto row = results.begin(); row != results.end(); ++row) { + uint32 id = atoul(row[0]); + + if(tribute_list.count(id) != 1) { + LogFile->write(EQEMuLog::Error, "Error in LoadTributes: unknown tribute %lu in tribute_levels", (unsigned long)id); + continue; + } + + TributeData &cur = tribute_list[id]; + + if(cur.tier_count >= MAX_TRIBUTE_TIERS) { + LogFile->write(EQEMuLog::Error, "Error in LoadTributes: on tribute %lu: more tiers defined than permitted", (unsigned long)id); + continue; + } + + TributeLevel_Struct &s = cur.tiers[cur.tier_count]; + + s.level = atoul(row[1]); + s.cost = atoul(row[2]); + s.tribute_item_id = atoul(row[3]); + cur.tier_count++; + } + return true; } diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 92bf3cc22..787e3a94e 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -3226,3 +3226,11 @@ bool ZoneDatabase::GetFactionIdsForNPC(uint32 nfl_id, std::listGetZoneID(), this->GetInstanceID()); + QServ->PlayerLogEvent(Player_Log_Zoning, this->CharacterID(), event_desc); + } + + /* Dont clear aggro until the zone is successful */ entity_list.RemoveFromHateLists(this); if(this->GetPet()) entity_list.RemoveFromHateLists(this->GetPet()); - LogFile->write(EQEMuLog::Status, "Zoning '%s' to: %s (%i) - (%i) x=%f, y=%f, z=%f", - m_pp.name, database.GetZoneName(zone_id), zone_id, instance_id, - dest_x, dest_y, dest_z); + LogFile->write(EQEMuLog::Status, "Zoning '%s' to: %s (%i) - (%i) x=%f, y=%f, z=%f", m_pp.name, database.GetZoneName(zone_id), zone_id, instance_id, dest_x, dest_y, dest_z); //set the player's coordinates in the new zone so they have them //when they zone into it