diff --git a/sql/characters.sql b/sql/characters.sql index 0876bc6ef..6bff1691c 100644 --- a/sql/characters.sql +++ b/sql/characters.sql @@ -21,7 +21,7 @@ DROP TABLE IF EXISTS `character_db_version`; CREATE TABLE `character_db_version` ( - `required_8339_02_characters_character_battleground_data` bit(1) default NULL + `required_8402_02_characters_guild_bank_eventlog` bit(1) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Last applied sql update to DB'; -- @@ -935,16 +935,16 @@ UNLOCK TABLES; DROP TABLE IF EXISTS `guild_bank_eventlog`; CREATE TABLE `guild_bank_eventlog` ( - `guildid` int(11) unsigned NOT NULL default '0', - `LogGuid` int(11) unsigned NOT NULL default '0', - `LogEntry` tinyint(1) unsigned NOT NULL default '0', - `TabId` tinyint(1) unsigned NOT NULL default '0', + `guildid` int(11) unsigned NOT NULL default '0' COMMENT 'Guild Identificator', + `LogGuid` int(11) unsigned NOT NULL default '0' COMMENT 'Log record identificator - auxiliary column', + `TabId` tinyint(3) unsigned NOT NULL default '0' COMMENT 'Guild bank TabId', + `EventType` tinyint(3) unsigned NOT NULL default '0' COMMENT 'Event type', `PlayerGuid` int(11) unsigned NOT NULL default '0', `ItemOrMoney` int(11) unsigned NOT NULL default '0', `ItemStackCount` tinyint(3) unsigned NOT NULL default '0', - `DestTabId` tinyint(1) unsigned NOT NULL default '0', - `TimeStamp` bigint(20) unsigned NOT NULL default '0', - PRIMARY KEY (`guildid`,`LogGuid`), + `DestTabId` tinyint(1) unsigned NOT NULL default '0' COMMENT 'Destination Tab Id', + `TimeStamp` bigint(20) unsigned NOT NULL default '0' COMMENT 'Event UNIX time', + PRIMARY KEY (`guildid`,`LogGuid`,`TabId`), KEY `guildid_key` (`guildid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; @@ -1036,12 +1036,13 @@ UNLOCK TABLES; DROP TABLE IF EXISTS `guild_eventlog`; CREATE TABLE `guild_eventlog` ( `guildid` int(11) NOT NULL COMMENT 'Guild Identificator', - `LogGuid` int(11) NOT NULL COMMENT 'Log entry identificator', + `LogGuid` int(11) NOT NULL COMMENT 'Log record identificator - auxiliary column', `EventType` tinyint(1) NOT NULL COMMENT 'Event type', `PlayerGuid1` int(11) NOT NULL COMMENT 'Player 1', `PlayerGuid2` int(11) NOT NULL COMMENT 'Player 2', `NewRank` tinyint(2) NOT NULL COMMENT 'New rank(in case promotion/demotion)', - `TimeStamp` bigint(20) NOT NULL COMMENT 'Event UNIX time' + `TimeStamp` bigint(20) NOT NULL COMMENT 'Event UNIX time', + PRIMARY KEY (`guildid`, `LogGuid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT 'Guild Eventlog'; -- diff --git a/sql/mangos.sql b/sql/mangos.sql index 415bef0d3..49fc0ed79 100644 --- a/sql/mangos.sql +++ b/sql/mangos.sql @@ -24,7 +24,7 @@ CREATE TABLE `db_version` ( `version` varchar(120) default NULL, `creature_ai_version` varchar(120) default NULL, `cache_id` int(10) default '0', - `required_8394_01_mangos_spell_proc_event` bit(1) default NULL + `required_8399_01_mangos_spell_elixir` bit(1) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Used DB version notes'; -- @@ -15382,14 +15382,6 @@ INSERT INTO spell_chain VALUES (30356,25258,23922,6,0), (47487,30356,23922,7,0), (47488,47487,23922,8,0), -/*SunderArmor*/ -(7386,0,7386,1,0), -(7405,7386,7386,2,0), -(8380,7405,7386,3,0), -(11596,8380,7386,4,0), -(11597,11596,7386,5,0), -(25225,11597,7386,6,0), -(47467,25225,7386,7,0), /*------------------ -- (267) Protection (Paladin) ------------------*/ @@ -16974,12 +16966,19 @@ INSERT INTO `spell_elixir` VALUES (17627,0x3), (17629,0x3), (17628,0x3), +(18191,0x10), +(18192,0x10), +(18193,0x10), +(18194,0x10), +(18222,0x10), (21920,0x1), +(22730,0x10), (24361,0x2), (24363,0x2), (24382,0x2), (24383,0x2), (24417,0x2), +(25661,0x10), (26276,0x1), (27652,0x2), (27653,0x2), @@ -17022,6 +17021,8 @@ INSERT INTO `spell_elixir` VALUES (45373,0x1), (46837,0xB), (46839,0xB); + + /*!40000 ALTER TABLE `spell_elixir` ENABLE KEYS */; UNLOCK TABLES; @@ -18104,11 +18105,9 @@ INSERT INTO `spell_threat` VALUES (7373,141), (7379,235), (7386,100), -(7405,140), (8198,40), (8204,64), (8205,96), -(8380,180), (8972,118), (9745,148), (9880,178), @@ -18120,8 +18119,6 @@ INSERT INTO `spell_threat` VALUES (11567,145), (11580,143), (11581,180), -(11596,220), -(11597,261), (11600,275), (11601,315), (11775,395), @@ -18142,7 +18139,6 @@ INSERT INTO `spell_threat` VALUES (23925,250), (24394,580), (24583,5), -(25225,300), (25231,130), (25258,286), (25264,215), diff --git a/sql/updates/8397_01_mangos_spell_chain.sql b/sql/updates/8397_01_mangos_spell_chain.sql new file mode 100644 index 000000000..9f6143147 --- /dev/null +++ b/sql/updates/8397_01_mangos_spell_chain.sql @@ -0,0 +1,3 @@ +ALTER TABLE db_version CHANGE COLUMN required_8394_01_mangos_spell_proc_event required_8397_01_mangos_spell_chain bit; + +DELETE FROM spell_chain WHERE first_spell=7386; diff --git a/sql/updates/8397_02_mangos_spell_threat.sql b/sql/updates/8397_02_mangos_spell_threat.sql new file mode 100644 index 000000000..332b459c0 --- /dev/null +++ b/sql/updates/8397_02_mangos_spell_threat.sql @@ -0,0 +1,3 @@ +ALTER TABLE db_version CHANGE COLUMN required_8397_01_mangos_spell_chain required_8397_02_mangos_spell_threat bit; + +DELETE FROM spell_threat WHERE entry IN (7405,8380,11596,11597,25225); diff --git a/sql/updates/8397_03_characters_character_spell.sql b/sql/updates/8397_03_characters_character_spell.sql new file mode 100644 index 000000000..ee4c1dfe5 --- /dev/null +++ b/sql/updates/8397_03_characters_character_spell.sql @@ -0,0 +1,5 @@ +ALTER TABLE character_db_version CHANGE COLUMN required_8339_02_characters_character_battleground_data required_8397_03_characters_character_spell bit; + +UPDATE IGNORE character_spell SET spell=7386 WHERE spell IN (7405,8380,11596,11597,25225,47467); +UPDATE character_spell SET active=1 WHERE spell=7386; +DELETE FROM character_spell WHERE spell IN (7405,8380,11596,11597,25225,47467); diff --git a/sql/updates/8399_01_mangos_spell_elixir.sql b/sql/updates/8399_01_mangos_spell_elixir.sql new file mode 100644 index 000000000..50eae6424 --- /dev/null +++ b/sql/updates/8399_01_mangos_spell_elixir.sql @@ -0,0 +1,12 @@ +ALTER TABLE db_version CHANGE COLUMN required_8397_02_mangos_spell_threat required_8399_01_mangos_spell_elixir bit; + +-- Well Fed (SPELLFAMILY_POTION) +DELETE FROM spell_elixir WHERE entry IN (18191, 18192, 18193, 18194, 18222, 22730, 25661); +INSERT INTO spell_elixir (entry, mask) VALUES +(18191,0x10), +(18192,0x10), +(18193,0x10), +(18194,0x10), +(18222,0x10), +(22730,0x10), +(25661,0x10); diff --git a/sql/updates/8402_01_characters_guild_eventlog.sql b/sql/updates/8402_01_characters_guild_eventlog.sql new file mode 100644 index 000000000..32f5b09a9 --- /dev/null +++ b/sql/updates/8402_01_characters_guild_eventlog.sql @@ -0,0 +1,20 @@ +ALTER TABLE character_db_version CHANGE COLUMN required_8397_03_characters_character_spell required_8402_01_characters_guild_eventlog bit; + + +-- THIS SCRIPT DELETES table `guild_eventlog` - MAKE BACKUP, if you need it. + +DROP TABLE IF EXISTS `guild_eventlog`; +CREATE TABLE `guild_eventlog` ( + `guildid` int(11) NOT NULL COMMENT 'Guild Identificator', + `LogGuid` int(11) NOT NULL COMMENT 'Log record identificator - auxiliary column', + `EventType` tinyint(1) NOT NULL COMMENT 'Event type', + `PlayerGuid1` int(11) NOT NULL COMMENT 'Player 1', + `PlayerGuid2` int(11) NOT NULL COMMENT 'Player 2', + `NewRank` tinyint(2) NOT NULL COMMENT 'New rank(in case promotion/demotion)', + `TimeStamp` bigint(20) NOT NULL COMMENT 'Event UNIX time', + PRIMARY KEY (`guildid`, `LogGuid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT 'Guild Eventlog'; + +-- The reason i decided for such dramatic change is that old guild_eventlog table didn't have Primary key and +-- used LogGuids from 0 to infinity +-- New system uses LogGuids from 0 to number defined in config. \ No newline at end of file diff --git a/sql/updates/8402_02_characters_guild_bank_eventlog.sql b/sql/updates/8402_02_characters_guild_bank_eventlog.sql new file mode 100644 index 000000000..e2c271373 --- /dev/null +++ b/sql/updates/8402_02_characters_guild_bank_eventlog.sql @@ -0,0 +1,23 @@ +ALTER TABLE character_db_version CHANGE COLUMN required_8402_01_characters_guild_eventlog required_8402_02_characters_guild_bank_eventlog bit; + + +-- THIS SCRIPT DELETES table `guild_bank_eventlog` - MAKE BACKUP, if you need it. + +DROP TABLE IF EXISTS `guild_bank_eventlog`; +CREATE TABLE `guild_bank_eventlog` ( + `guildid` int(11) unsigned NOT NULL default '0' COMMENT 'Guild Identificator', + `LogGuid` int(11) unsigned NOT NULL default '0' COMMENT 'Log record identificator - auxiliary column', + `TabId` tinyint(3) unsigned NOT NULL default '0' COMMENT 'Guild bank TabId', + `EventType` tinyint(3) unsigned NOT NULL default '0' COMMENT 'Event type', + `PlayerGuid` int(11) unsigned NOT NULL default '0', + `ItemOrMoney` int(11) unsigned NOT NULL default '0', + `ItemStackCount` tinyint(3) unsigned NOT NULL default '0', + `DestTabId` tinyint(1) unsigned NOT NULL default '0' COMMENT 'Destination Tab Id', + `TimeStamp` bigint(20) unsigned NOT NULL default '0' COMMENT 'Event UNIX time', + PRIMARY KEY (`guildid`,`LogGuid`,`TabId`), + KEY `guildid_key` (`guildid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- The reason i decided for such dramatic change is that old guild_bank_eventlog table used `TabId` = 0 for Money events and +-- used `LogGuid` from 0 to infinity +-- New system uses `LogGuid` from 0 to number defined in config. \ No newline at end of file diff --git a/sql/updates/Makefile.am b/sql/updates/Makefile.am index 71168d47d..c07c1b667 100644 --- a/sql/updates/Makefile.am +++ b/sql/updates/Makefile.am @@ -86,6 +86,12 @@ pkgdata_DATA = \ 8392_01_mangos_spell_proc_event.sql \ 8392_02_mangos_spell_chain.sql \ 8394_01_mangos_spell_proc_event.sql \ + 8397_01_mangos_spell_chain.sql \ + 8397_02_mangos_spell_threat.sql \ + 8397_03_characters_character_spell.sql \ + 8399_01_mangos_spell_elixir.sql \ + 8402_01_characters_guild_eventlog.sql \ + 8402_02_characters_guild_bank_eventlog.sql \ README ## Additional files to include when running 'make dist' @@ -152,4 +158,10 @@ EXTRA_DIST = \ 8392_01_mangos_spell_proc_event.sql \ 8392_02_mangos_spell_chain.sql \ 8394_01_mangos_spell_proc_event.sql \ + 8397_01_mangos_spell_chain.sql \ + 8397_02_mangos_spell_threat.sql \ + 8397_03_characters_character_spell.sql \ + 8399_01_mangos_spell_elixir.sql \ + 8402_01_characters_guild_eventlog.sql \ + 8402_02_characters_guild_bank_eventlog.sql \ README diff --git a/src/game/BattleGroundMgr.cpp b/src/game/BattleGroundMgr.cpp index ab2a847b8..bb21a854b 100644 --- a/src/game/BattleGroundMgr.cpp +++ b/src/game/BattleGroundMgr.cpp @@ -1079,7 +1079,7 @@ bool BGQueueRemoveEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) sBattleGroundMgr.m_BattleGroundQueues[m_BgQueueTypeId].RemovePlayer(m_PlayerGuid, true); //update queues if battleground isn't ended if (bg) - sBattleGroundMgr.m_BattleGroundQueues[m_BgQueueTypeId].Update(m_BgTypeId, bg->GetQueueId()); + sBattleGroundMgr.ScheduleQueueUpdate(m_BgQueueTypeId, m_BgTypeId, bg->GetQueueId()); WorldPacket data; sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_NONE, 0, 0, 0); @@ -1163,6 +1163,22 @@ void BattleGroundMgr::Update(uint32 diff) } } } + + // update scheduled queues + if (!m_QueueUpdateScheduler.empty()) + { + //copy vector and clear the other + std::vector scheduled(m_QueueUpdateScheduler); + m_QueueUpdateScheduler.clear(); + for (uint8 i = 0; i < scheduled.size(); i++) + { + BattleGroundQueueTypeId bgQueueTypeId = BattleGroundQueueTypeId(scheduled[i] / 65536); + BattleGroundTypeId bgTypeId = BattleGroundTypeId((scheduled[i] % 65536) / 256); + BGQueueIdBasedOnLevel queue_id = BGQueueIdBasedOnLevel(scheduled[i] % 256); + m_BattleGroundQueues[bgQueueTypeId].Update(bgTypeId, queue_id); + } + } + // if rating difference counts, maybe force-update queues if (sWorld.getConfig(CONFIG_ARENA_MAX_RATING_DIFFERENCE) && sWorld.getConfig(CONFIG_ARENA_RATING_DISCARD_TIMER)) { @@ -2005,6 +2021,24 @@ void BattleGroundMgr::ToggleArenaTesting() sWorld.SendWorldText(LANG_DEBUG_ARENA_OFF); } +void BattleGroundMgr::ScheduleQueueUpdate(BattleGroundQueueTypeId bgQueueTypeId, BattleGroundTypeId bgTypeId, BGQueueIdBasedOnLevel queue_id) +{ + //This method must be atomic! + //we will use only 1 number created of bgTypeId and queue_id + uint32 schedule_id = (bgQueueTypeId * 65536) + (bgTypeId * 256) + queue_id; + bool found = false; + for (uint8 i = 0; i < m_QueueUpdateScheduler.size(); i++) + { + if (m_QueueUpdateScheduler[i] == schedule_id) + { + found = true; + break; + } + } + if (!found) + m_QueueUpdateScheduler.push_back(schedule_id); +} + uint32 BattleGroundMgr::GetMaxRatingDifference() const { // this is for stupid people who can't use brain and set max rating difference to 0 diff --git a/src/game/BattleGroundMgr.h b/src/game/BattleGroundMgr.h index 329a3772e..2b65b76c7 100644 --- a/src/game/BattleGroundMgr.h +++ b/src/game/BattleGroundMgr.h @@ -217,6 +217,7 @@ class BattleGroundMgr BGFreeSlotQueueType BGFreeSlotQueue[MAX_BATTLEGROUND_TYPE_ID]; + void ScheduleQueueUpdate(BattleGroundQueueTypeId bgQueueTypeId, BattleGroundTypeId bgTypeId, BGQueueIdBasedOnLevel queue_id); uint32 GetMaxRatingDifference() const; uint32 GetRatingDiscardTimer() const; uint32 GetPrematureFinishTime() const; @@ -248,6 +249,7 @@ class BattleGroundMgr /* Battlegrounds */ BattleGroundSet m_BattleGrounds[MAX_BATTLEGROUND_TYPE_ID]; + std::vectorm_QueueUpdateScheduler; std::set m_ClientBattleGroundIds[MAX_BATTLEGROUND_TYPE_ID][MAX_BATTLEGROUND_QUEUES]; //the instanceids just visible for the client uint32 m_NextRatingDiscardUpdate; time_t m_NextAutoDistributionTime; diff --git a/src/game/Guild.cpp b/src/game/Guild.cpp index ccfe5b9d1..761e834bd 100644 --- a/src/game/Guild.cpp +++ b/src/game/Guild.cpp @@ -27,22 +27,34 @@ #include "SocialMgr.h" #include "Util.h" #include "Language.h" +#include "World.h" Guild::Guild() { - Id = 0; - name = ""; - leaderGuid = 0; + m_Id = 0; + m_Name = ""; + m_LeaderGuid = 0; GINFO = MOTD = ""; - EmblemStyle = 0; - EmblemColor = 0; - BorderStyle = 0; - BorderColor = 0; - BackgroundColor = 0; + m_EmblemStyle = 0; + m_EmblemColor = 0; + m_BorderStyle = 0; + m_BorderColor = 0; + m_BackgroundColor = 0; - CreatedYear = 0; - CreatedMonth = 0; - CreatedDay = 0; + m_CreatedYear = 0; + m_CreatedMonth = 0; + m_CreatedDay = 0; + + m_EventLogLoaded = false; + m_GuildBankLoaded = false; + m_OnlineMembers = 0; + m_GuildBankMoney = 0; + m_PurchasedTabs = 0; + + m_GuildEventLogNextGuid = 0; + m_GuildBankEventLogNextGuid_Money = 0; + for (uint8 i = 0; i < GUILD_BANK_MAX_TABS; i++) + m_GuildBankEventLogNextGuid_Item[i] = 0; } Guild::~Guild() @@ -50,7 +62,7 @@ Guild::~Guild() } -bool Guild::create(Player* leader, std::string gname) +bool Guild::Create(Player* leader, std::string gname) { if(objmgr.GetGuildByName(gname)) return false; @@ -59,15 +71,15 @@ bool Guild::create(Player* leader, std::string gname) if(!lSession) return false; - leaderGuid = leader->GetGUID(); - name = gname; + m_LeaderGuid = leader->GetGUID(); + m_Name = gname; GINFO = ""; MOTD = "No message set."; - guildbank_money = 0; - purchased_tabs = 0; - Id = objmgr.GenerateGuildId(); + m_GuildBankMoney = 0; + m_PurchasedTabs = 0; + m_Id = objmgr.GenerateGuildId(); - sLog.outDebug("GUILD: creating guild %s to leader: %u", gname.c_str(), GUID_LOPART(leaderGuid)); + sLog.outDebug("GUILD: creating guild %s to leader: %u", gname.c_str(), GUID_LOPART(m_LeaderGuid)); // gname already assigned to Guild::name, use it to encode string for DB CharacterDatabase.escape_string(gname); @@ -79,20 +91,27 @@ bool Guild::create(Player* leader, std::string gname) CharacterDatabase.BeginTransaction(); // CharacterDatabase.PExecute("DELETE FROM guild WHERE guildid='%u'", Id); - MAX(guildid)+1 not exist - CharacterDatabase.PExecute("DELETE FROM guild_rank WHERE guildid='%u'", Id); - CharacterDatabase.PExecute("DELETE FROM guild_member WHERE guildid='%u'", Id); + CharacterDatabase.PExecute("DELETE FROM guild_member WHERE guildid='%u'", m_Id); CharacterDatabase.PExecute("INSERT INTO guild (guildid,name,leaderguid,info,motd,createdate,EmblemStyle,EmblemColor,BorderStyle,BorderColor,BackgroundColor,BankMoney) " "VALUES('%u','%s','%u', '%s', '%s', NOW(),'%u','%u','%u','%u','%u','" UI64FMTD "')", - Id, gname.c_str(), GUID_LOPART(leaderGuid), dbGINFO.c_str(), dbMOTD.c_str(), EmblemStyle, EmblemColor, BorderStyle, BorderColor, BackgroundColor, guildbank_money); + m_Id, gname.c_str(), GUID_LOPART(m_LeaderGuid), dbGINFO.c_str(), dbMOTD.c_str(), m_EmblemStyle, m_EmblemColor, m_BorderStyle, m_BorderColor, m_BackgroundColor, m_GuildBankMoney); CharacterDatabase.CommitTransaction(); - CreateRank(lSession->GetMangosString(LANG_GUILD_MASTER), GR_RIGHT_ALL); - CreateRank(lSession->GetMangosString(LANG_GUILD_OFFICER), GR_RIGHT_ALL); - CreateRank(lSession->GetMangosString(LANG_GUILD_VETERAN), GR_RIGHT_GCHATLISTEN | GR_RIGHT_GCHATSPEAK); - CreateRank(lSession->GetMangosString(LANG_GUILD_MEMBER), GR_RIGHT_GCHATLISTEN | GR_RIGHT_GCHATSPEAK); - CreateRank(lSession->GetMangosString(LANG_GUILD_INITIATE),GR_RIGHT_GCHATLISTEN | GR_RIGHT_GCHATSPEAK); + CreateDefaultGuildRanks(lSession->GetSessionDbLocaleIndex()); - return AddMember(leaderGuid, (uint32)GR_GUILDMASTER); + return AddMember(m_LeaderGuid, (uint32)GR_GUILDMASTER); +} + +void Guild::CreateDefaultGuildRanks(int locale_idx) +{ + CharacterDatabase.PExecute("DELETE FROM guild_rank WHERE guildid='%u'", m_Id); + CharacterDatabase.PExecute("DELETE FROM guild_bank_right WHERE guildid = '%u'", m_Id); + + CreateRank(objmgr.GetMangosString(LANG_GUILD_MASTER, locale_idx), GR_RIGHT_ALL); + CreateRank(objmgr.GetMangosString(LANG_GUILD_OFFICER, locale_idx), GR_RIGHT_ALL); + CreateRank(objmgr.GetMangosString(LANG_GUILD_VETERAN, locale_idx), GR_RIGHT_GCHATLISTEN | GR_RIGHT_GCHATSPEAK); + CreateRank(objmgr.GetMangosString(LANG_GUILD_MEMBER, locale_idx), GR_RIGHT_GCHATLISTEN | GR_RIGHT_GCHATSPEAK); + CreateRank(objmgr.GetMangosString(LANG_GUILD_INITIATE, locale_idx), GR_RIGHT_GCHATLISTEN | GR_RIGHT_GCHATSPEAK); } bool Guild::AddMember(uint64 plGuid, uint32 plRank) @@ -116,8 +135,33 @@ bool Guild::AddMember(uint64 plGuid, uint32 plRank) // fill player data MemberSlot newmember; - if(!FillPlayerData(plGuid, &newmember)) // problems with player data collection - return false; + if(pl) + { + newmember.name = pl->GetName(); + newmember.zoneId = pl->GetZoneId(); + newmember.level = pl->getLevel(); + newmember.Class = pl->getClass(); + } + else + { + QueryResult *result = CharacterDatabase.PQuery("SELECT name,zone,level,class FROM characters WHERE guid = '%u'", GUID_LOPART(plGuid)); + if(!result) + return false; // player doesn't exist + + Field *fields = result->Fetch(); + newmember.name = fields[0].GetCppString(); + newmember.zoneId = fields[1].GetUInt32(); + newmember.level = fields[2].GetUInt32(); + newmember.Class = fields[3].GetUInt32(); + delete result; + if(newmember.level < 1 || newmember.level > STRONG_MAX_LEVEL + || !newmember.zoneId + || newmember.Class < CLASS_WARRIOR || newmember.Class >= MAX_CLASSES) + { + sLog.outError("Player (GUID: %u) has a broken data in field `characters` table, cannot add him to guild.",GUID_LOPART(plGuid)); + return false; + } + } newmember.RankId = plRank; newmember.OFFnote = (std::string)""; @@ -134,12 +178,12 @@ bool Guild::AddMember(uint64 plGuid, uint32 plRank) CharacterDatabase.escape_string(dbOFFnote); CharacterDatabase.PExecute("INSERT INTO guild_member (guildid,guid,rank,pnote,offnote) VALUES ('%u', '%u', '%u','%s','%s')", - Id, GUID_LOPART(plGuid), newmember.RankId, dbPnote.c_str(), dbOFFnote.c_str()); + m_Id, GUID_LOPART(plGuid), newmember.RankId, dbPnote.c_str(), dbOFFnote.c_str()); // If player not in game data in data field will be loaded from guild tables, no need to update it!! if(pl) { - pl->SetInGuild(Id); + pl->SetInGuild(m_Id); pl->SetRank(newmember.RankId); pl->SetGuildIdInvited(0); } @@ -152,7 +196,7 @@ void Guild::SetMOTD(std::string motd) // motd now can be used for encoding to DB CharacterDatabase.escape_string(motd); - CharacterDatabase.PExecute("UPDATE guild SET motd='%s' WHERE guildid='%u'", motd.c_str(), Id); + CharacterDatabase.PExecute("UPDATE guild SET motd='%s' WHERE guildid='%u'", motd.c_str(), m_Id); } void Guild::SetGINFO(std::string ginfo) @@ -161,26 +205,29 @@ void Guild::SetGINFO(std::string ginfo) // ginfo now can be used for encoding to DB CharacterDatabase.escape_string(ginfo); - CharacterDatabase.PExecute("UPDATE guild SET info='%s' WHERE guildid='%u'", ginfo.c_str(), Id); + CharacterDatabase.PExecute("UPDATE guild SET info='%s' WHERE guildid='%u'", ginfo.c_str(), m_Id); } bool Guild::LoadGuildFromDB(uint32 GuildId) { - if(!LoadRanksFromDB(GuildId)) - return false; - - if(!LoadMembersFromDB(GuildId)) - return false; + //set m_Id in case guild data are broken in DB and Guild will be Disbanded (deleted from DB) + m_Id = GuildId; QueryResult *result = CharacterDatabase.PQuery("SELECT MAX(TabId) FROM guild_bank_tab WHERE guildid='%u'", GuildId); if(result) { Field *fields = result->Fetch(); - purchased_tabs = fields[0].GetUInt8()+1; // Because TabId begins at 0 + m_PurchasedTabs = fields[0].GetUInt8() + 1; // Because TabId begins at 0 + if (m_PurchasedTabs > GUILD_BANK_MAX_TABS) + m_PurchasedTabs = GUILD_BANK_MAX_TABS; delete result; } - else - purchased_tabs = 0; + + if(!LoadRanksFromDB(GuildId)) + return false; + + if(!LoadMembersFromDB(GuildId)) + return false; LoadBankRightsFromDB(GuildId); // Must be after LoadRanksFromDB because it populates rank struct @@ -194,94 +241,110 @@ bool Guild::LoadGuildFromDB(uint32 GuildId) Field *fields = result->Fetch(); - Id = fields[0].GetUInt32(); - name = fields[1].GetCppString(); - leaderGuid = MAKE_NEW_GUID(fields[2].GetUInt32(), 0, HIGHGUID_PLAYER); + m_Id = fields[0].GetUInt32(); + m_Name = fields[1].GetCppString(); + m_LeaderGuid = MAKE_NEW_GUID(fields[2].GetUInt32(), 0, HIGHGUID_PLAYER); - EmblemStyle = fields[3].GetUInt32(); - EmblemColor = fields[4].GetUInt32(); - BorderStyle = fields[5].GetUInt32(); - BorderColor = fields[6].GetUInt32(); - BackgroundColor = fields[7].GetUInt32(); + m_EmblemStyle = fields[3].GetUInt32(); + m_EmblemColor = fields[4].GetUInt32(); + m_BorderStyle = fields[5].GetUInt32(); + m_BorderColor = fields[6].GetUInt32(); + m_BackgroundColor = fields[7].GetUInt32(); GINFO = fields[8].GetCppString(); MOTD = fields[9].GetCppString(); uint64 time = fields[10].GetUInt64(); //datetime is uint64 type ... YYYYmmdd:hh:mm:ss - guildbank_money = fields[11].GetUInt64(); + m_GuildBankMoney = fields[11].GetUInt64(); delete result; uint64 dTime = time /1000000; - CreatedDay = dTime%100; - CreatedMonth = (dTime/100)%100; - CreatedYear = (dTime/10000)%10000; + m_CreatedDay = dTime%100; + m_CreatedMonth = (dTime/100)%100; + m_CreatedYear = (dTime/10000)%10000; - // If the leader does not exist attempt to promote another member - if(!objmgr.GetPlayerAccountIdByGUID(leaderGuid )) + // Repair the structure of guild + // If the guildmaster does not exist attempt to promote another member + // or guildmaster isn't present in guild + int32 GM_rights = GetRank(GUID_LOPART(m_LeaderGuid)); + if(!objmgr.GetPlayerAccountIdByGUID(m_LeaderGuid) || GM_rights == -1) { - DelMember(leaderGuid); - + DelMember(m_LeaderGuid); // check no members case (disbanded) if(members.empty()) return false; } + else if (GM_rights != GR_GUILDMASTER) + SetLeader(m_LeaderGuid); + + // Allow only 1 guildmaster + for (MemberList::const_iterator itr = members.begin(); itr != members.end(); ++itr) + { + if (itr->second.RankId == GR_GUILDMASTER && GUID_LOPART(m_LeaderGuid) != itr->first) + //set right of member to officer + ChangeRank(itr->first, GR_OFFICER); + } + + sLog.outDebug("Guild %u Creation time Loaded day: %u, month: %u, year: %u", GuildId, m_CreatedDay, m_CreatedMonth, m_CreatedYear); - sLog.outDebug("Guild %u Creation time Loaded day: %u, month: %u, year: %u", GuildId, CreatedDay, CreatedMonth, CreatedYear); - m_bankloaded = false; - m_eventlogloaded = false; - m_onlinemembers = 0; - RenumBankLogs(); - RenumGuildEventlog(); return true; } bool Guild::LoadRanksFromDB(uint32 GuildId) { Field *fields; - QueryResult *result = CharacterDatabase.PQuery("SELECT rname,rights,BankMoneyPerDay,rid FROM guild_rank WHERE guildid = '%u' ORDER BY rid ASC", GuildId); + // 0 1 2 3 + QueryResult *result = CharacterDatabase.PQuery("SELECT rid,rname,rights,BankMoneyPerDay FROM guild_rank WHERE guildid = '%u' ORDER BY rid ASC", GuildId); if(!result) return false; bool broken_ranks = false; + //GUILD RANKS are sequence starting from 0 = GUILD_MASTER (ALL PRIVILEGES) to max 9 (lowest privileges) + //the lower rank id is considered higher rank - so promotion does rank-- and demotion does rank++ + //between ranks in sequence cannot be gaps - so 0,1,2,4 cannot be + //min ranks count is 5 and max is 10. + do { fields = result->Fetch(); - std::string rankName = fields[0].GetCppString(); - uint32 rankRights = fields[1].GetUInt32(); - uint32 rankMoney = fields[2].GetUInt32(); - uint32 rankRID = fields[3].GetUInt32(); + uint32 rankID = fields[0].GetUInt32(); + std::string rankName = fields[1].GetCppString(); + uint32 rankRights = fields[2].GetUInt32(); + uint32 rankMoney = fields[3].GetUInt32(); - if(rankRID != m_ranks.size()+1) // guild_rank.rid always store rank+1 + if(rankID != m_Ranks.size()) // guild_rank.ids are sequence 0,1,2,3.. broken_ranks = true; - if(m_ranks.size()==GR_GUILDMASTER) // prevent loss leader rights + //first rank is guildmaster, prevent loss leader rights + if(m_Ranks.empty()) rankRights |= GR_RIGHT_ALL; AddRank(rankName,rankRights,rankMoney); }while( result->NextRow() ); delete result; - if(m_ranks.size()==0) // empty rank table? + if(m_Ranks.size() < GUILD_RANKS_MIN_COUNT) // if too few ranks, renew them { - AddRank("Guild Master",GR_RIGHT_ALL,0); - broken_ranks = true; + m_Ranks.clear(); + sLog.outError("Guild %u has broken `guild_rank` data, creating new...",GuildId); + CreateDefaultGuildRanks(0); // 0 is default locale_idx + broken_ranks = false; } - // guild_rank have wrong numbered ranks, repair if(broken_ranks) { - sLog.outError("Guild %u have broken `guild_rank` data, repairing...",GuildId); + sLog.outError("Guild %u has broken `guild_rank` data, repairing...",GuildId); CharacterDatabase.BeginTransaction(); CharacterDatabase.PExecute("DELETE FROM guild_rank WHERE guildid='%u'", GuildId); - for(size_t i =0; i < m_ranks.size(); ++i) + for(size_t i = 0; i < m_Ranks.size(); ++i) { // guild_rank.rid always store rank+1 - std::string name = m_ranks[i].name; - uint32 rights = m_ranks[i].rights; + std::string name = m_Ranks[i].name; + uint32 rights = m_Ranks[i].rights; CharacterDatabase.escape_string(name); - CharacterDatabase.PExecute( "INSERT INTO guild_rank (guildid,rid,rname,rights) VALUES ('%u', '%u', '%s', '%u')", GuildId, uint32(i+1), name.c_str(), rights); + CharacterDatabase.PExecute( "INSERT INTO guild_rank (guildid,rid,rname,rights) VALUES ('%u', '%u', '%s', '%u')", GuildId, uint32(i), name.c_str(), rights); } CharacterDatabase.CommitTransaction(); } @@ -297,8 +360,9 @@ bool Guild::LoadMembersFromDB(uint32 GuildId) "BankResetTimeTab0, BankRemSlotsTab0, BankResetTimeTab1, BankRemSlotsTab1, BankResetTimeTab2, BankRemSlotsTab2," // 12 13 14 15 16 17 "BankResetTimeTab3, BankRemSlotsTab3, BankResetTimeTab4, BankRemSlotsTab4, BankResetTimeTab5, BankRemSlotsTab5," - // 18 - "logout_time FROM guild_member LEFT JOIN characters ON characters.guid = guild_member.guid WHERE guildid = '%u'", GuildId); + // 18 19 20 21 22 + "characters.name, characters.level, characters.zone, characters.class, characters.logout_time " + "FROM guild_member LEFT JOIN characters ON characters.guid = guild_member.guid WHERE guildid = '%u'", GuildId); if(!result) return false; @@ -307,12 +371,11 @@ bool Guild::LoadMembersFromDB(uint32 GuildId) { Field *fields = result->Fetch(); MemberSlot newmember; - newmember.RankId = fields[1].GetUInt32(); uint64 guid = MAKE_NEW_GUID(fields[0].GetUInt32(), 0, HIGHGUID_PLAYER); - - // Player does not exist - if(!FillPlayerData(guid, &newmember)) - continue; + newmember.RankId = fields[1].GetUInt32(); + //don't allow member to have not existing rank! + if (newmember.RankId >= m_Ranks.size()) + newmember.RankId = GetLowestRank(); newmember.Pnote = fields[2].GetCppString(); newmember.OFFnote = fields[3].GetCppString(); @@ -323,7 +386,33 @@ bool Guild::LoadMembersFromDB(uint32 GuildId) newmember.BankResetTimeTab[i] = fields[6+(2*i)].GetUInt32(); newmember.BankRemSlotsTab[i] = fields[7+(2*i)].GetUInt32(); } - newmember.logout_time = fields[18].GetUInt64(); + + newmember.name = fields[18].GetCppString(); + newmember.level = fields[19].GetUInt32(); + newmember.zoneId = fields[20].GetUInt32(); + newmember.Class = fields[21].GetUInt32(); + newmember.logout_time = fields[22].GetUInt64(); + + if(newmember.level < 1 || newmember.level > STRONG_MAX_LEVEL) // can be at broken `data` field + { + sLog.outError("Player (GUID: %u) has a broken data in field `characters`.`data`, deleting him from guild!",GUID_LOPART(guid)); + CharacterDatabase.PExecute("DELETE FROM guild_member WHERE guid = '%u'", GUID_LOPART(guid)); + continue; + } + if(!newmember.zoneId) + { + sLog.outError("Player (GUID: %u) has broken zone-data", GUID_LOPART(guid)); + // here it will also try the same, to get the zone from characters-table, but additional it tries to find + // the zone through xy coords .. this is a bit redundant, but shouldn't be called often + newmember.zoneId = Player::GetZoneIdFromDB(guid); + } + if(newmember.Class < CLASS_WARRIOR || newmember.Class >= MAX_CLASSES) // can be at broken `class` field + { + sLog.outError("Player (GUID: %u) has a broken data in field `characters`.`class`, deleting him from guild!",GUID_LOPART(guid)); + CharacterDatabase.PExecute("DELETE FROM guild_member WHERE guid = '%u'", GUID_LOPART(guid)); + continue; + } + members[GUID_LOPART(guid)] = newmember; }while( result->NextRow() ); @@ -335,66 +424,7 @@ bool Guild::LoadMembersFromDB(uint32 GuildId) return true; } -bool Guild::FillPlayerData(uint64 guid, MemberSlot* memslot) -{ - std::string plName; - uint32 plLevel; - uint32 plClass; - uint32 plZone; - - Player* pl = objmgr.GetPlayer(guid); - if(pl) - { - plName = pl->GetName(); - plLevel = pl->getLevel(); - plClass = pl->getClass(); - plZone = pl->GetZoneId(); - } - else - { - QueryResult *result = CharacterDatabase.PQuery("SELECT name,level,zone,class FROM characters WHERE guid = '%u'", GUID_LOPART(guid)); - if(!result) - return false; // player doesn't exist - - Field *fields = result->Fetch(); - - plName = fields[0].GetCppString(); - plLevel = fields[1].GetUInt32(); - plZone = fields[2].GetUInt32(); - plClass = fields[3].GetUInt32(); - delete result; - - if(plLevel<1||plLevel>STRONG_MAX_LEVEL) // can be at broken `data` field - { - sLog.outError("Player (GUID: %u) has a broken data in field `characters`.`data`.",GUID_LOPART(guid)); - return false; - } - - if(!plZone) - { - sLog.outError("Player (GUID: %u) has broken zone-data",GUID_LOPART(guid)); - //here it will also try the same, to get the zone from characters-table, but additional it tries to find - plZone = Player::GetZoneIdFromDB(guid); - //the zone through xy coords.. this is a bit redundant, but - //shouldn't be called often - } - - if(plClass=MAX_CLASSES) // can be at broken `class` field - { - sLog.outError("Player (GUID: %u) has a broken data in field `characters`.`class`.",GUID_LOPART(guid)); - return false; - } - } - - memslot->name = plName; - memslot->level = plLevel; - memslot->Class = plClass; - memslot->zoneId = plZone; - - return(true); -} - -void Guild::LoadPlayerStatsByGuid(uint64 guid) +void Guild::SetMemberStats(uint64 guid) { MemberList::iterator itr = members.find(GUID_LOPART(guid)); if (itr == members.end() ) @@ -403,22 +433,25 @@ void Guild::LoadPlayerStatsByGuid(uint64 guid) Player *pl = ObjectAccessor::FindPlayer(guid); if(!pl) return; - itr->second.name = pl->GetName(); - itr->second.level = pl->getLevel(); - itr->second.Class = pl->getClass(); + itr->second.name = pl->GetName(); + itr->second.level = pl->getLevel(); + itr->second.Class = pl->getClass(); + itr->second.zoneId = pl->GetZoneId(); } void Guild::SetLeader(uint64 guid) { - leaderGuid = guid; + m_LeaderGuid = guid; ChangeRank(guid, GR_GUILDMASTER); - CharacterDatabase.PExecute("UPDATE guild SET leaderguid='%u' WHERE guildid='%u'", GUID_LOPART(guid), Id); + CharacterDatabase.PExecute("UPDATE guild SET leaderguid='%u' WHERE guildid='%u'", GUID_LOPART(guid), m_Id); } void Guild::DelMember(uint64 guid, bool isDisbanding) { - if(leaderGuid == guid && !isDisbanding) + //guild master can be deleted when loading guild and guid doesn't exist in characters table + //or when he is removed from guild by gm command + if(m_LeaderGuid == guid && !isDisbanding) { MemberSlot* oldLeader = NULL; MemberSlot* best = NULL; @@ -579,76 +612,79 @@ void Guild::BroadcastPacketToRank(WorldPacket *packet, uint32 rankId) void Guild::CreateRank(std::string name_,uint32 rights) { - if(m_ranks.size() >= GUILD_MAX_RANKS) + if(m_Ranks.size() >= GUILD_RANKS_MAX_COUNT) return; - AddRank(name_,rights,0); + // ranks are sequence 0,1,2,... where 0 means guildmaster + uint32 new_rank_id = m_Ranks.size(); - for (int i = 0; i < purchased_tabs; ++i) + AddRank(name_, rights, 0); + + //existing records in db should be deleted before calling this procedure and m_PurchasedTabs must be loaded already + + for (uint32 i = 0; i < m_PurchasedTabs; ++i) { - CreateBankRightForTab(m_ranks.size()-1, uint8(i)); + //create bank rights with 0 + CharacterDatabase.PExecute("INSERT INTO guild_bank_right (guildid,TabId,rid) VALUES ('%u','%u','%u')", m_Id, i, new_rank_id); } - - // guild_rank.rid always store rank+1 value - // name now can be used for encoding to DB CharacterDatabase.escape_string(name_); - CharacterDatabase.PExecute( "INSERT INTO guild_rank (guildid,rid,rname,rights) VALUES ('%u', '%u', '%s', '%u')", Id, (unsigned int)m_ranks.size(), name_.c_str(), rights ); + CharacterDatabase.PExecute( "INSERT INTO guild_rank (guildid,rid,rname,rights) VALUES ('%u', '%u', '%s', '%u')", m_Id, new_rank_id, name_.c_str(), rights ); } void Guild::AddRank(const std::string& name_,uint32 rights, uint32 money) { - m_ranks.push_back(RankInfo(name_,rights,money)); + m_Ranks.push_back(RankInfo(name_,rights,money)); } void Guild::DelRank() { - if(m_ranks.empty()) + if(m_Ranks.empty()) return; - // guild_rank.rid always store rank+1 value - uint32 rank = m_ranks.size()-1; - CharacterDatabase.PExecute("DELETE FROM guild_rank WHERE rid>='%u' AND guildid='%u'", (rank+1), Id); + // delete lowest guild_rank + uint32 rank = GetLowestRank(); + CharacterDatabase.PExecute("DELETE FROM guild_rank WHERE rid>='%u' AND guildid='%u'", rank, m_Id); - m_ranks.pop_back(); + m_Ranks.pop_back(); } std::string Guild::GetRankName(uint32 rankId) { - if(rankId >= m_ranks.size()) + if(rankId >= m_Ranks.size()) return ""; - return m_ranks[rankId].name; + return m_Ranks[rankId].name; } uint32 Guild::GetRankRights(uint32 rankId) { - if(rankId >= m_ranks.size()) + if(rankId >= m_Ranks.size()) return 0; - return m_ranks[rankId].rights; + return m_Ranks[rankId].rights; } void Guild::SetRankName(uint32 rankId, std::string name_) { - if(rankId >= m_ranks.size()) + if(rankId >= m_Ranks.size()) return; - m_ranks[rankId].name = name_; + m_Ranks[rankId].name = name_; // name now can be used for encoding to DB CharacterDatabase.escape_string(name_); - CharacterDatabase.PExecute("UPDATE guild_rank SET rname='%s' WHERE rid='%u' AND guildid='%u'", name_.c_str(), (rankId+1), Id); + CharacterDatabase.PExecute("UPDATE guild_rank SET rname='%s' WHERE rid='%u' AND guildid='%u'", name_.c_str(), (rankId+1), m_Id); } void Guild::SetRankRights(uint32 rankId, uint32 rights) { - if(rankId >= m_ranks.size()) + if(rankId >= m_Ranks.size()) return; - m_ranks[rankId].rights = rights; + m_Ranks[rankId].rights = rights; - CharacterDatabase.PExecute("UPDATE guild_rank SET rights='%u' WHERE rid='%u' AND guildid='%u'", rights, (rankId+1), Id); + CharacterDatabase.PExecute("UPDATE guild_rank SET rights='%u' WHERE rid='%u' AND guildid='%u'", rights, (rankId+1), m_Id); } int32 Guild::GetRank(uint32 LowGuid) @@ -673,27 +709,27 @@ void Guild::Disband() } CharacterDatabase.BeginTransaction(); - CharacterDatabase.PExecute("DELETE FROM guild WHERE guildid = '%u'",Id); - CharacterDatabase.PExecute("DELETE FROM guild_rank WHERE guildid = '%u'",Id); - CharacterDatabase.PExecute("DELETE FROM guild_bank_tab WHERE guildid = '%u'",Id); - CharacterDatabase.PExecute("DELETE FROM guild_bank_item WHERE guildid = '%u'",Id); - CharacterDatabase.PExecute("DELETE FROM guild_bank_right WHERE guildid = '%u'",Id); - CharacterDatabase.PExecute("DELETE FROM guild_bank_eventlog WHERE guildid = '%u'",Id); - CharacterDatabase.PExecute("DELETE FROM guild_eventlog WHERE guildid = '%u'",Id); + CharacterDatabase.PExecute("DELETE FROM guild WHERE guildid = '%u'", m_Id); + CharacterDatabase.PExecute("DELETE FROM guild_rank WHERE guildid = '%u'", m_Id); + CharacterDatabase.PExecute("DELETE FROM guild_bank_tab WHERE guildid = '%u'", m_Id); + CharacterDatabase.PExecute("DELETE FROM guild_bank_item WHERE guildid = '%u'", m_Id); + CharacterDatabase.PExecute("DELETE FROM guild_bank_right WHERE guildid = '%u'", m_Id); + CharacterDatabase.PExecute("DELETE FROM guild_bank_eventlog WHERE guildid = '%u'", m_Id); + CharacterDatabase.PExecute("DELETE FROM guild_eventlog WHERE guildid = '%u'", m_Id); CharacterDatabase.CommitTransaction(); - objmgr.RemoveGuild(Id); + objmgr.RemoveGuild(m_Id); } void Guild::Roster(WorldSession *session) { // we can only guess size - WorldPacket data(SMSG_GUILD_ROSTER, (4+MOTD.length()+1+GINFO.length()+1+4+m_ranks.size()*(4+4+GUILD_BANK_MAX_TABS*(4+4))+members.size()*50)); + WorldPacket data(SMSG_GUILD_ROSTER, (4+MOTD.length()+1+GINFO.length()+1+4+m_Ranks.size()*(4+4+GUILD_BANK_MAX_TABS*(4+4))+members.size()*50)); data << (uint32)members.size(); data << MOTD; data << GINFO; - data << (uint32)m_ranks.size(); - for (RankList::const_iterator ritr = m_ranks.begin(); ritr != m_ranks.end(); ++ritr) + data << (uint32)m_Ranks.size(); + for (RankList::const_iterator ritr = m_Ranks.begin(); ritr != m_Ranks.end(); ++ritr) { data << uint32(ritr->rights); data << uint32(ritr->BankMoneyPerDay); // count of: withdraw gold(gold/day) Note: in game set gold, in packet set bronze. @@ -741,22 +777,22 @@ void Guild::Query(WorldSession *session) { WorldPacket data(SMSG_GUILD_QUERY_RESPONSE, (8*32+200));// we can only guess size - data << Id; - data << name; + data << m_Id; + data << m_Name; for (size_t i = 0 ; i < 10; ++i) // show always 10 ranks { - if(i < m_ranks.size()) - data << m_ranks[i].name; + if(i < m_Ranks.size()) + data << m_Ranks[i].name; else data << (uint8)0; // null string } - data << uint32(EmblemStyle); - data << uint32(EmblemColor); - data << uint32(BorderStyle); - data << uint32(BorderColor); - data << uint32(BackgroundColor); + data << uint32(m_EmblemStyle); + data << uint32(m_EmblemColor); + data << uint32(m_BorderStyle); + data << uint32(m_BorderColor); + data << uint32(m_BackgroundColor); data << uint32(0); // something new in WotLK session->SendPacket( &data ); @@ -765,13 +801,13 @@ void Guild::Query(WorldSession *session) void Guild::SetEmblem(uint32 emblemStyle, uint32 emblemColor, uint32 borderStyle, uint32 borderColor, uint32 backgroundColor) { - EmblemStyle = emblemStyle; - EmblemColor = emblemColor; - BorderStyle = borderStyle; - BorderColor = borderColor; - BackgroundColor = backgroundColor; + m_EmblemStyle = emblemStyle; + m_EmblemColor = emblemColor; + m_BorderStyle = borderStyle; + m_BorderColor = borderColor; + m_BackgroundColor = backgroundColor; - CharacterDatabase.PExecute("UPDATE guild SET EmblemStyle=%u, EmblemColor=%u, BorderStyle=%u, BorderColor=%u, BackgroundColor=%u WHERE guildid = %u", EmblemStyle, EmblemColor, BorderStyle, BorderColor, BackgroundColor, Id); + CharacterDatabase.PExecute("UPDATE guild SET EmblemStyle=%u, EmblemColor=%u, BorderStyle=%u, BorderColor=%u, BackgroundColor=%u WHERE guildid = %u", m_EmblemStyle, m_EmblemColor, m_BorderStyle, m_BorderColor, m_BackgroundColor, m_Id); } void Guild::UpdateLogoutTime(uint64 guid) @@ -782,12 +818,12 @@ void Guild::UpdateLogoutTime(uint64 guid) itr->second.logout_time = time(NULL); - if (m_onlinemembers > 0) - --m_onlinemembers; + if (m_OnlineMembers > 0) + --m_OnlineMembers; else { UnloadGuildBank(); - UnloadGuildEventlog(); + UnloadGuildEventLog(); } } @@ -795,17 +831,17 @@ void Guild::UpdateLogoutTime(uint64 guid) // Guild Eventlog part // ************************************************* // Display guild eventlog -void Guild::DisplayGuildEventlog(WorldSession *session) +void Guild::DisplayGuildEventLog(WorldSession *session) { // Load guild eventlog, if not already done - if (!m_eventlogloaded) + if (!m_EventLogLoaded) LoadGuildEventLogFromDB(); // Sending result WorldPacket data(MSG_GUILD_EVENT_LOG_QUERY, 0); // count, max count == 100 - data << uint8(m_GuildEventlog.size()); - for (GuildEventlog::const_iterator itr = m_GuildEventlog.begin(); itr != m_GuildEventlog.end(); ++itr) + data << uint8(m_GuildEventLog.size()); + for (GuildEventLog::const_iterator itr = m_GuildEventLog.begin(); itr != m_GuildEventLog.end(); ++itr) { // Event type data << uint8(itr->EventType); @@ -828,82 +864,76 @@ void Guild::DisplayGuildEventlog(WorldSession *session) void Guild::LoadGuildEventLogFromDB() { // Return if already loaded - if (m_eventlogloaded) + if (m_EventLogLoaded) return; - QueryResult *result = CharacterDatabase.PQuery("SELECT LogGuid, EventType, PlayerGuid1, PlayerGuid2, NewRank, TimeStamp FROM guild_eventlog WHERE guildid=%u ORDER BY LogGuid DESC LIMIT %u", Id, GUILD_EVENTLOG_MAX_ENTRIES); + // 0 1 2 3 4 5 + QueryResult *result = CharacterDatabase.PQuery("SELECT LogGuid, EventType, PlayerGuid1, PlayerGuid2, NewRank, TimeStamp FROM guild_eventlog WHERE guildid=%u ORDER BY TimeStamp DESC,LogGuid DESC LIMIT %u", m_Id, GUILD_EVENTLOG_MAX_RECORDS); if(!result) return; + bool isNextLogGuidSet = false; + //uint32 configCount = sWorld.getConfig(CONFIG_GUILD_EVENT_LOG_COUNT); + // First event in list will be the oldest and the latest event is last event in list do { Field *fields = result->Fetch(); - GuildEventlogEntry NewEvent; + if (!isNextLogGuidSet) + { + m_GuildEventLogNextGuid = fields[0].GetUInt32(); + isNextLogGuidSet = true; + } // Fill entry - NewEvent.LogGuid = fields[0].GetUInt32(); + GuildEventLogEntry NewEvent; NewEvent.EventType = fields[1].GetUInt8(); NewEvent.PlayerGuid1 = fields[2].GetUInt32(); NewEvent.PlayerGuid2 = fields[3].GetUInt32(); NewEvent.NewRank = fields[4].GetUInt8(); NewEvent.TimeStamp = fields[5].GetUInt64(); - // Add entry to map - m_GuildEventlog.push_front(NewEvent); + + // There can be a problem if more events have same TimeStamp the ORDER can be broken when fields[0].GetUInt32() == configCount, but + // events with same timestamp can appear when there is lag, and we naivly suppose that mangos isn't laggy + // but if problem appears, player will see set of guild events that have same timestamp in bad order + + // Add entry to list + m_GuildEventLog.push_front(NewEvent); } while( result->NextRow() ); delete result; - // Check lists size in case to many event entries in db - // This cases can happen only if a crash occured somewhere and table has too many log entries - if (!m_GuildEventlog.empty()) - CharacterDatabase.PExecute("DELETE FROM guild_eventlog WHERE guildid=%u AND LogGuid < %u", Id, m_GuildEventlog.front().LogGuid); - - m_eventlogloaded = true; + m_EventLogLoaded = true; } // Unload guild eventlog -void Guild::UnloadGuildEventlog() +void Guild::UnloadGuildEventLog() { - if (!m_eventlogloaded) + if (!m_EventLogLoaded) return; - m_GuildEventlog.clear(); - m_eventlogloaded = false; -} - -// This will renum guids used at load to prevent always going up until infinit -void Guild::RenumGuildEventlog() -{ - QueryResult *result = CharacterDatabase.PQuery("SELECT Min(LogGuid), Max(LogGuid) FROM guild_eventlog WHERE guildid = %u", Id); - if(!result) - return; - - Field *fields = result->Fetch(); - CharacterDatabase.PExecute("UPDATE guild_eventlog SET LogGuid=LogGuid-%u+1 WHERE guildid=%u ORDER BY LogGuid %s",fields[0].GetUInt32(), Id, fields[0].GetUInt32()?"ASC":"DESC"); - GuildEventlogMaxGuid = fields[1].GetUInt32()+1; - delete result; + m_GuildEventLog.clear(); + m_EventLogLoaded = false; } // Add entry to guild eventlog void Guild::LogGuildEvent(uint8 EventType, uint32 PlayerGuid1, uint32 PlayerGuid2, uint8 NewRank) { - GuildEventlogEntry NewEvent; - // Fill entry - NewEvent.LogGuid = GuildEventlogMaxGuid++; + GuildEventLogEntry NewEvent; + // Create event NewEvent.EventType = EventType; NewEvent.PlayerGuid1 = PlayerGuid1; NewEvent.PlayerGuid2 = PlayerGuid2; NewEvent.NewRank = NewRank; NewEvent.TimeStamp = uint32(time(NULL)); - // Check max entry limit and delete from db if needed - if (m_GuildEventlog.size() > GUILD_EVENTLOG_MAX_ENTRIES) - { - CharacterDatabase.PExecute("DELETE FROM guild_eventlog WHERE guildid='%u' AND LogGuid='%u'", Id, m_GuildEventlog.front().LogGuid); - m_GuildEventlog.pop_front(); - } - // Add entry to map - m_GuildEventlog.push_back(NewEvent); - // Add new eventlog entry into DB + // Count new LogGuid + m_GuildEventLogNextGuid = (m_GuildEventLogNextGuid + 1) % sWorld.getConfig(CONFIG_GUILD_EVENT_LOG_COUNT); + // Check max records limit + if (m_GuildEventLog.size() >= GUILD_EVENTLOG_MAX_RECORDS) + m_GuildEventLog.pop_front(); + // Add event to list + m_GuildEventLog.push_back(NewEvent); + // Save event to DB + CharacterDatabase.PExecute("DELETE FROM guild_eventlog WHERE guildid='%u' AND LogGuid='%u'", m_Id, m_GuildEventLogNextGuid); CharacterDatabase.PExecute("INSERT INTO guild_eventlog (guildid, LogGuid, EventType, PlayerGuid1, PlayerGuid2, NewRank, TimeStamp) VALUES ('%u','%u','%u','%u','%u','%u','" UI64FMTD "')", - Id, NewEvent.LogGuid, uint32(NewEvent.EventType), NewEvent.PlayerGuid1, NewEvent.PlayerGuid2, uint32(NewEvent.NewRank), NewEvent.TimeStamp); + m_Id, m_GuildEventLogNextGuid, uint32(NewEvent.EventType), NewEvent.PlayerGuid1, NewEvent.PlayerGuid2, uint32(NewEvent.NewRank), NewEvent.TimeStamp); } // ************************************************* @@ -1052,7 +1082,7 @@ Item* Guild::GetItem(uint8 TabId, uint8 SlotId) void Guild::DisplayGuildBankTabsInfo(WorldSession *session) { // Time to load bank if not already done - if (!m_bankloaded) + if (!m_GuildBankLoaded) LoadGuildBankFromDB(); WorldPacket data(SMSG_GUILD_BANK_LIST, 500); @@ -1062,9 +1092,9 @@ void Guild::DisplayGuildBankTabsInfo(WorldSession *session) data << uint32(0xFFFFFFFF); // bit 9 must be set for this packet to work data << uint8(1); // Tell Client this is a TabInfo packet - data << uint8(purchased_tabs); // here is the number of tabs + data << uint8(m_PurchasedTabs); // here is the number of tabs - for(int i = 0; i < purchased_tabs; ++i) + for(int i = 0; i < m_PurchasedTabs; ++i) { data << m_TabListMap[i]->Name.c_str(); data << m_TabListMap[i]->Icon.c_str(); @@ -1077,19 +1107,19 @@ void Guild::DisplayGuildBankTabsInfo(WorldSession *session) void Guild::CreateNewBankTab() { - if (purchased_tabs >= GUILD_BANK_MAX_TABS) + if (m_PurchasedTabs >= GUILD_BANK_MAX_TABS) return; - ++purchased_tabs; + ++m_PurchasedTabs; GuildBankTab* AnotherTab = new GuildBankTab; memset(AnotherTab->Slots, 0, GUILD_BANK_MAX_SLOTS * sizeof(Item*)); - m_TabListMap.resize(purchased_tabs); - m_TabListMap[purchased_tabs-1] = AnotherTab; + m_TabListMap.resize(m_PurchasedTabs); + m_TabListMap[m_PurchasedTabs-1] = AnotherTab; CharacterDatabase.BeginTransaction(); - CharacterDatabase.PExecute("DELETE FROM guild_bank_tab WHERE guildid='%u' AND TabId='%u'", Id, uint32(purchased_tabs-1)); - CharacterDatabase.PExecute("INSERT INTO guild_bank_tab (guildid,TabId) VALUES ('%u','%u')", Id, uint32(purchased_tabs-1)); + CharacterDatabase.PExecute("DELETE FROM guild_bank_tab WHERE guildid='%u' AND TabId='%u'", m_Id, uint32(m_PurchasedTabs-1)); + CharacterDatabase.PExecute("INSERT INTO guild_bank_tab (guildid,TabId) VALUES ('%u','%u')", m_Id, uint32(m_PurchasedTabs-1)); CharacterDatabase.CommitTransaction(); } @@ -1111,29 +1141,15 @@ void Guild::SetGuildBankTabInfo(uint8 TabId, std::string Name, std::string Icon) CharacterDatabase.escape_string(Name); CharacterDatabase.escape_string(Icon); - CharacterDatabase.PExecute("UPDATE guild_bank_tab SET TabName='%s',TabIcon='%s' WHERE guildid='%u' AND TabId='%u'", Name.c_str(), Icon.c_str(), Id, uint32(TabId)); -} - -void Guild::CreateBankRightForTab(uint32 rankId, uint8 TabId) -{ - sLog.outDebug("CreateBankRightForTab. rank: %u, TabId: %u", rankId, uint32(TabId)); - if (rankId >= m_ranks.size() || TabId >= GUILD_BANK_MAX_TABS) - return; - - m_ranks[rankId].TabRight[TabId]=0; - m_ranks[rankId].TabSlotPerDay[TabId]=0; - CharacterDatabase.BeginTransaction(); - CharacterDatabase.PExecute("DELETE FROM guild_bank_right WHERE guildid = '%u' AND TabId = '%u' AND rid = '%u'", Id, uint32(TabId), rankId); - CharacterDatabase.PExecute("INSERT INTO guild_bank_right (guildid,TabId,rid) VALUES ('%u','%u','%u')", Id, uint32(TabId), rankId); - CharacterDatabase.CommitTransaction(); + CharacterDatabase.PExecute("UPDATE guild_bank_tab SET TabName='%s',TabIcon='%s' WHERE guildid='%u' AND TabId='%u'", Name.c_str(), Icon.c_str(), m_Id, uint32(TabId)); } uint32 Guild::GetBankRights(uint32 rankId, uint8 TabId) const { - if(rankId >= m_ranks.size() || TabId >= GUILD_BANK_MAX_TABS) + if(rankId >= m_Ranks.size() || TabId >= GUILD_BANK_MAX_TABS) return 0; - return m_ranks[rankId].TabRight[TabId]; + return m_Ranks[rankId].TabRight[TabId]; } // ************************************************* @@ -1142,21 +1158,21 @@ uint32 Guild::GetBankRights(uint32 rankId, uint8 TabId) const // This load should be called when the bank is first accessed by a guild member void Guild::LoadGuildBankFromDB() { - if (m_bankloaded) + if (m_GuildBankLoaded) return; - m_bankloaded = true; + m_GuildBankLoaded = true; LoadGuildBankEventLogFromDB(); // 0 1 2 3 - QueryResult *result = CharacterDatabase.PQuery("SELECT TabId, TabName, TabIcon, TabText FROM guild_bank_tab WHERE guildid='%u' ORDER BY TabId", Id); + QueryResult *result = CharacterDatabase.PQuery("SELECT TabId, TabName, TabIcon, TabText FROM guild_bank_tab WHERE guildid='%u' ORDER BY TabId", m_Id); if(!result) { - purchased_tabs = 0; + m_PurchasedTabs = 0; return; } - m_TabListMap.resize(purchased_tabs); + m_TabListMap.resize(m_PurchasedTabs); do { Field *fields = result->Fetch(); @@ -1176,7 +1192,7 @@ void Guild::LoadGuildBankFromDB() // data needs to be at first place for Item::LoadFromDB // 0 1 2 3 4 - result = CharacterDatabase.PQuery("SELECT data, TabId, SlotId, item_guid, item_entry FROM guild_bank_item JOIN item_instance ON item_guid = guid WHERE guildid='%u' ORDER BY TabId", Id); + result = CharacterDatabase.PQuery("SELECT data, TabId, SlotId, item_guid, item_entry FROM guild_bank_item JOIN item_instance ON item_guid = guid WHERE guildid='%u' ORDER BY TabId", m_Id); if(!result) return; @@ -1188,7 +1204,7 @@ void Guild::LoadGuildBankFromDB() uint32 ItemGuid = fields[3].GetUInt32(); uint32 ItemEntry = fields[4].GetUInt32(); - if (TabId >= purchased_tabs || TabId >= GUILD_BANK_MAX_TABS) + if (TabId >= m_PurchasedTabs || TabId >= GUILD_BANK_MAX_TABS) { sLog.outError( "Guild::LoadGuildBankFromDB: Invalid tab for item (GUID: %u id: #%u) in guild bank, skipped.", ItemGuid,ItemEntry); continue; @@ -1211,7 +1227,7 @@ void Guild::LoadGuildBankFromDB() Item *pItem = NewItemOrBag(proto); if(!pItem->LoadFromDB(ItemGuid, 0, result)) { - CharacterDatabase.PExecute("DELETE FROM guild_bank_item WHERE guildid='%u' AND TabId='%u' AND SlotId='%u'", Id, uint32(TabId), uint32(SlotId)); + CharacterDatabase.PExecute("DELETE FROM guild_bank_item WHERE guildid='%u' AND TabId='%u' AND SlotId='%u'", m_Id, uint32(TabId), uint32(SlotId)); sLog.outError("Item GUID %u not found in item_instance, deleting from Guild Bank!", ItemGuid); delete pItem; continue; @@ -1227,9 +1243,9 @@ void Guild::LoadGuildBankFromDB() // This unload should be called when the last member of the guild gets offline void Guild::UnloadGuildBank() { - if (!m_bankloaded) + if (!m_GuildBankLoaded) return; - for (uint8 i = 0 ; i < purchased_tabs ; ++i ) + for (uint8 i = 0 ; i < m_PurchasedTabs ; ++i ) { for (uint8 j = 0 ; j < GUILD_BANK_MAX_SLOTS ; ++j) { @@ -1244,7 +1260,7 @@ void Guild::UnloadGuildBank() m_TabListMap.clear(); UnloadGuildBankEventLog(); - m_bankloaded = false; + m_GuildBankLoaded = false; } // ************************************************* @@ -1274,7 +1290,7 @@ bool Guild::MemberMoneyWithdraw(uint32 amount, uint32 LowGuid) return false; itr->second.BankRemMoney -= amount; CharacterDatabase.PExecute("UPDATE guild_member SET BankRemMoney='%u' WHERE guildid='%u' AND guid='%u'", - itr->second.BankRemMoney, Id, LowGuid); + itr->second.BankRemMoney, m_Id, LowGuid); } return true; } @@ -1283,9 +1299,9 @@ void Guild::SetBankMoney(int64 money) { if (money < 0) // I don't know how this happens, it does!! money = 0; - guildbank_money = money; + m_GuildBankMoney = money; - CharacterDatabase.PExecute("UPDATE guild SET BankMoney='" UI64FMTD "' WHERE guildid='%u'", money, Id); + CharacterDatabase.PExecute("UPDATE guild SET BankMoney='" UI64FMTD "' WHERE guildid='%u'", money, m_Id); } // ************************************************* @@ -1305,7 +1321,7 @@ bool Guild::MemberItemWithdraw(uint8 TabId, uint32 LowGuid) return false; --itr->second.BankRemSlotsTab[TabId]; CharacterDatabase.PExecute("UPDATE guild_member SET BankRemSlotsTab%u='%u' WHERE guildid='%u' AND guid='%u'", - uint32(TabId), itr->second.BankRemSlotsTab[TabId], Id, LowGuid); + uint32(TabId), itr->second.BankRemSlotsTab[TabId], m_Id, LowGuid); } return true; } @@ -1340,7 +1356,7 @@ uint32 Guild::GetMemberSlotWithdrawRem(uint32 LowGuid, uint8 TabId) itr->second.BankResetTimeTab[TabId] = curTime; itr->second.BankRemSlotsTab[TabId] = GetBankSlotPerDay(itr->second.RankId, TabId); CharacterDatabase.PExecute("UPDATE guild_member SET BankResetTimeTab%u='%u',BankRemSlotsTab%u='%u' WHERE guildid='%u' AND guid='%u'", - uint32(TabId), itr->second.BankResetTimeTab[TabId], uint32(TabId), itr->second.BankRemSlotsTab[TabId], Id, LowGuid); + uint32(TabId), itr->second.BankResetTimeTab[TabId], uint32(TabId), itr->second.BankRemSlotsTab[TabId], m_Id, LowGuid); } return itr->second.BankRemSlotsTab[TabId]; } @@ -1361,34 +1377,34 @@ uint32 Guild::GetMemberMoneyWithdrawRem(uint32 LowGuid) itr->second.BankResetTimeMoney = curTime; itr->second.BankRemMoney = GetBankMoneyPerDay(itr->second.RankId); CharacterDatabase.PExecute("UPDATE guild_member SET BankResetTimeMoney='%u',BankRemMoney='%u' WHERE guildid='%u' AND guid='%u'", - itr->second.BankResetTimeMoney, itr->second.BankRemMoney, Id, LowGuid); + itr->second.BankResetTimeMoney, itr->second.BankRemMoney, m_Id, LowGuid); } return itr->second.BankRemMoney; } void Guild::SetBankMoneyPerDay(uint32 rankId, uint32 money) { - if (rankId >= m_ranks.size()) + if (rankId >= m_Ranks.size()) return; if (rankId == GR_GUILDMASTER) money = WITHDRAW_MONEY_UNLIMITED; - m_ranks[rankId].BankMoneyPerDay = money; + m_Ranks[rankId].BankMoneyPerDay = money; for (MemberList::iterator itr = members.begin(); itr != members.end(); ++itr) if (itr->second.RankId == rankId) itr->second.BankResetTimeMoney = 0; - CharacterDatabase.PExecute("UPDATE guild_rank SET BankMoneyPerDay='%u' WHERE rid='%u' AND guildid='%u'", money, (rankId+1), Id); - CharacterDatabase.PExecute("UPDATE guild_member SET BankResetTimeMoney='0' WHERE guildid='%u' AND rank='%u'", Id, rankId); + CharacterDatabase.PExecute("UPDATE guild_rank SET BankMoneyPerDay='%u' WHERE rid='%u' AND guildid='%u'", money, (rankId+1), m_Id); + CharacterDatabase.PExecute("UPDATE guild_member SET BankResetTimeMoney='0' WHERE guildid='%u' AND rank='%u'", m_Id, rankId); } void Guild::SetBankRightsAndSlots(uint32 rankId, uint8 TabId, uint32 right, uint32 nbSlots, bool db) { - if(rankId >= m_ranks.size() || + if(rankId >= m_Ranks.size() || TabId >= GUILD_BANK_MAX_TABS || - TabId >= purchased_tabs) + TabId >= m_PurchasedTabs) return; if (rankId == GR_GUILDMASTER) @@ -1397,8 +1413,8 @@ void Guild::SetBankRightsAndSlots(uint32 rankId, uint8 TabId, uint32 right, uint right = GUILD_BANK_RIGHT_FULL; } - m_ranks[rankId].TabSlotPerDay[TabId]=nbSlots; - m_ranks[rankId].TabRight[TabId]=right; + m_Ranks[rankId].TabSlotPerDay[TabId] = nbSlots; + m_Ranks[rankId].TabRight[TabId] = right; if (db) { @@ -1407,31 +1423,31 @@ void Guild::SetBankRightsAndSlots(uint32 rankId, uint8 TabId, uint32 right, uint for (int i = 0; i < GUILD_BANK_MAX_TABS; ++i) itr->second.BankResetTimeTab[i] = 0; - CharacterDatabase.PExecute("DELETE FROM guild_bank_right WHERE guildid='%u' AND TabId='%u' AND rid='%u'", Id, uint32(TabId), rankId); + CharacterDatabase.PExecute("DELETE FROM guild_bank_right WHERE guildid='%u' AND TabId='%u' AND rid='%u'", m_Id, uint32(TabId), rankId); CharacterDatabase.PExecute("INSERT INTO guild_bank_right (guildid,TabId,rid,gbright,SlotPerDay) VALUES " - "('%u','%u','%u','%u','%u')", Id, uint32(TabId), rankId, m_ranks[rankId].TabRight[TabId], m_ranks[rankId].TabSlotPerDay[TabId]); - CharacterDatabase.PExecute("UPDATE guild_member SET BankResetTimeTab%u='0' WHERE guildid='%u' AND rank='%u'", uint32(TabId), Id, rankId); + "('%u','%u','%u','%u','%u')", m_Id, uint32(TabId), rankId, m_Ranks[rankId].TabRight[TabId], m_Ranks[rankId].TabSlotPerDay[TabId]); + CharacterDatabase.PExecute("UPDATE guild_member SET BankResetTimeTab%u='0' WHERE guildid='%u' AND rank='%u'", uint32(TabId), m_Id, rankId); } } uint32 Guild::GetBankMoneyPerDay(uint32 rankId) { - if(rankId >= m_ranks.size()) + if(rankId >= m_Ranks.size()) return 0; if (rankId == GR_GUILDMASTER) return WITHDRAW_MONEY_UNLIMITED; - return m_ranks[rankId].BankMoneyPerDay; + return m_Ranks[rankId].BankMoneyPerDay; } uint32 Guild::GetBankSlotPerDay(uint32 rankId, uint8 TabId) { - if(rankId >= m_ranks.size() || TabId >= GUILD_BANK_MAX_TABS) + if(rankId >= m_Ranks.size() || TabId >= GUILD_BANK_MAX_TABS) return 0; if (rankId == GR_GUILDMASTER) return WITHDRAW_SLOT_UNLIMITED; - return m_ranks[rankId].TabSlotPerDay[TabId]; + return m_Ranks[rankId].TabSlotPerDay[TabId]; } // ************************************************* @@ -1466,59 +1482,88 @@ void Guild::LoadBankRightsFromDB(uint32 GuildId) void Guild::LoadGuildBankEventLogFromDB() { - // We can't add a limit as in Guild::LoadGuildEventLogFromDB since we fetch both money and bank log and know nothing about the composition - // 0 1 2 3 4 5 6 7 - QueryResult *result = CharacterDatabase.PQuery("SELECT LogGuid, LogEntry, TabId, PlayerGuid, ItemOrMoney, ItemStackCount, DestTabId, TimeStamp FROM guild_bank_eventlog WHERE guildid='%u' ORDER BY TimeStamp DESC", Id); + // Money log is in TabId = GUILD_BANK_MONEY_LOGS_TAB + + //uint32 configCount = sWorld.getConfig(CONFIG_GUILD_BANK_EVENT_LOG_COUNT); + //cycle through all purchased guild bank item tabs + for (uint32 tabId = 0; tabId < m_PurchasedTabs; tabId++) + { + // 0 1 2 3 4 5 6 + QueryResult *result = CharacterDatabase.PQuery("SELECT LogGuid, EventType, PlayerGuid, ItemOrMoney, ItemStackCount, DestTabId, TimeStamp FROM guild_bank_eventlog WHERE guildid='%u' AND TabId='%u' ORDER BY TimeStamp DESC,LogGuid DESC LIMIT %u", m_Id, tabId, GUILD_BANK_MAX_LOGS); + if(!result) + continue; + + bool isNextLogGuidSet = false; + do + { + Field *fields = result->Fetch(); + + GuildBankEventLogEntry NewEvent; + NewEvent.EventType = fields[1].GetUInt8(); + NewEvent.PlayerGuid = fields[2].GetUInt32(); + NewEvent.ItemOrMoney = fields[3].GetUInt32(); + NewEvent.ItemStackCount = fields[4].GetUInt8(); + NewEvent.DestTabId = fields[5].GetUInt8(); + NewEvent.TimeStamp = fields[6].GetUInt64(); + + //if newEvent is moneyEvent, move it to moneyEventTab in DB and report error + if (NewEvent.isMoneyEvent()) + { + uint32 logGuid = fields[0].GetUInt32(); + CharacterDatabase.PExecute("UPDATE guild_bank_eventlog SET TabId='%u' WHERE guildid='%u' AND TabId='%u' AND LogGuid='%u'", GUILD_BANK_MONEY_LOGS_TAB, m_Id, tabId, logGuid); + sLog.outError("GuildBankEventLog ERROR: MoneyEvent LogGuid %u for Guild %u had incorrectly set its TabId to %u, correcting it to %u TabId", logGuid, m_Id, tabId, GUILD_BANK_MONEY_LOGS_TAB); + continue; + } + else + //add event to list + //events are ordered from oldest (in beginning) to latest (in the end) + m_GuildBankEventLog_Item[tabId].push_front(NewEvent); + + if (!isNextLogGuidSet) + { + m_GuildBankEventLogNextGuid_Item[tabId] = fields[0].GetUInt32(); + //we don't have to do m_GuildBankEventLogNextGuid_Item[tabId] %= configCount; - it will be done when creating new record + isNextLogGuidSet = true; + } + } while( result->NextRow() ); + delete result; + } + + //special handle for guild bank money log + // 0 1 2 3 4 5 6 + QueryResult *result = CharacterDatabase.PQuery("SELECT LogGuid, EventType, PlayerGuid, ItemOrMoney, ItemStackCount, DestTabId, TimeStamp FROM guild_bank_eventlog WHERE guildid='%u' AND TabId='%u' ORDER BY TimeStamp DESC,LogGuid DESC LIMIT %u", m_Id, GUILD_BANK_MONEY_LOGS_TAB, GUILD_BANK_MAX_LOGS); if(!result) return; + bool isNextMoneyLogGuidSet = false; do { Field *fields = result->Fetch(); - GuildBankEvent NewEvent; - - NewEvent.LogGuid = fields[0].GetUInt32(); - NewEvent.LogEntry = fields[1].GetUInt8(); - uint8 TabId = fields[2].GetUInt8(); - NewEvent.PlayerGuid = fields[3].GetUInt32(); - NewEvent.ItemOrMoney = fields[4].GetUInt32(); - NewEvent.ItemStackCount = fields[5].GetUInt8(); - NewEvent.DestTabId = fields[6].GetUInt8(); - NewEvent.TimeStamp = fields[7].GetUInt64(); - - if (TabId >= GUILD_BANK_MAX_TABS) + if (!isNextMoneyLogGuidSet) { - sLog.outError( "Guild::LoadGuildBankEventLogFromDB: Invalid tabid '%u' for guild bank log entry (guild: '%s', LogGuid: %u), skipped.", TabId, GetName().c_str(), NewEvent.LogGuid); - continue; + m_GuildBankEventLogNextGuid_Money = fields[0].GetUInt32(); + //we don't have to do m_GuildBankEventLogNextGuid_Money %= configCount; - it will be done when creating new record + isNextMoneyLogGuidSet = true; } + GuildBankEventLogEntry NewEvent; - if (NewEvent.isMoneyEvent() && m_GuildBankEventLog_Money.size() >= GUILD_BANK_MAX_LOGS || - m_GuildBankEventLog_Item[TabId].size() >= GUILD_BANK_MAX_LOGS) - continue; + NewEvent.EventType = fields[1].GetUInt8(); + NewEvent.PlayerGuid = fields[2].GetUInt32(); + NewEvent.ItemOrMoney = fields[3].GetUInt32(); + NewEvent.ItemStackCount = fields[4].GetUInt8(); + NewEvent.DestTabId = fields[5].GetUInt8(); + NewEvent.TimeStamp = fields[6].GetUInt64(); - if (NewEvent.isMoneyEvent()) - m_GuildBankEventLog_Money.push_front(NewEvent); + //if newEvent is not moneyEvent, then report error + if (!NewEvent.isMoneyEvent()) + sLog.outError("GuildBankEventLog ERROR: MoneyEvent LogGuid %u for Guild %u is not MoneyEvent - ignoring...", fields[0].GetUInt32(), m_Id); else - m_GuildBankEventLog_Item[TabId].push_front(NewEvent); + //add event to list + //events are ordered from oldest (in beginning) to latest (in the end) + m_GuildBankEventLog_Money.push_front(NewEvent); - }while( result->NextRow() ); + } while( result->NextRow() ); delete result; - - // Check lists size in case to many event entries in db for a tab or for money - // This cases can happen only if a crash occured somewhere and table has too many log entries - if (!m_GuildBankEventLog_Money.empty()) - { - CharacterDatabase.PExecute("DELETE FROM guild_bank_eventlog WHERE guildid=%u AND LogGuid < %u", - Id, m_GuildBankEventLog_Money.front().LogGuid); - } - for (int i = 0; i < GUILD_BANK_MAX_TABS; ++i) - { - if (!m_GuildBankEventLog_Item[i].empty()) - { - CharacterDatabase.PExecute("DELETE FROM guild_bank_eventlog WHERE guildid=%u AND LogGuid < %u", - Id, m_GuildBankEventLog_Item[i].front().LogGuid); - } - } } void Guild::UnloadGuildBankEventLog() @@ -1542,13 +1587,13 @@ void Guild::DisplayGuildBankLogs(WorldSession *session, uint8 TabId) data << uint8(m_GuildBankEventLog_Money.size()); // number of log entries for (GuildBankEventLog::const_iterator itr = m_GuildBankEventLog_Money.begin(); itr != m_GuildBankEventLog_Money.end(); ++itr) { - data << uint8(itr->LogEntry); + data << uint8(itr->EventType); data << uint64(MAKE_NEW_GUID(itr->PlayerGuid,0,HIGHGUID_PLAYER)); - if (itr->LogEntry == GUILD_BANK_LOG_DEPOSIT_MONEY || - itr->LogEntry == GUILD_BANK_LOG_WITHDRAW_MONEY || - itr->LogEntry == GUILD_BANK_LOG_REPAIR_MONEY || - itr->LogEntry == GUILD_BANK_LOG_UNK1 || - itr->LogEntry == GUILD_BANK_LOG_UNK2) + if (itr->EventType == GUILD_BANK_LOG_DEPOSIT_MONEY || + itr->EventType == GUILD_BANK_LOG_WITHDRAW_MONEY || + itr->EventType == GUILD_BANK_LOG_REPAIR_MONEY || + itr->EventType == GUILD_BANK_LOG_UNK1 || + itr->EventType == GUILD_BANK_LOG_UNK2) { data << uint32(itr->ItemOrMoney); } @@ -1556,7 +1601,7 @@ void Guild::DisplayGuildBankLogs(WorldSession *session, uint8 TabId) { data << uint32(itr->ItemOrMoney); data << uint32(itr->ItemStackCount); - if (itr->LogEntry == GUILD_BANK_LOG_MOVE_ITEM || itr->LogEntry == GUILD_BANK_LOG_MOVE_ITEM2) + if (itr->EventType == GUILD_BANK_LOG_MOVE_ITEM || itr->EventType == GUILD_BANK_LOG_MOVE_ITEM2) data << uint8(itr->DestTabId); // moved tab } data << uint32(time(NULL) - itr->TimeStamp); @@ -1572,13 +1617,13 @@ void Guild::DisplayGuildBankLogs(WorldSession *session, uint8 TabId) data << uint8(m_GuildBankEventLog_Item[TabId].size()); for (GuildBankEventLog::const_iterator itr = m_GuildBankEventLog_Item[TabId].begin(); itr != m_GuildBankEventLog_Item[TabId].end(); ++itr) { - data << uint8(itr->LogEntry); + data << uint8(itr->EventType); data << uint64(MAKE_NEW_GUID(itr->PlayerGuid,0,HIGHGUID_PLAYER)); - if (itr->LogEntry == GUILD_BANK_LOG_DEPOSIT_MONEY || - itr->LogEntry == GUILD_BANK_LOG_WITHDRAW_MONEY || - itr->LogEntry == GUILD_BANK_LOG_REPAIR_MONEY || - itr->LogEntry == GUILD_BANK_LOG_UNK1 || - itr->LogEntry == GUILD_BANK_LOG_UNK2) + if (itr->EventType == GUILD_BANK_LOG_DEPOSIT_MONEY || + itr->EventType == GUILD_BANK_LOG_WITHDRAW_MONEY || + itr->EventType == GUILD_BANK_LOG_REPAIR_MONEY || + itr->EventType == GUILD_BANK_LOG_UNK1 || + itr->EventType == GUILD_BANK_LOG_UNK2) { data << uint32(itr->ItemOrMoney); } @@ -1586,7 +1631,7 @@ void Guild::DisplayGuildBankLogs(WorldSession *session, uint8 TabId) { data << uint32(itr->ItemOrMoney); data << uint32(itr->ItemStackCount); - if (itr->LogEntry == GUILD_BANK_LOG_MOVE_ITEM || itr->LogEntry == GUILD_BANK_LOG_MOVE_ITEM2) + if (itr->EventType == GUILD_BANK_LOG_MOVE_ITEM || itr->EventType == GUILD_BANK_LOG_MOVE_ITEM2) data << uint8(itr->DestTabId); // moved tab } data << uint32(time(NULL) - itr->TimeStamp); @@ -1596,53 +1641,45 @@ void Guild::DisplayGuildBankLogs(WorldSession *session, uint8 TabId) sLog.outDebug("WORLD: Sent (MSG_GUILD_BANK_LOG_QUERY)"); } -void Guild::LogBankEvent(uint8 LogEntry, uint8 TabId, uint32 PlayerGuidLow, uint32 ItemOrMoney, uint8 ItemStackCount, uint8 DestTabId) +void Guild::LogBankEvent(uint8 EventType, uint8 TabId, uint32 PlayerGuidLow, uint32 ItemOrMoney, uint8 ItemStackCount, uint8 DestTabId) { - GuildBankEvent NewEvent; - - NewEvent.LogGuid = LogMaxGuid++; - NewEvent.LogEntry = LogEntry; + //create Event + GuildBankEventLogEntry NewEvent; + NewEvent.EventType = EventType; NewEvent.PlayerGuid = PlayerGuidLow; NewEvent.ItemOrMoney = ItemOrMoney; NewEvent.ItemStackCount = ItemStackCount; NewEvent.DestTabId = DestTabId; NewEvent.TimeStamp = uint32(time(NULL)); + //add new event to the end of event list + uint32 currentTabId = TabId; + uint32 currentLogGuid = 0; if (NewEvent.isMoneyEvent()) { - if (m_GuildBankEventLog_Money.size() > GUILD_BANK_MAX_LOGS) - { - CharacterDatabase.PExecute("DELETE FROM guild_bank_eventlog WHERE guildid='%u' AND LogGuid='%u'", - Id, m_GuildBankEventLog_Money.front().LogGuid); + m_GuildBankEventLogNextGuid_Money = (m_GuildBankEventLogNextGuid_Money + 1) % sWorld.getConfig(CONFIG_GUILD_BANK_EVENT_LOG_COUNT); + currentLogGuid = m_GuildBankEventLogNextGuid_Money; + currentTabId = GUILD_BANK_MONEY_LOGS_TAB; + if (m_GuildBankEventLog_Money.size() >= GUILD_BANK_MAX_LOGS) m_GuildBankEventLog_Money.pop_front(); - } + m_GuildBankEventLog_Money.push_back(NewEvent); } else { - if (m_GuildBankEventLog_Item[TabId].size() > GUILD_BANK_MAX_LOGS) - { - CharacterDatabase.PExecute("DELETE FROM guild_bank_eventlog WHERE guildid='%u' AND LogGuid='%u'", - Id, m_GuildBankEventLog_Item[TabId].front().LogGuid); + m_GuildBankEventLogNextGuid_Item[TabId] = ((m_GuildBankEventLogNextGuid_Item[TabId]) + 1) % sWorld.getConfig(CONFIG_GUILD_BANK_EVENT_LOG_COUNT); + currentLogGuid = m_GuildBankEventLogNextGuid_Item[TabId]; + if (m_GuildBankEventLog_Item[TabId].size() >= GUILD_BANK_MAX_LOGS) m_GuildBankEventLog_Item[TabId].pop_front(); - } + m_GuildBankEventLog_Item[TabId].push_back(NewEvent); } - CharacterDatabase.PExecute("INSERT INTO guild_bank_eventlog (guildid,LogGuid,LogEntry,TabId,PlayerGuid,ItemOrMoney,ItemStackCount,DestTabId,TimeStamp) VALUES ('%u','%u','%u','%u','%u','%u','%u','%u','" UI64FMTD "')", - Id, NewEvent.LogGuid, uint32(NewEvent.LogEntry), uint32(TabId), NewEvent.PlayerGuid, NewEvent.ItemOrMoney, uint32(NewEvent.ItemStackCount), uint32(NewEvent.DestTabId), NewEvent.TimeStamp); -} -// This will renum guids used at load to prevent always going up until infinit -void Guild::RenumBankLogs() -{ - QueryResult *result = CharacterDatabase.PQuery("SELECT Min(LogGuid), Max(LogGuid) FROM guild_bank_eventlog WHERE guildid = %u", Id); - if(!result) - return; + //save event to database + CharacterDatabase.PExecute("DELETE FROM guild_bank_eventlog WHERE guildid='%u' AND LogGuid='%u' AND TabId='%u'", m_Id, currentLogGuid, currentTabId); - Field *fields = result->Fetch(); - CharacterDatabase.PExecute("UPDATE guild_bank_eventlog SET LogGuid=LogGuid-%u+1 WHERE guildid=%u ORDER BY LogGuid %s",fields[0].GetUInt32(), Id, fields[0].GetUInt32()?"ASC":"DESC"); - LogMaxGuid = fields[1].GetUInt32()+1; - delete result; + CharacterDatabase.PExecute("INSERT INTO guild_bank_eventlog (guildid,LogGuid,TabId,EventType,PlayerGuid,ItemOrMoney,ItemStackCount,DestTabId,TimeStamp) VALUES ('%u','%u','%u','%u','%u','%u','%u','%u','" UI64FMTD "')", + m_Id, currentLogGuid, currentTabId, uint32(NewEvent.EventType), NewEvent.PlayerGuid, NewEvent.ItemOrMoney, uint32(NewEvent.ItemStackCount), uint32(NewEvent.DestTabId), NewEvent.TimeStamp); } bool Guild::AddGBankItemToDB(uint32 GuildId, uint32 BankTab , uint32 BankTabSlot , uint32 GUIDLow, uint32 Entry ) @@ -1924,7 +1961,7 @@ void Guild::SetGuildBankTabText(uint8 TabId, std::string text) m_TabListMap[TabId]->Text = text; CharacterDatabase.escape_string(text); - CharacterDatabase.PExecute("UPDATE guild_bank_tab SET TabText='%s' WHERE guildid='%u' AND TabId='%u'", text.c_str(), Id, uint32(TabId)); + CharacterDatabase.PExecute("UPDATE guild_bank_tab SET TabText='%s' WHERE guildid='%u' AND TabId='%u'", text.c_str(), m_Id, uint32(TabId)); // announce SendGuildBankTabText(NULL,TabId); @@ -1950,6 +1987,418 @@ void Guild::SendGuildBankTabText(WorldSession *session, uint8 TabId) } +void Guild::SwapItems(Player * pl, uint8 BankTab, uint8 BankTabSlot, uint8 BankTabDst, uint8 BankTabSlotDst, uint32 SplitedAmount ) +{ + // empty operation + if(BankTab==BankTabDst && BankTabSlot==BankTabSlotDst) + return; + + Item *pItemSrc = GetItem(BankTab, BankTabSlot); + if (!pItemSrc) // may prevent crash + return; + + if(SplitedAmount > pItemSrc->GetCount()) + return; // cheating? + else if(SplitedAmount == pItemSrc->GetCount()) + SplitedAmount = 0; // no split + + Item *pItemDst = GetItem(BankTabDst, BankTabSlotDst); + + if(BankTab!=BankTabDst) + { + // check dest pos rights (if different tabs) + if(!IsMemberHaveRights(pl->GetGUIDLow(), BankTabDst, GUILD_BANK_RIGHT_DEPOSIT_ITEM)) + return; + + // check source pos rights (if different tabs) + uint32 remRight = GetMemberSlotWithdrawRem(pl->GetGUIDLow(), BankTab); + if(remRight <= 0) + return; + } + + if (SplitedAmount) + { // Bank -> Bank item split (in empty or non empty slot + GuildItemPosCountVec dest; + uint8 msg = CanStoreItem(BankTabDst,BankTabSlotDst,dest,SplitedAmount,pItemSrc,false); + if( msg != EQUIP_ERR_OK ) + { + pl->SendEquipError( msg, pItemSrc, NULL ); + return; + } + + Item *pNewItem = pItemSrc->CloneItem( SplitedAmount ); + if( !pNewItem ) + { + pl->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, pItemSrc, NULL ); + return; + } + + CharacterDatabase.BeginTransaction(); + LogBankEvent(GUILD_BANK_LOG_MOVE_ITEM, BankTab, pl->GetGUIDLow(), pItemSrc->GetEntry(), SplitedAmount, BankTabDst); + + pl->ItemRemovedQuestCheck( pItemSrc->GetEntry(), SplitedAmount ); + pItemSrc->SetCount( pItemSrc->GetCount() - SplitedAmount ); + pItemSrc->FSetState(ITEM_CHANGED); + pItemSrc->SaveToDB(); // not in inventory and can be save standalone + StoreItem(BankTabDst,dest,pNewItem); + CharacterDatabase.CommitTransaction(); + } + else // non split + { + GuildItemPosCountVec gDest; + uint8 msg = CanStoreItem(BankTabDst,BankTabSlotDst,gDest,pItemSrc->GetCount(),pItemSrc,false); + if( msg == EQUIP_ERR_OK ) // merge to + { + CharacterDatabase.BeginTransaction(); + LogBankEvent(GUILD_BANK_LOG_MOVE_ITEM, BankTab, pl->GetGUIDLow(), pItemSrc->GetEntry(), pItemSrc->GetCount(), BankTabDst); + + RemoveItem(BankTab, BankTabSlot); + StoreItem(BankTabDst, gDest, pItemSrc); + CharacterDatabase.CommitTransaction(); + } + else // swap + { + gDest.clear(); + msg = CanStoreItem(BankTabDst,BankTabSlotDst,gDest,pItemSrc->GetCount(),pItemSrc,true); + if( msg != EQUIP_ERR_OK ) + { + pl->SendEquipError( msg, pItemSrc, NULL ); + return; + } + + GuildItemPosCountVec gSrc; + msg = CanStoreItem(BankTab,BankTabSlot,gSrc,pItemDst->GetCount(),pItemDst,true); + if( msg != EQUIP_ERR_OK ) + { + pl->SendEquipError( msg, pItemDst, NULL ); + return; + } + + if(BankTab!=BankTabDst) + { + // check source pos rights (item swapped to src) + if(!IsMemberHaveRights(pl->GetGUIDLow(), BankTab, GUILD_BANK_RIGHT_DEPOSIT_ITEM)) + return; + + // check dest pos rights (item swapped to src) + uint32 remRightDst = GetMemberSlotWithdrawRem(pl->GetGUIDLow(), BankTabDst); + if(remRightDst <= 0) + return; + } + + CharacterDatabase.BeginTransaction(); + LogBankEvent(GUILD_BANK_LOG_MOVE_ITEM, BankTab, pl->GetGUIDLow(), pItemSrc->GetEntry(), pItemSrc->GetCount(), BankTabDst); + LogBankEvent(GUILD_BANK_LOG_MOVE_ITEM, BankTabDst, pl->GetGUIDLow(), pItemDst->GetEntry(), pItemDst->GetCount(), BankTab); + + RemoveItem(BankTab, BankTabSlot); + RemoveItem(BankTabDst, BankTabSlotDst); + StoreItem(BankTab, gSrc, pItemDst); + StoreItem(BankTabDst, gDest, pItemSrc); + CharacterDatabase.CommitTransaction(); + } + } + DisplayGuildBankContentUpdate(BankTab,BankTabSlot,BankTab==BankTabDst ? BankTabSlotDst : -1); + if(BankTab!=BankTabDst) + DisplayGuildBankContentUpdate(BankTabDst,BankTabSlotDst); +} + + +void Guild::MoveFromBankToChar( Player * pl, uint8 BankTab, uint8 BankTabSlot, uint8 PlayerBag, uint8 PlayerSlot, uint32 SplitedAmount) +{ + Item *pItemBank = GetItem(BankTab, BankTabSlot); + Item *pItemChar = pl->GetItemByPos(PlayerBag, PlayerSlot); + + if (!pItemBank) // Problem to get bank item + return; + + if(SplitedAmount > pItemBank->GetCount()) + return; // cheating? + else if(SplitedAmount == pItemBank->GetCount()) + SplitedAmount = 0; // no split + + if (SplitedAmount) + { // Bank -> Char split to slot (patly move) + Item *pNewItem = pItemBank->CloneItem( SplitedAmount ); + if( !pNewItem ) + { + pl->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, pItemBank, NULL ); + return; + } + + ItemPosCountVec dest; + uint8 msg = pl->CanStoreItem(PlayerBag, PlayerSlot, dest, pNewItem, false); + if( msg != EQUIP_ERR_OK ) + { + pl->SendEquipError( msg, pNewItem, NULL ); + delete pNewItem; + return; + } + + // check source pos rights (item moved to inventory) + uint32 remRight = GetMemberSlotWithdrawRem(pl->GetGUIDLow(), BankTab); + if(remRight <= 0) + { + delete pNewItem; + return; + } + + CharacterDatabase.BeginTransaction(); + LogBankEvent(GUILD_BANK_LOG_WITHDRAW_ITEM, BankTab, pl->GetGUIDLow(), pItemBank->GetEntry(), SplitedAmount); + + pItemBank->SetCount(pItemBank->GetCount()-SplitedAmount); + pItemBank->FSetState(ITEM_CHANGED); + pItemBank->SaveToDB(); // not in inventory and can be save standalone + pl->MoveItemToInventory(dest,pNewItem,true); + pl->SaveInventoryAndGoldToDB(); + + MemberItemWithdraw(BankTab, pl->GetGUIDLow()); + CharacterDatabase.CommitTransaction(); + } + else // Bank -> Char swap with slot (move) + { + ItemPosCountVec dest; + uint8 msg = pl->CanStoreItem(PlayerBag, PlayerSlot, dest, pItemBank, false); + if( msg == EQUIP_ERR_OK ) // merge case + { + // check source pos rights (item moved to inventory) + uint32 remRight = GetMemberSlotWithdrawRem(pl->GetGUIDLow(), BankTab); + if(remRight <= 0) + return; + + CharacterDatabase.BeginTransaction(); + LogBankEvent(GUILD_BANK_LOG_WITHDRAW_ITEM, BankTab, pl->GetGUIDLow(), pItemBank->GetEntry(), pItemBank->GetCount()); + + RemoveItem(BankTab, BankTabSlot); + pl->MoveItemToInventory(dest,pItemBank,true); + pl->SaveInventoryAndGoldToDB(); + + MemberItemWithdraw(BankTab, pl->GetGUIDLow()); + CharacterDatabase.CommitTransaction(); + } + else // Bank <-> Char swap items + { + // check source pos rights (item swapped to bank) + if(!IsMemberHaveRights(pl->GetGUIDLow(), BankTab, GUILD_BANK_RIGHT_DEPOSIT_ITEM)) + return; + + if(pItemChar) + { + if(!pItemChar->CanBeTraded()) + { + pl->SendEquipError( EQUIP_ERR_ITEMS_CANT_BE_SWAPPED, pItemChar, NULL ); + return; + } + } + + ItemPosCountVec iDest; + msg = pl->CanStoreItem(PlayerBag, PlayerSlot, iDest, pItemBank, true); + if( msg != EQUIP_ERR_OK ) + { + pl->SendEquipError( msg, pItemBank, NULL ); + return; + } + + GuildItemPosCountVec gDest; + if(pItemChar) + { + msg = CanStoreItem(BankTab,BankTabSlot,gDest,pItemChar->GetCount(),pItemChar,true); + if( msg != EQUIP_ERR_OK ) + { + pl->SendEquipError( msg, pItemChar, NULL ); + return; + } + } + + // check source pos rights (item moved to inventory) + uint32 remRight = GetMemberSlotWithdrawRem(pl->GetGUIDLow(), BankTab); + if(remRight <= 0) + return; + + if(pItemChar) + { + // logging item move to bank + if(pl->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE)) + { + sLog.outCommand(pl->GetSession()->GetAccountId(),"GM %s (Account: %u) deposit item: %s (Entry: %d Count: %u) to guild bank (Guild ID: %u )", + pl->GetName(),pl->GetSession()->GetAccountId(), + pItemChar->GetProto()->Name1,pItemChar->GetEntry(),pItemChar->GetCount(), + m_Id); + } + } + + CharacterDatabase.BeginTransaction(); + LogBankEvent(GUILD_BANK_LOG_WITHDRAW_ITEM, BankTab, pl->GetGUIDLow(), pItemBank->GetEntry(), pItemBank->GetCount()); + if(pItemChar) + LogBankEvent(GUILD_BANK_LOG_DEPOSIT_ITEM, BankTab, pl->GetGUIDLow(), pItemChar->GetEntry(), pItemChar->GetCount()); + + RemoveItem(BankTab, BankTabSlot); + if(pItemChar) + { + pl->MoveItemFromInventory(PlayerBag, PlayerSlot, true); + pItemChar->DeleteFromInventoryDB(); + } + + if(pItemChar) + StoreItem(BankTab, gDest, pItemChar); + pl->MoveItemToInventory(iDest,pItemBank,true); + pl->SaveInventoryAndGoldToDB(); + + MemberItemWithdraw(BankTab, pl->GetGUIDLow()); + CharacterDatabase.CommitTransaction(); + } + } + DisplayGuildBankContentUpdate(BankTab,BankTabSlot); +} + + +void Guild::MoveFromCharToBank( Player * pl, uint8 PlayerBag, uint8 PlayerSlot, uint8 BankTab, uint8 BankTabSlot, uint32 SplitedAmount ) +{ + Item *pItemBank = GetItem(BankTab, BankTabSlot); + Item *pItemChar = pl->GetItemByPos(PlayerBag, PlayerSlot); + + if (!pItemChar) // Problem to get item from player + return; + + if(!pItemChar->CanBeTraded()) + { + pl->SendEquipError( EQUIP_ERR_ITEMS_CANT_BE_SWAPPED, pItemChar, NULL ); + return; + } + + // check source pos rights (item moved to bank) + if(!IsMemberHaveRights(pl->GetGUIDLow(), BankTab, GUILD_BANK_RIGHT_DEPOSIT_ITEM)) + return; + + if(SplitedAmount > pItemChar->GetCount()) + return; // cheating? + else if(SplitedAmount == pItemChar->GetCount()) + SplitedAmount = 0; // no split + + if (SplitedAmount) + { // Char -> Bank split to empty or non-empty slot (partly move) + GuildItemPosCountVec dest; + uint8 msg = CanStoreItem(BankTab,BankTabSlot,dest,SplitedAmount,pItemChar,false); + if( msg != EQUIP_ERR_OK ) + { + pl->SendEquipError( msg, pItemChar, NULL ); + return; + } + + Item *pNewItem = pItemChar->CloneItem( SplitedAmount ); + if( !pNewItem ) + { + pl->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, pItemChar, NULL ); + return; + } + + // logging item move to bank (before items merge + if(pl->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE)) + { + sLog.outCommand(pl->GetSession()->GetAccountId(),"GM %s (Account: %u) deposit item: %s (Entry: %d Count: %u) to guild bank (Guild ID: %u )", + pl->GetName(),pl->GetSession()->GetAccountId(), + pItemChar->GetProto()->Name1,pItemChar->GetEntry(),SplitedAmount,m_Id); + } + + CharacterDatabase.BeginTransaction(); + LogBankEvent(GUILD_BANK_LOG_DEPOSIT_ITEM, BankTab, pl->GetGUIDLow(), pItemChar->GetEntry(), SplitedAmount); + + pl->ItemRemovedQuestCheck( pItemChar->GetEntry(), SplitedAmount ); + pItemChar->SetCount(pItemChar->GetCount()-SplitedAmount); + pItemChar->SetState(ITEM_CHANGED); + pl->SaveInventoryAndGoldToDB(); + StoreItem(BankTab, dest, pNewItem); + CharacterDatabase.CommitTransaction(); + + DisplayGuildBankContentUpdate(BankTab,dest); + } + else // Char -> Bank swap with empty or non-empty (move) + { + GuildItemPosCountVec dest; + uint8 msg = CanStoreItem(BankTab,BankTabSlot,dest,pItemChar->GetCount(),pItemChar,false); + if( msg == EQUIP_ERR_OK ) // merge + { + // logging item move to bank + if(pl->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE)) + { + sLog.outCommand(pl->GetSession()->GetAccountId(),"GM %s (Account: %u) deposit item: %s (Entry: %d Count: %u) to guild bank (Guild ID: %u )", + pl->GetName(),pl->GetSession()->GetAccountId(), + pItemChar->GetProto()->Name1,pItemChar->GetEntry(),pItemChar->GetCount(), + m_Id); + } + + CharacterDatabase.BeginTransaction(); + LogBankEvent(GUILD_BANK_LOG_DEPOSIT_ITEM, BankTab, pl->GetGUIDLow(), pItemChar->GetEntry(), pItemChar->GetCount()); + + pl->MoveItemFromInventory(PlayerBag, PlayerSlot, true); + pItemChar->DeleteFromInventoryDB(); + + StoreItem(BankTab,dest,pItemChar); + pl->SaveInventoryAndGoldToDB(); + CharacterDatabase.CommitTransaction(); + + DisplayGuildBankContentUpdate(BankTab,dest); + } + else // Char <-> Bank swap items (posible NULL bank item) + { + ItemPosCountVec iDest; + if(pItemBank) + { + msg = pl->CanStoreItem(PlayerBag, PlayerSlot, iDest, pItemBank, true); + if( msg != EQUIP_ERR_OK ) + { + pl->SendEquipError( msg, pItemBank, NULL ); + return; + } + } + + GuildItemPosCountVec gDest; + msg = CanStoreItem(BankTab,BankTabSlot,gDest,pItemChar->GetCount(),pItemChar,true); + if( msg != EQUIP_ERR_OK ) + { + pl->SendEquipError( msg, pItemChar, NULL ); + return; + } + + if(pItemBank) + { + // check bank pos rights (item swapped with inventory) + uint32 remRight = GetMemberSlotWithdrawRem(pl->GetGUIDLow(), BankTab); + if(remRight <= 0) + return; + } + + // logging item move to bank + if(pl->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE)) + { + sLog.outCommand(pl->GetSession()->GetAccountId(),"GM %s (Account: %u) deposit item: %s (Entry: %d Count: %u) to guild bank (Guild ID: %u )", + pl->GetName(),pl->GetSession()->GetAccountId(), + pItemChar->GetProto()->Name1,pItemChar->GetEntry(),pItemChar->GetCount(), + m_Id); + } + + CharacterDatabase.BeginTransaction(); + if(pItemBank) + LogBankEvent(GUILD_BANK_LOG_WITHDRAW_ITEM, BankTab, pl->GetGUIDLow(), pItemBank->GetEntry(), pItemBank->GetCount()); + LogBankEvent(GUILD_BANK_LOG_DEPOSIT_ITEM, BankTab, pl->GetGUIDLow(), pItemChar->GetEntry(), pItemChar->GetCount()); + + pl->MoveItemFromInventory(PlayerBag, PlayerSlot, true); + pItemChar->DeleteFromInventoryDB(); + if(pItemBank) + RemoveItem(BankTab, BankTabSlot); + + StoreItem(BankTab,gDest,pItemChar); + if(pItemBank) + pl->MoveItemToInventory(iDest,pItemBank,true); + pl->SaveInventoryAndGoldToDB(); + if(pItemBank) + MemberItemWithdraw(BankTab, pl->GetGUIDLow()); + CharacterDatabase.CommitTransaction(); + + DisplayGuildBankContentUpdate(BankTab,gDest); + } + } +} + bool GuildItemPosCount::isContainedIn(GuildItemPosCountVec const &vec) const { for(GuildItemPosCountVec::const_iterator itr = vec.begin(); itr != vec.end();++itr) diff --git a/src/game/Guild.h b/src/game/Guild.h index 9794668ee..1c6a19c2e 100644 --- a/src/game/Guild.h +++ b/src/game/Guild.h @@ -26,13 +26,19 @@ class Item; +#define GUILD_RANKS_MIN_COUNT 5 +#define GUILD_RANKS_MAX_COUNT 10 + enum GuildDefaultRanks { + //these ranks can be modified, but they cannot be deleted GR_GUILDMASTER = 0, GR_OFFICER = 1, - //GR_VETERAN = 2, -- not used anywhere and possible incorrect in modified rank list - //GR_MEMBER = 3, - //GR_INITIATE = 4, -- use Guild::GetLowestRank() instead for lowest rank + GR_VETERAN = 2, + GR_MEMBER = 3, + GR_INITIATE = 4, + //When promoting member server does: rank--;! + //When demoting member server does: rank++;! }; enum GuildRankRights @@ -146,7 +152,7 @@ enum GuildBankRights GUILD_BANK_RIGHT_FULL = 0xFF, }; -enum GuildBankLogEntries +enum GuildBankEventLogTypes { GUILD_BANK_LOG_DEPOSIT_ITEM = 1, GUILD_BANK_LOG_WITHDRAW_ITEM = 2, @@ -159,7 +165,7 @@ enum GuildBankLogEntries GUILD_BANK_LOG_UNK2 = 9, }; -enum GuildEventLogEntryTypes +enum GuildEventLogTypes { GUILD_EVENT_LOG_INVITE_PLAYER = 1, GUILD_EVENT_LOG_JOIN_GUILD = 2, @@ -169,16 +175,6 @@ enum GuildEventLogEntryTypes GUILD_EVENT_LOG_LEAVE_GUILD = 6, }; -struct GuildEventlogEntry -{ - uint32 LogGuid; - uint8 EventType; - uint32 PlayerGuid1; - uint32 PlayerGuid2; - uint8 NewRank; - uint64 TimeStamp; -}; - enum GuildEmblem { ERR_GUILDEMBLEM_SUCCESS = 0, @@ -205,11 +201,18 @@ inline uint32 GetGuildBankTabPrice(uint8 Index) return 0; } -struct GuildBankEvent +struct GuildEventLogEntry { - uint32 LogGuid; - uint8 LogEntry; - uint8 TabId; + uint8 EventType; + uint32 PlayerGuid1; + uint32 PlayerGuid2; + uint8 NewRank; + uint64 TimeStamp; +}; + +struct GuildBankEventLogEntry +{ + uint8 EventType; uint32 PlayerGuid; uint32 ItemOrMoney; uint8 ItemStackCount; @@ -218,9 +221,9 @@ struct GuildBankEvent bool isMoneyEvent() const { - return LogEntry == GUILD_BANK_LOG_DEPOSIT_MONEY || - LogEntry == GUILD_BANK_LOG_WITHDRAW_MONEY || - LogEntry == GUILD_BANK_LOG_REPAIR_MONEY; + return EventType == GUILD_BANK_LOG_DEPOSIT_MONEY || + EventType == GUILD_BANK_LOG_WITHDRAW_MONEY || + EventType == GUILD_BANK_LOG_REPAIR_MONEY; } }; @@ -283,38 +286,40 @@ class Guild Guild(); ~Guild(); - bool create(Player* leader, std::string gname); + bool Create(Player* leader, std::string gname); + void CreateDefaultGuildRanks(int locale_idx); void Disband(); typedef std::map MemberList; typedef std::vector RankList; - uint32 GetId(){ return Id; } - const uint64& GetLeader(){ return leaderGuid; } - std::string GetName(){ return name; } + uint32 GetId(){ return m_Id; } + const uint64& GetLeader(){ return m_LeaderGuid; } + std::string GetName(){ return m_Name; } std::string GetMOTD(){ return MOTD; } std::string GetGINFO(){ return GINFO; } - uint32 GetCreatedYear(){ return CreatedYear; } - uint32 GetCreatedMonth(){ return CreatedMonth; } - uint32 GetCreatedDay(){ return CreatedDay; } + uint32 GetCreatedYear(){ return m_CreatedYear; } + uint32 GetCreatedMonth(){ return m_CreatedMonth; } + uint32 GetCreatedDay(){ return m_CreatedDay; } - uint32 GetEmblemStyle(){ return EmblemStyle; } - uint32 GetEmblemColor(){ return EmblemColor; } - uint32 GetBorderStyle(){ return BorderStyle; } - uint32 GetBorderColor(){ return BorderColor; } - uint32 GetBackgroundColor(){ return BackgroundColor; } + uint32 GetEmblemStyle(){ return m_EmblemStyle; } + uint32 GetEmblemColor(){ return m_EmblemColor; } + uint32 GetBorderStyle(){ return m_BorderStyle; } + uint32 GetBorderColor(){ return m_BorderColor; } + uint32 GetBackgroundColor(){ return m_BackgroundColor; } void SetLeader(uint64 guid); bool AddMember(uint64 plGuid, uint32 plRank); void ChangeRank(uint64 guid, uint32 newRank); - void DelMember(uint64 guid, bool isDisbanding=false); - uint32 GetLowestRank() const { return GetNrRanks()-1; } + void DelMember(uint64 guid, bool isDisbanding = false); + //lowest rank is the count of ranks - 1 (the highest rank_id in table) + uint32 GetLowestRank() const { return m_Ranks.size() - 1; } void SetMOTD(std::string motd); void SetGINFO(std::string ginfo); - void SetPNOTE(uint64 guid,std::string pnote); - void SetOFFNOTE(uint64 guid,std::string offnote); + void SetPNOTE(uint64 guid, std::string pnote); + void SetOFFNOTE(uint64 guid, std::string offnote); void SetEmblem(uint32 emblemStyle, uint32 emblemColor, uint32 borderStyle, uint32 borderColor, uint32 backgroundColor); uint32 GetMemberSize() const { return members.size(); } @@ -323,8 +328,7 @@ class Guild bool LoadRanksFromDB(uint32 GuildId); bool LoadMembersFromDB(uint32 GuildId); - bool FillPlayerData(uint64 guid, MemberSlot* memslot); - void LoadPlayerStatsByGuid(uint64 guid); + void SetMemberStats(uint64 guid); void BroadcastToGuild(WorldSession *session, const std::string& msg, uint32 language = LANG_UNIVERSAL); void BroadcastToOfficers(WorldSession *session, const std::string& msg, uint32 language = LANG_UNIVERSAL); @@ -344,7 +348,7 @@ class Guild void DelRank(); std::string GetRankName(uint32 rankId); uint32 GetRankRights(uint32 rankId); - uint32 GetNrRanks() const { return m_ranks.size(); } + uint32 GetRanksSize() const { return m_Ranks.size(); } void SetRankName(uint32 rankId, std::string name); void SetRankRights(uint32 rankId, uint32 rights); @@ -373,25 +377,21 @@ class Guild void Roster(WorldSession *session); void Query(WorldSession *session); - void UpdateLogoutTime(uint64 guid); - // Guild eventlog + void UpdateLogoutTime(uint64 guid); + // Guild EventLog void LoadGuildEventLogFromDB(); - void UnloadGuildEventlog(); - void DisplayGuildEventlog(WorldSession *session); + void UnloadGuildEventLog(); + void DisplayGuildEventLog(WorldSession *session); void LogGuildEvent(uint8 EventType, uint32 PlayerGuid1, uint32 PlayerGuid2, uint8 NewRank); - void RenumGuildEventlog(); // ** Guild bank ** // Content & item deposit/withdraw void DisplayGuildBankContent(WorldSession *session, uint8 TabId); - void DisplayGuildBankContentUpdate(uint8 TabId, int32 slot1, int32 slot2 = -1); - void DisplayGuildBankContentUpdate(uint8 TabId, GuildItemPosCountVec const& slots); void DisplayGuildBankMoneyUpdate(); - Item* GetItem(uint8 TabId, uint8 SlotId); - uint8 CanStoreItem( uint8 tab, uint8 slot, GuildItemPosCountVec& dest, uint32 count, Item *pItem, bool swap = false) const; - Item* StoreItem( uint8 tab, GuildItemPosCountVec const& pos, Item *pItem ); - void RemoveItem(uint8 tab, uint8 slot ); + void SwapItems( Player * pl, uint8 BankTab, uint8 BankTabSlot, uint8 BankTabDst, uint8 BankTabSlotDst, uint32 SplitedAmount); + void MoveFromBankToChar( Player * pl, uint8 BankTab, uint8 BankTabSlot, uint8 PlayerBag, uint8 PlayerSlot, uint32 SplitedAmount); + void MoveFromCharToBank( Player * pl, uint8 PlayerBag, uint8 PlayerSlot, uint8 BankTab, uint8 BankTabSlot, uint32 SplitedAmount); // Tabs void DisplayGuildBankTabsInfo(WorldSession *session); @@ -399,20 +399,19 @@ class Guild void SetGuildBankTabText(uint8 TabId, std::string text); void SendGuildBankTabText(WorldSession *session, uint8 TabId); void SetGuildBankTabInfo(uint8 TabId, std::string name, std::string icon); - void CreateBankRightForTab(uint32 rankid, uint8 TabId); const GuildBankTab *GetBankTab(uint8 index) { if(index >= m_TabListMap.size()) return NULL; return m_TabListMap[index]; } - uint8 GetPurchasedTabs() const { return purchased_tabs; } + uint8 GetPurchasedTabs() const { return m_PurchasedTabs; } uint32 GetBankRights(uint32 rankId, uint8 TabId) const; bool IsMemberHaveRights(uint32 LowGuid, uint8 TabId,uint32 rights) const; bool CanMemberViewTab(uint32 LowGuid, uint8 TabId) const; // Load/unload void LoadGuildBankFromDB(); void UnloadGuildBank(); - void IncOnlineMemberCount() { ++m_onlinemembers; } + void IncOnlineMemberCount() { ++m_OnlineMembers; } // Money deposit/withdraw void SendMoneyInfo(WorldSession *session, uint32 LowGuid); bool MemberMoneyWithdraw(uint32 amount, uint32 LowGuid); - uint64 GetGuildBankMoney() { return guildbank_money; } + uint64 GetGuildBankMoney() { return m_GuildBankMoney; } void SetBankMoney(int64 money); // per days bool MemberItemWithdraw(uint8 TabId, uint32 LowGuid); @@ -424,33 +423,32 @@ class Guild uint32 GetBankSlotPerDay(uint32 rankId, uint8 TabId); // rights per day void LoadBankRightsFromDB(uint32 GuildId); - // logs + // Guild Bank Event Logs void LoadGuildBankEventLogFromDB(); void UnloadGuildBankEventLog(); void DisplayGuildBankLogs(WorldSession *session, uint8 TabId); - void LogBankEvent(uint8 LogEntry, uint8 TabId, uint32 PlayerGuidLow, uint32 ItemOrMoney, uint8 ItemStackCount=0, uint8 DestTabId=0); - void RenumBankLogs(); + void LogBankEvent(uint8 EventType, uint8 TabId, uint32 PlayerGuidLow, uint32 ItemOrMoney, uint8 ItemStackCount=0, uint8 DestTabId=0); bool AddGBankItemToDB(uint32 GuildId, uint32 BankTab , uint32 BankTabSlot , uint32 GUIDLow, uint32 Entry ); protected: void AddRank(const std::string& name,uint32 rights,uint32 money); - uint32 Id; - std::string name; - uint64 leaderGuid; + uint32 m_Id; + std::string m_Name; + uint64 m_LeaderGuid; std::string MOTD; std::string GINFO; - uint32 CreatedYear; - uint32 CreatedMonth; - uint32 CreatedDay; + uint32 m_CreatedYear; + uint32 m_CreatedMonth; + uint32 m_CreatedDay; - uint32 EmblemStyle; - uint32 EmblemColor; - uint32 BorderStyle; - uint32 BorderColor; - uint32 BackgroundColor; + uint32 m_EmblemStyle; + uint32 m_EmblemColor; + uint32 m_BorderStyle; + uint32 m_BorderColor; + uint32 m_BackgroundColor; - RankList m_ranks; + RankList m_Ranks; MemberList members; @@ -458,21 +456,31 @@ class Guild TabListMap m_TabListMap; /** These are actually ordered lists. The first element is the oldest entry.*/ - typedef std::list GuildEventlog; - typedef std::list GuildBankEventLog; - GuildEventlog m_GuildEventlog; + typedef std::list GuildEventLog; + typedef std::list GuildBankEventLog; + GuildEventLog m_GuildEventLog; GuildBankEventLog m_GuildBankEventLog_Money; GuildBankEventLog m_GuildBankEventLog_Item[GUILD_BANK_MAX_TABS]; - bool m_bankloaded; - bool m_eventlogloaded; - uint32 m_onlinemembers; - uint64 guildbank_money; - uint8 purchased_tabs; + uint32 m_GuildEventLogNextGuid; + uint32 m_GuildBankEventLogNextGuid_Money; + uint32 m_GuildBankEventLogNextGuid_Item[GUILD_BANK_MAX_TABS]; + + bool m_GuildBankLoaded; + bool m_EventLogLoaded; + uint32 m_OnlineMembers; + uint64 m_GuildBankMoney; + uint8 m_PurchasedTabs; - uint32 LogMaxGuid; - uint32 GuildEventlogMaxGuid; private: + // used only from high level Swap/Move functions + Item* GetItem(uint8 TabId, uint8 SlotId); + uint8 CanStoreItem( uint8 tab, uint8 slot, GuildItemPosCountVec& dest, uint32 count, Item *pItem, bool swap = false) const; + Item* StoreItem( uint8 tab, GuildItemPosCountVec const& pos, Item *pItem ); + void RemoveItem(uint8 tab, uint8 slot ); + void DisplayGuildBankContentUpdate(uint8 TabId, int32 slot1, int32 slot2 = -1); + void DisplayGuildBankContentUpdate(uint8 TabId, GuildItemPosCountVec const& slots); + // internal common parts for CanStore/StoreItem functions void AppendDisplayGuildBankSlot( WorldPacket& data, GuildBankTab const *tab, int32 slot ); uint8 _CanStoreItem_InSpecificSlot( uint8 tab, uint8 slot, GuildItemPosCountVec& dest, uint32& count, bool swap, Item *pSrcItem ) const; diff --git a/src/game/GuildHandler.cpp b/src/game/GuildHandler.cpp index 393b8a08f..ddacd2aec 100644 --- a/src/game/GuildHandler.cpp +++ b/src/game/GuildHandler.cpp @@ -58,7 +58,7 @@ void WorldSession::HandleGuildCreateOpcode(WorldPacket& recvPacket) return; Guild *guild = new Guild; - if(!guild->create(GetPlayer(),gname)) + if(!guild->Create(GetPlayer(),gname)) { delete guild; return; @@ -176,6 +176,13 @@ void WorldSession::HandleGuildRemoveOpcode(WorldPacket& recvPacket) return; } + //do not allow to kick player with same or higher rights + if(GetPlayer()->GetRank() >= slot->RankId) + { + SendGuildCommandResult(GUILD_QUIT_S, plName, GUILD_RANK_TOO_HIGH_S); + return; + } + guild->DelMember(plGuid); // Put record into guildlog guild->LogGuildEvent(GUILD_EVENT_LOG_UNINVITE_PLAYER, GetPlayer()->GetGUIDLow(), GUID_LOPART(plGuid), 0); @@ -210,7 +217,7 @@ void WorldSession::HandleGuildAcceptOpcode(WorldPacket& /*recvPacket*/) WorldPacket data(SMSG_GUILD_EVENT, (2+10)); // guess size data << (uint8)GE_JOINED; - data << (uint8)1; + data << (uint8)1; // strings count data << player->GetName(); guild->BroadcastPacket(&data); @@ -297,10 +304,16 @@ void WorldSession::HandleGuildPromoteOpcode(WorldPacket& recvPacket) return; } - if(slot->RankId < 2 || (slot->RankId-1) < GetPlayer()->GetRank()) + //allow to promote only to lower rank than member's rank + //guildmaster's rank = 0 + //GetPlayer()->GetRank() + 1 is highest rank that current player can promote to + if(GetPlayer()->GetRank() + 1 >= slot->RankId) + { + SendGuildCommandResult(GUILD_INVITE_S, plName, GUILD_RANK_TOO_HIGH_S); return; + } - uint32 newRankId = slot->RankId < guild->GetNrRanks() ? slot->RankId-1 : guild->GetNrRanks()-1; + uint32 newRankId = slot->RankId - 1; //when promoting player, rank is decreased guild->ChangeRank(plGuid, newRankId); // Put record into guildlog @@ -308,7 +321,7 @@ void WorldSession::HandleGuildPromoteOpcode(WorldPacket& recvPacket) WorldPacket data(SMSG_GUILD_EVENT, (2+30)); // guess size data << (uint8)GE_PROMOTION; - data << (uint8)3; + data << (uint8)3; // strings count data << GetPlayer()->GetName(); data << plName; data << guild->GetRankName(newRankId); @@ -355,16 +368,29 @@ void WorldSession::HandleGuildDemoteOpcode(WorldPacket& recvPacket) return; } - if((slot->RankId+1) >= guild->GetNrRanks() || slot->RankId <= GetPlayer()->GetRank()) + //do not allow to demote same or higher rank + if(GetPlayer()->GetRank() >= slot->RankId) + { + SendGuildCommandResult(GUILD_INVITE_S, plName, GUILD_RANK_TOO_HIGH_S); return; + } - guild->ChangeRank(plGuid, (slot->RankId+1)); + //do not allow to demote lowest rank + if(slot->RankId >= guild->GetLowestRank()) + { + SendGuildCommandResult(GUILD_INVITE_S, plName, GUILD_ALREADY_LOWEST_RANK_S); + return; + } + + uint32 newRankId = slot->RankId + 1; //when demoting player, rank is increased + + guild->ChangeRank(plGuid, newRankId); // Put record into guildlog - guild->LogGuildEvent(GUILD_EVENT_LOG_DEMOTE_PLAYER, GetPlayer()->GetGUIDLow(), GUID_LOPART(plGuid), slot->RankId); + guild->LogGuildEvent(GUILD_EVENT_LOG_DEMOTE_PLAYER, GetPlayer()->GetGUIDLow(), GUID_LOPART(plGuid), newRankId); WorldPacket data(SMSG_GUILD_EVENT, (2+30)); // guess size data << (uint8)GE_DEMOTION; - data << (uint8)3; + data << (uint8)3; // strings count data << GetPlayer()->GetName(); data << plName; data << guild->GetRankName(slot->RankId); @@ -404,7 +430,7 @@ void WorldSession::HandleGuildLeaveOpcode(WorldPacket& /*recvPacket*/) WorldPacket data(SMSG_GUILD_EVENT, (2+10)); // guess size data << (uint8)GE_LEFT; - data << (uint8)1; + data << (uint8)1; // strings count data << plName; guild->BroadcastPacket(&data); @@ -478,7 +504,7 @@ void WorldSession::HandleGuildLeaderOpcode(WorldPacket& recvPacket) WorldPacket data(SMSG_GUILD_EVENT, (2+20)); // guess size data << (uint8)GE_LEADER_CHANGED; - data << (uint8)2; + data << (uint8)2; // strings count data << oldLeader->GetName(); data << name.c_str(); guild->BroadcastPacket(&data); @@ -514,7 +540,7 @@ void WorldSession::HandleGuildMOTDOpcode(WorldPacket& recvPacket) WorldPacket data(SMSG_GUILD_EVENT, (2+MOTD.size()+1)); data << (uint8)GE_MOTD; - data << (uint8)1; + data << (uint8)1; // strings count data << MOTD; guild->BroadcastPacket(&data); @@ -646,7 +672,7 @@ void WorldSession::HandleGuildRankOpcode(WorldPacket& recvPacket) guild->SetBankMoneyPerDay(rankId, MoneyPerDay); guild->SetRankName(rankId, rankname); - if(rankId==GR_GUILDMASTER) // prevent loss leader rights + if (rankId == GR_GUILDMASTER) // prevent loss leader rights rights = GR_RIGHT_ALL; guild->SetRankRights(rankId, rights); @@ -675,7 +701,7 @@ void WorldSession::HandleGuildAddRankOpcode(WorldPacket& recvPacket) return; } - if(guild->GetNrRanks() >= GUILD_MAX_RANKS) // client not let create more 10 than ranks + if(guild->GetRanksSize() >= GUILD_RANKS_MAX_COUNT) // client not let create more 10 than ranks return; recvPacket >> rankname; @@ -825,7 +851,7 @@ void WorldSession::HandleGuildEventLogQueryOpcode(WorldPacket& /* recvPacket */) if(!pGuild) return; - pGuild->DisplayGuildEventlog(this); + pGuild->DisplayGuildEventLog(this); } /****** GUILD BANK *******/ @@ -866,6 +892,7 @@ void WorldSession::HandleGuildPermissions( WorldPacket& /* recv_data */ ) // money per day left data << uint32(pGuild->GetMemberMoneyWithdrawRem(GetPlayer()->GetGUIDLow())); data << uint8(pGuild->GetPurchasedTabs()); // tabs count + // why sending all info when not all tabs are purchased??? for(int i = 0; i < GUILD_BANK_MAX_TABS; ++i) { data << uint32(pGuild->GetBankRights(rankId, uint8(i))); @@ -1098,117 +1125,7 @@ void WorldSession::HandleGuildBankSwapItems( WorldPacket & recv_data ) // Bank <-> Bank if (BankToBank) { - // empty operation - if(BankTab==BankTabDst && BankTabSlot==BankTabSlotDst) - return; - - Item *pItemSrc = pGuild->GetItem(BankTab, BankTabSlot); - if (!pItemSrc) // may prevent crash - return; - - if(SplitedAmount > pItemSrc->GetCount()) - return; // cheating? - else if(SplitedAmount == pItemSrc->GetCount()) - SplitedAmount = 0; // no split - - Item *pItemDst = pGuild->GetItem(BankTabDst, BankTabSlotDst); - - if(BankTab!=BankTabDst) - { - // check dest pos rights (if different tabs) - if(!pGuild->IsMemberHaveRights(pl->GetGUIDLow(), BankTabDst, GUILD_BANK_RIGHT_DEPOSIT_ITEM)) - return; - - // check source pos rights (if different tabs) - uint32 remRight = pGuild->GetMemberSlotWithdrawRem(pl->GetGUIDLow(), BankTab); - if(remRight <= 0) - return; - } - - if (SplitedAmount) - { // Bank -> Bank item split (in empty or non empty slot - GuildItemPosCountVec dest; - uint8 msg = pGuild->CanStoreItem(BankTabDst,BankTabSlotDst,dest,SplitedAmount,pItemSrc,false); - if( msg != EQUIP_ERR_OK ) - { - pl->SendEquipError( msg, pItemSrc, NULL ); - return; - } - - Item *pNewItem = pItemSrc->CloneItem( SplitedAmount ); - if( !pNewItem ) - { - pl->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, pItemSrc, NULL ); - return; - } - - CharacterDatabase.BeginTransaction(); - pGuild->LogBankEvent(GUILD_BANK_LOG_MOVE_ITEM, BankTab, pl->GetGUIDLow(), pItemSrc->GetEntry(), SplitedAmount, BankTabDst); - - pl->ItemRemovedQuestCheck( pItemSrc->GetEntry(), SplitedAmount ); - pItemSrc->SetCount( pItemSrc->GetCount() - SplitedAmount ); - pItemSrc->FSetState(ITEM_CHANGED); - pItemSrc->SaveToDB(); // not in inventory and can be save standalone - pGuild->StoreItem(BankTabDst,dest,pNewItem); - CharacterDatabase.CommitTransaction(); - } - else // non split - { - GuildItemPosCountVec gDest; - uint8 msg = pGuild->CanStoreItem(BankTabDst,BankTabSlotDst,gDest,pItemSrc->GetCount(),pItemSrc,false); - if( msg == EQUIP_ERR_OK ) // merge to - { - CharacterDatabase.BeginTransaction(); - pGuild->LogBankEvent(GUILD_BANK_LOG_MOVE_ITEM, BankTab, pl->GetGUIDLow(), pItemSrc->GetEntry(), pItemSrc->GetCount(), BankTabDst); - - pGuild->RemoveItem(BankTab, BankTabSlot); - pGuild->StoreItem(BankTabDst, gDest, pItemSrc); - CharacterDatabase.CommitTransaction(); - } - else // swap - { - gDest.clear(); - msg = pGuild->CanStoreItem(BankTabDst,BankTabSlotDst,gDest,pItemSrc->GetCount(),pItemSrc,true); - if( msg != EQUIP_ERR_OK ) - { - pl->SendEquipError( msg, pItemSrc, NULL ); - return; - } - - GuildItemPosCountVec gSrc; - msg = pGuild->CanStoreItem(BankTab,BankTabSlot,gSrc,pItemDst->GetCount(),pItemDst,true); - if( msg != EQUIP_ERR_OK ) - { - pl->SendEquipError( msg, pItemDst, NULL ); - return; - } - - if(BankTab!=BankTabDst) - { - // check source pos rights (item swapped to src) - if(!pGuild->IsMemberHaveRights(pl->GetGUIDLow(), BankTab, GUILD_BANK_RIGHT_DEPOSIT_ITEM)) - return; - - // check dest pos rights (item swapped to src) - uint32 remRightDst = pGuild->GetMemberSlotWithdrawRem(pl->GetGUIDLow(), BankTabDst); - if(remRightDst <= 0) - return; - } - - CharacterDatabase.BeginTransaction(); - pGuild->LogBankEvent(GUILD_BANK_LOG_MOVE_ITEM, BankTab, pl->GetGUIDLow(), pItemSrc->GetEntry(), pItemSrc->GetCount(), BankTabDst); - pGuild->LogBankEvent(GUILD_BANK_LOG_MOVE_ITEM, BankTabDst, pl->GetGUIDLow(), pItemDst->GetEntry(), pItemDst->GetCount(), BankTab); - - pGuild->RemoveItem(BankTab, BankTabSlot); - pGuild->RemoveItem(BankTabDst, BankTabSlotDst); - pGuild->StoreItem(BankTab, gSrc, pItemDst); - pGuild->StoreItem(BankTabDst, gDest, pItemSrc); - CharacterDatabase.CommitTransaction(); - } - } - pGuild->DisplayGuildBankContentUpdate(BankTab,BankTabSlot,BankTab==BankTabDst ? BankTabSlotDst : -1); - if(BankTab!=BankTabDst) - pGuild->DisplayGuildBankContentUpdate(BankTabDst,BankTabSlotDst); + pGuild->SwapItems(pl, BankTab, BankTabSlot, BankTabDst, BankTabSlotDst, SplitedAmount); return; } @@ -1221,301 +1138,11 @@ void WorldSession::HandleGuildBankSwapItems( WorldPacket & recv_data ) return; } - Item *pItemBank = pGuild->GetItem(BankTab, BankTabSlot); - Item *pItemChar = GetPlayer()->GetItemByPos(PlayerBag, PlayerSlot); - if (!pItemChar && !pItemBank) // Nothing to do - return; - - if (!pItemChar && !ToChar) // Problem to get item from player - return; - - if (!pItemBank && ToChar) // Problem to get bank item - return; - // BankToChar swap or char to bank remaining - if (ToChar) // Bank -> Char cases - { - if(SplitedAmount > pItemBank->GetCount()) - return; // cheating? - else if(SplitedAmount == pItemBank->GetCount()) - SplitedAmount = 0; // no split - - if (SplitedAmount) - { // Bank -> Char split to slot (patly move) - Item *pNewItem = pItemBank->CloneItem( SplitedAmount ); - if( !pNewItem ) - { - pl->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, pItemBank, NULL ); - return; - } - - ItemPosCountVec dest; - uint8 msg = pl->CanStoreItem(PlayerBag, PlayerSlot, dest, pNewItem, false); - if( msg != EQUIP_ERR_OK ) - { - pl->SendEquipError( msg, pNewItem, NULL ); - delete pNewItem; - return; - } - - // check source pos rights (item moved to inventory) - uint32 remRight = pGuild->GetMemberSlotWithdrawRem(pl->GetGUIDLow(), BankTab); - if(remRight <= 0) - { - delete pNewItem; - return; - } - - CharacterDatabase.BeginTransaction(); - pGuild->LogBankEvent(GUILD_BANK_LOG_WITHDRAW_ITEM, BankTab, pl->GetGUIDLow(), pItemBank->GetEntry(), SplitedAmount); - - pItemBank->SetCount(pItemBank->GetCount()-SplitedAmount); - pItemBank->FSetState(ITEM_CHANGED); - pItemBank->SaveToDB(); // not in inventory and can be save standalone - pl->MoveItemToInventory(dest,pNewItem,true); - pl->SaveInventoryAndGoldToDB(); - - pGuild->MemberItemWithdraw(BankTab, pl->GetGUIDLow()); - CharacterDatabase.CommitTransaction(); - } - else // Bank -> Char swap with slot (move) - { - ItemPosCountVec dest; - uint8 msg = pl->CanStoreItem(PlayerBag, PlayerSlot, dest, pItemBank, false); - if( msg == EQUIP_ERR_OK ) // merge case - { - // check source pos rights (item moved to inventory) - uint32 remRight = pGuild->GetMemberSlotWithdrawRem(pl->GetGUIDLow(), BankTab); - if(remRight <= 0) - return; - - CharacterDatabase.BeginTransaction(); - pGuild->LogBankEvent(GUILD_BANK_LOG_WITHDRAW_ITEM, BankTab, pl->GetGUIDLow(), pItemBank->GetEntry(), pItemBank->GetCount()); - - pGuild->RemoveItem(BankTab, BankTabSlot); - pl->MoveItemToInventory(dest,pItemBank,true); - pl->SaveInventoryAndGoldToDB(); - - pGuild->MemberItemWithdraw(BankTab, pl->GetGUIDLow()); - CharacterDatabase.CommitTransaction(); - } - else // Bank <-> Char swap items - { - // check source pos rights (item swapped to bank) - if(!pGuild->IsMemberHaveRights(pl->GetGUIDLow(), BankTab, GUILD_BANK_RIGHT_DEPOSIT_ITEM)) - return; - - if(pItemChar) - { - if(!pItemChar->CanBeTraded()) - { - _player->SendEquipError( EQUIP_ERR_ITEMS_CANT_BE_SWAPPED, pItemChar, NULL ); - return; - } - } - - ItemPosCountVec iDest; - msg = pl->CanStoreItem(PlayerBag, PlayerSlot, iDest, pItemBank, true); - if( msg != EQUIP_ERR_OK ) - { - pl->SendEquipError( msg, pItemBank, NULL ); - return; - } - - GuildItemPosCountVec gDest; - if(pItemChar) - { - msg = pGuild->CanStoreItem(BankTab,BankTabSlot,gDest,pItemChar->GetCount(),pItemChar,true); - if( msg != EQUIP_ERR_OK ) - { - pl->SendEquipError( msg, pItemChar, NULL ); - return; - } - } - - // check source pos rights (item moved to inventory) - uint32 remRight = pGuild->GetMemberSlotWithdrawRem(pl->GetGUIDLow(), BankTab); - if(remRight <= 0) - return; - - if(pItemChar) - { - // logging item move to bank - if(_player->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE)) - { - sLog.outCommand(_player->GetSession()->GetAccountId(),"GM %s (Account: %u) deposit item: %s (Entry: %d Count: %u) to guild bank (Guild ID: %u )", - _player->GetName(),_player->GetSession()->GetAccountId(), - pItemChar->GetProto()->Name1,pItemChar->GetEntry(),pItemChar->GetCount(), - GuildId); - } - } - - CharacterDatabase.BeginTransaction(); - pGuild->LogBankEvent(GUILD_BANK_LOG_WITHDRAW_ITEM, BankTab, pl->GetGUIDLow(), pItemBank->GetEntry(), pItemBank->GetCount()); - if(pItemChar) - pGuild->LogBankEvent(GUILD_BANK_LOG_DEPOSIT_ITEM, BankTab, pl->GetGUIDLow(), pItemChar->GetEntry(), pItemChar->GetCount()); - - pGuild->RemoveItem(BankTab, BankTabSlot); - if(pItemChar) - { - pl->MoveItemFromInventory(PlayerBag, PlayerSlot, true); - pItemChar->DeleteFromInventoryDB(); - } - - if(pItemChar) - pGuild->StoreItem(BankTab, gDest, pItemChar); - pl->MoveItemToInventory(iDest,pItemBank,true); - pl->SaveInventoryAndGoldToDB(); - - pGuild->MemberItemWithdraw(BankTab, pl->GetGUIDLow()); - CharacterDatabase.CommitTransaction(); - } - } - pGuild->DisplayGuildBankContentUpdate(BankTab,BankTabSlot); - return; - } // End "To char" part - - // Char -> Bank cases - - if(!pItemChar->CanBeTraded()) - { - _player->SendEquipError( EQUIP_ERR_ITEMS_CANT_BE_SWAPPED, pItemChar, NULL ); - return; - } - - // check source pos rights (item moved to bank) - if(!pGuild->IsMemberHaveRights(pl->GetGUIDLow(), BankTab, GUILD_BANK_RIGHT_DEPOSIT_ITEM)) - return; - - if(SplitedAmount > pItemChar->GetCount()) - return; // cheating? - else if(SplitedAmount == pItemChar->GetCount()) - SplitedAmount = 0; // no split - - if (SplitedAmount) - { // Char -> Bank split to empty or non-empty slot (partly move) - GuildItemPosCountVec dest; - uint8 msg = pGuild->CanStoreItem(BankTab,BankTabSlot,dest,SplitedAmount,pItemChar,false); - if( msg != EQUIP_ERR_OK ) - { - pl->SendEquipError( msg, pItemChar, NULL ); - return; - } - - Item *pNewItem = pItemChar->CloneItem( SplitedAmount ); - if( !pNewItem ) - { - pl->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, pItemChar, NULL ); - return; - } - - // logging item move to bank (before items merge - if(_player->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE)) - { - sLog.outCommand(_player->GetSession()->GetAccountId(),"GM %s (Account: %u) deposit item: %s (Entry: %d Count: %u) to guild bank (Guild ID: %u )", - _player->GetName(),_player->GetSession()->GetAccountId(), - pItemChar->GetProto()->Name1,pItemChar->GetEntry(),SplitedAmount,GuildId); - } - - CharacterDatabase.BeginTransaction(); - pGuild->LogBankEvent(GUILD_BANK_LOG_DEPOSIT_ITEM, BankTab, pl->GetGUIDLow(), pItemChar->GetEntry(), SplitedAmount); - - pl->ItemRemovedQuestCheck( pItemChar->GetEntry(), SplitedAmount ); - pItemChar->SetCount(pItemChar->GetCount()-SplitedAmount); - pItemChar->SetState(ITEM_CHANGED); - pl->SaveInventoryAndGoldToDB(); - pGuild->StoreItem(BankTab, dest, pNewItem); - CharacterDatabase.CommitTransaction(); - - pGuild->DisplayGuildBankContentUpdate(BankTab,dest); - } - else // Char -> Bank swap with empty or non-empty (move) - { - GuildItemPosCountVec dest; - uint8 msg = pGuild->CanStoreItem(BankTab,BankTabSlot,dest,pItemChar->GetCount(),pItemChar,false); - if( msg == EQUIP_ERR_OK ) // merge - { - // logging item move to bank - if(_player->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE)) - { - sLog.outCommand(_player->GetSession()->GetAccountId(),"GM %s (Account: %u) deposit item: %s (Entry: %d Count: %u) to guild bank (Guild ID: %u )", - _player->GetName(),_player->GetSession()->GetAccountId(), - pItemChar->GetProto()->Name1,pItemChar->GetEntry(),pItemChar->GetCount(), - GuildId); - } - - CharacterDatabase.BeginTransaction(); - pGuild->LogBankEvent(GUILD_BANK_LOG_DEPOSIT_ITEM, BankTab, pl->GetGUIDLow(), pItemChar->GetEntry(), pItemChar->GetCount()); - - pl->MoveItemFromInventory(PlayerBag, PlayerSlot, true); - pItemChar->DeleteFromInventoryDB(); - - pGuild->StoreItem(BankTab,dest,pItemChar); - pl->SaveInventoryAndGoldToDB(); - CharacterDatabase.CommitTransaction(); - - pGuild->DisplayGuildBankContentUpdate(BankTab,dest); - } - else // Char <-> Bank swap items (posible NULL bank item) - { - ItemPosCountVec iDest; - if(pItemBank) - { - msg = pl->CanStoreItem(PlayerBag, PlayerSlot, iDest, pItemBank, true); - if( msg != EQUIP_ERR_OK ) - { - pl->SendEquipError( msg, pItemBank, NULL ); - return; - } - } - - GuildItemPosCountVec gDest; - msg = pGuild->CanStoreItem(BankTab,BankTabSlot,gDest,pItemChar->GetCount(),pItemChar,true); - if( msg != EQUIP_ERR_OK ) - { - pl->SendEquipError( msg, pItemChar, NULL ); - return; - } - - if(pItemBank) - { - // check bank pos rights (item swapped with inventory) - uint32 remRight = pGuild->GetMemberSlotWithdrawRem(pl->GetGUIDLow(), BankTab); - if(remRight <= 0) - return; - } - - // logging item move to bank - if(_player->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE)) - { - sLog.outCommand(_player->GetSession()->GetAccountId(),"GM %s (Account: %u) deposit item: %s (Entry: %d Count: %u) to guild bank (Guild ID: %u )", - _player->GetName(),_player->GetSession()->GetAccountId(), - pItemChar->GetProto()->Name1,pItemChar->GetEntry(),pItemChar->GetCount(), - GuildId); - } - - CharacterDatabase.BeginTransaction(); - if(pItemBank) - pGuild->LogBankEvent(GUILD_BANK_LOG_WITHDRAW_ITEM, BankTab, pl->GetGUIDLow(), pItemBank->GetEntry(), pItemBank->GetCount()); - pGuild->LogBankEvent(GUILD_BANK_LOG_DEPOSIT_ITEM, BankTab, pl->GetGUIDLow(), pItemChar->GetEntry(), pItemChar->GetCount()); - - pl->MoveItemFromInventory(PlayerBag, PlayerSlot, true); - pItemChar->DeleteFromInventoryDB(); - if(pItemBank) - pGuild->RemoveItem(BankTab, BankTabSlot); - - pGuild->StoreItem(BankTab,gDest,pItemChar); - if(pItemBank) - pl->MoveItemToInventory(iDest,pItemBank,true); - pl->SaveInventoryAndGoldToDB(); - if(pItemBank) - pGuild->MemberItemWithdraw(BankTab, pl->GetGUIDLow()); - CharacterDatabase.CommitTransaction(); - - pGuild->DisplayGuildBankContentUpdate(BankTab,gDest); - } - } + pGuild->MoveFromBankToChar(pl, BankTab, BankTabSlot, PlayerBag, PlayerSlot, SplitedAmount); + else // Char -> Bank cases + pGuild->MoveFromCharToBank(pl, PlayerBag, PlayerSlot, BankTab, BankTabSlot, SplitedAmount); } void WorldSession::HandleGuildBankBuyTab( WorldPacket & recv_data ) @@ -1546,7 +1173,7 @@ void WorldSession::HandleGuildBankBuyTab( WorldPacket & recv_data ) if (pGuild->GetPurchasedTabs() >= GUILD_BANK_MAX_TABS) return; - if (TabId != pGuild->GetPurchasedTabs()) // purchased_tabs = 0 when buying Tab 0, that is why this check can be made + if (TabId != pGuild->GetPurchasedTabs()) // m_PurchasedTabs = 0 when buying Tab 0, that is why this check can be made { sLog.outError("Error: trying to buy a tab non contigous to owned ones"); return; diff --git a/src/game/Level3.cpp b/src/game/Level3.cpp index cdcf023fd..fd7b693a9 100644 --- a/src/game/Level3.cpp +++ b/src/game/Level3.cpp @@ -3210,7 +3210,7 @@ bool ChatHandler::HandleGuildCreateCommand(const char* args) } Guild *guild = new Guild; - if (!guild->create (target,guildname)) + if (!guild->Create (target,guildname)) { delete guild; SendSysMessage (LANG_GUILD_NOT_CREATED); diff --git a/src/game/MovementHandler.cpp b/src/game/MovementHandler.cpp index f946b86cf..0f48fe60b 100644 --- a/src/game/MovementHandler.cpp +++ b/src/game/MovementHandler.cpp @@ -236,7 +236,10 @@ void WorldSession::HandleMovementOpcodes( WorldPacket & recv_data ) recv_data.rpos(recv_data.wpos()); // prevent warnings spam if (!MaNGOS::IsValidMapCoord(movementInfo.x, movementInfo.y, movementInfo.z, movementInfo.o)) + { + recv_data.rpos(recv_data.wpos()); // prevent warnings spam return; + } /* handle special cases */ if (movementInfo.HasMovementFlag(MOVEMENTFLAG_ONTRANSPORT)) @@ -244,11 +247,17 @@ void WorldSession::HandleMovementOpcodes( WorldPacket & recv_data ) // transports size limited // (also received at zeppelin leave by some reason with t_* as absolute in continent coordinates, can be safely skipped) if( movementInfo.t_x > 50 || movementInfo.t_y > 50 || movementInfo.t_z > 50 ) + { + recv_data.rpos(recv_data.wpos()); // prevent warnings spam return; + } if( !MaNGOS::IsValidMapCoord(movementInfo.x+movementInfo.t_x, movementInfo.y + movementInfo.t_y, movementInfo.z + movementInfo.t_z, movementInfo.o + movementInfo.t_o) ) + { + recv_data.rpos(recv_data.wpos()); // prevent warnings spam return; + } // if we boarded a transport, add us to it if (plMover && !plMover->m_transport) diff --git a/src/game/ObjectMgr.cpp b/src/game/ObjectMgr.cpp index b1b080967..673fa3537 100644 --- a/src/game/ObjectMgr.cpp +++ b/src/game/ObjectMgr.cpp @@ -2886,6 +2886,11 @@ void ObjectMgr::LoadGuilds() delete result; + //delete unused LogGuid records in guild_eventlog and guild_bank_eventlog table + //you can comment these lines if you don't plan to change CONFIG_GUILD_EVENT_LOG_COUNT and CONFIG_GUILD_BANK_EVENT_LOG_COUNT + CharacterDatabase.PQuery("DELETE FROM guild_eventlog WHERE LogGuid > '%u'", sWorld.getConfig(CONFIG_GUILD_EVENT_LOG_COUNT)); + CharacterDatabase.PQuery("DELETE FROM guild_bank_eventlog WHERE LogGuid > '%u'", sWorld.getConfig(CONFIG_GUILD_BANK_EVENT_LOG_COUNT)); + sLog.outString(); sLog.outString( ">> Loaded %u guild definitions", count ); } diff --git a/src/game/PetitionsHandler.cpp b/src/game/PetitionsHandler.cpp index ba4333f30..a4a1201cd 100644 --- a/src/game/PetitionsHandler.cpp +++ b/src/game/PetitionsHandler.cpp @@ -805,7 +805,7 @@ void WorldSession::HandleTurnInPetitionOpcode(WorldPacket & recv_data) if(type == 9) // create guild { Guild* guild = new Guild; - if(!guild->create(_player, name)) + if(!guild->Create(_player, name)) { delete guild; delete result; diff --git a/src/game/Player.cpp b/src/game/Player.cpp index 1b5c65b07..8ee3a8d3e 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -4018,6 +4018,9 @@ void Player::DeleteFromDB(uint64 playerguid, uint32 accountId, bool updateRealmC CharacterDatabase.PExecute("DELETE FROM character_achievement WHERE guid = '%u'",guid); CharacterDatabase.PExecute("DELETE FROM character_achievement_progress WHERE guid = '%u'",guid); CharacterDatabase.PExecute("DELETE FROM character_equipmentsets WHERE guid = '%u'",guid); + CharacterDatabase.PExecute("DELETE FROM guild_eventlog WHERE PlayerGuid1 = '%u'",guid); + CharacterDatabase.PExecute("DELETE FROM guild_eventlog WHERE PlayerGuid2 = '%u'",guid); + CharacterDatabase.PExecute("DELETE FROM guild_bank_eventlog WHERE PlayerGuid = '%u'",guid); CharacterDatabase.CommitTransaction(); //loginDatabase.PExecute("UPDATE realmcharacters SET numchars = numchars - 1 WHERE acctid = %d AND realmid = %d", accountId, realmID); diff --git a/src/game/SharedDefines.h b/src/game/SharedDefines.h index caa07bb30..ad713a2d2 100644 --- a/src/game/SharedDefines.h +++ b/src/game/SharedDefines.h @@ -308,7 +308,7 @@ enum ItemQualities #define SPELL_ATTR_EX2_UNK28 0x10000000 // 28 no breaks stealth if it fails?? #define SPELL_ATTR_EX2_CANT_CRIT 0x20000000 // 29 Spell can't crit #define SPELL_ATTR_EX2_UNK30 0x40000000 // 30 -#define SPELL_ATTR_EX2_UNK31 0x80000000 // 31 +#define SPELL_ATTR_EX2_FOOD_BUFF 0x80000000 // 31 Food or Drink Buff (like Well Fed) #define SPELL_ATTR_EX3_UNK0 0x00000001 // 0 #define SPELL_ATTR_EX3_UNK1 0x00000002 // 1 @@ -2329,9 +2329,11 @@ enum PetDiet // Max values for Guild & Guild Bank #define GUILD_BANK_MAX_TABS 6 #define GUILD_BANK_MAX_SLOTS 98 -#define GUILD_BANK_MAX_LOGS 24 -#define GUILD_EVENTLOG_MAX_ENTRIES 100 -#define GUILD_MAX_RANKS 10 +#define GUILD_BANK_MAX_LOGS 25 +#define GUILD_BANK_MONEY_LOGS_TAB 100 +#define GUILD_EVENTLOG_MAX_RECORDS 100 +#define GUILD_RANKS_MIN_COUNT 5 +#define GUILD_RANKS_MAX_COUNT 10 enum AiReaction { diff --git a/src/game/SpellHandler.cpp b/src/game/SpellHandler.cpp index 41d752315..2112325f2 100644 --- a/src/game/SpellHandler.cpp +++ b/src/game/SpellHandler.cpp @@ -289,7 +289,10 @@ void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket) // ignore for remote control state (for player case) Unit* mover = _player->m_mover; if(mover != _player && mover->GetTypeId()==TYPEID_PLAYER) + { + recvPacket.rpos(recvPacket.wpos()); // prevent spam at ignore packet return; + } sLog.outDebug("WORLD: got cast spell packet, spellId - %u, cast_count: %u, unk_flags %u, data length = %i", spellId, cast_count, unk_flags, (uint32)recvPacket.size()); @@ -299,6 +302,7 @@ void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket) if(!spellInfo) { sLog.outError("WORLD: unknown spell id %u", spellId); + recvPacket.rpos(recvPacket.wpos()); // prevent spam at ignore packet return; } @@ -308,6 +312,7 @@ void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket) if (!((Player*)mover)->HasActiveSpell (spellId) || IsPassiveSpell(spellId) ) { //cheater? kick? ban? + recvPacket.rpos(recvPacket.wpos()); // prevent spam at ignore packet return; } } @@ -317,6 +322,7 @@ void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket) if (!((Creature*)mover)->HasSpell(spellId) || IsPassiveSpell(spellId) ) { //cheater? kick? ban? + recvPacket.rpos(recvPacket.wpos()); // prevent spam at ignore packet return; } } @@ -324,7 +330,21 @@ void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket) // client provided targets SpellCastTargets targets; if(!targets.read(&recvPacket,mover)) + { + recvPacket.rpos(recvPacket.wpos()); // prevent spam at ignore packet return; + } + + // some spell cast packet including more data (for projectiles?) + if (unk_flags & 0x02) + { + recvPacket.read_skip(); // unk1, coords? + recvPacket.read_skip(); // unk1, coords? + recvPacket.read_skip(); // >> 1 + recvPacket.read_skip(); // >> MSG_MOVE_STOP + MovementInfo movementInfo; + ReadMovementInfo(recvPacket, &movementInfo); + } // auto-selection buff level base at target level (in spellInfo) if(targets.getUnitTarget()) diff --git a/src/game/SpellMgr.cpp b/src/game/SpellMgr.cpp index 0738f1d19..9b27ec841 100644 --- a/src/game/SpellMgr.cpp +++ b/src/game/SpellMgr.cpp @@ -135,6 +135,47 @@ SpellSpecific GetSpellSpecific(uint32 spellId) switch(spellInfo->SpellFamilyName) { + case SPELLFAMILY_GENERIC: + { + // Food / Drinks (mostly) + if(spellInfo->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_SEATED) + { + bool food = false; + bool drink = false; + for(int i = 0; i < 3; ++i) + { + switch(spellInfo->EffectApplyAuraName[i]) + { + // Food + case SPELL_AURA_MOD_REGEN: + case SPELL_AURA_OBS_MOD_HEALTH: + food = true; + break; + // Drink + case SPELL_AURA_MOD_POWER_REGEN: + case SPELL_AURA_OBS_MOD_MANA: + drink = true; + break; + default: + break; + } + } + + if(food && drink) + return SPELL_FOOD_AND_DRINK; + else if(food) + return SPELL_FOOD; + else if(drink) + return SPELL_DRINK; + } + else + { + // Well Fed buffs (must be exclusive with Food / Drink replenishment effects, or else Well Fed will cause them to be removed) + // SpellIcon 2560 is Spell 46687, does not have this flag + if ((spellInfo->AttributesEx2 & SPELL_ATTR_EX2_FOOD_BUFF) || spellInfo->SpellIconID == 2560) + return SPELL_WELL_FED; + } + } case SPELLFAMILY_MAGE: { // family flags 18(Molten), 25(Frost/Ice), 28(Mage) @@ -165,6 +206,15 @@ SpellSpecific GetSpellSpecific(uint32 spellId) break; } + case SPELLFAMILY_PRIEST: + { + // "Well Fed" buff from Blessed Sunfruit, Blessed Sunfruit Juice, Alterac Spring Water + if ((spellInfo->Attributes & SPELL_ATTR_CASTABLE_WHILE_SITTING) && + (spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_AUTOATTACK) && + (spellInfo->SpellIconID == 52 || spellInfo->SpellIconID == 79)) + return SPELL_WELL_FED; + break; + } case SPELLFAMILY_HUNTER: { // only hunter stings have this @@ -224,6 +274,8 @@ SpellSpecific GetSpellSpecific(uint32 spellId) return SPELL_NORMAL; } + +// target not allow have more one spell specific from same caster bool IsSingleFromSpellSpecificPerCaster(SpellSpecific spellSpec1,SpellSpecific spellSpec2) { switch(spellSpec1) @@ -242,7 +294,17 @@ bool IsSingleFromSpellSpecificPerCaster(SpellSpecific spellSpec1,SpellSpecific s case SPELL_JUDGEMENT: case SPELL_PRESENCE: case SPELL_HAND: - return spellSpec1==spellSpec2; + case SPELL_WELL_FED: + case SPELL_FOOD: + return spellSpec2==SPELL_FOOD + || spellSpec2==SPELL_FOOD_AND_DRINK; + case SPELL_DRINK: + return spellSpec2==SPELL_DRINK + || spellSpec2==SPELL_FOOD_AND_DRINK; + case SPELL_FOOD_AND_DRINK: + return spellSpec2==SPELL_FOOD + || spellSpec2==SPELL_DRINK + || spellSpec2==SPELL_FOOD_AND_DRINK; case SPELL_BATTLE_ELIXIR: return spellSpec2==SPELL_BATTLE_ELIXIR || spellSpec2==SPELL_FLASK_ELIXIR; @@ -258,6 +320,7 @@ bool IsSingleFromSpellSpecificPerCaster(SpellSpecific spellSpec1,SpellSpecific s } } +// target not allow have more one ranks from spell from spell specific per target bool IsSingleFromSpellSpecificRanksPerTarget(SpellSpecific spellId_spec, SpellSpecific i_spellId_spec) { switch(spellId_spec) @@ -266,6 +329,7 @@ bool IsSingleFromSpellSpecificRanksPerTarget(SpellSpecific spellId_spec, SpellSp case SPELL_AURA: case SPELL_CURSE: case SPELL_HAND: + case SPELL_ELEMENTAL_SHIELD: return spellId_spec==i_spellId_spec; default: return false; @@ -1505,10 +1569,6 @@ bool SpellMgr::IsNoStackSpellDueToSpell(uint32 spellId_1, uint32 spellId_2) cons case SPELLFAMILY_SHAMAN: if( spellInfo_2->SpellFamilyName == SPELLFAMILY_SHAMAN ) { - // shaman shields - if( IsElementalShield(spellInfo_1) && IsElementalShield(spellInfo_2) ) - return true; - // Windfury weapon if( spellInfo_1->SpellIconID==220 && spellInfo_2->SpellIconID==220 && spellInfo_1->SpellFamilyFlags != spellInfo_2->SpellFamilyFlags ) diff --git a/src/game/SpellMgr.h b/src/game/SpellMgr.h index e225aa1c3..a5760ea8a 100644 --- a/src/game/SpellMgr.h +++ b/src/game/SpellMgr.h @@ -99,6 +99,10 @@ enum SpellSpecific SPELL_FLASK_ELIXIR = 16, SPELL_PRESENCE = 17, SPELL_HAND = 18, + SPELL_WELL_FED = 19, + SPELL_FOOD = 20, + SPELL_DRINK = 21, + SPELL_FOOD_AND_DRINK = 22, }; SpellSpecific GetSpellSpecific(uint32 spellId); @@ -508,11 +512,12 @@ struct SpellBonusEntry typedef UNORDERED_MAP SpellProcEventMap; typedef UNORDERED_MAP SpellBonusMap; -#define ELIXIR_BATTLE_MASK 0x1 -#define ELIXIR_GUARDIAN_MASK 0x2 +#define ELIXIR_BATTLE_MASK 0x01 +#define ELIXIR_GUARDIAN_MASK 0x02 #define ELIXIR_FLASK_MASK (ELIXIR_BATTLE_MASK|ELIXIR_GUARDIAN_MASK) -#define ELIXIR_UNSTABLE_MASK 0x4 -#define ELIXIR_SHATTRATH_MASK 0x8 +#define ELIXIR_UNSTABLE_MASK 0x04 +#define ELIXIR_SHATTRATH_MASK 0x08 +#define ELIXIR_WELL_FED 0x10 // Some foods have SPELLFAMILY_POTION typedef std::map SpellElixirMap; typedef std::map SpellProcItemEnchantMap; @@ -730,6 +735,8 @@ class SpellMgr return SPELL_BATTLE_ELIXIR; else if(mask & ELIXIR_GUARDIAN_MASK) return SPELL_GUARDIAN_ELIXIR; + else if(mask & ELIXIR_WELL_FED) + return SPELL_WELL_FED; else return SPELL_NORMAL; } diff --git a/src/game/Unit.cpp b/src/game/Unit.cpp index dabb9ae62..aa2ee8a85 100644 --- a/src/game/Unit.cpp +++ b/src/game/Unit.cpp @@ -3631,9 +3631,8 @@ bool Unit::RemoveNoStackAurasDueToAura(Aura *Aur) SpellSpecific i_spellId_spec = GetSpellSpecific(i_spellId); + // single allowed spell specific from same caster bool is_sspc = IsSingleFromSpellSpecificPerCaster(spellId_spec,i_spellId_spec); - bool is_sspt = IsSingleFromSpellSpecificRanksPerTarget(spellId_spec,i_spellId_spec); - if( is_sspc && Aur->GetCasterGUID() == (*i).second->GetCasterGUID() ) { // cannot remove higher rank @@ -3653,8 +3652,15 @@ bool Unit::RemoveNoStackAurasDueToAura(Aura *Aur) break; else next = m_Auras.begin(); + + continue; } - else if( is_sspt && Aur->GetCasterGUID() != (*i).second->GetCasterGUID() && spellmgr.IsRankSpellDueToSpell(spellProto, i_spellId) ) + + // spell with spell specific that allow single ranks for spell from diff caster + // same caster case processed or early or later + bool is_sspt = IsSingleFromSpellSpecificRanksPerTarget(spellId_spec,i_spellId_spec); + if ( is_sspt && Aur->GetCasterGUID() != (*i).second->GetCasterGUID() && + (spellProto->Id == i_spellId || spellmgr.IsRankSpellDueToSpell(spellProto, i_spellId))) { // cannot remove higher rank if(CompareAuraRanks(spellId, effIndex, i_spellId, i_effIndex) < 0) @@ -3672,8 +3678,12 @@ bool Unit::RemoveNoStackAurasDueToAura(Aura *Aur) break; else next = m_Auras.begin(); + + continue; } - else if( !is_sspc && spellmgr.IsNoStackSpellDueToSpell(spellId, i_spellId) ) + + // non single per caster spell specific (possible single per target spells at caster) + if( !is_sspc && spellmgr.IsNoStackSpellDueToSpell(spellId, i_spellId) ) { // Its a parent aura (create this aura in ApplyModifier) if ((*i).second->IsInUse()) @@ -3687,9 +3697,12 @@ bool Unit::RemoveNoStackAurasDueToAura(Aura *Aur) break; else next = m_Auras.begin(); + + continue; } + // Potions stack aura by aura (elixirs/flask already checked) - else if( spellProto->SpellFamilyName == SPELLFAMILY_POTION && i_spellProto->SpellFamilyName == SPELLFAMILY_POTION ) + if( spellProto->SpellFamilyName == SPELLFAMILY_POTION && i_spellProto->SpellFamilyName == SPELLFAMILY_POTION ) { if (IsNoStackAuraDueToAura(spellId, effIndex, i_spellId, i_effIndex)) { @@ -11043,6 +11056,10 @@ void Unit::ProcDamageAndSpellFor( bool isVictim, Unit * pTarget, uint32 procFlag // Fill procTriggered list for(AuraMap::const_iterator itr = GetAuras().begin(); itr!= GetAuras().end(); ++itr) { + // skip deleted auras (possible at recursive triggered call + if(itr->second->IsDeleted()) + continue; + SpellProcEventEntry const* spellProcEvent = NULL; if(!IsTriggeredAtSpellProcEvent(pTarget, itr->second, procSpell, procFlag, procExtra, attType, isVictim, (damage > 0), spellProcEvent)) continue; diff --git a/src/game/Unit.h b/src/game/Unit.h index 9fc9d5160..cbd827e97 100644 --- a/src/game/Unit.h +++ b/src/game/Unit.h @@ -73,7 +73,7 @@ enum SpellAuraInterruptFlags AURA_INTERRUPT_FLAG_UNK15 = 0x00008000, // 15 removed by casting a spell? AURA_INTERRUPT_FLAG_UNK16 = 0x00010000, // 16 AURA_INTERRUPT_FLAG_MOUNTING = 0x00020000, // 17 removed by mounting - AURA_INTERRUPT_FLAG_NOT_SEATED = 0x00040000, // 18 removed by standing up + AURA_INTERRUPT_FLAG_NOT_SEATED = 0x00040000, // 18 removed by standing up (used by food and drink mostly and sleep/Fake Death like) AURA_INTERRUPT_FLAG_CHANGE_MAP = 0x00080000, // 19 leaving map/getting teleported AURA_INTERRUPT_FLAG_IMMUNE_OR_LOST_SELECTION = 0x00100000, // 20 removed by auras that make you invulnerable, or make other to loose selection on you AURA_INTERRUPT_FLAG_UNK21 = 0x00200000, // 21 diff --git a/src/game/World.cpp b/src/game/World.cpp index 88a82bb48..c4f7183e5 100644 --- a/src/game/World.cpp +++ b/src/game/World.cpp @@ -975,6 +975,13 @@ void World::LoadConfigSettings(bool reload) m_configs[CONFIG_INSTANT_LOGOUT] = sConfig.GetIntDefault("InstantLogout", SEC_MODERATOR); + m_configs[CONFIG_GUILD_EVENT_LOG_COUNT] = sConfig.GetIntDefault("Guild.EventLogRecordsCount", GUILD_EVENTLOG_MAX_RECORDS); + if (m_configs[CONFIG_GUILD_EVENT_LOG_COUNT] < GUILD_EVENTLOG_MAX_RECORDS) + m_configs[CONFIG_GUILD_EVENT_LOG_COUNT] = GUILD_EVENTLOG_MAX_RECORDS; + m_configs[CONFIG_GUILD_BANK_EVENT_LOG_COUNT] = sConfig.GetIntDefault("Guild.BankEventLogRecordsCount", GUILD_BANK_MAX_LOGS); + if (m_configs[CONFIG_GUILD_BANK_EVENT_LOG_COUNT] < GUILD_BANK_MAX_LOGS) + m_configs[CONFIG_GUILD_BANK_EVENT_LOG_COUNT] = GUILD_BANK_MAX_LOGS; + m_VisibleUnitGreyDistance = sConfig.GetFloatDefault("Visibility.Distance.Grey.Unit", 1); if(m_VisibleUnitGreyDistance > MAX_VISIBILITY_DISTANCE) { diff --git a/src/game/World.h b/src/game/World.h index ab3dbc77c..ad708f9af 100644 --- a/src/game/World.h +++ b/src/game/World.h @@ -213,6 +213,8 @@ enum WorldConfigs CONFIG_ARENA_SEASON_IN_PROGRESS, CONFIG_OFFHAND_CHECK_AT_TALENTS_RESET, CONFIG_CLIENTCACHE_VERSION, + CONFIG_GUILD_EVENT_LOG_COUNT, + CONFIG_GUILD_BANK_EVENT_LOG_COUNT, CONFIG_VALUE_COUNT }; diff --git a/src/game/WorldSession.cpp b/src/game/WorldSession.cpp index 3887b1bca..d51fd1d9b 100644 --- a/src/game/WorldSession.cpp +++ b/src/game/WorldSession.cpp @@ -368,7 +368,7 @@ void WorldSession::LogoutPlayer(bool Save) Guild *guild = objmgr.GetGuildById(_player->GetGuildId()); if(guild) { - guild->LoadPlayerStatsByGuid(_player->GetGUID()); + guild->SetMemberStats(_player->GetGUID()); guild->UpdateLogoutTime(_player->GetGUID()); WorldPacket data(SMSG_GUILD_EVENT, (1+1+12+8)); // name limited to 12 in character table. diff --git a/src/game/WorldSession.h b/src/game/WorldSession.h index 5be87469f..0e96edc06 100644 --- a/src/game/WorldSession.h +++ b/src/game/WorldSession.h @@ -697,6 +697,7 @@ class MANGOS_DLL_SPEC WorldSession void HandleGuildBankDepositMoney(WorldPacket& recv_data); void HandleGuildBankWithdrawMoney(WorldPacket& recv_data); void HandleGuildBankSwapItems(WorldPacket& recv_data); + void HandleGuildBankUpdateTab(WorldPacket& recv_data); void HandleGuildBankBuyTab(WorldPacket& recv_data); void HandleQueryGuildBankTabText(WorldPacket& recv_data); diff --git a/src/mangosd/mangosd.conf.dist.in b/src/mangosd/mangosd.conf.dist.in index 75396690d..0246f3393 100644 --- a/src/mangosd/mangosd.conf.dist.in +++ b/src/mangosd/mangosd.conf.dist.in @@ -537,6 +537,18 @@ LogColors = "" # Default: 7 # -1 (show all available quests marks) # +# Guild.EventLogRecordsCount +# Count of guild event log records stored in guild_eventlog table +# Increase to store more guild events in table, minimum is 100 +# You can set it to very high value to prevent oldest guild events to be rewritten by latest guild events - but it can slow down performance +# Default: 100 +# +# Guild.BankEventLogRecordsCount +# Count of guild_bank event log records stored in guild_bank_eventlog table +# Increase to store more guild_bank events in table - minimum is 25 (GUILD_BANK_MAX_LOGS) for each guild_bank tab +# Useful when you don't want old log events to be overwritten by new, but increasing can slow down performance +# Default: 25 +# # MaxPrimaryTradeSkill # Max count that player can learn the primary trade skill. # Default: 2 @@ -624,6 +636,8 @@ Instance.ResetTimeHour = 4 Instance.UnloadDelay = 1800000 Quests.LowLevelHideDiff = 4 Quests.HighLevelHideDiff = 7 +Guild.EventLogRecordsCount = 100 +Guild.BankEventLogRecordsCount = 25 MaxPrimaryTradeSkill = 2 MinPetitionSigns = 9 MaxGroupXPDistance = 74 diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index 448c53470..366fb0af2 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "8396" + #define REVISION_NR "8408" #endif // __REVISION_NR_H__