1
0
Protocol/packet.go
2025-09-02 12:30:36 -05:00

180 lines
3.7 KiB
Go

package eq2net
import (
"encoding/binary"
"fmt"
"net"
"time"
)
type EQPacket struct {
Buffer []byte // Raw packet data
Opcode uint16 // Packet opcode
SrcIP net.IP // Source IP address
SrcPort uint16 // Source port
DstIP net.IP // Destination IP address
DstPort uint16 // Destination port
Timestamp time.Time // When packet was created/received
Priority uint32 // Priority in processing
Version int16 // Protocol version
}
func NewEQPacket(opcode uint16, data []byte) *EQPacket {
p := &EQPacket{
Opcode: opcode,
Timestamp: time.Now(),
Priority: 0,
Version: 0,
}
if len(data) > 0 {
p.Buffer = make([]byte, len(data))
copy(p.Buffer, data)
}
return p
}
// Size returns the size of the packet data (excluding opcode)
func (p *EQPacket) Size() uint32 {
return uint32(len(p.Buffer))
}
// TotalSize returns the total size including opcode
func (p *EQPacket) TotalSize() uint32 {
opcodeSize := uint32(1)
if p.Opcode > 0xFF {
opcodeSize = 2
}
return p.Size() + opcodeSize
}
// GetRawOpcode returns the raw opcode value
func (p *EQPacket) GetRawOpcode() uint16 {
return p.Opcode
}
// GetOpcodeName returns the string name of the opcode
func (p *EQPacket) GetOpcodeName() string {
switch p.Opcode {
case OP_SessionRequest:
return "OP_SessionRequest"
case OP_SessionResponse:
return "OP_SessionResponse"
case OP_Combined:
return "OP_Combined"
case OP_SessionDisconnect:
return "OP_SessionDisconnect"
case OP_KeepAlive:
return "OP_KeepAlive"
case OP_SessionStatRequest:
return "OP_SessionStatRequest"
case OP_SessionStatResponse:
return "OP_SessionStatResponse"
case OP_Packet:
return "OP_Packet"
case OP_Fragment:
return "OP_Fragment"
case OP_OutOfOrderAck:
return "OP_OutOfOrderAck"
case OP_Ack:
return "OP_Ack"
case OP_AppCombined:
return "OP_AppCombined"
case OP_OutOfSession:
return "OP_OutOfSession"
default:
return fmt.Sprintf("Unknown(0x%04X)", p.Opcode)
}
}
// Serialize writes the packet to a byte buffer
func (p *EQPacket) Serialize() []byte {
// Determine opcode size
opcodeSize := 1
if p.Opcode > 0xFF {
opcodeSize = 2
}
// Create buffer
buf := make([]byte, opcodeSize+len(p.Buffer))
// Write opcode
if opcodeSize == 2 {
binary.BigEndian.PutUint16(buf[0:2], p.Opcode)
} else {
buf[0] = 0x00
buf[1] = byte(p.Opcode)
opcodeSize = 2 // Even 1-byte opcodes use 2 bytes on wire
}
// Copy data
if len(p.Buffer) > 0 {
copy(buf[opcodeSize:], p.Buffer)
}
return buf
}
// SetNetworkInfo sets the network address info
func (p *EQPacket) SetNetworkInfo(srcIP net.IP, srcPort uint16, dstIP net.IP, dstPort uint16) {
p.SrcIP = srcIP
p.SrcPort = srcPort
p.DstIP = dstIP
p.DstPort = dstPort
}
// Clone creates a deep copy of the packet
func (p *EQPacket) Clone() *EQPacket {
newPacket := &EQPacket{
Opcode: p.Opcode,
SrcIP: make(net.IP, len(p.SrcIP)),
SrcPort: p.SrcPort,
DstIP: make(net.IP, len(p.DstIP)),
DstPort: p.DstPort,
Timestamp: p.Timestamp,
Priority: p.Priority,
Version: p.Version,
}
if p.SrcIP != nil {
copy(newPacket.SrcIP, p.SrcIP)
}
if p.DstIP != nil {
copy(newPacket.DstIP, p.DstIP)
}
if len(p.Buffer) > 0 {
newPacket.Buffer = make([]byte, len(p.Buffer))
copy(newPacket.Buffer, p.Buffer)
}
return newPacket
}
// ParsePacket creates a packet from raw network data
func ParsePacket(data []byte) (*EQPacket, error) {
if len(data) < 2 {
return nil, fmt.Errorf("packet too small: %d bytes", len(data))
}
// Extract opcode (always 2 bytes on wire)
opcode := binary.BigEndian.Uint16(data[0:2])
// Create packet
p := &EQPacket{
Opcode: opcode,
Timestamp: time.Now(),
}
// Copy remaining data if any
if len(data) > 2 {
p.Buffer = make([]byte, len(data)-2)
copy(p.Buffer, data[2:])
}
return p, nil
}