817 lines
21 KiB
Markdown
817 lines
21 KiB
Markdown
# EverQuest II Login Server - Complete Technical Documentation
|
|
|
|
## Table of Contents
|
|
1. [Overview](#overview)
|
|
2. [Architecture](#architecture)
|
|
3. [Network Protocol Stack](#network-protocol-stack)
|
|
4. [Encryption and Security](#encryption-and-security)
|
|
5. [Packet Structure](#packet-structure)
|
|
6. [Complete Login Flow](#complete-login-flow)
|
|
7. [Character Management](#character-management)
|
|
8. [World Server Communication](#world-server-communication)
|
|
9. [Error Handling](#error-handling)
|
|
10. [Database Operations](#database-operations)
|
|
|
|
## Overview
|
|
|
|
The EverQuest II Login Server is responsible for:
|
|
- Client authentication and authorization
|
|
- World server discovery and status management
|
|
- Character listing and management
|
|
- Secure handoff to world servers
|
|
- Account management and creation
|
|
|
|
### Key Components
|
|
- **NetConnection**: Main network manager
|
|
- **Client**: Individual client connection handler
|
|
- **ClientList**: Thread-safe client container
|
|
- **LWorld**: World server representation
|
|
- **LWorldList**: World server manager
|
|
- **LoginDatabase**: Database interface
|
|
- **EQStream**: Network stream handler with encryption
|
|
|
|
## Architecture
|
|
|
|
```
|
|
┌─────────────┐ UDP ┌──────────────┐
|
|
│ EQ2 Client ├─────────────────►│ Login Server │
|
|
└─────────────┘ └──────┬───────┘
|
|
│ TCP
|
|
▼
|
|
┌──────────────────┐
|
|
│ World Servers │
|
|
└──────────────────┘
|
|
```
|
|
|
|
### Port Configuration
|
|
- **Default Login Port**: 5999 (UDP for game clients)
|
|
- **Web API Port**: Configurable (TCP/HTTPS)
|
|
- **World Server Communication**: TCP (internal)
|
|
- **World Server Game Port**: Dynamic per world (UDP for game clients)
|
|
|
|
## Network Protocol Stack
|
|
|
|
### Layer Structure
|
|
```
|
|
Application Layer: [Game Packets]
|
|
Presentation Layer: [PacketStruct Serialization]
|
|
Session Layer: [EQStream with RC4/CRC]
|
|
Transport Layer: [UDP Datagram Protocol]
|
|
Network Layer: [IP Protocol]
|
|
```
|
|
|
|
**Note**: EQStream implements its own reliability layer on top of UDP, providing:
|
|
- Sequence numbers for ordering
|
|
- Acknowledgments for reliability
|
|
- Retransmission for lost packets
|
|
- This gives the benefits of TCP while maintaining lower latency
|
|
|
|
### EQStream Protocol
|
|
|
|
The EQStream protocol provides:
|
|
1. **Sequencing**: Packet ordering and acknowledgment
|
|
2. **Fragmentation**: Large packet splitting
|
|
3. **Compression**: zlib compression for efficiency
|
|
4. **Encryption**: RC4 stream cipher
|
|
5. **Integrity**: CRC32 checksums
|
|
|
|
#### Packet Types
|
|
```cpp
|
|
enum EQStreamOp {
|
|
OP_SessionRequest = 0x0001, // Initial connection
|
|
OP_SessionResponse = 0x0002, // Server accepts connection
|
|
OP_Combined = 0x0003, // Multiple packets in one
|
|
OP_SessionDisconnect = 0x0005, // Connection termination
|
|
OP_KeepAlive = 0x0006, // Connection heartbeat
|
|
OP_SessionStatRequest = 0x0007, // Statistics request
|
|
OP_SessionStatResponse = 0x0008,// Statistics response
|
|
OP_Packet = 0x0009, // Application data
|
|
OP_Fragment = 0x000d, // Fragmented packet piece
|
|
OP_Ack = 0x0015, // Acknowledgment
|
|
OP_AckFuture = 0x0016, // Future acknowledgment
|
|
OP_AckPast = 0x0017 // Past acknowledgment
|
|
};
|
|
```
|
|
|
|
## Encryption and Security
|
|
|
|
### RC4 Encryption
|
|
|
|
The login server uses RC4 stream cipher for packet encryption:
|
|
|
|
1. **Key Exchange**:
|
|
```cpp
|
|
// Initial session setup
|
|
struct SessionRequest {
|
|
uint32_t unknown; // Protocol version
|
|
uint32_t session_id; // Client session ID
|
|
uint32_t max_length; // Max packet size
|
|
};
|
|
|
|
struct SessionResponse {
|
|
uint32_t session_id; // Server session ID
|
|
uint32_t key; // RC4 key seed
|
|
uint8_t crc_length; // CRC bytes (2)
|
|
uint8_t compression; // Compression flag
|
|
uint32_t unknown; // Reserved
|
|
uint32_t max_length; // Max packet size
|
|
};
|
|
```
|
|
|
|
2. **RC4 Key Generation**:
|
|
```cpp
|
|
// Key is derived from session response
|
|
void GenerateRC4Key(uint32_t key_seed) {
|
|
// Initialize RC4 state with key_seed
|
|
RC4_KEY encrypt_key, decrypt_key;
|
|
unsigned char key_bytes[4];
|
|
memcpy(key_bytes, &key_seed, 4);
|
|
|
|
// Setup encryption/decryption keys
|
|
RC4_set_key(&encrypt_key, 4, key_bytes);
|
|
RC4_set_key(&decrypt_key, 4, key_bytes);
|
|
}
|
|
```
|
|
|
|
3. **Packet Encryption**:
|
|
```cpp
|
|
void EncryptPacket(uint8_t* data, size_t length) {
|
|
// Skip protocol header (2 bytes)
|
|
RC4(&encrypt_key, length - 2, data + 2, data + 2);
|
|
}
|
|
```
|
|
|
|
### CRC32 Integrity Check
|
|
|
|
Every packet includes a CRC32 checksum:
|
|
|
|
```cpp
|
|
struct EQProtocolPacket {
|
|
uint16_t opcode; // Operation code
|
|
uint8_t data[]; // Packet data
|
|
uint32_t crc32; // CRC32 of opcode + data
|
|
};
|
|
|
|
uint32_t CalculateCRC(const uint8_t* data, size_t length) {
|
|
// Standard CRC32 calculation
|
|
return crc32(0, data, length);
|
|
}
|
|
```
|
|
|
|
### CRC Verification Process:
|
|
1. Extract CRC from packet end
|
|
2. Calculate CRC of packet data
|
|
3. Compare calculated vs received CRC
|
|
4. Reject packet if mismatch
|
|
|
|
## Packet Structure
|
|
|
|
### Base Packet Header
|
|
```cpp
|
|
struct EQ2Packet {
|
|
uint16_t opcode; // Operation identifier
|
|
uint16_t sequence; // Packet sequence number
|
|
uint32_t size; // Payload size
|
|
uint8_t compressed; // Compression flag
|
|
uint8_t data[]; // Actual payload
|
|
};
|
|
```
|
|
|
|
### Application Packet Structure
|
|
```cpp
|
|
struct EQApplicationPacket {
|
|
uint16_t opcode; // Game operation code
|
|
uint32_t version; // Client version
|
|
uint8_t data[]; // Serialized data
|
|
};
|
|
```
|
|
|
|
## Complete Login Flow
|
|
|
|
### Phase 1: Connection Establishment
|
|
|
|
1. **Client Initiates UDP Communication**:
|
|
```
|
|
Client → Server: UDP Datagram with OP_SessionRequest
|
|
(No TCP handshake - connectionless protocol)
|
|
```
|
|
|
|
2. **EQStream Session**:
|
|
```
|
|
Client → Server: OP_SessionRequest
|
|
{
|
|
protocol_version: 0x0003
|
|
session_id: random()
|
|
max_packet_size: 512
|
|
}
|
|
|
|
Server → Client: OP_SessionResponse
|
|
{
|
|
session_id: server_session
|
|
rc4_key: generated_key
|
|
crc_length: 2
|
|
compression: true
|
|
max_packet_size: 512
|
|
}
|
|
```
|
|
|
|
3. **Encryption Initialization**:
|
|
- Both sides initialize RC4 with the shared key
|
|
- All subsequent packets are encrypted
|
|
|
|
### Phase 2: Authentication
|
|
|
|
1. **Login Request**:
|
|
```cpp
|
|
Client → Server: OP_LoginRequestMsg
|
|
{
|
|
version: client_version // e.g., 1208
|
|
username: "account_name" // EQ2_16BitString
|
|
password: "hashed_password" // EQ2_16BitString
|
|
unknown3: cl_eqversion // From eq2_defaults.ini
|
|
}
|
|
```
|
|
|
|
2. **Version Validation**:
|
|
```cpp
|
|
// Server checks version compatibility
|
|
if (!EQOpcodeManager.count(GetOpcodeVersion(version))) {
|
|
// Send incompatible version error
|
|
SendLoginDeniedBadVersion();
|
|
return;
|
|
}
|
|
```
|
|
|
|
3. **Account Verification**:
|
|
```cpp
|
|
// Database lookup
|
|
LoginAccount* acct = database.LoadAccount(username, password);
|
|
|
|
// Check for duplicate login
|
|
Client* existing = client_list.FindByLSID(acct->getLoginAccountID());
|
|
if (existing) {
|
|
existing->getConnection()->SendDisconnect();
|
|
}
|
|
|
|
// Update account info
|
|
database.UpdateAccountIPAddress(acct->id, client_ip);
|
|
database.UpdateAccountClientDataVersion(acct->id, version);
|
|
```
|
|
|
|
4. **Login Response**:
|
|
```cpp
|
|
Server → Client: OP_LoginReplyMsg
|
|
{
|
|
account_id: account_id
|
|
login_response: 0 // 0 = success
|
|
sub_level: 0xFFFFFFFF // Subscription level
|
|
race_flag: 0x1FFFFF // Available races
|
|
class_flag: 0x7FFFFFE // Available classes
|
|
expansion_flag: 0x7CFF // Enabled expansions
|
|
cities_flag: 0xFF // Starting cities
|
|
enabled_races: 0xFFFF // Race availability
|
|
}
|
|
```
|
|
|
|
### Phase 3: World List Request
|
|
|
|
1. **Client Requests World List**:
|
|
```cpp
|
|
Client → Server: OP_AllWSDescRequestMsg
|
|
{} // Empty request
|
|
```
|
|
|
|
2. **Server Builds World List**:
|
|
```cpp
|
|
Server → Client: OP_AllWSDescReplyMsg
|
|
{
|
|
num_worlds: count
|
|
worlds[]: {
|
|
world_id: id
|
|
world_name: "ServerName"
|
|
world_status: 1 // 0=down, 1=up, 2=locked
|
|
num_players: current
|
|
max_players: maximum
|
|
language: "en"
|
|
recommended: false
|
|
}
|
|
}
|
|
```
|
|
|
|
3. **World Status Updates**:
|
|
- Server sends periodic updates (every 10 seconds)
|
|
- Updates include player counts and status changes
|
|
|
|
### Phase 4: Character List
|
|
|
|
1. **Character Loading**:
|
|
```cpp
|
|
// Server loads characters from database
|
|
database.LoadCharacters(GetLoginAccount(), GetVersion());
|
|
```
|
|
|
|
2. **Character List Response**:
|
|
```cpp
|
|
Server → Client: LS_CharSelectList
|
|
{
|
|
account_id: account_id
|
|
num_characters: count
|
|
characters[]: {
|
|
char_id: database_id
|
|
server_id: world_id
|
|
name: "CharacterName"
|
|
race: race_id
|
|
class: class_id
|
|
level: current_level
|
|
zone: "ZoneName"
|
|
gender: 0/1
|
|
deity: deity_id
|
|
body_size: size
|
|
body_age: age
|
|
soga_race_type: soga_id
|
|
x: position_x
|
|
y: position_y
|
|
z: position_z
|
|
}
|
|
}
|
|
```
|
|
|
|
### Phase 5: Character Creation
|
|
|
|
1. **Creation Request**:
|
|
```cpp
|
|
Client → Server: OP_CreateCharacterRequestMsg
|
|
{
|
|
server_id: target_world
|
|
name: "NewCharacter"
|
|
race: selected_race
|
|
class: selected_class
|
|
gender: 0/1
|
|
deity: selected_deity
|
|
body_size: size_value
|
|
body_age: age_value
|
|
// Appearance data...
|
|
}
|
|
```
|
|
|
|
2. **Forward to World Server**:
|
|
```cpp
|
|
Login → World: ServerOP_CharacterCreate
|
|
{
|
|
version: client_version
|
|
account_id: account_id
|
|
[original_request_data]
|
|
}
|
|
```
|
|
|
|
3. **World Server Validation**:
|
|
- Check name availability
|
|
- Validate race/class combination
|
|
- Check character limits
|
|
|
|
4. **Creation Response**:
|
|
```cpp
|
|
World → Login: ServerOP_CharacterCreateResponse
|
|
{
|
|
success: true/false
|
|
char_id: new_character_id // If successful
|
|
reason: error_code // If failed
|
|
}
|
|
|
|
Login → Client: OP_CreateCharacterReplyMsg
|
|
{
|
|
response: CREATESUCCESS_REPLY or error_code
|
|
name: "NewCharacter"
|
|
account_id: account_id
|
|
}
|
|
```
|
|
|
|
### Phase 6: World Entry
|
|
|
|
1. **Play Request**:
|
|
```cpp
|
|
Client → Server: OP_PlayCharacterRequestMsg
|
|
{
|
|
char_id: selected_character
|
|
server_id: world_server // Version > 283
|
|
name: "CharacterName" // Version <= 283
|
|
}
|
|
```
|
|
|
|
2. **World Server Handoff**:
|
|
```cpp
|
|
Login → World: ServerOP_UsertoWorldReq
|
|
{
|
|
char_id: character_id
|
|
lsaccountid: account_id
|
|
worldid: server_id
|
|
ip_address: client_ip
|
|
}
|
|
```
|
|
|
|
3. **World Server Response**:
|
|
```cpp
|
|
World → Login: ServerOP_UsertoWorldResp
|
|
{
|
|
worldid: server_id
|
|
response: 1 // 1 = success
|
|
ip_address: world_ip
|
|
port: world_port
|
|
access_key: session_key // For authentication
|
|
}
|
|
```
|
|
|
|
4. **Client Redirect**:
|
|
```cpp
|
|
Login → Client: OP_PlayCharacterReplyMsg
|
|
{
|
|
response: 1 // Success
|
|
server: "world.ip.address"
|
|
port: world_port
|
|
account_id: account_id
|
|
access_code: session_key
|
|
}
|
|
```
|
|
|
|
5. **Client Connects to World**:
|
|
- Client disconnects from login server
|
|
- Connects to world server using provided IP:port
|
|
- Authenticates using access_key
|
|
|
|
## Character Management
|
|
|
|
### Character Deletion
|
|
|
|
1. **Delete Request**:
|
|
```cpp
|
|
Client → Server: OP_DeleteCharacterRequestMsg
|
|
{
|
|
char_id: character_id
|
|
server_id: world_id
|
|
name: "CharacterName"
|
|
}
|
|
```
|
|
|
|
2. **Verification**:
|
|
```cpp
|
|
// Verify ownership
|
|
bool valid = database.VerifyDelete(account_id, char_id, name);
|
|
```
|
|
|
|
3. **World Server Notification**:
|
|
```cpp
|
|
Login → World: ServerOP_DeleteCharacter
|
|
{
|
|
char_id: character_id
|
|
account_id: account_id
|
|
}
|
|
```
|
|
|
|
4. **Delete Response**:
|
|
```cpp
|
|
Server → Client: OP_DeleteCharacterReplyMsg
|
|
{
|
|
response: 1 // 1 = success
|
|
char_id: deleted_id
|
|
server_id: world_id
|
|
name: "CharacterName"
|
|
}
|
|
```
|
|
|
|
## World Server Communication
|
|
|
|
### Inter-Server Protocol
|
|
|
|
1. **World Server Registration**:
|
|
```cpp
|
|
World → Login: ServerOP_LSInfo
|
|
{
|
|
name: "WorldName"
|
|
address: "public_ip"
|
|
port: game_port
|
|
admin_port: admin_port
|
|
status: 1 // 0=down, 1=up, 2=locked
|
|
players: current_count
|
|
max_players: maximum
|
|
}
|
|
```
|
|
|
|
2. **Heartbeat**:
|
|
```cpp
|
|
World → Login: ServerOP_LSStatus
|
|
{
|
|
status: current_status
|
|
players: current_count
|
|
zones: active_zones
|
|
}
|
|
// Sent every 30 seconds
|
|
```
|
|
|
|
3. **Player Updates**:
|
|
```cpp
|
|
World → Login: ServerOP_UsertoWorldResp
|
|
{
|
|
lsaccountid: account_id
|
|
worldid: server_id
|
|
response: status
|
|
// 0 = rejected
|
|
// 1 = accepted
|
|
// -1 = world full
|
|
// -2 = character not found
|
|
// -3 = world locked
|
|
}
|
|
```
|
|
|
|
## Error Handling
|
|
|
|
### Login Error Codes
|
|
```cpp
|
|
enum LoginResponseCodes {
|
|
LOGIN_SUCCESS = 0,
|
|
LOGIN_BADPASS = 1, // Invalid username/password
|
|
LOGIN_BADVERSION = 6, // Client version mismatch
|
|
LOGIN_SUSPENDED = 7, // Account suspended
|
|
LOGIN_BANNED = 9, // Account banned
|
|
LOGIN_WORLDFULL = 10, // World at capacity
|
|
LOGIN_DISCONNECT = 100 // Generic disconnect
|
|
};
|
|
```
|
|
|
|
### Play Error Codes
|
|
```cpp
|
|
enum PlayResponseCodes {
|
|
PLAY_SUCCESS = 1,
|
|
PLAY_ERROR_PROBLEM = 2, // Generic error
|
|
PLAY_ERROR_WORLDFULL = 3, // World full
|
|
PLAY_ERROR_LOCKED = 4, // World locked
|
|
PLAY_ERROR_BANNED = 5, // Character banned
|
|
PLAY_ERROR_SUSPENDED = 6, // Character suspended
|
|
PLAY_ERROR_SERVER_TIMEOUT = 7, // Timeout waiting for world
|
|
PLAY_ERROR_CHAR_NOT_LOADED = 8 // Character data issue
|
|
};
|
|
```
|
|
|
|
### Character Creation Error Codes
|
|
```cpp
|
|
enum CreateResponseCodes {
|
|
CREATESUCCESS_REPLY = 1,
|
|
INVALIDRACE_REPLY = 2, // Invalid race selection
|
|
INVALIDGENDER_REPLY = 3, // Invalid gender
|
|
BADNAMELENGTH_REPLY = 9, // Name too short/long
|
|
NAMEINVALID_REPLY = 10, // Invalid characters
|
|
NAMEFILTER_REPLY = 11, // Profanity filter
|
|
NAMETAKEN_REPLY = 12, // Name already exists
|
|
OVERLOADEDSERVER_REPLY = 13, // Server overloaded
|
|
UNKNOWNERROR_REPLY = 20 // Generic error
|
|
};
|
|
```
|
|
|
|
## Database Operations
|
|
|
|
### Account Management
|
|
|
|
1. **Account Loading**:
|
|
```sql
|
|
SELECT id, name, password, suspended, banned
|
|
FROM login_accounts
|
|
WHERE name = ? AND password = SHA2(?, 256)
|
|
```
|
|
|
|
2. **Account Creation** (if enabled):
|
|
```sql
|
|
INSERT INTO login_accounts (name, password, created)
|
|
VALUES (?, SHA2(?, 256), NOW())
|
|
```
|
|
|
|
3. **IP Address Update**:
|
|
```sql
|
|
UPDATE login_accounts
|
|
SET last_ip = ?, last_login = NOW()
|
|
WHERE id = ?
|
|
```
|
|
|
|
### Character Operations
|
|
|
|
1. **Character List**:
|
|
```sql
|
|
SELECT c.id, c.name, c.server_id, c.level, c.race,
|
|
c.class, c.zone, c.gender, c.deity
|
|
FROM characters c
|
|
WHERE c.account_id = ? AND c.deleted = 0
|
|
ORDER BY c.server_id, c.name
|
|
```
|
|
|
|
2. **Character Creation**:
|
|
```sql
|
|
INSERT INTO characters
|
|
(account_id, server_id, name, race, class, gender,
|
|
deity, body_size, body_age, created)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NOW())
|
|
```
|
|
|
|
3. **Character Deletion**:
|
|
```sql
|
|
UPDATE characters
|
|
SET deleted = 1, deleted_date = NOW()
|
|
WHERE id = ? AND account_id = ?
|
|
```
|
|
|
|
### World Server Tracking
|
|
|
|
1. **World Registration**:
|
|
```sql
|
|
INSERT INTO login_worldservers
|
|
(name, ip_address, port, status)
|
|
VALUES (?, ?, ?, ?)
|
|
ON DUPLICATE KEY UPDATE
|
|
ip_address = VALUES(ip_address),
|
|
port = VALUES(port),
|
|
status = VALUES(status)
|
|
```
|
|
|
|
2. **Status Updates**:
|
|
```sql
|
|
UPDATE login_worldservers
|
|
SET status = ?, players = ?, last_update = NOW()
|
|
WHERE id = ?
|
|
```
|
|
|
|
## Security Considerations
|
|
|
|
### Password Handling
|
|
- Passwords are hashed using SHA256 before transmission
|
|
- Never store plaintext passwords
|
|
- Use prepared statements to prevent SQL injection
|
|
|
|
### Session Management
|
|
- Generate unique session keys for world handoff
|
|
- Session keys expire after use or timeout
|
|
- IP address validation between login and world entry
|
|
|
|
### Rate Limiting
|
|
- Limit login attempts per IP
|
|
- Throttle character creation requests
|
|
- Monitor for abnormal packet patterns
|
|
|
|
### Packet Validation
|
|
- Verify packet size limits
|
|
- Check sequence numbers for replay attacks
|
|
- Validate all input data ranges
|
|
|
|
## Performance Optimizations
|
|
|
|
### Connection Pooling
|
|
- Maintain persistent database connections
|
|
- Reuse world server connections
|
|
- Implement connection timeout and retry logic
|
|
|
|
### Caching
|
|
- Cache world server status for 10 seconds
|
|
- Cache character lists until modified
|
|
- Cache opcode mappings per version
|
|
|
|
### Threading Model
|
|
- Main thread handles network I/O
|
|
- Database operations on thread pool
|
|
- World server communication async
|
|
- Client processing single-threaded per client
|
|
|
|
## Monitoring and Logging
|
|
|
|
### Key Metrics
|
|
- Active client connections
|
|
- Login success/failure rates
|
|
- World server availability
|
|
- Database query performance
|
|
- Packet processing latency
|
|
|
|
### Log Levels
|
|
```cpp
|
|
enum LogLevel {
|
|
LOGIN__INFO, // General information
|
|
LOGIN__DEBUG, // Detailed debugging
|
|
LOGIN__ERROR, // Error conditions
|
|
LOGIN__WARNING, // Warning conditions
|
|
WORLD__INFO, // World server info
|
|
WORLD__ERROR, // World server errors
|
|
OPCODE__DEBUG, // Packet opcodes
|
|
INIT__INFO, // Initialization
|
|
INIT__ERROR // Init failures
|
|
};
|
|
```
|
|
|
|
### Critical Events to Log
|
|
- Failed login attempts
|
|
- Account creation
|
|
- Character creation/deletion
|
|
- World server registration/deregistration
|
|
- Network errors and timeouts
|
|
- Database connection issues
|
|
|
|
## Configuration
|
|
|
|
### Main Configuration (config.json)
|
|
```json
|
|
{
|
|
"loginconfig": {
|
|
"serverport": 5999,
|
|
"serverip": "",
|
|
"accountcreation": 1,
|
|
"expansionflag": 32463,
|
|
"citiesflag": 255,
|
|
"defaultsubscriptionlevel": -1,
|
|
"enabledraces": 65535,
|
|
"webloginaddress": "0.0.0.0",
|
|
"webloginport": 8080,
|
|
"webcertfile": "",
|
|
"webkeyfile": "",
|
|
"webkeypassword": "",
|
|
"webhardcodeuser": "admin",
|
|
"webhardcodepassword": "password"
|
|
}
|
|
}
|
|
```
|
|
|
|
### Database Configuration
|
|
```json
|
|
{
|
|
"database": {
|
|
"host": "localhost",
|
|
"port": 3306,
|
|
"username": "eq2login",
|
|
"password": "password",
|
|
"database": "eq2login",
|
|
"max_connections": 10,
|
|
"connection_timeout": 5
|
|
}
|
|
}
|
|
```
|
|
|
|
## Troubleshooting
|
|
|
|
### Common Issues
|
|
|
|
1. **"Version Mismatch" Error**:
|
|
- Client version not in opcode database
|
|
- Solution: Update opcodes.sql
|
|
|
|
2. **"Cannot Connect to Login Server"**:
|
|
- Firewall blocking port 5999
|
|
- Server not running
|
|
- Network configuration issue
|
|
|
|
3. **"World Server Unavailable"**:
|
|
- World server not registered
|
|
- World server crashed
|
|
- Network issue between login and world
|
|
|
|
4. **Character Creation Fails**:
|
|
- Name already taken
|
|
- Invalid race/class combination
|
|
- World server communication timeout
|
|
|
|
5. **Cannot Enter World**:
|
|
- Session key mismatch
|
|
- World server full
|
|
- Character data corruption
|
|
|
|
### Debug Commands
|
|
|
|
The login server supports console commands (Linux):
|
|
- `l` - List all connected world servers
|
|
- `v` - Display version information
|
|
- `h` - Show help menu
|
|
- `q` - Quit server gracefully
|
|
|
|
## Protocol Evolution
|
|
|
|
### Version Differences
|
|
|
|
**Version <= 283**:
|
|
- Server ID not included in play request
|
|
- Character name used for lookup
|
|
|
|
**Version <= 561**:
|
|
- Auto-enter world after character creation
|
|
- Different packet structure alignment
|
|
|
|
**Version >= 546**:
|
|
- Enhanced error logging format
|
|
- Larger packet size support
|
|
|
|
**Version >= 1208**:
|
|
- New login request structure
|
|
- Additional client version field
|
|
|
|
## Conclusion
|
|
|
|
The EverQuest II login server implements a sophisticated authentication and routing system with multiple layers of security and error handling. The combination of RC4 encryption, CRC32 integrity checking, and session-based authentication provides a robust framework for managing player connections and character data.
|
|
|
|
Key design principles:
|
|
- **Security First**: All communications encrypted
|
|
- **Scalability**: Support for multiple world servers
|
|
- **Reliability**: Comprehensive error handling
|
|
- **Performance**: Efficient caching and threading
|
|
- **Maintainability**: Clear separation of concerns
|
|
|
|
The login server acts as the gateway to the game world, ensuring only authenticated players can access their characters while maintaining the integrity and security of the game environment. |