15 KiB
Realm List Protocol Guide
Overview
The realm list protocol allows the client to retrieve the list of available game servers (realms) from the authentication server after successful authentication. This is the second step in the connection flow, following authentication.
Connection Flow
1. Connect to auth server (port 3724)
2. Authenticate (LOGON_CHALLENGE + LOGON_PROOF)
3. Request realm list (REALM_LIST)
4. Select realm
5. Connect to world server (realm's address)
Implementation
Data Structures
Realm
The Realm struct contains all information about a game server:
struct Realm {
uint8_t icon; // Realm icon type
uint8_t lock; // Lock status
uint8_t flags; // Realm flags (bit 0x04 = has version info)
std::string name; // Realm name (e.g., "My Private Server")
std::string address; // Server address (e.g., "localhost:8085")
float population; // Population level (0.0 to 2.0+)
uint8_t characters; // Number of characters player has on this realm
uint8_t timezone; // Timezone ID
uint8_t id; // Realm ID
// Version info (conditional - only if flags & 0x04)
uint8_t majorVersion; // Major version (e.g., 3)
uint8_t minorVersion; // Minor version (e.g., 3)
uint8_t patchVersion; // Patch version (e.g., 5)
uint16_t build; // Build number (e.g., 12340 for 3.3.5a)
bool hasVersionInfo() const { return (flags & 0x04) != 0; }
};
RealmListResponse
Container for the list of realms:
struct RealmListResponse {
std::vector<Realm> realms; // All available realms
};
API Usage
Basic Usage
#include "auth/auth_handler.hpp"
using namespace wowee::auth;
// Create auth handler
AuthHandler auth;
// Connect to auth server
if (!auth.connect("logon.myserver.com", 3724)) {
std::cerr << "Failed to connect" << std::endl;
return;
}
// Set up callbacks
auth.setOnSuccess([&auth](const std::vector<uint8_t>& sessionKey) {
std::cout << "Authentication successful!" << std::endl;
std::cout << "Session key size: " << sessionKey.size() << " bytes" << std::endl;
// Request realm list after successful authentication
auth.requestRealmList();
});
auth.setOnRealmList([](const std::vector<Realm>& realms) {
std::cout << "Received " << realms.size() << " realms:" << std::endl;
for (const auto& realm : realms) {
std::cout << " - " << realm.name << " (" << realm.address << ")" << std::endl;
std::cout << " Population: " << realm.population << std::endl;
std::cout << " Characters: " << (int)realm.characters << std::endl;
}
});
auth.setOnFailure([](const std::string& reason) {
std::cerr << "Authentication failed: " << reason << std::endl;
});
// Start authentication
auth.authenticate("username", "password");
// Main loop
while (auth.getState() != AuthState::REALM_LIST_RECEIVED &&
auth.getState() != AuthState::FAILED) {
auth.update(0.016f); // ~60 FPS
std::this_thread::sleep_for(std::chrono::milliseconds(16));
}
// Access realm list
const auto& realms = auth.getRealms();
if (!realms.empty()) {
std::cout << "First realm: " << realms[0].name << std::endl;
}
Complete Example with Realm Selection
#include "auth/auth_handler.hpp"
#include <iostream>
#include <thread>
#include <chrono>
int main() {
using namespace wowee::auth;
AuthHandler auth;
// Connect
std::cout << "Connecting to authentication server..." << std::endl;
if (!auth.connect("localhost", 3724)) {
std::cerr << "Connection failed" << std::endl;
return 1;
}
// Set up success callback to request realms
auth.setOnSuccess([&auth](const std::vector<uint8_t>& sessionKey) {
std::cout << "\n========================================" << std::endl;
std::cout << " AUTHENTICATION SUCCESSFUL!" << std::endl;
std::cout << "========================================" << std::endl;
std::cout << "Session key: " << sessionKey.size() << " bytes" << std::endl;
// Automatically request realm list
std::cout << "\nRequesting realm list..." << std::endl;
auth.requestRealmList();
});
// Set up realm list callback
bool gotRealms = false;
auth.setOnRealmList([&gotRealms](const std::vector<Realm>& realms) {
std::cout << "\n========================================" << std::endl;
std::cout << " AVAILABLE REALMS" << std::endl;
std::cout << "========================================" << std::endl;
for (size_t i = 0; i < realms.size(); ++i) {
const auto& realm = realms[i];
std::cout << "\n[" << (i + 1) << "] " << realm.name << std::endl;
std::cout << " Address: " << realm.address << std::endl;
std::cout << " Population: ";
// Interpret population level
if (realm.population < 0.5f) {
std::cout << "Low (Green)";
} else if (realm.population < 1.0f) {
std::cout << "Medium (Yellow)";
} else if (realm.population < 2.0f) {
std::cout << "High (Red)";
} else {
std::cout << "Full (Red)";
}
std::cout << " (" << realm.population << ")" << std::endl;
std::cout << " Your characters: " << (int)realm.characters << std::endl;
std::cout << " Icon: " << (int)realm.icon << std::endl;
std::cout << " Lock: " << (realm.lock ? "Locked" : "Unlocked") << std::endl;
if (realm.hasVersionInfo()) {
std::cout << " Version: " << (int)realm.majorVersion << "."
<< (int)realm.minorVersion << "."
<< (int)realm.patchVersion << " (build "
<< realm.build << ")" << std::endl;
}
}
gotRealms = true;
});
// Set up failure callback
auth.setOnFailure([](const std::string& reason) {
std::cerr << "\n========================================" << std::endl;
std::cerr << " AUTHENTICATION FAILED" << std::endl;
std::cerr << "========================================" << std::endl;
std::cerr << "Reason: " << reason << std::endl;
});
// Authenticate
std::cout << "Authenticating..." << std::endl;
auth.authenticate("myuser", "mypass");
// Main loop - wait for realm list or failure
while (!gotRealms && auth.getState() != AuthState::FAILED) {
auth.update(0.016f);
std::this_thread::sleep_for(std::chrono::milliseconds(16));
}
// Check result
if (gotRealms) {
const auto& realms = auth.getRealms();
// Example: Select first realm
if (!realms.empty()) {
const auto& selectedRealm = realms[0];
std::cout << "\n========================================" << std::endl;
std::cout << " REALM SELECTED" << std::endl;
std::cout << "========================================" << std::endl;
std::cout << "Realm: " << selectedRealm.name << std::endl;
std::cout << "Address: " << selectedRealm.address << std::endl;
// TODO: Parse address and connect to world server
// Example: "localhost:8085" -> host="localhost", port=8085
}
}
// Cleanup
auth.disconnect();
return gotRealms ? 0 : 1;
}
Protocol Details
REALM_LIST Request
Packet Structure:
Opcode: 0x10 (REALM_LIST)
Size: 5 bytes total
Bytes:
0: Opcode (0x10)
1-4: Unknown uint32 (always 0x00000000)
Building the Packet:
network::Packet RealmListPacket::build() {
network::Packet packet(static_cast<uint16_t>(AuthOpcode::REALM_LIST));
packet.writeUInt32(0x00); // Unknown field
return packet;
}
REALM_LIST Response
Packet Structure:
Opcode: 0x10 (REALM_LIST)
Variable length
Header:
Byte 0: Opcode (0x10)
Bytes 1-2: Packet size (uint16, little-endian)
Bytes 3-6: Unknown (uint32)
Bytes 7-8: Realm count (uint16, little-endian)
For each realm:
1 byte: Icon
1 byte: Lock
1 byte: Flags
C-string: Name (null-terminated)
C-string: Address (null-terminated, format: "host:port")
4 bytes: Population (float, little-endian)
1 byte: Characters (character count on this realm)
1 byte: Timezone
1 byte: ID
[Conditional - only if flags & 0x04:]
1 byte: Major version
1 byte: Minor version
1 byte: Patch version
2 bytes: Build (uint16, little-endian)
Packet Framing:
The TCPSocket automatically handles variable-length REALM_LIST packets:
// In TCPSocket::getExpectedPacketSize()
case 0x10: // REALM_LIST response
if (receiveBuffer.size() >= 3) {
uint16_t size = receiveBuffer[1] | (receiveBuffer[2] << 8);
return 1 + 2 + size; // opcode + size field + payload
}
return 0; // Need more data
Realm Flags
The flags field contains bitwise flags:
- Bit 0x04: Realm has version info (major, minor, patch, build)
- Other bits are for realm type and status (see TrinityCore documentation)
Example:
if (realm.flags & 0x04) {
// Realm includes version information
std::cout << "Version: " << (int)realm.majorVersion << "."
<< (int)realm.minorVersion << "."
<< (int)realm.patchVersion << std::endl;
}
Population Levels
The population field is a float representing server load:
- 0.0 - 0.5: Low (Green) - Server is not crowded
- 0.5 - 1.0: Medium (Yellow) - Moderate population
- 1.0 - 2.0: High (Red) - Server is crowded
- 2.0+: Full (Red) - Server is at capacity
Parsing Address
Realm addresses are in the format "host:port". Example parsing:
std::string parseHost(const std::string& address) {
size_t colonPos = address.find(':');
if (colonPos != std::string::npos) {
return address.substr(0, colonPos);
}
return address;
}
uint16_t parsePort(const std::string& address) {
size_t colonPos = address.find(':');
if (colonPos != std::string::npos) {
std::string portStr = address.substr(colonPos + 1);
return static_cast<uint16_t>(std::stoi(portStr));
}
return 8085; // Default world server port
}
// Usage
const auto& realm = realms[0];
std::string host = parseHost(realm.address);
uint16_t port = parsePort(realm.address);
std::cout << "Connecting to " << host << ":" << port << std::endl;
Authentication States
The AuthState enum now includes realm list states:
enum class AuthState {
DISCONNECTED, // Not connected
CONNECTED, // Connected, ready for auth
CHALLENGE_SENT, // LOGON_CHALLENGE sent
CHALLENGE_RECEIVED, // LOGON_CHALLENGE response received
PROOF_SENT, // LOGON_PROOF sent
AUTHENTICATED, // Authentication successful, can request realms
REALM_LIST_REQUESTED, // REALM_LIST request sent
REALM_LIST_RECEIVED, // REALM_LIST response received
FAILED // Authentication or connection failed
};
State Transitions:
DISCONNECTED
↓ connect()
CONNECTED
↓ authenticate()
CHALLENGE_SENT
↓ (server response)
CHALLENGE_RECEIVED
↓ (automatic)
PROOF_SENT
↓ (server response)
AUTHENTICATED
↓ requestRealmList()
REALM_LIST_REQUESTED
↓ (server response)
REALM_LIST_RECEIVED
Testing
With Live Server
// Test against a WoW 3.3.5a private server
auth.connect("logon.my-wotlk-server.com", 3724);
auth.authenticate("testuser", "testpass");
// Wait for realm list
while (auth.getState() != AuthState::REALM_LIST_RECEIVED) {
auth.update(0.016f);
std::this_thread::sleep_for(std::chrono::milliseconds(16));
}
// Verify realms received
const auto& realms = auth.getRealms();
assert(!realms.empty());
assert(!realms[0].name.empty());
assert(!realms[0].address.empty());
With Mock Data
For testing without a live server, you can create mock realms:
Realm mockRealm;
mockRealm.id = 1;
mockRealm.name = "Test Realm";
mockRealm.address = "localhost:8085";
mockRealm.icon = 1;
mockRealm.lock = 0;
mockRealm.flags = 0x04; // Has version info
mockRealm.population = 1.5f; // High population
mockRealm.characters = 3; // 3 characters
mockRealm.timezone = 1;
mockRealm.majorVersion = 3;
mockRealm.minorVersion = 3;
mockRealm.patchVersion = 5;
mockRealm.build = 12340;
// Test parsing address
std::string host = parseHost(mockRealm.address);
assert(host == "localhost");
uint16_t port = parsePort(mockRealm.address);
assert(port == 8085);
Common Issues
1. "Cannot request realm list: not authenticated"
Cause: Tried to request realm list before authentication completed.
Solution: Only call requestRealmList() after authentication succeeds (in onSuccess callback or when state is AUTHENTICATED).
// WRONG
auth.authenticate("user", "pass");
auth.requestRealmList(); // Too soon!
// CORRECT
auth.setOnSuccess([&auth](const std::vector<uint8_t>& sessionKey) {
auth.requestRealmList(); // Call here
});
auth.authenticate("user", "pass");
2. Empty Realm List
Cause: Server has no realms configured.
Solution: Check server configuration. A typical WoW server should have at least one realm in its realmlist table.
3. "Unknown opcode or indeterminate size"
Cause: Server sent unexpected packet or packet framing failed.
Solution: Enable debug logging to see raw packet data:
Logger::getInstance().setLogLevel(LogLevel::DEBUG);
Next Steps
After receiving the realm list:
- Display realms to user - Show realm name, population, character count
- Let user select realm - Prompt for realm selection
- Parse realm address - Extract host and port from
addressfield - Connect to world server - Use the parsed host:port to connect
- Send CMSG_AUTH_SESSION - Authenticate with world server using session key
Example next step:
auth.setOnRealmList([&auth](const std::vector<Realm>& realms) {
if (realms.empty()) {
std::cerr << "No realms available" << std::endl;
return;
}
// Select first realm
const auto& realm = realms[0];
// Parse address
size_t colonPos = realm.address.find(':');
std::string host = realm.address.substr(0, colonPos);
uint16_t port = std::stoi(realm.address.substr(colonPos + 1));
// TODO: Connect to world server
std::cout << "Next: Connect to " << host << ":" << port << std::endl;
std::cout << "Send CMSG_AUTH_SESSION with session key" << std::endl;
// Get session key for world server authentication
const auto& sessionKey = auth.getSessionKey();
std::cout << "Session key: " << sessionKey.size() << " bytes" << std::endl;
});
Summary
The realm list protocol:
- ✅ Automatically handles variable-length packets
- ✅ Parses all realm information including version info
- ✅ Provides easy-to-use callback interface
- ✅ Includes comprehensive logging
- ✅ Ready for live server testing
Status: ✅ Complete and production-ready
Next Protocol: World server connection (CMSG_AUTH_SESSION)