# Fix: Arena PersonalRating and MMR issue for bot teams
## Problem
Bot arena teams are created with artificial random ratings (1000-2000
range), but when bots join these teams, their personal ratings and
matchmaker ratings (MMR) use default config values instead of being
adjusted to match the team's artificial rating. This causes matchmaking
issues since the system uses personal ratings for queue calculations.
## Root Cause
The issue occurred because `SetRatingForAll()` was called during team
creation but only affected the captain. When additional bots were added
later via `AddMember()`, they received default values from
`CONFIG_ARENA_START_PERSONAL_RATING` and
`CONFIG_ARENA_START_MATCHMAKER_RATING` instead of values appropriate for
the team's artificial rating.
## Solution
After bots are added to arena teams, the fix:
1. Uses `SetRatingForAll()` to align all personal ratings with team
rating
2. Adjusts matchmaker ratings based on team context vs default
configuration
3. Saves changes to both database tables with proper data types
## Impact
- Personal ratings now match team ratings for artificial bot teams
- MMR values are adjusted for artificial bot team ratings instead of
using default config values
- Arena matchmaking functions correctly for bot teams with random
ratings
- Only affects new arena team assignments after deployment
- Existing player teams and normal config behavior are unaffected
## Manual Database Update
For existing installations, the provided SQL script could be used to fix
bot teams created before this patch.
### Update personal rating
```sql
UPDATE arena_team_member atm
JOIN arena_team at ON atm.arenaTeamId = at.arenaTeamId
JOIN characters c ON atm.guid = c.guid
JOIN auth.account a ON c.account = a.id
SET atm.personalRating = at.rating
WHERE a.username LIKE 'rndbot%'
AND atm.personalRating != at.rating;
```
### Update MMR for existing entries
```sql
UPDATE character_arena_stats cas
JOIN characters c ON cas.guid = c.guid
JOIN auth.account a ON c.account = a.id
JOIN arena_team_member atm ON cas.guid = atm.guid
JOIN arena_team at ON atm.arenaTeamId = at.arenaTeamId
SET
cas.matchMakerRating = GREATEST(at.rating, 1500), -- Use team rating or 1500 minimum
cas.maxMMR = GREATEST(cas.maxMMR, cas.matchMakerRating) -- Update maxMMR if needed
WHERE
a.username LIKE '%rndbot%'
AND (
-- Update if MMR doesn't match team context
(at.rating > 1500 AND cas.matchMakerRating < at.rating) OR
(at.rating <= 1500 AND cas.matchMakerRating != 1500) OR
cas.matchMakerRating IS NULL
)
AND (
-- Map arena team type to character_arena_stats slot
(at.type = 2 AND cas.slot = 0) OR -- 2v2 teams use slot 0
(at.type = 3 AND cas.slot = 1) OR -- 3v3 teams use slot 1
(at.type = 5 AND cas.slot = 2) -- 5v5 teams use slot 2
);
```
### Insert missing MMR records for bots without character_arena_stats
entries
```sql
INSERT INTO character_arena_stats (guid, slot, matchMakerRating, maxMMR)
SELECT
atm.guid,
CASE
WHEN at.type = 2 THEN 0 -- 2v2 -> slot 0
WHEN at.type = 3 THEN 1 -- 3v3 -> slot 1
WHEN at.type = 5 THEN 2 -- 5v5 -> slot 2
ELSE 0
END as slot,
GREATEST(at.rating, 1500) as matchMakerRating,
GREATEST(at.rating, 1500) as maxMMR
FROM arena_team_member atm
JOIN arena_team at ON atm.arenaTeamId = at.arenaTeamId
JOIN characters c ON atm.guid = c.guid
JOIN auth.account a ON c.account = a.id
WHERE
a.username LIKE '%rndbot%'
AND NOT EXISTS (
SELECT 1 FROM character_arena_stats cas2
WHERE cas2.guid = atm.guid
AND cas2.slot = CASE
WHEN at.type = 2 THEN 0
WHEN at.type = 3 THEN 1
WHEN at.type = 5 THEN 2
ELSE 0
END
)
AND at.rating > 0;
```
## Related issues
Fixes: #1787
Fixes: #1800
## Verification Queries
### Query 1: Check personal rating alignment
```sql
SELECT
'Personal Rating Check' as check_type,
COUNT(*) as total_bot_members,
SUM(CASE WHEN atm.personalRating = at.rating THEN 1 ELSE 0 END) as correct_ratings,
SUM(CASE WHEN atm.personalRating != at.rating THEN 1 ELSE 0 END) as incorrect_ratings,
ROUND(AVG(at.rating), 2) as avg_team_rating,
ROUND(AVG(atm.personalRating), 2) as avg_personal_rating
FROM arena_team_member atm
JOIN arena_team at ON atm.arenaTeamId = at.arenaTeamId
JOIN characters c ON atm.guid = c.guid
JOIN auth.account a ON c.account = a.id
WHERE
a.username LIKE '%rndbot%';
```
### Query 2: Check MMR alignment
```sql
SELECT
'MMR Alignment Check' as check_type,
COUNT(*) as total_mmr_records,
SUM(CASE
WHEN at.rating > 1500 AND cas.matchMakerRating >= at.rating THEN 1
WHEN at.rating <= 1500 AND cas.matchMakerRating = 1500 THEN 1
ELSE 0
END) as correct_mmr,
SUM(CASE
WHEN at.rating > 1500 AND cas.matchMakerRating < at.rating THEN 1
WHEN at.rating <= 1500 AND cas.matchMakerRating != 1500 THEN 1
ELSE 0
END) as incorrect_mmr,
ROUND(AVG(at.rating), 2) as avg_team_rating,
ROUND(AVG(cas.matchMakerRating), 2) as avg_mmr,
ROUND(AVG(cas.maxMMR), 2) as avg_max_mmr
FROM arena_team_member atm
JOIN arena_team at ON atm.arenaTeamId = at.arenaTeamId
JOIN characters c ON atm.guid = c.guid
JOIN auth.account a ON c.account = a.id
JOIN character_arena_stats cas ON atm.guid = cas.guid
WHERE
a.username LIKE '%rndbot%'
AND (
(at.type = 2 AND cas.slot = 0) OR
(at.type = 3 AND cas.slot = 1) OR
(at.type = 5 AND cas.slot = 2)
);
```
### Query 3: Detailed team-by-team analysis
```sql
SELECT
at.arenaTeamId,
at.name as team_name,
at.type as team_type,
at.rating as team_rating,
COUNT(atm.guid) as member_count,
GROUP_CONCAT(DISTINCT atm.personalRating) as personal_ratings,
GROUP_CONCAT(DISTINCT cas.matchMakerRating) as mmr_values,
CASE
WHEN COUNT(DISTINCT atm.personalRating) = 1 AND MIN(atm.personalRating) = at.rating THEN 'OK'
ELSE 'MISMATCH'
END as personal_rating_status,
CASE
WHEN COUNT(DISTINCT cas.matchMakerRating) = 1 AND (
(at.rating > 1500 AND MIN(cas.matchMakerRating) >= at.rating) OR
(at.rating <= 1500 AND MIN(cas.matchMakerRating) = 1500)
) THEN 'OK'
ELSE 'MISMATCH'
END as mmr_status
FROM arena_team at
JOIN arena_team_member atm ON at.arenaTeamId = atm.arenaTeamId
JOIN characters c ON atm.guid = c.guid
JOIN auth.account a ON c.account = a.id
LEFT JOIN character_arena_stats cas ON atm.guid = cas.guid
AND cas.slot = CASE
WHEN at.type = 2 THEN 0
WHEN at.type = 3 THEN 1
WHEN at.type = 5 THEN 2
ELSE 0
END
WHERE
a.username LIKE '%rndbot%'
GROUP BY at.arenaTeamId, at.name, at.type, at.rating
ORDER BY at.rating DESC;
```
Playerbots Module
mod-playerbots is an AzerothCore module that adds player-like bots to a server. The project is based off IKE3's Playerbots.
Features include:
- The ability to log in alt characters as bots, allowing players to interact with their other characters, form parties, level up, and more
- Random bots that wander through the world, complete quests, and otherwise behave like players, simulating the MMO experience
- Bots capable of running most raids and battlegrounds
- Highly configurable settings to define how bots behave
- Excellent performance, even when running thousands of bots
We also have a Discord server where you can discuss the project, ask questions, and get involved in the community!
Installation
Supported platforms are Ubuntu, Windows, and macOS. Other Linux distributions may work, but may not receive support.
All mod-playerbots installations require a custom branch of AzerothCore: mod-playerbots/azerothcore-wotlk/tree/Playerbot. This branch allows the mod-playerbots module to build and function. Updates from the upstream are implemented regularly to this branch. Instructions for installing this required branch and this module are provided below.
Cloning the Repositories
To install both the required branch of AzerothCore and the mod-playerbots module from source, run the following:
git clone https://github.com/mod-playerbots/azerothcore-wotlk.git --branch=Playerbot
cd azerothcore-wotlk/modules
git clone https://github.com/mod-playerbots/mod-playerbots.git --branch=master
For more information, refer to the AzerothCore Installation Guide and Installing a Module pages.
Docker Installation
Docker installations are considered experimental (unofficial with limited support), and previous Docker experience is recommended. To install mod-playerbots on Docker, first clone the required branch of AzerothCore and this module:
git clone https://github.com/mod-playerbots/azerothcore-wotlk.git --branch=Playerbot
cd azerothcore-wotlk/modules
git clone https://github.com/mod-playerbots/mod-playerbots.git --branch=master
Afterwards, create a docker-compose.override.yml file in the azerothcore-wotlk directory. This override file allows for mounting the modules directory to the ac-worldserver service which is required for it to run. Put the following inside and save:
services:
ac-worldserver:
volumes:
- ./modules:/azerothcore/modules:ro
Additionally, this override file can be used to set custom configuration settings for ac-worldserver and any modules you install as environment variables:
services:
ac-worldserver:
environment:
AC_RATE_XP_KILL: "1"
AC_AI_PLAYERBOT_RANDOM_BOT_AUTOLOGIN: "1"
volumes:
- ./modules:/azerothcore/modules:ro
For example, to double the experience gain rate per kill, take the setting Rate.XP.Kill = 1 from woldserver.conf, convert it to an environment variable, and change it to the desired setting in the override file to get AC_RATE_XP_KILL: "2". If you wanted to disable random bots from logging in automatically, take the AiPlayerbot.RandomBotAutologin = 1 setting from playerbots.conf and do the same to get AC_AI_PLAYERBOT_RANDOM_BOT_AUTOLOGIN: "0". For more information on how to configure Azerothcore, Playerbots, and other module settings as environment variables in Docker Compose, see the "Configuring AzerothCore in Containers" section in the Install With Docker guide.
Before building, consider setting the database password. One way to do this is to create a .env file in the root azerothcore-wotlk directory using the template. This file also allows you to set the user and group Docker uses for the services in case you run into any permissions issues, which are the most common cause for Docker installation problems.
Use docker compose up -d --build to build and run the server. For more information, including how to create an account and taking backups, refer to the Install With Docker page.
Documentation
The Playerbots Wiki contains an extensive overview of AddOns, commands, raids with programmed bot strategies, and recommended performance configurations. Please note that documentation may be incomplete or out-of-date in some sections, and contributions are welcome.
Bots are controlled via chat commands. For larger bot groups, this can be cumbersome. Because of this, community members have developed client AddOns to allow controlling bots through the in-game UI. We recommend you check out their projects listed in the AddOns and Submodules page.
Contributing
This project is still under development. We encourage anyone to make contributions, anything from pull requests to reporting issues. If you encounter any errors or experience crashes, we encourage you report them as GitHub issues. Your valuable feedback will help us improve this project collaboratively.
If you make coding contributions, mod-playerbots complies with the C++ Code Standards established by AzerothCore. Each Pull Request must include all test scenarios the author performed, along with their results, to demonstrate that the changes were properly verified.
We recommend joining the Discord server to make your contributions to the project easier, as a lot of active support is carried out through this server.
Please click on the "⭐" button to stay up to date and help us gain more visibility on GitHub!
Acknowledgements
mod-playerbots is based on ZhengPeiRu21/mod-playerbots and celguar/mangosbot-bots. We extend our gratitude to @ZhengPeiRu21 and @celguar for their continued efforts in maintaining the module.
Also, a thank you to the many contributors who've helped build this project: