Handle SMSG_CHARACTER_LOGIN_FAILED (0x041) for AzerothCore/Playerbot compatibility

Previously this opcode was unrecognised and silently dropped, leaving the
client stuck in ENTERING_WORLD with no feedback when the server rejected a
login (duplicate session, world down, disabled race/class, etc.). Now we
decode the LoginFailureReason byte, reset state to CHAR_LIST_RECEIVED so
the player can retry, and surface a red error message on the character screen
via the new CharLoginFailCallback. Also adds isError colour support to
CharacterScreen::setStatus so failures show in red and successes in green.
This commit is contained in:
Kelsi 2026-02-17 13:59:29 -08:00
parent 36fc1df706
commit 7cf060a9f6
11 changed files with 61 additions and 4 deletions

View file

@ -699,6 +699,10 @@ void GameHandler::handlePacket(network::Packet& packet) {
}
break;
case Opcode::SMSG_CHARACTER_LOGIN_FAILED:
handleCharLoginFailed(packet);
break;
case Opcode::SMSG_LOGIN_VERIFY_WORLD:
if (state == WorldState::ENTERING_WORLD || state == WorldState::IN_WORLD) {
handleLoginVerifyWorld(packet);
@ -1889,6 +1893,32 @@ const Character* GameHandler::getFirstCharacter() const {
void GameHandler::handleCharLoginFailed(network::Packet& packet) {
uint8_t reason = packet.readUInt8();
static const char* reasonNames[] = {
"Login failed", // 0
"World server is down", // 1
"Duplicate character", // 2 (session still active)
"No instance servers", // 3
"Login disabled", // 4
"Character not found", // 5
"Locked for transfer", // 6
"Locked by billing", // 7
"Using remote", // 8
};
const char* msg = (reason < 9) ? reasonNames[reason] : "Unknown reason";
LOG_ERROR("SMSG_CHARACTER_LOGIN_FAILED: reason=", (int)reason, " (", msg, ")");
// Allow the player to re-select a character
setState(WorldState::CHAR_LIST_RECEIVED);
if (charLoginFailCallback_) {
charLoginFailCallback_(msg);
}
}
void GameHandler::selectCharacter(uint64_t characterGuid) {
if (state != WorldState::CHAR_LIST_RECEIVED) {
LOG_WARNING("Cannot select character in state: ", (int)state);