Skip to content

Commit

Permalink
Fix dungeon reset exploit when switching leader.
Browse files Browse the repository at this point in the history
1. A enters dungeon.

2. B enters dungeon.

3. A invites B to group.

4. A promotes B to leader.

5. A relogs.

6. A finds himself in B's instance but at his old position on the map.
  • Loading branch information
ratkosrb committed Oct 2, 2024
1 parent 4bb5b25 commit 827ea07
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 12 deletions.
22 changes: 22 additions & 0 deletions sql/migrations/20241002031703_characters.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
DROP PROCEDURE IF EXISTS add_migration;
DELIMITER ??
CREATE PROCEDURE `add_migration`()
BEGIN
DECLARE v INT DEFAULT 1;
SET v = (SELECT COUNT(*) FROM `migrations` WHERE `id`='20241002031703');
IF v = 0 THEN
INSERT INTO `migrations` VALUES ('20241002031703');
-- Add your query below.


-- Add instance id player logged out inside of to characters table to prevent exploits.
ALTER TABLE `characters`
ADD COLUMN `instance_id` INT(11) UNSIGNED NOT NULL DEFAULT '0' AFTER `map`;


-- End of migration.
END IF;
END??
DELIMITER ;
CALL add_migration();
DROP PROCEDURE IF EXISTS add_migration;
25 changes: 19 additions & 6 deletions src/game/Group/Group.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1688,11 +1688,12 @@ void Group::_setLeader(ObjectGuid guid)
if (slot == m_memberSlots.end())
return;

MANGOS_ASSERT(guid != m_leaderGuid);

if (!isBGGroup())
{
uint32 slot_lowguid = slot->guid.GetCounter();

uint32 leader_lowguid = m_leaderGuid.GetCounter();
uint32 newLeaderLowGuid = slot->guid.GetCounter();
uint32 oldLeaderLowGuid = m_leaderGuid.GetCounter();

// TODO: set a time limit to have this function run rarely cause it can be slow
CharacterDatabase.BeginTransaction(m_Id);
Expand All @@ -1705,7 +1706,7 @@ void Group::_setLeader(ObjectGuid guid)
CharacterDatabase.PExecute(
"DELETE FROM `group_instance` WHERE `leader_guid`='%u' AND (`permanent` = 1 OR "
"`instance` IN (SELECT `instance` FROM `character_instance` WHERE `guid` = '%u')"
")", leader_lowguid, slot_lowguid);
")", oldLeaderLowGuid, newLeaderLowGuid);

Player* player = sObjectMgr.GetPlayer(slot->guid);

Expand All @@ -1718,22 +1719,34 @@ void Group::_setLeader(ObjectGuid guid)
itr->second.state->RemoveGroup(this);
m_boundInstances.erase(itr++);
}
else if (InstancePlayerBind* pPersonalBind = player->GetBoundInstance(itr->first))
{
// if the new leader already has a personal save for this instance, then remove the current group save
if (Player* pOldLeader = sObjectMgr.GetPlayer(m_leaderGuid))
if (!pOldLeader->GetBoundInstance(itr->first))
pOldLeader->BindToInstance(itr->second.state, itr->second.perm, false);

CharacterDatabase.PExecute("DELETE FROM `group_instance` WHERE `leader_guid` = '%u' AND `instance` = '%u'",
oldLeaderLowGuid, itr->second.state->GetInstanceId());
itr->second.state->RemoveGroup(this);
m_boundInstances.erase(itr++);
}
else
++itr;
}
}

// update the group's solo binds to the new leader
CharacterDatabase.PExecute("UPDATE `group_instance` SET `leader_guid`='%u' WHERE `leader_guid` = '%u'",
slot_lowguid, leader_lowguid);
newLeaderLowGuid, oldLeaderLowGuid);

// copy the permanent binds from the new leader to the group
// overwriting the solo binds with permanent ones if necessary
// in the DB those have been deleted already
Player::ConvertInstancesToGroup(player, this, slot->guid);

// update the group leader
CharacterDatabase.PExecute("UPDATE `groups` SET `leader_guid`='%u' WHERE `group_id`='%u'", slot_lowguid, m_Id);
CharacterDatabase.PExecute("UPDATE `groups` SET `leader_guid`='%u' WHERE `group_id`='%u'", newLeaderLowGuid, m_Id);
CharacterDatabase.CommitTransaction();
}

Expand Down
2 changes: 1 addition & 1 deletion src/game/Handlers/CharacterHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ bool LoginQueryHolder::Initialize()
"`reset_talents_time`, `transport_guid`, `transport_x`, `transport_y`, `transport_z`, `transport_o`, `extra_flags`, `stable_slots`, `at_login_flags`, `zone`, `online`, `death_expire_time`, `current_taxi_path`, "
"`honor_rank_points`, `honor_highest_rank`, `honor_standing`, `honor_last_week_hk`, `honor_last_week_cp`, `honor_stored_hk`, `honor_stored_dk`, "
"`watched_faction`, `drunk`, `health`, `power1`, `power2`, `power3`, `power4`, `power5`, `explored_zones`, `equipment_cache`, `ammo_id`, `action_bars`, "
"`world_phase_mask`, `create_time` FROM `characters` WHERE `guid` = '%u'", m_guid.GetCounter());
"`world_phase_mask`, `create_time`, `instance_id` FROM `characters` WHERE `guid` = '%u'", m_guid.GetCounter());
res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADGROUP, "SELECT `group_id` FROM `group_member` WHERE `member_guid` ='%u'", m_guid.GetCounter());
res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADBOUNDINSTANCES, "SELECT `id`, `permanent`, `map`, `reset_time` FROM `character_instance` LEFT JOIN `instance` ON `instance` = `id` WHERE `guid` = '%u'", m_guid.GetCounter());
res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADAURAS, "SELECT `caster_guid`, `item_guid`, `spell`, `stacks`, `charges`, `base_points0`, `base_points1`, `base_points2`, `periodic_time0`, `periodic_time1`, `periodic_time2`, `max_duration`, `duration`, `effect_index_mask` FROM `character_aura` WHERE `guid` = '%u'", m_guid.GetCounter());
Expand Down
13 changes: 8 additions & 5 deletions src/game/Objects/Player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15083,8 +15083,8 @@ bool Player::LoadFromDB(ObjectGuid guid, SqlQueryHolder* holder)
//"honor_rank_points, honor_highest_rank, honor_standing, honor_last_week_hk, honor_last_week_cp, honor_stored_hk, honor_stored_dk,"
// 48 49 50 51 52 53 54 55 56 57 58 59
//"watched_faction, drunk, health, power1, power2, power3, power4, power5, explored_zones, equipment_cache, ammo_id, action_bars,"
// 60 61
//"world_phase_mask, create_time FROM characters WHERE guid = '%u'", GUID_LOPART(m_guid));
// 60 61 62
//"world_phase_mask, create_time, instance_id FROM characters WHERE guid = '%u'", GUID_LOPART(m_guid));

std::unique_ptr<QueryResult> result = holder->TakeResult(PLAYER_LOGIN_QUERY_LOADFROM);

Expand Down Expand Up @@ -15341,7 +15341,8 @@ bool Player::LoadFromDB(ObjectGuid guid, SqlQueryHolder* holder)
if (mapEntry && mapEntry->IsDungeon())
{
// if the player is in an instance and it has been reset in the meantime teleport him to the entrance
if (!state)
uint32 instanceId = fields[62].GetUInt32();
if (!state || state->GetInstanceId() != instanceId)
{
AreaTriggerTeleport const* at = sObjectMgr.GetGoBackTrigger(GetMapId());
if (at)
Expand Down Expand Up @@ -16848,7 +16849,7 @@ void Player::SaveToDB(bool online, bool force)
static SqlStatementID insChar;

SqlStatement uberInsert = CharacterDatabase.CreateStatement(insChar, "REPLACE INTO `characters` (`guid`, `account`, `name`, `race`, `class`, `gender`, `level`, `xp`, `money`, `skin`, `face`, `hair_style`, `hair_color`, `facial_hair`, `bank_bag_slots`, `player_flags`,"
"`map`, `position_x`, `position_y`, `position_z`, `orientation`, "
"`map`, `instance_id`, `position_x`, `position_y`, `position_z`, `orientation`, "
"`transport_guid`, `transport_x`, `transport_y`, `transport_z`, `transport_o`, "
"`known_taxi_mask`, `current_taxi_path`, `online`, `played_time_total`, `played_time_level`, "
"`rest_bonus`, `logout_time`, `is_logout_resting`, `reset_talents_multiplier`, `reset_talents_time`, "
Expand All @@ -16857,7 +16858,7 @@ void Player::SaveToDB(bool online, bool force)
"`watched_faction`, `drunk`, `health`, `power1`, `power2`, `power3`, `power4`, `power5`, "
"`explored_zones`, `equipment_cache`, `ammo_id`, `action_bars`, `world_phase_mask`, `create_time`) "
"VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, "
"?, ?, ?, ?, ?, "
"?, ?, ?, ?, ?, ?, "
"?, ?, ?, ?, ?, "
"?, ?, ?, ?, ?, "
"?, ?, ?, ?, ?, "
Expand Down Expand Up @@ -16891,6 +16892,7 @@ void Player::SaveToDB(bool online, bool force)
if (!IsBeingTeleported())
{
uberInsert.addUInt32(GetMapId());
uberInsert.addUInt32(GetInstanceId());
uberInsert.addFloat(finiteAlways(GetPositionX()));
uberInsert.addFloat(finiteAlways(GetPositionY()));
uberInsert.addFloat(finiteAlways(GetPositionZ()));
Expand All @@ -16899,6 +16901,7 @@ void Player::SaveToDB(bool online, bool force)
else
{
uberInsert.addUInt32(GetTeleportDest().mapId);
uberInsert.addUInt32(0);
uberInsert.addFloat(finiteAlways(GetTeleportDest().x));
uberInsert.addFloat(finiteAlways(GetTeleportDest().y));
uberInsert.addFloat(finiteAlways(GetTeleportDest().z));
Expand Down

0 comments on commit 827ea07

Please sign in to comment.