1
0
2025-09-05 18:42:57 -05:00
2025-09-05 13:50:10 -05:00
2025-09-03 14:19:51 -05:00
2025-09-02 20:25:42 -05:00
2025-09-03 11:27:01 -05:00
2025-09-03 20:48:08 -05:00
2025-09-05 17:56:00 -05:00
2025-09-05 17:56:00 -05:00
2025-09-02 09:41:39 -05:00
2025-09-02 09:41:39 -05:00
2025-09-05 18:42:57 -05:00

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:

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

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

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:

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

type Packet interface {
    GetOpcode() uint16
    SetOpcode(uint16)
    GetVersion() uint16
    SetVersion(uint16)
    GetSequence() uint16
    SetSequence(uint16)
    GetData() []byte
    GetSize() uint32
}

Base Packet Types

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

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

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

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:

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:

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

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

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.

Description
No description provided
Readme 2.2 MiB
Languages
C++ 96.3%
Go 2.4%
C 1.3%