174 lines
4.5 KiB
Markdown
174 lines
4.5 KiB
Markdown
# 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
|
|
|
|
```go
|
|
// 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
|
|
|
|
```go
|
|
// 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
|
|
|
|
```go
|
|
// 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:
|
|
|
|
```xml
|
|
<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:
|
|
|
|
```xml
|
|
<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
|
|
|
|
```go
|
|
// 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
|
|
``` |