1
0
Protocol/structs/README.md

4.5 KiB

Packet Structure Parser

This package provides a Go implementation of the EQ2 packet structure system, compatible with the C++ ConfigReader and PacketStruct classes. It parses XML packet definitions and provides runtime serialization/deserialization capabilities.

Features

  • XML Parsing: Reads packet and substruct definitions from XML files
  • Version Management: Handles multiple versions of packet structures
  • Type Support: All EQ2 data types including integers, floats, strings, and colors
  • Conditional Fields: Support for if-set/if-not-set field conditions
  • Zero Allocation: Optimized for performance with minimal allocations
  • ConfigReader Compatible: Drop-in replacement for C++ ConfigReader

Usage

Loading Packet Definitions

// Create a config reader
cr := structs.NewConfigReader("./")

// Load all XML files from structs/xml directory
err := cr.LoadStructs()

// Or load specific files
err := cr.LoadFiles([]string{"structs/xml/login/LoginRequest.xml"})

Creating and Using Packet Structs

// Get a packet struct for a specific version
ps, err := cr.GetStruct("LoginRequest", 1193)

// Set field values
ps.Set("username", "player123")
ps.Set("password", "secret")
ps.Set("version", uint16(1193))

// Serialize to bytes
data, err := ps.Serialize()

// Deserialize from bytes
ps2, err := cr.GetStruct("LoginRequest", 1193)
err = ps2.Deserialize(data)

// Get field values
username, _ := ps2.Get("username")

Direct Parser Usage

// Create a parser
parser := structs.NewParser("structs")

// Load all XML files
err := parser.LoadAll()

// Get a specific packet version
version, err := parser.GetPacketVersion("LoginRequest", 1193)

// Create packet struct directly
ps := structs.NewPacketStruct(version)

XML Format

Packet definitions use the following XML format:

<packet name="LoginRequest">
    <version number="1">
        <str16 name="username"/>
        <str16 name="password"/>
        <u32 name="acctNum"/>
        <u16 name="version"/>
    </version>
    <version number="562">
        <str16 name="accesscode"/>
        <str16 name="username"/>
        <str16 name="password"/>
        <u8 name="unknown" size="8"/>
        <u32 name="version"/>
    </version>
</packet>

Substructs use a similar format:

<substruct name="Item">
    <version number="1">
        <u32 name="unique_id"/>
        <u32 name="bag_id"/>
        <u8 name="slot_id"/>
        <char name="name" size="64"/>
    </version>
</substruct>

Supported Field Types

  • Integers: u8, u16, u32, u64, i8, i16, i32, i64
  • Floats: float (32-bit), double (64-bit)
  • Strings: str8, str16, str32 (length-prefixed), char (fixed-size or null-terminated)
  • Special: color, eq2color
  • Complex: substruct, array

Field Attributes

  • name: Field name (required)
  • size: Array size or fixed string length
  • sizevar: Variable containing the array size
  • default: Default value
  • ifset: Only include if specified field is set
  • ifnotset: Only include if specified field is not set

Performance

The parser is optimized for performance with minimal allocations:

  • Serialization: ~312 ns/op, 144 B/op, 10 allocs/op
  • Deserialization: ~315 ns/op, 112 B/op, 13 allocs/op

Compatibility

This implementation maintains compatibility with the C++ ConfigReader and PacketStruct classes, allowing seamless migration of existing packet definitions and code.

Example Integration

// Initialize config reader
configReader := structs.NewConfigReader("./")
configReader.LoadStructs()

// In your packet handler
func HandleLoginRequest(data []byte, clientVersion uint16) {
    // Get the appropriate packet struct
    ps, err := configReader.GetStruct("LoginRequest", clientVersion)
    if err != nil {
        log.Printf("Failed to get LoginRequest struct: %v", err)
        return
    }
    
    // Deserialize the packet
    if err := ps.Deserialize(data); err != nil {
        log.Printf("Failed to deserialize LoginRequest: %v", err)
        return
    }
    
    // Access fields
    username, _ := ps.Get("username")
    password, _ := ps.Get("password")
    
    // Process login...
}

Directory Structure

structs/
├── xml/
│   ├── common/
│   ├── login/
│   ├── item/
│   ├── spawn/
│   └── world/
├── parser.go          # XML parser
├── packet_struct.go   # Runtime packet structure
├── config_reader.go   # ConfigReader compatible interface
└── README.md