aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/server/game/Entities/Object/Object.cpp2
-rw-r--r--src/server/game/Entities/Player/Player.cpp39
-rw-r--r--src/server/game/Entities/Player/Player.h4
-rw-r--r--src/server/game/Handlers/CharacterHandler.cpp49
-rw-r--r--src/server/game/Handlers/TicketHandler.cpp19
-rw-r--r--src/server/game/Server/Packets/TicketPackets.cpp82
-rw-r--r--src/server/game/Server/Packets/TicketPackets.h135
-rw-r--r--src/server/game/Server/Protocol/Opcodes.cpp11
-rw-r--r--src/server/game/Server/WorldSession.h14
-rw-r--r--src/server/game/Spells/SpellEffects.cpp2
-rw-r--r--src/server/game/Spells/SpellMgr.cpp2
-rw-r--r--src/server/game/Tickets/TicketMgr.cpp51
-rw-r--r--src/server/game/Tickets/TicketMgr.h2
-rw-r--r--src/server/game/World/World.cpp8
-rw-r--r--src/server/game/World/World.h4
-rw-r--r--src/server/shared/DataStores/DB2Store.h3
-rw-r--r--src/server/shared/Database/DatabaseWorkerPool.h9
-rw-r--r--src/server/shared/Database/MySQLConnection.cpp12
-rw-r--r--src/server/shared/Database/MySQLConnection.h2
-rw-r--r--src/server/shared/Database/Transaction.cpp8
-rw-r--r--src/server/shared/Packets/ByteBuffer.h6
-rw-r--r--src/server/worldserver/worldserver.conf.dist25
22 files changed, 386 insertions, 103 deletions
diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp
index 5e419c2017f..335df6505a6 100644
--- a/src/server/game/Entities/Object/Object.cpp
+++ b/src/server/game/Entities/Object/Object.cpp
@@ -1401,7 +1401,7 @@ void Object::AddDynamicValue(uint16 index, uint32 value)
if (mask.GetCount() < values.size())
mask.AddBlock();
- mask.SetBit(values.size());
+ mask.SetBit(values.size() - 1);
if (m_inWorld && !m_objectUpdated)
{
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index 0a95fed3138..46e455765d3 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -2935,6 +2935,7 @@ void Player::GiveLevel(uint8 level)
SetLevel(level);
UpdateSkillsForLevel();
+ LearnSpecializationSpells();
// save base values (bonuses already included in stored stats
for (uint8 i = STAT_STRENGTH; i < MAX_STATS; ++i)
@@ -3046,6 +3047,7 @@ void Player::InitStatsForLevel(bool reapplyMods)
SetUInt32Value(UNIT_FIELD_AURASTATE, 0);
UpdateSkillsForLevel();
+ LearnSpecializationSpells();
// set default cast time multiplier
SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0f);
@@ -4141,12 +4143,6 @@ bool Player::ResetTalents(bool noCost)
RemoveTalent(talentInfo);
}
- // Remove spec specific spells
- RemoveSpecializationSpells();
-
- SetSpecId(GetActiveTalentGroup(), 0);
- SetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID, 0);
-
SQLTransaction trans = CharacterDatabase.BeginTransaction();
_SaveTalents(trans);
_SaveSpells(trans);
@@ -27163,25 +27159,28 @@ Difficulty Player::CheckLoadedLegacyRaidDifficultyID(Difficulty difficulty)
SpellInfo const* Player::GetCastSpellInfo(SpellInfo const* spellInfo) const
{
- auto range = m_overrideSpells.equal_range(spellInfo->Id);
- for (auto itr = range.first; itr != range.second; ++itr)
- if (SpellInfo const* newInfo = sSpellMgr->GetSpellInfo(itr->second))
+ auto overrides = m_overrideSpells.find(spellInfo->Id);
+ for (uint32 spellId : overrides->second)
+ if (SpellInfo const* newInfo = sSpellMgr->GetSpellInfo(spellId))
return Unit::GetCastSpellInfo(newInfo);
return Unit::GetCastSpellInfo(spellInfo);
}
+void Player::AddOverrideSpell(uint32 overridenSpellId, uint32 newSpellId)
+{
+ m_overrideSpells[overridenSpellId].insert(newSpellId);
+}
+
void Player::RemoveOverrideSpell(uint32 overridenSpellId, uint32 newSpellId)
{
- auto range = m_overrideSpells.equal_range(overridenSpellId);
- for (auto itr = range.first; itr != range.second; ++itr)
- {
- if (itr->second == newSpellId)
- {
- m_overrideSpells.erase(itr);
- break;
- }
- }
+ auto overrides = m_overrideSpells.find(overridenSpellId);
+ if (overrides == m_overrideSpells.end())
+ return;
+
+ overrides->second.erase(newSpellId);
+ if (overrides->second.empty())
+ m_overrideSpells.erase(overrides);
}
void Player::LearnSpecializationSpells()
@@ -27191,6 +27190,10 @@ void Player::LearnSpecializationSpells()
for (size_t j = 0; j < specSpells->size(); ++j)
{
SpecializationSpellsEntry const* specSpell = specSpells->at(j);
+ SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(specSpell->SpellID);
+ if (!spellInfo || spellInfo->SpellLevel > getLevel())
+ continue;
+
LearnSpell(specSpell->SpellID, false);
if (specSpell->OverridesSpellID)
AddOverrideSpell(specSpell->OverridesSpellID, specSpell->SpellID);
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index 064fba79fb1..7366bde52d7 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -1837,7 +1837,7 @@ class Player : public Unit, public GridObject<Player>
void LearnSpellHighestRank(uint32 spellid);
void AddTemporarySpell(uint32 spellId);
void RemoveTemporarySpell(uint32 spellId);
- void AddOverrideSpell(uint32 overridenSpellId, uint32 newSpellId) { m_overrideSpells.emplace(overridenSpellId, newSpellId); }
+ void AddOverrideSpell(uint32 overridenSpellId, uint32 newSpellId);
void RemoveOverrideSpell(uint32 overridenSpellId, uint32 newSpellId);
void LearnSpecializationSpells();
void RemoveSpecializationSpells();
@@ -2795,7 +2795,7 @@ class Player : public Unit, public GridObject<Player>
PlayerMails m_mail;
PlayerSpellMap m_spells;
- std::unordered_multimap<uint32 /*overridenSpellId*/, uint32 /*newSpellId*/> m_overrideSpells;
+ std::unordered_map<uint32 /*overridenSpellId*/, std::unordered_set<uint32> /*newSpellId*/> m_overrideSpells;
uint32 m_lastPotionId; // last used health/mana potion in combat, that block next potion use
GlobalCooldownMgr m_GlobalCooldownMgr;
diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp
index 16fb72c88a4..f121eefa71c 100644
--- a/src/server/game/Handlers/CharacterHandler.cpp
+++ b/src/server/game/Handlers/CharacterHandler.cpp
@@ -867,24 +867,7 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder* holder)
SendPacket(accountDataTimes.Write());
- /// Send FeatureSystemStatus
- {
- WorldPackets::System::FeatureSystemStatus features;
-
- /// START OF DUMMY VALUES
- features.ComplaintStatus = 2;
- features.ScrollOfResurrectionRequestsRemaining = 1;
- features.ScrollOfResurrectionMaxRequestsPerDay = 1;
- features.CfgRealmID = 2;
- features.CfgRealmRecID = 0;
- features.VoiceEnabled = false;
- /// END OF DUMMY VALUES
-
- features.CharUndeleteEnabled = sWorld->getBoolConfig(CONFIG_FEATURE_SYSTEM_CHARACTER_UNDELETE_ENABLED);
- features.BpayStoreEnabled = sWorld->getBoolConfig(CONFIG_FEATURE_SYSTEM_BPAY_STORE_ENABLED);
-
- SendPacket(features.Write());
- }
+ SendFeatureSystemStatus();
// Send MOTD
{
@@ -1091,10 +1074,36 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder* holder)
delete holder;
}
-void WorldSession::HandleSetFactionAtWar(WorldPacket& recvData)
+void WorldSession::SendFeatureSystemStatus()
{
- TC_LOG_DEBUG("network", "WORLD: Received CMSG_SET_FACTION_AT_WAR");
+ WorldPackets::System::FeatureSystemStatus features;
+
+ /// START OF DUMMY VALUES
+ features.ComplaintStatus = 2;
+ features.ScrollOfResurrectionRequestsRemaining = 1;
+ features.ScrollOfResurrectionMaxRequestsPerDay = 1;
+ features.CfgRealmID = 2;
+ features.CfgRealmRecID = 0;
+ features.VoiceEnabled = false;
+ features.BrowserEnabled = false; // Has to be false, otherwise client will crash if "Customer Support" is opened
+
+ features.EuropaTicketSystemStatus.HasValue = true;
+ features.EuropaTicketSystemStatus.Value.ThrottleState.MaxTries = 5;
+ features.EuropaTicketSystemStatus.Value.ThrottleState.PerMilliseconds = 5;
+ features.EuropaTicketSystemStatus.Value.ThrottleState.TryCount = 0;
+ features.EuropaTicketSystemStatus.Value.ThrottleState.LastResetTimeBeforeNow = time(nullptr) - 5000;
+ /// END OF DUMMY VALUES
+
+ features.EuropaTicketSystemStatus.Value.SubmitBugEnabled = sWorld->getBoolConfig(CONFIG_TICKET_SUBMIT_BUG);
+ features.EuropaTicketSystemStatus.Value.TicketSystemEnabled = sWorld->getBoolConfig(CONFIG_TICKET_SUBMIT_TICKET);
+ features.CharUndeleteEnabled = sWorld->getBoolConfig(CONFIG_FEATURE_SYSTEM_CHARACTER_UNDELETE_ENABLED);
+ features.BpayStoreEnabled = sWorld->getBoolConfig(CONFIG_FEATURE_SYSTEM_BPAY_STORE_ENABLED);
+
+ SendPacket(features.Write());
+}
+void WorldSession::HandleSetFactionAtWar(WorldPacket& recvData)
+{
uint32 repListID;
uint8 flag;
diff --git a/src/server/game/Handlers/TicketHandler.cpp b/src/server/game/Handlers/TicketHandler.cpp
index a6b58f18ef4..5b2e197ae7a 100644
--- a/src/server/game/Handlers/TicketHandler.cpp
+++ b/src/server/game/Handlers/TicketHandler.cpp
@@ -23,6 +23,7 @@
#include "Opcodes.h"
#include "Player.h"
#include "TicketMgr.h"
+#include "TicketPackets.h"
#include "Util.h"
#include "World.h"
#include "WorldPacket.h"
@@ -156,13 +157,19 @@ void WorldSession::HandleGMTicketDeleteOpcode(WorldPacket & /*recvData*/)
}
}
-void WorldSession::HandleGMTicketGetTicketOpcode(WorldPacket & /*recvData*/)
+void WorldSession::HandleGMTicketGetCaseStatusOpcode(WorldPackets::Ticket::GMTicketGetCaseStatus& /*packet*/)
+{
+ // TODO: Implement GmCase and handle this packet correctly
+}
+
+void WorldSession::HandleGMTicketGetTicketOpcode(WorldPackets::Ticket::GMTicketGetTicket& /*packet*/)
{
SendQueryTimeResponse();
if (GmTicket* ticket = sTicketMgr->GetTicketByPlayer(GetPlayer()->GetGUID()))
{
if (ticket->IsCompleted())
+ // TODO: Update SMSG_GM_TICKET_RESPONSE
ticket->SendResponse(this);
else
sTicketMgr->SendTicket(this, ticket);
@@ -171,13 +178,13 @@ void WorldSession::HandleGMTicketGetTicketOpcode(WorldPacket & /*recvData*/)
sTicketMgr->SendTicket(this, NULL);
}
-void WorldSession::HandleGMTicketSystemStatusOpcode(WorldPacket & /*recvData*/)
+void WorldSession::HandleGMTicketSystemStatusOpcode(WorldPackets::Ticket::GMTicketGetSystemStatus& /*packet*/)
{
// Note: This only disables the ticket UI at client side and is not fully reliable
- // are we sure this is a uint32? Should ask Zor
- WorldPacket data(SMSG_GM_TICKET_SYSTEM_STATUS, 4);
- data << uint32(sTicketMgr->GetStatus() ? GMTICKET_QUEUE_STATUS_ENABLED : GMTICKET_QUEUE_STATUS_DISABLED);
- SendPacket(&data);
+ // Note: This disables the whole customer support UI after trying to send a ticket in disabled state (MessageBox: "GM Help Tickets are currently unavaiable."). UI remains disabled until the character relogs.
+ WorldPackets::Ticket::GMTicketSystemStatus response;
+ response.Status = sTicketMgr->GetStatus() ? GMTICKET_QUEUE_STATUS_ENABLED : GMTICKET_QUEUE_STATUS_DISABLED;
+ SendPacket(response.Write());
}
void WorldSession::HandleGMSurveySubmit(WorldPacket& recvData)
diff --git a/src/server/game/Server/Packets/TicketPackets.cpp b/src/server/game/Server/Packets/TicketPackets.cpp
new file mode 100644
index 00000000000..8567cebd736
--- /dev/null
+++ b/src/server/game/Server/Packets/TicketPackets.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "TicketPackets.h"
+
+WorldPacket const* WorldPackets::Ticket::GMTicketSystemStatus::Write()
+{
+ _worldPacket << int32(Status);
+
+ return &_worldPacket;
+}
+
+WorldPacket const* WorldPackets::Ticket::GMTicketCaseStatus::Write()
+{
+ _worldPacket.AppendPackedTime(OldestTicketTime);
+ _worldPacket.AppendPackedTime(UpdateTime);
+
+ _worldPacket << int32(Cases.size());
+
+ for (auto const& c : Cases)
+ {
+ _worldPacket << int32(c.CaseID);
+ _worldPacket << int32(c.CaseOpened);
+ _worldPacket << int32(c.CaseStatus);
+ _worldPacket << int16(c.CfgRealmID);
+ _worldPacket << int64(c.CharacterID);
+ _worldPacket << int32(c.WaitTimeOverrideMinutes);
+
+ _worldPacket.WriteBits(c.Url.size(), 11);
+ _worldPacket.WriteBits(c.WaitTimeOverrideMessage.size(), 10);
+ }
+
+ _worldPacket.FlushBits();
+ return &_worldPacket;
+}
+
+void WorldPackets::Ticket::GMTicketAcknowledgeSurvey::Read()
+{
+ _worldPacket >> CaseID;
+}
+
+WorldPacket const* WorldPackets::Ticket::GMTicketGetTicketResponse::Write()
+{
+ _worldPacket << Result;
+ _worldPacket.WriteBit(Info.HasValue);
+
+ if (Info.HasValue)
+ {
+ _worldPacket << int32(Info.Value.TicketID);
+ _worldPacket << uint8(Info.Value.Category);
+ _worldPacket.AppendPackedTime(Info.Value.TicketOpenTime);
+ _worldPacket.AppendPackedTime(Info.Value.OldestTicketTime);
+ _worldPacket.AppendPackedTime(Info.Value.UpdateTime);
+ _worldPacket << uint8(Info.Value.AssignedToGM);
+ _worldPacket << uint8(Info.Value.OpenedByGM);
+ _worldPacket << int32(Info.Value.WaitTimeOverrideMinutes);
+
+ _worldPacket.WriteBits(Info.Value.TicketDescription.size(), 11);
+ _worldPacket.WriteBits(Info.Value.WaitTimeOverrideMessage.size(), 10);
+
+ _worldPacket.WriteString(Info.Value.TicketDescription);
+ _worldPacket.WriteString(Info.Value.WaitTimeOverrideMessage);
+ }
+
+ _worldPacket.FlushBits();
+
+ return &_worldPacket;
+}
diff --git a/src/server/game/Server/Packets/TicketPackets.h b/src/server/game/Server/Packets/TicketPackets.h
new file mode 100644
index 00000000000..076db67fb96
--- /dev/null
+++ b/src/server/game/Server/Packets/TicketPackets.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef TicketPackets_h__
+#define TicketPackets_h__
+
+#include "Packet.h"
+
+namespace WorldPackets
+{
+ namespace Ticket
+ {
+ class GMTicketGetSystemStatus final : public ClientPacket
+ {
+ public:
+ GMTicketGetSystemStatus(WorldPacket&& packet) : ClientPacket(CMSG_GM_TICKET_GET_SYSTEM_STATUS, std::move(packet)) { }
+
+ void Read() override { };
+ };
+
+ class GMTicketSystemStatus final : public ServerPacket
+ {
+ public:
+ GMTicketSystemStatus() : ServerPacket(SMSG_GM_TICKET_SYSTEM_STATUS, 4) { }
+
+ WorldPacket const* Write() override;
+
+ int32 Status = 0;
+ };
+
+ class GMTicketGetCaseStatus final : public ClientPacket
+ {
+ public:
+ GMTicketGetCaseStatus(WorldPacket&& packet) : ClientPacket(CMSG_GM_TICKET_GET_CASE_STATUS, std::move(packet)) { }
+
+ void Read() override { };
+ };
+
+ class GMTicketCaseStatus final : public ServerPacket
+ {
+ public:
+ struct GMTicketCase
+ {
+ int32 CaseID = 0;
+ int32 CaseOpened = 0;
+ int32 CaseStatus = 0;
+ int16 CfgRealmID = 0;
+ int64 CharacterID = 0;
+ int32 WaitTimeOverrideMinutes = 0;
+ std::string Url;
+ std::string WaitTimeOverrideMessage;
+
+ bool operator<(GMTicketCase const& right) const
+ {
+ return CaseID < right.CaseID;
+ }
+ };
+
+ GMTicketCaseStatus() : ServerPacket(SMSG_GM_TICKET_CASE_STATUS, 12) { }
+
+ WorldPacket const* Write() override;
+
+ time_t OldestTicketTime = 0;
+ time_t UpdateTime = 0;
+ std::set<GMTicketCase> Cases;
+ };
+
+ class GMTicketGetTicket final : public ClientPacket
+ {
+ public:
+ GMTicketGetTicket(WorldPacket&& packet) : ClientPacket(CMSG_GM_TICKET_GET_TICKET, std::move(packet)) { }
+
+ void Read() override { }
+ };
+
+ class GMTicketGetTicketResponse final : public ServerPacket
+ {
+ public:
+ struct GMTicketInfo
+ {
+ int32 TicketID = 0;
+ std::string TicketDescription;
+ uint8 Category = 0;
+ time_t TicketOpenTime = 0;
+ time_t OldestTicketTime = 0;
+ time_t UpdateTime = 0;
+ uint8 AssignedToGM = 0;
+ uint8 OpenedByGM = 0;
+ std::string WaitTimeOverrideMessage;
+ int32 WaitTimeOverrideMinutes = 0;
+ };
+
+ GMTicketGetTicketResponse() : ServerPacket(SMSG_GM_TICKET_GET_TICKET_RESPONSE, 5) { }
+
+ WorldPacket const* Write() override;
+
+ int32 Result = 0;
+ Optional<GMTicketInfo> Info;
+ };
+
+ class GMTicketAcknowledgeSurvey final : public ClientPacket
+ {
+ public:
+ GMTicketAcknowledgeSurvey(WorldPacket&& packet) : ClientPacket(CMSG_GM_TICKET_ACKNOWLEDGE_SURVEY, std::move(packet)) { }
+
+ void Read() override;
+
+ int32 CaseID;
+ };
+
+ class GMTicketResponseError final : public ServerPacket
+ {
+ public:
+ GMTicketResponseError() : ServerPacket(SMSG_GM_TICKET_RESPONSE_ERROR, 0) { }
+
+ WorldPacket const* Write() { return &_worldPacket; }
+ };
+ }
+}
+
+#endif // TicketPackets_h__
diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp
index 0cdd9ffa1fe..0dfb5b9d016 100644
--- a/src/server/game/Server/Protocol/Opcodes.cpp
+++ b/src/server/game/Server/Protocol/Opcodes.cpp
@@ -40,6 +40,7 @@
#include "Packets/QuestPackets.h"
#include "Packets/TalentPackets.h"
#include "Packets/TradePackets.h"
+#include "Packets/TicketPackets.h"
template<class PacketClass, void(WorldSession::*HandlerFunction)(PacketClass&)>
class PacketHandler : public ClientOpcodeHandler
@@ -387,11 +388,13 @@ void OpcodeTable::Initialize()
DEFINE_OPCODE_HANDLER_OLD(CMSG_GM_SET_SECURITY_GROUP, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL );
DEFINE_OPCODE_HANDLER_OLD(CMSG_GM_SURVEY_SUBMIT, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleGMSurveySubmit );
DEFINE_OPCODE_HANDLER_OLD(CMSG_GM_TICKET_ACKNOWLEDGE_SURVEY, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL );
+ //DEFINE_HANDLER(CMSG_GM_TICKET_ACKNOWLEDGE_SURVEY, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Ticket::GMTicketAcknowledgeSurvey, &WorldSession::HandleGMTicketAcknowledgeSurveyOpcode);
DEFINE_OPCODE_HANDLER_OLD(CMSG_GM_TICKET_CREATE, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleGMTicketCreateOpcode );
DEFINE_OPCODE_HANDLER_OLD(CMSG_GM_TICKET_DELETE_TICKET, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleGMTicketDeleteOpcode );
DEFINE_OPCODE_HANDLER_OLD(CMSG_GM_TICKET_GET_CASE_STATUS, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL );
- DEFINE_OPCODE_HANDLER_OLD(CMSG_GM_TICKET_GET_SYSTEM_STATUS, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleGMTicketSystemStatusOpcode);
- DEFINE_OPCODE_HANDLER_OLD(CMSG_GM_TICKET_GET_TICKET, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleGMTicketGetTicketOpcode );
+ //DEFINE_HANDLER(CMSG_GM_TICKET_GET_CASE_STATUS, STATUS_LOGGEDIN, PROCESS_INPLACE, WorldPackets::Ticket::GMTicketGetCaseStatus, &WorldSession::HandleGMTicketGetCaseStatusOpcode);
+ DEFINE_HANDLER(CMSG_GM_TICKET_GET_SYSTEM_STATUS, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Ticket::GMTicketGetSystemStatus, &WorldSession::HandleGMTicketSystemStatusOpcode);
+ DEFINE_HANDLER(CMSG_GM_TICKET_GET_TICKET, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Ticket::GMTicketGetTicket, &WorldSession::HandleGMTicketGetTicketOpcode);
DEFINE_OPCODE_HANDLER_OLD(CMSG_GM_TICKET_RESPONSE_RESOLVE, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleGMResponseResolve );
DEFINE_OPCODE_HANDLER_OLD(CMSG_GM_TICKET_UPDATE_TEXT, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleGMTicketUpdateOpcode );
DEFINE_HANDLER(CMSG_GOSSIP_HELLO, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::NPC::Hello, &WorldSession::HandleGossipHelloOpcode);
@@ -1277,12 +1280,12 @@ void OpcodeTable::Initialize()
DEFINE_SERVER_OPCODE_HANDLER(SMSG_GM_PLAYER_INFO, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_GM_REQUEST_PLAYER_INFO, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_GM_TICKET_CASE_STATUS, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
- DEFINE_SERVER_OPCODE_HANDLER(SMSG_GM_TICKET_GET_TICKET_RESPONSE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
+ DEFINE_SERVER_OPCODE_HANDLER(SMSG_GM_TICKET_GET_TICKET_RESPONSE, STATUS_NEVER, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_GM_TICKET_RESOLVE_RESPONSE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_GM_TICKET_RESPONSE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_GM_TICKET_RESPONSE_ERROR, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_GM_TICKET_STATUS_UPDATE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
- DEFINE_SERVER_OPCODE_HANDLER(SMSG_GM_TICKET_SYSTEM_STATUS, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
+ DEFINE_SERVER_OPCODE_HANDLER(SMSG_GM_TICKET_SYSTEM_STATUS, STATUS_NEVER, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_GM_TICKET_UPDATE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_GOD_MODE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_GOSSIP_COMPLETE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h
index a97acc23ead..2b54b5d794d 100644
--- a/src/server/game/Server/WorldSession.h
+++ b/src/server/game/Server/WorldSession.h
@@ -267,6 +267,14 @@ namespace WorldPackets
class LearnTalents;
}
+ namespace Ticket
+ {
+ class GMTicketGetSystemStatus;
+ class GMTicketGetCaseStatus;
+ class GMTicketGetTicket;
+ class GMTicketAcknowledgeSurvey;
+ }
+
namespace Trade
{
class CancelTrade;
@@ -510,6 +518,7 @@ class WorldSession
void SendAuthWaitQue(uint32 position);
void SendSetTimeZoneInformation();
+ void SendFeatureSystemStatus();
void SendFeatureSystemStatusGlueScreen();
void SendNameQueryOpcode(ObjectGuid guid);
@@ -736,8 +745,9 @@ class WorldSession
void HandleGMTicketCreateOpcode(WorldPacket& recvPacket);
void HandleGMTicketUpdateOpcode(WorldPacket& recvPacket);
void HandleGMTicketDeleteOpcode(WorldPacket& recvPacket);
- void HandleGMTicketGetTicketOpcode(WorldPacket& recvPacket);
- void HandleGMTicketSystemStatusOpcode(WorldPacket& recvPacket);
+ void HandleGMTicketGetCaseStatusOpcode(WorldPackets::Ticket::GMTicketGetCaseStatus& packet);
+ void HandleGMTicketGetTicketOpcode(WorldPackets::Ticket::GMTicketGetTicket& packet);
+ void HandleGMTicketSystemStatusOpcode(WorldPackets::Ticket::GMTicketGetSystemStatus& packet);
void HandleGMSurveySubmit(WorldPacket& recvPacket);
void HandleReportLag(WorldPacket& recvPacket);
void HandleGMResponseResolve(WorldPacket& recvPacket);
diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp
index 7c5ab810ce4..4e52adf9944 100644
--- a/src/server/game/Spells/SpellEffects.cpp
+++ b/src/server/game/Spells/SpellEffects.cpp
@@ -5769,7 +5769,7 @@ void Spell::EffectCreateAreaTrigger(SpellEffIndex /*effIndex*/)
delete areaTrigger;
}
-void Spell::EffectRemoveTalent(SpellEffIndex effIndex)
+void Spell::EffectRemoveTalent(SpellEffIndex /*effIndex*/)
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp
index d0eff5fff00..5dd41eb9bcf 100644
--- a/src/server/game/Spells/SpellMgr.cpp
+++ b/src/server/game/Spells/SpellMgr.cpp
@@ -3072,6 +3072,8 @@ void SpellMgr::LoadSpellInfoCorrections()
case 45976: // Muru Portal Channel
case 52124: // Sky Darkener Assault
case 53096: // Quetz'lun's Judgment
+ case 70743: // AoD Special
+ case 70614: // AoD Special - Vegard
case 52479: // Gift of the Harvester
case 61588: // Blazing Harpoon
spellInfo->MaxAffectedTargets = 1;
diff --git a/src/server/game/Tickets/TicketMgr.cpp b/src/server/game/Tickets/TicketMgr.cpp
index 7cd27fdaa62..84dac6cc175 100644
--- a/src/server/game/Tickets/TicketMgr.cpp
+++ b/src/server/game/Tickets/TicketMgr.cpp
@@ -18,6 +18,7 @@
#include "Common.h"
#include "TicketMgr.h"
+#include "TicketPackets.h"
#include "DatabaseEnv.h"
#include "Log.h"
#include "Language.h"
@@ -28,7 +29,7 @@
#include "Player.h"
#include "Opcodes.h"
-inline float GetAge(uint64 t) { return float(time(NULL) - t) / DAY; }
+inline time_t GetAge(uint64 t) { return (time(NULL) - t) / DAY; }
///////////////////////////////////////////////////////////////////////////////////////////////////
// GM ticket
@@ -113,30 +114,6 @@ void GmTicket::DeleteFromDB()
CharacterDatabase.Execute(stmt);
}
-void GmTicket::WritePacket(WorldPacket& data) const
-{
- data << uint32(GMTICKET_STATUS_HASTEXT);
- data << uint32(_id);
- data << _message;
- data << uint8(_needMoreHelp);
- data << GetAge(_lastModifiedTime);
- if (GmTicket* ticket = sTicketMgr->GetOldestOpenTicket())
- data << GetAge(ticket->GetLastModifiedTime());
- else
- data << float(0);
-
- // I am not sure how blizzlike this is, and we don't really have a way to find out
- data << GetAge(sTicketMgr->GetLastChange());
-
- data << uint8(std::min(_escalatedStatus, TICKET_IN_ESCALATION_QUEUE)); // escalated data
- data << uint8(_viewed ? GMTICKET_OPENEDBYGM_STATUS_OPENED : GMTICKET_OPENEDBYGM_STATUS_NOT_OPENED); // whether or not it has been viewed
-
- // TODO: implement these
- std::string waitTimeOverrideMessage = "";
- data << waitTimeOverrideMessage;
- data << uint32(0); // waitTimeOverrideMinutes
-}
-
void GmTicket::SendResponse(WorldSession* session) const
{
WorldPacket data(SMSG_GM_TICKET_RESPONSE);
@@ -267,7 +244,7 @@ TicketMgr::~TicketMgr()
void TicketMgr::Initialize()
{
- SetStatus(sWorld->getBoolConfig(CONFIG_ALLOW_TICKETS));
+ SetStatus(sWorld->getBoolConfig(CONFIG_TICKET_SYSTEM_STATUS));
}
void TicketMgr::ResetTickets()
@@ -408,12 +385,26 @@ void TicketMgr::ShowEscalatedList(ChatHandler& handler) const
void TicketMgr::SendTicket(WorldSession* session, GmTicket* ticket) const
{
- WorldPacket data(SMSG_GM_TICKET_GET_TICKET_RESPONSE, (ticket ? (4 + 4 + 1 + 4 + 4 + 4 + 1 + 1) : 4));
+ WorldPackets::Ticket::GMTicketGetTicketResponse response;
if (ticket)
- ticket->WritePacket(data);
+ {
+ response.Result = GMTICKET_STATUS_HASTEXT;
+ response.Info.HasValue = true;
+
+ response.Info.Value.TicketID = ticket->GetId();
+ response.Info.Value.TicketDescription = ticket->GetMessage();
+ response.Info.Value.Category = ticket->GetNeedMoreHelp();
+ response.Info.Value.TicketOpenTime = GetAge(ticket->GetLastModifiedTime());
+ response.Info.Value.OldestTicketTime = sTicketMgr->GetOldestOpenTicket() ? GetAge(sTicketMgr->GetOldestOpenTicket()->GetLastModifiedTime()) : float(0);
+ response.Info.Value.UpdateTime = GetAge(sTicketMgr->GetLastChange());
+ response.Info.Value.AssignedToGM = ticket->IsAssigned();
+ response.Info.Value.OpenedByGM = ticket->IsViewed();
+ response.Info.Value.WaitTimeOverrideMessage = "";
+ response.Info.Value.WaitTimeOverrideMinutes = 0;
+ }
else
- data << uint32(GMTICKET_STATUS_DEFAULT);
+ response.Result = GMTICKET_STATUS_DEFAULT;
- session->SendPacket(&data);
+ session->SendPacket(response.Write());
}
diff --git a/src/server/game/Tickets/TicketMgr.h b/src/server/game/Tickets/TicketMgr.h
index 4dd94cc1762..578d93bb128 100644
--- a/src/server/game/Tickets/TicketMgr.h
+++ b/src/server/game/Tickets/TicketMgr.h
@@ -90,11 +90,13 @@ public:
bool IsAssigned() const { return !_assignedTo.IsEmpty(); }
bool IsAssignedTo(ObjectGuid guid) const { return guid == _assignedTo; }
bool IsAssignedNotTo(ObjectGuid guid) const { return IsAssigned() && !IsAssignedTo(guid); }
+ bool IsViewed() const { return _viewed; }
uint32 GetId() const { return _id; }
Player* GetPlayer() const { return ObjectAccessor::FindPlayer(_playerGuid); }
std::string const& GetPlayerName() const { return _playerName; }
std::string const& GetMessage() const { return _message; }
+ bool const GetNeedMoreHelp() const { return _needMoreHelp; }
Player* GetAssignedPlayer() const { return ObjectAccessor::FindPlayer(_assignedTo); }
ObjectGuid GetAssignedToGUID() const { return _assignedTo; }
std::string GetAssignedToName() const
diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp
index fe3c4b76167..7885819af9f 100644
--- a/src/server/game/World/World.cpp
+++ b/src/server/game/World/World.cpp
@@ -278,6 +278,7 @@ void World::AddSession_(WorldSession* s)
s->SendAuthResponse(AUTH_OK, false);
s->SendSetTimeZoneInformation();
+ s->SendFeatureSystemStatus();
s->SendFeatureSystemStatusGlueScreen();
s->SendAddonsInfo();
@@ -377,6 +378,7 @@ bool World::RemoveQueuedPlayer(WorldSession* sess)
pop_sess->SendAuthWaitQue(0);
pop_sess->SendSetTimeZoneInformation();
+ pop_sess->SendFeatureSystemStatus();
pop_sess->SendFeatureSystemStatusGlueScreen();
pop_sess->SendAddonsInfo();
@@ -428,7 +430,11 @@ void World::LoadConfigSettings(bool reload)
SetMotd(sConfigMgr->GetStringDefault("Motd", "Welcome to a Trinity Core Server."));
///- Read ticket system setting from the config file
- m_bool_configs[CONFIG_ALLOW_TICKETS] = sConfigMgr->GetBoolDefault("AllowTickets", true);
+ m_bool_configs[CONFIG_TICKET_SYSTEM_STATUS] = sConfigMgr->GetBoolDefault("Ticket.SystemStatus", true);
+ if (reload)
+ sTicketMgr->SetStatus(m_bool_configs[CONFIG_TICKET_SYSTEM_STATUS]);
+ m_bool_configs[CONFIG_TICKET_SUBMIT_TICKET] = sConfigMgr->GetBoolDefault("Ticket.SubmitTicket", false);
+ m_bool_configs[CONFIG_TICKET_SUBMIT_BUG] = sConfigMgr->GetBoolDefault("Ticket.SubmitBug", false);
///- Get string for new logins (newly created characters)
SetNewCharString(sConfigMgr->GetStringDefault("PlayerStart.String", ""));
diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h
index 2526516d778..c30d4f6ca35 100644
--- a/src/server/game/World/World.h
+++ b/src/server/game/World/World.h
@@ -151,7 +151,9 @@ enum WorldBoolConfigs
CONFIG_SHOW_MUTE_IN_WORLD,
CONFIG_SHOW_BAN_IN_WORLD,
CONFIG_AUTOBROADCAST,
- CONFIG_ALLOW_TICKETS,
+ CONFIG_TICKET_SYSTEM_STATUS,
+ CONFIG_TICKET_SUBMIT_TICKET,
+ CONFIG_TICKET_SUBMIT_BUG,
CONFIG_DBC_ENFORCE_ITEM_ATTRIBUTES,
CONFIG_PRESERVE_CUSTOM_CHANNELS,
CONFIG_PDUMP_NO_PATHS,
diff --git a/src/server/shared/DataStores/DB2Store.h b/src/server/shared/DataStores/DB2Store.h
index c4ca929065b..9514f34b294 100644
--- a/src/server/shared/DataStores/DB2Store.h
+++ b/src/server/shared/DataStores/DB2Store.h
@@ -80,8 +80,7 @@ void WriteDB2RecordToPacket(DB2Storage<T> const& store, uint32 id, uint32 locale
char const* str = locStr->Str[locale];
size_t len = strlen(str);
buffer << uint16(len);
- if (len)
- buffer << str;
+ buffer.WriteString(str, len);
entry += sizeof(char*);
break;
}
diff --git a/src/server/shared/Database/DatabaseWorkerPool.h b/src/server/shared/Database/DatabaseWorkerPool.h
index 5a06ad69a1d..f0ddbe91ad8 100644
--- a/src/server/shared/Database/DatabaseWorkerPool.h
+++ b/src/server/shared/Database/DatabaseWorkerPool.h
@@ -29,6 +29,8 @@
#include "QueryHolder.h"
#include "AdhocStatement.h"
+#include <mysqld_error.h>
+
#define MIN_MYSQL_SERVER_VERSION 50100u
#define MIN_MYSQL_CLIENT_VERSION 50100u
@@ -368,7 +370,8 @@ class DatabaseWorkerPool
void DirectCommitTransaction(SQLTransaction& transaction)
{
T* con = GetFreeConnection();
- if (con->ExecuteTransaction(transaction))
+ int errorCode = con->ExecuteTransaction(transaction);
+ if (!errorCode)
{
con->Unlock(); // OK, operation succesful
return;
@@ -376,12 +379,12 @@ class DatabaseWorkerPool
//! Handle MySQL Errno 1213 without extending deadlock to the core itself
/// @todo More elegant way
- if (con->GetLastError() == 1213)
+ if (errorCode == ER_LOCK_DEADLOCK)
{
uint8 loopBreaker = 5;
for (uint8 i = 0; i < loopBreaker; ++i)
{
- if (con->ExecuteTransaction(transaction))
+ if (!con->ExecuteTransaction(transaction))
break;
}
}
diff --git a/src/server/shared/Database/MySQLConnection.cpp b/src/server/shared/Database/MySQLConnection.cpp
index bea229df184..1a9f973d47b 100644
--- a/src/server/shared/Database/MySQLConnection.cpp
+++ b/src/server/shared/Database/MySQLConnection.cpp
@@ -359,11 +359,11 @@ void MySQLConnection::CommitTransaction()
Execute("COMMIT");
}
-bool MySQLConnection::ExecuteTransaction(SQLTransaction& transaction)
+int MySQLConnection::ExecuteTransaction(SQLTransaction& transaction)
{
std::list<SQLElementData> const& queries = transaction->m_queries;
if (queries.empty())
- return false;
+ return -1;
BeginTransaction();
@@ -380,8 +380,9 @@ bool MySQLConnection::ExecuteTransaction(SQLTransaction& transaction)
if (!Execute(stmt))
{
TC_LOG_WARN("sql.sql", "Transaction aborted. %u queries not executed.", (uint32)queries.size());
+ int errorCode = GetLastError();
RollbackTransaction();
- return false;
+ return errorCode;
}
}
break;
@@ -392,8 +393,9 @@ bool MySQLConnection::ExecuteTransaction(SQLTransaction& transaction)
if (!Execute(sql))
{
TC_LOG_WARN("sql.sql", "Transaction aborted. %u queries not executed.", (uint32)queries.size());
+ int errorCode = GetLastError();
RollbackTransaction();
- return false;
+ return errorCode;
}
}
break;
@@ -406,7 +408,7 @@ bool MySQLConnection::ExecuteTransaction(SQLTransaction& transaction)
// and not while iterating over every element.
CommitTransaction();
- return true;
+ return 0;
}
MySQLPreparedStatement* MySQLConnection::GetPreparedStatement(uint32 index)
diff --git a/src/server/shared/Database/MySQLConnection.h b/src/server/shared/Database/MySQLConnection.h
index 33f17d02228..d486f5b4679 100644
--- a/src/server/shared/Database/MySQLConnection.h
+++ b/src/server/shared/Database/MySQLConnection.h
@@ -86,7 +86,7 @@ class MySQLConnection
void BeginTransaction();
void RollbackTransaction();
void CommitTransaction();
- bool ExecuteTransaction(SQLTransaction& transaction);
+ int ExecuteTransaction(SQLTransaction& transaction);
operator bool () const { return m_Mysql != NULL; }
void Ping() { mysql_ping(m_Mysql); }
diff --git a/src/server/shared/Database/Transaction.cpp b/src/server/shared/Database/Transaction.cpp
index 3dee865267b..b83b787a106 100644
--- a/src/server/shared/Database/Transaction.cpp
+++ b/src/server/shared/Database/Transaction.cpp
@@ -17,6 +17,7 @@
#include "DatabaseEnv.h"
#include "Transaction.h"
+#include <mysqld_error.h>
//- Append a raw ad-hoc query to the transaction
void Transaction::Append(const char* sql)
@@ -74,14 +75,15 @@ void Transaction::Cleanup()
bool TransactionTask::Execute()
{
- if (m_conn->ExecuteTransaction(m_trans))
+ int errorCode = m_conn->ExecuteTransaction(m_trans);
+ if (!errorCode)
return true;
- if (m_conn->GetLastError() == 1213)
+ if (errorCode == ER_LOCK_DEADLOCK)
{
uint8 loopBreaker = 5; // Handle MySQL Errno 1213 without extending deadlock to the core itself
for (uint8 i = 0; i < loopBreaker; ++i)
- if (m_conn->ExecuteTransaction(m_trans))
+ if (!m_conn->ExecuteTransaction(m_trans))
return true;
}
diff --git a/src/server/shared/Packets/ByteBuffer.h b/src/server/shared/Packets/ByteBuffer.h
index 1063883d217..afa10207321 100644
--- a/src/server/shared/Packets/ByteBuffer.h
+++ b/src/server/shared/Packets/ByteBuffer.h
@@ -544,6 +544,12 @@ class ByteBuffer
append(str.c_str(), len);
}
+ void WriteString(char const* str, size_t len)
+ {
+ if (len)
+ append(str, len);
+ }
+
uint32 ReadPackedTime()
{
uint32 packedDate = read<uint32>();
diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist
index 9cb25475ecf..0327d0f8884 100644
--- a/src/server/worldserver/worldserver.conf.dist
+++ b/src/server/worldserver/worldserver.conf.dist
@@ -1060,12 +1060,31 @@ Server.LoginInfo = 0
Command.LookupMaxResults = 0
#
-# AllowTickets
-# Description: Allow/disallow sending new tickets.
+# Ticket.SystemStatus
+# Description: Enable/disable the ticket system. This disables the whole customer
+# support UI after trying to send a ticket in disabled state
+# (MessageBox: "GM Help Tickets are currently unavailable.").
+# UI remains disabled until the character relogs.
# Default: 1 - (Enabled)
# 0 - (Disabled)
-AllowTickets = 1
+Ticket.SystemStatus = 1
+
+#
+# Ticket.SubmitTicket
+# Description: Allow/disallow opening new tickets.
+# Default: 0 - (Disabled)
+# 1 - (Enabled, Experimental)
+
+Ticket.SubmitTicket = 0
+
+#
+# Ticket.SubmitBug
+# Description: Allow/disallow opening new bug reports.
+# Default: 0 - (Disabled)
+# 1 - (Enabled, Experimental)
+
+Ticket.SubmitBug = 0
#
# DungeonFinder.OptionsMask