281 lines
9.6 KiB
Markdown
281 lines
9.6 KiB
Markdown
# EQ2EMu Protocol Structure Report
|
|
|
|
Overview
|
|
|
|
The EQ2EMu protocol is a custom UDP-based networking protocol designed for EverQuest II server
|
|
emulation. It implements reliability, compression, encryption, and packet fragmentation on top
|
|
of UDP.
|
|
|
|
Core Architecture
|
|
|
|
1. Protocol Layers
|
|
|
|
Application Layer
|
|
|
|
- EQApplicationPacket: High-level game packets containing game logic
|
|
- PacketStruct: Dynamic packet structure system using XML definitions
|
|
- DataBuffer: Serialization/deserialization buffer management
|
|
|
|
Protocol Layer
|
|
|
|
- EQProtocolPacket: Low-level protocol packets with sequencing and reliability
|
|
- EQStream: Stream management with connection state, retransmission, and flow control
|
|
- EQPacket: Base packet class with common functionality
|
|
|
|
2. Packet Types
|
|
|
|
Protocol Control Packets (opcodes.h)
|
|
|
|
OP_SessionRequest = 0x01 // Client initiates connection
|
|
OP_SessionResponse = 0x02 // Server accepts connection
|
|
OP_Combined = 0x03 // Multiple packets combined
|
|
OP_SessionDisconnect = 0x05 // Connection termination
|
|
OP_KeepAlive = 0x06 // Keep connection alive
|
|
OP_ServerKeyRequest = 0x07 // Request encryption key
|
|
OP_SessionStatResponse = 0x08 // Connection statistics
|
|
OP_Packet = 0x09 // Regular data packet
|
|
OP_Fragment = 0x0d // Fragmented packet piece
|
|
OP_OutOfOrderAck = 0x11 // Acknowledge out-of-order packet
|
|
OP_Ack = 0x15 // Acknowledge packet receipt
|
|
OP_AppCombined = 0x19 // Combined application packets
|
|
OP_OutOfSession = 0x1d // Out of session notification
|
|
|
|
3. Connection Flow
|
|
|
|
1. Session Establishment
|
|
- Client sends OP_SessionRequest with session parameters
|
|
- Server responds with OP_SessionResponse containing session ID and encryption key
|
|
- RC4 encryption initialized using the provided key
|
|
2. Data Transfer
|
|
- Packets are sequenced (16-bit sequence numbers)
|
|
- Reliable packets require acknowledgment
|
|
- Retransmission on timeout (default 500ms * 3.0 multiplier)
|
|
- Packet combining for efficiency (max 256 bytes combined)
|
|
3. Session Termination
|
|
- Either side sends OP_SessionDisconnect
|
|
- Graceful shutdown with cleanup of pending packets
|
|
|
|
4. Key Features
|
|
|
|
Compression
|
|
|
|
- Uses zlib compression (indicated by 0x5a flag byte)
|
|
- Applied after protocol header
|
|
- Decompression required before processing
|
|
|
|
Encryption
|
|
|
|
- RC4 stream cipher
|
|
- Separate keys for client/server directions
|
|
- Client uses ~key (bitwise NOT), server uses key directly
|
|
- 20 bytes of dummy data prime both ciphers
|
|
|
|
CRC Validation
|
|
|
|
- Custom CRC16 implementation
|
|
- Applied to entire packet except last 2 bytes
|
|
- Session packets exempt from CRC
|
|
|
|
Fragmentation
|
|
|
|
- Large packets split into fragments
|
|
- Each fragment marked with OP_Fragment
|
|
- Reassembly at receiver before processing
|
|
|
|
5. Packet Structure
|
|
|
|
Basic Protocol Packet
|
|
|
|
[1-2 bytes] Opcode (1 byte if < 0xFF, otherwise 2 bytes)
|
|
[variable] Payload
|
|
[2 bytes] CRC16 (if applicable)
|
|
|
|
Combined Packet Format
|
|
|
|
[1 byte] Size of packet 1
|
|
[variable] Packet 1 data
|
|
[1 byte] Size of packet 2
|
|
[variable] Packet 2 data
|
|
...
|
|
|
|
6. Dynamic Packet System
|
|
|
|
The PacketStruct system allows runtime-defined packet structures:
|
|
|
|
- XML Configuration: Packet structures defined in XML files
|
|
- Version Support: Multiple versions per packet type
|
|
- Data Types: int8/16/32/64, float, double, string, array, color, equipment
|
|
- Conditional Fields: Fields can be conditional based on other field values
|
|
- Oversized Support: Dynamic sizing for variable-length fields
|
|
|
|
Example structure:
|
|
<Struct Name="PlayerUpdate" ClientVersion="1096">
|
|
<Data ElementName="activity" Type="int32"/>
|
|
<Data ElementName="speed" Type="float"/>
|
|
<Data ElementName="x" Type="float"/>
|
|
<Data ElementName="y" Type="float"/>
|
|
<Data ElementName="z" Type="float"/>
|
|
</Struct>
|
|
|
|
7. Opcode Management
|
|
|
|
- Dynamic Mapping: Runtime opcode mapping via configuration files
|
|
- Version Support: Different opcode sets per client version
|
|
- Name Resolution: String names mapped to numeric opcodes
|
|
- Shared Memory Option: Multi-process opcode sharing support
|
|
|
|
8. Implementation Considerations for Porting
|
|
|
|
1. Memory Management
|
|
- Extensive use of dynamic allocation
|
|
- Custom safe_delete macros for cleanup
|
|
- Reference counting for shared packets
|
|
2. Threading
|
|
- Mutex protection for all shared resources
|
|
- Separate threads for packet processing
|
|
- Condition variables for synchronization
|
|
3. Platform Dependencies
|
|
- POSIX sockets and threading
|
|
- Network byte order conversions
|
|
- Platform-specific timing functions
|
|
4. Performance Optimizations
|
|
- Packet combining to reduce overhead
|
|
- Preallocated buffers for common sizes
|
|
- Fast CRC table lookup
|
|
5. Error Handling
|
|
- Graceful degradation on errors
|
|
- Extensive logging for debugging
|
|
- Automatic retransmission on failure
|
|
|
|
9. Critical Classes to Port
|
|
|
|
1. EQStream: Core connection management
|
|
2. EQPacket/EQProtocolPacket: Packet representation
|
|
3. PacketStruct: Dynamic packet structure system
|
|
4. DataBuffer: Serialization utilities
|
|
5. OpcodeManager: Opcode mapping system
|
|
6. Crypto: RC4 encryption wrapper
|
|
7. CRC16: Custom CRC implementation
|
|
|
|
10. Protocol Quirks
|
|
|
|
- Opcode byte order depends on value (0x00 prefix for opcodes < 0xFF)
|
|
- Special handling for session control packets (no CRC)
|
|
- 20-byte RC4 priming sequence required
|
|
- Custom CRC polynomial (0xEDB88320 reversed)
|
|
- Compression flag at variable offset based on opcode size
|
|
|
|
This protocol is designed for reliability over UDP while maintaining low latency for game
|
|
traffic. The dynamic packet structure system allows flexibility in supporting multiple client
|
|
versions without recompilation.
|
|
|
|
# UDP Network Layer Architecture
|
|
|
|
1. EQStreamFactory - The UDP Socket Manager
|
|
|
|
- Opens and manages the main UDP socket (line 148: socket(AF_INET, SOCK_DGRAM, 0))
|
|
- Binds to a specific port (line 153: bind())
|
|
- Sets socket to non-blocking mode (line 159: fcntl(sock, F_SETFL, O_NONBLOCK))
|
|
- Runs three main threads:
|
|
- ReaderLoop(): Uses recvfrom() to read incoming UDP packets (line 228)
|
|
- WriterLoop(): Manages outgoing packet transmission
|
|
- CombinePacketLoop(): Combines small packets for efficiency
|
|
|
|
2. EQStream - Per-Connection Protocol Handler
|
|
|
|
- Manages individual client connections over the shared UDP socket
|
|
- Handles packet serialization/deserialization
|
|
- Implements reliability layer (sequencing, acknowledgments, retransmission)
|
|
- WritePacket() method uses sendto() for actual UDP transmission (line 1614)
|
|
|
|
3. Key UDP Operations:
|
|
|
|
Receiving (EQStreamFactory::ReaderLoop):
|
|
|
|
recvfrom(sock, buffer, 2048, 0, (struct sockaddr *)&from, (socklen_t *)&socklen)
|
|
- Uses select() for non-blocking I/O
|
|
- Creates new EQStream instances for new connections (OP_SessionRequest)
|
|
- Routes packets to existing streams by IP:port mapping
|
|
|
|
Sending (EQStream::WritePacket):
|
|
|
|
sendto(eq_fd,(char *)buffer,length,0,(sockaddr *)&address,sizeof(address))
|
|
- Serializes protocol packets to byte arrays
|
|
- Applies compression and encryption if enabled
|
|
- Adds CRC checksums
|
|
- Sends via UDP to specific client address
|
|
|
|
4. Connection Multiplexing:
|
|
|
|
- Single UDP socket serves multiple clients
|
|
- Stream identification by IP:port combination (sprintf(temp, "%u.%d",
|
|
ntohl(from.sin_addr.s_addr), ntohs(from.sin_port)))
|
|
- Thread-safe stream management with mutex protection
|
|
|
|
So yes, EQStream and EQStreamFactory together provide a complete UDP networking layer that
|
|
implements a reliable, connection-oriented protocol on top of unreliable UDP datagrams. The
|
|
factory manages the socket and dispatches packets to individual stream instances that handle
|
|
the protocol logic for each client connection.
|
|
|
|
# TCP Usage in EQ2EMu
|
|
|
|
The TCP implementation serves a completely separate purpose from the main game client
|
|
communication:
|
|
|
|
1. HTTP Web Server (web_server.hpp)
|
|
|
|
- Purpose: Administrative/monitoring interface
|
|
- Features:
|
|
- REST API endpoints (/version, / root)
|
|
- SSL/TLS support with certificate-based encryption
|
|
- HTTP Basic Authentication with session management
|
|
- Database-driven user authentication and authorization levels
|
|
- JSON response formatting for API calls
|
|
- Use Cases:
|
|
- Server status monitoring
|
|
- Administrative controls
|
|
- Third-party tool integration
|
|
- Web-based dashboards
|
|
|
|
2. Server-to-Server Communication (tcp_connection.hpp)
|
|
|
|
- Purpose: Inter-server communication within EQ2EMu cluster
|
|
- Features:
|
|
- Custom packet framing protocol
|
|
- Optional zlib compression for bandwidth efficiency
|
|
- Message relay capabilities between servers
|
|
- Connection state management
|
|
- Use Cases:
|
|
- Login server to world server communication
|
|
- Zone server coordination
|
|
- Cross-server messaging
|
|
- Load balancing and failover
|
|
|
|
Key Differences: UDP vs TCP
|
|
|
|
| Aspect | UDP (Game Clients) | TCP (Admin/Server)
|
|
|
|
|
|------------|--------------------------------------------|------------------------------------
|
|
----|
|
|
| Protocol | Custom EQ2 protocol with reliability layer | Standard HTTP/Custom framing
|
|
|
|
|
| Encryption | RC4 stream cipher | SSL/TLS or none
|
|
|
|
|
| Clients | Game clients (players) | Web browsers/Admin tools/Other
|
|
servers |
|
|
| Port | 9001 (World), 9100 (Login) | Configurable web port
|
|
|
|
|
| Threading | 3 worker threads per factory | Thread-per-connection
|
|
|
|
|
|
|
Architecture Summary
|
|
|
|
Game Clients ←→ [UDP EQStream] ←→ World/Login Servers
|
|
↕
|
|
Admin Tools ←→ [TCP WebServer] ←→ World/Login Servers
|
|
↕
|
|
Other Servers ←→ [TCP Connection] ←→ World/Login Servers
|
|
|
|
The TCP implementation provides out-of-band management and inter-server communication, while
|
|
the UDP implementation handles all real-time game traffic. This separation allows for robust
|
|
administration without interfering with game performance. |