Skip to content

Instantly share code, notes, and snippets.

@Ovahlord
Last active December 26, 2022 11:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save Ovahlord/c9a26aa5b4d1292effaa15383ad07e0b to your computer and use it in GitHub Desktop.
Save Ovahlord/c9a26aa5b4d1292effaa15383ad07e0b to your computer and use it in GitHub Desktop.
Spell and Loot batching
diff --git a/src/server/game/Entities/Player/ActionBatchObject.cpp b/src/server/game/Entities/Player/ActionBatchObject.cpp
new file mode 100644
index 0000000000..dfefef657a
--- /dev/null
+++ b/src/server/game/Entities/Player/ActionBatchObject.cpp
@@ -0,0 +1,126 @@
+/*
+* Copyright (C) 2008-2018 TrinityCore <https://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 "ActionBatchObject.h"
+#include "SpellInfo.h"
+#include "SpellMgr.h"
+#include "WorldSession.h"
+
+ActionBatchObject::ActionBatchObject(Player* owner) : m_owner(owner)
+{
+}
+
+void ActionBatchObject::CreateBatchObject(WorldPacket& data)
+{
+ if (IsPacketBatchable(data)) // checking for some special cases (eg. self casts)
+ m_packetBatch.push(data);
+}
+
+void ActionBatchObject::ProcessBatchedObjects()
+{
+ while (!m_packetBatch.empty())
+ {
+ WorldPacket data = m_packetBatch.front();
+
+ WorldSession* session = m_owner->GetSession();
+
+ switch (data.GetOpcode())
+ {
+ case CMSG_CAST_SPELL:
+ session->HandleCastSpellOpcode(data);
+ break;
+ case CMSG_CANCEL_CAST:
+ session->HandleCancelCastOpcode(data);
+ break;
+ case CMSG_CANCEL_AURA:
+ session->HandleCancelAuraOpcode(data);
+ break;
+ case CMSG_CANCEL_AUTO_REPEAT_SPELL:
+ session->HandleCancelAutoRepeatSpellOpcode(data);
+ break;
+ case CMSG_CANCEL_CHANNELLING:
+ session->HandleCancelChanneling(data);
+ break;
+ case CMSG_CANCEL_GROWTH_AURA:
+ session->HandleCancelGrowthAuraOpcode(data);
+ break;
+ case CMSG_CANCEL_MOUNT_AURA:
+ session->HandleCancelMountAuraOpcode(data);
+ break;
+ case CMSG_LOOT:
+ session->HandleLootOpcode(data);
+ break;
+ case CMSG_LOOT_CURRENCY:
+ session->HandleLootCurrencyOpcode(data);
+ break;
+ case CMSG_LOOT_MASTER_GIVE:
+ session->HandleLootMasterGiveOpcode(data);
+ break;
+ case CMSG_LOOT_METHOD:
+ session->HandleLootMethodOpcode(data);
+ break;
+ case CMSG_LOOT_MONEY:
+ session->HandleLootMoneyOpcode(data);
+ break;
+ case CMSG_LOOT_RELEASE:
+ session->HandleLootReleaseOpcode(data);
+ break;
+ case CMSG_LOOT_ROLL:
+ session->HandleLootRoll(data);
+ break;
+ default:
+ break;
+ }
+ m_packetBatch.pop();
+ }
+}
+
+bool ActionBatchObject::IsPacketBatchable(WorldPacket& data) const
+{
+ switch (data.GetOpcode())
+ {
+ case CMSG_CAST_SPELL:
+ {
+ if (m_owner->m_unitMovedByMe != m_owner) // vehicle casts and mind controls are also getting batched
+ return true;
+
+ // only reading the spell targets for now
+ data.read_skip<uint8>(); // cast count
+ data.read_skip<uint32>(); // spell Id
+ data.read_skip<uint32>(); // glyph index
+ data.read_skip<uint8>(); // cast flags
+ SpellCastTargets targets;
+ targets.Read(data, m_owner);
+ data.rfinish();
+
+ // if we target ourself the cast will be instant. Otherwise it will be batched
+ if (targets.GetUnitTarget() && targets.GetUnitTarget() == m_owner)
+ {
+ if (WorldSession* session = m_owner->GetSession())
+ session->HandleCastSpellOpcode(data);
+
+ return false;
+ }
+
+ return true;
+ }
+ default:
+ return true;
+ }
+
+ return true;
+}
diff --git a/src/server/game/Entities/Player/ActionBatchObject.h b/src/server/game/Entities/Player/ActionBatchObject.h
new file mode 100644
index 0000000000..2fb7b57bd6
--- /dev/null
+++ b/src/server/game/Entities/Player/ActionBatchObject.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2008-2018 TrinityCore <https://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 ActionBatchObject_h__
+#define ActionBatchObject_h__
+
+#include "Define.h"
+#include "Spell.h"
+#include "WorldPacket.h"
+
+class TC_GAME_API ActionBatchObject
+{
+ public:
+ ActionBatchObject(Player* owner);
+
+ void CreateBatchObject(WorldPacket& data);
+ void ProcessBatchedObjects();
+
+ bool IsPacketBatchable(WorldPacket& data) const;
+
+ private:
+ // Packet storage which contains all stored up packets
+ std::queue<WorldPacket> m_packetBatch;
+
+ // Player owner who is going to process all batched up packets
+ Player* m_owner;
+};
+
+#endif // ActionBatchObject_h__
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index df7d7e481f..1a8406c819 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -19,6 +19,7 @@
#include "Player.h"
#include "AccountMgr.h"
#include "AchievementMgr.h"
+#include "ActionBatchObject.h"
#include "Archaeology.h"
#include "ArenaTeam.h"
#include "ArenaTeamMgr.h"
@@ -413,6 +414,7 @@ Player::Player(WorldSession* session): Unit(true)
m_reputationMgr = new ReputationMgr(this);
_hasValidLFGLeavePoint = false;
_archaeology = new Archaeology(this);
+ m_actionBatchObjects = new ActionBatchObject(this);
}
Player::~Player()
@@ -447,6 +449,7 @@ Player::~Player()
delete m_reputationMgr;
delete _cinematicMgr;
delete _archaeology;
+ delete m_actionBatchObjects;
for (uint8 i = 0; i < VOID_STORAGE_MAX_SLOT; ++i)
delete _voidStorageItems[i];
@@ -1388,6 +1391,12 @@ void Player::Update(uint32 p_time)
if (IsHasDelayedTeleport() && IsAlive())
TeleportTo(m_teleport_dest, m_teleport_options);
+ m_batchProcessingTimer.Update(p_time);
+ if (m_batchProcessingTimer.Passed())
+ {
+ m_actionBatchObjects->ProcessBatchedObjects();
+ m_batchProcessingTimer.Reset(400); // confirmed by blueposts
+ }
}
void Player::setDeathState(DeathState s)
@@ -28412,3 +28421,8 @@ void Player::SendTamePetFailure(PetTameFailureReason reason)
data << uint8(reason);
SendDirectMessage(&data);
}
+
+void Player::AddBatchAction(WorldPacket& packet)
+{
+ m_actionBatchObjects->CreateBatchObject(packet);
+}
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index da89290b97..f1d5ec6dfc 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -56,7 +56,7 @@ struct VendorItem;
template<class T>
class AchievementMgr;
-
+class ActionBatchObject;
class Archaeology;
class Bag;
class Battleground;
@@ -2373,6 +2373,9 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
void DeleteFromPlayerPetDataStore(uint32 petNumber);
void AddToPlayerPetDataStore(PlayerPetData* playerPetData);
+ // Action Batching
+ void AddBatchAction(WorldPacket& packet);
+
protected:
// Gamemaster whisper whitelist
GuidList WhisperList;
@@ -2751,6 +2754,10 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
Archaeology* _archaeology;
std::vector<PlayerPetData*> PlayerPetDataStore;
+
+ // Action batching system
+ TimeTrackerSmall m_batchProcessingTimer;
+ ActionBatchObject* m_actionBatchObjects;
};
TC_GAME_API void AddItemsSetItem(Player* player, Item* item);
diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp
index c9097271e9..de629d7625 100644
--- a/src/server/game/Handlers/MiscHandler.cpp
+++ b/src/server/game/Handlers/MiscHandler.cpp
@@ -2061,3 +2061,8 @@ void WorldSession::HandleRequestResearchHistory(WorldPacket & /*recv_data*/)
if (Player* player = GetPlayer())
player->NotifyRequestResearchHistory();
}
+
+void WorldSession::HandleBatchedAction(WorldPacket& recv_data)
+{
+ _player->AddBatchAction(recv_data);
+}
diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp
index 9df9b15eeb..7399d9fa4a 100644
--- a/src/server/game/Server/Protocol/Opcodes.cpp
+++ b/src/server/game/Server/Protocol/Opcodes.cpp
@@ -147,16 +147,16 @@ void OpcodeTable::Initialize()
DEFINE_OPCODE_HANDLER(CMSG_CALENDAR_GUILD_FILTER, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleCalendarGuildFilter );
DEFINE_OPCODE_HANDLER(CMSG_CALENDAR_REMOVE_EVENT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleCalendarRemoveEvent );
DEFINE_OPCODE_HANDLER(CMSG_CALENDAR_UPDATE_EVENT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleCalendarUpdateEvent );
- DEFINE_OPCODE_HANDLER(CMSG_CANCEL_AURA, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleCancelAuraOpcode );
- DEFINE_OPCODE_HANDLER(CMSG_CANCEL_AUTO_REPEAT_SPELL, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleCancelAutoRepeatSpellOpcode);
- DEFINE_OPCODE_HANDLER(CMSG_CANCEL_CAST, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleCancelCastOpcode );
- DEFINE_OPCODE_HANDLER(CMSG_CANCEL_CHANNELLING, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleCancelChanneling );
- DEFINE_OPCODE_HANDLER(CMSG_CANCEL_GROWTH_AURA, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleCancelGrowthAuraOpcode );
- DEFINE_OPCODE_HANDLER(CMSG_CANCEL_MOUNT_AURA, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleCancelMountAuraOpcode );
+ DEFINE_OPCODE_HANDLER(CMSG_CANCEL_AURA, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleBatchedAction );
+ DEFINE_OPCODE_HANDLER(CMSG_CANCEL_AUTO_REPEAT_SPELL, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleBatchedAction );
+ DEFINE_OPCODE_HANDLER(CMSG_CANCEL_CAST, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleBatchedAction );
+ DEFINE_OPCODE_HANDLER(CMSG_CANCEL_CHANNELLING, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleBatchedAction );
+ DEFINE_OPCODE_HANDLER(CMSG_CANCEL_GROWTH_AURA, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleBatchedAction );
+ DEFINE_OPCODE_HANDLER(CMSG_CANCEL_MOUNT_AURA, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleBatchedAction );
DEFINE_OPCODE_HANDLER(CMSG_CANCEL_QUEUED_SPELL, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL );
DEFINE_OPCODE_HANDLER(CMSG_CANCEL_TEMP_ENCHANTMENT, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleCancelTempEnchantmentOpcode);
DEFINE_OPCODE_HANDLER(CMSG_CANCEL_TRADE, STATUS_LOGGEDIN_OR_RECENTLY_LOGGOUT, PROCESS_THREADUNSAFE, &WorldSession::HandleCancelTradeOpcode);
- DEFINE_OPCODE_HANDLER(CMSG_CAST_SPELL, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleCastSpellOpcode );
+ DEFINE_OPCODE_HANDLER(CMSG_CAST_SPELL, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleBatchedAction );
DEFINE_OPCODE_HANDLER(CMSG_CHANGEPLAYER_DIFFICULTY, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleChangePlayerDifficulty );
DEFINE_OPCODE_HANDLER(CMSG_CHANGE_SEATS_ON_CONTROLLED_VEHICLE, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleChangeSeatsOnControlledVehicle);
DEFINE_OPCODE_HANDLER(CMSG_CHANNEL_ANNOUNCEMENTS, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleChannelAnnouncements );
@@ -337,13 +337,13 @@ void OpcodeTable::Initialize()
DEFINE_OPCODE_HANDLER(CMSG_LOGOUT_CANCEL, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleLogoutCancelOpcode );
DEFINE_OPCODE_HANDLER(CMSG_LOGOUT_REQUEST, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleLogoutRequestOpcode );
DEFINE_OPCODE_HANDLER(CMSG_LOG_DISCONNECT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_EarlyProccess );
- DEFINE_OPCODE_HANDLER(CMSG_LOOT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleLootOpcode );
- DEFINE_OPCODE_HANDLER(CMSG_LOOT_CURRENCY, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleLootCurrencyOpcode );
- DEFINE_OPCODE_HANDLER(CMSG_LOOT_MASTER_GIVE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleLootMasterGiveOpcode );
- DEFINE_OPCODE_HANDLER(CMSG_LOOT_METHOD, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleLootMethodOpcode );
- DEFINE_OPCODE_HANDLER(CMSG_LOOT_MONEY, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleLootMoneyOpcode );
- DEFINE_OPCODE_HANDLER(CMSG_LOOT_RELEASE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleLootReleaseOpcode );
- DEFINE_OPCODE_HANDLER(CMSG_LOOT_ROLL, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleLootRoll );
+ DEFINE_OPCODE_HANDLER(CMSG_LOOT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleBatchedAction );
+ DEFINE_OPCODE_HANDLER(CMSG_LOOT_CURRENCY, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleBatchedAction );
+ DEFINE_OPCODE_HANDLER(CMSG_LOOT_MASTER_GIVE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleBatchedAction );
+ DEFINE_OPCODE_HANDLER(CMSG_LOOT_METHOD, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleBatchedAction );
+ DEFINE_OPCODE_HANDLER(CMSG_LOOT_MONEY, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleBatchedAction );
+ DEFINE_OPCODE_HANDLER(CMSG_LOOT_RELEASE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleBatchedAction );
+ DEFINE_OPCODE_HANDLER(CMSG_LOOT_ROLL, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleBatchedAction );
DEFINE_OPCODE_HANDLER(CMSG_MAIL_CREATE_TEXT_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleMailCreateTextItem );
DEFINE_OPCODE_HANDLER(CMSG_MAIL_DELETE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleMailDelete );
DEFINE_OPCODE_HANDLER(CMSG_MAIL_MARK_AS_READ, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleMailMarkAsRead );
diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h
index 1f775bc56d..262e137a40 100644
--- a/src/server/game/Server/WorldSession.h
+++ b/src/server/game/Server/WorldSession.h
@@ -1067,6 +1067,7 @@ class TC_GAME_API WorldSession
void HandleViolenceLevel(WorldPacket& recvPacket);
void HandleObjectUpdateFailedOpcode(WorldPacket& recvPacket);
void HandleRequestCategoryCooldowns(WorldPacket& recvPacket);
+ void HandleBatchedAction(WorldPacket& recvPacket);
void SendStreamingMovie();
void HandleRequestResearchHistory(WorldPacket& recv_data);
int32 HandleEnableNagleAlgorithm();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment