Skip to content

feat(Maps): implement MapPartitioned for parallel continent map updates#26445

Closed
hanu-14 wants to merge 7 commits into
azerothcore:masterfrom
hanu-14:master
Closed

feat(Maps): implement MapPartitioned for parallel continent map updates#26445
hanu-14 wants to merge 7 commits into
azerothcore:masterfrom
hanu-14:master

Conversation

@hanu-14

@hanu-14 hanu-14 commented Jul 3, 2026

Copy link
Copy Markdown

Changes Proposed:

This PR proposes changes to:

  • Core (units, players, creatures, game systems).
  • Scripts (bosses, spell scripts, creature scripts).
  • Database (SAI, creatures, etc).

Adds MapPartitioned class that splits continent maps into N partitions by X coordinate. Each partition runs its own Update on the MapUpdater thread pool, enabling parallel entity processing on large continents.

New classes

  • MapPartitioned: extends Map; creates N child Map instances at construction, routes players by position, schedules partition updates on MapUpdater thread pool
  • MapPartition: thin Map subclass with position-guarded respawn handling - each partition only processes creatures/GOs in its X-range

Collision data

Shared via existing MapCollisionData(parent) mechanism: static tree + nav mesh via shared_ptr; dynamic tree is per-partition.

Integration

  • DoForAllMaps/DoForAllMapsWithMapId iterates over all partitions so GM commands, statistics, and scripts work transparently
  • MapMgr::FindMap resolves instance IDs against partitions for direct lookup
  • GridObjectLoader filters spawns by partition during grid load to prevent duplicate boundary objects

New config

  • MapUpdate.PartitionCount (default: 4) - number of partitions per continent

AI-assisted Pull Requests

Important

While the use of AI tools when preparing pull requests is not prohibited, contributors must clearly disclose when such tools have been used and specify the model involved.

Contributors are also expected to fully understand the changes they are submitting and must be able to explain and justify those changes when requested by maintainers.

  • AI tools (e.g. ChatGPT, Claude, or similar) were used entirely or partially in preparing this pull request. Please specify which tools were used, if any.

opencode

Issues Addressed:

SOURCE:

The changes have been validated through:

  • Live research (checked on live servers, e.g Classic WotLK, Retail, etc.)
  • Sniffs (remember to share them with the open source community!)
  • Video evidence, knowledge databases or other public sources (e.g forums, Wowhead, etc.)
  • The changes promoted by this pull request come partially or entirely from another project (cherry-pick). Cherry-picks must be committed using the proper --author tag in order to be accepted, thus crediting the original authors, unless otherwise unable to be found

Tests Performed:

This PR has been:

  • Tested in-game by the author.
  • Tested in-game by other community members/someone else other than the author/has been live on production servers.
  • This pull request requires further testing and may have edge cases to be tested.

Builds successfully with GCC 14 and Clang 18 on Ubuntu (pending CI re-run after latest fixes).

How to Test the Changes:

  • This pull request can be tested by following the reproduction steps provided in the linked issue
  • This pull request requires further testing. Provide steps to test your changes.
  1. Set MapUpdate.PartitionCount in worldserver.conf (default: 4)
  2. Start server, observe that continent maps (e.g. Eastern Kingdoms, Kalimdor) are partitioned
  3. Verify players can move, quest, fight normally
  4. Check that .server info and .npc info commands work across partitions

Known Issues and TODO List:

  • Same-map teleport (blink, flight path) across partition boundaries keeps player in original partition until next cross-map teleport
  • Weather timers are per-partition (minor desync possible between partitions of the same continent)
  • Investigate weather consistency across partitions

Add MapPartitioned class that splits continent maps into N partitions
by X coordinate. Each partition runs its own Update on the MapUpdater
thread pool, enabling parallel entity processing on large continents.

Key changes:
- MapPartitioned: new class extending Map; creates N child Map instances
  at construction, routes players by position, schedules partition
  updates on MapUpdater
- MapPartition: thin Map subclass with position-guarded respawn handling
  to prevent duplicate creature/GO respawns across partitions
- Collision data shared via existing MapCollisionData(parent) mechanism
  (static tree + nav mesh shared, dynamic tree per-partition)
- DoForAllMaps/DoForAllMapsWithMapId iterates over partitions
- CONFIG_MAP_PARTITION_COUNT (default: 4) controls partition count

Configuration: MapUpdate.PartitionCount = 4
Copilot AI review requested due to automatic review settings July 3, 2026 19:59
@github-actions github-actions Bot added CORE Related to the core file-cpp Used to trigger the matrix build labels Jul 3, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a new MapPartitioned implementation intended to parallelize continent (“world map”) updates by splitting a base map into multiple X-coordinate partitions and scheduling each partition’s Map::Update() on the existing MapUpdater thread pool.

Changes:

  • Added MapPartitioned / MapPartition classes and integrated them into base-map creation for non-instanceable maps.
  • Updated MapMgr iteration helpers to treat partitions as transparent map units for commands/stats/scripts.
  • Added a new configuration option MapUpdate.PartitionCount (default 4) to control partition count.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
src/server/game/World/WorldConfig.h Adds config enum for partition count.
src/server/game/World/WorldConfig.cpp Registers MapUpdate.PartitionCount with default value.
src/server/game/Maps/MapPartitioned.h Declares MapPartitioned and MapPartition map types.
src/server/game/Maps/MapPartitioned.cpp Implements partition creation, routing, update scheduling, and respawn filtering logic.
src/server/game/Maps/MapMgr.h Updates DoForAllMaps* helpers to iterate partitions.
src/server/game/Maps/MapMgr.cpp Creates partitioned maps for non-instanceable maps and extends FindMap.
src/server/game/Maps/Map.h Adds partition-type helpers and makes respawn hooks overridable/accessible for partition filtering.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/server/game/Maps/MapPartitioned.cpp Outdated
Comment on lines +96 to +100
if (!IsPositionInPartition(data->posX))
{
RemoveCreatureRespawnTime(spawnId);
return;
}
Comment thread src/server/game/Maps/MapPartitioned.cpp Outdated
Comment on lines +114 to +118
if (!IsPositionInPartition(data->posX))
{
RemoveGORespawnTime(spawnId);
return;
}
Comment on lines +18 to +25
#include "MapPartitioned.h"
#include "MapMgr.h"
#include "Player.h"
#include "Log.h"
#include "ObjectMgr.h"
#include "SpawnData.h"
#include "ScriptMgr.h"

Comment on lines +125 to +133
void MapPartitioned::LoadRespawnTimes()
{
for (Map* partition : m_Partitions)
{
partition->LoadRespawnTimes();
}

Map::LoadRespawnTimes();
}
Comment on lines +59 to +65
Map* MapPartitioned::GetPartitionForPosition(float x, float y) const
{
uint32 index = GetPartitionIndex(x);
if (index < m_Partitions.size())
return m_Partitions[index];
return m_Partitions[0];
}
Comment on lines +51 to +57
uint32 MapPartitioned::GetPartitionIndex(float x) const
{
float const MAP_HALF_SIZE = 17066.0f;
float clampedX = std::max(-MAP_HALF_SIZE, std::min(x, MAP_HALF_SIZE));
float normalizedX = (clampedX + MAP_HALF_SIZE) / (2.0f * MAP_HALF_SIZE);
return uint32(normalizedX * m_partitionCount) % m_partitionCount;
}
Comment on lines 84 to 88
if (entry->Instanceable())
map = new MapInstanced(id);
else
map = new Map(id, 0, REGULAR_DIFFICULTY);
map = new MapPartitioned(id, sWorld->getIntConfig(CONFIG_MAP_PARTITION_COUNT));

@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds map partitioning support to AzerothCore. New MapPartition and MapPartitioned classes split a base continent map into configurable partitions by X-coordinate. Map gains virtual respawn hooks and partition-cast helpers. MapMgr creates, finds, and iterates partitions, and a new CONFIG_MAP_PARTITION_COUNT config setting controls partition count.

Changes

Map Partitioning

Layer / File(s) Summary
Map base class partition hooks
src/server/game/Maps/Map.h
Adds IsPartitioned(), ToMapPartitioned()/const overload, makes LoadRespawnTimes, ProcessCreatureRespawn, ProcessGameObjectRespawn virtual, and inserts a protected: access specifier before respawn-time storage.
MapPartitioned and MapPartition implementation
src/server/game/Maps/MapPartitioned.h, src/server/game/Maps/MapPartitioned.cpp
New classes managing a vector of partitions: construction/destruction, X-based partition index computation, per-position/per-player partition lookup, per-partition respawn gating, and delegated lifecycle (respawn loading, map creation, visibility, update/delayed update, unload) and player add/remove/routing.
MapMgr integration and config
src/server/game/Maps/MapMgr.cpp, src/server/game/Maps/MapMgr.h, src/server/game/World/WorldConfig.h, src/server/game/World/WorldConfig.cpp
CreateBaseMap builds MapPartitioned for non-instanceable maps using CONFIG_MAP_PARTITION_COUNT; FindMap resolves instance IDs against partitions; DoForAllMaps/DoForAllMapsWithMapId iterate partitions; new CONFIG_MAP_PARTITION_COUNT enum value and default config (MapUpdate.PartitionCount = 4).

Estimated code review effort: 4 (Complex) | ~60 minutes

Related issues: Directly addresses "Feature: Map Partitioning Support for AzerothCore" (#22571), which requested splitting continent maps into partitions for parallel processing.

Suggested labels: enhancement, performance, maps

Suggested reviewers: Core maintainers familiar with the Maps subsystem and MapMgr threading model

🐰 A map once whole, now cut in four,

Each slice its own small world to explore,

Players glide across the seam unseen,

Respawns sorted where their patch has been,

Partitions hum — the continent purrs once more.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The partitioning feature is implemented, but same-map cross-partition movement and shared weather consistency are still listed as limitations for #22571. Finish cross-partition movement and move shared systems like weather back to the base map so the partitioned continents match the issue requirements.
✅ Passed checks (4 passed)
Check name Status Explanation
Out of Scope Changes check ✅ Passed All changes appear directly related to MapPartitioned support, map iteration, respawn routing, and the new partition-count config.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed The title clearly summarizes the main change: adding MapPartitioned for parallel continent map updates.
Description check ✅ Passed The description follows the template well, covers changes, AI use, issues, tests, setup steps, and known limitations.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (2)
src/server/game/Maps/MapPartitioned.cpp (1)

196-203: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Drop the redundant guard and braces in src/server/game/Maps/MapPartitioned.cpp:196-203

AddPlayerToMap() already assigns the child partition to player->SetMap(partition), so partition != this is effectively always true here. Remove the braces to match the style rule, and either drop the check or replace it with a real membership test if you meant to guard against an unrelated map.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/server/game/Maps/MapPartitioned.cpp` around lines 196 - 203, Remove the
redundant partition guard in MapPartitioned::RemovePlayerFromMap and match the
brace/style convention. Since AddPlayerToMap() already sets the child partition
via player->SetMap(partition), the partition != this check is unnecessary here;
either call partition->RemovePlayerFromMap(player, remove) directly after a null
check, or replace the condition with a real membership validation if that was
the intent.

Source: Coding guidelines

src/server/game/Maps/Map.h (1)

645-645: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Keep the protected scope narrow. This change makes the rest of Map’s internals and helper state available to every subclass, not just the respawn bookkeeping. Split the new state into a smaller protected block or keep unrelated members private.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/server/game/Maps/Map.h` at line 645, The Map class exposes too much
internal state under a broad protected scope, making unrelated members available
to all subclasses. Narrow the access in Map by keeping only the respawn-related
state in a small protected section and moving the rest of the internals/helper
members back to private, using the Map class and its respawn bookkeeping members
to locate the affected block.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/server/game/Maps/MapPartitioned.cpp`:
- Around line 46-49: MapPartitioned::CreatePartition is hardcoding instanceId to
0, which prevents MapMgr::FindMap from ever matching a partition in its
partition lookup path. Update CreatePartition so each MapPartition gets a unique
non-zero instance id derived from the partition index (for example, based on
index) while keeping the existing MapPartition constructor call and identifiers
like GetId(), m_partitionCount, and static_cast<uint8>(index) intact.
- Around line 125-133: The trailing Map::LoadRespawnTimes() call in
MapPartitioned::LoadRespawnTimes() is redundant because the base Map object is
never ticked by MapPartitioned::Update(), so its respawn data is never used.
Remove that base-object load and keep only the per-partition loading loop in
MapPartitioned::LoadRespawnTimes() so respawn times are loaded only for the maps
that actually run Update().
- Around line 87-121: Initial grid loading still bypasses partition filtering,
so update GridObjectLoader::LoadCreatures() and
GridObjectLoader::LoadGameObjects() to skip spawns whose coordinates are not in
the current partition using IsPositionInPartition, matching the respawn-time
checks already added in MapPartition::ProcessCreatureRespawn and
MapPartition::ProcessGameObjectRespawn. Make sure the partition-aware load path
filters both creature and gameobject spawns before instantiation so
MapPartitioned::LoadRespawnTimes does not cause duplicate off-partition objects
across partitions.
- Around line 189-194: Remove the eager player map assignment in
MapPartitioned::AddPlayerToMap and let Map::AddPlayerToMap handle SetMap only
after a successful add. Update the MapPartitioned::AddPlayerToMap flow so it
just finds the partition with GetPartitionForPlayer and forwards the player to
partition->AddPlayerToMap, avoiding any pre-set state that could leave Player
pointing at the partition when the add fails.

In `@src/server/game/Maps/MapPartitioned.h`:
- Around line 34-38: The partition index calculation is duplicated between
MapPartition and MapPartitioned, so extract the shared X-to-partition math into
a single helper (for example, a free function or static utility near
MapPartitioned) and have both GetPartitionIndex implementations call it. Keep
the normalization logic and MAP_HALF_SIZE handling in one place so
IsPositionInPartition and the respawn/player routing code cannot drift apart.

In `@src/server/game/World/WorldConfig.cpp`:
- Line 566: Add validation for CONFIG_MAP_PARTITION_COUNT in
WorldConfig::SetConfigValue so only safe non-zero values within the uint8 range
are accepted. The current MapUpdate.PartitionCount setting is passed into
MapPartitioned’s uint8 partitionCount and can truncate large values or allow 0,
which later breaks MapPartitioned::GetPartitionIndex. Update the config
registration for MapUpdate.PartitionCount to use a validator lambda like the
other numeric options in this file and reject 0 and values above 255.

---

Nitpick comments:
In `@src/server/game/Maps/Map.h`:
- Line 645: The Map class exposes too much internal state under a broad
protected scope, making unrelated members available to all subclasses. Narrow
the access in Map by keeping only the respawn-related state in a small protected
section and moving the rest of the internals/helper members back to private,
using the Map class and its respawn bookkeeping members to locate the affected
block.

In `@src/server/game/Maps/MapPartitioned.cpp`:
- Around line 196-203: Remove the redundant partition guard in
MapPartitioned::RemovePlayerFromMap and match the brace/style convention. Since
AddPlayerToMap() already sets the child partition via player->SetMap(partition),
the partition != this check is unnecessary here; either call
partition->RemovePlayerFromMap(player, remove) directly after a null check, or
replace the condition with a real membership validation if that was the intent.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro Plus

Run ID: f7a12367-b092-40bf-b58a-72a5262de5f2

📥 Commits

Reviewing files that changed from the base of the PR and between 931201d and 12994a8.

📒 Files selected for processing (7)
  • src/server/game/Maps/Map.h
  • src/server/game/Maps/MapMgr.cpp
  • src/server/game/Maps/MapMgr.h
  • src/server/game/Maps/MapPartitioned.cpp
  • src/server/game/Maps/MapPartitioned.h
  • src/server/game/World/WorldConfig.cpp
  • src/server/game/World/WorldConfig.h

Comment on lines +46 to +49
Map* MapPartitioned::CreatePartition(uint32 index)
{
return new MapPartition(GetId(), 0, REGULAR_DIFFICULTY, this, m_partitionCount, static_cast<uint8>(index));
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Every partition is created with instanceId = 0, so MapMgr::FindMap's partition lookup can never match.

CreatePartition hardcodes 0 for instanceId:

return new MapPartition(GetId(), 0, REGULAR_DIFFICULTY, this, m_partitionCount, static_cast<uint8>(index));

All partitions therefore have GetInstanceId() == 0. In MapMgr::FindMap (see MapMgr.cpp), instanceId == 0 already short-circuits to return the base MapPartitioned map before the partition-search loop runs, and for any non-zero instanceId the loop can never find a match since no partition has a non-zero id. The new partition-resolution code path added to FindMap is effectively dead.

Give each partition a unique, non-zero instance id (e.g. index + 1).

🐛 Proposed fix
 Map* MapPartitioned::CreatePartition(uint32 index)
 {
-    return new MapPartition(GetId(), 0, REGULAR_DIFFICULTY, this, m_partitionCount, static_cast<uint8>(index));
+    return new MapPartition(GetId(), index + 1, REGULAR_DIFFICULTY, this, m_partitionCount, static_cast<uint8>(index));
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/server/game/Maps/MapPartitioned.cpp` around lines 46 - 49,
MapPartitioned::CreatePartition is hardcoding instanceId to 0, which prevents
MapMgr::FindMap from ever matching a partition in its partition lookup path.
Update CreatePartition so each MapPartition gets a unique non-zero instance id
derived from the partition index (for example, based on index) while keeping the
existing MapPartition constructor call and identifiers like GetId(),
m_partitionCount, and static_cast<uint8>(index) intact.

Comment on lines +87 to +121
void MapPartition::ProcessCreatureRespawn(ObjectGuid::LowType spawnId)
{
CreatureData const* data = sObjectMgr->GetCreatureData(spawnId);
if (!data)
{
RemoveCreatureRespawnTime(spawnId);
return;
}

if (!IsPositionInPartition(data->posX))
{
RemoveCreatureRespawnTime(spawnId);
return;
}

Map::ProcessCreatureRespawn(spawnId);
}

void MapPartition::ProcessGameObjectRespawn(ObjectGuid::LowType spawnId)
{
GameObjectData const* data = sObjectMgr->GetGameObjectData(spawnId);
if (!data)
{
RemoveGORespawnTime(spawnId);
return;
}

if (!IsPositionInPartition(data->posX))
{
RemoveGORespawnTime(spawnId);
return;
}

Map::ProcessGameObjectRespawn(spawnId);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect how creature/GO grid loading decides to spawn vs. respawn, to check whether it bypasses ProcessCreatureRespawn/ProcessGameObjectRespawn
ast-grep outline src/server/game/Maps/Map.cpp --view expanded | head -100
rg -n 'GetCreatureRespawnTime|LoadGridObjects|ObjectGridLoader' src/server/game/Maps/Map.cpp src/server/game/Grids -A3 -B3

Repository: azerothcore/azerothcore-wotlk

Length of output: 1772


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- MapPartitioned.cpp outline ---'
ast-grep outline src/server/game/Maps/MapPartitioned.cpp --view expanded | head -200

echo
echo '--- Map.cpp outline (relevant symbols) ---'
ast-grep outline src/server/game/Maps/Map.cpp --view expanded | head -250

echo
echo '--- Search for grid-load spawn paths and respawn table usage ---'
rg -n 'ObjectGridLoader|LoadGrid|LoadCreatures|LoadGameObjects|GetCreatureRespawnTime|GetGORespawnTime|_creatureRespawnTimes|_gameObjectRespawnTimes|Respawn' src/server/game/Maps src/server/game/Grids src/server/game -g '!*.o' -A3 -B3

Repository: azerothcore/azerothcore-wotlk

Length of output: 50386


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- GridObjectLoader outline ---'
ast-grep outline src/server/game/Grids/GridObjectLoader.h --view expanded
echo
ast-grep outline src/server/game/Grids/GridObjectLoader.cpp --view expanded | head -200

echo
echo '--- GridObjectLoader implementation slices ---'
sed -n '1,240p' src/server/game/Grids/GridObjectLoader.cpp | cat -n
echo
echo '--- Map.cpp grid-load / respawn slices ---'
sed -n '180,240p' src/server/game/Maps/Map.cpp | cat -n
echo
sed -n '2480,2865p' src/server/game/Maps/Map.cpp | cat -n
echo
echo '--- MapPartitioned respawn/load slices ---'
sed -n '1,170p' src/server/game/Maps/MapPartitioned.cpp | cat -n

Repository: azerothcore/azerothcore-wotlk

Length of output: 31126


Initial grid loading still bypasses partition filtering

GridObjectLoader::LoadCreatures() / LoadGameObjects() still load every spawn in a grid without checking IsPositionInPartition, so this change only guards the respawn-timer path. Since MapPartitioned::LoadRespawnTimes() loads respawn state into every partition, boundary grids can still instantiate off-partition creatures/GOs and duplicate them across partitions.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/server/game/Maps/MapPartitioned.cpp` around lines 87 - 121, Initial grid
loading still bypasses partition filtering, so update
GridObjectLoader::LoadCreatures() and GridObjectLoader::LoadGameObjects() to
skip spawns whose coordinates are not in the current partition using
IsPositionInPartition, matching the respawn-time checks already added in
MapPartition::ProcessCreatureRespawn and MapPartition::ProcessGameObjectRespawn.
Make sure the partition-aware load path filters both creature and gameobject
spawns before instantiation so MapPartitioned::LoadRespawnTimes does not cause
duplicate off-partition objects across partitions.

Comment on lines +125 to +133
void MapPartitioned::LoadRespawnTimes()
{
for (Map* partition : m_Partitions)
{
partition->LoadRespawnTimes();
}

Map::LoadRespawnTimes();
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win

Map::LoadRespawnTimes() on the un-ticked base object loads data that is never used.

MapPartitioned::Update() (below) only schedules updates for m_Partitions; it never calls Map::Update() on this. Since respawn processing happens inside Map::Update()'s tick loop, the respawn times loaded into this via the trailing Map::LoadRespawnTimes(); call are never consumed — this is a full, redundant DB query and in-memory table for every partitioned continent that has no functional effect.

⚡ Proposed fix
 void MapPartitioned::LoadRespawnTimes()
 {
     for (Map* partition : m_Partitions)
     {
         partition->LoadRespawnTimes();
     }
-
-    Map::LoadRespawnTimes();
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
void MapPartitioned::LoadRespawnTimes()
{
for (Map* partition : m_Partitions)
{
partition->LoadRespawnTimes();
}
Map::LoadRespawnTimes();
}
void MapPartitioned::LoadRespawnTimes()
{
for (Map* partition : m_Partitions)
{
partition->LoadRespawnTimes();
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/server/game/Maps/MapPartitioned.cpp` around lines 125 - 133, The trailing
Map::LoadRespawnTimes() call in MapPartitioned::LoadRespawnTimes() is redundant
because the base Map object is never ticked by MapPartitioned::Update(), so its
respawn data is never used. Remove that base-object load and keep only the
per-partition loading loop in MapPartitioned::LoadRespawnTimes() so respawn
times are loaded only for the maps that actually run Update().

Comment on lines +189 to +194
bool MapPartitioned::AddPlayerToMap(Player* player)
{
Map* partition = GetPartitionForPlayer(player);
player->SetMap(partition);
return partition->AddPlayerToMap(player);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== MapPartitioned.cpp ==\n'
git ls-files 'src/server/game/Maps/MapPartitioned.cpp'
wc -l src/server/game/Maps/MapPartitioned.cpp
cat -n src/server/game/Maps/MapPartitioned.cpp | sed -n '170,210p'

printf '\n== Search AddPlayerToMap implementations ==\n'
rg -n "AddPlayerToMap\\(" src/server/game/Maps src/server/game -g'*.cpp' -g'*.h'

printf '\n== Map class outline ==\n'
ast-grep outline src/server/game/Maps/Map.h --view expanded || true
ast-grep outline src/server/game/Maps/Map.cpp --view expanded || true

Repository: azerothcore/azerothcore-wotlk

Length of output: 3574


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== MapPartitioned.cpp ==\n'
wc -l src/server/game/Maps/MapPartitioned.cpp
cat -n src/server/game/Maps/MapPartitioned.cpp | sed -n '180,205p'

printf '\n== Relevant Map APIs ==\n'
rg -n "class Map|bool AddPlayerToMap|void SetMap|SetMap\\(" src/server/game/Maps src/server/game/Entities src/server/game -g'*.h' -g'*.cpp' | sed -n '1,200p'

Repository: azerothcore/azerothcore-wotlk

Length of output: 10989


🏁 Script executed:

#!/bin/bash
set -euo pipefail
wc -l src/server/game/Maps/MapPartitioned.cpp
sed -n '180,205p' src/server/game/Maps/MapPartitioned.cpp
rg -n "bool Map::AddPlayerToMap|AddPlayerToMap\\(Player\\*|SetMap\\(" src/server/game/Maps src/server/game -g'*.cpp' -g'*.h'

Repository: azerothcore/azerothcore-wotlk

Length of output: 4939


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '245,310p' src/server/game/Maps/Map.cpp
printf '\n== InstanceMap::AddPlayerToMap ==\n'
sed -n '1980,2038p' src/server/game/Maps/Map.cpp
printf '\n== BattlegroundMap::AddPlayerToMap ==\n'
sed -n '2310,2345p' src/server/game/Maps/Map.cpp

Repository: azerothcore/azerothcore-wotlk

Length of output: 5732


🏁 Script executed:

#!/bin/bash
set -euo pipefail
sed -n '255,290p' src/server/game/Maps/Map.cpp

Repository: azerothcore/azerothcore-wotlk

Length of output: 1170


Remove the eager SetMap here
Map::AddPlayerToMap already sets the player’s map on success, so doing it before the add can leave the player pointing at a partition when AddPlayerToMap returns false.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/server/game/Maps/MapPartitioned.cpp` around lines 189 - 194, Remove the
eager player map assignment in MapPartitioned::AddPlayerToMap and let
Map::AddPlayerToMap handle SetMap only after a successful add. Update the
MapPartitioned::AddPlayerToMap flow so it just finds the partition with
GetPartitionForPlayer and forwards the player to partition->AddPlayerToMap,
avoiding any pre-set state that could leave Player pointing at the partition
when the add fails.

Comment on lines +34 to +38
bool IsPositionInPartition(float x) const;
uint32 GetPartitionIndex(float x) const;

uint8 m_partitionCount;
uint8 m_partitionIndex;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Duplicate partition-index math across MapPartition and MapPartitioned.

Both classes independently declare GetPartitionIndex(float x) (and, in the .cpp, duplicate the MAP_HALF_SIZE constant and normalization formula). If the two implementations ever diverge, player routing (MapPartitioned) and respawn gating (MapPartition) would silently disagree about which partition owns a given X coordinate, causing missed/duplicated respawns at partition boundaries.

Extract the shared math into one free function or static helper (e.g. in MapPartitioned.h) and have both classes call it.

Also applies to: 71-74

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/server/game/Maps/MapPartitioned.h` around lines 34 - 38, The partition
index calculation is duplicated between MapPartition and MapPartitioned, so
extract the shared X-to-partition math into a single helper (for example, a free
function or static utility near MapPartitioned) and have both GetPartitionIndex
implementations call it. Keep the normalization logic and MAP_HALF_SIZE handling
in one place so IsPositionInPartition and the respawn/player routing code cannot
drift apart.

Comment thread src/server/game/World/WorldConfig.cpp Outdated
SetConfigValue<bool>(CONFIG_SHOW_MUTE_IN_WORLD, "ShowMuteInWorld", false);
SetConfigValue<bool>(CONFIG_SHOW_BAN_IN_WORLD, "ShowBanInWorld", false);
SetConfigValue<uint32>(CONFIG_NUMTHREADS, "MapUpdate.Threads", 1);
SetConfigValue<uint32>(CONFIG_MAP_PARTITION_COUNT, "MapUpdate.PartitionCount", 4);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Missing validation on MapUpdate.PartitionCount; also implicitly truncated to uint8.

Unlike most numeric options in this file (e.g. CONFIG_COMPRESSION, CONFIG_MAX_OVERSPEED_PINGS), this has no validator lambda. A value of 0 set by an operator reaches MapPartitioned's uint8 partitionCount constructor parameter (truncating any value ≥ 256), and MapPartitioned::GetPartitionIndex performs % m_partitionCount0 triggers UB/crash on every position lookup.

🛡️ Proposed fix
-    SetConfigValue<uint32>(CONFIG_MAP_PARTITION_COUNT, "MapUpdate.PartitionCount", 4);
+    SetConfigValue<uint32>(CONFIG_MAP_PARTITION_COUNT, "MapUpdate.PartitionCount", 4, ConfigValueCache::Reloadable::No, [](uint32 const& value) { return value > 0 && value <= 255; }, "> 0 && <= 255");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
SetConfigValue<uint32>(CONFIG_MAP_PARTITION_COUNT, "MapUpdate.PartitionCount", 4);
SetConfigValue<uint32>(CONFIG_MAP_PARTITION_COUNT, "MapUpdate.PartitionCount", 4, ConfigValueCache::Reloadable::No, [](uint32 const& value) { return value > 0 && value <= 255; }, "> 0 && <= 255");
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/server/game/World/WorldConfig.cpp` at line 566, Add validation for
CONFIG_MAP_PARTITION_COUNT in WorldConfig::SetConfigValue so only safe non-zero
values within the uint8 range are accepted. The current MapUpdate.PartitionCount
setting is passed into MapPartitioned’s uint8 partitionCount and can truncate
large values or allow 0, which later breaks MapPartitioned::GetPartitionIndex.
Update the config registration for MapUpdate.PartitionCount to use a validator
lambda like the other numeric options in this file and reject 0 and values above
255.

@Kitzunu

Kitzunu commented Jul 4, 2026

Copy link
Copy Markdown
Member

I would love to see the resource increase running this PR

@blinkysc

blinkysc commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

We need a DJ Khaled label

@hanu-14

hanu-14 commented Jul 6, 2026

Copy link
Copy Markdown
Author

All review feedback has been addressed in the latest code:

  1. ? instanceId=0 - CreatePartition now uses index + 1 for unique non-zero instance IDs
  2. ? Missing #include - Already included
  3. ? Duplicate partition-index math - Extracted into shared GetPartitionIndexForCount() static helper
  4. ? Redundant Map::LoadRespawnTimes() - Removed trailing base-object call (not ticked)
  5. ? Missing config validation - MapUpdate.PartitionCount now has a validator rejecting 0 and >255
  6. ? Eager SetMap in AddPlayerToMap - Removed; now delegates to partition->AddPlayerToMap directly
  7. ? Respawn DB race - Non-owning partitions now early-return instead of deleting respawn times
  8. ? GridObjectLoader bypass - LoadCreatures/LoadGameObjects check ShouldLoadGridObject()
  9. ? MAP_HALFSIZE magic number - Uses the shared constant from GridDefines.h
  10. ? GetPartitionForPosition crash - Added empty m_Partitions guard

All Copilot and CodeRabbit issues resolved. Ready for re-review.

@Kitzunu

Kitzunu commented Jul 14, 2026

Copy link
Copy Markdown
Member

Okay let's be real. This is way too complex of a system to be purely AI built just for a quick bounty cashin.

I will close this, as many other AI generated Map partitioning PRs.

@Kitzunu Kitzunu closed this Jul 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CORE Related to the core file-cpp Used to trigger the matrix build

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature: Map Partitioning Support for AzerothCore

4 participants