# EQ2 Protocol This library is meant to replicate EverQuest 2's UDP protocol. The work here is based on the immense work done by contributors to the EQ2Emu project. I am porting it to Go with the idea of a simpler, faster development pipeline - and as a personal experiment. ## Opcodes The opcodes package manages the mapping between internal emulator opcodes and client-specific opcodes across different client versions. ### OpcodeManager The `OpcodeManager` handles opcode translation for specific client versions: ```go import "git.sharkk.net/EQ2/Protocol/opcodes" // Create a manager for client version 60085 om := opcodes.NewOpcodeManager(60085) // Load opcode mappings from database err := om.LoadFromDatabase(db, "opcodes_table") // Convert between emulator and client opcodes clientOpcode := om.EmuToEQ(opcodes.OP_LoginRequestMsg) emuOpcode := om.EQToEmu(0x0123) // Get opcode names name := om.EmuToName(opcodes.OP_LoginRequestMsg) ``` ### Opcode Types - **EmuOpcode**: Internal emulator opcodes (constants like `OP_LoginRequestMsg`) - **Protocol opcodes**: Low-level protocol opcodes (e.g., `OP_SessionRequest`, `OP_Packet`) - **Client opcodes**: Version-specific opcodes sent to/from the game client ### Global Manager Cache ```go // Get or create a manager for a specific version mgr := opcodes.GetManager(version, db, tableName) // Get all loaded versions versions := opcodes.GetLoadedVersions() ``` ## Packet Definitions The defs package provides structured packet definitions with automatic version selection based on client version. ### PacketManager Manages packet structure lookups with version-aware selection: ```go import "git.sharkk.net/EQ2/Protocol/defs" // Create a manager for client version 60085 pm := defs.NewPacketManager(60085) // Get the appropriate packet struct for this version ps := pm.GetStruct("CreateCharacter", 60085) // Returns CreateCharacterV60085 struct // Get by exact version ps := pm.GetStructByVersion("CreateCharacter", 373) // Get by opcode ps := pm.GetStructByOpcode(opcodes.OP_CreateCharacterRequestMsg) // Create a new packet instance packet := pm.CreateInstance("CreateCharacter") ``` ### Version Selection The manager automatically selects the highest packet version that doesn't exceed the client version: - Client 1000 requesting "CreateCharacter" gets version 869 - Client 60085 requesting "CreateCharacter" gets version 60085 - Client 70000 requesting "CreateCharacter" gets version 65534 ### Code Generation Packet structures and registrations are generated from XML definitions: ```bash # Regenerate packet structures from XML cd defs && go generate # XML definitions are in defs/xml/ # Generated code goes to defs/generated/ ``` ## Packets The packets package provides base packet types and interfaces for all protocol packets. ### Packet Interface All packets implement the `Packet` interface: ```go type Packet interface { GetOpcode() uint16 SetOpcode(uint16) GetVersion() uint16 SetVersion(uint16) GetSequence() uint16 SetSequence(uint16) GetData() []byte GetSize() uint32 } ``` ### Base Packet Types ```go // RawPacket - Basic packet with opcode and data packet := &packets.RawPacket{ Opcode: 0x0123, Data: []byte{...}, } // EncodedPacket - Packet with protocol encoding encoded := &packets.EncodedPacket{ Sequence: seq, Opcode: opcode, Data: data, Compressed: false, } ``` ### Packet Processing ```go // Check if packet needs processing if packet.ShouldCombine() { // Handle combined packets } if packet.IsProtocolPacket() { // Handle protocol-level packets } // Encode for transmission encoded := packet.Encode() // Decode received data packet, err := packets.Decode(data) ``` ## Stream The stream package handles bidirectional packet streaming with buffering, fragmentation, and reassembly. ### PacketStream Manages packet flow between client and server: ```go import "git.sharkk.net/EQ2/Protocol/stream" // Create a new packet stream stream := stream.NewPacketStream(conn, bufferSize) // Set packet handlers stream.SetIncomingHandler(func(packet packets.Packet) { // Process incoming packets }) stream.SetOutgoingHandler(func(packet packets.Packet) { // Process outgoing packets }) // Start processing stream.Start() // Send a packet err := stream.Send(packet) // Receive packets (handled by callback) // Packets are automatically decoded and reassembled ``` ### Features - **Automatic fragmentation**: Large packets are split into fragments - **Reassembly**: Fragments are automatically reassembled - **Buffering**: Configurable send/receive buffers - **Sequencing**: Automatic sequence number management - **Compression**: Optional packet compression support ### Stream Configuration ```go config := &stream.Config{ MaxPacketSize: 512, BufferSize: 8192, EnableCompression: true, Timeout: 30 * time.Second, } stream := stream.NewPacketStreamWithConfig(conn, config) ``` ## Factory The factory package provides centralized packet creation and registration. ### PacketFactory Creates packets based on opcodes and manages packet type registration: ```go import "git.sharkk.net/EQ2/Protocol/factory" // Get the global factory instance factory := factory.GetInstance() // Register a packet type factory.RegisterPacket(opcodes.OP_LoginRequestMsg, func() packets.Packet { return &LoginRequestPacket{} }) // Create a packet by opcode packet := factory.CreatePacket(opcodes.OP_LoginRequestMsg) // Create with data packet := factory.CreatePacketWithData(opcode, data) // Create from raw bytes (auto-detects type) packet, err := factory.CreateFromBytes(rawData) ``` ### Packet Registration Packets can be registered globally or per-version: ```go // Register for all versions factory.RegisterPacket(opcode, constructor) // Register version-specific factory.RegisterVersionedPacket(opcode, version, constructor) // Bulk registration factory.RegisterPackets(map[uint16]PacketConstructor{ opcodes.OP_LoginRequestMsg: NewLoginRequest, opcodes.OP_CharacterCreate: NewCharacterCreate, }) ``` ### Integration with PacketManager The factory can work with the defs package for automatic struct-based packets: ```go // Set packet manager for automatic struct registration factory.SetPacketManager(defs.GetManager(version)) // Now factory can create packets from struct definitions packet := factory.CreatePacket(opcodes.OP_CreateCharacterRequestMsg) // Returns properly versioned CreateCharacter struct ``` ## Usage Example Complete example showing protocol usage: ```go package main import ( "git.sharkk.net/EQ2/Protocol/opcodes" "git.sharkk.net/EQ2/Protocol/defs" "git.sharkk.net/EQ2/Protocol/stream" "git.sharkk.net/EQ2/Protocol/factory" ) func main() { version := uint16(60085) // Initialize managers opcodeManager := opcodes.GetManager(version, db, "opcodes") packetManager := defs.GetManager(version) // Setup factory factory := factory.GetInstance() factory.SetPacketManager(packetManager) // Create stream for connection stream := stream.NewPacketStream(conn, 8192) // Handle incoming packets stream.SetIncomingHandler(func(packet packets.Packet) { // Translate opcode emuOpcode := opcodeManager.EQToEmu(packet.GetOpcode()) // Process based on opcode switch emuOpcode { case opcodes.OP_LoginRequestMsg: handleLogin(packet) case opcodes.OP_CreateCharacterRequestMsg: handleCharacterCreate(packet) } }) // Send a packet packet := packetManager.CreateInstance("LSWorldList") populateWorldList(packet) // Convert to client opcode clientOpcode := opcodeManager.EmuToEQ(opcodes.OP_WorldListMsg) packet.SetOpcode(clientOpcode) // Send to client stream.Send(packet) // Start processing stream.Start() } ``` ## Protocol Architecture The protocol library is organized in layers: 1. **Opcodes**: Manages opcode mappings and translations 2. **Defs**: Defines packet structures with version management 3. **Packets**: Base packet types and interfaces 4. **Factory**: Creates packet instances from opcodes/data 5. **Stream**: Handles network I/O, fragmentation, and reassembly Each layer builds on the previous, providing a complete protocol implementation for EQ2 server emulation.