1
0
Protocol/LOGIN.md
2025-09-01 13:23:10 -05:00

21 KiB

EverQuest II Login Server - Complete Technical Documentation

Table of Contents

  1. Overview
  2. Architecture
  3. Network Protocol Stack
  4. Encryption and Security
  5. Packet Structure
  6. Complete Login Flow
  7. Character Management
  8. World Server Communication
  9. Error Handling
  10. 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

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:

    // 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:

    // 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:

    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:

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

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

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:

    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:

    // Server checks version compatibility
    if (!EQOpcodeManager.count(GetOpcodeVersion(version))) {
        // Send incompatible version error
        SendLoginDeniedBadVersion();
        return;
    }
    
  3. Account Verification:

    // 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:

    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:

    Client  Server: OP_AllWSDescRequestMsg
    {}  // Empty request
    
  2. Server Builds World List:

    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:

    // Server loads characters from database
    database.LoadCharacters(GetLoginAccount(), GetVersion());
    
  2. Character List Response:

    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:

    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:

    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:

    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:

    Client  Server: OP_PlayCharacterRequestMsg
    {
        char_id: selected_character
        server_id: world_server      // Version > 283
        name: "CharacterName"        // Version <= 283
    }
    
  2. World Server Handoff:

    Login  World: ServerOP_UsertoWorldReq
    {
        char_id: character_id
        lsaccountid: account_id
        worldid: server_id
        ip_address: client_ip
    }
    
  3. World Server Response:

    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:

    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:

    Client  Server: OP_DeleteCharacterRequestMsg
    {
        char_id: character_id
        server_id: world_id
        name: "CharacterName"
    }
    
  2. Verification:

    // Verify ownership
    bool valid = database.VerifyDelete(account_id, char_id, name);
    
  3. World Server Notification:

    Login  World: ServerOP_DeleteCharacter
    {
        char_id: character_id
        account_id: account_id
    }
    
  4. Delete Response:

    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:

    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:

    World  Login: ServerOP_LSStatus
    {
        status: current_status
        players: current_count
        zones: active_zones
    }
    // Sent every 30 seconds
    
  3. Player Updates:

    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

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

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

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:

    SELECT id, name, password, suspended, banned 
    FROM login_accounts 
    WHERE name = ? AND password = SHA2(?, 256)
    
  2. Account Creation (if enabled):

    INSERT INTO login_accounts (name, password, created) 
    VALUES (?, SHA2(?, 256), NOW())
    
  3. IP Address Update:

    UPDATE login_accounts 
    SET last_ip = ?, last_login = NOW() 
    WHERE id = ?
    

Character Operations

  1. Character List:

    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:

    INSERT INTO characters 
    (account_id, server_id, name, race, class, gender, 
     deity, body_size, body_age, created)
    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NOW())
    
  3. Character Deletion:

    UPDATE characters 
    SET deleted = 1, deleted_date = NOW() 
    WHERE id = ? AND account_id = ?
    

World Server Tracking

  1. World Registration:

    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:

    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

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)

{
    "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

{
    "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.