3 Commits

Author SHA1 Message Date
Hokken
ce1adebc78
fix(Core): scope AddPlayerBot loading count to master account (#2307)
## Problem

`AddPlayerBot()` falsely rejects player bot additions with *"You have
added too many bots (more than 40)"* even when the player has zero
personal bots.

This happens because the `MaxAddedBots` check at `PlayerbotMgr.cpp:124`
adds `botLoading.size()` to the player's personal bot count:

```cpp
uint32 count = mgr->GetPlayerbotsCount() + botLoading.size();
```

`botLoading` is a `static std::unordered_set<ObjectGuid>` on
`PlayerbotHolder` — shared by both `PlayerbotMgr` (per-player) and
`RandomPlayerbotMgr` (singleton). When `RandomPlayerbotMgr` loads random
bots at startup (up to 60 per interval via `RandomBotsPerInterval`),
their GUIDs go into the same global set. During the startup loading
window, `botLoading.size()` can easily reach 100–300, far exceeding the
default `MaxAddedBots = 40` limit.

The result: any player who logs in during the random bot loading window
and tries `.playerbot add <name>` gets blocked, even though the limit is
intended to be per-player.

### How to reproduce

1. Set `AiPlayerbot.RandomBotAutologin = 1` (default) with 500 random
bots
2. Start the server
3. Log in immediately while random bots are still loading
4. Run `.playerbot add <character_name>` for an offline character on
your account
5. Get *"You have added too many bots (more than 40)"* despite having 0
personal bots
6. Wait 1–2 minutes for random bot loading to finish, try again — works

### Root cause

- `PlayerbotHolder::botLoading` is declared `static` at
`PlayerbotMgr.h:60`, so both `PlayerbotMgr` and `RandomPlayerbotMgr`
share the same set
- `AddPlayerBot()` inserts into `botLoading` at line 147 for ALL callers
— both player-initiated adds (`masterAccountId > 0`) and random bot
spawns (`masterAccountId = 0`)
- The count check at line 124 uses `botLoading.size()` (the entire
global set) instead of filtering to bots being loaded for the requesting
player
- The config comment confirms the intended scope: *"The maximum number
of bots that a player can control simultaneously"*

## Fix

Change `botLoading` from `unordered_set<ObjectGuid>` to
`unordered_map<ObjectGuid, uint32>` where the value is the
`masterAccountId` passed to `AddPlayerBot()`. Random bots are loaded
with `masterAccountId = 0`.

The count check now iterates the map and only counts entries matching
the current player's `masterAccountId`:

```cpp
uint32 loadingForMaster = 0;
for (auto const& [guid, acctId] : botLoading)
{
    if (acctId == masterAccountId)
        ++loadingForMaster;
}
uint32 count = mgr->GetPlayerbotsCount() + loadingForMaster;
```

### Callsite compatibility

All 10 existing `botLoading` callsites were audited:

| Callsite | Operation | Compatible |
|----------|-----------|-----------|
| `PlayerbotMgr.cpp:85` | `find()` by key | Yes |
| `PlayerbotMgr.cpp:153` | `emplace()` (was `insert()`) | Changed |
| `PlayerbotMgr.cpp:174` | `erase()` by key | Yes |
| `PlayerbotMgr.cpp:209` | `erase()` by key | Yes |
| `PlayerbotMgr.cpp:229` | `erase()` by key | Yes |
| `PlayerbotMgr.cpp:1163` | `find()` by key | Yes |
| `RandomPlayerbotMgr.cpp:429` | `empty()` | Yes |

The six unchanged callsites use `find()`, `erase()`, and `empty()` which
operate on keys identically for both `unordered_set` and
`unordered_map`.

## Files changed

| File | Change |
|------|--------|
| `src/Bot/PlayerbotMgr.h` | `botLoading` type:
`unordered_set<ObjectGuid>` → `unordered_map<ObjectGuid, uint32>` |
| `src/Bot/PlayerbotMgr.cpp` | Definition type updated, `insert` →
`emplace` with `masterAccountId`, count check filters by
`masterAccountId` |

## What is NOT changed

- `MaxAddedBots` config key and default value (40) — unchanged
- Random bot loading behavior — unchanged
- The `botLoading.empty()` throttle in `RandomPlayerbotMgr` — unchanged
- In-game group invite flow — unaffected (does not go through
`AddPlayerBot`)
- No new config keys, no schema changes, no API changes

---------

Co-authored-by: Keleborn <22352763+Celandriel@users.noreply.github.com>
Co-authored-by: bash <hermensb@gmail.com>
Co-authored-by: Revision <tkn963@gmail.com>
Co-authored-by: kadeshar <kadeshar@gmail.com>
Co-authored-by: Hokken <Hokken@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 11:26:42 -07:00
bashermens
13fff46fa0
Improper singletons migration to clean Meyer's singletons (cherry-pick) (#2082)
# Pull Request

- Applies the clean and corrected singletons, Meyer pattern. (cherry
picked from @SmashingQuasar )

Testing by just playing the game in various ways. Been tested by myself
@Celandriel and @SmashingQuasar
---

## Complexity & Impact

- Does this change add new decision branches?
    - [x] No
    - [ ] Yes (**explain below**)

- Does this change increase per-bot or per-tick processing?
    - [x] No
    - [ ] Yes (**describe and justify impact**)

- Could this logic scale poorly under load?
    - [x] No
    - [ ] Yes (**explain why**)

---

## Defaults & Configuration

- Does this change modify default bot behavior?
    - [x] No
    - [ ] Yes (**explain why**)

---

## AI Assistance

- Was AI assistance (e.g. ChatGPT or similar tools) used while working
on this change?
    - [x] No
    - [ ] Yes (**explain below**)
---

## Final Checklist

- [x] Stability is not compromised
- [x] Performance impact is understood, tested, and acceptable
- [x] Added logic complexity is justified and explained
- [x] Documentation updated if needed

---

## Notes for Reviewers

Anything that significantly improves realism at the cost of stability or
performance should be carefully discussed
before merging.

---------

Co-authored-by: Nicolas Lebacq <nicolas.cordier@outlook.com>
Co-authored-by: Keleborn <22352763+Celandriel@users.noreply.github.com>
2026-01-30 21:49:37 +01:00
bashermens
41c53365ae
[HOT FIX] MS build issues regarding folder / command lenght usage or rc.exe (#2038) 2026-01-19 22:45:28 +01:00